From 11754c0b758a005d2cedd28c8acfa28caeb1b077 Mon Sep 17 00:00:00 2001 From: stujones11 Date: Sat, 21 Dec 2013 18:20:46 +0000 Subject: [PATCH] Initial commit --- .gitignore | 7 ++ LICENSE.txt | 5 + README.txt | 50 ++++++++ depends.txt | 0 init.lua | 226 +++++++++++++++++++++++++++++++++++++ meshnode.conf.example | 14 +++ textures/meshnode_side.png | Bin 0 -> 699 bytes textures/meshnode_top.png | Bin 0 -> 878 bytes 8 files changed, 302 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.txt create mode 100644 depends.txt create mode 100644 init.lua create mode 100644 meshnode.conf.example create mode 100644 textures/meshnode_side.png create mode 100644 textures/meshnode_top.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a57dbc9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +## Generic ignorable patterns and files +*~ +.*.swp +*bak* +tags +*.vim + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..28b412b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,5 @@ +Meshnodes for Minetest [meshnode] +================================= + +Copyright (C) 2013 Stuart Jones - LGPL + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..2c3b482 --- /dev/null +++ b/README.txt @@ -0,0 +1,50 @@ +Meshnodes for Minetest [meshnode] +================================= + +Meshnodes is a mod that transforms ordinary cubic nodes into a connected +array of replica entities to which players can attach to and manoeuvre. + +To use, simply build or place a model using only fully cubic nodes +(i.e. no stairs, slabs or nodeboxes for now). Next place a meshnode controller +in the appropriate position, the operator will be attached directly on top +of the controller node. Now right click the controller and enter the +minimum and maximum extents of the model (relative to the controller x,y,z) +e.g. for a 3x3 square of nodes with the controller placed above the center +node, the relative positions would be minp = -1,-1,-1 maxp = 1,0,1 + +Alternatively, if you are using worldedit, you can use the position +markers to define the extents of the model. However, this must be done +before the controller is placed. + +Controls +======== + +[Up] Forward +[Down] Reverse +[Left] Rotate Left +[Right] Rotate Right +[Jump] Up +[Sneak] Down +[RMB] Attach/Detach + +Limitations +=========== + +Only works for nodes with drawtype normal, allfaces_optional or glasslike. +Models will disappear or become detached from the controller if they become +unloaded. This can cause the server to warn against excessive objects and +destroy them accordingly. + +I have tried a number of ways the get the models to persist after a server +restart but so far all of my efforts have been thwarted by minetest's buggy +object management system. I also wanted to be able to attach 'passengers' +to the entities, however, this has not been possible for the same reason. + +Know Issues +=========== + +The player controlling the entity may appear to be connected to the wrong +part of the model when viewed by a player that was not present during the +initial attachment. Currently the only solution is for the operator to +detach then re-attach to the model in the presence of said player. + diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..e69de29 diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..e4c7851 --- /dev/null +++ b/init.lua @@ -0,0 +1,226 @@ +MESHNODE_MAX_SPEED = 2 +MESHNODE_MAX_LIFT = 1 +MESHNODE_YAW_AMOUNT = 0.02 +MESHNODE_MAX_RADIUS = 10 + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +local input = io.open(modpath.."/meshnode.conf", "r") +if input then + dofile(modpath.."/meshnode.conf") + input:close() + input = nil +end + +local allowed_drawtypes = { + "normal", + "allfaces_optional", + "glasslike", +} + +local function is_allowed_drawtype(drawtype) + for _, v in pairs(allowed_drawtypes) do + if v == drawtype then + return true + end + end +end + +local function is_valid_pos(pos) + if pos then + if pos.x and pos.y and pos.z then + return math.abs(pos.x) <= MESHNODE_MAX_RADIUS and + math.abs(pos.y) <= MESHNODE_MAX_RADIUS and + math.abs(pos.z) <= MESHNODE_MAX_RADIUS + end + end +end + +local function get_step(a, b) + if a > b then + return -1 + end + return 1 +end + +local function create_meshnode(pos, parent) + local node = minetest.get_node(pos) + local item = minetest.registered_items[node.name] + if item then + if item.tiles and is_allowed_drawtype(item.drawtype) then + local t = item.tiles + local textures = {t[1], t[1], t[1], t[1], t[1], t[1]} + if #t == 3 then + textures = {t[1], t[2], t[3], t[3], t[3], t[3]} + elseif #t == 6 then + textures = t + end + local object = minetest.add_entity(pos, "meshnode:mesh") + if object then + object:set_properties({textures=textures}) + local y = 0 + if node.param2 then + y = 90 * node.param2 + end + local offset = vector.subtract(pos, parent:getpos()) + offset = vector.multiply(offset, {x=10,y=10,z=10}) + object:set_attach(parent, "", offset, {x=0,y=y,z=0}) + end + end + minetest.remove_node(pos) + end +end + +minetest.register_entity("meshnode:ctrl", { + physical = true, + visual = "cube", + visual_size = {x=1, y=1}, + textures = { + "meshnode_top.png", + "meshnode_side.png", + "meshnode_side.png", + "meshnode_side.png", + "meshnode_side.png", + "meshnode_side.png", + }, + player = nil, + speed = 0, + lift = 0, + on_activate = function(self, staticdata, dtime_s) + if staticdata == "expired" then + self.object:remove() + end + self.object:set_armor_groups({cracky=50}) + end, + on_rightclick = function(self, clicker) + if self.player == nil then + clicker:set_attach(self.object, "", {x=0,y=15,z=0}, {x=0,y=90,z=0}) + self.player = clicker + else + self.player:set_detach() + self.player = nil + end + end, + on_step = function(self, dtime) + if self.player then + local velocity = self.object:getvelocity() + local yaw = self.object:getyaw() + local speed = self.speed + local lift = self.lift + local ctrl = self.player:get_player_control() + if ctrl.up then + speed = speed + 0.1 + elseif ctrl.down then + speed = speed - 0.1 + else + speed = speed * 0.99 + end + if speed > MESHNODE_MAX_SPEED then + speed = MESHNODE_MAX_SPEED + elseif speed < 0 then + speed = 0 + end + if ctrl.jump then + lift = lift + 0.1 + elseif ctrl.sneak then + lift = lift - 0.1 + else + lift = lift * 0.9 + end + if lift > MESHNODE_MAX_LIFT then + lift = MESHNODE_MAX_LIFT + elseif lift < 0 - MESHNODE_MAX_LIFT then + lift = 0 - MESHNODE_MAX_LIFT + end + if ctrl.left then + yaw = yaw + MESHNODE_YAW_AMOUNT + elseif ctrl.right then + yaw = yaw - MESHNODE_YAW_AMOUNT + end + velocity.x = math.cos(yaw) * speed + velocity.y = lift + velocity.z = math.sin(yaw) * speed + self.object:setyaw(yaw) + self.object:setvelocity(velocity) + self.speed = speed + self.lift = lift + else + self.object:setvelocity({x=0, y=0, z=0}) + self.speed = 0 + self.lift = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_entity("meshnode:mesh", { + physical = true, + visual = "cube", + visual_size = {x=1, y=1}, + on_activate = function(self, staticdata, dtime_s) + if staticdata == "expired" then + self.object:remove() + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_node("meshnode:controller", { + description = "Meshnode Controller", + paramtype2 = "facedir", + tiles = {"meshnode_top.png", "meshnode_side.png", "meshnode_side.png"}, + is_ground_content = true, + groups = {cracky=3, oddly_breakable_by_hand=3}, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", "size[5,3]" + .."field[0.5,1;2,0.5;minp;Minp;${minp}]" + .."field[3.0,1;2,0.5;maxp;Maxp;${maxp}]" + .."button_exit[1.0,2;3,0.5;connect;Generate Entity]" + ) + meta:set_string("infotext", "Meshnode Controller") + end, + after_place_node = function(pos, placer) + if worldedit then + local name = placer:get_player_name() + local meta = minetest.get_meta(pos) + if worldedit.pos1[name] then + local p = vector.subtract(worldedit.pos1[name], pos) + meta:set_string("minp", p.x..","..p.y..","..p.z) + end + if worldedit.pos2[name] then + local p = vector.subtract(worldedit.pos2[name], pos) + meta:set_string("maxp", p.x..","..p.y..","..p.z) + end + end + end, + on_receive_fields = function(pos, formname, fields, sender) + if fields.connect and not fields.quit then + local minp = minetest.string_to_pos(fields.minp) + local maxp = minetest.string_to_pos(fields.maxp) + if is_valid_pos(minp) and is_valid_pos(maxp) then + local nodes = {} + local node = minetest.get_node(pos) + minetest.remove_node(pos) + local object = minetest.add_entity(pos, "meshnode:ctrl") + if object then + for x = minp.x, maxp.x, get_step(minp.x, maxp.x) do + for y = minp.y, maxp.y, get_step(minp.y, maxp.y) do + for z = minp.z, maxp.z, get_step(minp.z, maxp.z) do + local node_pos = vector.add(pos, {x=x, y=y, z=z}) + create_meshnode(node_pos, object) + end + end + end + end + else + local name = sender:get_player_name() + minetest.chat_send_player(name, "Invalid Position!") + end + end + end, +}) + diff --git a/meshnode.conf.example b/meshnode.conf.example new file mode 100644 index 0000000..6f50658 --- /dev/null +++ b/meshnode.conf.example @@ -0,0 +1,14 @@ +-- Meshnode configuration (defaults) + +-- Maximum Entity Speed +MESHNODE_MAX_SPEED = 2 + +-- Maximum Up/Down Velocity +MESHNODE_MAX_LIFT = 1 + +-- Rotational Step (Radians) +MESHNODE_YAW_AMOUNT = 0.02 + +-- Maximum Entity Area +MESHNODE_MAX_RADIUS = 10 + diff --git a/textures/meshnode_side.png b/textures/meshnode_side.png new file mode 100644 index 0000000000000000000000000000000000000000..7dff41a762056c8e91cb26cb3674ee16ff249458 GIT binary patch literal 699 zcmV;s0!00ZP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i*)3 z6AdR*PytH-00KKnL_t(I%T1F@ZWJ*PhQBJ?-93{R3ZMvO#eywILgFs$xd3P21Y86O zfmnhFAmzg#Bt~GPSMpOj%4h{-Ig-ud&GltN&NI_VuVFnIF6=D5RJ?XfP>d8-HlBn5Xz zMFOWu*$zq&*d!&Ykizj*6`~4LRGK-bqjD%lRKd-N8UpdyYz}8rzXuU^OZ$(CvsPn> zN>U*R#5AD8$=RcLW9;NNNxNkZyQOstn?HnF=sB0jWCxE+^@z`>`1Yvu$fb>uM=@6oAc$zfw}g$UW`ihuF0R( z_Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i*)3 z6AdF&J;hQ000QnwL_t(I%WaciXj^p@$3ORX@6Ao_(xzz%V@sXBWQd@y2>LJt-}I#q z!n7|s-B_zao0_`KE^VsmAG58^`d>l!FjPc}46&dP_9V^+U+SC6mC3|`5XV-{dXxL_ z_%IhX=79rWzMSv*$2r=smuAI`$BEe~Jhy?;b#&8WpnsIR?bD4pQrR4V+aU0p1kGE- zJA2T6{(3?Nk5x$+8j<`LEkY}T&C46qoh^>MQXvu{n>28Ph#Sp_l%+U-aS^2q6E76j z&yUmFZSg>l#eqtjobYmc06SmpHYu z9Db}zu6|ejUmt$5N1R$&p5Nag&t&8B`}gO>FiZ=jX^6ng@kL4t1?I=zLqwR{xIo?6 zV&T*>UKmk4TcC1s2>}eH5&Aw#(+zYDY)dioeu-*vnbFximZ{n!xc+T%`K@!jvo?v+ zAf`YWChE|0uj7S+7f4&MT3X?acSqR1<`A=!lokq{d3y<6(*W2NQ?x?ir5B25Pw$P( z=H(3lwy!y)vbi?)b{b@IefRNqaR0#qkt?7l_$O0KA?}Kv185S;f$`cE*ml&cu9y z^{G`-*&N;vEnf%)hK^J)bq&q1(6+B!l;M1& literal 0 HcmV?d00001