1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Use lua for damage calculation formula evaluation

This commit is contained in:
Ivan Savenko 2024-12-16 20:06:34 +00:00
parent 543ce94132
commit fdfb7fca12
3 changed files with 127 additions and 38 deletions

View File

@ -518,21 +518,19 @@ if(NOT ENABLE_MINIMAL_LIB)
find_package(TBB REQUIRED)
endif()
if(ENABLE_LUA)
find_package(luajit)
find_package(luajit)
if(TARGET luajit::luajit)
message(STATUS "Using LuaJIT provided by system")
else()
message(STATUS "Cannot find LuaJIT! Fallback to using usual Lua.")
find_package(Lua REQUIRED)
if(Lua_FOUND)
add_library(luajit::luajit UNKNOWN IMPORTED)
set_target_properties(luajit::luajit PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}")
set_target_properties(luajit::luajit PROPERTIES
IMPORTED_LOCATION "${LUA_LIBRARIES}")
endif()
if(TARGET luajit::luajit)
message(STATUS "Using LuaJIT provided by system")
else()
message(STATUS "Cannot find LuaJIT! Fallback to using usual Lua.")
find_package(Lua REQUIRED)
if(Lua_FOUND)
add_library(luajit::luajit UNKNOWN IMPORTED)
set_target_properties(luajit::luajit PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}")
set_target_properties(luajit::luajit PROPERTIES
IMPORTED_LOCATION "${LUA_LIBRARIES}")
endif()
endif()

View File

@ -751,7 +751,7 @@ endif()
set_target_properties(vcmi PROPERTIES COMPILE_DEFINITIONS "VCMI_DLL=1")
target_link_libraries(vcmi PUBLIC
minizip::minizip ZLIB::ZLIB TBB::tbb
minizip::minizip ZLIB::ZLIB TBB::tbb luajit::luajit
${SYSTEM_LIBS} Boost::boost Boost::thread Boost::filesystem Boost::program_options Boost::locale Boost::date_time
)

View File

@ -20,6 +20,110 @@
#include "../IGameSettings.h"
#include "../VCMI_Lib.h"
#include <lua.hpp>
class LuaExpressionEvaluator : boost::noncopyable
{
lua_State * luaState = nullptr;
const std::string expression;
int compiledReference = -1;
void compile();
void registerLibrary();
public:
LuaExpressionEvaluator(const std::string & expression);
~LuaExpressionEvaluator();
void setVariable(const std::string & name, double value);
double evaluate();
};
void LuaExpressionEvaluator::registerLibrary()
{
const auto & luaMax = [](lua_State * state)
{
lua_Number a = luaL_checknumber(state, 1);
lua_Number b = luaL_checknumber(state, 2);
lua_pushnumber(state, std::max(a,b));
return 1;
};
const auto & luaMin = [](lua_State * state)
{
lua_Number a = luaL_checknumber(state, 1);
lua_Number b = luaL_checknumber(state, 2);
lua_pushnumber(state, std::min(a,b));
return 1;
};
const auto & luaClamp = [](lua_State * state)
{
lua_Number a = luaL_checknumber(state, 1);
lua_Number b = luaL_checknumber(state, 2);
lua_Number c = luaL_checknumber(state, 3);
lua_pushnumber(state, std::clamp(a,b,c));
return 1;
};
const luaL_Reg registry[] = {
{ "max", luaMax },
{ "min", luaMin },
{ "clamp", luaClamp },
{ nullptr, nullptr }
};
lua_pushvalue(luaState, LUA_GLOBALSINDEX);
luaL_setfuncs(luaState,registry,0);
}
LuaExpressionEvaluator::LuaExpressionEvaluator(const std::string & expression)
: expression(expression)
{
luaState = luaL_newstate();
registerLibrary();
compile();
}
LuaExpressionEvaluator::~LuaExpressionEvaluator()
{
lua_close(luaState);
}
void LuaExpressionEvaluator::setVariable(const std::string & name, double value)
{
lua_pushnumber(luaState, value);
lua_setglobal(luaState, name.c_str());
}
void LuaExpressionEvaluator::compile()
{
int result = luaL_loadstring(luaState, expression.c_str());
if (result)
{
logGlobal->error("Lua compilation failure: %s", lua_tostring(luaState,-1));
lua_pop(luaState,1);
}
assert(result == LUA_OK);
compiledReference = luaL_ref(luaState, LUA_REGISTRYINDEX);
}
double LuaExpressionEvaluator::evaluate()
{
lua_rawgeti(luaState, LUA_REGISTRYINDEX, compiledReference);
int errorCode = lua_pcall(luaState,0,1,0);
if (errorCode)
{
logGlobal->error("Lua evaluation failure: %s", lua_tostring(luaState,-1));
lua_pop(luaState,1);
return 0;
}
double result = lua_tonumber(luaState,-1);
lua_pop(luaState,1);
return result;
}
VCMI_LIB_NAMESPACE_BEGIN
@ -208,18 +312,12 @@ int DamageCalculator::getTargetDefenseIgnored() const
double DamageCalculator::getAttackSkillFactor() const
{
int attackAdvantage = getActorAttackEffective() - getTargetDefenseEffective();
static LuaExpressionEvaluator evaluator("return clamp((attack - defense) * 0.05, 0, 4)");
if(attackAdvantage > 0)
{
// FIXME: use cb to acquire these settings
const double attackMultiplier = VLC->engineSettings()->getDouble(EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR);
const double attackMultiplierCap = VLC->engineSettings()->getDouble(EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP);
const double attackFactor = std::min(attackMultiplier * attackAdvantage, attackMultiplierCap);
return attackFactor;
}
return 0.f;
evaluator.setVariable("defense", getTargetDefenseEffective());
evaluator.setVariable("attack", getActorAttackEffective());
double result = evaluator.evaluate();
return result;
}
double DamageCalculator::getAttackBlessFactor() const
@ -308,19 +406,12 @@ double DamageCalculator::getAttackRevengeFactor() const
double DamageCalculator::getDefenseSkillFactor() const
{
int defenseAdvantage = getTargetDefenseEffective() - getActorAttackEffective();
static LuaExpressionEvaluator evaluator("return clamp((defense - attack) * 0.025, 0, 0.7)");
//bonus from attack/defense skills
if(defenseAdvantage > 0) //decreasing dmg
{
// FIXME: use cb to acquire these settings
const double defenseMultiplier = VLC->engineSettings()->getDouble(EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR);
const double defenseMultiplierCap = VLC->engineSettings()->getDouble(EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP);
const double dec = std::min(defenseMultiplier * defenseAdvantage, defenseMultiplierCap);
return dec;
}
return 0.0;
evaluator.setVariable("defense", getTargetDefenseEffective());
evaluator.setVariable("attack", getActorAttackEffective());
double result = evaluator.evaluate();
return result;
}
double DamageCalculator::getDefenseArmorerFactor() const