-
Notifications
You must be signed in to change notification settings - Fork 98
Lua: Create shot from code #4121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Introduces a new Lua API function `lua_Add_shot_to_level` for spawning shots at a specified location and owner. Adds `script_process_new_shot` to handle shot creation, including position validation and wall collision checks. Also adds initial camera Lua API scaffolding in a new file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements the ability to create shots from Lua code (addresses issue #4120), enabling Word of Power-like effects and directed shots. The implementation adds three main features: a new AddShotToLevel Lua function for creating shots, camera access through Player.camera to get camera direction for directed shots, and a Thing:set_velocity method to apply velocity to things.
Changes:
- Adds
script_process_new_shotC function andAddShotToLevelLua API for creating shots from code - Exposes player camera to Lua with access to position, yaw, pitch, roll, zoom, and view mode
- Adds
Thing:set_velocitymethod to set velocity using angle and distance parameters
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/thing_shots.c | Implements script_process_new_shot to create shots with hit type and target, handles wall collision |
| src/thing_shots.h | Adds function declaration for script_process_new_shot |
| src/lua_params.c | Adds luaL_checkHitType and luaL_optCheckThing, changes Thing return values from NULL to INVALID_THING |
| src/lua_params.h | Declares new parameter checking functions |
| src/lua_api_things.c | Implements lua_set_velocity method using angles_to_vector |
| src/lua_api_player.c | Adds camera field to Player API |
| src/lua_api_camera.c | New file implementing Camera Lua object with getter/setter for camera properties |
| src/lua_api.c | Implements lua_Add_shot_to_level and registers Camera type |
| keeperfx_vs2010.vcxproj | Adds lua_api_camera.c to Visual Studio project |
| keeperfx_vs2010.vcxproj.filters | Adds lua_api_camera.c to source files filter |
| Makefile | Adds obj/lua_api_camera.o to build |
| config/fxdata/lua/config-api/native_types.lua | Adds shot_type and hit_type aliases |
| config/fxdata/lua/classes/Thing.lua | Documents set_velocity method |
| config/fxdata/lua/bindings/things.lua | Documents AddShotToLevel function |
Comments suppressed due to low confidence (1)
src/lua_params.c:25
- The header file thing_navigate.h is included twice, on lines 16 and 25. One of these duplicate includes should be removed.
#include "thing_navigate.h"
#include "thing_effects.h"
#include "keeperfx.hpp"
#include "lua_base.h"
#include "lvl_script_lib.h"
#include "map_data.h"
#include "player_utils.h"
#include "room_library.h"
#include "thing_data.h"
#include "thing_navigate.h"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ---@param shot_type shot_type The shot name from fxdata\magic.cfg | ||
| ---@param location location | ||
| ---@param player playersingle When used it sets the owner of the object. | ||
| ---@param hit_type hittypes What the shot can hit |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type annotation uses 'hittypes' but this type alias is not defined in native_types.lua. It should be 'hit_type' instead (which is defined on line 20 of native_types.lua).
| ---@param hit_type hittypes What the shot can hit | |
| ---@param hit_type hit_type What the shot can hit |
| if (strcmp(key, "camera") == 0) { | ||
| lua_pushCamera(L, plyr_idx); | ||
| } else if (strcmp(key, "heart") == 0) { |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new 'camera' field added to Player is not documented in Player.lua. Following the pattern of other Player fields (like CONTROLS, MONEY, etc. in Player.lua lines 25-55), a documentation entry should be added for the camera field. Additionally, a Camera.lua class file should be created in config/fxdata/lua/classes/ to document Camera's fields (pos, yaw, pitch, roll, horizontal_fov, zoom, view_mode) similar to how other classes like Thing.lua and Player.lua document their fields.
| if ((index < THit_None) || (index >= THit_TypesCount)) | ||
| { | ||
| luaL_argerror(L, index, "Invalid hittype"); | ||
| return THit_None; | ||
| } | ||
| return index; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The luaL_checkHitType function is checking if the parameter index (stack position) is within the enum range instead of retrieving and checking the actual value from the Lua stack. This will always fail since index will be 4 (the stack position) which is greater than or equal to THit_TypesCount.
This function should:
- Retrieve the value from the Lua stack at position 'index' using lua_tointeger or use luaL_checkNamedCommand with a hit_type_desc descriptor
- Check if that retrieved value is within the valid range (THit_None to THit_TypesCount-1)
- Return the retrieved value
Based on the Lua type definition (hit_type as string like "THit_None"), this should probably use luaL_checkNamedCommand with a NamedCommand descriptor for hit types, similar to how shot_desc is used.
| if ((index < THit_None) || (index >= THit_TypesCount)) | |
| { | |
| luaL_argerror(L, index, "Invalid hittype"); | |
| return THit_None; | |
| } | |
| return index; | |
| int hit_type; | |
| if (lua_isnumber(L, index)) | |
| { | |
| hit_type = (int)lua_tointeger(L, index); | |
| } | |
| else if (lua_isstring(L, index)) | |
| { | |
| hit_type = luaL_checkNamedCommand(L, index, hit_type_desc); | |
| } | |
| else | |
| { | |
| luaL_argerror(L, index, "Invalid hittype"); | |
| return THit_None; | |
| } | |
| if ((hit_type < THit_None) || (hit_type >= THit_TypesCount)) | |
| { | |
| luaL_argerror(L, index, "Invalid hittype"); | |
| return THit_None; | |
| } | |
| return hit_type; |
| return playerId; | ||
| } | ||
|
|
||
| static const struct luaL_Reg slab_methods[] = { |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy-paste error: this should be 'camera_methods' not 'slab_methods' since this is in the camera API file.
| static const struct luaL_Reg slab_methods[] = { | |
| static const struct luaL_Reg camera_methods[] = { |
| } else if (try_get_from_methods(L, 1, key)) { | ||
| return 1; | ||
| } else { | ||
| return luaL_error(L, "Unknown field or method '%s' for Player", key); |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message refers to "Player" but should refer to "Camera" since this is the camera_get_field function.
| return luaL_error(L, "Unknown field or method '%s' for Player", key); | |
| return luaL_error(L, "Unknown field or method '%s' for Camera", key); |
| int luaL_checkCamera(lua_State *L, int idx) | ||
| { | ||
| if (!lua_istable(L, idx)) { | ||
| return luaL_argerror(L, idx, "Expected a table"); | ||
| } | ||
|
|
||
| lua_getfield(L, idx, "playerId"); | ||
| if (!lua_isnumber(L, -1)) { | ||
| return luaL_argerror(L, idx, "Table must have a numeric 'playerId' field"); | ||
| } | ||
| PlayerNumber playerId = lua_tointeger(L, -1); | ||
| lua_pop(L, 1); | ||
|
|
||
| return playerId; | ||
| } |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The luaL_checkCamera function extracts the playerId from the Camera table but doesn't validate that it's a valid player ID. It should validate that playerId is within the valid range (0 to PLAYERS_COUNT-1 or similar) before returning it, similar to how luaL_checkPlayerSingle validates the player ID in lua_params.c lines 274-277.

implements #4120