From ba78194636a9a498f6979cc21cd39399f23d658a Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 24 Feb 2013 16:00:35 -0500 Subject: Allow any character in formspec strings with escape char --- doc/lua_api.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 005d7c010..8246377e2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -778,6 +778,9 @@ string:trim() minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)" ^ Convert position to a printable string minetest.string_to_pos(string) -> position +^ Same but in reverse +minetest.formspec_escape(string) -> string +^ escapes characters like [, ], and \ that can not be used in formspecs minetest namespace reference ----------------------------- -- cgit v1.2.3 From 5af8acfa6e41e258dd7e2135e8e75f03096c1d5c Mon Sep 17 00:00:00 2001 From: RealBadAngel Date: Mon, 4 Mar 2013 01:55:16 +0100 Subject: Added method to get all registered recipes for item(node) --- doc/lua_api.txt | 5 ++++ src/craftdef.cpp | 37 ++++++++++++++++++++++++++++ src/craftdef.h | 4 +++ src/scriptapi.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8246377e2..809d3d9d0 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -922,12 +922,17 @@ minetest.get_craft_result(input) -> output, decremented_input ^ output.time = number, if unsuccessful: 0 ^ decremented_input = like input minetest.get_craft_recipe(output) -> input +^ returns last registered recipe for output item (node) ^ output is a node or item type such as 'default:torch' ^ input.method = 'normal' or 'cooking' or 'fuel' ^ input.width = for example 3 ^ input.items = for example { stack 1, stack 2, stack 3, stack 4, stack 5, stack 6, stack 7, stack 8, stack 9 } ^ input.items = nil if no recipe found +minetest.get_all_craft_recipes(output) -> table or nil +^ returns table with all registered recipes for output item (node) +^ returns nil if no recipe was found +^ table entries have same format as minetest.get_craft_recipe minetest.handle_node_drops(pos, drops, digger) ^ drops: list of itemstrings ^ Handles drops from nodes after digging: Default action is to put them into diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 99e3fcc3d..c79408f99 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -987,6 +987,43 @@ public: } return false; } + virtual std::vector getCraftRecipes(CraftOutput &output, + IGameDef *gamedef) const + { + std::vector recipes_list; + CraftInput input; + CraftOutput tmpout; + tmpout.item = ""; + tmpout.time = 0; + + for(std::vector::const_reverse_iterator + i = m_craft_definitions.rbegin(); + i != m_craft_definitions.rend(); i++) + { + CraftDefinition *def = *i; + + /*infostream<<"Checking "<dump()<getOutput(input, gamedef); + if(tmpout.item.substr(0,output.item.length()) == output.item) + { + // Get output, then decrement input (if requested) + input = def->getInput(output, gamedef); + recipes_list.push_back(*i); + } + } + catch(SerializationError &e) + { + errorstream<<"getCraftResult: ERROR: " + <<"Serialization error in recipe " + <dump()< getCraftRecipes(CraftOutput &output, + IGameDef *gamedef) const=0; // Print crafting recipes for debugging virtual std::string dump() const=0; @@ -376,6 +378,8 @@ public: bool decrementInput, IGameDef *gamedef) const=0; virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output, IGameDef *gamedef) const=0; + virtual std::vector getCraftRecipes(CraftOutput &output, + IGameDef *gamedef) const=0; // Print crafting recipes for debugging virtual std::string dump() const=0; diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index ef8a1454a..074a2eb0d 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -1000,6 +1000,79 @@ static int l_notify_authentication_modified(lua_State *L) return 0; } +// get_craft_recipes(result item) +static int l_get_all_craft_recipes(lua_State *L) +{ + char tmp[20]; + int input_i = 1; + std::string o_item = luaL_checkstring(L,input_i); + IGameDef *gdef = get_server(L); + ICraftDefManager *cdef = gdef->cdef(); + CraftInput input; + CraftOutput output(o_item,0); + std::vector recipes_list = cdef->getCraftRecipes(output, gdef); + if (recipes_list.empty()) + { + lua_pushnil(L); + return 1; + } + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + lua_newtable(L); + int table = lua_gettop(L); + for(std::vector::const_iterator + i = recipes_list.begin(); + i != recipes_list.end(); i++) + { + CraftOutput tmpout; + tmpout.item = ""; + tmpout.time = 0; + CraftDefinition *def = *i; + tmpout = def->getOutput(input, gdef); + if(tmpout.item.substr(0,output.item.length()) == output.item) + { + input = def->getInput(output, gdef); + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + lua_newtable(L); + int k = 0; + lua_newtable(L); + for(std::vector::const_iterator + i = input.items.begin(); + i != input.items.end(); i++, k++) + { + if (i->empty()) continue; + sprintf(tmp,"%d",k); + lua_pushstring(L,tmp); + lua_pushstring(L,i->name.c_str()); + lua_settable(L, -3); + } + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", input.width); + switch (input.method) + { + case CRAFT_METHOD_NORMAL: + lua_pushstring(L,"normal"); + break; + case CRAFT_METHOD_COOKING: + lua_pushstring(L,"cooking"); + break; + case CRAFT_METHOD_FUEL: + lua_pushstring(L,"fuel"); + break; + default: + lua_pushstring(L,"unknown"); + } + lua_setfield(L, -2, "type"); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + } + return 1; +} + // rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds static int l_rollback_get_last_node_actor(lua_State *L) { @@ -1086,6 +1159,7 @@ static const struct luaL_Reg minetest_f [] = { {"notify_authentication_modified", l_notify_authentication_modified}, {"get_craft_result", l_get_craft_result}, {"get_craft_recipe", l_get_craft_recipe}, + {"get_all_craft_recipes", l_get_all_craft_recipes}, {"rollback_get_last_node_actor", l_rollback_get_last_node_actor}, {"rollback_revert_actions_by", l_rollback_revert_actions_by}, {NULL, NULL} -- cgit v1.2.3 From fc5d2074b99d22022d2bf8e693351274bc3f6d09 Mon Sep 17 00:00:00 2001 From: Jeija Date: Mon, 11 Feb 2013 09:58:58 +0100 Subject: Allow minetest.after to take a variable number of arguments --- builtin/misc.lua | 6 +++--- doc/lua_api.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'doc/lua_api.txt') diff --git a/builtin/misc.lua b/builtin/misc.lua index 496435b33..2cc76e8ec 100644 --- a/builtin/misc.lua +++ b/builtin/misc.lua @@ -14,14 +14,14 @@ minetest.register_globalstep(function(dtime) for index, timer in ipairs(minetest.timers) do timer.time = timer.time - dtime if timer.time <= 0 then - timer.func(timer.param) + timer.func(unpack(timer.args)) table.remove(minetest.timers,index) end end end) -function minetest.after(time, func, param) - table.insert(minetest.timers_to_add, {time=time, func=func, param=param}) +function minetest.after(time, func, ...) + table.insert(minetest.timers_to_add, {time=time, func=func, args=arg}) end function minetest.check_player_privs(name, privs) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 809d3d9d0..eb05d117e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -977,9 +977,9 @@ minetest.sound_play(spec, parameters) -> handle minetest.sound_stop(handle) Timing: -minetest.after(time, func, param) +minetest.after(time, func, ...) ^ Call function after time seconds -^ param is optional; to pass multiple parameters, pass a table. +^ Optional: Variable number of arguments that are passed to func Server: minetest.request_shutdown() -> request for server shutdown -- cgit v1.2.3 From 7f51b2da28e004b82fc6835e5257e619da4666b2 Mon Sep 17 00:00:00 2001 From: RealBadAngel Date: Sun, 17 Mar 2013 12:16:57 +0100 Subject: lua methods set_look_pitch and set_look_yaw --- doc/lua_api.txt | 2 ++ src/content_sao.cpp | 14 ++++++++++++++ src/content_sao.h | 2 ++ src/scriptapi_object.cpp | 27 ++++++++++++++++++++++++++- src/scriptapi_object.h | 8 +++++++- 5 files changed, 51 insertions(+), 2 deletions(-) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index eb05d117e..7d8fa149f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1228,6 +1228,8 @@ Player-only: (no-op for other objects) - get_look_dir(): get camera direction as a unit vector - get_look_pitch(): pitch in radians - get_look_yaw(): yaw in radians (wraps around pretty randomly as of now) +- set_look_pitch(radians): sets look pitch +- set_look_yaw(radians): sets look yaw - set_inventory_formspec(formspec) ^ Redefine player's inventory form ^ Should usually be called in on_joinplayer diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 1e02ea5a5..d7afc31d8 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1230,6 +1230,20 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) m_moved = true; } +void PlayerSAO::setYaw(float yaw) +{ + m_player->setYaw(yaw); + // Force change on client + m_moved = true; +} + +void PlayerSAO::setPitch(float pitch) +{ + m_player->setPitch(pitch); + // Force change on client + m_moved = true; +} + int PlayerSAO::punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher, diff --git a/src/content_sao.h b/src/content_sao.h index 2fd1034eb..e5b89d447 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -147,6 +147,8 @@ public: void setBasePosition(const v3f &position); void setPos(v3f pos); void moveTo(v3f pos, bool continuous); + void setYaw(float); + void setPitch(float); /* Interaction interface diff --git a/src/scriptapi_object.cpp b/src/scriptapi_object.cpp index ba72840c0..a0f93cbba 100644 --- a/src/scriptapi_object.cpp +++ b/src/scriptapi_object.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scriptapi_entity.h" #include "scriptapi_common.h" - /* ObjectRef */ @@ -582,6 +581,30 @@ int ObjectRef::l_get_look_yaw(lua_State *L) return 1; } +// set_look_pitch(self, radians) +int ObjectRef::l_set_look_pitch(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if(co == NULL) return 0; + float pitch = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setPitch(pitch); + return 1; +} + +// set_look_yaw(self, radians) +int ObjectRef::l_set_look_yaw(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if(co == NULL) return 0; + float yaw = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setYaw(yaw); + return 1; +} + // set_inventory_formspec(self, formspec) int ObjectRef::l_set_inventory_formspec(lua_State *L) { @@ -755,6 +778,8 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_look_dir), luamethod(ObjectRef, get_look_pitch), luamethod(ObjectRef, get_look_yaw), + luamethod(ObjectRef, set_look_yaw), + luamethod(ObjectRef, set_look_pitch), luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_player_control), diff --git a/src/scriptapi_object.h b/src/scriptapi_object.h index ba1e7db39..a37abbb78 100644 --- a/src/scriptapi_object.h +++ b/src/scriptapi_object.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -169,6 +169,12 @@ private: // get_look_yaw(self) static int l_get_look_yaw(lua_State *L); + // set_look_pitch(self, radians) + static int l_set_look_pitch(lua_State *L); + + // set_look_yaw(self, radians) + static int l_set_look_yaw(lua_State *L); + // set_inventory_formspec(self, formspec) static int l_set_inventory_formspec(lua_State *L); -- cgit v1.2.3 From adc52f3f3c041e5914f665b6f96d07f49bbb6487 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 21 Mar 2013 20:04:00 +0200 Subject: lua_api.txt: Document paths, games and common mod loading --- doc/lua_api.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 7d8fa149f..19fa81473 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -27,6 +27,36 @@ Startup Mods are loaded during server startup from the mod load paths by running the init.lua scripts in a shared environment. +Paths +----- +RUN_IN_PLACE=1: (Windows release, local build) + $path_user: Linux: + Windows: + $path_share: Linux: + Windows: + +RUN_IN_PLACE=0: (Linux release) + $path_share: Linux: /usr/share/minetest + Windows: /minetest-0.4.x + $path_user: Linux: ~/.minetest + Windows: C:/users//AppData/minetest (maybe) + +Games +----- +Games are looked up from: + $path_share/games/gameid/ + $path_user/games/gameid/ +where gameid is unique to each game. + +The game directory contains the file game.conf, which contains these fields: + name = + common_mods = +eg. + name = Minetest + common_mods = bucket, default, doors, fire, stairs + +Common mods are loaded from the pseudo-game "common". + Mod load path ------------- Generic: -- cgit v1.2.3 From c2250d95c4da368d1535794a1c7f2092ce479d7a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 21 Mar 2013 21:42:23 +0200 Subject: Support game-specific minetest.conf --- doc/lua_api.txt | 3 +++ src/defaultsettings.cpp | 9 +++++++++ src/defaultsettings.h | 1 + src/server.cpp | 8 ++++++++ src/subgame.cpp | 6 ++++++ src/subgame.h | 3 +++ 6 files changed, 30 insertions(+) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 19fa81473..af8b1cc9a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -57,6 +57,9 @@ eg. Common mods are loaded from the pseudo-game "common". +The game directory can contain the file minetest.conf, which will be used +to set default settings when running the particular game. + Mod load path ------------- Generic: diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index b0ae271ce..25edffe32 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -243,3 +243,12 @@ void set_default_settings(Settings *settings) } +void override_default_settings(Settings *settings, Settings *from) +{ + std::vector names = from->getNames(); + for(size_t i=0; isetDefault(name, from->get(name)); + } +} + diff --git a/src/defaultsettings.h b/src/defaultsettings.h index 37e3f717f..00aacad87 100644 --- a/src/defaultsettings.h +++ b/src/defaultsettings.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Settings; void set_default_settings(Settings *settings); +void override_default_settings(Settings *settings, Settings *from); #endif diff --git a/src/server.cpp b/src/server.cpp index 2dcab63b8..f77ac6ad9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -58,6 +58,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/mathconstants.h" #include "rollback.h" #include "util/serialize.h" +#include "defaultsettings.h" void * ServerThread::Thread() { @@ -687,6 +688,13 @@ Server::Server( infostream<<"- config: "< Date: Sat, 23 Mar 2013 19:17:00 +0100 Subject: 6d facedir --- doc/lua_api.txt | 4 ++ src/content_mapblock.cpp | 114 +++++++++++++++++++++++++++------ src/mapblock_mesh.cpp | 163 +++++++++++++++++++++++++++++++++-------------- src/mapnode.cpp | 136 ++++++++++++++++++++++++++++++++++----- src/tile.h | 4 +- 5 files changed, 335 insertions(+), 86 deletions(-) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index af8b1cc9a..3d47785ba 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -310,6 +310,10 @@ param2 is reserved for the engine when any of these are used: paramtype2 == "facedir" ^ The rotation of the node is stored in param2. Furnaces and chests are rotated this way. Can be made by using minetest.dir_to_facedir(). + Values range 0 - 23 + facedir modulo 4 = axisdir + 0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y- + facedir's two less significant bits are rotation around the axis Nodes can also contain extra data. See "Node Metadata". diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 0fbdda303..84d5f067c 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -42,14 +42,16 @@ with this program; if not, write to the Free Software Foundation, Inc., // (compatible with ContentFeatures). If you specified 0,0,1,1 // for each face, that would be the same as passing NULL. void makeCuboid(MeshCollector *collector, const aabb3f &box, - const TileSpec *tiles, int tilecount, + TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc) { assert(tilecount >= 1 && tilecount <= 6); v3f min = box.MinEdge; v3f max = box.MaxEdge; - + + + if(txc == NULL) { static const f32 txc_default[24] = { @@ -97,15 +99,70 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]), }; - for(s32 j=0; j<24; j++) + v2f t; + for(int i = 0; i < tilecount; i++) + { + switch (tiles[i].rotation) + { + case 0: + break; + case 1: //R90 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + break; + case 2: //R180 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0)); + break; + case 3: //R270 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + break; + case 4: //FXR90 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + + tiles[i].texture.pos.Y += tiles[i].texture.size.Y; + tiles[i].texture.size.Y *= -1; + break; + case 5: //FXR270 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + t=vertices[i*4].TCoords; + tiles[i].texture.pos.Y += tiles[i].texture.size.Y; + tiles[i].texture.size.Y *= -1; + break; + case 6: //FYR90 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + tiles[i].texture.pos.X += tiles[i].texture.size.X; + tiles[i].texture.size.X *= -1; + break; + case 7: //FYR270 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + tiles[i].texture.pos.X += tiles[i].texture.size.X; + tiles[i].texture.size.X *= -1; + break; + case 8: //FX + tiles[i].texture.pos.Y += tiles[i].texture.size.Y; + tiles[i].texture.size.Y *= -1; + break; + case 9: //FY + tiles[i].texture.pos.X += tiles[i].texture.size.X; + tiles[i].texture.size.X *= -1; + break; + default: + break; + } + } + for(s32 j=0; j<24; j++) { int tileindex = MYMIN(j/4, tilecount-1); vertices[j].TCoords *= tiles[tileindex].texture.size; vertices[j].TCoords += tiles[tileindex].texture.pos; } - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector for(s32 j=0; j<24; j+=4) { @@ -1154,14 +1211,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16(0, 0, 1), v3s16(0, 0, -1) }; - TileSpec tiles[6]; - for(int i = 0; i < 6; i++) - { - // Handles facedir rotation for textures - tiles[i] = getNodeTile(n, p, tile_dirs[i], data); - } - + u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source)); @@ -1172,17 +1223,43 @@ void mapblock_mesh_generate_special(MeshMakeData *data, i = boxes.begin(); i != boxes.end(); i++) { + for(int j = 0; j < 6; j++) + { + // Handles facedir rotation for textures + tiles[j] = getNodeTile(n, p, tile_dirs[j], data); + } aabb3f box = *i; box.MinEdge += pos; box.MaxEdge += pos; + + f32 temp; + if (box.MinEdge.X > box.MaxEdge.X) + { + temp=box.MinEdge.X; + box.MinEdge.X=box.MaxEdge.X; + box.MaxEdge.X=temp; + } + if (box.MinEdge.Y > box.MaxEdge.Y) + { + temp=box.MinEdge.Y; + box.MinEdge.Y=box.MaxEdge.Y; + box.MaxEdge.Y=temp; + } + if (box.MinEdge.Z > box.MaxEdge.Z) + { + temp=box.MinEdge.Z; + box.MinEdge.Z=box.MaxEdge.Z; + box.MaxEdge.Z=temp; + } + // // Compute texture coords - f32 tx1 = (i->MinEdge.X/BS)+0.5; - f32 ty1 = (i->MinEdge.Y/BS)+0.5; - f32 tz1 = (i->MinEdge.Z/BS)+0.5; - f32 tx2 = (i->MaxEdge.X/BS)+0.5; - f32 ty2 = (i->MaxEdge.Y/BS)+0.5; - f32 tz2 = (i->MaxEdge.Z/BS)+0.5; + f32 tx1 = (box.MinEdge.X/BS)+0.5; + f32 ty1 = (box.MinEdge.Y/BS)+0.5; + f32 tz1 = (box.MinEdge.Z/BS)+0.5; + f32 tx2 = (box.MaxEdge.X/BS)+0.5; + f32 ty2 = (box.MaxEdge.Y/BS)+0.5; + f32 tz2 = (box.MaxEdge.Z/BS)+0.5; f32 txc[24] = { // up tx1, 1-tz2, tx2, 1-tz1, @@ -1197,7 +1274,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // front tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, tiles, 6, c, txc); } break;} diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index d098065f8..f68a79e41 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -455,6 +455,80 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, v3f vertex_pos[4]; v3s16 vertex_dirs[4]; getNodeVertexDirs(dir, vertex_dirs); + v3s16 t; + switch (tile.rotation) + { + case 0: + break; + case 1: //R90 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[3]; + vertex_dirs[3] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[1]; + vertex_dirs[1] = t; + break; + case 2: //R180 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[2]; + vertex_dirs[2] = t; + t = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[3]; + vertex_dirs[3] = t; + break; + case 3: //R270 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[3]; + vertex_dirs[3] = t; + break; + case 4: //FXR90 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[3]; + vertex_dirs[3] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[1]; + vertex_dirs[1] = t; + tile.texture.pos.Y += tile.texture.size.Y; + tile.texture.size.Y *= -1; + break; + case 5: //FXR270 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[3]; + vertex_dirs[3] = t; + tile.texture.pos.Y += tile.texture.size.Y; + tile.texture.size.Y *= -1; + break; + case 6: //FYR90 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[3]; + vertex_dirs[3] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[1]; + vertex_dirs[1] = t; + tile.texture.pos.X += tile.texture.size.X; + tile.texture.size.X *= -1; + break; + case 7: //FYR270 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[3]; + vertex_dirs[3] = t; + tile.texture.pos.X += tile.texture.size.X; + tile.texture.size.X *= -1; + break; + case 8: //FX + tile.texture.pos.Y += tile.texture.size.Y; + tile.texture.size.Y *= -1; + break; + case 9: //FY + tile.texture.pos.X += tile.texture.size.X; + tile.texture.size.X *= -1; + break; + default: + break; + } for(u16 i=0; i<4; i++) { vertex_pos[i] = v3f( @@ -601,60 +675,50 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) // 5 = (0,0,-1) // 6 = (0,-1,0) // 7 = (-1,0,0) - u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7; + u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2; // Get rotation for things like chests u8 facedir = mn.getFaceDir(ndef); - assert(facedir <= 3); - - static const u8 dir_to_tile[4 * 8] = + assert(facedir <= 23); + static const u16 dir_to_tile[24 * 16] = { - // 0 +X +Y +Z 0 -Z -Y -X - 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0 - 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1 - 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2 - 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3 + // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation + 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3 + 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 , + 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 , + 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 , + + 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7 + 0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 , + 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 , + 0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 , + + 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11 + 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 , + 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 , + 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 , + + 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15 + 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 , + 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 , + 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 , + + 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19 + 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 , + 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 , + 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 , + + 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23 + 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 , + 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 , + 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2 + }; - u8 tileindex = dir_to_tile[facedir*8 + dir_i]; - - // If not rotated or is side tile, we're done - if(facedir == 0 || (tileindex != 0 && tileindex != 1)) - return getNodeTileN(mn, p, tileindex, data); - - // This is the top or bottom tile, and it shall be rotated; thus rotate it - TileSpec spec = getNodeTileN(mn, p, tileindex, data); - if(tileindex == 0){ - if(facedir == 1){ // -90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR270"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - else if(facedir == 2){ // 180 - spec.texture.pos += spec.texture.size; - spec.texture.size *= -1; - } - else if(facedir == 3){ // 90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR90"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - } - else if(tileindex == 1){ - if(facedir == 1){ // -90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR90"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - else if(facedir == 2){ // 180 - spec.texture.pos += spec.texture.size; - spec.texture.size *= -1; - } - else if(facedir == 3){ // 90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR270"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - } + u16 tile_index=facedir*16 + dir_i; + TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data); + spec.rotation=dir_to_tile[tile_index + 1]; + std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); + spec.texture = data->m_gamedef->tsrc()->getTexture(name); return spec; } @@ -794,6 +858,7 @@ static void updateFastFaceRow( && next_lights[2] == lights[2] && next_lights[3] == lights[3] && next_tile == tile + && tile.rotation == 0 && next_light_source == light_source) { next_is_different = false; diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 0513e688c..bba845fcc 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -107,7 +107,7 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const { const ContentFeatures &f = nodemgr->get(*this); if(f.param_type_2 == CPT2_FACEDIR) - return getParam2() & 0x03; + return getParam2() & 0x1F; return 0; } @@ -140,29 +140,131 @@ static std::vector transformNodeBox(const MapNode &n, { const std::vector &fixed = nodebox.fixed; int facedir = n.getFaceDir(nodemgr); + u8 axisdir = facedir>>2; + facedir&=0x03; for(std::vector::const_iterator i = fixed.begin(); i != fixed.end(); i++) { aabb3f box = *i; - if(facedir == 1) + switch (axisdir) { - box.MinEdge.rotateXZBy(-90); - box.MaxEdge.rotateXZBy(-90); - box.repair(); - } - else if(facedir == 2) - { - box.MinEdge.rotateXZBy(180); - box.MaxEdge.rotateXZBy(180); - box.repair(); - } - else if(facedir == 3) - { - box.MinEdge.rotateXZBy(90); - box.MaxEdge.rotateXZBy(90); - box.repair(); + case 0: + if(facedir == 1) + { + box.MinEdge.rotateXZBy(-90); + box.MaxEdge.rotateXZBy(-90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXZBy(180); + box.MaxEdge.rotateXZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXZBy(90); + box.MaxEdge.rotateXZBy(90); + } + break; + case 1: // z+ + box.MinEdge.rotateYZBy(90); + box.MaxEdge.rotateYZBy(90); + if(facedir == 1) + { + box.MinEdge.rotateXYBy(90); + box.MaxEdge.rotateXYBy(90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXYBy(180); + box.MaxEdge.rotateXYBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXYBy(-90); + box.MaxEdge.rotateXYBy(-90); + } + break; + case 2: //z- + box.MinEdge.rotateYZBy(-90); + box.MaxEdge.rotateYZBy(-90); + if(facedir == 1) + { + box.MinEdge.rotateXYBy(-90); + box.MaxEdge.rotateXYBy(-90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXYBy(180); + box.MaxEdge.rotateXYBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXYBy(90); + box.MaxEdge.rotateXYBy(90); + } + break; + case 3: //x+ + box.MinEdge.rotateXYBy(-90); + box.MaxEdge.rotateXYBy(-90); + if(facedir == 1) + { + box.MinEdge.rotateYZBy(90); + box.MaxEdge.rotateYZBy(90); + } + else if(facedir == 2) + { + box.MinEdge.rotateYZBy(180); + box.MaxEdge.rotateYZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateYZBy(-90); + box.MaxEdge.rotateYZBy(-90); + } + break; + case 4: //x- + box.MinEdge.rotateXYBy(90); + box.MaxEdge.rotateXYBy(90); + if(facedir == 1) + { + box.MinEdge.rotateYZBy(-90); + box.MaxEdge.rotateYZBy(-90); + } + else if(facedir == 2) + { + box.MinEdge.rotateYZBy(180); + box.MaxEdge.rotateYZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateYZBy(90); + box.MaxEdge.rotateYZBy(90); + } + break; + case 5: + box.MinEdge.rotateXYBy(-180); + box.MaxEdge.rotateXYBy(-180); + if(facedir == 1) + { + box.MinEdge.rotateXZBy(90); + box.MaxEdge.rotateXZBy(90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXZBy(180); + box.MaxEdge.rotateXZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXZBy(-90); + box.MaxEdge.rotateXZBy(-90); + } + break; + default: + break; } + box.repair(); boxes.push_back(box); } } diff --git a/src/tile.h b/src/tile.h index 07e5bcb56..c5c7f9303 100644 --- a/src/tile.h +++ b/src/tile.h @@ -205,7 +205,8 @@ struct TileSpec texture == other.texture && alpha == other.alpha && material_type == other.material_type && - material_flags == other.material_flags + material_flags == other.material_flags && + rotation == other.rotation ); } @@ -264,6 +265,7 @@ struct TileSpec // Animation parameters u8 animation_frame_count; u16 animation_frame_length_ms; + u8 rotation; }; #endif -- cgit v1.2.3 From e1ff5b13619666e5b987ecf4faaf294400ffd979 Mon Sep 17 00:00:00 2001 From: Jeija Date: Wed, 23 Jan 2013 18:32:02 +0100 Subject: Allow spawning particles from the server, from lua Spawn single particles or make use of ParticleSpawner for many randomly spawned particles. Accessible in Lua using minetest.spawn_particle and minetest.add_particlespawner. Increase Protocol Version to 17. Conflicts: src/clientserver.h --- doc/lua_api.txt | 31 +++++ src/CMakeLists.txt | 1 + src/client.cpp | 83 ++++++++++++ src/client.h | 34 ++++- src/clientserver.h | 44 ++++++ src/game.cpp | 49 ++++++- src/particles.cpp | 320 +++++++++++++++++++++++++++++++++++++------- src/particles.h | 72 +++++++++- src/scriptapi.cpp | 4 + src/scriptapi_particles.cpp | 143 ++++++++++++++++++++ src/scriptapi_particles.h | 32 +++++ src/server.cpp | 232 ++++++++++++++++++++++++++++++++ src/server.h | 69 ++++++++++ 13 files changed, 1059 insertions(+), 55 deletions(-) create mode 100644 src/scriptapi_particles.cpp create mode 100644 src/scriptapi_particles.h (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 3d47785ba..4edca5adb 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1028,6 +1028,37 @@ minetest.get_ban_description(ip_or_name) -> ban description (string) minetest.ban_player(name) -> ban a player minetest.unban_player_or_ip(name) -> unban player or IP address +Particles: +minetest.add_particle(pos, velocity, acceleration, expirationtime, + size, collisiondetection, texture, playername) +^ Spawn particle at pos with velocity and acceleration +^ Disappears after expirationtime seconds +^ collisiondetection: if true collides with physical objects +^ Uses texture (string) +^ Playername is optional, if specified spawns particle only on the player's client + +minetest.add_particlespawner(amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, texture, playername) +^ Add a particlespawner, an object that spawns an amount of particles over time seconds +^ The particle's properties are random values in between the boundings: +^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration), +^ minsize/maxsize, minexptime/maxexptime (expirationtime) +^ collisiondetection: if true uses collisiondetection +^ Uses texture (string) +^ Playername is optional, if specified spawns particle only on the player's client +^ If time is 0 has infinite lifespan and spawns the amount on a per-second base +^ Returns and id + +minetest.delete_particlespawner(id, player) +^ Delete ParticleSpawner with id (return value from add_particlespawner) +^ If playername is specified, only deletes on the player's client, +^ otherwise on all clients + Random: minetest.get_connected_players() -> list of ObjectRefs minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8c2890f76..8f0cc1ac5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -219,6 +219,7 @@ set(common_SRCS scriptapi_object.cpp scriptapi_nodemeta.cpp scriptapi_inventory.cpp + scriptapi_particles.cpp scriptapi.cpp script.cpp log.cpp diff --git a/src/client.cpp b/src/client.cpp index 2ccace842..f27f95d98 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1936,6 +1936,89 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.show_formspec.formname = new std::string(formname); m_client_event_queue.push_back(event); } + else if(command == TOCLIENT_SPAWN_PARTICLE) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + v3f pos = readV3F1000(is); + v3f vel = readV3F1000(is); + v3f acc = readV3F1000(is); + float expirationtime = readF1000(is); + float size = readF1000(is); + bool collisiondetection = readU8(is); + std::string texture = deSerializeLongString(is); + + ClientEvent event; + event.type = CE_SPAWN_PARTICLE; + event.spawn_particle.pos = new v3f (pos); + event.spawn_particle.vel = new v3f (vel); + event.spawn_particle.acc = new v3f (acc); + + event.spawn_particle.expirationtime = expirationtime; + event.spawn_particle.size = size; + event.add_particlespawner.collisiondetection = + collisiondetection; + event.spawn_particle.texture = new std::string(texture); + + m_client_event_queue.push_back(event); + } + else if(command == TOCLIENT_ADD_PARTICLESPAWNER) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + u16 amount = readU16(is); + float spawntime = readF1000(is); + v3f minpos = readV3F1000(is); + v3f maxpos = readV3F1000(is); + v3f minvel = readV3F1000(is); + v3f maxvel = readV3F1000(is); + v3f minacc = readV3F1000(is); + v3f maxacc = readV3F1000(is); + float minexptime = readF1000(is); + float maxexptime = readF1000(is); + float minsize = readF1000(is); + float maxsize = readF1000(is); + bool collisiondetection = readU8(is); + std::string texture = deSerializeLongString(is); + u32 id = readU32(is); + + ClientEvent event; + event.type = CE_ADD_PARTICLESPAWNER; + event.add_particlespawner.amount = amount; + event.add_particlespawner.spawntime = spawntime; + + event.add_particlespawner.minpos = new v3f (minpos); + event.add_particlespawner.maxpos = new v3f (maxpos); + event.add_particlespawner.minvel = new v3f (minvel); + event.add_particlespawner.maxvel = new v3f (maxvel); + event.add_particlespawner.minacc = new v3f (minacc); + event.add_particlespawner.maxacc = new v3f (maxacc); + + event.add_particlespawner.minexptime = minexptime; + event.add_particlespawner.maxexptime = maxexptime; + event.add_particlespawner.minsize = minsize; + event.add_particlespawner.maxsize = maxsize; + event.add_particlespawner.collisiondetection = collisiondetection; + event.add_particlespawner.texture = new std::string(texture); + event.add_particlespawner.id = id; + + m_client_event_queue.push_back(event); + } + else if(command == TOCLIENT_DELETE_PARTICLESPAWNER) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + u32 id = readU16(is); + + ClientEvent event; + event.type = CE_DELETE_PARTICLESPAWNER; + event.delete_particlespawner.id = id; + + m_client_event_queue.push_back(event); + } else { infostream<<"Client: Ignoring unknown command " diff --git a/src/client.h b/src/client.h index 570a6b3b0..d476a1d51 100644 --- a/src/client.h +++ b/src/client.h @@ -157,7 +157,10 @@ enum ClientEventType CE_PLAYER_FORCE_MOVE, CE_DEATHSCREEN, CE_TEXTURES_UPDATED, - CE_SHOW_FORMSPEC + CE_SHOW_FORMSPEC, + CE_SPAWN_PARTICLE, + CE_ADD_PARTICLESPAWNER, + CE_DELETE_PARTICLESPAWNER }; struct ClientEvent @@ -185,6 +188,35 @@ struct ClientEvent } show_formspec; struct{ } textures_updated; + struct{ + v3f *pos; + v3f *vel; + v3f *acc; + f32 expirationtime; + f32 size; + bool collisiondetection; + std::string *texture; + } spawn_particle; + struct{ + u16 amount; + f32 spawntime; + v3f *minpos; + v3f *maxpos; + v3f *minvel; + v3f *maxvel; + v3f *minacc; + v3f *maxacc; + f32 minexptime; + f32 maxexptime; + f32 minsize; + f32 maxsize; + bool collisiondetection; + std::string *texture; + u32 id; + } add_particlespawner; + struct{ + u32 id; + } delete_particlespawner; }; }; diff --git a/src/clientserver.h b/src/clientserver.h index 6d830b92b..535fc04d8 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -82,6 +82,9 @@ SharedBuffer makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed); PROTOCOL_VERSION 17: Serialization format change: include backface_culling flag in TileDef Added rightclickable field in nodedef + TOCLIENT_SPAWN_PARTICLE + TOCLIENT_ADD_PARTICLESPAWNER + TOCLIENT_DELETE_PARTICLESPAWNER */ #define LATEST_PROTOCOL_VERSION 17 @@ -359,6 +362,7 @@ enum ToClientCommand u8[len] name [2] serialized inventory */ + TOCLIENT_SHOW_FORMSPEC = 0x44, /* [0] u16 command @@ -384,6 +388,46 @@ enum ToClientCommand f1000 movement_liquid_sink f1000 movement_gravity */ + + TOCLIENT_SPAWN_PARTICLE = 0x46, + /* + u16 command + v3f1000 pos + v3f1000 velocity + v3f1000 acceleration + f1000 expirationtime + f1000 size + u8 bool collisiondetection + u32 len + u8[len] texture + */ + + TOCLIENT_ADD_PARTICLESPAWNER = 0x47, + /* + u16 command + u16 amount + f1000 spawntime + v3f1000 minpos + v3f1000 maxpos + v3f1000 minvel + v3f1000 maxvel + v3f1000 minacc + v3f1000 maxacc + f1000 minexptime + f1000 maxexptime + f1000 minsize + f1000 maxsize + u8 bool collisiondetection + u32 len + u8[len] texture + u32 id + */ + + TOCLIENT_DELETE_PARTICLESPAWNER = 0x48, + /* + u16 command + u32 id + */ }; enum ToServerCommand diff --git a/src/game.cpp b/src/game.cpp index 1ae29b13b..58ca654a0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2186,6 +2186,47 @@ void the_game( { update_wielded_item_trigger = true; } + else if(event.type == CE_SPAWN_PARTICLE) + { + LocalPlayer* player = client.getEnv().getLocalPlayer(); + AtlasPointer ap = + gamedef->tsrc()->getTexture(*(event.spawn_particle.texture)); + + new Particle(gamedef, smgr, player, client.getEnv(), + *event.spawn_particle.pos, + *event.spawn_particle.vel, + *event.spawn_particle.acc, + event.spawn_particle.expirationtime, + event.spawn_particle.size, + event.spawn_particle.collisiondetection, ap); + } + else if(event.type == CE_ADD_PARTICLESPAWNER) + { + LocalPlayer* player = client.getEnv().getLocalPlayer(); + AtlasPointer ap = + gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture)); + + new ParticleSpawner(gamedef, smgr, player, + event.add_particlespawner.amount, + event.add_particlespawner.spawntime, + *event.add_particlespawner.minpos, + *event.add_particlespawner.maxpos, + *event.add_particlespawner.minvel, + *event.add_particlespawner.maxvel, + *event.add_particlespawner.minacc, + *event.add_particlespawner.maxacc, + event.add_particlespawner.minexptime, + event.add_particlespawner.maxexptime, + event.add_particlespawner.minsize, + event.add_particlespawner.maxsize, + event.add_particlespawner.collisiondetection, + ap, + event.add_particlespawner.id); + } + else if(event.type == CE_DELETE_PARTICLESPAWNER) + { + delete_particlespawner (event.delete_particlespawner.id); + } } } @@ -2415,7 +2456,8 @@ void the_game( const ContentFeatures &features = client.getNodeDefManager()->get(n); addPunchingParticles - (gamedef, smgr, player, nodepos, features.tiles); + (gamedef, smgr, player, client.getEnv(), + nodepos, features.tiles); } } @@ -2453,7 +2495,8 @@ void the_game( const ContentFeatures &features = client.getNodeDefManager()->get(wasnode); addDiggingParticles - (gamedef, smgr, player, nodepos, features.tiles); + (gamedef, smgr, player, client.getEnv(), + nodepos, features.tiles); } dig_time = 0; @@ -2734,6 +2777,7 @@ void the_game( */ allparticles_step(dtime, client.getEnv()); + allparticlespawners_step(dtime, client.getEnv()); /* Fog @@ -3220,6 +3264,7 @@ void the_game( clouds->drop(); if(gui_chat_console) gui_chat_console->drop(); + clear_particles (); /* Draw a "shutting down" screen, which will be shown while the map diff --git a/src/particles.cpp b/src/particles.cpp index fef21d91b..0445d7726 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -32,19 +32,34 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientmap.h" #include "mapnode.h" +/* + Utility +*/ + +v3f random_v3f(v3f min, v3f max) +{ + return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X, + rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y, + rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z); +} + +std::vector all_particles; +std::map all_particlespawners; + Particle::Particle( IGameDef *gamedef, scene::ISceneManager* smgr, LocalPlayer *player, - s32 id, + ClientEnvironment &env, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, + bool collisiondetection, AtlasPointer ap ): - scene::ISceneNode(smgr->getRootSceneNode(), smgr, id) + scene::ISceneNode(smgr->getRootSceneNode(), smgr) { // Misc m_gamedef = gamedef; @@ -57,7 +72,6 @@ Particle::Particle( m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; m_material.setTexture(0, ap.atlas); m_ap = ap; - m_light = 0; // Particle related @@ -68,10 +82,20 @@ Particle::Particle( m_time = 0; m_player = player; m_size = size; + m_collisiondetection = collisiondetection; - // Irrlicht stuff (TODO) - m_collisionbox = core::aabbox3d(-size/2,-size/2,-size/2,size/2,size/2,size/2); + // Irrlicht stuff + m_collisionbox = core::aabbox3d + (-size/2,-size/2,-size/2,size/2,size/2,size/2); this->setAutomaticCulling(scene::EAC_OFF); + + // Init lighting + updateLight(env); + + // Init model + updateVertices(); + + all_particles.push_back(this); } Particle::~Particle() @@ -82,8 +106,10 @@ void Particle::OnRegisterSceneNode() { if (IsVisible) { - SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); - SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID); + SceneManager->registerNodeForRendering + (this, scene::ESNRP_TRANSPARENT); + SceneManager->registerNodeForRendering + (this, scene::ESNRP_SOLID); } ISceneNode::OnRegisterSceneNode(); @@ -96,45 +122,45 @@ void Particle::render() video::IVideoDriver* driver = SceneManager->getVideoDriver(); driver->setMaterial(m_material); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); - video::SColor c(255, m_light, m_light, m_light); - - video::S3DVertex vertices[4] = - { - video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x0(), m_ap.y1()), - video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y1()), - video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y0()), - video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, c ,m_ap.x0(), m_ap.y0()), - }; - - for(u16 i=0; i<4; i++) - { - vertices[i].Pos.rotateYZBy(m_player->getPitch()); - vertices[i].Pos.rotateXZBy(m_player->getYaw()); - m_box.addInternalPoint(vertices[i].Pos); - vertices[i].Pos += m_pos*BS; - } u16 indices[] = {0,1,2, 2,3,0}; - driver->drawVertexPrimitiveList(vertices, 4, indices, 2, - video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); + driver->drawVertexPrimitiveList(m_vertices, 4, + indices, 2, video::EVT_STANDARD, + scene::EPT_TRIANGLES, video::EIT_16BIT); } void Particle::step(float dtime, ClientEnvironment &env) { - core::aabbox3d box = m_collisionbox; - v3f p_pos = m_pos*BS; - v3f p_velocity = m_velocity*BS; - v3f p_acceleration = m_acceleration*BS; - collisionMoveSimple(&env.getClientMap(), m_gamedef, - BS*0.5, box, - 0, dtime, - p_pos, p_velocity, p_acceleration); - m_pos = p_pos/BS; - m_velocity = p_velocity/BS; - m_acceleration = p_acceleration/BS; m_time += dtime; + if (m_collisiondetection) + { + core::aabbox3d box = m_collisionbox; + v3f p_pos = m_pos*BS; + v3f p_velocity = m_velocity*BS; + v3f p_acceleration = m_acceleration*BS; + collisionMoveSimple(&env.getClientMap(), m_gamedef, + BS*0.5, box, + 0, dtime, + p_pos, p_velocity, p_acceleration); + m_pos = p_pos/BS; + m_velocity = p_velocity/BS; + m_acceleration = p_acceleration/BS; + } + else + { + m_velocity += m_acceleration * dtime; + m_pos += m_velocity * dtime; + } // Update lighting + updateLight(env); + + // Update model + updateVertices(); +} + +void Particle::updateLight(ClientEnvironment &env) +{ u8 light = 0; try{ v3s16 p = v3s16( @@ -151,11 +177,37 @@ void Particle::step(float dtime, ClientEnvironment &env) m_light = decode_light(light); } -std::vector all_particles; +void Particle::updateVertices() +{ + video::SColor c(255, m_light, m_light, m_light); + m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, + c, m_ap.x0(), m_ap.y1()); + m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, + c, m_ap.x1(), m_ap.y1()); + m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, + c, m_ap.x1(), m_ap.y0()); + m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, + c ,m_ap.x0(), m_ap.y0()); + + for(u16 i=0; i<4; i++) + { + m_vertices[i].Pos.rotateYZBy(m_player->getPitch()); + m_vertices[i].Pos.rotateXZBy(m_player->getYaw()); + m_box.addInternalPoint(m_vertices[i].Pos); + m_vertices[i].Pos += m_pos*BS; + } +} + + +/* + Helpers +*/ + void allparticles_step (float dtime, ClientEnvironment &env) { - for(std::vector::iterator i = all_particles.begin(); i != all_particles.end();) + for(std::vector::iterator i = all_particles.begin(); + i != all_particles.end();) { if ((*i)->get_expired()) { @@ -171,22 +223,28 @@ void allparticles_step (float dtime, ClientEnvironment &env) } } -void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]) { for (u16 j = 0; j < 32; j++) // set the amount of particles here { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, env, pos, tiles); } } -void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, + v3s16 pos, const TileSpec tiles[]) { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, env, pos, tiles); } // add a particle of a node // used by digging and punching particles -void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]) { // Texture u8 texid = myrand_range(0,5); @@ -205,7 +263,10 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer ap.pos.Y = ap.y0() + (y1 - ap.y0()) * ((rand()%64)/64.-texsize); // Physics - v3f velocity((rand()%100/50.-1)/1.5, rand()%100/35., (rand()%100/50.-1)/1.5); + v3f velocity( (rand()%100/50.-1)/1.5, + rand()%100/35., + (rand()%100/50.-1)/1.5); + v3f acceleration(0,-9,0); v3f particlepos = v3f( (f32)pos.X+rand()%100/200.-0.25, @@ -213,17 +274,180 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer (f32)pos.Z+rand()%100/200.-0.25 ); - Particle *particle = new Particle( + new Particle( gamedef, smgr, player, - 0, + env, particlepos, velocity, acceleration, rand()%100/100., // expiration time visual_size, + true, ap); +} - all_particles.push_back(particle); +/* + ParticleSpawner +*/ + +ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player, + u16 amount, float time, + v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, + float minexptime, float maxexptime, float minsize, float maxsize, + bool collisiondetection, AtlasPointer ap, u32 id) +{ + m_gamedef = gamedef; + m_smgr = smgr; + m_player = player; + m_amount = amount; + m_spawntime = time; + m_minpos = minpos; + m_maxpos = maxpos; + m_minvel = minvel; + m_maxvel = maxvel; + m_minacc = minacc; + m_maxacc = maxacc; + m_minexptime = minexptime; + m_maxexptime = maxexptime; + m_minsize = minsize; + m_maxsize = maxsize; + m_collisiondetection = collisiondetection; + m_ap = ap; + m_time = 0; + + for (u16 i = 0; i<=m_amount; i++) + { + float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime; + m_spawntimes.push_back(spawntime); + } + + all_particlespawners.insert(std::pair(id, this)); +} + +ParticleSpawner::~ParticleSpawner() {} + +void ParticleSpawner::step(float dtime, ClientEnvironment &env) +{ + m_time += dtime; + + if (m_spawntime != 0) // Spawner exists for a predefined timespan + { + for(std::vector::iterator i = m_spawntimes.begin(); + i != m_spawntimes.end();) + { + if ((*i) <= m_time && m_amount > 0) + { + m_amount--; + + v3f pos = random_v3f(m_minpos, m_maxpos); + v3f vel = random_v3f(m_minvel, m_maxvel); + v3f acc = random_v3f(m_minacc, m_maxacc); + float exptime = rand()/(float)RAND_MAX + *(m_maxexptime-m_minexptime) + +m_minexptime; + float size = rand()/(float)RAND_MAX + *(m_maxsize-m_minsize) + +m_minsize; + + new Particle( + m_gamedef, + m_smgr, + m_player, + env, + pos, + vel, + acc, + exptime, + size, + m_collisiondetection, + m_ap); + m_spawntimes.erase(i); + } + else + { + i++; + } + } + } + else // Spawner exists for an infinity timespan, spawn on a per-second base + { + for (int i = 0; i <= m_amount; i++) + { + if (rand()/(float)RAND_MAX < dtime) + { + v3f pos = random_v3f(m_minpos, m_maxpos); + v3f vel = random_v3f(m_minvel, m_maxvel); + v3f acc = random_v3f(m_minacc, m_maxacc); + float exptime = rand()/(float)RAND_MAX + *(m_maxexptime-m_minexptime) + +m_minexptime; + float size = rand()/(float)RAND_MAX + *(m_maxsize-m_minsize) + +m_minsize; + + new Particle( + m_gamedef, + m_smgr, + m_player, + env, + pos, + vel, + acc, + exptime, + size, + m_collisiondetection, + m_ap); + } + } + } +} + +void allparticlespawners_step (float dtime, ClientEnvironment &env) +{ + for(std::map::iterator i = + all_particlespawners.begin(); + i != all_particlespawners.end();) + { + if (i->second->get_expired()) + { + delete i->second; + all_particlespawners.erase(i++); + } + else + { + i->second->step(dtime, env); + i++; + } + } +} + +void delete_particlespawner (u32 id) +{ + if (all_particlespawners.find(id) != all_particlespawners.end()) + { + delete all_particlespawners.find(id)->second; + all_particlespawners.erase(id); + } +} + +void clear_particles () +{ + for(std::map::iterator i = + all_particlespawners.begin(); + i != all_particlespawners.end();) + { + delete i->second; + all_particlespawners.erase(i++); + } + + for(std::vector::iterator i = + all_particles.begin(); + i != all_particles.end();) + { + (*i)->remove(); + delete *i; + all_particles.erase(i); + } } diff --git a/src/particles.h b/src/particles.h index b317549e7..308da551f 100644 --- a/src/particles.h +++ b/src/particles.h @@ -35,12 +35,13 @@ class Particle : public scene::ISceneNode IGameDef* gamedef, scene::ISceneManager* mgr, LocalPlayer *player, - s32 id, + ClientEnvironment &env, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, + bool collisiondetection, AtlasPointer texture ); ~Particle(); @@ -69,6 +70,10 @@ class Particle : public scene::ISceneNode { return m_expiration < m_time; } private: + void updateLight(ClientEnvironment &env); + void updateVertices(); + + video::S3DVertex m_vertices[4]; float m_time; float m_expiration; @@ -87,12 +92,71 @@ private: float m_size; AtlasPointer m_ap; u8 m_light; + bool m_collisiondetection; +}; + +class ParticleSpawner +{ + public: + ParticleSpawner(IGameDef* gamedef, + scene::ISceneManager *smgr, + LocalPlayer *player, + u16 amount, + float time, + v3f minp, v3f maxp, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, + AtlasPointer ap, + u32 id); + + ~ParticleSpawner(); + + void step(float dtime, ClientEnvironment &env); + + bool get_expired () + { return (m_amount <= 0) && m_spawntime != 0; } + + private: + float m_time; + IGameDef *m_gamedef; + scene::ISceneManager *m_smgr; + LocalPlayer *m_player; + u16 m_amount; + float m_spawntime; + v3f m_minpos; + v3f m_maxpos; + v3f m_minvel; + v3f m_maxvel; + v3f m_minacc; + v3f m_maxacc; + float m_minexptime; + float m_maxexptime; + float m_minsize; + float m_maxsize; + AtlasPointer m_ap; + std::vector m_spawntimes; + bool m_collisiondetection; }; void allparticles_step (float dtime, ClientEnvironment &env); +void allparticlespawners_step (float dtime, ClientEnvironment &env); + +void delete_particlespawner (u32 id); +void clear_particles (); + +void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]); + +void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]); -void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); -void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); -void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); +void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]); #endif diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 13696eabf..29494ff69 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -44,6 +44,7 @@ extern "C" { #include "scriptapi_item.h" #include "scriptapi_content.h" #include "scriptapi_craft.h" +#include "scriptapi_particles.h" /*****************************************************************************/ /* Mod related */ @@ -1089,6 +1090,9 @@ static const struct luaL_Reg minetest_f [] = { {"get_all_craft_recipes", l_get_all_craft_recipes}, {"rollback_get_last_node_actor", l_rollback_get_last_node_actor}, {"rollback_revert_actions_by", l_rollback_revert_actions_by}, + {"add_particle", l_add_particle}, + {"add_particlespawner", l_add_particlespawner}, + {"delete_particlespawner", l_delete_particlespawner}, {NULL, NULL} }; diff --git a/src/scriptapi_particles.cpp b/src/scriptapi_particles.cpp new file mode 100644 index 000000000..dc9b3776e --- /dev/null +++ b/src/scriptapi_particles.cpp @@ -0,0 +1,143 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_particles.h" +#include "server.h" +#include "script.h" +#include "scriptapi_types.h" +#include "scriptapi_common.h" + +// add_particle(pos, velocity, acceleration, expirationtime, +// size, collisiondetection, texture, player) +// pos/velocity/acceleration = {x=num, y=num, z=num} +// expirationtime = num (seconds) +// size = num +// texture = e.g."default_wood.png" +int l_add_particle(lua_State *L) +{ + // Get server from registry + Server *server = get_server(L); + // Get parameters + v3f pos = check_v3f(L, 1); + v3f vel = check_v3f(L, 2); + v3f acc = check_v3f(L, 3); + float expirationtime = luaL_checknumber(L, 4); + float size = luaL_checknumber(L, 5); + bool collisiondetection = lua_toboolean(L, 6); + std::string texture = luaL_checkstring(L, 7); + + if (lua_gettop(L) == 8) // only spawn for a single player + { + const char *playername = luaL_checkstring(L, 8); + server->spawnParticle(playername, + pos, vel, acc, expirationtime, + size, collisiondetection, texture); + } + else // spawn for all players + { + server->spawnParticleAll(pos, vel, acc, + expirationtime, size, collisiondetection, texture); + } + return 1; +} + +// add_particlespawner(amount, time, +// minpos, maxpos, +// minvel, maxvel, +// minacc, maxacc, +// minexptime, maxexptime, +// minsize, maxsize, +// collisiondetection, +// texture, +// player) +// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num} +// minexptime/maxexptime = num (seconds) +// minsize/maxsize = num +// collisiondetection = bool +// texture = e.g."default_wood.png" +int l_add_particlespawner(lua_State *L) +{ + // Get server from registry + Server *server = get_server(L); + // Get parameters + u16 amount = luaL_checknumber(L, 1); + float time = luaL_checknumber(L, 2); + v3f minpos = check_v3f(L, 3); + v3f maxpos = check_v3f(L, 4); + v3f minvel = check_v3f(L, 5); + v3f maxvel = check_v3f(L, 6); + v3f minacc = check_v3f(L, 7); + v3f maxacc = check_v3f(L, 8); + float minexptime = luaL_checknumber(L, 9); + float maxexptime = luaL_checknumber(L, 10); + float minsize = luaL_checknumber(L, 11); + float maxsize = luaL_checknumber(L, 12); + bool collisiondetection = lua_toboolean(L, 13); + std::string texture = luaL_checkstring(L, 14); + + if (lua_gettop(L) == 15) // only spawn for a single player + { + const char *playername = luaL_checkstring(L, 15); + u32 id = server->addParticleSpawner(playername, + amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, + texture); + lua_pushnumber(L, id); + } + else // spawn for all players + { + u32 id = server->addParticleSpawnerAll( amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, + texture); + lua_pushnumber(L, id); + } + return 1; +} + +// delete_particlespawner(id, player) +// player (string) is optional +int l_delete_particlespawner(lua_State *L) +{ + // Get server from registry + Server *server = get_server(L); + // Get parameters + u32 id = luaL_checknumber(L, 1); + + if (lua_gettop(L) == 2) // only delete for one player + { + const char *playername = luaL_checkstring(L, 2); + server->deleteParticleSpawner(playername, id); + } + else // delete for all players + { + server->deleteParticleSpawnerAll(id); + } + return 1; +} diff --git a/src/scriptapi_particles.h b/src/scriptapi_particles.h new file mode 100644 index 000000000..4b37d7ce1 --- /dev/null +++ b/src/scriptapi_particles.h @@ -0,0 +1,32 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_PARTICLES_H_ +#define LUA_PARTICLES_H_ + +extern "C" { +#include +#include +} + +int l_add_particle(lua_State *L); +int l_add_particlespawner(lua_State *L); +int l_delete_particlespawner(lua_State *L); + +#endif diff --git a/src/server.cpp b/src/server.cpp index f77ac6ad9..644f89349 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include #include +#include #include "clientserver.h" #include "map.h" #include "jmutexautolock.h" @@ -3474,6 +3475,132 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, co m_con.Send(peer_id, 0, data, true); } +// Spawns a particle on peer with peer_id +void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_SPAWN_PARTICLE); + writeV3F1000(os, pos); + writeV3F1000(os, velocity); + writeV3F1000(os, acceleration); + writeF1000(os, expirationtime); + writeF1000(os, size); + writeU8(os, collisiondetection); + os< data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +// Spawns a particle on all peers +void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture) +{ + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendSpawnParticle(client->peer_id, pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); + } +} + +// Adds a ParticleSpawner on peer with peer_id +void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, + float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER); + + writeU16(os, amount); + writeF1000(os, spawntime); + writeV3F1000(os, minpos); + writeV3F1000(os, maxpos); + writeV3F1000(os, minvel); + writeV3F1000(os, maxvel); + writeV3F1000(os, minacc); + writeV3F1000(os, maxacc); + writeF1000(os, minexptime); + writeF1000(os, maxexptime); + writeF1000(os, minsize); + writeF1000(os, maxsize); + writeU8(os, collisiondetection); + os< data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +// Adds a ParticleSpawner on all peers +void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, + float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) +{ + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendAddParticleSpawner(client->peer_id, amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id); + } +} + +void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER); + + writeU16(os, id); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendDeleteParticleSpawnerAll(u32 id) +{ + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendDeleteParticleSpawner(client->peer_id, id); + } +} + void Server::BroadcastChatMessage(const std::wstring &message) { for(std::map::iterator @@ -4432,6 +4559,111 @@ void Server::notifyPlayers(const std::wstring msg) BroadcastChatMessage(msg); } +void Server::spawnParticle(const char *playername, v3f pos, + v3f velocity, v3f acceleration, + float expirationtime, float size, bool + collisiondetection, std::string texture) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return; + SendSpawnParticle(player->peer_id, pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); +} + +void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture) +{ + SendSpawnParticleAll(pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); +} + +u32 Server::addParticleSpawner(const char *playername, + u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return -1; + + u32 id = 0; + for(;;) // look for unused particlespawner id + { + id++; + if (std::find(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id) + == m_particlespawner_ids.end()) + { + m_particlespawner_ids.push_back(id); + break; + } + } + + SendAddParticleSpawner(player->peer_id, amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, + collisiondetection, texture, id); + + return id; +} + +u32 Server::addParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture) +{ + u32 id = 0; + for(;;) // look for unused particlespawner id + { + id++; + if (std::find(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id) + == m_particlespawner_ids.end()) + { + m_particlespawner_ids.push_back(id); + break; + } + } + + SendAddParticleSpawnerAll(amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, + collisiondetection, texture, id); + + return id; +} + +void Server::deleteParticleSpawner(const char *playername, u32 id) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return; + + m_particlespawner_ids.erase( + std::remove(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id), + m_particlespawner_ids.end()); + SendDeleteParticleSpawner(player->peer_id, id); +} + +void Server::deleteParticleSpawnerAll(u32 id) +{ + m_particlespawner_ids.erase( + std::remove(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id), + m_particlespawner_ids.end()); + SendDeleteParticleSpawnerAll(id); +} + void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) { m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate); diff --git a/src/server.h b/src/server.h index 63717eaec..813dc9828 100644 --- a/src/server.h +++ b/src/server.h @@ -456,6 +456,35 @@ public: // Envlock and conlock should be locked when calling this void notifyPlayer(const char *name, const std::wstring msg); void notifyPlayers(const std::wstring msg); + void spawnParticle(const char *playername, + v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + void spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + u32 addParticleSpawner(const char *playername, + u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture); + + u32 addParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture); + + void deleteParticleSpawner(const char *playername, u32 id); + void deleteParticleSpawnerAll(u32 id); + void queueBlockEmerge(v3s16 blockpos, bool allow_generate); @@ -574,6 +603,41 @@ private: void sendDetachedInventoryToAll(const std::string &name); void sendDetachedInventories(u16 peer_id); + // Adds a ParticleSpawner on peer with peer_id + void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture, u32 id); + + // Adds a ParticleSpawner on all peers + void SendAddParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture, u32 id); + + // Deletes ParticleSpawner on a single client + void SendDeleteParticleSpawner(u16 peer_id, u32 id); + + // Deletes ParticleSpawner on all clients + void SendDeleteParticleSpawnerAll(u32 id); + + // Spawns particle on single client + void SendSpawnParticle(u16 peer_id, + v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + // Spawns particle on all clients + void SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + /* Something random */ @@ -790,6 +854,11 @@ private: */ // key = name std::map m_detached_inventories; + + /* + Particles + */ + std::vector m_particlespawner_ids; }; /* -- cgit v1.2.3 From 6767ed74f838bbb0c7786f85ef1e8f5b9fb88ef7 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 24 Mar 2013 15:12:29 -0400 Subject: Update lua_api.txt --- doc/lua_api.txt | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 4edca5adb..c77a1e939 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.4 +Minetest Lua Modding API Reference 0.4.5 ========================================== More information at http://c55.me/minetest/ @@ -372,6 +372,25 @@ A box is defined as: A box of a regular node would look like: {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, +Ore types +--------------- +These tell in what manner the ore is generated. +All default ores are of the uniformly-distributed scatter type. + +- scatter + Randomly chooses a location and generates a cluster of ore. + If noise_params is specified, the ore will be placed if the 3d perlin noise at + that point is greater than the noise_threshhold, giving the ability to create a non-equal + distribution of ore. +- sheet + Creates a sheet of ore in a blob shape according to the 2d perlin noise described by + the noise_params structure. The height of the blob is randomly scattered, with a maximum + height of clust_size. Here, clust_scarcity and clust_num_ores are ignored. + This is essentially an improved version of the so-called "stratus" ore seen in some unofficial mods. +- claylike - NOT YET IMPLEMENTED + Places ore if there are no more than clust_scarcity number of specified nodes within a Von Neumann + neighborhood of clust_size radius. + Representations of simple things -------------------------------- Position/vector: @@ -844,6 +863,7 @@ minetest.register_tool(name, item definition) minetest.register_craftitem(name, item definition) minetest.register_alias(name, convert_to) minetest.register_craft(recipe) +minetest.register_ore(ore definition) Global callback registration functions: (Call these only at load time) minetest.register_globalstep(func(dtime)) @@ -1669,6 +1689,28 @@ Recipe for register_craft (furnace fuel) burntime = 1, } +Ore definition (register_ore) +{ + ore_type = "scatter" -- See "Ore types" + ore = "default:stone_with_coal", + wherein = "default:stone", + clust_scarcity = 8*8*8, + ^ Ore has a 1 out of clust_scarcity chance of spawning in a node + ^ This value should be *MUCH* higher than your intuition might tell you! + clust_num_ores = 8, + ^ Number of ores in a cluster + clust_size = 3, + ^ Size of the bounding box of the cluster + ^ In this example, there is a 3x3x3 cluster where 8 out of the 27 nodes are coal ore + height_min = -31000, + height_max = 64, + noise_threshhold = 0.5, + ^ If noise is above this threshhold, ore is placed. Not needed for a uniform distribution + noise_params = {offset=0, scale=1, spread={x=100, y=100, z=100}, seed=23, octaves=3, persist=0.70} + ^ NoiseParams structure describing the perlin noise used for ore distribution. + ^ Needed for sheet ore_type. Omit from scatter ore_type for a uniform ore distribution +} + Chatcommand definition (register_chatcommand) { params = " ", -- short parameter description -- cgit v1.2.3 From 423d69bd4095970068b5431b4b33007a3c069576 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 24 Mar 2013 21:39:15 +0200 Subject: Fix indentation of lua_api.txt --- doc/lua_api.txt | 148 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 74 deletions(-) (limited to 'doc/lua_api.txt') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c77a1e939..23f7f8568 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -203,18 +203,18 @@ from the available ones of the following files: Examples of sound parameter tables: -- Play locationless on all clients { - gain = 1.0, -- default + gain = 1.0, -- default } -- Play locationless to a player { - to_player = name, - gain = 1.0, -- default + to_player = name, + gain = 1.0, -- default } -- Play in a location { - pos = {x=1,y=2,z=3}, - gain = 1.0, -- default - max_hear_distance = 32, -- default + pos = {x=1,y=2,z=3}, + gain = 1.0, -- default + max_hear_distance = 32, -- default } -- Play connected to an object, looped { @@ -266,11 +266,11 @@ local drawtype = get_nodedef_field(nodename, "drawtype") Example: minetest.get_item_group(name, group) has been implemented as: function minetest.get_item_group(name, group) - if not minetest.registered_items[name] or not - minetest.registered_items[name].groups[group] then - return 0 - end - return minetest.registered_items[name].groups[group] + if not minetest.registered_items[name] or not + minetest.registered_items[name].groups[group] then + return 0 + end + return minetest.registered_items[name].groups[group] end Nodes @@ -491,7 +491,7 @@ An example: Make meat soup from any meat, any water and any bowl } An another example: Make red wool from white wool and red dye { - type = 'shapeless', + type = 'shapeless', output = 'wool:red', recipe = {'wool:white', 'group:dye,basecolor_red'}, } @@ -502,7 +502,7 @@ Special groups - level: Can be used to give an additional sense of progression in the game. - A larger level will cause eg. a weapon of a lower level make much less damage, and get weared out much faster, or not be able to get drops - from destroyed nodes. + from destroyed nodes. - 0 is something that is directly accessible at the start of gameplay - There is no upper limit - dig_immediate: (player can always pick up node without tool wear) @@ -609,11 +609,11 @@ maximum level. Example definition of the capabilities of a tool ------------------------------------------------- tool_capabilities = { - full_punch_interval=1.5, - max_drop_level=1, - groupcaps={ - crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} - } + full_punch_interval=1.5, + max_drop_level=1, + groupcaps={ + crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} + } } This makes the tool be able to dig nodes that fullfill both of these: @@ -773,7 +773,7 @@ field[,;,;;