summaryrefslogtreecommitdiff
path: root/castle_gates
diff options
context:
space:
mode:
Diffstat (limited to 'castle_gates')
-rw-r--r--castle_gates/LICENSE21
-rw-r--r--castle_gates/README.txt11
-rw-r--r--castle_gates/api.txt76
-rw-r--r--castle_gates/class_pointset.lua101
-rw-r--r--castle_gates/depends.txt6
-rw-r--r--castle_gates/description.txt1
-rw-r--r--castle_gates/doc.lua52
-rw-r--r--castle_gates/doors.lua62
-rw-r--r--castle_gates/gate_functions.lua389
-rw-r--r--castle_gates/gate_slots.lua138
-rw-r--r--castle_gates/gates.lua243
-rw-r--r--castle_gates/init.lua8
-rw-r--r--castle_gates/intllib.lua45
-rw-r--r--castle_gates/locale/template.pot202
-rw-r--r--castle_gates/mod.conf1
-rw-r--r--castle_gates/screenshot.pngbin0 -> 166238 bytes
-rw-r--r--castle_gates/textures/LICENSE.txt11
-rw-r--r--castle_gates/textures/castle_door_edge_mask.pngbin0 -> 190 bytes
-rw-r--r--castle_gates/textures/castle_door_handle_mask.pngbin0 -> 194 bytes
-rw-r--r--castle_gates/textures/castle_door_jail.pngbin0 -> 878 bytes
-rw-r--r--castle_gates/textures/castle_door_oak.pngbin0 -> 1343 bytes
-rw-r--r--castle_gates/textures/castle_door_side_mask.pngbin0 -> 189 bytes
-rw-r--r--castle_gates/textures/castle_jail_door_inv.pngbin0 -> 186 bytes
-rw-r--r--castle_gates/textures/castle_jailbars.pngbin0 -> 253 bytes
-rw-r--r--castle_gates/textures/castle_oak_door_inv.pngbin0 -> 411 bytes
-rw-r--r--castle_gates/textures/castle_portcullis_mask.pngbin0 -> 194 bytes
-rw-r--r--castle_gates/textures/castle_steel.pngbin0 -> 214 bytes
27 files changed, 1367 insertions, 0 deletions
diff --git a/castle_gates/LICENSE b/castle_gates/LICENSE
new file mode 100644
index 0000000..5c3c583
--- /dev/null
+++ b/castle_gates/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Minetest Mods Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/castle_gates/README.txt b/castle_gates/README.txt
new file mode 100644
index 0000000..113bd89
--- /dev/null
+++ b/castle_gates/README.txt
@@ -0,0 +1,11 @@
+Castle Gates
+
+Licence: MIT
+
+see: LICENSE
+
+=-=-=-=-=-=-=-=-=-=
+
+This is a mod all about creating castle gates and dungeons. It contains dungeon doors and bars, and also a set of nodes for constructing larger swinging and sliding gates.
+
+This allows the construction of portcullises and drawbridges as well as conventional swinging doors. Use the screwdriver to reorient gate pieces as needed for these purposes.
diff --git a/castle_gates/api.txt b/castle_gates/api.txt
new file mode 100644
index 0000000..8d8b4d6
--- /dev/null
+++ b/castle_gates/api.txt
@@ -0,0 +1,76 @@
+This document is intended primarily for modders interested in adding their own gate nodes and gate types.
+
+This mod allows for additional gate nodes to be defined in other dependent mods that will work seamlessly with existing gate nodes. The following is a minimalist example that shows a node using all of the features offered by this mod's gate API:
+
+minetest.register_node("castle_gates:example_gate_piece", {
+ description = "Example gate",
+ groups = {castle_gate = 1},
+ tiles = {"default_wood.png"},
+ paramtype2 = "facedir",
+ on_rightclick = castle_gates.trigger_gate,
+ _gate_edges = {"right"=true},
+ _gate_hinge = {axis="top", offset={"front","left"}},
+})
+
+The key features are:
+
+* paramtype2 = "facedir" (mandatory)
+* "castle_gate" group membership (mandatory)
+* on_rightclick = castle_gates.trigger_gate (optional, but recommended)
+* _gate_edges = <edge definition> (optional)
+* _gate_hinge = <hinge defintion> (optional)
+
+
+Paramtype2
+==========
+
+All gate pieces must be orientable via facedir.
+
+Castle_gate group
+=================
+
+When a gate is triggered by right-clicking on a gate node, the gate code does a "flood fill" operation to find all connected gate nodes. This flood fill operation looks for adjacent nodes that belong to the same castle_gate group, so all gate nodes need to belong to this group.
+
+Gates defined in this mod all belong to castle_gate=1.
+
+on_rightclick
+=============
+
+The castle_gates.trigger_gate method is a right-click handler that will trigger the movement of the gate. If you want your gate pieces to respond to a player's right click by opening, use this call to make the gate magic happen.
+
+Note that if you wish you can embed the call to The castle_gates.trigger_gate inside your own on_rightclick function, so that for example you could make a "locked" gate that will only respond to specific players (you may wish to use a different castle_gate group ID for such a gate). Or you can omit this function, in which case the gate piece will still move with the rest of the gate but right-clicking on it will not trigger the gate's movement.
+
+_gate_edges
+===========
+
+This is an optional property you can put on a gate node's definition to prevent flood-fill from extending beyond this node in a particular direction. This is useful if, for example, you want players to be able to build double doors that would otherwise connect together when both doors are closed.
+
+It consists of a table with directions defined as edges set to true. You can use this template:
+
+_gate_edges = {right=false, left=false, top=false, bottom=false}
+
+("front" and "back" are also possible but are unlikely to be of any real use)
+
+Note that the flood-fill search for gate nodes will flow *around* an edge piece if a path exists, the mere fact that there's an edge piece does not guarantee that the door node beyond the edge will not be considered part of the same door as it.
+
+_gate_hinge
+===========
+
+The hinge definition for a node def is of the following form:
+
+_gate_hinge = {axis=<axis>, offset=<offset>}
+
+<axis> is one of "top", "bottom", "left", "right", "front" or "back".
+Top/bottom, left/right and front/back are interchangeable pairings as far as this code is concerned.
+These directions are relative to the orientation of the block. Existing gates use "top", so for
+maximum compatibility it's advised that new gate hinges are defined with the same axis.
+
+<offset> is optional. If it is not defined, the gate hinge will try to rotate around the center of the node.
+
+<offset> can be a single direction ("top", "bottom", "left", "right", "front", "back"). If a single direction is
+given the hinge will try to rotate around the center of the node that lies in that direction relative to the hinge node.
+
+<offset> can also be a pair of directions given in a table. This is how the *edge* of a node can be made into the
+center of rotation. For example, existing gate hinges in this mod have the offset {"front","left"}. This means that the
+gate will try to rotate around the center of the edge of the node where the front and left faces intersect.
+You should only use direction pairs that form a 90 degree angle. \ No newline at end of file
diff --git a/castle_gates/class_pointset.lua b/castle_gates/class_pointset.lua
new file mode 100644
index 0000000..3cbbd89
--- /dev/null
+++ b/castle_gates/class_pointset.lua
@@ -0,0 +1,101 @@
+-- A simple special-purpose class, this is used for building up sets of three-dimensional points for fast reference
+
+Pointset = {}
+Pointset.__index = Pointset
+
+function Pointset.create()
+ local set = {}
+ setmetatable(set,Pointset)
+ set.points = {}
+ return set
+end
+
+function Pointset:set(x, y, z, value)
+ -- sets a value in the 3D array "points".
+ if self.points[x] == nil then
+ self.points[x] = {}
+ end
+ if self.points[x][y] == nil then
+ self.points[x][y] = {}
+ end
+ self.points[x][y][z] = value
+end
+
+function Pointset:set_if_not_in(excluded, x, y, z, value)
+ -- If a value is not already set for this point in the 3D array "excluded", set it in "points"
+ if excluded:get(x, y, z) ~= nil then
+ return
+ end
+ self:set(x, y, z, value)
+end
+
+function Pointset:get(x, y, z)
+ -- return a value from the 3D array "points"
+ if self.points[x] == nil or self.points[x][y] == nil then
+ return nil
+ end
+ return self.points[x][y][z]
+end
+
+function Pointset:set_pos(pos, value)
+ self:set(pos.x, pos.y, pos.z, value)
+end
+
+function Pointset:set_pos_if_not_in(excluded, pos, value)
+ self:set_if_not_in(excluded, pos.x, pos.y, pos.z, value)
+end
+
+function Pointset:get_pos(pos)
+ return self:get(pos.x, pos.y, pos.z)
+end
+
+function Pointset:pop()
+ -- returns a point that's in the 3D array, and then removes it.
+ local pos = {}
+ local ytable
+ local ztable
+ local val
+
+ local count = 0
+ for _ in pairs(self.points) do count = count + 1 end
+ if count == 0 then
+ return nil
+ end
+
+ pos.x, ytable = next(self.points)
+ pos.y, ztable = next(ytable)
+ pos.z, val = next(ztable)
+
+ self.points[pos.x][pos.y][pos.z] = nil
+
+ count = 0
+ for _ in pairs(self.points[pos.x][pos.y]) do count = count + 1 end
+ if count == 0 then
+ self.points[pos.x][pos.y] = nil
+ end
+
+ count = 0
+ for _ in pairs(self.points[pos.x]) do count = count + 1 end
+ if count == 0 then
+ self.points[pos.x] = nil
+ end
+
+ return pos, val
+end
+
+function Pointset:get_pos_list(value)
+ -- Returns a list of all points with the given value in standard Minetest vector format. If no value is provided, returns all points
+ local outlist = {}
+ for x, ytable in ipairs(self.points) do
+ for y, ztable in ipairs(ytable) do
+ for z, val in ipairs(ztable) do
+ if (value == nil and val ~= nil ) or val == value then
+ table.insert(outlist, {x=x, y=y, z=z})
+ end
+ end
+ end
+ end
+ return outlist
+end
+
+ \ No newline at end of file
diff --git a/castle_gates/depends.txt b/castle_gates/depends.txt
new file mode 100644
index 0000000..acb8d9e
--- /dev/null
+++ b/castle_gates/depends.txt
@@ -0,0 +1,6 @@
+default
+castle_masonry?
+doors?
+xpanes?
+intllib?
+doc? \ No newline at end of file
diff --git a/castle_gates/description.txt b/castle_gates/description.txt
new file mode 100644
index 0000000..2399226
--- /dev/null
+++ b/castle_gates/description.txt
@@ -0,0 +1 @@
+This is a mod all about creating castles and castle dungeons. Many of the nodes are used for the outer-walls or dungeons.
diff --git a/castle_gates/doc.lua b/castle_gates/doc.lua
new file mode 100644
index 0000000..ac868e7
--- /dev/null
+++ b/castle_gates/doc.lua
@@ -0,0 +1,52 @@
+castle_gates.doc = {}
+
+if not minetest.get_modpath("doc") then
+ return
+end
+
+-- internationalization boilerplate
+local MP = minetest.get_modpath(minetest.get_current_modname())
+local S, NS = dofile(MP.."/intllib.lua")
+
+castle_gates.doc.portcullis_bars_longdesc = S("Heavy wooden bars designed to prevent entry even to siege equipment.")
+castle_gates.doc.portcullis_bars_usagehelp = S("Place these bars in a structure together and they will slide as a unified gate when clicked on.")
+
+castle_gates.doc.portcullis_bars_bottom_longdesc = S("The bottom edge of a portcullis gate, with knobs to lock securely into the floor.")
+castle_gates.doc.portcullis_bars_bottom_usagehelp = S("This block can be used to define the edge of a portcullius that meets up with another gate, should you have an arrangement like that. Otherwise it's just decorative.")
+
+castle_gates.doc.gate_panel_longdesc = S("A basic gate panel.")
+castle_gates.doc.gate_panel_usagehelp = S("This gate segment will move in unison with adjoining gate segments when right-clicked.")
+
+castle_gates.doc.gate_edge_longdesc = S("A gate panel with a defined edge.")
+castle_gates.doc.gate_edge_usagehelp = S("The darkened edge of this panel marks the edge of the gate it's a part of. You can use these when building double doors to ensure the two parts swing separately, for example. Note that edges aren't strictly necessary for gates that stand alone.")
+
+castle_gates.doc.gate_edge_handle_longdesc = S("A gate edge with a handle.")
+castle_gates.doc.gate_edge_handle_usagehelp = S("The handle is basically decorative, a door this size can be swung by clicking anywhere on it. But the darkened edge of this panel is useful for defining the edge of a gate when it abuts a partner to the side.")
+
+castle_gates.doc.gate_hinge_longdesc = S("A hinged gate segment that allows a gate to swing.")
+castle_gates.doc.gate_hinge_usagehelp = S("If you have more than one hinge in your gate, make sure the hinges line up correctly otherwise the gate will not be able to swing. The hinge is the protruding block along the edge of the gate panel.")
+
+castle_gates.doc.gate_slot_longdesc = S("A block with a slot to allow an adjacent sliding gate through.")
+castle_gates.doc.gate_slot_usagehelp = S("This block is designed to extend into a neighboring node that a sliding gate passes through, to provide a tight seal for the gate to move through without allowing anything else to squeeze in.")
+
+castle_gates.doc.gate_slot_reverse_longdesc = S("A block that extends into an adjacent node to provide a tight seal for a large gate.")
+castle_gates.doc.gate_slot_reverse_usagehelp = S("Two nodes cannot occupy the same space, but this block extends into a neighboring node's space to allow for gates to form a tight seal. It can be used with sliding gates or swinging gates.")
+
+doc.add_category("castle_gates",
+{
+ name = S("Gates"),
+ description = S("Gates are large multi-node constructions that swing on hinges or slide out of the way when triggered."),
+ build_formspec = doc.entry_builders.text_and_gallery,
+})
+
+doc.add_entry("castle_gates", "construction", {
+ name = S("Gate construction"),
+ data = { text =
+S("Gates are multi-node constructions, usually (though not always) consisting of multiple node types that fit together into a unified whole. The orientation of gate nodes is significant, so a screwdriver will be a helpful tool when constructing gates."
+.."\n\n"..
+"A gate's extent is determined by a \"flood fill\" operation. When you trigger a gate block, all compatible neighboring blocks will be considered part of the same structure and will move in unison. Only gate blocks that are aligned with each other will be considered part of the same gate. If you wish to build adjoining gates (for example, a large pair of double doors that meet in the center) you'll need to make use of gate edge blocks to prevent it all from being considered one big door. Note that if your gate does not abut any other gates you don't actually need to define its edges this way - you don't have to use edge blocks in this case."
+.."\n\n"..
+"If a gate has no hinge nodes it will be considered a sliding gate. When triggered, the gate code will search for a direction that the gate can slide in and will move it in that direction at a rate of one block-length per second. Once it reaches an obstruction it will stop, and when triggered again it will try sliding in the opposite direction."
+.."\n\n"..
+"If a gate has hinge nodes then triggering it will cause the gate to try swinging around the hinge. If the gate has multiple hinges and they don't line up properly the gate will be unable to move. Note that the gate can only exist in 90-degree increments of orientation, but the gate still looks for obstructions in the region it is swinging through and will not swing if there's something in the way.")
+}}) \ No newline at end of file
diff --git a/castle_gates/doors.lua b/castle_gates/doors.lua
new file mode 100644
index 0000000..58e3e4f
--- /dev/null
+++ b/castle_gates/doors.lua
@@ -0,0 +1,62 @@
+-- internationalization boilerplate
+local MP = minetest.get_modpath(minetest.get_current_modname())
+local S, NS = dofile(MP.."/intllib.lua")
+
+if minetest.get_modpath("doors") then
+ doors.register("castle_gates:oak_door", {
+ tiles = {{ name = "castle_door_oak.png", backface_culling = true }},
+ description = S("Oak Door"),
+ inventory_image = "castle_oak_door_inv.png",
+ protected = true,
+ groups = { choppy = 2, door = 1 },
+ sounds = default.node_sound_wood_defaults(),
+ recipe = {
+ {"default:tree", "default:tree"},
+ {"default:tree", "default:tree"},
+ {"default:tree", "default:tree"},
+ }
+ })
+
+ doors.register("castle_gates:jail_door", {
+ tiles = {{ name = "castle_door_jail.png", backface_culling = true }},
+ description = S("Jail Door"),
+ inventory_image = "castle_jail_door_inv.png",
+ protected = true,
+ groups = { cracky = 2, door = 1, flow_through = 1},
+ sound_open = "doors_steel_door_open",
+ sound_close = "doors_steel_door_close",
+ recipe = {
+ {"castle_gates:jailbars", "castle_gates:jailbars"},
+ {"castle_gates:jailbars", "castle_gates:jailbars"},
+ {"castle_gates:jailbars", "castle_gates:jailbars"},
+ }
+ })
+
+ minetest.register_alias("castle:oak_door_a", "castle_gates:oak_door_a")
+ minetest.register_alias("castle:oak_door_b", "castle_gates:oak_door_b")
+ minetest.register_alias("castle:jail_door_a", "castle_gates:jail_door_a")
+ minetest.register_alias("castle:jail_door_b", "castle_gates:jail_door_b")
+end
+
+if minetest.get_modpath("xpanes") then
+ xpanes.register_pane("jailbars", {
+ description = S("Jail Bars"),
+ tiles = {"castle_jailbars.png"},
+ drawtype = "airlike",
+ paramtype = "light",
+ textures = {"castle_jailbars.png", "castle_jailbars.png", "xpanes_space.png"},
+ inventory_image = "castle_jailbars.png",
+ wield_image = "castle_jailbars.png",
+ sounds = default.node_sound_metal_defaults(),
+ groups = {cracky=1, pane=1, flow_through=1},
+ recipe = {
+ {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
+ {"default:steel_ingot", "", "default:steel_ingot"},
+ {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}},
+ })
+end
+
+for i = 1, 15 do
+ minetest.register_alias("castle:jailbars_"..i, "xpanes:jailbars_"..i)
+end
+minetest.register_alias("castle:jailbars", "xpanes:jailbars")
diff --git a/castle_gates/gate_functions.lua b/castle_gates/gate_functions.lua
new file mode 100644
index 0000000..4a3c7f7
--- /dev/null
+++ b/castle_gates/gate_functions.lua
@@ -0,0 +1,389 @@
+local MP = minetest.get_modpath(minetest.get_current_modname())
+dofile(MP.."/class_pointset.lua")
+
+-- Given a facedir, returns a set of all the corresponding directions
+local get_dirs = function(facedir)
+ local dirs = {}
+ local top = {[0]={x=0, y=1, z=0},
+ {x=0, y=0, z=1},
+ {x=0, y=0, z=-1},
+ {x=1, y=0, z=0},
+ {x=-1, y=0, z=0},
+ {x=0, y=-1, z=0}}
+ dirs.back = minetest.facedir_to_dir(facedir)
+ dirs.top = top[math.floor(facedir/4)]
+ dirs.right = {
+ x=dirs.top.y*dirs.back.z - dirs.back.y*dirs.top.z,
+ y=dirs.top.z*dirs.back.x - dirs.back.z*dirs.top.x,
+ z=dirs.top.x*dirs.back.y - dirs.back.x*dirs.top.y
+ }
+ dirs.front = vector.multiply(dirs.back, -1)
+ dirs.bottom = vector.multiply(dirs.top, -1)
+ dirs.left = vector.multiply(dirs.right, -1)
+ return dirs
+end
+
+-- Returns the axis that dir points along
+local dir_to_axis = function(dir)
+ if dir.x ~= 0 then
+ return "x"
+ elseif dir.y ~= 0 then
+ return "y"
+ else
+ return "z"
+ end
+end
+
+-- Given a hinge definition, turns it into an axis and placement that can be used by the door rotation.
+local interpret_hinge = function(hinge_def, pos, node_dirs)
+ local axis = dir_to_axis(node_dirs[hinge_def.axis])
+
+ local placement
+ if type(hinge_def.offset) == "string" then
+ placement = vector.add(pos, node_dirs[hinge_def.offset])
+ elseif type(hinge_def.offset) == "table" then
+ placement = vector.new(0,0,0)
+ local divisor = 0
+ for _, val in pairs(hinge_def.offset) do
+ placement = vector.add(placement, node_dirs[val])
+ divisor = divisor + 1
+ end
+ placement = vector.add(pos, vector.divide(placement, divisor))
+ else
+ placement = pos
+ end
+
+ return axis, placement
+end
+
+
+--------------------------------------------------------------------------
+-- Rotation (slightly more complex than sliding)
+
+local facedir_rotate = {
+ ['x'] = {
+ [-1] = {[0]=4, 5, 6, 7, 22, 23, 20, 21, 0, 1, 2, 3, 13, 14, 15, 12, 19, 16, 17, 18, 10, 11, 8, 9}, -- 270 degrees
+ [1] = {[0]=8, 9, 10, 11, 0, 1, 2, 3, 22, 23, 20, 21, 15, 12, 13, 14, 17, 18, 19, 16, 6, 7, 4, 5}, -- 90 degrees
+ },
+ ['y'] = {
+ [-1] = {[0]=3, 0, 1, 2, 19, 16, 17, 18, 15, 12, 13, 14, 7, 4, 5, 6, 11, 8, 9, 10, 21, 22, 23, 20}, -- 270 degrees
+ [1] = {[0]=1, 2, 3, 0, 13, 14, 15, 12, 17, 18, 19, 16, 9, 10, 11, 8, 5, 6, 7, 4, 23, 20, 21, 22}, -- 90 degrees
+ },
+ ['z'] = {
+ [-1] = {[0]=16, 17, 18, 19, 5, 6, 7, 4, 11, 8, 9, 10, 0, 1, 2, 3, 20, 21, 22, 23, 12, 13, 14, 15}, -- 270 degrees
+ [1] = {[0]=12, 13, 14, 15, 7, 4, 5, 6, 9, 10, 11, 8, 20, 21, 22, 23, 0, 1, 2, 3, 16, 17, 18, 19}, -- 90 degrees
+ }
+}
+ --90 degrees CW about x-axis: (x, y, z) -> (x, -z, y)
+ --90 degrees CCW about x-axis: (x, y, z) -> (x, z, -y)
+ --90 degrees CW about y-axis: (x, y, z) -> (-z, y, x)
+ --90 degrees CCW about y-axis: (x, y, z) -> (z, y, -x)
+ --90 degrees CW about z-axis: (x, y, z) -> (y, -x, z)
+ --90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z)
+local rotate_pos = function(axis, direction, pos)
+ if axis == "x" then
+ if direction < 0 then
+ return {x= pos.x, y= -pos.z, z= pos.y}
+ else
+ return {x= pos.x, y= pos.z, z= -pos.y}
+ end
+ elseif axis == "y" then
+ if direction < 0 then
+ return {x= -pos.z, y= pos.y, z= pos.x}
+ else
+ return {x= pos.z, y= pos.y, z= -pos.x}
+ end
+ else
+ if direction < 0 then
+ return {x= -pos.y, y= pos.x, z= pos.z}
+ else
+ return {x= pos.y, y= -pos.x, z= pos.z}
+ end
+ end
+end
+
+local rotate_pos_displaced = function(pos, origin, axis, direction)
+ -- position in space relative to origin
+ local newpos = vector.subtract(pos, origin)
+ newpos = rotate_pos(axis, direction, newpos)
+ -- Move back to original reference frame
+ return vector.add(newpos, origin)
+end
+
+local get_buildable_to = function(pos)
+ return minetest.registered_nodes[minetest.get_node(pos).name].buildable_to
+end
+
+
+local get_door_layout = function(pos, facedir, player)
+ -- This method does a flood-fill looking for all nodes that meet the following criteria:
+ -- belongs to a "castle_gate" group
+ -- has the same "back" direction as the initial node
+ -- is accessible via up, down, left or right directions unless one of those directions goes through an edge that one of the two nodes has marked as a gate edge
+ local door = {}
+
+ door.all = {}
+ door.contains_protected_node = false
+ door.directions = get_dirs(facedir)
+ door.previous_move = minetest.get_meta(pos):get_string("previous_move")
+
+ -- temporary pointsets used while searching
+ local to_test = Pointset.create()
+ local tested = Pointset.create()
+ local can_slide_to = Pointset.create()
+
+ local castle_gate_group_value -- this will be populated from the first gate node we encounter, which will be the one that was clicked on
+
+ to_test:set_pos(pos, true)
+
+ local test_pos, _ = to_test:pop()
+ while test_pos ~= nil do
+ tested:set_pos(test_pos, true) -- track nodes we've looked at
+ local test_node = minetest.get_node(test_pos)
+
+ if test_node.name == "ignore" then
+ --array is next to unloaded nodes, too dangerous to do anything. Abort.
+ return nil
+ end
+
+ if minetest.is_protected(test_pos, player:get_player_name()) and not minetest.check_player_privs(player, "protection_bypass") then
+ door.contains_protected_node = true
+ end
+
+ local test_node_def = minetest.registered_nodes[test_node.name]
+ can_slide_to:set_pos(test_pos, test_node_def.buildable_to == true)
+
+ if test_node_def.paramtype2 == "facedir" then
+ local test_node_dirs = get_dirs(test_node.param2)
+ local coplanar = vector.equals(test_node_dirs.back, door.directions.back)
+
+ if castle_gate_group_value == nil and test_node_def.groups.castle_gate ~= nil then
+ castle_gate_group_value = test_node_def.groups.castle_gate -- read the group value from the first gate node encountered
+ end
+
+ if coplanar and test_node_def.groups.castle_gate == castle_gate_group_value then
+ local entry = {["pos"] = test_pos, ["node"] = test_node}
+ table.insert(door.all, entry)
+ if test_node_def._gate_hinge ~= nil then
+ local axis, placement = interpret_hinge(test_node_def._gate_hinge, test_pos, test_node_dirs)
+ if door.hinge == nil then
+ door.hinge = {axis=axis, placement=placement}
+ elseif door.hinge.axis ~= axis then
+ return nil -- Misaligned hinge axes, door cannot rotate.
+ else
+ local axis_dir = {x=0, y=0, z=0}
+ axis_dir[axis] = 1
+ local displacement = vector.normalize(vector.subtract(placement, door.hinge.placement))
+ if not (vector.equals(displacement, axis_dir) or vector.equals(displacement, vector.multiply(axis_dir, -1))) then
+ return nil -- Misaligned hinge offset, door cannot rotate.
+ end
+ end
+ end
+
+ can_slide_to:set_pos(test_pos, true) -- since this is part of the door, other parts of the door can slide into it
+
+ local test_directions = {"top", "bottom", "left", "right"}
+ for _, dir in pairs(test_directions) do
+ local adjacent_pos = vector.add(test_pos, door.directions[dir])
+ local adjacent_node = minetest.get_node(adjacent_pos)
+ local adjacent_def = minetest.registered_nodes[adjacent_node.name]
+ can_slide_to:set_pos(adjacent_pos, adjacent_def.buildable_to == true or adjacent_def.groups.castle_gate)
+
+ if test_node_def._gate_edges == nil or not test_node_def._gate_edges[dir] then -- if we ourselves are an edge node, don't look in the direction we're an edge in
+ if tested:get_pos(adjacent_pos) == nil then -- don't look at nodes that have already been looked at
+ if adjacent_def.paramtype2 == "facedir" then -- all doors are facedir nodes so we can pre-screen some targets
+
+ local edge_points_back_at_test_pos = false
+ -- Look at the adjacent node's definition. If it's got gate edges, check if they point back at us.
+ if adjacent_def._gate_edges ~= nil then
+ local adjacent_directions = get_dirs(adjacent_node.param2)
+ for dir, val in pairs(adjacent_def._gate_edges) do
+ if vector.equals(vector.add(adjacent_pos, adjacent_directions[dir]), test_pos) then
+ edge_points_back_at_test_pos = true
+ break
+ end
+ end
+ end
+
+ if not edge_points_back_at_test_pos then
+ to_test:set_pos(adjacent_pos, true)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ test_pos, _ = to_test:pop()
+ end
+
+ if door.hinge == nil then
+ --sliding door, evaluate which directions it can go
+ door.can_slide = {top=true, bottom=true, left=true, right=true}
+ for _,door_node in pairs(door.all) do
+ door.can_slide.top = door.can_slide.top and can_slide_to:get_pos(vector.add(door_node.pos, door.directions.top))
+ door.can_slide.bottom = door.can_slide.bottom and can_slide_to:get_pos(vector.add(door_node.pos, door.directions.bottom))
+ door.can_slide.left = door.can_slide.left and can_slide_to:get_pos(vector.add(door_node.pos, door.directions.left))
+ door.can_slide.right = door.can_slide.right and can_slide_to:get_pos(vector.add(door_node.pos, door.directions.right))
+ end
+ else
+ --rotating door, evaluate which direction it can go. Slightly more complicated.
+ local origin = door.hinge.placement
+ local axis = door.hinge.axis
+ local backfront = dir_to_axis(door.directions.back)
+ local leftright = dir_to_axis(door.directions.right)
+
+ door.swings = {}
+
+ for _, direction in pairs({-1, 1}) do
+ door.swings[direction] = true
+ for _, door_node in pairs(door.all) do
+ origin[axis] = door_node.pos[axis]
+ if not vector.equals(door_node.pos, origin) then -- There's no obstruction if the node is literally located along the rotation axis
+ local newpos = rotate_pos_displaced(door_node.pos, origin, axis, direction)
+ local newnode = minetest.get_node(newpos)
+ local newdef = minetest.registered_nodes[newnode.name]
+ if not newdef.buildable_to then -- check if the destination node is free.
+ door.swings[direction] = false
+ break
+ end
+
+ local swing_corner = {} -- the corner of the square "arc" that a Minetest gate swings through
+ local scan_dir
+ swing_corner[axis] = door_node.pos[axis]
+ swing_corner[backfront] = newpos[backfront]
+ swing_corner[leftright] = door_node.pos[leftright]
+ if not (vector.equals(newpos, swing_corner) or vector.equals(door_node.pos, swing_corner)) then -- we're right next to the hinge, no need for further testing
+ scan_dir = vector.direction(newpos, swing_corner) -- get the direction from the new door position toward the swing corner
+ repeat
+ newpos = vector.add(newpos, scan_dir) -- we start with newpos on the destination node, which has already been tested.
+ if not get_buildable_to(newpos) then
+ door.swings[direction] = false
+ end
+ until vector.equals(newpos, swing_corner) or door.swings[direction] == false
+
+ if not (vector.equals(newpos, door_node.pos) or door.swings[direction] == false) then
+ scan_dir = vector.direction(newpos, door_node.pos)
+ newpos = vector.add(newpos, scan_dir) -- the first step here is a freebie since we've already checked swing_corner
+ while not (vector.equals(newpos, door_node.pos) or door.swings[direction] == false) do
+ if not get_buildable_to(newpos) then
+ door.swings[direction] = false
+ end
+ newpos = vector.add(newpos, scan_dir)
+ end
+ end
+ end
+ end
+
+ if door.swings[direction] == false then
+ break
+ end
+
+ end
+ end
+ end
+ return door
+end
+
+
+local slide_gate = function(door, direction)
+ for _, door_node in pairs(door.all) do
+ door_node.pos = vector.add(door_node.pos, door.directions[direction])
+ end
+ door.previous_move = direction
+end
+
+local rotate_door = function (door, direction)
+ if not door.swings[direction] then
+ return false
+ end
+
+ local origin = door.hinge.placement
+ local axis = door.hinge.axis
+
+ for _, door_node in pairs(door.all) do
+ door_node.pos = rotate_pos_displaced(door_node.pos, origin, axis, direction)
+ door_node.node.param2 = facedir_rotate[axis][direction][door_node.node.param2]
+ end
+ return true
+end
+
+
+----------------------------------------------------------------------------------------------------
+-- When creating new gate pieces use this as the "on_rightclick" method of their node definitions
+-- if you want the player to be able to trigger the gate by clicking on that particular node.
+-- If you just want the node to move with the gate and not trigger it this isn't necessary,
+-- only the "castle_gate" group is needed for that.
+
+castle_gates.trigger_gate = function(pos, node, player)
+ local door = get_door_layout(pos, node.param2, player)
+
+ if door ~= nil then
+ for _, door_node in pairs(door.all) do
+ minetest.set_node(door_node.pos, {name="air"})
+ end
+
+ local door_moved = false
+ if door.can_slide ~= nil then -- this is a sliding door
+ if door.previous_move == "top" and door.can_slide.top then
+ slide_gate(door, "top")
+ door_moved = true
+ elseif door.previous_move == "bottom" and door.can_slide.bottom then
+ slide_gate(door, "bottom")
+ door_moved = true
+ elseif door.previous_move == "left" and door.can_slide.left then
+ slide_gate(door, "left")
+ door_moved = true
+ elseif door.previous_move == "right" and door.can_slide.right then
+ slide_gate(door, "right")
+ door_moved = true
+ end
+
+ if not door_moved then -- reverse door's direction for next time
+ if door.previous_move == "top" and door.can_slide.bottom then
+ door.previous_move = "bottom"
+ elseif door.previous_move == "bottom" and door.can_slide.top then
+ door.previous_move = "top"
+ elseif door.previous_move == "left" and door.can_slide.right then
+ door.previous_move = "right"
+ elseif door.previous_move == "right" and door.can_slide.left then
+ door.previous_move = "left"
+ else
+ -- find any open direction
+ for slide_dir, enabled in pairs(door.can_slide) do
+ if enabled then
+ door.previous_move = slide_dir
+ break
+ end
+ end
+ end
+ end
+ elseif door.hinge ~= nil then -- this is a hinged door
+ if door.previous_move == "deosil" then
+ door_moved = rotate_door(door, 1)
+ elseif door.previous_move == "widdershins" then
+ door_moved = rotate_door(door, -1)
+ end
+
+ if not door_moved then
+ if door.previous_move == "deosil" then
+ door.previous_move = "widdershins"
+ else
+ door.previous_move = "deosil"
+ end
+ end
+ end
+
+ for _, door_node in pairs(door.all) do
+ minetest.set_node(door_node.pos, door_node.node)
+ minetest.get_meta(door_node.pos):set_string("previous_move", door.previous_move)
+ end
+
+ if door_moved then
+ minetest.after(1, function()
+ castle_gates.trigger_gate(door.all[1].pos, door.all[1].node, player)
+ end)
+ end
+ end
+end \ No newline at end of file
diff --git a/castle_gates/gate_slots.lua b/castle_gates/gate_slots.lua
new file mode 100644
index 0000000..8f3c095
--- /dev/null
+++ b/castle_gates/gate_slots.lua
@@ -0,0 +1,138 @@
+-- internationalization boilerplate
+local MP = minetest.get_modpath(minetest.get_current_modname())
+local S, NS = dofile(MP.."/intllib.lua")
+
+-- copied from castle_masonry in case that mod is not loaded
+local get_material_properties = function(material)
+ local composition_def
+ local burn_time
+ if material.composition_material ~= nil then
+ composition_def = minetest.registered_nodes[material.composition_material]
+ burn_time = minetest.get_craft_result({method="fuel", width=1, items={ItemStack(material.composition_material)}}).time
+ else
+ composition_def = minetest.registered_nodes[material.craft_material]
+ burn_time = minetest.get_craft_result({method="fuel", width=1, items={ItemStack(material.craft_materia)}}).time
+ end
+
+ local tiles = material.tile
+ if tiles == nil then
+ tiles = composition_def.tile
+ elseif type(tiles) == "string" then
+ tiles = {tiles}
+ end
+
+ local desc = material.desc
+ if desc == nil then
+ desc = composition_def.description
+ end
+
+ return composition_def, burn_time, tiles, desc
+end
+
+local materials
+if minetest.get_modpath("castle_masonry") then
+ materials = castle_masonry.materials
+else
+ materials = {{name="stonebrick", desc=S("Stonebrick"), tile="default_stone_brick.png", craft_material="default:stonebrick"}}
+end
+
+castle_gates.register_gate_slot = function(material)
+ local composition_def, burn_time, tile, desc = get_material_properties(material)
+ local mod_name = minetest.get_current_modname()
+
+ minetest.register_node(mod_name..":"..material.name.."_gate_slot", {
+ drawtype = "nodebox",
+ description = S("@1 Gate Slot", desc),
+ _doc_items_longdesc = castle_gates.doc.gate_slot_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.gate_slot_usagehelp,
+ tiles = tile,
+ paramtype = "light",
+ paramtype2 = "facedir",
+ groups = composition_def.groups,
+ sounds = composition_def.sounds,
+
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, -- body
+ {-0.5, -0.5, -0.75, 0.5, 0.5, -1.5}, -- bracket
+ }
+ },
+
+ collision_box = {
+ type = "fixed",
+ fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 1.5}, -- body
+ },
+ })
+
+ minetest.register_node(mod_name..":"..material.name.."_gate_slot_reverse", {
+ drawtype = "nodebox",
+ description = S("@1 Gate Slot Reverse", desc),
+ _doc_items_longdesc = castle_gates.doc.gate_slot_reverse_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.gate_slot_reverse_usagehelp,
+ tiles = tile,
+ paramtype = "light",
+ paramtype2 = "facedir",
+ groups = composition_def.groups,
+ sounds = composition_def.sounds,
+
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, -1.25, 0.5, 0.5, 0.5}, -- body
+ }
+ },
+
+ collision_box = {
+ type = "fixed",
+ fixed = {-0.5, -0.5, -1.25, 0.5, 0.5, 0.5}, -- body
+ },
+ })
+
+
+ minetest.register_craft({
+ output = mod_name..":"..material.name.."_portcullis_slot 3",
+ recipe = {
+ {material.craft_material,"",material.craft_material},
+ {material.craft_material,"",material.craft_material},
+ {material.craft_material,"",material.craft_material},
+ },
+ })
+
+ minetest.register_craft({
+ output = mod_name..":"..material.name.."_portcullis_slot",
+ type = "shapeless",
+ recipe = {mod_name..":"..material.name.."_portcullis_slot_reverse"},
+ })
+ minetest.register_craft({
+ output = mod_name..":"..material.name.."_portcullis_slot_reverse",
+ type = "shapeless",
+ recipe = {mod_name..":"..material.name.."_portcullis_slot"},
+ })
+
+ if burn_time > 0 then
+ minetest.register_craft({
+ type = "fuel",
+ recipe = mod_name..":"..material.name.."_portcullis_slot",
+ burntime = burn_time * 2,
+ })
+ minetest.register_craft({
+ type = "fuel",
+ recipe = mod_name..":"..material.name.."_portcullis_slot_reverse",
+ burntime = burn_time * 2,
+ })
+ end
+end
+
+castle_gates.register_gate_slot_alias = function(old_mod_name, old_material_name, new_mod_name, new_material_name)
+ minetest.register_alias(old_mod_name..":"..old_material_name.."_gate_slot", new_mod_name..":"..new_material_name.."_gate_slot")
+ minetest.register_alias(old_mod_name..":"..old_material_name.."_gate_slot_reverse", new_mod_name..":"..new_material_name.."_gate_slot_reverse")
+end
+castle_gates.register_gate_slot_alias_force = function(old_mod_name, old_material_name, new_mod_name, new_material_name)
+ minetest.register_alias_force(old_mod_name..":"..old_material_name.."_gate_slot", new_mod_name..":"..new_material_name.."_gate_slot")
+ minetest.register_alias_force(old_mod_name..":"..old_material_name.."_gate_slot_reverse", new_mod_name..":"..new_material_name.."_gate_slot_reverse")
+end
+
+for _, material in pairs(materials) do
+ castle_gates.register_gate_slot(material)
+end
diff --git a/castle_gates/gates.lua b/castle_gates/gates.lua
new file mode 100644
index 0000000..e7195ef
--- /dev/null
+++ b/castle_gates/gates.lua
@@ -0,0 +1,243 @@
+-- internationalization boilerplate
+local MP = minetest.get_modpath(minetest.get_current_modname())
+local S, NS = dofile(MP.."/intllib.lua")
+
+minetest.register_node("castle_gates:portcullis_bars", {
+ drawtype = "nodebox",
+ description = S("Portcullis Bars"),
+ _doc_items_longdesc = castle_gates.doc.portcullis_bars_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.portcullis_bars_usagehelp,
+ groups = {castle_gate = 1, choppy = 1, flow_through = 1},
+ tiles = {
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ },
+ sounds = default.node_sound_wood_defaults(),
+ paramtype = "light",
+ paramtype2 = "facedir",
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.125, -0.5, -0.5, 0.125, 0.5, -0.25}, -- middle bar
+ {-0.5, -0.5, -0.5, -0.375, 0.5, -0.25}, -- side bar
+ {0.375, -0.5, -0.5, 0.5, 0.5, -0.25}, -- side bar
+ {-0.375, 0.1875, -0.4375, 0.375, 0.3125, -0.3125}, -- crosspiece
+ {-0.375, -0.3125, -0.4375, 0.375, -0.1875, -0.3125}, -- crosspiece
+ }
+ },
+ on_rightclick = castle_gates.trigger_gate,
+})
+
+minetest.register_node("castle_gates:portcullis_bars_bottom", {
+ drawtype = "nodebox",
+ description = S("Portcullis Bottom"),
+ _doc_items_longdesc = castle_gates.doc.portcullis_bars_bottom_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.portcullis_bars_bottom_usagehelp,
+ groups = {castle_gate = 1, choppy = 1, flow_through = 1},
+ tiles = {
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_portcullis_mask.png)",
+ },
+ sounds = default.node_sound_wood_defaults(),
+ paramtype = "light",
+ paramtype2 = "facedir",
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.125, -0.5, -0.5, 0.125, 0.5, -0.25}, -- middle bar
+ {-0.5, -0.5, -0.5, -0.375, 0.5, -0.25}, -- side bar
+ {0.375, -0.5, -0.5, 0.5, 0.5, -0.25}, -- side bar
+ {-0.375, 0.1875, -0.4375, 0.375, 0.3125, -0.3125}, -- crosspiece
+ {-0.375, -0.3125, -0.4375, 0.375, -0.1875, -0.3125}, -- crosspiece
+ {-0.0625, -0.5, -0.4375, 0.0625, -0.625, -0.3125}, -- peg
+ {0.4375, -0.5, -0.4375, 0.5, -0.625, -0.3125}, -- peg
+ {-0.5, -0.5, -0.4375, -0.4375, -0.625, -0.3125}, -- peg
+ }
+ },
+ _gate_edges = {bottom=true},
+ on_rightclick = castle_gates.trigger_gate,
+})
+
+minetest.register_craft({
+ output = "castle_gates:portcullis_bars 3",
+ recipe = {
+ {"group:wood","default:steel_ingot","group:wood" },
+ {"group:wood","default:steel_ingot","group:wood" },
+ {"group:wood","default:steel_ingot","group:wood" },
+ },
+})
+
+minetest.register_craft({
+ output = "castle_gates:portcullis_bars",
+ recipe = {
+ {"castle_gates:portcullis_bars_bottom"}
+ },
+})
+
+minetest.register_craft({
+ output = "castle_gates:portcullis_bars_bottom",
+ recipe = {
+ {"castle_gates:portcullis_bars"}
+ },
+})
+
+--------------------------------------------------------------------------------------------------------------
+
+minetest.register_craft({
+ output = "castle_gates:gate_panel 8",
+ recipe = {
+ {"stairs:slab_wood","stairs:slab_wood", ""},
+ {"stairs:slab_wood","stairs:slab_wood", ""},
+ },
+})
+
+minetest.register_node("castle_gates:gate_panel", {
+ drawtype = "nodebox",
+ description = S("Gate Door"),
+ _doc_items_longdesc = castle_gates.doc.gate_panel_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.gate_panel_usagehelp,
+ groups = {choppy = 1, castle_gate = 1},
+ tiles = {
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ },
+ sounds = default.node_sound_wood_defaults(),
+ paramtype = "light",
+ paramtype2 = "facedir",
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, -0.5, 0.5, 0.5, -0.25},
+ }
+ },
+ on_rightclick = castle_gates.trigger_gate,
+})
+
+minetest.register_craft({
+ output = "castle_gates:gate_edge",
+ type = "shapeless",
+ recipe = {"castle_gates:gate_panel"},
+})
+
+minetest.register_node("castle_gates:gate_edge", {
+ drawtype = "nodebox",
+ description = S("Gate Door Edge"),
+ _doc_items_longdesc = castle_gates.doc.gate_edge_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.gate_edge_usagehelp,
+ groups = {choppy = 1, castle_gate = 1},
+ tiles = {
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90",
+ "default_wood.png^[transformR90^(default_coal_block.png^[mask:castle_door_edge_mask.png^[transformFX)",
+ "default_wood.png^[transformR90^(default_coal_block.png^[mask:castle_door_edge_mask.png)",
+ },
+ sounds = default.node_sound_wood_defaults(),
+ paramtype = "light",
+ paramtype2 = "facedir",
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, -0.5, 0.5, 0.5, -0.25},
+ }
+ },
+ _gate_edges = {right=true},
+ on_rightclick = castle_gates.trigger_gate,
+})
+
+minetest.register_craft({
+ output = "castle_gates:gate_edge_handle",
+ type = "shapeless",
+ recipe = {"castle_gates:gate_edge"},
+})
+
+minetest.register_craft({
+ output = "castle_gates:gate_panel",
+ type = "shapeless",
+ recipe = {"castle_gates:gate_edge_handle"},
+})
+
+minetest.register_node("castle_gates:gate_edge_handle", {
+ drawtype = "nodebox",
+ description = S("Gate Door With Handle"),
+ _doc_items_longdesc = castle_gates.doc.gate_edge_handle_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.gate_edge_handle_usagehelp,
+ groups = {choppy = 1, castle_gate = 1},
+ tiles = {
+ "castle_steel.png^(default_wood.png^[mask:castle_door_side_mask.png^[transformR90)",
+ "castle_steel.png^(default_wood.png^[mask:castle_door_side_mask.png^[transformR270)",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:castle_door_side_mask.png)",
+ "castle_steel.png^(default_wood.png^[transformR90^[mask:(castle_door_side_mask.png^[transformFX))",
+ "default_wood.png^[transformR90^(default_coal_block.png^[mask:castle_door_edge_mask.png^[transformFX)^(castle_steel.png^[mask:castle_door_handle_mask.png^[transformFX)",
+ "default_wood.png^[transformR90^(default_coal_block.png^[mask:castle_door_edge_mask.png)^(castle_steel.png^[mask:castle_door_handle_mask.png)",
+ },
+ sounds = default.node_sound_wood_defaults(),
+ paramtype = "light",
+ paramtype2 = "facedir",
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, -0.5, 0.5, 0.5, -0.25},
+ {4/16, -4/16, -2/16, 6/16, 4/16, -3/16},
+ {4/16, -4/16, -9/16, 6/16, 4/16, -10/16},
+ {4/16, -4/16, -9/16, 6/16, -3/16, -3/16},
+ {4/16, 4/16, -9/16, 6/16, 3/16, -3/16},
+ }
+ },
+ _gate_edges = {right=true},
+ on_rightclick = castle_gates.trigger_gate,
+})
+
+
+------------------------------------------------------------------------------
+
+minetest.register_craft({
+ output = "castle_gates:gate_hinge 3",
+ recipe = {
+ {"", "castle_gates:gate_panel", ""},
+ {"default:steel_ingot", "castle_gates:gate_panel", ""},
+ {"", "castle_gates:gate_panel", ""}
+ },
+})
+
+minetest.register_node("castle_gates:gate_hinge", {
+ drawtype = "nodebox",
+ description = S("Gate Door With Hinge"),
+ _doc_items_longdesc = castle_gates.doc.gate_hinge_longdesc,
+ _doc_items_usagehelp = castle_gates.doc.gate_hinge_usagehelp,
+ groups = {choppy = 1, castle_gate = 1},
+ tiles = {
+ "default_wood.png^[transformR90",
+ },
+ sounds = default.node_sound_wood_defaults(),
+ paramtype = "light",
+ paramtype2 = "facedir",
+
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, -0.5, 0.5, 0.5, -0.25},
+ {-10/16, -4/16, -10/16, -6/16, 4/16, -6/16},
+ }
+ },
+ collision_box = {
+ type = "fixed",
+ fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, -0.25},
+ },
+
+ _gate_hinge = {axis="top", offset={"front","left"}},
+ on_rightclick = castle_gates.trigger_gate,
+})
diff --git a/castle_gates/init.lua b/castle_gates/init.lua
new file mode 100644
index 0000000..3292cef
--- /dev/null
+++ b/castle_gates/init.lua
@@ -0,0 +1,8 @@
+castle_gates = {}
+
+local modpath = minetest.get_modpath(minetest.get_current_modname())
+dofile(modpath.."/doc.lua")
+dofile(modpath.."/gate_functions.lua")
+dofile(modpath.."/gate_slots.lua")
+dofile(modpath.."/gates.lua")
+dofile(modpath.."/doors.lua")
diff --git a/castle_gates/intllib.lua b/castle_gates/intllib.lua
new file mode 100644
index 0000000..6669d72
--- /dev/null
+++ b/castle_gates/intllib.lua
@@ -0,0 +1,45 @@
+
+-- Fallback functions for when `intllib` is not installed.
+-- Code released under Unlicense <http://unlicense.org>.
+
+-- Get the latest version of this file at:
+-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
+
+local function format(str, ...)
+ local args = { ... }
+ local function repl(escape, open, num, close)
+ if escape == "" then
+ local replacement = tostring(args[tonumber(num)])
+ if open == "" then
+ replacement = replacement..close
+ end
+ return replacement
+ else
+ return "@"..open..num..close
+ end
+ end
+ return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
+end
+
+local gettext, ngettext
+if minetest.get_modpath("intllib") then
+ if intllib.make_gettext_pair then
+ -- New method using gettext.
+ gettext, ngettext = intllib.make_gettext_pair()
+ else
+ -- Old method using text files.
+ gettext = intllib.Getter()
+ end
+end
+
+-- Fill in missing functions.
+
+gettext = gettext or function(msgid, ...)
+ return format(msgid, ...)
+end
+
+ngettext = ngettext or function(msgid, msgid_plural, n, ...)
+ return format(n==1 and msgid or msgid_plural, ...)
+end
+
+return gettext, ngettext
diff --git a/castle_gates/locale/template.pot b/castle_gates/locale/template.pot
new file mode 100644
index 0000000..2122c17
--- /dev/null
+++ b/castle_gates/locale/template.pot
@@ -0,0 +1,202 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-03-01 23:41-0700\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: doc.lua:11
+msgid "Heavy wooden bars designed to prevent entry even to siege equipment."
+msgstr ""
+
+#: doc.lua:12
+msgid ""
+"Place these bars in a structure together and they will slide as a unified "
+"gate when clicked on."
+msgstr ""
+
+#: doc.lua:14
+msgid ""
+"The bottom edge of a portcullis gate, with knobs to lock securely into the "
+"floor."
+msgstr ""
+
+#: doc.lua:15
+msgid ""
+"This block can be used to define the edge of a portcullius that meets up "
+"with another gate, should you have an arrangement like that. Otherwise it's "
+"just decorative."
+msgstr ""
+
+#: doc.lua:17
+msgid "A basic gate panel."
+msgstr ""
+
+#: doc.lua:18
+msgid ""
+"This gate segment will move in unison with adjoining gate segments when "
+"right-clicked."
+msgstr ""
+
+#: doc.lua:20
+msgid "A gate panel with a defined edge."
+msgstr ""
+
+#: doc.lua:21
+msgid ""
+"The darkened edge of this panel marks the edge of the gate it's a part of. "
+"You can use these when building double doors to ensure the two parts swing "
+"separately, for example. Note that edges aren't strictly necessary for gates "
+"that stand alone."
+msgstr ""
+
+#: doc.lua:23
+msgid "A gate edge with a handle."
+msgstr ""
+
+#: doc.lua:24
+msgid ""
+"The handle is basically decorative, a door this size can be swung by "
+"clicking anywhere on it. But the darkened edge of this panel is useful for "
+"defining the edge of a gate when it abuts a partner to the side."
+msgstr ""
+
+#: doc.lua:26
+msgid "A hinged gate segment that allows a gate to swing."
+msgstr ""
+
+#: doc.lua:27
+msgid ""
+"If you have more than one hinge in your gate, make sure the hinges line up "
+"correctly otherwise the gate will not be able to swing. The hinge is the "
+"protruding block along the edge of the gate panel."
+msgstr ""
+
+#: doc.lua:29
+msgid "A block with a slot to allow an adjacent sliding gate through."
+msgstr ""
+
+#: doc.lua:30
+msgid ""
+"This block is designed to extend into a neighboring node that a sliding gate "
+"passes through, to provide a tight seal for the gate to move through without "
+"allowing anything else to squeeze in."
+msgstr ""
+
+#: doc.lua:32
+msgid ""
+"A block that extends into an adjacent node to provide a tight seal for a "
+"large gate."
+msgstr ""
+
+#: doc.lua:33
+msgid ""
+"Two nodes cannot occupy the same space, but this block extends into a "
+"neighboring node's space to allow for gates to form a tight seal. It can be "
+"used with sliding gates or swinging gates."
+msgstr ""
+
+#: doc.lua:37
+msgid "Gates"
+msgstr ""
+
+#: doc.lua:38
+msgid ""
+"Gates are large multi-node constructions that swing on hinges or slide out "
+"of the way when triggered."
+msgstr ""
+
+#: doc.lua:43
+msgid "Gate construction"
+msgstr ""
+
+#: doc.lua:45
+msgid ""
+"Gates are multi-node constructions, usually (though not always) consisting "
+"of multiple node types that fit together into a unified whole. The "
+"orientation of gate nodes is significant, so a screwdriver will be a helpful "
+"tool when constructing gates.\n"
+"\n"
+"A gate's extent is determined by a \"flood fill\" operation. When you "
+"trigger a gate block, all compatible neighboring blocks will be considered "
+"part of the same structure and will move in unison. Only gate blocks that "
+"are aligned with each other will be considered part of the same gate. If you "
+"wish to build adjoining gates (for example, a large pair of double doors "
+"that meet in the center) you'll need to make use of gate edge blocks to "
+"prevent it all from being considered one big door. Note that if your gate "
+"does not abut any other gates you don't actually need to define its edges "
+"this way - you don't have to use edge blocks in this case.\n"
+"\n"
+"If a gate has no hinge nodes it will be considered a sliding gate. When "
+"triggered, the gate code will search for a direction that the gate can slide "
+"in and will move it in that direction at a rate of one block-length per "
+"second. Once it reaches an obstruction it will stop, and when triggered "
+"again it will try sliding in the opposite direction.\n"
+"\n"
+"If a gate has hinge nodes then triggering it will cause the gate to try "
+"swinging around the hinge. If the gate has multiple hinges and they don't "
+"line up properly the gate will be unable to move. Note that the gate can "
+"only exist in 90-degree increments of orientation, but the gate still looks "
+"for obstructions in the region it is swinging through and will not swing if "
+"there's something in the way."
+msgstr ""
+
+#: doors.lua:8
+msgid "Oak Door"
+msgstr ""
+
+#: doors.lua:22
+msgid "Jail Door"
+msgstr ""
+
+#: doors.lua:43
+msgid "Jail Bars"
+msgstr ""
+
+#: gate_slots.lua:36
+msgid "Stonebrick"
+msgstr ""
+
+#: gate_slots.lua:45
+msgid "@1 Gate Slot"
+msgstr ""
+
+#: gate_slots.lua:70
+msgid "@1 Gate Slot Reverse"
+msgstr ""
+
+#: gates.lua:7
+msgid "Portcullis Bars"
+msgstr ""
+
+#: gates.lua:37
+msgid "Portcullis Bottom"
+msgstr ""
+
+#: gates.lua:104
+msgid "Gate Door"
+msgstr ""
+
+#: gates.lua:136
+msgid "Gate Door Edge"
+msgstr ""
+
+#: gates.lua:175
+msgid "Gate Door With Handle"
+msgstr ""
+
+#: gates.lua:218
+msgid "Gate Door With Hinge"
+msgstr ""
diff --git a/castle_gates/mod.conf b/castle_gates/mod.conf
new file mode 100644
index 0000000..1ad7f80
--- /dev/null
+++ b/castle_gates/mod.conf
@@ -0,0 +1 @@
+name = castle_gates
diff --git a/castle_gates/screenshot.png b/castle_gates/screenshot.png
new file mode 100644
index 0000000..54163c8
--- /dev/null
+++ b/castle_gates/screenshot.png
Binary files differ
diff --git a/castle_gates/textures/LICENSE.txt b/castle_gates/textures/LICENSE.txt
new file mode 100644
index 0000000..38224f3
--- /dev/null
+++ b/castle_gates/textures/LICENSE.txt
@@ -0,0 +1,11 @@
+16 px textures based on Castle mod
+original textures by Philipner
+
+License Textures: Napiophelios - CC-BY-SA 3.0
+
+-castle_door_jail.png
+-castle_door_oak.png
+-castle_jail_door_inv.png
+-castle_jailbars.png
+-castle_oak_door_inv.png
+-castle_steel.png
diff --git a/castle_gates/textures/castle_door_edge_mask.png b/castle_gates/textures/castle_door_edge_mask.png
new file mode 100644
index 0000000..59ae96b
--- /dev/null
+++ b/castle_gates/textures/castle_door_edge_mask.png
Binary files differ
diff --git a/castle_gates/textures/castle_door_handle_mask.png b/castle_gates/textures/castle_door_handle_mask.png
new file mode 100644
index 0000000..6417aaf
--- /dev/null
+++ b/castle_gates/textures/castle_door_handle_mask.png
Binary files differ
diff --git a/castle_gates/textures/castle_door_jail.png b/castle_gates/textures/castle_door_jail.png
new file mode 100644
index 0000000..0d4f7bf
--- /dev/null
+++ b/castle_gates/textures/castle_door_jail.png
Binary files differ
diff --git a/castle_gates/textures/castle_door_oak.png b/castle_gates/textures/castle_door_oak.png
new file mode 100644
index 0000000..2282fb2
--- /dev/null
+++ b/castle_gates/textures/castle_door_oak.png
Binary files differ
diff --git a/castle_gates/textures/castle_door_side_mask.png b/castle_gates/textures/castle_door_side_mask.png
new file mode 100644
index 0000000..039803c
--- /dev/null
+++ b/castle_gates/textures/castle_door_side_mask.png
Binary files differ
diff --git a/castle_gates/textures/castle_jail_door_inv.png b/castle_gates/textures/castle_jail_door_inv.png
new file mode 100644
index 0000000..5121fb8
--- /dev/null
+++ b/castle_gates/textures/castle_jail_door_inv.png
Binary files differ
diff --git a/castle_gates/textures/castle_jailbars.png b/castle_gates/textures/castle_jailbars.png
new file mode 100644
index 0000000..e2cc911
--- /dev/null
+++ b/castle_gates/textures/castle_jailbars.png
Binary files differ
diff --git a/castle_gates/textures/castle_oak_door_inv.png b/castle_gates/textures/castle_oak_door_inv.png
new file mode 100644
index 0000000..1122d51
--- /dev/null
+++ b/castle_gates/textures/castle_oak_door_inv.png
Binary files differ
diff --git a/castle_gates/textures/castle_portcullis_mask.png b/castle_gates/textures/castle_portcullis_mask.png
new file mode 100644
index 0000000..ac5d8e2
--- /dev/null
+++ b/castle_gates/textures/castle_portcullis_mask.png
Binary files differ
diff --git a/castle_gates/textures/castle_steel.png b/castle_gates/textures/castle_steel.png
new file mode 100644
index 0000000..28ecd60
--- /dev/null
+++ b/castle_gates/textures/castle_steel.png
Binary files differ