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) find_package(TBB REQUIRED)
endif() endif()
if(ENABLE_LUA) find_package(luajit)
find_package(luajit)
if(TARGET luajit::luajit) if(TARGET luajit::luajit)
message(STATUS "Using LuaJIT provided by system") message(STATUS "Using LuaJIT provided by system")
else() else()
message(STATUS "Cannot find LuaJIT! Fallback to using usual Lua.") message(STATUS "Cannot find LuaJIT! Fallback to using usual Lua.")
find_package(Lua REQUIRED) find_package(Lua REQUIRED)
if(Lua_FOUND) if(Lua_FOUND)
add_library(luajit::luajit UNKNOWN IMPORTED) add_library(luajit::luajit UNKNOWN IMPORTED)
set_target_properties(luajit::luajit PROPERTIES set_target_properties(luajit::luajit PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}") INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}")
set_target_properties(luajit::luajit PROPERTIES set_target_properties(luajit::luajit PROPERTIES
IMPORTED_LOCATION "${LUA_LIBRARIES}") IMPORTED_LOCATION "${LUA_LIBRARIES}")
endif()
endif() endif()
endif() endif()

View File

@ -751,7 +751,7 @@ endif()
set_target_properties(vcmi PROPERTIES COMPILE_DEFINITIONS "VCMI_DLL=1") set_target_properties(vcmi PROPERTIES COMPILE_DEFINITIONS "VCMI_DLL=1")
target_link_libraries(vcmi PUBLIC 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 ${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 "../IGameSettings.h"
#include "../VCMI_Lib.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 VCMI_LIB_NAMESPACE_BEGIN
@ -208,18 +312,12 @@ int DamageCalculator::getTargetDefenseIgnored() const
double DamageCalculator::getAttackSkillFactor() const double DamageCalculator::getAttackSkillFactor() const
{ {
int attackAdvantage = getActorAttackEffective() - getTargetDefenseEffective(); static LuaExpressionEvaluator evaluator("return clamp((attack - defense) * 0.05, 0, 4)");
if(attackAdvantage > 0) evaluator.setVariable("defense", getTargetDefenseEffective());
{ evaluator.setVariable("attack", getActorAttackEffective());
// FIXME: use cb to acquire these settings double result = evaluator.evaluate();
const double attackMultiplier = VLC->engineSettings()->getDouble(EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR); return result;
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;
} }
double DamageCalculator::getAttackBlessFactor() const double DamageCalculator::getAttackBlessFactor() const
@ -308,19 +406,12 @@ double DamageCalculator::getAttackRevengeFactor() const
double DamageCalculator::getDefenseSkillFactor() 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 evaluator.setVariable("defense", getTargetDefenseEffective());
if(defenseAdvantage > 0) //decreasing dmg evaluator.setVariable("attack", getActorAttackEffective());
{ double result = evaluator.evaluate();
// FIXME: use cb to acquire these settings return result;
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;
} }
double DamageCalculator::getDefenseArmorerFactor() const double DamageCalculator::getDefenseArmorerFactor() const