mirror of
https://github.com/vcmi/vcmi.git
synced 2026-05-22 09:55:17 +02:00
All but 4 summon effect test now pass
- Added logging support for Lua - Added exporting of enums to Lua - Added few more missing pieces to support summon spell effect
This commit is contained in:
@@ -30,7 +30,6 @@ public:
|
||||
virtual ~Context() = default;
|
||||
|
||||
virtual void run(const JsonNode & initialState) = 0;
|
||||
virtual void run(ServerCallback * server, const JsonNode & initialState) = 0;
|
||||
|
||||
virtual JsonNode callGlobal(const std::string & name, const JsonNode & parameters) = 0;
|
||||
|
||||
|
||||
@@ -198,5 +198,6 @@ extern DLL_LINKAGE vstd::CLoggerBase * logAi;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logAnim;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logMod;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logRng;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logScript;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "BattleSide.h"
|
||||
#include <vcmi/scripting/ApiTags.h>
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@@ -30,7 +31,7 @@ class BattleHexArray;
|
||||
* Valid hexes are within the range 0 to 186, excluding some invalid values, ex. castle towers (-2, -3, -4).
|
||||
* Available hexes are those valid ones but NOT in the first or last column.
|
||||
*/
|
||||
class DLL_LINKAGE BattleHex
|
||||
class DLL_LINKAGE BattleHex : public scripting::ApiCopyable<BattleHex>
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
+2
-2
@@ -286,14 +286,14 @@ void UnitInfo::save(JsonNode & data)
|
||||
{
|
||||
data.clear();
|
||||
JsonSerializer ser(nullptr, data);
|
||||
ser.serializeStruct("newUnitInfo", *this);
|
||||
serializeJson(ser);
|
||||
}
|
||||
|
||||
void UnitInfo::load(uint32_t id_, const JsonNode & data)
|
||||
{
|
||||
id = id_;
|
||||
JsonDeserializer deser(nullptr, data);
|
||||
deser.serializeStruct("newUnitInfo", *this);
|
||||
serializeJson(deser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ DLL_LINKAGE vstd::CLoggerBase * logAi = CLogger::getLogger(CLoggerDomain("ai"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logAnim = CLogger::getLogger(CLoggerDomain("animation"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logMod = CLogger::getLogger(CLoggerDomain("mod"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logRng = CLogger::getLogger(CLoggerDomain("rng"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logScript = CLogger::getLogger(CLoggerDomain("script"));
|
||||
|
||||
CLogger * CLogger::getLogger(const CLoggerDomain & domain)
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ set(lib_SRCS
|
||||
api/BattleCb.cpp
|
||||
api/BonusSystem.cpp
|
||||
api/Creature.cpp
|
||||
api/Enums.cpp
|
||||
api/Faction.cpp
|
||||
api/GameCb.cpp
|
||||
api/HeroClass.cpp
|
||||
@@ -89,6 +90,7 @@ set(lib_HDRS
|
||||
api/BattleCb.h
|
||||
api/BonusSystem.h
|
||||
api/Creature.h
|
||||
api/Enums.h
|
||||
api/Faction.h
|
||||
api/GameCb.h
|
||||
api/HeroClass.h
|
||||
|
||||
+38
-28
@@ -13,6 +13,8 @@
|
||||
#include "LuaStack.h"
|
||||
#include "LuaReference.h"
|
||||
|
||||
#include "api/Enums.h"
|
||||
|
||||
#include "../lib/callback/IGameInfoCallback.h"
|
||||
#include "../lib/json/JsonNode.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
@@ -24,6 +26,31 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
/// Custom text printing function for use in scripting
|
||||
/// based on luaB_print (part of Lua source code)
|
||||
/// adapted to C++ & VCMI logging facilities
|
||||
static int luaPrint(lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
lua_getglobal(L, "tostring");
|
||||
std::string out;
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushvalue(L, i);
|
||||
lua_call(L, 1, 1);
|
||||
const char *s = lua_tostring(L, -1);
|
||||
if (s)
|
||||
out += s;
|
||||
if (i > 1)
|
||||
out += '\t';
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
logScript->info("%s", out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
|
||||
@@ -34,14 +61,13 @@ LuaContext::LuaContext(const Script * source, const Environment * env_):
|
||||
script(source),
|
||||
env(env_)
|
||||
{
|
||||
static const std::vector<luaL_Reg> STD_LIBS =
|
||||
{
|
||||
static constexpr std::array<luaL_Reg, 4> STD_LIBS =
|
||||
{{
|
||||
{"", luaopen_base},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_BITLIBNAME, luaopen_bit}
|
||||
};
|
||||
{LUA_MATHLIBNAME, luaopen_math}
|
||||
}};
|
||||
|
||||
for(const luaL_Reg & lib : STD_LIBS)
|
||||
{
|
||||
@@ -65,6 +91,7 @@ LuaContext::LuaContext(const Script * source, const Environment * env_):
|
||||
popAll();
|
||||
|
||||
LuaStack S(L);
|
||||
api::Enums enums;
|
||||
|
||||
S.push(env->game());
|
||||
lua_setglobal(L, "GAME");
|
||||
@@ -72,6 +99,9 @@ LuaContext::LuaContext(const Script * source, const Environment * env_):
|
||||
S.push(env->services());
|
||||
lua_setglobal(L, "LIBRARY");
|
||||
|
||||
S.push(enums);
|
||||
lua_setglobal(L, "ENUM");
|
||||
|
||||
popAll();
|
||||
}
|
||||
|
||||
@@ -101,7 +131,7 @@ void LuaContext::cleanupGlobals()
|
||||
S.pushNil();
|
||||
lua_setglobal(L, "loadstring");
|
||||
|
||||
S.pushNil();
|
||||
lua_pushcfunction(L, luaPrint);
|
||||
lua_setglobal(L, "print");
|
||||
|
||||
S.clear();
|
||||
@@ -119,32 +149,12 @@ void LuaContext::cleanupGlobals()
|
||||
S.pushNil();
|
||||
lua_rawset(L, -3);
|
||||
|
||||
|
||||
S.push("randomseed");
|
||||
S.pushNil();
|
||||
lua_rawset(L, -3);
|
||||
S.clear();
|
||||
}
|
||||
|
||||
void LuaContext::run(ServerCallback * server, const JsonNode & initialState)
|
||||
{
|
||||
{
|
||||
LuaStack S(L);
|
||||
S.push(server);
|
||||
lua_setglobal(L, "SERVER");
|
||||
S.clear();
|
||||
}
|
||||
|
||||
run(initialState);
|
||||
|
||||
// {
|
||||
// LuaStack S(L);
|
||||
// S.pushNil();
|
||||
// lua_setglobal(L, "SERVER");
|
||||
// S.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
void LuaContext::run(const JsonNode & initialState)
|
||||
{
|
||||
setGlobal(STATE_FIELD, initialState);
|
||||
@@ -153,7 +163,7 @@ void LuaContext::run(const JsonNode & initialState)
|
||||
|
||||
if(ret)
|
||||
{
|
||||
logGlobal->error("Script '%s' failed to load, error: %s", script->getJsonKey(), toStringRaw(-1));
|
||||
logScript->error("Script '%s' failed to load, error: %s", script->getJsonKey(), toStringRaw(-1));
|
||||
popAll();
|
||||
return;
|
||||
}
|
||||
@@ -166,7 +176,7 @@ void LuaContext::run(const JsonNode & initialState)
|
||||
|
||||
if(ret)
|
||||
{
|
||||
logGlobal->error("Script '%s' failed to run, error: '%s'", script->getJsonKey(), toStringRaw(-1));
|
||||
logScript->error("Script '%s' failed to run, error: '%s'", script->getJsonKey(), toStringRaw(-1));
|
||||
popAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public:
|
||||
virtual ~LuaContext();
|
||||
|
||||
void run(const JsonNode & initialState) override;
|
||||
void run(ServerCallback * server, const JsonNode & initialState) override;
|
||||
|
||||
//log error and return nil from LuaCFunction
|
||||
int errorRetVoid(const std::string & message);
|
||||
@@ -111,9 +110,9 @@ ReturnType LuaContext::callGlobalWithParameters(const std::string & name, Args&&
|
||||
|
||||
if(lua_pcall(L, argc, 1, 0))
|
||||
{
|
||||
std::string error = lua_tostring(L, -1);
|
||||
S.clear();
|
||||
|
||||
std::string error = lua_tostring(L, -1);
|
||||
boost::format fmt("Lua function %s failed with message: %s");
|
||||
fmt % name % error;
|
||||
logGlobal->error(fmt.str());
|
||||
@@ -123,7 +122,7 @@ ReturnType LuaContext::callGlobalWithParameters(const std::string & name, Args&&
|
||||
if constexpr (!std::is_void_v<ReturnType>)
|
||||
{
|
||||
ReturnType ret;
|
||||
S.getOrThrow(-1, ret);
|
||||
S.getOrThrow(S.absindex(-1), ret);
|
||||
S.balance();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -80,20 +80,16 @@ bool LuaSpellEffect::applicable(Problem & problem, const Mechanics * m, const Ef
|
||||
if(target.empty())
|
||||
return false;
|
||||
|
||||
JsonNode targetJson = spellTargetToJson(target);
|
||||
bool result = context->callGlobalWithParameters<bool>(APPLICABLE_TARGET, parameters, m, targetJson);
|
||||
bool result = context->callGlobalWithParameters<bool>(APPLICABLE_TARGET, parameters, m, target);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void LuaSpellEffect::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
|
||||
{
|
||||
if(target.empty())
|
||||
return;
|
||||
|
||||
std::shared_ptr<scripting::LuaContext> context = resolveScript(m);
|
||||
|
||||
context->callGlobalWithParameters<void>(APPLY, parameters, m, server, spellTargetToJson(target));
|
||||
context->callGlobalWithParameters<void>(APPLY, parameters, m, server, target);
|
||||
}
|
||||
|
||||
EffectTarget LuaSpellEffect::filterTarget(const Mechanics * m, const EffectTarget & target) const
|
||||
@@ -105,11 +101,9 @@ EffectTarget LuaSpellEffect::transformTarget(const Mechanics * m, const Target &
|
||||
{
|
||||
std::shared_ptr<scripting::LuaContext> context = resolveScript(m);
|
||||
|
||||
JsonNode aimPointJson = spellTargetToJson(aimPoint);
|
||||
JsonNode spellTargetJson = spellTargetToJson(spellTarget);
|
||||
JsonNode response = context->callGlobalWithParameters<JsonNode>(TRANSFORM_TARGET, parameters, m, aimPointJson, spellTargetJson);
|
||||
Target response = context->callGlobalWithParameters<Target>(TRANSFORM_TARGET, parameters, m, aimPoint, spellTarget);
|
||||
|
||||
return spellTargetFromJson(m, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
void LuaSpellEffect::serializeJsonEffect(JsonSerializeFormat & handler)
|
||||
@@ -129,45 +123,6 @@ std::shared_ptr<scripting::LuaContext> LuaSpellEffect::resolveScript(const Mecha
|
||||
return luaContext;
|
||||
}
|
||||
|
||||
JsonNode LuaSpellEffect::spellTargetToJson(const Target & target) const
|
||||
{
|
||||
JsonNode requestP;
|
||||
|
||||
for(const auto & dest : target)
|
||||
{
|
||||
JsonNode targetData;
|
||||
targetData.Vector().emplace_back(dest.hexValue.toInt());
|
||||
|
||||
if(dest.unitValue)
|
||||
targetData.Vector().emplace_back(dest.unitValue->unitId());
|
||||
else
|
||||
targetData.Vector().emplace_back(-1);
|
||||
|
||||
requestP.Vector().push_back(targetData);
|
||||
}
|
||||
|
||||
return requestP;
|
||||
}
|
||||
|
||||
Target LuaSpellEffect::spellTargetFromJson(const Mechanics * m, const JsonNode & config) const
|
||||
{
|
||||
Target result;
|
||||
|
||||
for (const auto & entry : config.Vector())
|
||||
{
|
||||
Destination dest;
|
||||
|
||||
if (!entry[1].isNull() && entry[1].Integer() != -1 )
|
||||
dest = Destination(m->battle()->battleGetUnitByID(entry[1].Integer()), BattleHex(entry[0].Integer()));
|
||||
else
|
||||
dest = Destination(BattleHex(entry[0].Integer()));
|
||||
|
||||
result.push_back(dest);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,6 @@ private:
|
||||
JsonNode parameters;
|
||||
|
||||
std::shared_ptr<LuaContext> resolveScript(const Mechanics * m) const;
|
||||
|
||||
JsonNode spellTargetToJson(const Target & spellTarget) const;
|
||||
Target spellTargetFromJson(const Mechanics * m, const JsonNode & config) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,14 @@ void LuaStack::clear()
|
||||
lua_settop(L, 0);
|
||||
}
|
||||
|
||||
int LuaStack::absindex(int idx)
|
||||
{
|
||||
if (idx > 0)
|
||||
return idx;
|
||||
|
||||
return lua_gettop(L) + 1 + idx;
|
||||
}
|
||||
|
||||
void LuaStack::pushByIndex(lua_Integer index)
|
||||
{
|
||||
lua_pushvalue(L, index);
|
||||
@@ -168,6 +176,7 @@ bool LuaStack::tryGet(int position, int3 & value)
|
||||
bool LuaStack::tryGet(int position, JsonNode & value)
|
||||
{
|
||||
auto type = lua_type(L, position);
|
||||
value.setModScope("game");
|
||||
|
||||
switch(type)
|
||||
{
|
||||
|
||||
+112
-67
@@ -30,32 +30,6 @@ class LuaApiException : public std::runtime_error
|
||||
|
||||
class LuaStack;
|
||||
|
||||
class LuaDeserializer
|
||||
{
|
||||
LuaStack & stack;
|
||||
lua_State * L;
|
||||
int idx;
|
||||
|
||||
public:
|
||||
LuaDeserializer(LuaStack & stack, int idx);
|
||||
|
||||
template<typename T>
|
||||
void operator()(const std::string &keyName, T & data) const;
|
||||
};
|
||||
|
||||
class LuaSerializer
|
||||
{
|
||||
LuaStack & stack;
|
||||
lua_State * L;
|
||||
int idx;
|
||||
|
||||
public:
|
||||
LuaSerializer(LuaStack & stack, int idx);
|
||||
|
||||
template<typename T>
|
||||
void operator()(const std::string &keyName, const T & data) const;
|
||||
};
|
||||
|
||||
class LuaStack
|
||||
{
|
||||
public:
|
||||
@@ -72,6 +46,10 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Converts stack position relative to top (e.g. -1) into absolute position
|
||||
/// Behavior is identical to `lua_absindex` function, available from Lua 5.2
|
||||
int absindex(int idx);
|
||||
|
||||
void pushNil();
|
||||
void pushInteger(lua_Integer value);
|
||||
void push(bool value);
|
||||
@@ -164,6 +142,72 @@ public:
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<scripting::TagCopyable, T>, int> = 0>
|
||||
void push(const T & value)
|
||||
{
|
||||
using DataType = T;
|
||||
using BaseType = DataType::ScriptingApiName;
|
||||
static_assert(std::is_same_v<std::remove_const_t<DataType>, BaseType>, "Can not push derived class as copyable!");
|
||||
|
||||
static auto KEY = api::TypeRegistry::get()->getKey<BaseType>();
|
||||
|
||||
void * raw = lua_newuserdata(L, sizeof(BaseType));
|
||||
if(!raw)
|
||||
throw LuaApiException("Failed to allocate new user data!");
|
||||
|
||||
new(raw) BaseType(value);
|
||||
|
||||
luaL_getmetatable(L, KEY);
|
||||
if(lua_isnil(L, -1))
|
||||
throw LuaApiException(std::string("Unregistered type pushed on Lua stack: ") + KEY);
|
||||
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void push(const std::vector<T> & value)
|
||||
{
|
||||
lua_newtable(L);
|
||||
int tableIndex = lua_gettop(L);
|
||||
|
||||
for (size_t i = 0; i < value.size(); ++i)
|
||||
{
|
||||
push(value[i]);
|
||||
lua_rawseti(L, tableIndex, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void push(const std::map<std::string, T> & value)
|
||||
{
|
||||
lua_newtable(L);
|
||||
int tableIndex = lua_gettop(L);
|
||||
|
||||
for (const auto &entry : value)
|
||||
{
|
||||
push(entry.second);
|
||||
lua_setfield(L, tableIndex, entry.first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<scripting::TagSerializable, T>, int> = 0>
|
||||
void push(const T & value)
|
||||
{
|
||||
lua_newtable(L);
|
||||
int tableIndex = lua_gettop(L);
|
||||
|
||||
// get non-const value - ugly, but required since same template method is used for deserialization
|
||||
T & nonConstValue = const_cast<T&>(value);
|
||||
|
||||
const auto & luaSerializer = [this, tableIndex]<typename Field>(const std::string &keyName, const Field & data)
|
||||
{
|
||||
push(data);
|
||||
lua_setfield(L, tableIndex, keyName.c_str());
|
||||
};
|
||||
|
||||
nonConstValue.serializeScript(luaSerializer);
|
||||
}
|
||||
|
||||
bool tryGetInteger(int position, lua_Integer & value);
|
||||
|
||||
bool tryGet(int position, bool & value);
|
||||
@@ -232,13 +276,36 @@ public:
|
||||
tryGet(-1, out[i]);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<scripting::TagSerializable, T>, int> = 0>
|
||||
STRONG_INLINE bool tryGet(int position, T & value)
|
||||
{
|
||||
LuaDeserializer serializer(*this, position);
|
||||
value->serializeLua(serializer);
|
||||
const auto & deserializer = [this, position]<typename Data>(const std::string &keyName, Data & data)
|
||||
{
|
||||
if (!lua_istable(L, position))
|
||||
throw LuaApiException("value at index is not a table");
|
||||
|
||||
// pushes table[keyName] on stack
|
||||
// NOTE: if value is not present or set to nil, top of stack will contain nil
|
||||
// do not handle it here, but in tryGet - attempt to tryGet values that don't support nil values
|
||||
// will throw exceptions, while allowing null values (where supported) to load
|
||||
lua_getfield(L, position, keyName.c_str());
|
||||
|
||||
try {
|
||||
tryGet(absindex(-1), data);
|
||||
} catch (...) {
|
||||
// restore stack
|
||||
lua_pop(L, 1);
|
||||
throw;
|
||||
}
|
||||
|
||||
lua_pop(L, 1); // pop pushed value
|
||||
};
|
||||
|
||||
value.serializeScript(deserializer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -296,6 +363,16 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<scripting::TagCopyable, T>, int> = 0>
|
||||
STRONG_INLINE bool tryGet(int position, T & value)
|
||||
{
|
||||
using DataType = T;
|
||||
using BaseType = DataType::ScriptingApiName;
|
||||
static_assert(std::is_same_v<DataType, BaseType>, "Can not push derived class as copyable!");
|
||||
|
||||
return tryGetUData<BaseType>(position, value);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool tryGetAll(int position, Args &...args) {
|
||||
bool failed = false;
|
||||
@@ -314,7 +391,7 @@ public:
|
||||
{
|
||||
const char * expectedType = typeid(T).name();
|
||||
const char * actualType = lua_typename(L, position);
|
||||
std::string message = std::string("Invalid Lua value! Expected ") + expectedType + "at position" + std::to_string(position) + ", but found " + actualType;
|
||||
std::string message = std::string("Invalid Lua value! Expected ") + expectedType + " at position" + std::to_string(position) + ", but found " + actualType;
|
||||
throw LuaApiException( message );
|
||||
}
|
||||
}
|
||||
@@ -322,10 +399,13 @@ public:
|
||||
template<typename BaseType>
|
||||
bool tryGetUData(int position, BaseType & value)
|
||||
{
|
||||
if (lua_isnil(L, position))
|
||||
if constexpr (std::is_assignable_v<std::remove_cvref_t<BaseType>&, std::nullptr_t>)
|
||||
{
|
||||
value = nullptr;
|
||||
return true;
|
||||
if (lua_isnil(L, position))
|
||||
{
|
||||
value = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static auto KEY = api::TypeRegistry::get()->getKey<BaseType>();
|
||||
@@ -466,41 +546,6 @@ private:
|
||||
int initialTop;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void LuaDeserializer::operator()(const std::string &keyName, T & data) const
|
||||
{
|
||||
if (!lua_istable(L, idx))
|
||||
throw LuaApiException("value at index is not a table");
|
||||
|
||||
// pushes table[keyName] on stack
|
||||
lua_getfield(L, idx, keyName.c_str());
|
||||
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
throw LuaApiException("Missing required field '" + keyName + "'");
|
||||
}
|
||||
|
||||
try {
|
||||
stack.tryGet(-1, data);
|
||||
} catch (...) {
|
||||
// restore stack
|
||||
lua_pop(L, 1);
|
||||
throw;
|
||||
}
|
||||
|
||||
lua_pop(L, 1); // pop pushed value
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LuaSerializer::operator()(const std::string &keyName, const T & data) const
|
||||
{
|
||||
if (!lua_istable(L, idx))
|
||||
throw LuaApiException("value at index is not a table");
|
||||
|
||||
stack.push(data);
|
||||
lua_setfield(L, idx, keyName.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
+53
-1
@@ -81,7 +81,7 @@ namespace detail
|
||||
if(objPtr)
|
||||
{
|
||||
auto obj = static_cast<UDataType *>(objPtr);
|
||||
obj->reset();
|
||||
obj->~UDataType();
|
||||
}
|
||||
lua_settop(L, 0);
|
||||
return 0;
|
||||
@@ -117,6 +117,7 @@ public:
|
||||
|
||||
static_assert(std::is_base_of_v<TagRawPointer, ObjectType>, "Class must inherit from ApiRawPointer to be used with this class!");
|
||||
static_assert(!std::is_base_of_v<TagSharedPointer, ObjectType>, "Class must not inherit from ApiSharedPointer to be used with this class!");
|
||||
static_assert(!std::is_base_of_v<TagCopyable, ObjectType>, "Class must not inherit from ApiCopyable to be used with this class!");
|
||||
|
||||
void pushMetatable(lua_State * L) const override final
|
||||
{
|
||||
@@ -157,6 +158,7 @@ public:
|
||||
|
||||
static_assert(std::is_base_of_v<TagSharedPointer, ObjectType>, "Class must inherit from ApiSharedPointer to be used with this class!");
|
||||
static_assert(!std::is_base_of_v<TagRawPointer, ObjectType>, "Class must not inherit from ApiRawPointer to be used with this class!");
|
||||
static_assert(!std::is_base_of_v<TagCopyable, ObjectType>, "Class must not inherit from ApiCopyable to be used with this class!");
|
||||
|
||||
static int constructor(lua_State * L)
|
||||
{
|
||||
@@ -195,6 +197,56 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class Proxy = T>
|
||||
class CopyableWrapper : public RegistarBase
|
||||
{
|
||||
public:
|
||||
using ObjectType = typename std::remove_cv_t<T>;
|
||||
using UDataType = T;
|
||||
using CustomRegType = detail::CustomRegType;
|
||||
|
||||
static_assert(std::is_base_of_v<TagCopyable, ObjectType>, "Class must inherit from ApiCopyable to be used with this class!");
|
||||
static_assert(!std::is_base_of_v<TagRawPointer, ObjectType>, "Class must not inherit from ApiRawPointer to be used with this class!");
|
||||
static_assert(!std::is_base_of_v<TagSharedPointer, ObjectType>, "Class must not inherit from ApiSharedPointer to be used with this class!");
|
||||
|
||||
static int constructor(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
S.clear();//we do not accept any parameters in constructor
|
||||
ObjectType obj;
|
||||
S.push(obj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void pushMetatable(lua_State * L) const override final
|
||||
{
|
||||
static auto KEY = api::TypeRegistry::get()->getKey<UDataType>();
|
||||
|
||||
LuaStack S(L);
|
||||
|
||||
if(luaL_newmetatable(L, KEY) != 0)
|
||||
{
|
||||
adjustMetatable(L);
|
||||
|
||||
S.push("__gc");
|
||||
lua_pushcfunction(L, &(detail::Dispatcher<Proxy, UDataType>::destructor));
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
S.balance();
|
||||
|
||||
detail::Dispatcher<Proxy, UDataType>::pushStaticTable(L);
|
||||
|
||||
adjustStaticTable(L);
|
||||
}
|
||||
protected:
|
||||
void adjustMetatable(lua_State * L) const override
|
||||
{
|
||||
detail::Dispatcher<Proxy, UDataType>::setIndexTable(L);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
namespace api
|
||||
namespace scripting::api
|
||||
{
|
||||
|
||||
VCMI_REGISTER_CORE_SCRIPT_API(BattleCbProxy, "game.Battle");
|
||||
@@ -46,7 +44,7 @@ int BattleCbProxy::getBattlefieldType(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
const CBattleInfoCallback * object;
|
||||
const IBattleInfoCallback * object;
|
||||
if(!S.tryGet(1, object))
|
||||
return S.retVoid();
|
||||
|
||||
@@ -59,7 +57,7 @@ int BattleCbProxy::getTerrainType(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
const CBattleInfoCallback * object;
|
||||
const IBattleInfoCallback * object;
|
||||
if(!S.tryGet(1, object))
|
||||
return S.retVoid();
|
||||
|
||||
@@ -70,7 +68,7 @@ int BattleCbProxy::getAvailableHex(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
const CBattleInfoCallback * object;
|
||||
const IBattleInfoCallback * object;
|
||||
if(!S.tryGet(1, object))
|
||||
return S.retVoid();
|
||||
|
||||
@@ -85,16 +83,15 @@ int BattleCbProxy::getAvailableHex(lua_State * L)
|
||||
|
||||
S.clear();
|
||||
BattleHex result = object->getAvailableHex(creature, side, hexVal);
|
||||
S.push(result.toInt());
|
||||
S.push(result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int BattleCbProxy::getUnitByPos(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
const CBattleInfoCallback * object;
|
||||
const IBattleInfoCallback * object;
|
||||
if(!S.tryGet(1, object))
|
||||
return S.retVoid();
|
||||
|
||||
@@ -119,7 +116,7 @@ int BattleCbProxy::getAnyUnitIf(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
const CBattleInfoCallback * object;
|
||||
const IBattleInfoCallback * object;
|
||||
if(!S.tryGet(1, object))
|
||||
return S.retVoid();
|
||||
|
||||
@@ -134,14 +131,14 @@ int BattleCbProxy::getAnyUnitIf(lua_State * L)
|
||||
S2.push(unit);
|
||||
|
||||
if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
|
||||
std::string error = lua_tostring(L, -1);
|
||||
std::string error = lua_tostring(L, S2.absindex(-1));
|
||||
logGlobal->error("Lua getAnyUnitIf callback failed with message: %s", error);
|
||||
S2.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
S2.tryGet(-1, result);
|
||||
S2.tryGet(S2.absindex(-1), result);
|
||||
S2.balance();
|
||||
return result;
|
||||
});
|
||||
@@ -156,7 +153,6 @@ int BattleCbProxy::getAnyUnitIf(lua_State * L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -11,21 +11,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <vcmi/scripting/Service.h>
|
||||
#include "../../lib/battle/CBattleInfoCallback.h"
|
||||
#include "../../lib/battle/IBattleInfoCallback.h"
|
||||
|
||||
#include "../LuaWrapper.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
namespace api
|
||||
namespace scripting::api
|
||||
{
|
||||
|
||||
class BattleCbProxy : public RawPointerWrapper<const CBattleInfoCallback, BattleCbProxy>
|
||||
class BattleCbProxy : public RawPointerWrapper<const IBattleInfoCallback, BattleCbProxy>
|
||||
{
|
||||
public:
|
||||
using Wrapper = RawPointerWrapper<const CBattleInfoCallback, BattleCbProxy>;
|
||||
using Wrapper = RawPointerWrapper<const IBattleInfoCallback, BattleCbProxy>;
|
||||
|
||||
static const std::vector<typename Wrapper::CustomRegType> REGISTER_CUSTOM;
|
||||
|
||||
@@ -36,7 +34,6 @@ public:
|
||||
static int getAnyUnitIf(lua_State * L);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Enums.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "Enums.h"
|
||||
|
||||
|
||||
namespace scripting::api
|
||||
{
|
||||
|
||||
Enums::EnumMap<EHealLevel> Enums::exportHealLevel() const
|
||||
{
|
||||
return {
|
||||
{ "heal", EHealLevel::HEAL },
|
||||
{ "resurrect", EHealLevel::RESURRECT },
|
||||
{ "overheal", EHealLevel::OVERHEAL },
|
||||
};
|
||||
}
|
||||
|
||||
Enums::EnumMap<EHealPower> Enums::exportHealPower() const
|
||||
{
|
||||
return {
|
||||
{ "oneBattle", EHealPower::ONE_BATTLE },
|
||||
{ "permanent", EHealPower::PERMANENT },
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Enums.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vcmi/scripting/ApiTags.h>
|
||||
|
||||
#include "../../lib/constants/Enumerations.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace scripting::api
|
||||
{
|
||||
|
||||
class Enums : public scripting::ApiSerializable<Enums>
|
||||
{
|
||||
template<typename EnumType>
|
||||
using EnumMap = std::map<std::string, EnumType>;
|
||||
|
||||
EnumMap<EHealLevel> exportHealLevel() const;
|
||||
EnumMap<EHealPower> exportHealPower() const;
|
||||
|
||||
public:
|
||||
template<typename Serializer>
|
||||
void serializeScript(Serializer & s)
|
||||
{
|
||||
s("HealLevel", exportHealLevel());
|
||||
s("HealPower", exportHealPower());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+34
-17
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "../LuaStack.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
#include "../../lib/battle/Unit.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@@ -27,24 +28,10 @@ VCMI_REGISTER_CORE_SCRIPT_API(ServerCbProxy, "game.Server");
|
||||
|
||||
const std::vector<ServerCbProxy::CustomRegType> ServerCbProxy::REGISTER_CUSTOM =
|
||||
{
|
||||
// {
|
||||
// "addToBattleLog",
|
||||
// &ServerCbProxy::apply<BattleLogMessage>,
|
||||
// false
|
||||
// },
|
||||
// {
|
||||
// "moveUnit",
|
||||
// &ServerCbProxy::apply<BattleStackMoved>,
|
||||
// false
|
||||
// },
|
||||
{ "createUnit", &ServerCbProxy::createUnit, false },
|
||||
{ "updateUnit", &ServerCbProxy::updateUnit, false },
|
||||
{ "healUnit", &ServerCbProxy::healUnit, false },
|
||||
{ "removeUnit", &ServerCbProxy::removeUnit, false },
|
||||
// {
|
||||
// "commitPackage",
|
||||
// &ServerCbProxy::commitPackage,
|
||||
// false
|
||||
// }
|
||||
};
|
||||
|
||||
int ServerCbProxy::commitPackage(lua_State * L)
|
||||
@@ -106,9 +93,9 @@ int ServerCbProxy::createUnit(lua_State * L)
|
||||
ServerCallback * object = nullptr;
|
||||
BattleUnitsChanged buc;
|
||||
UnitChanges uc;
|
||||
uc.operation = UnitChanges::EOperation::UPDATE;
|
||||
uc.operation = UnitChanges::EOperation::ADD;
|
||||
|
||||
if (!S.tryGetAll(1, object, buc.battleID, uc.data))
|
||||
if (!S.tryGetAll(1, object, buc.battleID, uc.id, uc.data))
|
||||
return S.retVoid();
|
||||
|
||||
buc.changedStacks.push_back(uc);
|
||||
@@ -117,6 +104,36 @@ int ServerCbProxy::createUnit(lua_State * L)
|
||||
return S.retVoid();
|
||||
}
|
||||
|
||||
int ServerCbProxy::healUnit(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
ServerCallback * object = nullptr;
|
||||
BattleID battleID;
|
||||
const battle::Unit * unit = nullptr;
|
||||
int64_t healthDelta;
|
||||
EHealPower healPower;
|
||||
EHealLevel healLevel;
|
||||
|
||||
if (!S.tryGetAll(1, object, battleID, unit, healthDelta, healLevel, healPower))
|
||||
return S.retVoid();
|
||||
|
||||
auto changedUnit = unit->acquire();
|
||||
changedUnit->heal(healthDelta, healLevel, healPower);
|
||||
|
||||
BattleUnitsChanged buc;
|
||||
UnitChanges uc;
|
||||
buc.battleID = battleID;
|
||||
uc.operation = UnitChanges::EOperation::UPDATE;
|
||||
uc.id = unit->unitId();
|
||||
uc.data = changedUnit->save();
|
||||
uc.healthDelta = healthDelta;
|
||||
buc.changedStacks.push_back(uc);
|
||||
|
||||
object->apply(buc);
|
||||
return S.retVoid();
|
||||
}
|
||||
|
||||
int ServerCbProxy::updateUnit(lua_State * L)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
|
||||
static int createUnit(lua_State * L);
|
||||
static int updateUnit(lua_State * L);
|
||||
static int healUnit(lua_State * L);
|
||||
static int removeUnit(lua_State * L);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
/*
|
||||
* BattleHexProxy.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "BattleHexProxy.h"
|
||||
|
||||
BattleHexProxy::BattleHexProxy() {}
|
||||
#include "../../../lib/GameLibrary.h"
|
||||
#include "../../LuaStack.h"
|
||||
#include "../../LuaCallWrapper.h"
|
||||
#include "../Registry.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace scripting::api::battle
|
||||
{
|
||||
|
||||
VCMI_REGISTER_CORE_SCRIPT_API(BattleHexProxy, "battle.BattleHex")
|
||||
|
||||
const std::vector<BattleHexProxy::CustomRegType> BattleHexProxy::REGISTER_CUSTOM =
|
||||
{
|
||||
// {"isValid", LuaMethodWrapper<BattleHex, decltype(&BattleHex::isValid), &BattleHex::isValid>::invoke, false},
|
||||
{"isValid", LuaFunctionWrapper<BattleHexProxy::isValid>::invoke, false},
|
||||
{"toInteger", LuaFunctionWrapper<BattleHexProxy::toInteger>::invoke, false},
|
||||
};
|
||||
|
||||
bool BattleHexProxy::isValid(BattleHex & hex)
|
||||
{
|
||||
return hex.isValid();
|
||||
}
|
||||
|
||||
int BattleHexProxy::toInteger(BattleHex & hex)
|
||||
{
|
||||
return hex.toInt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -1,10 +1,40 @@
|
||||
#ifndef BATTLEHEXPROXY_H
|
||||
#define BATTLEHEXPROXY_H
|
||||
/*
|
||||
* BattleHexProxy.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
class BattleHexProxy
|
||||
#pragma once
|
||||
|
||||
#include <vcmi/scripting/Service.h>
|
||||
#include "../../../lib/battle/BattleHex.h"
|
||||
#include "../../LuaWrapper.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
public:
|
||||
BattleHexProxy();
|
||||
};
|
||||
namespace api
|
||||
{
|
||||
namespace battle
|
||||
{
|
||||
|
||||
#endif // BATTLEHEXPROXY_H
|
||||
class BattleHexProxy : public CopyableWrapper<const BattleHex, BattleHexProxy>
|
||||
{
|
||||
public:
|
||||
using Wrapper = CopyableWrapper<const BattleHex, BattleHexProxy>;
|
||||
static const std::vector<typename Wrapper::CustomRegType> REGISTER_CUSTOM;
|
||||
|
||||
static bool isValid(BattleHex & hex);
|
||||
static int toInteger(BattleHex & hex);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -19,11 +19,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace battle
|
||||
namespace scripting::api::battle
|
||||
{
|
||||
|
||||
VCMI_REGISTER_CORE_SCRIPT_API(UnitProxy, "battle.Unit")
|
||||
@@ -37,29 +33,23 @@ const std::vector<UnitProxy::CustomRegType> UnitProxy::REGISTER_CUSTOM =
|
||||
{"isAlive", LuaMethodWrapper<Unit, decltype(&Unit::alive), &Unit::alive>::invoke, false},
|
||||
{"isClone", LuaMethodWrapper<Unit, decltype(&Unit::isClone), &Unit::isClone>::invoke, false},
|
||||
{"isSummoned", LuaMethodWrapper<Unit, decltype(&Unit::isSummoned), &Unit::isSummoned>::invoke, false},
|
||||
// {"unitId", LuaMethodWrapper<Unit, decltype(&IUnitInfo::unitId), &IUnitInfo::unitId>::invoke, false},
|
||||
{"getOwner", LuaMethodWrapper<Unit, decltype(&IUnitInfo::unitOwner), &IUnitInfo::unitOwner>::invoke, false},
|
||||
{"getSlot", LuaMethodWrapper<Unit, decltype(&IUnitInfo::unitSlot), &IUnitInfo::unitSlot>::invoke, false},
|
||||
|
||||
{"getCreature", &UnitProxy::getCreature, false },
|
||||
{"heal", LuaFunctionWrapper<UnitProxy::heal>::invoke, false},
|
||||
{"getCreature", LuaFunctionWrapper<UnitProxy::getCreature>::invoke, false },
|
||||
};
|
||||
|
||||
int UnitProxy::getCreature(lua_State * L)
|
||||
void UnitProxy::heal(Unit * unit, int64_t & amount, EHealLevel level, EHealPower power)
|
||||
{
|
||||
LuaStack S(L);
|
||||
|
||||
const Unit * object;
|
||||
if(!S.tryGet(1, object))
|
||||
return S.retVoid();
|
||||
|
||||
S.clear();
|
||||
const Creature * result = object->creatureId().toEntity(LIBRARY);
|
||||
S.push(result);
|
||||
return 1;
|
||||
unit->heal(amount, level, power);
|
||||
}
|
||||
|
||||
const Creature * UnitProxy::getCreature(const Unit * unit)
|
||||
{
|
||||
return unit->creatureId().toEntity(LIBRARY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -32,7 +32,8 @@ public:
|
||||
using Wrapper = RawPointerWrapper<const Unit, UnitProxy>;
|
||||
static const std::vector<typename Wrapper::CustomRegType> REGISTER_CUSTOM;
|
||||
|
||||
static int getCreature(lua_State * L);
|
||||
static void heal(Unit *, int64_t & amount, EHealLevel level, EHealPower power);
|
||||
static const Creature * getCreature(const Unit *);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -111,31 +111,25 @@ apply = function(parameters, mechanics, server, target)
|
||||
local creature = LIBRARY:getCreatureByName(parameters.id)
|
||||
|
||||
for _, dest in ipairs(target) do
|
||||
if dest.unitValue then
|
||||
local summoned = dest.unitValue
|
||||
local state = summoned:acquire()
|
||||
local healthValue = summonedCreatureHealth(parameters, mechanics)
|
||||
|
||||
state:heal(
|
||||
healthValue,
|
||||
EHealLevel.OVERHEAL,
|
||||
(parameters.permanent and EHealPower.PERMANENT or EHealPower.ONE_BATTLE)
|
||||
)
|
||||
|
||||
server:updateUnit(
|
||||
if dest.unit ~= nil then
|
||||
server:healUnit(
|
||||
mechanics:getBattleID(),
|
||||
summoned:unitId(),
|
||||
state:save(),
|
||||
healthValue
|
||||
dest.unit,
|
||||
summonedCreatureHealth(parameters, mechanics),
|
||||
ENUM.HealLevel.overheal,
|
||||
parameters.permanent and ENUM.HealPower.permanent or ENUM.HealPower.oneBattle
|
||||
)
|
||||
else
|
||||
print("SpellEffectSummon. Hex: ", dest.hex)
|
||||
assert(dest.hex ~= nil)
|
||||
server:createUnit(
|
||||
mechanics:getBattleID(),
|
||||
mechanics:getBattle():getNextUnitId(),
|
||||
{
|
||||
count = summonedCreatureAmount(parameters, mechanics),
|
||||
type = creature:getJsonKey(),
|
||||
side = mechanics:getCasterSide(),
|
||||
position = dest.hexValue,
|
||||
position = dest.hex:toInteger(),
|
||||
summoned = not parameters.permanent
|
||||
}
|
||||
)
|
||||
@@ -158,7 +152,7 @@ transformTarget = function(parameters, mechanics, aimPoint, spellTarget)
|
||||
|
||||
if sameSummoned == nil or not parameters.summonSameUnit then
|
||||
local hex = mechanics:getBattle():getAvailableHex(creature, mechanics:getCasterSide())
|
||||
if not hex.isValid() then
|
||||
if not hex:isValid() then
|
||||
return {} -- no free space. FIXME: should be in isApplicable
|
||||
else
|
||||
return {
|
||||
@@ -171,8 +165,8 @@ transformTarget = function(parameters, mechanics, aimPoint, spellTarget)
|
||||
else
|
||||
return {
|
||||
{
|
||||
hex = sameSummoned[1]:getPosition(),
|
||||
unit = sameSummoned[1]
|
||||
hex = sameSummoned:getPosition(),
|
||||
unit = sameSummoned
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
@@ -87,6 +87,7 @@ void EffectFixture::setUp()
|
||||
|
||||
EXPECT_CALL(mechanicsMock, game()).WillRepeatedly(Return(&gameMock));
|
||||
EXPECT_CALL(mechanicsMock, battle()).WillRepeatedly(Return(battleFake.get()));
|
||||
EXPECT_CALL(mechanicsMock, getBattleID()).WillRepeatedly(Return(BattleID()));
|
||||
EXPECT_CALL(mechanicsMock, getHeroCaster()).WillRepeatedly(Return(nullptr));
|
||||
|
||||
EXPECT_CALL(*battleFake, getScriptContextPool()).WillRepeatedly(ReturnRef(*pool));
|
||||
|
||||
Reference in New Issue
Block a user