diff --git a/library/Core.cpp b/library/Core.cpp index afaf23683..b298b9550 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -972,6 +972,7 @@ int Core::UnicodeAwareSym(const SDL::KeyboardEvent& ke) { // Assume keyboard layouts don't change the order of numbers: if( '0' <= ke.ksym.sym && ke.ksym.sym <= '9') return ke.ksym.sym; + if(SDL::K_F1 <= ke.ksym.sym && ke.ksym.sym <= SDL::K_F12) return ke.ksym.sym; int unicode = ke.ksym.unicode; diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 536bcd72a..79f515d40 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -65,9 +65,22 @@ void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) push_object_internal(state, this, ptr); } +static void invoke_assign(lua_State *state, type_identity *id, void *ptr, int val_index) +{ + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + push_object_internal(state, id, ptr); + lua_pushvalue(state, val_index); + lua_call(state, 2, 0); +} + void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) { - field_error(state, fname_idx, "complex object", "write"); + if (lua_istable(state, val_index)) + { + invoke_assign(state, this, ptr, val_index); + } + else + field_error(state, fname_idx, "complex object", "write"); } void enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) @@ -150,6 +163,58 @@ void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) lua_read(state, fname_idx, ptr, target); } +static void autovivify_ptr(lua_State *state, int fname_idx, void **pptr, + type_identity *target, int val_index) +{ + lua_getfield(state, val_index, "new"); + + // false or nil => bail out + if (!lua_toboolean(state, -1)) + field_error(state, fname_idx, "null and autovivify not requested", "write"); + + // not 'true' => call df.new() + if (!lua_isboolean(state, -1)) + { + int top = lua_gettop(state); + + // Verify new points to a reasonable type of object + type_identity *suggested = get_object_identity(state, top, "autovivify", true, true); + + if (!is_type_compatible(state, target, 0, suggested, top+1, false)) + field_error(state, fname_idx, "incompatible suggested autovivify type", "write"); + + lua_pop(state, 1); + + // Invoke df.new() + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); + lua_swap(state); + lua_call(state, 1, 1); + + // Retrieve the pointer + void *nval = get_object_internal(state, target, top, false); + + // shouldn't happen: this means suggested type is compatible, + // but its new() result isn't for some reason. + if (!nval) + field_error(state, fname_idx, "inconsistent autovivify type", "write"); + + *pptr = nval; + } + // otherwise use the target type + else + { + if (!target) + field_error(state, fname_idx, "trying to autovivify void*", "write"); + + *pptr = target->allocate(); + + if (!*pptr) + field_error(state, fname_idx, "could not allocate in autovivify", "write"); + } + + lua_pop(state, 1); +} + void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index) { @@ -157,6 +222,13 @@ void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, if (lua_isnil(state, val_index)) *pptr = NULL; + else if (lua_istable(state, val_index)) + { + if (!*pptr) + autovivify_ptr(state, fname_idx, pptr, target, val_index); + + invoke_assign(state, target, *pptr, val_index); + } else { void *nval = get_object_internal(state, target, val_index, false); @@ -435,10 +507,15 @@ static void write_field(lua_State *state, const struct_field_info *field, void * case struct_field_info::POINTER: df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx); + return; case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: - field_error(state, 2, "complex object", "write"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + read_field(state, field, ptr); + lua_pushvalue(state, value_idx); + lua_call(state, 2, 0); + return; case struct_field_info::END: return; diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 4758f04f1..36b24a403 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -255,8 +255,8 @@ static void fetch_container_details(lua_State *state, int meta, type_identity ** /** * Check if type1 and type2 are compatible, possibly using additional metatable data. */ -static bool is_type_compatible(lua_State *state, type_identity *type1, int meta1, - type_identity *type2, int meta2, bool exact_equal) +bool LuaWrapper::is_type_compatible(lua_State *state, type_identity *type1, int meta1, + type_identity *type2, int meta2, bool exact_equal) { if (type1 == type2) return true; @@ -417,9 +417,9 @@ static bool is_valid_metatable(lua_State *state, int objidx, int metaidx) /** * Given a DF object reference or type, safely retrieve its identity pointer. */ -static type_identity *get_object_identity(lua_State *state, int objidx, - const char *ctx, bool allow_type = false, - bool keep_metatable = false) +type_identity *LuaWrapper::get_object_identity(lua_State *state, int objidx, + const char *ctx, bool allow_type, + bool keep_metatable) { if (!lua_getmetatable(state, objidx)) luaL_error(state, "Invalid object in %s", ctx); @@ -608,6 +608,43 @@ static int meta_new(lua_State *state) return 1; } +static void invoke_resize(lua_State *state, int table, lua_Integer size) +{ + lua_getfield(state, table, "resize"); + lua_pushvalue(state, table); + lua_pushinteger(state, size); + lua_call(state, 2, 0); +} + +static void copy_table(lua_State *state, int dest, int src, int skipbase) +{ + // stack: (skipbase) skipkey skipkey | + + int top = lua_gettop(state); + + lua_pushnil(state); + + while (lua_next(state, src)) + { + for (int i = skipbase+1; i <= top; i++) + { + if (lua_rawequal(state, -2, i)) + { + lua_pop(state, 1); + goto next_outer; + } + } + + { + lua_pushvalue(state, -2); + lua_swap(state); + lua_settable(state, dest); + } + + next_outer:; + } +} + /** * Method: assign data between objects. */ @@ -618,11 +655,105 @@ static int meta_assign(lua_State *state) if (argc != 2) luaL_error(state, "Usage: target:assign(src) or df.assign(target,src)"); - type_identity *id1, *id2; - check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false); + if (!lua_istable(state, 2)) + { + type_identity *id1, *id2; + check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false); + + if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2))) + luaL_error(state, "No copy support for %s", id1->getFullName().c_str()); + } + else + { + type_identity *id = get_object_identity(state, 1, "df.assign()", false); + + int base = lua_gettop(state); + + // x:assign{ assign = foo } => x:assign(foo) + bool has_assign = false; + + lua_pushstring(state, "assign"); + lua_dup(state); + lua_rawget(state, 2); + + if (!lua_isnil(state,-1)) + { + has_assign = true; + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + lua_pushvalue(state, 1); + lua_pushvalue(state, base+2); + lua_call(state, 2, 0); + } + + lua_pop(state, 1); + + // new is used by autovivification and should be skipped + lua_pushstring(state, "new"); + + if (id->isContainer()) + { + // check resize field + lua_pushstring(state, "resize"); + lua_dup(state); + lua_rawget(state, 2); + + if (lua_isnil(state,-1) && !has_assign) + { + /* + * no assign && nil or missing resize field => 1-based lua array + */ + int size = lua_objlen(state, 2); + + lua_pop(state, 1); + invoke_resize(state, 1, size); + + for (int i = 1; i <= size; i++) + { + lua_pushinteger(state, i-1); + lua_rawgeti(state, 2, i); + lua_settable(state, 1); + } + } + else + { + if (lua_isboolean(state, -1) || lua_isnil(state, -1)) + { + // resize=false => just assign + // resize=true => find the largest index + if (lua_toboolean(state, -1)) + { + lua_Integer size = 0; + + lua_pushnil(state); + while (lua_next(state, 2)) + { + lua_pop(state, 1); + if (lua_isnumber(state,-1)) + size = std::max(size, lua_tointeger(state,-1)+1); + } + + invoke_resize(state, 1, size); + } + } + else + { + // otherwise, must be an explicit number + if (!lua_isnumber(state,-1)) + luaL_error(state, "Invalid container.resize value in df.assign()"); + + invoke_resize(state, 1, lua_tointeger(state, -1)); + } + + lua_pop(state, 1); + copy_table(state, 1, 2, base); + } + } + else + { - if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2))) - luaL_error(state, "No copy support for %s", id1->getFullName().c_str()); + copy_table(state, 1, 2, base); + } + } return 0; } diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index d043d96f7..64ebf4de6 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -149,6 +149,13 @@ namespace DFHack { namespace LuaWrapper { */ uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode); + bool is_type_compatible(lua_State *state, type_identity *type1, int meta1, + type_identity *type2, int meta2, bool exact_equal); + + type_identity *get_object_identity(lua_State *state, int objidx, + const char *ctx, bool allow_type = false, + bool keep_metatable = false); + void LookupInTable(lua_State *state, void *id, const char *tname); void SaveInTable(lua_State *state, void *node, const char *tname); void SaveTypeInfo(lua_State *state, void *node); diff --git a/library/xml b/library/xml index b41c666c6..8bb7f923b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b41c666c6be6fe18906a98dababdc4ff681b8382 +Subproject commit 8bb7f923b1d124610db7e30aeb3be8f4cb9bd021 diff --git a/plugins/Dfusion/src/lua_VersionInfo.cpp b/plugins/Dfusion/src/lua_VersionInfo.cpp index 5ce469d35..9f5cd17e7 100644 --- a/plugins/Dfusion/src/lua_VersionInfo.cpp +++ b/plugins/Dfusion/src/lua_VersionInfo.cpp @@ -96,7 +96,8 @@ const luaL_Reg lua_vinfo_func[]= VI_FUNC(setAddress), VI_FUNC(getAddress), VI_FUNC(setOS), - VI_FUNC(getOS) + VI_FUNC(getOS), + {NULL,NULL} }; #undef VI_FUNC void lua::RegisterVersionInfo(lua::state &st) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 3ef0f9e5c..c87d6ffbf 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -802,7 +802,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } // Idle dwarves come first, then we sort from least-skilled to most-skilled. - std::sort(hauler_ids.begin(), hauler_ids.end(), [&dwarf_info] (int i, int j) -> bool { if (dwarf_info[i].state == IDLE && dwarf_info[j].state != IDLE) @@ -812,6 +811,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return dwarf_info[i].mastery_penalty > dwarf_info[j].mastery_penalty; }); + // don't set any haulers if everyone is off drinking or something + if (hauler_ids.size() == 0) { + num_haulers = 0; + } + FOR_ENUM_ITEMS(unit_labor, labor) { if (labor == df::enums::unit_labor::NONE) @@ -831,7 +835,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) assert(dwarf >= 0); assert(dwarf < n_dwarfs); - dwarfs[dwarf]->status.labors[labor] = true; dwarf_info[dwarf].assigned_jobs++; } @@ -848,7 +851,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) dwarfs[dwarf]->status.labors[labor] = false; } } - return CR_OK; }