This commit is contained in:
1F616EMO 2023-04-06 08:05:32 +08:00
parent db4ee49cae
commit 3ece6a545a
No known key found for this signature in database
GPG Key ID: CDF659A4657D3557
10 changed files with 1031 additions and 1 deletions

7
CHANGELOG.md Normal file
View File

@ -0,0 +1,7 @@
This mod is a fork of [Sokomine's apartment mod][upstream] from the commit [`86b9888`](https://github.com/Sokomine/apartment/commit/86b9888c703fd2711dd86f75102753024ebfac2b). This fork redone the formspec of apartment controller and added configuration copiers. Apart from that, this fork removed the apartment spawner, which was seldomly used and gliched.
## Changelog
* _Refer to [the upstream][upstream] for changelog before the fork._
* 2023/04/05: Removed apartment spawner, redone formspec, added configuration copier.
[upstream]: https://github.com/Sokomine/apartment/

15
COPYING.txt Normal file
View File

@ -0,0 +1,15 @@
Copyright (C) 2014 Sokomine
Copyright (C) 2023 1F616EMO
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -1 +1,8 @@
# apartment
This mod is a fork of [Sokomine's apartment mod][upstream] from the commit [`86b9888`](https://github.com/Sokomine/apartment/commit/86b9888c703fd2711dd86f75102753024ebfac2b). This fork redone the formspec of apartment controller and added configuration copiers. Apart from that, this fork removed the apartment spawner, which was seldom used and glitched.
## Features
This mod provides a "Apartment Configuration Panel", which allows apartment owners to set up a self-service rental system. Upon renting by another player, all the locked blocks inside the apartment will become the player's.
This mod also provides an "Apartment Configuration Copier", for copying apartment configurations (i.e. category, description, size) to another not configured panel. To copy, punch a configured panel with the copier; to paste, right-click a not configured panel. When being pasted, the description will be altered if the original description ends with a number.

173
chg_owner.lua Normal file
View File

@ -0,0 +1,173 @@
local S_default = minetest.get_translator("default")
local S_doors = minetest.get_translator("doors")
local S_currency = minetest.get_translator("currency")
local S = minetest.get_translator("apartment")
local function sstarts(String,Start) -- http://stackoverflow.com/questions/22831701/ddg#22831842
return string.sub(String,1,string.len(Start))==Start
end
apartment.chg_owner = function(panel_pos,pos,category,descr,original_owner,now_owner,owner,actor)
local meta = minetest.get_meta(pos)
local n = minetest.get_node(pos)
if not meta then return false, "META_NF" end
local node_now_owner = meta:get_string("owner")
if node_now_owner == "" then
node_now_owner = meta:get_string("doors_owner")
end; if node_now_owner == "" then
node_now_owner = original_owner
end
if node_now_owner == original_owner or node_now_owner == now_owner then
local owner_or_orig = (owner ~= "" and owner) or original_owner
if n.name == "locks:shared_locked_chest" then
locks:lock_set_owner( pos, owner_or_orig, "Shared locked chest" )
elseif n.name == "locks:shared_locked_furnace" then
locks:lock_set_owner( pos, owner_or_orig, "Shared locked furnace" )
elseif n.name == "locks:shared_locked_sign_wall" then
locks:lock_set_owner( pos, owner_or_orig, "Shared locked sign" )
elseif sstarts(n.name,"locks:door") then
locks:lock_set_owner( pos, owner_or_orig, "Shared locked door" )
elseif n.name == "vendor:vendor" or n.name == "vendor:depositor" then
meta:set_string("owner", owner_or_orig)
vendor.refresh(pos, nil)
elseif n.name == "travelnet:travelnet" or n.name == "travelnet:elevator"
or n.name == "locked_travelnet:elevator" or n.name == "locked_travelnet:travelnet" then
local oldmetadata = meta:to_table()
travelnet.remove_box(pos, nil, oldmetadata, actor )
meta:set_string("owner", owner_or_orig)
minetest.registered_nodes[ n.name ].after_place_node(pos, actor, nil)
elseif n.name == "smartshop:shop" then
meta:set_string("owner", owner_or_orig)
if meta:get_int("type") == 0 and not (minetest.check_player_privs(owner_or_orig, {creative=true}) or minetest.check_player_privs(owner_or_orig, {give=true})) then
-- Avoid non-unlimited player taking unlimited player's smartshop'
meta:set_int("creative",0)
meta:set_int("type",1)
end
smartshop.update_info(pos)
else -- These does not require special processing
local disp_pname = owner or "- vacant -"
local disp_descr = descr .. "@" .. category
local infotext = ""
if n.name == "default:chest_locked" or n.name == "default:chest_locked_open" then
if original_owner == owner then
infotext = S_default("Locked Chest (owned by @1)",original_owner)
else
infotext = S("@1 in Ap. @2 (@3)",S_default("Locked Chest"),disp_descr,disp_pname)
end
elseif sstarts(n.name,"doors:door_steel_") then
if original_owner == owner then
infotext = S_doors("Steel Door") .. "\n" .. S_doors("Owned by @1",original_owner)
else
infotext = S("Apartment @1 (@2)",disp_descr,disp_pname)
end
elseif n.name == "locked_sign:sign_wall_locked" then
if original_owner == owner then
infotext = "\"\" ("..original_owner..")"
else
infotext = "\"\" ("..disp_pname..")"
end
elseif n.name == 'apartment:apartment_free' or n.name == 'apartment:apartment_occupied' then
if vector.equals(panel_pos,pos) then
if original_owner == owner then
infotext = S("Rent apartment @1 here by right-clicking this panel!",disp_descr)
else
infotext = S("Apartment rental control panel for apartment @1 (@2)",disp_descr,disp_pname)
end
end
elseif n.name == "technic:iron_locked_chest" then
if original_owner == owner then
infotext = "Iron Locked Chest (owned by "..original_owner..")"
else
infotext = "Iron Locked Chest in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "technic:copper_locked_chest" then
if original_owner == owner then
infotext = "Copper Locked Chest (owned by "..original_owner..")"
else
infotext = "Copper Locked Chest in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "technic:silver_locked_chest" then
if original_owner == owner then
infotext = "Silver Locked Chest (owned by "..original_owner..")"
else
infotext = "Silver Locked Chest in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "technic:gold_locked_chest" then
if original_owner == owner then
infotext = "Gold Locked Chest (owned by "..original_owner..")"
else
infotext = "Gold Locked Chest in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "technic:mithril_locked_chest" then
if original_owner == owner then
infotext = "Mithril Locked Chest (owned by "..original_owner..")"
else
infotext = "Mithril Locked Chest in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "inbox:empty" then
if original_owner == owner then
infotext = original_owner.."'s Mailbox";
else
infotext = disp_pname.."'s Mailbox";
end
elseif n.name == "itemframes:frame" then
if original_owner == owner then
infotext = "Item frame (owned by"..original_owner..")"
else
infotext = "Item frame in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "itemframes:pedestral" then
if original_owner == owner then
infotext = "Pedestral frame (owned by "..original_owner..")"
else
infotext = "Pedestral frame in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "currency:safe" then
if original_owner == owner then
infotext = S_currency("Safe (owned by @1)",original_owner)
else
infotext = S("@1 in Ap. @2 (@3)",S_currency("Safe"),disp_descr,disp_pname)
end
elseif n.name == "currency:shop" then
if original_owner == owner then
infotext = S_currency("Exchange shop (owned by @1)",original_owner)
else
infotext = S("@1 in Ap. @2 (@3)",S_currency("Shop"),disp_descr,disp_pname)
end
elseif n.name == "bitchange:bank" then
if original_owner == owner then
infotext = "Bank (owned by "..original_owner..")"
else
infotext = "Bank in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "bitchange:moneychanger" then
if original_owner == owner then
infotext = "Moneychanger (owned by "..original_owner..")"
else
infotext = "Moneychanger in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "bitchange:warehouse" then
if original_owner == owner then
infotext = "Warehouse (owned by "..original_owner..")"
else
infotext = "Warehouse in Ap. "..descr.." ("..disp_pname..")"
end
elseif n.name == "bitchange:shop" then
local pname = disp_pname
if original_owner == owner then
pname = original_owner
end
if meta:get_string('title') ~= '' then
infotext = "Exchange shop \""..( meta:get_string('title')).."\" ("..pname..")";
else
infotext = "Exchange shop ("..pname..")";
end
end
if infotext ~= "" then
meta:set_string("infotext",infotext)
meta:set_string("owner",owner_or_orig)
meta:set_string("doors_owner",owner_or_orig)
end
end
end
end

333
gui.lua Normal file
View File

@ -0,0 +1,333 @@
local S = minetest.get_translator("apartment")
local gui = flow.widgets
local p = {}
p.configure_gui = flow.make_gui(function(player,ctx)
local name = player:get_player_name()
local pos = ctx.pos
-- Error and privilege handling: These are unlikely to be reached, but anyway check them
if not apartment.working then return gui.Label { label = "MOD_ERR" } end
if not pos then return gui.Label { label = "NO_POS" } end
local meta = ctx.meta or minetest.get_meta(pos)
if meta:get_string( 'original_owner' ) ~= name then return gui.Label { label = "NOT_OWNER" } end
local defaults = {}
if ctx.defaults then
for x,y in pairs(ctx.defaults) do
defaults[x] = tostring(y)
end
ctx.defaults = nil
end
return gui.VBox { w = 10,
gui.Label { label = S("Apartment Configuration") },
gui.Box {w = 1, h = 0.05, color = "grey"},
gui.HBox {
gui.Label { label = S("Category"), w = 2 },
gui.Field { name = "category", expand = true, default = defaults.category },
gui.Tooltip {
tooltip_text = S("In one category, every player can only rent one apartment."),
gui_element_name = "category",
}
},
gui.HBox {
gui.Label { label = S("Name or ID"), w = 2 },
gui.Field { name = "descr", expand = true, default = defaults.descr },
gui.Tooltip {
tooltip_text = S("This is the unique ID of this apartment in this category."),
gui_element_name = "descr",
}
},
gui.Box {w = 1, h = 0.05, color = "grey"},
gui.Label { label = S("The apartment shall extend this many blocks from this panel:")},
gui.HBox {
gui.VBox {
gui.Field {
name = "size_back", label = S("Back"),
w = 1, expand = true, align_h = "center",
default = defaults.size_back,
},
gui.HBox {
gui.Field {
name = "size_left",
label = S("Left"),
w = 1, expand = true,
default = defaults.size_left,
},
gui.Image {
w = 1, h = 1, texture_name = "apartment_controls_vacant.png",
expand = true,
},
gui.Field {
name = "size_right",
label = S("Right"),
w = 1, expand = true,
default = defaults.size_right,
},
},
gui.Field {
name = "size_front", label = S("Front"),
w = 1, expand = true, align_h = "center",
default = defaults.size_front
},
},
gui.VBox { w = 4, expand = true, align_h = "right",
gui.HBox {
gui.Field {
name = "size_up",
label = S("Up"),
expand = true, w = 1,
default = defaults.size_up,
},
gui.Field {
name = "size_down",
label = S("Down"),
expand = true, w = 1,
default = defaults.size_down,
},
},
gui.Spacer {},
gui.ButtonExit {
name = "abort",
label = S("Abort"),
},
gui.ButtonExit {
name = "store",
label = S("Store and Offer"),
on_event = function(player,ctx)
local name = player:get_player_name()
local pos = ctx.pos
-- Error and privilege handling: These are unlikely to be reached, but anyway check them
if not apartment.working then return false end
if not pos then return false end
local meta = ctx.meta or minetest.get_meta(pos)
if meta:get_string( 'original_owner' ) ~= name then return gui.Label { label = "NOT_OWNER" } end
local fields = ctx.form
local size_left = tonumber( fields.size_left or -1) or -1
local size_right = tonumber( fields.size_right or -1) or -1
local size_up = tonumber( fields.size_up or -1) or -1
local size_down = tonumber( fields.size_down or -1) or -1
local size_front = tonumber( fields.size_front or -1) or -1
local size_back = tonumber( fields.size_back or -1) or -1
local category = tostring( fields.category ) or ""
local descr = tostring( fields.descr ) or ""
if size_left < 0 or size_left > 10
or size_right < 0 or size_right > 10
or size_up < 0 or size_up > 10
or size_down < 0 or size_down > 10
or size_front < 0 or size_front > 10
or size_back < 0 or size_back > 10
or not category or not descr then
minetest.chat_send_player(name, S('Error: Not all fields have been filled in or the area is too large.'))
return
end
if apartment.apartments[category] and apartment.apartments[category][descr] then
minetest.chat_send_player(name, S("Error: The apartment @1@@@2 already exists. Please choose a different name or category.", descr, category))
return
end
meta:set_int( 'size_up', size_up )
meta:set_int( 'size_down', size_down )
meta:set_int( 'size_right', size_right )
meta:set_int( 'size_left', size_left )
meta:set_int( 'size_front', size_front )
meta:set_int( 'size_back', size_back )
meta:set_string( 'descr', fields.descr )
meta:set_string( 'category', fields.category )
local status, msg = apartment.rent( pos, name, nil, player )
if status then
minetest.chat_send_player(name, S("Apartment @1@@@2 is ready for rental.",descr,category))
else
minetest.chat_send_player(name, S("Failed to create apartment @1@@@2. (@3)",descr,category,msg))
end
end
},
gui.Style {
selectors = {"abort"},
props = {textcolor = "#FF0000"}
}
}
}
}
end)
p.panel_control = flow.make_gui(function(player,ctx)
local name = player:get_player_name()
local pos = ctx.pos
-- Error and privilege handling: These are unlikely to be reached, but anyway check them
if not apartment.working then return gui.Label { label = "MOD_ERR" } end
if not pos then return gui.Label { label = "NO_POS" } end
local meta = ctx.meta or minetest.get_meta(pos)
local original_owner = meta:get_string('original_owner')
local owner = meta:get_string('owner')
local owner_disp = ((owner == original_owner or owner == "") and "- vacant -" or owner)
local descr = meta:get_string('descr')
local category = meta:get_string('category')
local btn = gui.Label { label = S("This apartment \nhave been rented.") }
if not(apartment.apartments[category] and apartment.apartments[category][descr]) then
btn = gui.Label { label = S("This apartment \nwas glitched.") }
elseif name == owner and owner ~= original_owner then
btn = gui.Button {
label = S("Unrent"),
on_event = function(player,ctx)
local name = player:get_player_name()
local pos = ctx.pos
-- Error and privilege handling: These are unlikely to be reached, but anyway check them
if not apartment.working then return end
if not pos then return end
local meta = ctx.meta or minetest.get_meta(pos)
local original_owner = meta:get_string( 'original_owner' )
local descr = meta:get_string('descr')
local category = meta:get_string('category')
local status, msg = apartment.rent(pos, original_owner, nil, player)
if status then
minetest.chat_send_player(name,S("You have ended your rent of apartment @1@@@2. It is free for others to rent again.",descr,category))
else
minetest.chat_send_player(name, 'Something went wrong when giving back the apartment @1@@@2. (@3)',descr,category,msg)
end
return true
end
}
elseif owner == original_owner then
if name == original_owner then
btn = gui.Label { label = S("Dig the panel \nto remove this apartment.") }
else
btn = gui.Button {
label = S("Rent"),
on_event = function(player,ctx)
local name = player:get_player_name()
local pos = ctx.pos
-- Error and privilege handling: These are unlikely to be reached, but anyway check them
if not apartment.working then return end
if not pos then return end
local meta = ctx.meta or minetest.get_meta(pos)
local descr = meta:get_string('descr')
local category = meta:get_string('category')
if not(apartment.apartments[category] and apartment.apartments[category][descr]) then
minetest.chat_send_player(name,S("This apartment (@1@@@2) is not registered. Please unrent it and ask the original builder to re-configure this panel.",descr,category))
return true
end
for k,v in pairs(apartment.apartments[category]) do
if v and v.owner == name then
minetest.chat_send_player(name,S("Sorry, you can only rent one apartment per category at a time. You have already rented apartment @1@@@2.",descr,category))
return false
end
end
local status, msg = apartment.rent(pos, name, nil, player)
if status then
minetest.chat_send_player(name,S("You have rented apartment @1@@@2. Enjoy your stay!",descr,category))
else
minetest.chat_send_player(name, 'Something went wrong when renting the apartment @1@@@2. (@3)',descr,category,msg)
end
return true
end
}
end
elseif name == original_owner or minetest.check_player_privs(name,{apartment_unrent=true}) then
btn = gui.Button {
label = S("Force Unrent"),
on_event = function(player,ctx)
local name = player:get_player_name()
local pos = ctx.pos
-- Error and privilege handling: These are unlikely to be reached, but anyway check them
if not apartment.working then return end
if not pos then return end
local meta = ctx.meta or minetest.get_meta(pos)
local original_owner = meta:get_string( 'original_owner' )
local descr = meta:get_string('descr')
local category = meta:get_string('category')
local owner = meta:get_string('owner')
if not(name == original_owner or minetest.check_player_privs(name,{apartment_unrent=true})) then return end
local status, msg = apartment.rent(pos, original_owner, nil, player)
if status then
minetest.chat_send_player(name,S("Player @1 has been thrown out of apartment @2@@@3. It can now be rented by another player.",owner,descr,category))
else
minetest.chat_send_player(name, 'Something went wrong when throwing @1 out of the apartment @2@@@3. (@4)',owner,descr,category,msg)
end
return true
end
}
end
local size_up = meta:get_int( 'size_up' );
local size_down = meta:get_int( 'size_down' );
local size_right = meta:get_int( 'size_right' );
local size_left = meta:get_int( 'size_left' );
local size_front = meta:get_int( 'size_front' );
local size_back = meta:get_int( 'size_back' );
return gui.VBox { w = 10,
gui.HBox {
gui.Label { label = S("Apartment @1@@@2",descr,category) },
gui.ButtonExit {
label = "x", w = 0.5, h = 0.5,
expand = true, align_h = "right",
}
},
gui.Box {w = 1, h = 0.05, color = "grey"},
gui.HBox {
gui.Label { label = S("Owner: @1",original_owner), w=1, expand = true },
gui.Label { label = S("Now rented by: @1",owner_disp), w = 1, expand = true },
},
gui.Box {w = 1, h = 0.05, color = "grey"},
gui.Label { label = S("The apartment extends this many blocks from this panel:")},
gui.HBox {
gui.VBox {
gui.Label {
label = S("Back\n@1",size_back),
w = 1, expand = true, align_h = "center"
},
gui.HBox {
gui.Label {
label = S("Left\n@1",size_left),
w = 1, expand = true,
},
gui.Image {
w = 1, h = 1, texture_name = "apartment_controls_vacant.png",
expand = true,
},
gui.Label {
label = S("Right\n@1",size_right),
w = 1, expand = true,
},
},
gui.Label {
label = S("Front\n@1",size_front),
w = 1, expand = true, align_h = "center"
},
},
gui.VBox { w = 4, expand = true, align_h = "right",
gui.HBox {
gui.Label {
label = S("Up\n@1",size_up),
expand = true, w = 1,
},
gui.Label {
label = S("Down\n@1",size_down),
expand = true, w = 1,
},
},
gui.Spacer {},
btn,
}
}
}
end)
return p

491
init.lua Normal file
View File

@ -0,0 +1,491 @@
local S = minetest.get_translator("apartment")
apartment = {}
apartment.enable_aphome_command = minetest.settings:get_bool("apartment.enable_aphome_command",true)
minetest.register_privilege("apartment_unrent", {
description = S("Allows player to throw other players out from apartments not owned by them."),
give_to_singleplayer = false
})
-- v2 will contain information about all apartments of the server in the form:
-- { cat = { ap_descr = { pos = {x=0,y=0,z=0}, original_owner='', owner='' } } }
apartment.apartments = {}
apartment.working = true
apartment.data_modified = false
local WP = minetest.get_worldpath()
local MP = minetest.get_modpath("apartment")
apartment.save_path_v1 = WP .. "/apartment.data"
apartment.save_path = WP .. "/apartment_v2.data"
apartment.save_path_bak = WP .. "/apartment_v2.data.bak"
apartment.gui = dofile(MP .. "/gui.lua")
apartment.save_data = function()
minetest.log("action","[Apartment] Saving data")
for k,v in pairs(apartment.apartments) do
if next(v) == nil then
minetest.log("action","[Apartment] Category " .. k .. " is empty, removed.")
apartment.apartments[k] = nil
end
end
local data = minetest.serialize(apartment.apartments)
if minetest.safe_file_write(apartment.save_path, data) then
return true
else
minetest.log("error","[Apartment] Failed to write save file: " .. apartment.save_path)
end
end
local function save_loop()
if apartment.data_modified then
apartment.data_modified = false
apartment.save_data()
end
minetest.after(1,save_loop)
end
minetest.after(1,save_loop)
minetest.register_on_shutdown(apartment.save_data)
apartment.migrate_v1 = function()
minetest.log("action","[Apartment] Finding old files...")
local file_v1 = io.open(apartment.save_path_v1, "r")
if not file_v1 then
minetest.log("error","[Apartment] v1 save file not found.")
return false
end
local s_data_v1 = file_v1:read("*all")
if s_data_v1 == "" then
minetest.log("error","[Apartment] v1 save file was empty.")
file_v1:close()
return false
end
local data_v1 = minetest.deserialize(s_data_v1)
if data_v1 == nil then -- Known bug of advtrains: when file grows, it may fail to load.
apartment.working = false -- Even if pcall()ed, the mod will still refuse to do any operations.
error("[Apartment] Failed to deserialize v1 data. Please solve this problem manually.")
return false
end
local data_v2 = {}
for k,v in pairs(data_v1) do
if v.category then
if not data_v2[v.category] then data_v2[v.category] = {} end
data_v2[v.category][k] = { pos = v.pos, owner = v.owner }
end
end
return data_v2
end
apartment.restore_data = function()
local file = io.open(apartment.save_path, "r")
if not file then
minetest.log("action","[Apartment] v2 save file not found. Finding v1 files...")
local data = apartment.migrate_v1()
if data then
apartment.apartments = data
end
apartment.data_modified = true
else
local s_data = file:read("*all")
if s_data == "" then
minetest.log("error","[Apartment] Save file was empty: " .. apartment.save_path)
file:close()
return false
end
local data = minetest.deserialize(s_data)
if data == nil then -- Known bug of advtrains: when file grows, it may fail to load.
apartment.working = false -- Even if pcall()ed, the mod will still refuse to do any operations.
error("[Apartment] Failed to deserialize data. Please solve this problem manually.")
return false
end
apartment.apartments = data
return true
end
end
dofile(MP .. "/chg_owner.lua")
apartment.rent = function( pos, owner, oldmetadata, actor )
local node = minetest.get_node(pos)
local meta, original_owner, now_owner, descr, category, size_up, size_down, size_right, size_left, size_front, size_back
if not oldmetadata then
meta = minetest.get_meta(pos)
original_owner = meta:get_string('original_owner')
now_owner = meta:get_string('owner')
descr = meta:get_string('descr')
category = meta:get_string('category')
size_up = meta:get_int( 'size_up' );
size_down = meta:get_int( 'size_down' );
size_right = meta:get_int( 'size_right' );
size_left = meta:get_int( 'size_left' );
size_front = meta:get_int( 'size_front' );
size_back = meta:get_int( 'size_back' );
else
original_owner = oldmetadata.fields[ "original_owner" ]
now_owner = oldmetadata.fields[ "owner" ]
descr = oldmetadata.fields[ "descr" ]
category = oldmetadata.fields[ "category" ]
size_up = tonumber(oldmetadata.fields[ "size_up" ])
size_down = tonumber(oldmetadata.fields[ "size_down" ])
size_right = tonumber(oldmetadata.fields[ "size_right" ])
size_left = tonumber(oldmetadata.fields[ "size_left" ])
size_front = tonumber(oldmetadata.fields[ "size_front" ])
size_back = tonumber(oldmetadata.fields[ "size_back" ])
node.param2 = oldmetadata.param2
end
if not(original_owner and now_owner and descr and size_up and size_down and size_right and size_left and size_front and size_back) then
return false, "META_LOOKUP_ERR"
end
local x1 = pos.x;
local y1 = pos.y;
local z1 = pos.z;
local x2 = pos.x;
local y2 = pos.y;
local z2 = pos.z;
if node.param2 == 0 then -- z gets larger
x1 = x1 - size_left; x2 = x2 + size_right;
z1 = z1 - size_front; z2 = z2 + size_back;
elseif node.param2 == 1 then -- x gets larger
z1 = z1 - size_right; z2 = z2 + size_left;
x1 = x1 - size_front; x2 = x2 + size_back;
elseif node.param2 == 2 then -- z gets smaller
x1 = x1 - size_right; x2 = x2 + size_left;
z1 = z1 - size_back; z2 = z2 + size_front;
elseif node.param2 == 3 then -- x gets smaller
z1 = z1 - size_left; z2 = z2 + size_right;
x1 = x1 - size_back; x2 = x2 + size_front;
end
y1 = y1 - size_down; y2 = y2 + size_up;
local px = x1;
local py = x1;
local pz = z1;
for px = x1, x2 do
for py = y1, y2 do
for pz = z1, z2 do
local npos = vector.new(px,py,pz)
apartment.chg_owner(pos,npos,category,descr,original_owner,now_owner,owner,actor)
end
end
end
meta:set_string("owner",owner)
if not apartment.apartments[category] then apartment.apartments[category] = {} end
apartment.apartments[category][descr] = {pos=pos,original_owner=original_owner,owner=owner}
apartment.data_modified = true
if not oldmetadata then
minetest.debug(owner,original_owner,node.name)
if (owner == "" or original_owner == owner) and (node.name == 'apartment:apartment_occupied') then
minetest.swap_node( pos, {name='apartment:apartment_free', param2 = node.param2} )
elseif (original_owner ~= owner) and (node.name == 'apartment:apartment_free') then
minetest.swap_node( pos, {name='apartment:apartment_occupied', param2 = node.param2} )
end
end
return true
end
apartment.on_construct = function(pos)
local meta = minetest.get_meta(pos);
meta:set_string('infotext', S('Apartment Management Panel (unconfigured)'))
meta:set_string('original_owner', '' )
meta:set_string('owner', '' )
meta:set_string('descr', '' )
meta:set_int( 'size_up', 0 )
meta:set_int( 'size_down', 0 )
meta:set_int( 'size_right', 0 )
meta:set_int( 'size_left', 0 )
meta:set_int( 'size_front', 0 )
meta:set_int( 'size_back', 0 )
end
apartment.after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos);
local pname = (placer:get_player_name() or "");
meta:set_string("original_owner", pname );
meta:set_string("owner", pname );
meta:set_string('infotext', S('Apartment Management Panel (owned by @1)',pname))
end
apartment.can_dig = function(pos,player)
local meta = minetest.get_meta(pos)
local owner = meta:get_string('owner')
local original_owner = meta:get_string( 'original_owner' )
local pname = player:get_player_name()
if original_owner == '' then
return true
end
if original_owner ~= pname then
minetest.chat_send_player( pname, S('Sorry. Only the original owner of this apartment control panel can dig it.'));
return false
end
if not(original_owner == owner or owner == "") then
minetest.chat_send_player( pname, S('The apartment is currently rented to @1. Please end that first.',owner));
return false
end
return true
end
apartment.after_dig_node = function(pos, oldnode, oldmetadata, digger)
if not( oldmetadata ) or oldmetadata=="nil" or not(oldmetadata.fields) then
minetest.chat_send_player( digger:get_player_name(), S("Error: Could not find information about the apartment panel that is to be removed."))
return
end
local descr = oldmetadata.fields[ "descr" ]
local category = oldmetadata.fields[ "category" ]
if( apartment.apartments[ descr ] ) then
-- actually remove the apartment
oldmetadata.param2 = oldnode.param2
apartment.rent( pos, '', oldmetadata, digger )
apartment.apartments[ descr ] = nil
apartment.data_modified = true
minetest.chat_send_player( digger:get_player_name(), S("Removed apartment @1@@@2 successfully.",descr,category))
end
end
apartment.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if not apartment.working then return end
if not clicker:is_player() then return end
local name = clicker:get_player_name()
local meta = minetest.get_meta(pos)
local ctx = {pos=pos,meta=meta}
local owner = meta:get_string( 'owner' )
local original_owner = meta:get_string( 'original_owner' )
local category = meta:get_string( 'category' )
if original_owner == owner and category == "" then
if itemstack:get_name() == "apartment:configuration_copier" then
local imeta = itemstack:get_meta()
ctx.defaults = {}
ctx.defaults.category = imeta:get_string("category")
ctx.defaults.descr = imeta:get_string("descr")
if ctx.defaults.category == "" or ctx.defaults.descr == "" then
ctx.defaults = nil -- Unconfigured copier
else
while apartment.apartments[ctx.defaults.category][ctx.defaults.descr] do
local number = string.match(ctx.defaults.descr,'%d+$')
local new_descr = ctx.defaults.descr .. "_1"
if number then
new_descr = string.sub(ctx.defaults.descr,1,-#tostring(number)-1) .. tostring(number + 1)
end
ctx.defaults.descr = new_descr
end
ctx.defaults.size_up = imeta:get_int( 'size_up' )
ctx.defaults.size_down = imeta:get_int( 'size_down' )
ctx.defaults.size_right = imeta:get_int( 'size_right' )
ctx.defaults.size_left = imeta:get_int( 'size_left' )
ctx.defaults.size_front = imeta:get_int( 'size_front' )
ctx.defaults.size_back = imeta:get_int( 'size_back' )
end
end
apartment.gui.configure_gui:show(clicker,ctx)
else
apartment.gui.panel_control:show(clicker,ctx)
end
end
minetest.register_craftitem("apartment:configuration_copier", {
description = S("Apartment Configuaration Copier"),
inventory_image = "apartment_configuration_copier.png",
on_use = function(itemstack, user, pointed_thing)
if not user:is_player() then return end
local name = user:get_player_name()
if pointed_thing.type ~= "node" then return end
local pos = pointed_thing.under
local node = minetest.get_node(pos)
if node.name ~= "apartment:apartment_free" and node.name ~= "apartment:apartment_occupied" then return end
local meta = minetest.get_meta(pos)
local category = meta:get_string("category")
local descr = meta:get_string("descr")
if category == "" or descr == "" then
minetest.chat_send_player(name, S("Please configure the panel first before copying configurations from it."))
return
end
local size_up = meta:get_int( 'size_up' )
local size_down = meta:get_int( 'size_down' )
local size_right = meta:get_int( 'size_right' )
local size_left = meta:get_int( 'size_left' )
local size_front = meta:get_int( 'size_front' )
local size_back = meta:get_int( 'size_back' )
local imeta = itemstack:get_meta()
imeta:set_string("category",category)
imeta:set_string("descr",descr)
imeta:set_int("size_up",size_up)
imeta:set_int("size_down",size_down)
imeta:set_int("size_right",size_right)
imeta:set_int("size_left",size_left)
imeta:set_int("size_front",size_front)
imeta:set_int("size_back",size_back)
imeta:set_string("description",S("Apartment Configuaration Copier\nU:@1 D:@2 L:@3 R:@4 F:@5 B:@6\n@7@@@8\nRightclick on a panel to paste the configurations.",size_up,size_down,size_right,size_left,size_front,size_back,descr,category))
minetest.chat_send_player(name, S("Configuration copied."))
return itemstack
end,
})
minetest.register_node("apartment:apartment_free", {
description = S("Apartment Management Panel"),
drawtype = "nodebox",
tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png",
"default_steel_block.png","apartment_controls_vacant.png","default_steel_block.png"},
paramtype = "light",
paramtype2 = "facedir",
light_source = 14,
groups = {cracky=2},
node_box = {
type = "fixed",
fixed = {
{ -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
}
},
on_construct = apartment.on_construct,
after_place_node = apartment.after_place_node,
can_dig = apartment.can_dig,
after_dig_node = apartment.after_dig_node,
on_rightclick = apartment.on_rightclick,
})
minetest.register_node("apartment:apartment_occupied", {
description = S("Apartment Management Panel") .. " Occupied",
drawtype = "nodebox",
tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png",
"default_steel_block.png","apartment_controls_occupied.png","default_steel_block.png"},
paramtype = "light",
paramtype2 = "facedir",
light_source = 14,
groups = {cracky=2, not_in_creative_inventory=1},
node_box = {
type = "fixed",
fixed = {
{ -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
}
},
on_construct = apartment.on_construct,
after_place_node = function(pos,placer)
local node = minetest.get_node(pos)
node.name = "apartment:apartment_free"
minetest.swap_node(pos,node)
return apartment.after_place_node(pos,placer)
end,
can_dig = apartment.can_dig,
after_dig_node = apartment.after_dig_node,
on_rightclick = apartment.on_rightclick,
})
if apartment.enable_aphome_command then
minetest.register_chatcommand("aphome", {
params = "[<category>]",
description = S("Teleports you back to the apartment you rented."),
privs = {home=true},
func = function(name, param)
local category = (param == "" and "apartment" or param)
local player = minetest.get_player_by_name(name)
if not apartment.apartments[category] then
return false, S("You do not have an apartment in category @1.",category)
end
for k,v in pairs( apartment.apartments[category] ) do
if v and v.owner == name then
player:set_pos(v.pos)
return true, S("Welcome back to your apartment @1@@@2.",k,category)
end
end
return false, S("You do not have an apartment in category @1.",category)
end
})
end
-- old version of the node - will transform into _free or _occupied
if minetest.settings:get_bool("apartment.old_compact_codes",true) then
minetest.register_node("apartment:apartment", {
drawtype = "nodebox",
description = "apartment management panel (transition state)",
tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png",
"default_steel_block.png","apartment_controls_vacant.png","default_steel_block.png"},
paramtype = "light",
paramtype2 = "facedir",
light_source = 14,
groups = {cracky=2,not_in_creative_inventory=1},
node_box = {
type = "fixed",
fixed = {
{ -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
}
},
selection_box = {
type = "fixed",
fixed = {
{ -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
}
},
})
minetest.register_abm({
nodenames = {"apartment:apartment"},
interval = 60,
chance = 1,
action = function(pos, node)
local node = minetest.get_node( pos )
local meta = minetest.get_meta( pos )
local owner = meta:get_string( 'owner' )
local original_owner = meta:get_string( 'original_owner' )
if owner == '' or original_owner==owner then
minetest.swap_node( pos, {name='apartment:apartment_free', param2 = node.param2} )
else
minetest.swap_node( pos, {name='apartment:apartment_occupied', param2 = node.param2} )
end
end
})
end
minetest.register_abm({
nodenames= {"apartment:apartment_free" },
interval = 1,
chance = 1,
action = function(pos,node)
local meta = minetest.get_meta(pos)
meta:set_string("formspec","") -- Remove formspec used in v1
local descr = meta:get_string("descr")
local category = meta:get_string("category")
local cat_data = apartment.apartments[category]
if not cat_data then return end
local ap_data = cat_data[descr]
if not ap_data then return end -- Still writing data, please wait!
if not vector.equals(ap_data.pos,pos) then -- Dulplicated!
local new_data = table.copy(ap_data)
new_data.pos = pos
local number = string.match(descr,'%d+$')
local new_descr = descr .. "_1"
if number then
new_descr = string.sub(descr,1,-#tostring(number)-1) .. tostring(number + 1)
end
meta:set_string("descr",new_descr)
cat_data[new_descr] = new_data
apartment.data_modified = true
end
end,
})
apartment.restore_data()

4
mod.conf Normal file
View File

@ -0,0 +1,4 @@
name = apartment
title = Self-service rental panels for players to rent apartments or shops
depends = flow
optional_depends = default

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB