From e4613fc75e136febe5ef00917d4d1bdb3e2e9a9e Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:21:58 -0500 Subject: [PATCH 01/10] light: a small range change from script is not dropped --- src/xrGame/script_light_inline.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/xrGame/script_light_inline.h b/src/xrGame/script_light_inline.h index edaaa9ec64..8b7653f464 100644 --- a/src/xrGame/script_light_inline.h +++ b/src/xrGame/script_light_inline.h @@ -89,7 +89,7 @@ class ScriptLight bool bShadow; bool bHudMode; float fBrightness; - float fRange; + float fRange = 0.f; Fcolor color; LPCSTR texture; int iType; @@ -157,9 +157,14 @@ class ScriptLight bShadow = state; } - IC void SetRange(float range) + IC void SetRange(float range) { - m_light->set_range(range); + if (fsimilar(fRange, range, EPS)) + return; + + // the light ignores a range change smaller than ten percent, so step past that band first + m_light->set_range(range * 2.f + 1.f); + m_light->set_range(range); fRange = range; } From 6aaa7958b6f26d0049fb83ddb2263d33ff47bbcf Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:21:59 -0500 Subject: [PATCH 02/10] input: fire the mouse wheel callback on the actor --- src/xrGame/Level_input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xrGame/Level_input.cpp b/src/xrGame/Level_input.cpp index 3f85153afb..3971fff6b1 100644 --- a/src/xrGame/Level_input.cpp +++ b/src/xrGame/Level_input.cpp @@ -66,7 +66,7 @@ void CLevel::IR_OnMouseWheel(int direction) return; } } - //g_actor->callback(GameObject::eMouseWheel)(direction); + g_actor->callback(GameObject::eMouseWheel)(direction); } #endif /* avo: end */ From aef4a554b459572310836e132e6e7e3d94b5d9fd Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:12 -0500 Subject: [PATCH 03/10] script: projection depth, projecting a list of points at once, and a check whether the ui holds the input --- src/xrGame/level_script.cpp | 38 ++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/xrGame/level_script.cpp b/src/xrGame/level_script.cpp index 7b22c58b69..761ff06ebd 100644 --- a/src/xrGame/level_script.cpp +++ b/src/xrGame/level_script.cpp @@ -712,6 +712,15 @@ void enable_input() #endif // #ifdef DEBUG } +bool is_input_captured() +{ + if (g_bDisableAllInput) + return true; + + CUIGameCustom* ui = CurrentGameUI(); + return ui && ui->TopInputReceiver() != NULL; +} + void spawn_phantom(const Fvector& position) { Level().spawn_item("m_phantom", position, u32(-1), u16(-1), false); @@ -1807,9 +1816,8 @@ const Fvector3 world2ui_with_depth(Fvector pos, bool hud = false, bool allow_off x /= width_fk; y /= height_fk; - float depth = v_res.w < 0 ? -1 : 1; - - return {x, y, depth}; + // post projection w, negative behind the camera, its magnitude is the view distance + return {x, y, v_res.w}; } const Fvector2 world2ui(Fvector pos, bool hud = false, bool allow_offscreen = false) @@ -1818,6 +1826,28 @@ const Fvector2 world2ui(Fvector pos, bool hud = false, bool allow_offscreen = fa return {res.x, res.y}; } +::luabind::object world2ui_many(::luabind::object points, bool hud, bool allow_offscreen) +{ + ::luabind::object table = ::luabind::newtable(ai().script_engine().lua()); + + if (!points || points.type() != LUA_TTABLE) + { + Msg("!world2ui_many: argument is not a table"); + return table; + } + + for (int i = 1;; ++i) + { + std::optional pos = ::luabind::object_cast_nothrow(points[i]); + if (!pos) + break; + + table[i] = world2ui_with_depth(*pos, hud, allow_offscreen); + } + + return table; +} + // demonized: unproject ui coordinates (ie mouse cursor coordinates) to world coordinates // returns position and underlying object id if found. If there is no object, obj_id will be 65535 void ui2world(Fvector2 pos, bool allow_offscreen, Fvector& res, u16& obj_id) @@ -2608,6 +2638,7 @@ void CLevel::script_register(lua_State* L) def("present", is_level_present), def("disable_input", disable_input), def("enable_input", enable_input), + def("is_input_captured", is_input_captured), def("spawn_phantom", spawn_phantom), def("get_bounding_volume", get_bounding_volume), @@ -2847,6 +2878,7 @@ void CLevel::script_register(lua_State* L) def("get_visual_userdata", GetVisualUserdata), def("world2ui", world2ui), def("world2ui_with_depth", world2ui_with_depth), + def("world2ui_many", world2ui_many), def("ui2world", (void (*)(Fvector2, Fvector&, u16&))&ui2world, pure_out_value<2>() + pure_out_value<3>()), def("ui2world", (void (*)(Fvector&, Fvector&, u16&))&ui2world, pure_out_value<2>() + pure_out_value<3>()), def("ui2world_offscreen", (void (*)(Fvector2, Fvector&, u16&))& ui2world_offscreen, pure_out_value<2>() + pure_out_value<3>()), From 883d0470576d350ad190cd9fab943e9e46ae0430 Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:13 -0500 Subject: [PATCH 04/10] script: drop a whole ini section and read a four float key --- src/xrCore/Xr_ini.cpp | 20 ++++++++++++ src/xrCore/xr_ini.h | 1 + src/xrServerEntities/script_ini_file.cpp | 31 +++++++++++++++++++ src/xrServerEntities/script_ini_file.h | 2 ++ .../script_ini_file_script.cpp | 14 ++++++++- 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/xrCore/Xr_ini.cpp b/src/xrCore/Xr_ini.cpp index e29b68304d..5372c43ee3 100644 --- a/src/xrCore/Xr_ini.cpp +++ b/src/xrCore/Xr_ini.cpp @@ -2031,3 +2031,23 @@ void CInifile::remove_line(LPCSTR S, LPCSTR L) data.Data.erase(A); } } + +bool CInifile::remove_section(LPCSTR S) +{ + R_ASSERT(!m_flags.test(eReadOnly)); + + if (!S || !S[0]) + return false; + + // sections are stored parsed and lower cased, the same way a write creates them + string256 sect; + _parse(sect, S); + _strlwr(sect); + + RootIt I = std::lower_bound(DATA.begin(), DATA.end(), sect, sect_pred); + if (I == DATA.end() || xr_strcmp(*(*I).Name, sect) != 0) + return false; + + DATA.erase(I); + return true; +} diff --git a/src/xrCore/xr_ini.h b/src/xrCore/xr_ini.h index 4e546abe4e..f149bce569 100644 --- a/src/xrCore/xr_ini.h +++ b/src/xrCore/xr_ini.h @@ -339,6 +339,7 @@ class XRCORE_API CInifile void w_bool(LPCSTR S, LPCSTR L, BOOL V, LPCSTR comment = 0); void remove_line(LPCSTR S, LPCSTR L); + bool remove_section(LPCSTR S); }; // Main configuration file diff --git a/src/xrServerEntities/script_ini_file.cpp b/src/xrServerEntities/script_ini_file.cpp index de241d9251..8ffe824fc0 100644 --- a/src/xrServerEntities/script_ini_file.cpp +++ b/src/xrServerEntities/script_ini_file.cpp @@ -104,6 +104,20 @@ Fvector CScriptIniFile::r_fvector3(LPCSTR S, LPCSTR L) return (inherited::r_fvector3(S, L)); } +Fvector4 CScriptIniFile::r_fvector4(LPCSTR S, LPCSTR L) +{ + Fvector4 res; + res.set(0.0f, 0.0f, 0.0f, 0.0f); + + if (!S || !L || !inherited::line_exist(S, L)) + { + Msg("!CScriptIniFile::r_vector4: no line [%s] in section [%s]", L ? L : "", S ? S : ""); + return res; + } + + return (inherited::r_fvector4(S, L)); +} + //AVO: additional methods to allow writing to ini files #ifdef INI_FILE_EXTENDED_EXPORTS void CScriptIniFile::w_bool(LPCSTR S, LPCSTR L, bool V, LPCSTR comment) @@ -236,6 +250,23 @@ void CScriptIniFile::remove_line(LPCSTR S, LPCSTR L) inherited::remove_line(S, L); } +bool CScriptIniFile::remove_section(LPCSTR S) +{ + if (!S || !S[0]) + { + Msg("!CScriptIniFile::remove_section: empty section name"); + return false; + } + + if (inherited::m_flags.test(eReadOnly)) + { + Msg("!CScriptIniFile::remove_section: [%s] is read only", S); + return false; + } + + return inherited::remove_section(S); +} + void CScriptIniFile::set_override_names(bool b) { inherited::set_override_names(b); diff --git a/src/xrServerEntities/script_ini_file.h b/src/xrServerEntities/script_ini_file.h index 349cb2e1cf..550aa99fdc 100644 --- a/src/xrServerEntities/script_ini_file.h +++ b/src/xrServerEntities/script_ini_file.h @@ -38,6 +38,7 @@ class CScriptIniFile : public CInifile int r_s32(LPCSTR S, LPCSTR L); float r_float(LPCSTR S, LPCSTR L); Fvector r_fvector3(LPCSTR S, LPCSTR L); + Fvector4 r_fvector4(LPCSTR S, LPCSTR L); //AVO: additional methods to allow writing to ini files #ifdef INI_FILE_EXTENDED_EXPORTS void w_bool(LPCSTR S, LPCSTR L, bool V, LPCSTR comment /* = 0 */); @@ -59,6 +60,7 @@ class CScriptIniFile : public CInifile bool save_as(LPCSTR new_fname /* = 0 */); void save_at_end(bool b); void remove_line(LPCSTR S, LPCSTR L); + bool remove_section(LPCSTR S); void set_override_names(bool b); u32 section_count(); void set_readonly(bool b); diff --git a/src/xrServerEntities/script_ini_file_script.cpp b/src/xrServerEntities/script_ini_file_script.cpp index a904a1233a..679a7cab1d 100644 --- a/src/xrServerEntities/script_ini_file_script.cpp +++ b/src/xrServerEntities/script_ini_file_script.cpp @@ -37,11 +37,21 @@ void section_for_each(CScriptIniFile* self, ::luabind::functor functor) typedef CInifile::Root sections_type; sections_type& sections = self->sections(); + // the functor can remove sections so walk a copy of the section list and skip whatever it removed + xr_vector names; + names.reserve(sections.size()); + sections_type::const_iterator i = sections.begin(); sections_type::const_iterator e = sections.end(); for (; i != e; ++i) + names.push_back((*i).Name); + + for (const shared_str& name : names) { - if (functor((LPCSTR)(*i).Name.c_str()) == true) + if (!self->section_exist((LPCSTR)name.c_str())) + continue; + + if (functor((LPCSTR)name.c_str()) == true) return; } } @@ -180,6 +190,7 @@ void CScriptIniFile::script_register(lua_State* L) .def("save_as", &CScriptIniFile::save_as) .def("save_at_end", &CScriptIniFile::save_at_end) .def("remove_line", &CScriptIniFile::remove_line) + .def("remove_section", &CScriptIniFile::remove_section) .def("set_override_names", &CScriptIniFile::set_override_names) .def("section_count", &CScriptIniFile::section_count) .def("section_for_each", &::section_for_each) @@ -198,6 +209,7 @@ void CScriptIniFile::script_register(lua_State* L) .def("r_s32", &CScriptIniFile::r_s32) .def("r_float", &CScriptIniFile::r_float) .def("r_vector", &CScriptIniFile::r_fvector3) + .def("r_vector4", &CScriptIniFile::r_fvector4) .def("close", &CScriptIniFile::close) .def("r_line", &::r_line, out_value<4>() + out_value<5>()) From a1157f48a257a60b3a9a8de264a7fb72299678b0 Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:16 -0500 Subject: [PATCH 05/10] weapon: stop zoom, texture zoom query and the hud and weapon state enums --- src/xrGame/Weapon.cpp | 17 ++++++++++++++ src/xrGame/Weapon.h | 3 +++ src/xrGame/WeaponAK74.cpp | 27 ++++++++++++++++++++++- src/xrGame/script_game_object.h | 2 ++ src/xrGame/script_game_object4.cpp | 2 ++ src/xrGame/script_game_object_script3.cpp | 2 ++ 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/xrGame/Weapon.cpp b/src/xrGame/Weapon.cpp index 7c271b4d8c..79e88961ce 100644 --- a/src/xrGame/Weapon.cpp +++ b/src/xrGame/Weapon.cpp @@ -2142,6 +2142,23 @@ void CWeapon::OnZoomOut() } +bool CWeapon::StopZoom() +{ + if (!IsZoomed()) + return false; + + if (GetState() != eAimEnd && HudAnimationExist("anm_idle_aim_end")) + SwitchState(eAimEnd); + + OnZoomOut(); + return true; +} + +bool CWeapon::IsTextureZoomActive() +{ + return IsZoomed() && ZoomTexture() && !IsRotatingToZoom(); +} + CUIWindow* CWeapon::ZoomTexture() { if (UseScopeTexture()) diff --git a/src/xrGame/Weapon.h b/src/xrGame/Weapon.h index 418e6d5919..1894bb96e1 100644 --- a/src/xrGame/Weapon.h +++ b/src/xrGame/Weapon.h @@ -420,6 +420,9 @@ class CWeapon : public CHudItemObject, }; CUIWindow* ZoomTexture(); + bool StopZoom(); + bool IsTextureZoomActive(); + bool ZoomHideCrosshair(); IC float GetZoomFactor() const diff --git a/src/xrGame/WeaponAK74.cpp b/src/xrGame/WeaponAK74.cpp index 8f3c0e113b..c31b46d52b 100644 --- a/src/xrGame/WeaponAK74.cpp +++ b/src/xrGame/WeaponAK74.cpp @@ -19,7 +19,30 @@ void CWeaponAK74::script_register (lua_State *L) [ class_("CWeaponAK74") .def(constructor<>()), - + + class_>("hud_state") + .enum_("states") + [ + value("idle", int(CHUDState::eIdle)), + value("showing", int(CHUDState::eShowing)), + value("hiding", int(CHUDState::eHiding)), + value("hidden", int(CHUDState::eHidden)), + value("bore", int(CHUDState::eBore)) + ], + + class_>("weapon_state") + .enum_("states") + [ + value("fire", int(CWeapon::eFire)), + value("fire2", int(CWeapon::eFire2)), + value("reload", int(CWeapon::eReload)), + value("misfire", int(CWeapon::eMisfire)), + value("switch", int(CWeapon::eSwitch)), + value("switch_mode", int(CWeapon::eSwitchMode)), + value("aim_start", int(CWeapon::eAimStart)), + value("aim_end", int(CWeapon::eAimEnd)) + ], + class_("CWeapon") .def(constructor<>()) .def("can_kill", (bool (CWeapon::*)() const)&CWeapon::can_kill) @@ -38,6 +61,8 @@ void CWeaponAK74::script_register (lua_State *L) .def("IsZoomEnabled", &CWeapon::IsZoomEnabled) .def("IsZoomed", &CWeapon::IsZoomed) + .def("StopZoom", &CWeapon::StopZoom) + .def("IsTextureZoomActive", &CWeapon::IsTextureZoomActive) .def("GetZoomFactor", &CWeapon::GetZoomFactor) .def("SetZoomFactor", &CWeapon::SetZoomFactor) diff --git a/src/xrGame/script_game_object.h b/src/xrGame/script_game_object.h index 71cc24df42..a29c96e562 100644 --- a/src/xrGame/script_game_object.h +++ b/src/xrGame/script_game_object.h @@ -114,6 +114,7 @@ class CScriptGameObject; class CZoneCampfire; class CPhysicObject; class CArtefact; +class CWeaponBinoculars; class script_attachment; #ifdef STATIONARYMGUN_NEW @@ -986,6 +987,7 @@ class CScriptGameObject _DECLARE_FUNCTION14(cast_Ammo, CWeaponAmmo); _DECLARE_FUNCTION14(cast_Weapon, CWeapon); _DECLARE_FUNCTION14(cast_Knife, CWeaponKnife); + _DECLARE_FUNCTION14(cast_Binoculars, CWeaponBinoculars); _DECLARE_FUNCTION14(cast_WeaponMagazined, CWeaponMagazined); _DECLARE_FUNCTION14(cast_WeaponMagazinedWGrenade, CWeaponMagazinedWGrenade); _DECLARE_FUNCTION14(cast_EatableItem, CEatableItem); diff --git a/src/xrGame/script_game_object4.cpp b/src/xrGame/script_game_object4.cpp index 23ddd872ea..8a6f6be25c 100644 --- a/src/xrGame/script_game_object4.cpp +++ b/src/xrGame/script_game_object4.cpp @@ -51,6 +51,7 @@ #include "antirad.h" #include "BottleItem.h" #include "WeaponKnife.h" +#include "WeaponBinoculars.h" class CWeapon; @@ -534,6 +535,7 @@ SPECIFIC_CAST(CScriptGameObject::cast_Artefact, CArtefact); SPECIFIC_CAST(CScriptGameObject::cast_Ammo, CWeaponAmmo); SPECIFIC_CAST(CScriptGameObject::cast_Weapon, CWeapon); SPECIFIC_CAST(CScriptGameObject::cast_Knife, CWeaponKnife); +SPECIFIC_CAST(CScriptGameObject::cast_Binoculars, CWeaponBinoculars); SPECIFIC_CAST(CScriptGameObject::cast_WeaponMagazined, CWeaponMagazined); SPECIFIC_CAST(CScriptGameObject::cast_WeaponMagazinedWGrenade, CWeaponMagazinedWGrenade); SPECIFIC_CAST(CScriptGameObject::cast_Missile, CMissile); diff --git a/src/xrGame/script_game_object_script3.cpp b/src/xrGame/script_game_object_script3.cpp index 3e462a03b6..03672d0ab9 100644 --- a/src/xrGame/script_game_object_script3.cpp +++ b/src/xrGame/script_game_object_script3.cpp @@ -34,6 +34,7 @@ #include "artefact.h" #include "sight_manager_space.h" #include "script_attachment_manager.h" +#include "WeaponBinoculars.h" using namespace luabind; @@ -565,6 +566,7 @@ class_ script_register_game_object2(class_ .def("cast_Ammo", &CScriptGameObject::cast_Ammo) .def("cast_Weapon", &CScriptGameObject::cast_Weapon) .def("cast_Knife", &CScriptGameObject::cast_Knife) + .def("cast_Binoculars", &CScriptGameObject::cast_Binoculars) .def("cast_WeaponMagazined", &CScriptGameObject::cast_WeaponMagazined) .def("cast_WeaponMagazinedWGrenade", &CScriptGameObject::cast_WeaponMagazinedWGrenade) .def("cast_EatableItem", SAFE_WRAP(&CScriptGameObject::cast_EatableItem)) From 5451c896976eb612f4325b95612e891d7d3ed577 Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:17 -0500 Subject: [PATCH 06/10] script: inventory slot enum --- src/xrGame/script_game_object_script.cpp | 28 ++++++++++++++++++++++++ src/xrServerEntities/inventory_space.h | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/xrGame/script_game_object_script.cpp b/src/xrGame/script_game_object_script.cpp index 5856576f73..b75b42afda 100644 --- a/src/xrGame/script_game_object_script.cpp +++ b/src/xrGame/script_game_object_script.cpp @@ -11,6 +11,7 @@ #include "game_object_space.h" #include "script_ini_file.h" #include "sight_manager_space.h" +#include "inventory_space.h" using namespace luabind; @@ -53,6 +54,33 @@ void CScriptGameObject::script_register(lua_State* L) ) ), + class_>("inventory") + .enum_("slot") + [ + value("no_active", int(NO_ACTIVE_SLOT)), + value("knife", int(KNIFE_SLOT)), + value("slot_2", int(INV_SLOT_2)), + value("slot_3", int(INV_SLOT_3)), + value("grenade", int(GRENADE_SLOT)), + value("binocular", int(BINOCULAR_SLOT)), + value("bolt", int(BOLT_SLOT)), + value("outfit", int(OUTFIT_SLOT)), + value("pda", int(PDA_SLOT)), + value("detector", int(DETECTOR_SLOT)), + value("torch", int(TORCH_SLOT)), + value("artefact", int(ARTEFACT_SLOT)), + value("helmet", int(HELMET_SLOT)), + value("backpack", int(BACKPACK_SLOT)), +#ifdef MORE_INVENTORY_SLOTS + value("custom_1", int(CUSTOM_SLOT_1)), + value("custom_2", int(CUSTOM_SLOT_2)), + value("custom_3", int(CUSTOM_SLOT_3)), + value("custom_4", int(CUSTOM_SLOT_4)), + value("custom_5", int(CUSTOM_SLOT_5)), +#endif + value("last", int(LAST_SLOT)) + ], + class_>("callback") .enum_("callback_types") [ diff --git a/src/xrServerEntities/inventory_space.h b/src/xrServerEntities/inventory_space.h index cc58a2cd1c..b39bbca213 100644 --- a/src/xrServerEntities/inventory_space.h +++ b/src/xrServerEntities/inventory_space.h @@ -3,7 +3,7 @@ #define CMD_START (1<<0) #define CMD_STOP (1<<1) -enum +enum EInventorySlots { NO_ACTIVE_SLOT = 0, KNIFE_SLOT = 1, From 1db4dbc0efe20189a91acebd80512eed89ed7d54 Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:18 -0500 Subject: [PATCH 07/10] script: actor zone influence readings --- src/xrGame/ActorCondition.cpp | 6 +++++ src/xrGame/ActorCondition.h | 1 + src/xrGame/ActorCondition_script.cpp | 33 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/xrGame/ActorCondition.cpp b/src/xrGame/ActorCondition.cpp index 9a9c3d2827..4a5d295f9b 100644 --- a/src/xrGame/ActorCondition.cpp +++ b/src/xrGame/ActorCondition.cpp @@ -461,6 +461,12 @@ void CActorCondition::SetZoneDanger(float danger, ALife::EInfluenceType type) clamp(m_zone_danger[type], 0.0f, 1.0f); } +float CActorCondition::GetZoneDanger(ALife::EInfluenceType type) const +{ + VERIFY(type != ALife::infl_max_count); + return m_zone_danger[type]; +} + float CActorCondition::GetZoneDanger() const { float sum = 0.0f; diff --git a/src/xrGame/ActorCondition.h b/src/xrGame/ActorCondition.h index bd068cc581..81c006e986 100644 --- a/src/xrGame/ActorCondition.h +++ b/src/xrGame/ActorCondition.h @@ -105,6 +105,7 @@ class CActorCondition : public CEntityCondition void SetZoneDanger(float danger, ALife::EInfluenceType type); float GetZoneDanger() const; + float GetZoneDanger(ALife::EInfluenceType type) const; public: IC CActor& object() const diff --git a/src/xrGame/ActorCondition_script.cpp b/src/xrGame/ActorCondition_script.cpp index d3fa5bbf50..68cfc2bba2 100644 --- a/src/xrGame/ActorCondition_script.cpp +++ b/src/xrGame/ActorCondition_script.cpp @@ -46,6 +46,28 @@ void WoundForEach(CActorCondition* conditions, const ::luabind::functor &f } } +float GetZoneMaxPower_script(CActorCondition* conditions, u32 infl_type) +{ + if (infl_type >= ALife::infl_max_count) + { + Msg("!GetZoneMaxPower: bad influence type %u", infl_type); + return 0.f; + } + + return conditions->GetZoneMaxPower((ALife::EInfluenceType)infl_type); +} + +float GetZoneDanger_script(CActorCondition* conditions, u32 infl_type) +{ + if (infl_type >= ALife::infl_max_count) + { + Msg("!GetZoneDanger: bad influence type %u", infl_type); + return 0.f; + } + + return conditions->GetZoneDanger((ALife::EInfluenceType)infl_type); +} + #pragma optimize("s",on) void CActorCondition::script_register(lua_State *L) { @@ -147,6 +169,9 @@ void CActorCondition::script_register(lua_State *L) .def("BoostRadiationProtection", &CActorCondition::BoostRadiationProtection) .def("BoostTelepaticProtection", &CActorCondition::BoostTelepaticProtection) .def("BoostChemicalBurnProtection", &CActorCondition::BoostChemicalBurnProtection) + .def("GetInjuriousMaterialDamage", &CActorCondition::GetInjuriousMaterialDamage) + .def("GetZoneMaxPower", &GetZoneMaxPower_script) + .def("GetZoneDanger", &GetZoneDanger_script) .def("IsLimping", &CActorCondition::IsLimping) .def("IsCantWalk", &CActorCondition::IsCantWalk) .def("IsCantWalkWeight", &CActorCondition::IsCantWalkWeight) @@ -161,6 +186,14 @@ void CActorCondition::script_register(lua_State *L) .def_readwrite("m_fAccelK", &CActorCondition::m_fAccelK) .def_readwrite("m_fSprintK", &CActorCondition::m_fSprintK) .def_readwrite("m_condition_flags", &CActorCondition::m_condition_flags) + .enum_("influence_type") + [ + value("infl_rad", int(ALife::infl_rad)), + value("infl_fire", int(ALife::infl_fire)), + value("infl_acid", int(ALife::infl_acid)), + value("infl_psi", int(ALife::infl_psi)), + value("infl_electra", int(ALife::infl_electra)) + ] .enum_("condition_flags") [ value("eCriticalPowerReached", int(CActorCondition::eCriticalPowerReached)), From aa694f7b66abf9e08a60d89a7f238c1e1cbceb96 Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:18 -0500 Subject: [PATCH 08/10] alife: server item condition and visual name --- .../xrServer_Objects_ALife_Items_script.cpp | 18 ++++++++++++++++++ .../xrServer_Objects_script.cpp | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/xrServerEntities/xrServer_Objects_ALife_Items_script.cpp b/src/xrServerEntities/xrServer_Objects_ALife_Items_script.cpp index c00abe8412..14dfac288e 100644 --- a/src/xrServerEntities/xrServer_Objects_ALife_Items_script.cpp +++ b/src/xrServerEntities/xrServer_Objects_ALife_Items_script.cpp @@ -20,6 +20,23 @@ bool has_upgrade_script(CSE_ALifeInventoryItem* ta, LPCSTR str) return ta->has_upgrade(str); } +float get_condition_script(CSE_ALifeInventoryItem* item) +{ + return item->m_fCondition; +} + +// the net packet quantises condition over zero to one, anything else corrupts the stored value +void set_condition_script(CSE_ALifeInventoryItem* item, float value) +{ + if (value < 0.f || value > 1.f) + { + Msg("!cse_alife_inventory_item.condition set to %f, must be between 0 and 1", value); + return; + } + + item->m_fCondition = value; +} + using namespace luabind; #pragma optimize("s",on) @@ -31,6 +48,7 @@ void CSE_ALifeInventoryItem::script_register(lua_State* L) // .def( constructor()) .def("has_upgrade", &has_upgrade) .def("add_upgrade", &add_upgrade) + .property("condition", &get_condition_script, &set_condition_script) ]; } diff --git a/src/xrServerEntities/xrServer_Objects_script.cpp b/src/xrServerEntities/xrServer_Objects_script.cpp index 7cf34f7959..9f1a8056b1 100644 --- a/src/xrServerEntities/xrServer_Objects_script.cpp +++ b/src/xrServerEntities/xrServer_Objects_script.cpp @@ -29,6 +29,23 @@ CScriptIniFile* get_spawn_ini(CSE_Abstract* abstract) return ((CScriptIniFile*)&abstract->spawn_ini()); } +LPCSTR get_visual_name(CSE_Visual* visual) +{ + LPCSTR name = visual->get_visual(); + return name ? name : ""; +} + +void set_visual_name(CSE_Visual* visual, LPCSTR name) +{ + if (!name || !name[0]) + { + Msg("!set_visual_name: empty visual name"); + return; + } + + visual->set_visual(name); +} + template struct CWrapperBase : public T, public ::luabind::wrap_base { @@ -141,6 +158,8 @@ void CSE_Visual::script_register(lua_State* L) ("cse_visual") // .def( constructor<>()) // .def( constructor()) + .def("visual_name", &get_visual_name) + .def("set_visual_name", &set_visual_name) ]; } From 6bbd8a28fbb62b703517ebcf526db34c6dfa3081 Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:22:23 -0500 Subject: [PATCH 09/10] luabind: too few arguments gives a lua error instead of reading past the overload table --- src/3rd party/luabind/luabind/src/class_rep.cpp | 13 +++++++++++++ src/3rd party/luabind/luabind/src/function.cpp | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/3rd party/luabind/luabind/src/class_rep.cpp b/src/3rd party/luabind/luabind/src/class_rep.cpp index 1a5eda991e..91dc3e8094 100644 --- a/src/3rd party/luabind/luabind/src/class_rep.cpp +++ b/src/3rd party/luabind/luabind/src/class_rep.cpp @@ -698,6 +698,19 @@ int luabind::detail::class_rep::function_dispatcher(lua_State* L) #endif + // a call that matched no overload raises an error instead of indexing before the overload table + if (match_index < 0) + { + { + string_class msg = "no matching overload for '"; + msg += rep->name; + msg += "'"; + lua_pushstring(L, msg.c_str()); + } + + lua_error(L); + } + #ifndef LUABIND_NO_EXCEPTIONS try diff --git a/src/3rd party/luabind/luabind/src/function.cpp b/src/3rd party/luabind/luabind/src/function.cpp index 4d2df1cf5f..d16ebb14f9 100644 --- a/src/3rd party/luabind/luabind/src/function.cpp +++ b/src/3rd party/luabind/luabind/src/function.cpp @@ -136,6 +136,19 @@ namespace luabind { namespace detail { namespace free_functions { lua_error(L); } #endif + // a call that matched no overload raises an error instead of indexing before the overload table + if (match_index < 0) + { + { + string_class msg = "no matching overload for '"; + msg += rep->name(); + msg += "'"; + lua_pushstring(L, msg.c_str()); + } + + lua_error(L); + } + overload_rep const& ov_rep = rep->overloads()[match_index]; #ifndef LUABIND_NO_EXCEPTIONS From 1f705c12463c9842c6348c6fa4a0d166cd884e0f Mon Sep 17 00:00:00 2001 From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com> Date: Tue, 15 Sep 2026 21:32:09 -0500 Subject: [PATCH 10/10] script: document the new exports --- gamedata/scripts/lua_help_ex.script | 79 ++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/gamedata/scripts/lua_help_ex.script b/gamedata/scripts/lua_help_ex.script index eba40586e0..3756a20b60 100644 --- a/gamedata/scripts/lua_help_ex.script +++ b/gamedata/scripts/lua_help_ex.script @@ -188,7 +188,8 @@ game { Fvector2 world2ui(Fvector pos, bool hud = false, bool allow_offscreen = false) - Fvector2 world2ui_with_depth(Fvector pos, bool hud = false, bool allow_offscreen = false) + Fvector world2ui_with_depth(Fvector pos, bool hud = false, bool allow_offscreen = false) // x and y are screen coordinates, z is the post projection w, negative behind the camera and its magnitude is the view distance in metres + table world2ui_many(table positions, bool hud, bool allow_offscreen) // projects an array of Fvector in one call, result[i] is the world2ui_with_depth vector for positions[i], stops at the first missing or non vector entry, empty table if the argument is not a table function ui2world(Fvector2 pos) -> Fvector result, u16 object_id function ui2world_offscreen(Fvector2 pos) -> Fvector result, u16 object_id change_game_news_show_time(CUIWindow* UIWindow, float show_time) @@ -199,6 +200,8 @@ ini_file() { string get_filename() + Fvector4 r_vector4(string sec, string line) // reads four floats from one line, zero vector and a log line when the section or line is missing + bool remove_section(string sec) // drops a whole section with its lines, false if the name is empty, the file is read only or the section is not there void dltx_print(string sec = nil, string line = nil) string dltx_get_filename_of_line(string sec, string line) table dltx_get_section(string sec) @@ -221,6 +224,15 @@ function clone_upgrades(cse_alife_item_weapon*) } + class cse_visual { + string visual_name() // current visual path, empty string when the entity has none + void set_visual_name(string name) // sets the visual path, ignores an empty name and logs it + } + + class cse_alife_inventory_item { + property condition // float 0 to 1, a write outside that range is dropped and logged because the net packet only carries that range + } + class cse_alife_level_changer : cse_alife_space_restrictor { property dest_game_vertex_id // u16 property dest_level_vertex_id // u32 @@ -557,6 +569,8 @@ float get_pp_effector_length(int id) bool check_pp_effector(int id) + bool is_input_captured() // true while input is disabled or a UI dialog holds the top input receiver, so game keys will not reach scripts + // MT only function scheduler_flush(bool) } @@ -713,9 +727,34 @@ function place_skeleton (game_object obj, string section, Fvector start, Fvector dir, float size, float ttl) } + // slot ids, the same numbers move_to_slot and active_slot use, read as inventory.knife + enum inventory.slot { + no_active = 0, + knife = 1, + slot_2 = 2, + slot_3 = 3, + grenade = 4, + binocular = 5, + bolt = 6, + outfit = 7, + pda = 8, + detector = 9, + torch = 10, + artefact = 11, + helmet = 12, + backpack = 13, + custom_1, // custom_1 to custom_5 only exist in a build with the extra inventory slots + custom_2, + custom_3, + custom_4, + custom_5, + last, // highest valid slot id in this build, backpack without the extra slots + } + class game_object { // Class casting function cast_Knife() + function cast_Binoculars() // CWeaponBinoculars, nil when the object is not a pair of binoculars function get_projector() function get_stmgun() function cast_Missile() @@ -931,6 +970,19 @@ same as set_shader but resets to the default shader/texture values, so you don't need to store them in a table to reset them later } + class CActorCondition : CEntityCondition { + enum influence_type + infl_rad + infl_fire + infl_acid + infl_psi + infl_electra + + function GetInjuriousMaterialDamage() // damage per second of the injurious material the actor stands on, 0 on a normal material + function GetZoneMaxPower(influence_type) // the actor's max power for that influence, the value zone danger is measured against, 0 and a log line on a bad type + function GetZoneDanger(influence_type) // current danger for that influence, 0 to 1, 0 and a log line on a bad type + } + class CArtefact : CGameObject { property m_additional_weight } @@ -963,6 +1015,27 @@ functiom SetParam(number id, Fvector val, string sec) } + // hud states shared by every hud item, read as hud_state.idle, compare against item:get_state() + enum hud_state.states { + idle, + showing, + hiding, + hidden, + bore, // the idle fidget animation + } + + // weapon only states, read as weapon_state.fire, they continue the hud_state numbering + enum weapon_state.states { + fire, + fire2, // second fire mode, grenade launcher or underbarrel + reload, + misfire, + switch, // switching ammo type + switch_mode, // switching fire mode + aim_start, // rotating into ADS + aim_end, // rotating out of ADS + } + class CWeapon : CGameObject { // Return RPM in actual RPM value like in configs function RealRPM() @@ -1057,6 +1130,10 @@ + // Zoom + bool StopZoom() // leaves ADS, plays the aim end animation when the weapon has one, false if the weapon was not zoomed + bool IsTextureZoomActive() // true only while the scope texture UI is actually drawn, so zoomed in with a scope texture and no longer rotating into ADS + //Scope UI //Returns table containing this data {