39 Commits

Author SHA1 Message Date
59a217ceb9 move router to its own file 2022-02-22 21:33:09 -05:00
0dbc57cbbe restructure format 2022-02-20 22:53:29 -05:00
ee4cbef811 remove unused node registration function 2022-02-20 22:39:40 -05:00
fc7c0ff095 reformat node file structure 2022-02-20 22:36:46 -05:00
1f28124569 split tetris out to seperate file 2022-02-20 22:22:12 -05:00
1ea698a1e1 update documentation 2022-02-20 22:05:23 -05:00
c46ec76c63 meow 2022-02-20 20:28:41 -05:00
aca2f138a8 add limited network functionality 2022-02-20 19:03:52 -05:00
8267c4d71f improve browser tab default 2022-02-19 16:06:17 -05:00
fa1f4894af add lua sandbox, tabs, stuff 2022-02-18 17:29:31 -05:00
2593c55ee3 priv gate things + bugfixes 2022-02-13 10:43:41 -05:00
3e4e65e01b additional security 2022-02-13 00:44:14 -05:00
2060c59920 add missing filepaths 2022-02-13 00:33:22 -05:00
4cfacb1abc add filesystem 2022-02-13 00:31:20 -05:00
ef2ceed755 add commands to terminal 2022-02-12 21:56:17 -05:00
d0a4ace94e mvp terminal 2022-02-12 20:52:12 -05:00
1397b08bcb add tab views 2022-02-12 19:21:39 -05:00
a844c7ec5e add demo formspec 2022-02-12 17:13:16 -05:00
f57b1642b9 init 2022-02-12 16:36:11 -05:00
eacbe80fae Merge pull request #6 from mt-mods/vending_crafts
add crafts for vending machines
2022-02-02 15:46:23 -05:00
b3ca88e58d Merge pull request #8 from mt-mods/SwissalpS-patch-1
fix single glass bug
2022-02-02 15:46:12 -05:00
b5ec85d037 fix single glass bug
weirdly this is all it takes to fix https://github.com/mt-mods/home_workshop_modpack/issues/7

closes: https://github.com/mt-mods/home_workshop_modpack/issues/7
2022-02-02 17:48:35 +01:00
0af4ae2eb2 add crafts for vending machines 2022-02-02 06:33:58 -05:00
2058c58738 Merge pull request #3 from mt-mods/mtg_removal
mtg dep removal
2021-12-26 13:00:07 -05:00
1ce0533672 make OgelGames happy 2021-12-26 12:22:40 -05:00
6f9a27ff05 relicense WFTPL -> MIT 2021-12-26 10:26:34 -05:00
c350f40405 finish default dep removal 2021-12-25 22:01:50 -05:00
35a3be337d mrg dep removal pass 1 2021-12-04 23:51:15 -05:00
0664552799 small fixes 2021-11-28 22:09:13 -05:00
7043a7d15d Merge pull request #1 from mt-mods/vending_machines
add ending machines and code cleanup
2021-11-28 14:33:22 -05:00
2e2d904261 small fixes 2021-11-28 14:32:38 -05:00
daf04bd400 add ending machines and code cleanup 2021-11-28 11:40:59 -05:00
427b6b1f3f make luacheck happy 2021-11-28 01:31:53 -05:00
92ee7e419e add luacheck, fix errors, add github actions 2021-11-28 01:28:44 -05:00
0a43ffe46a add documentation 2021-11-28 01:20:41 -05:00
eca7c55028 Merge branch 'master' into 'master'
Fix missing paramtype for mesh nodes

See merge request VanessaE/home_workshop_modpack!1
2021-05-19 11:22:10 +00:00
7e517d8386 fix missing paramtype for mesh nodes 2021-05-19 20:50:05 +10:00
03325e886a Fix missing "slaystation_off" alias 2021-04-15 06:48:28 -04:00
d37bc20673 fix a couple of missing aliases 2021-03-14 10:30:27 -04:00
77 changed files with 2234 additions and 696 deletions

18
.github/workflows/luacheck.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: luacheck
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: apt
run: sudo apt-get install -y luarocks
- name: luacheck install
run: luarocks install --local luacheck
- name: luacheck run
run: $HOME/.luarocks/bin/luacheck ./

25
.luacheckrc Normal file
View File

@ -0,0 +1,25 @@
unused_args = false
allow_defined_top = true
exclude_files = {".luacheckrc"}
globals = {
"minetest", "core",
--mod provided
"home_vending_machines", "computers",
}
read_globals = {
string = {fields = {"split"}},
table = {fields = {"copy", "getn"}},
--luac
"math", "table",
-- Builtin
"vector", "ItemStack", "dump", "DIR_DELIM", "VoxelArea", "Settings", "PcgRandom", "VoxelManip", "PseudoRandom",
--mod produced
"default", "screwdriver", "unifieddyes", "formspec_ast",
}

View File

@ -1,21 +0,0 @@
unused_args = false
allow_defined_top = true
max_comment_line_length = 999
read_globals = {
"DIR_DELIM",
"minetest", "core",
"unpack",
"dump",
table = { fields = { "copy", "getn" } },
"vector", "nodeupdate",
"VoxelManip", "VoxelArea",
"PseudoRandom", "ItemStack",
"default",
"unifieddyes",
"screwdriver",
}
globals = {
}

View File

@ -1,38 +0,0 @@
version 0.2.3:
- Added animated router front
- Added all crafts for new items
version 0.2.2:
- Added printer
- Added server
version 0.2.1:
- Added modern tower pc
- Added wifi router
- Added flatscreen LCD and keyboard
Version 0.2.0:
- Added recipes.
- Partly re-written.
- Removed original baby tower (seemed out of place).
- You can now turn devices on and off by right-clicking.
- Mod is now on github.
Version 0.1.4:
- Added Admiral 64 & 128 (Commodore 64 & 128 lookalikes)
Version 0.1.3:
- Added SX Spectre (Sinclair ZX Spectrum lookalike)
- Added Pony SlayStation 2 (Sony PlayStation lookalike)
- Minor fixes to textures.
Version 0.1.2:
- Nodes now use the node box as selection box.
- Added Pony SlayStation (Sony PlayStation lookalike)
- Added Pony Vanio (Sony VAIO lookalike...err...just a generic laptop)
Version 0.1.1:
- Added SheFriend SOO (Amiga 500 lookalike)
Version 0.1.0:
- Initial Version only a baby tower.

37
computers/README.md Normal file
View File

@ -0,0 +1,37 @@

# Minetest Computers Mod
By [Diego Martínez](kaeza@users.sf.net) and Contributors
License: [MIT](https://spdx.org/licenses/MIT.html)
Adds interactive computers and networking to minetest
## Links
[Github](https://github.com/mt-mods/home_workshop_modpack)
[Contentdb](https://content.minetest.net/packages/VanessaE/home_workshop_modpack/)
## Setup
You should have recieved this mod as a part of the home workshop modpack.
place the modpack in your /mods folder and enable the computer mod and its dependancies
from the minetest GUI. For further information or help see:
http://wiki.minetest.com/wiki/Installing_Mods
## Privs
This mod provides the computers_filesystem priv which is used to limit the access
of players without it.
## Notes
Much thanks goes to the maintainers of the mod:
* Diego Martínez
* VannessaE
* mt-mods organization
Additional thanks goes to those who helped/advised with the core functionality of computers:
* Luk3yx
* BuckarooBanzai
* OgelGames

View File

@ -1,49 +0,0 @@

Decorative Computers Mod for Minetest
by Diego Martínez <kaeza@users.sf.net>
How to install:
Unzip the archive an place it in minetest-base-directory/mods/minetest/
if you have a windows client or a linux run-in-place client. If you have
a linux system-wide instalation place it in ~/.minetest/mods/minetest/.
If you want to install this mod only in one world create the folder
worldmods/ in your worlddirectory.
For further information or help see:
http://wiki.minetest.com/wiki/Installing_Mods
How to use the mod:
For now just use creative mode or the `/give' or `/giveme' chat commands
to get the items.
These are the items currently defined by this mod:
"computers:shefriendSOO",
"computers:vanio",
"computers:vanio_off",
"computers:slaystation",
"computers:slaystation2",
"computers:specter",
"computers:wee",
"computers:piepad",
"computers:admiral64",
"computers:admiral128",
"computers:hueg_box",
"computers:monitor",
"computers:monitor_on",
"computers:router",
"computers:tower",
"computers:server",
"computers:server_on",
"computers:printer",
"computers:tetris_arcade",
There's also a `computers:computer' alias to `computers:babytower'.
Thanks to all the people in the forums and the #minetest IRC channel for
their support and suggestions; in no particular order:
OldCoder, Josh, tonyka, VanessaE, davidpace, Jordach, and all the other
sirs/madammes that I forgot to mention (sorry, please remind me if it
was you ;) ).
See also:
http://minetest.net/

View File

@ -1,29 +0,0 @@
TO-DO List:
- New Nodes:
- Computers:
- Mainframe (well me have a rackserver now)
- My Computer :P
- Peripherals:
- Scanner ( well we have a printer/scanner combo now)
- Consoles:
- NES
- SNES
- DC
- Handhelds
- GB/C
- GBA
- Calculator
- Smartphone
- Animated screens
- Implement some kind of games (take code from `tetris' mod?). [It would be
nice if Minetest provided a "canvas" GUI widget :)].
- Get more suggestions :)

View File

@ -0,0 +1,7 @@
{
"name": "cat",
"license": "MIT",
"version": 0.1,
"engine": 0.44,
"help": "output file to the terminal"
}

View File

@ -0,0 +1,15 @@
function cat(pos, input, data)
local path = computers.devicepath .. "/" .. minetest.hash_node_position(pos) .. data.element.pwd
local files = computers.api.get_dir_keyed_list(path, false)
if files[input] then
local f = io.open(path .. "/" .. input)
local fdata = f:read("*all")
f:close()
return fdata
else
return "file not found"
end
end
return cat

View File

@ -0,0 +1,7 @@
{
"name": "cd",
"license": "MIT",
"version": 0.11,
"engine": 0.41,
"help": "move in and out of folders"
}

View File

@ -0,0 +1,28 @@
function cd(pos, input, data)
if not minetest.check_player_privs(data.player, "computers_filesystem") then
return "ERROR: permision denied"
end
local path = computers.devicepath .. "/" .. minetest.hash_node_position(pos) .. data.element.pwd
if input and input ~= "" and not input:find("/") and not input:find("\\") then
if input == ".." then
local uri = data.element.pwd:split("/")
uri[#uri] = nil
data.element.pwd = "/" .. table.concat(uri, "/")
if data.element.pwd == "/" then data.element.pwd = "" end
return "sucess"
else
for _, folder in pairs(minetest.get_dir_list(path, true)) do
if folder == input then
data.element.pwd = data.element.pwd .. "/" .. input
return "sucess"
end
end
return "ERROR: path not found"
end
else
return "invalid or missing input"
end
end
return cd

View File

@ -0,0 +1,7 @@
{
"name": "echo",
"license": "MIT",
"version": 0.1,
"engine": 0.3,
"help": "prints out input to the terminal"
}

View File

@ -0,0 +1,5 @@
function echo(pos, input, data)
return input
end
return echo

View File

@ -0,0 +1,7 @@
{
"name": "exit",
"license": "MIT",
"version": 0.1,
"engine": 0.42,
"help": "exits the computer"
}

View File

@ -0,0 +1,7 @@
function exit(pos, input, data)
minetest.close_formspec(data.player:get_player_name(), "")
return "you shouldnt see this"
end
return exit

View File

@ -0,0 +1,7 @@
{
"name": "expr",
"license": "MIT",
"version": 0.1,
"engine": 0.42,
"help": "exicutes mathmatical expresions"
}

View File

@ -0,0 +1,14 @@
function expr(pos, input, data)
local output
local func = computers.sandbox.loadstring("return " .. input, {env={}})
if func then output = func() end
if type(output) ~= "number" or output == nil then
return "Error: invalid or missing input"
end
return tostring(output)
end
return expr

View File

@ -0,0 +1,7 @@
{
"name": "help",
"license": "MIT",
"version": 0.1,
"engine": 0.4,
"help": "prints out help"
}

View File

@ -0,0 +1,9 @@
function help(pos, input, data)
local output = ""
for command, def in pairs(computers.registered_confs) do
output = output .. "\n" .. command .. ": " .. def.help
end
return output
end
return help

View File

@ -0,0 +1,7 @@
{
"name": "ls",
"license": "MIT",
"version": 0.1,
"engine": 0.4,
"help": "lists out all files and folders in the current path"
}

View File

@ -0,0 +1,9 @@
function ls(pos, input, data)
local path = computers.devicepath .. "/" .. minetest.hash_node_position(pos) .. data.element.pwd
--make dir to be safe
minetest.mkdir(path)
return table.concat(minetest.get_dir_list(path), " ")
end
return ls

View File

@ -0,0 +1,7 @@
{
"name": "mkdir",
"license": "MIT",
"version": 0.11,
"engine": 0.41,
"help": "makes a dir in the current path"
}

View File

@ -0,0 +1,15 @@
function mkdir(pos, input, data)
if not minetest.check_player_privs(data.player, "computers_filesystem") then
return "ERROR: permision denied"
end
local path = computers.devicepath .. "/" .. minetest.hash_node_position(pos) .. data.element.pwd
if input and input ~= "" and not input:find("/") and not input:find("\\") then
minetest.mkdir(path .. "/" .. input)
return "folder " .. input .. " created"
else
return "invalid or missing input"
end
end
return mkdir

View File

@ -0,0 +1,7 @@
{
"name": "nano",
"license": "MIT",
"version": 0.1,
"engine": 0.42,
"help": "allows editing of files"
}

View File

@ -0,0 +1,113 @@
function nano(pos, input, data)
local path = computers.devicepath .. "/" .. minetest.hash_node_position(pos) .. data.element.pwd
local files = computers.api.get_dir_keyed_list(path, false)
if not input then return "no file designated" end
if not files[input:split(" ")[1]] then return "file " .. input:split(" ")[1] .. " not found" end
if computers.formspec.get_index_by_name(
computers.formspec.registered_kast[data.player:get_player_name()],
"nano_btn"
) then return "nano is already open, please close it" end
local f = io.open(path .. "/" .. input:split(" ")[1])
if not f then return "error reading file" end
local file = f:read("*all")
f:close()
--clean up if there exists a legacy nano_ctn
local nctn = computers.formspec.get_index_by_name(
computers.formspec.registered_kast[data.player:get_player_name()],
"nano_ctn"
)
if nctn then
local fs = computers.formspec.registered_kast[data.player:get_player_name()]
table.remove(fs, nctn)
computers.formspec.registered_kast[data.player:get_player_name()] = fs
minetest.chat_send_all(dump(fs))
end
local form = computers.gui.add_tab(data.player, "Nano", {
type = "container",
name = "nano_ctn",
state = 1,
x = 0,
y = 1,
{
type = "background",
x = 0,
y = 0,
w = 10,
h = 11,
texture_name = "[combine:16x16^[noalpha"
},
{
type = "textarea",
x = 0,
y = 0,
w = 10,
h = 10,
name = "nano_editor",
--read_only = 1,
--label = "test",
default = file,
close_on_enter = false,
--this breaks the scrollbar
--[[ props = {
border = false,
} ]]
},
{
type = "box",
x = 0,
y = 10,
w = 10,
h = 1,
color = "#ffffff"
},
{
type = "field",
x = 0,
y = 10,
w = 10,
h = 1,
name = "nano_commands",
close_on_enter = false,
pwd = "",
props = {
border = false,
},
on_event = function(form, player, element, value, fields)
if value == "save" and fields.nano_editor then
--minetest.chat_send_all(fields.nano_editor)
if #fields.nano_editor < 12000 then --12000 is luacheck line length of 120 * 100 lines
minetest.safe_file_write(path .. "/" .. input:split(" ")[1], fields.nano_editor)
else
computers.api.chat_send_player(player, minetest.colorize("red", "[Nano]: file is to long"))
end
elseif value == "exit" then
local btn = computers.formspec.get_index_by_name(form, "nano_btn")
local ctn = computers.formspec.get_index_by_name(form, "nano_ctn")
form[ctn].state = 1
table.remove(form, btn)
--table.remove(form, ctn) cant actually remove it from within itself, therefore we hide it
local tindex = 0
for index, tab in pairs(form.tabs) do
if tab == "nano" then tindex = index end
end
table.remove(form.tabs, tindex)
tindex = computers.formspec.get_index_by_name(form, "terminal_ctn")
form[tindex].state = 0
else
computers.api.chat_send_player(player, minetest.colorize("red", "[Nano]: invalid command"))
end
return form
end,
},
})
return form
end
return nano

View File

@ -0,0 +1,7 @@
{
"name": "net",
"license": "MIT",
"version": 0.1,
"engine": 0.43,
"help": "configuration related to networking"
}

View File

@ -0,0 +1,64 @@
function net(pos, input, data)
input = input or ""
local lookup = {
list = function(param)
if param == "-ids" then
return table.concat(computers.networks.get_networks_in_area(pos), " ")
elseif not param or param == "" then
return table.concat(computers.networks.get_net_names_in_area(pos), " ")
else
return "invalid sub command"
end
end,
join = function(param)
local id = computers.networks.get_id_by_net_name(data.player, param)
if not id then
return "invalid network name"
elseif minetest.get_meta(pos):get("net_id") then
return "already joined to a network"
else
local _, networks = computers.networks.get_net_names_in_area(pos)
if not networks[param] then return "network not in range" end
minetest.get_meta(pos):set_string("net_id", id)
local status = computers.networks.add_device(id, pos)
if status then return "joined network " .. param else return "network join failed" end
end
end,
leave = function(param)
local id = minetest.get_meta(pos):get("net_id")
if id then
computers.networks.remove_device(id, pos)
return "removed device from network " .. computers.networks.get_net_name(id)
else
return "not currently connected to any network"
end
end,
status = function(param)
local id = minetest.get_meta(pos):get("net_id")
if id then
return "connected to network " .. computers.networks.get_net_name(id)
else
return "not connected to any network"
end
end,
dns = function(param)
if minetest.get_item_group(minetest.get_node(pos).name, "computers_server") == 0 then
return "command only available to servers"
end
if not param or param == "" then return "invalid or missing input" end
if param:match("%W") then return "only alphanumerics allowed" end
local id = minetest.get_meta(pos):get("net_id")
if id then
computers.networks.set_dns(id, pos, param)
return "created dns record"
else
return "not connected to network"
end
end
}
if not lookup[input:split(" ")[1]] then return "invalid sub command" end
return lookup[input:split(" ")[1]](input:split(" ", 1)[2])
end
return net

View File

@ -0,0 +1,7 @@
{
"name": "pwd",
"license": "MIT",
"version": 0.1,
"engine": 0.4,
"help": "list the current path"
}

View File

@ -0,0 +1,7 @@
function pwd(pos, input, data)
local output = data.element.pwd
if output == "" then output = "/" end
return output
end
return pwd

View File

@ -0,0 +1,7 @@
{
"name": "test",
"license": "MIT",
"version": 0.1,
"engine": 100000000,
"help": "literally worthless"
}

View File

@ -0,0 +1,6 @@
function test(pos, input)
--minetest.chat_send_all("test")
return 0
end
return test

View File

@ -0,0 +1,7 @@
{
"name": "touch",
"license": "MIT",
"version": 0.3,
"engine": 0.42,
"help": "creates a file in the current path"
}

View File

@ -0,0 +1,22 @@
function touch(pos, input, data)
local path = computers.devicepath .. "/" .. minetest.hash_node_position(pos) .. data.element.pwd
--make dir to be safe
minetest.mkdir(path)
if #minetest.get_dir_list(path, false) > 10
and not minetest.check_player_privs(data.player, "computers_filesystem") then
return "ERROR: you have reached your max file limit"
end
if input and input ~= "" and not input:find("/") then
if computers.api.get_dir_keyed_list(path, nil)[input] then
return "ERROR: trying to create already existing file/folder"
end
minetest.safe_file_write(path .. "/" .. input, "")
return "file " .. input .. " created"
else
return "invalid or missing input"
end
end
return touch

142
computers/core/api.lua Normal file
View File

@ -0,0 +1,142 @@
--[[
computers.sandbox code is from https://github.com/kikito/lua-sandbox
License MIT 2013 Enrique García Cota
additionally modified by wsor to work with lua 5.1/luajit
modifications include:
* additional protections
* tweaking of structure
* modifications to the default env
]]
computers.sandbox = {}
computers.api = {}
--default sandbox enviroment used if none is provided
computers.sandbox.default_env = {}
([[
_VERSION assert error ipairs next pairs
select tonumber tostring type unpack
math.abs math.acos math.asin math.atan math.atan2 math.ceil
math.cos math.cosh math.deg math.exp math.fmod math.floor
math.frexp math.huge math.ldexp math.log math.log10 math.max
math.min math.modf math.pi math.pow math.rad math.random
math.sin math.sinh math.sqrt math.tan math.tanh
os.clock os.difftime os.time
string.byte string.char
string.len string.lower string.reverse
string.sub string.upper
table.insert table.maxn table.remove table.sort table.concat
]]):gsub('%S+', function(id)
local module, method = id:match('([^%.]+)%.([^%.]+)')
if module then
computers.sandbox.default_env[module] = computers.sandbox.default_env[module] or {}
computers.sandbox.default_env[module][method] = _G[module][method]
else
computers.sandbox.default_env[id] = _G[id]
end
end)
--takes a code string, returns a sandboxed function to exicute
function computers.sandbox.loadstring(code, options)
local defaults = {
env = table.copy(computers.sandbox.default_env),
quota = 5000
}
options = options or {}
for k, v in pairs(defaults) do
if not options[k] then options[k] = v end
end
assert(type(code) == "string", "[computers.sandbox]: passed is not a string")
local env = options.env
env._G = env
if code:byte(1) == 27 then
minetest.log("warning", "[computers.sandbox]: attempted bytecode execution termminated")
return nil, "bytecode disallowed"
end
local f, err = loadstring(code)
if not f then return nil, err end
setfenv(f, env)
if jit then jit.off(f, true) end
local function timeout()
debug.sethook()
error("quota exceeded: " .. tostring(options.quota))
end
return function(...)
debug.sethook(timeout, "", options.quota)
local string_metatable = getmetatable("")
assert(string_metatable.__index == string, "[computers.sandbox]: error with the string metatable")
string_metatable.__index = env.string
local status, ret = pcall(f, ...)
debug.sethook()
string_metatable.__index = string
if not status then error(ret) end
return ret
end
end
--supports string input or table env input with a default env of the base
function computers.sandbox.merge_env(nenv, denv)
local base_env = table.copy(denv or computers.sandbox.default_env)
if type(nenv) == "table" then
for k, v in pairs(nenv) do
base_env[k] = base_env[k] or {}
for key, value in pairs(v) do
base_env[k][key] = value
end
end
elseif type(nenv) == "string" then
nenv:gsub('%S+', function(id)
local module, method = id:match('([^%.]+)%.([^%.]+)')
if module then
base_env[module] = base_env[module] or {}
base_env[module][method] = _G[module][method]
else
base_env[id] = _G[id]
end
end)
end
return base_env
end
function computers.api.get_dir_keyed_list(path, is_dir)
local files = minetest.get_dir_list(path, is_dir)
local keyed = {}
for _, file in pairs(files) do
keyed[file] = true
end
return keyed
end
function computers.api.chat_send_player(player, msg)
local name = player
if type(name) == "userdata" then name = player:get_player_name() end
minetest.chat_send_player(name, msg)
end
--basically for dev usage only
function computers.api.benchmark(func, ...)
local start_time = minetest.get_us_time()
for i=1, 1000 do
func(...)
end
minetest.chat_send_all(minetest.get_us_time()-start_time)
end

View File

@ -0,0 +1,26 @@
computers.registered_commands = {}
computers.registered_confs = {}
minetest.register_privilege("computers_filesystem", {
description = "advanced use of computers filesystem",
give_to_singleplayer = false,
give_to_admin = false,
})
function computers.register_command(modpath)
local func = dofile(modpath .. "/init.lua")
local f = io.open(modpath .. "/conf.json")
local conf = minetest.parse_json(f:read("*all"))
f:close()
if func and computers.os.version >= conf.engine then
computers.registered_commands[conf.name] = func
computers.registered_confs[conf.name] = conf
end
end
--load default commands
local dirs = minetest.get_dir_list(computers.modpath .. "/commands", true)
for _, dir in pairs(dirs) do
computers.register_command(computers.modpath .. "/commands/" .. dir)
end

173
computers/core/formspec.lua Normal file
View File

@ -0,0 +1,173 @@
computers.formspec = {}
computers.formspec.registered_kast = {}
computers.formspec.get_element_by_name = formspec_ast.get_element_by_name
function computers.formspec.get_index_by_name(tree, name)
--this doesnt support containers, use this to get container index and then pass container as tree
if type(tree) ~= "table" then return end
for key, element in pairs(tree) do
if type(element) == "table" and element.name and element.name == name then
return key
end
end
end
--note this is terrible hard coded
local function insert_styles(form, styles)
local headers = {size = true, position = true, anchor = true, no_prepend = true, real_cordinates = true}
local cindex = 0
local fs = {}
for key, val in pairs(form) do
if type(val) == "number" and not tonumber(key) then
fs[key] = val
elseif type(val) == "table" and val.type and headers[val.type] then
table.insert(fs, val)
cindex = key
end
end
for _, val in pairs(styles) do
table.insert(fs, val)
end
cindex = cindex+1
for i=cindex, #form do
table.insert(fs, form[i])
end
return fs
end
function computers.formspec.convert_to_ast(form)
local styles = {}
local function rfind(fs)
for key, val in pairs(fs) do
if type(val) == "table" then
if val.type and val.type:find("container") then
if val.state and val.state == 1 then
--cant use nil so swaping in thing that will never render
fs[key] = {type = "label",x = 100,y = 100,label = "nil",}
else
rfind(val)
end
elseif val.props then
table.insert(styles, {type = "style", selectors = val.selectors or {val.name}, props = val.props})
elseif val.read_only == 1 then
val.name = nil
end
if val.type == "field" then
table.insert(
styles,
{type = "field_close_on_enter", name = val.name, close_on_enter = val.close_on_enter}
)
end
end
end
end
rfind(form)
local fs = insert_styles(form, styles)
return fs
end
local forms = {}
function computers.formspec.show_formspec(player, formname, fs)
local playername = player
local formspec = fs
if type(player) == "userdata" then
playername = player:get_player_name()
end
if type(fs) == "table" then
computers.formspec.registered_kast[playername] = table.copy(fs)
formspec = formspec_ast.unparse(computers.formspec.convert_to_ast(fs))
end
forms[formname] = true
minetest.show_formspec(playername, formname, formspec)
end
function computers.formspec.close_formspec(player, formname)
local name = player
if type(name) == "userdata" then name = player:get_player_name() end
minetest.close_formspec(name, formname or "")
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
--if formname ~= "computers:gui" then return end
if not forms[formname] then return end
local pname = player:get_player_name()
if fields.quit then computers.formspec.registered_kast[pname] = nil return end
--[[
trying to figure out what a player actually did can be a mess
input can validly be nil for a text field sometimes, othertimes it can come in first
when a user has actual selected a button. buttons only come in when selected,
so we override whatever the first key is if there a button since we are sure it was pressed
since we cant get types from fields, we rely on the button being named name_btn with _btn being a suffix
]]
local keys = {}
local btn_override
for key, val in pairs(fields) do
table.insert(keys, key)
local split = key:split("_")
if #split >= 2 and split[2] == "btn" then btn_override = key end
end
local element = computers.formspec.get_element_by_name(
computers.formspec.registered_kast[pname],
btn_override or keys[1]
)
--minetest.chat_send_all(btn_override or keys[1])
--minetest.chat_send_all(fields[keys[1]])
if element and element.on_event then
--on_event(form, player, element)
local form = element.on_event(
computers.formspec.registered_kast[pname],
player,
element,
fields[btn_override or keys[1]],
fields
)
if form then computers.formspec.show_formspec(player, formname, form) end
end
end)
--[[
yes, i know this isnt perfect
requires a name field in the subtags
returns a table keyed by the name field with sub tables containing the other sub tags
]]
function computers.formspec.get_hypertext_subtags(hypertext, tag)
local adata = {}
for a in string.gmatch(hypertext, "<"..tag..".->.-</"..tag..">") do
local len = #tag+1
local tags = string.split(string.sub(a:split(">")[1], len, -1), " ")
local name
local storage = {}
for _, tag_string in pairs(tags) do
local split = tag_string:split("=")
if split[1] == "name" then
name = split[2]
else
storage[split[1]] = split[2]
end
end
adata[name] = storage
end
--minetest.chat_send_all(dump(adata))
return adata
end

330
computers/core/gui.lua Normal file
View File

@ -0,0 +1,330 @@
computers.gui = {}
local futil = computers.formspec
local f = io.open(computers.modpath .. "/default_page.page")
local default_page = f:read("*all")
f:close()
f = io.open(computers.modpath .. "/demo_page.page")
local demo_page = f:read("*all")
f:close()
local function select_btn(form, btn)
--to hardcoded
for _, obtn in pairs(form.tabs) do
local cindex = futil.get_index_by_name(form, obtn .. "_btn")
form[cindex].props.bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70"
form[cindex].props.bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90"
cindex = futil.get_index_by_name(form, obtn .. "_ctn")
form[cindex].state = 1
end
local cindex = futil.get_index_by_name(form, btn .. "_btn")
form[cindex].props.bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff20"
form[cindex].props.bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff40"
local aindex = futil.get_index_by_name(form, btn .. "_ctn")
form[aindex].state = 0
end
function computers.gui.load(pos, node, clicker)
local formspec = {
formspec_version = 4,
tabs = {"terminal", "browser"},
{
type = "size",
w = 10,
h = 12,
},
{
type = "no_prepend"
},
{
type = "bgcolor",
bgcolor = "black",
fullscreen = "neither"
},
{
type = "button",
x = 0,
y = 0,
w = 2,
h = 1,
name = "terminal_btn",
label = "Terminal",
on_event = function(form, player, element)
select_btn(form, "terminal")
return form
end,
props = {
border = false,
bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70",
bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90",
bgimg_middle = "4,4",
}
},
{
type = "button",
x = 2,
y = 0,
w = 2,
h = 1,
name = "browser_btn",
label = "Browser",
on_event = function(form, player, element)
select_btn(form, "browser")
return form
end,
props = {
border = false,
bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70",
bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90",
bgimg_middle = "4,4",
}
},
{
type = "container",
name = "terminal_ctn",
state = 0,
x = 0,
y = 1,
{
type = "background",
x = 0,
y = 0,
w = 10,
h = 11,
texture_name = "[combine:16x16^[noalpha"
},
{
type = "textarea",
x = 0,
y = 0,
w = 10,
h = 10,
name = "terminal_output",
read_only = 1,
--label = "test",
default = "welcome to kuto\nversion " .. computers.os.version .. "\n\nuser:~$ ",
},
{
type = "box",
x = 0,
y = 10,
w = 10,
h = 1,
color = "#ffffff"
},
{
type = "field",
x = 0,
y = 10,
w = 10,
h = 1,
name = "terminal_input",
close_on_enter = false,
pwd = "",
props = {
border = false,
},
on_event = function(form, player, element, value)
local cindex = futil.get_index_by_name(form, "terminal_ctn")
local eindex = futil.get_index_by_name(form[cindex], "terminal_output")
local text = form[cindex][eindex].default
local pass_table = {
element = element,
player = player
}
if value == "clear" then
form[cindex][eindex].default = "user:~" .. element.pwd .."$" .."\n"
end
local cdata = value:split(" ", false, 1)
if value == "clear" then
form[cindex][eindex].default = "user:~$ "
elseif value == "" then
form[cindex][eindex].default = text .. "user:~" .. element.pwd .."$" .. "\n"
elseif computers.registered_commands[cdata[1]] and cdata[2] == "-v" then
form[cindex][eindex].default = text .. value .. "\n" ..
computers.registered_confs[cdata[1]].version .. "\nuser:~" .. element.pwd .."$" .. "\n"
elseif computers.registered_commands[cdata[1]] then
form[cindex][eindex].default = text .. value .. "\n"
text = form[cindex][eindex].default
local output = computers.registered_commands[cdata[1]](pos, cdata[2], pass_table)
if output and type(output) == "string" then
form[cindex][eindex].default = text .. output .. "\n" .. "user:~" .. element.pwd .."$" .." "
elseif output and type(output) == "table" then
form = output
end
else
form[cindex][eindex].default = text .. value ..
"\nERROR: command not found\n" .. "user:~" .. element.pwd .."$" .. " "
end
return form
end,
},
},
{
type = "container",
name = "browser_ctn",
state = 1,
x = 0,
y = 1,
--[[
hardcoded background for now, need to build in custom element support
into hypertext to map to formspec elements
]]
{
type = "background",
x = 0,
y = 0,
w = 10,
h = 10,
texture_name = "[combine:16x16^[noalpha^[colorize:#ffffff70"
},
{
type = "hypertext",
name = "browser_content",
x = 0,
y = 0,
w = 10,
h = 10,
text = default_page,
on_event = function(form, player, element, value, fields)
--minetest.chat_send_all("reached")
--hard coding some network stuff for now
local name = value:split(":")[2]
local tags = futil.get_hypertext_subtags(element.text, "action")
if tags[name] and tags[name]._href and tags[name]._href == "demo_page" then
--minetest.chat_send_all(tags[name]._href)
element.text = demo_page
elseif tags[name] and tags[name]._href and tags[name]._href == "default_page" then
element.text = default_page
end
return form
end
},
{
type = "background",
x = 0,
y = 10,
w = 10,
h = 1,
texture_name = "[combine:16x16^[noalpha"
},
{
type = "box",
x = 0,
y = 10,
w = 10,
h = 1,
color = "#ffffff"
},
{
type = "field",
x = 0,
y = 10,
w = 10,
h = 1,
name = "browser_url",
close_on_enter = false,
pwd = "",
props = {
border = false,
},
on_event = function(form, player, element, value, fields)
local id = minetest.get_meta(pos):get("net_id")
if id then
local status, data = computers.networks.resolve_url(id, pos, value)
if status and data then
local cindex = futil.get_index_by_name(form, "browser_ctn")
local eindex = futil.get_index_by_name(form[cindex], "browser_content")
form[cindex][eindex].text = data
end
else
computers.api.chat_send_player(player, "[computers]: not attached to a network")
end
return form
end,
},
},
}
futil.show_formspec(clicker, "computers:gui", formspec)
end
--legacy compat
computers.load_gui = computers.gui.load
--note you can create to many pages thuse overflowing the formspec, need to be fixed
function computers.gui.add_tab(player, tname, tab)
local name = player
if type(player) == "userdata" then name = player:get_player_name() end
assert(tab, "[computers.sandbox]: new tab for " .. name .. " not found")
assert(tab.type == "container", "[computers.sandbox]: invalid new tab format for " .. name)
assert(tab.name:split("_")[2] == "ctn", "[computers.sandbox]: invalid tab name for " .. name)
assert(tab.x == 0 and tab.y == 1, "[computers.sandbox]: invalid tab name for " .. name)
local formspec = table.copy(computers.formspec.registered_kast[name])
local fs = {}
local btn
for key, val in pairs(formspec) do
if type(key) == "string" then
fs[key] = val
elseif type(val) == "table" then
if val.name and #val.name:split("_") >= 2 and val.name:split("_")[2] == "btn" then
btn = 1
elseif btn and btn == 1 then
table.insert(fs, {
type = "button",
x = #formspec.tabs*2,
y = 0,
w = 2,
h = 1,
name = tname:lower() .. "_btn",
label = tname,
on_event = function(form, _, element)
select_btn(form, tname:lower())
return form
end,
props = {
border = false,
bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70",
bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90",
bgimg_middle = "4,4",
}
})
btn = nil
end
table.insert(fs, val)
end
end
table.insert(fs, tab)
table.insert(fs.tabs, tname:lower())
return fs
end
--[[ minetest.register_on_player_receive_fields(function(player, formname, fields)
minetest.chat_send_all(dump(fields))
end) ]]

7
computers/core/init.lua Normal file
View File

@ -0,0 +1,7 @@
local path = computers.modpath .. "/core"
dofile(path .. "/api.lua")
dofile(path .. "/networks.lua")
dofile(path .. "/formspec.lua")
dofile(path .. "/commands.lua")
dofile(path .. "/gui.lua")

152
computers/core/networks.lua Normal file
View File

@ -0,0 +1,152 @@
local networks = {}
local network_lookup = {}
computers.networks = {}
local function init_networks()
local path = computers.networkpath
local files = minetest.get_dir_list(computers.networkpath, false)
for _, file in pairs(files) do
local id = file:sub(1,-6)
local f = io.open(path .. "/" .. file)
networks[id] = minetest.parse_json(f:read("*all"))
f:close()
if not network_lookup[networks[id].pname] then network_lookup[networks[id].pname] = {} end
network_lookup[networks[id].pname][networks[id].net_name] = id
end
end
init_networks()
local function update_file(id)
local data = networks[id]
local path = computers.networkpath .. "/" .. id .. ".json"
minetest.safe_file_write(path, minetest.write_json(data))
end
function computers.networks.create(player, net_name)
local pname = player
if type(player) == "userdata" then pname = player:get_player_name() end
local id = pname .. "_" .. minetest.get_us_time()
if net_name:match("%W") then return false end --pnly accept alphanumerics
if not network_lookup[pname] then network_lookup[pname] = {} end
if network_lookup[pname][net_name] then return false end
if networks[id] then return false end
--create storage on disk
local path = computers.networkpath .. "/" .. id .. ".json"
local data = {
pname = pname,
net_name = net_name,
devices = {},
dns = {
}
}
minetest.safe_file_write(path, minetest.write_json(data))
--store data in memory
networks[id] = data
network_lookup[pname][net_name] = id
return true, id
end
function computers.networks.verify_network(id)
if networks[id] then return true else return false end
end
function computers.networks.get_net_name(id)
return networks[id].net_name
end
function computers.networks.get_network_devices(id)
return table.copy(networks[id].devices)
end
function computers.networks.get_network_dns(id)
return table.copy(networks[id].dns)
end
function computers.networks.get_id_by_net_name(player, net_name)
local pname = player
if type(player) == "userdata" then pname = player:get_player_name() end
if not network_lookup[pname] then network_lookup[pname] = {} end
return network_lookup[pname][net_name]
end
function computers.networks.get_networks_in_area(pos, rad)
rad = rad or 10
local locations = {}
local keyed_locations = {}
local meta_nodes = minetest.find_nodes_with_meta(
vector.new(pos.x+rad, pos.y+rad, pos.z+rad),
vector.new(pos.x-rad, pos.y-rad, pos.z-rad)
)
for _, location in pairs(meta_nodes) do
local status = minetest.get_meta(location):get("net_id")
if status and networks[status] then
table.insert(locations, status)
keyed_locations[status] = true
end
end
return locations, keyed_locations
end
function computers.networks.get_net_names_in_area(pos, rad)
rad = rad or 10
local locations = {}
local keyed_locations = {}
local meta_nodes = minetest.find_nodes_with_meta(
vector.new(pos.x+rad, pos.y+rad, pos.z+rad),
vector.new(pos.x-rad, pos.y-rad, pos.z-rad)
)
for _, location in pairs(meta_nodes) do
local status = minetest.get_meta(location):get("net_id")
if status and networks[status] and not keyed_locations[networks[status].net_name] then
table.insert(locations, networks[status].net_name)
keyed_locations[networks[status].net_name] = true
end
end
return locations, keyed_locations
end
function computers.networks.add_device(id, pos)
if type(pos) ~= "table" or not networks[id] then return false end
networks[id].devices[minetest.pos_to_string(pos)] = minetest.get_node(pos).name
minetest.get_meta(pos):set_string("net_id", id)
update_file(id)
return true
end
function computers.networks.remove_device(id, pos)
if type(pos) ~= "table" or not networks[id] then return false end
networks[id].devices[minetest.pos_to_string(pos)] = nil
minetest.get_meta(pos):set_string("net_id", "")
update_file(id)
return true
end
function computers.networks.set_dns(id, pos, url)
if type(pos) ~= "table" or not networks[id] then return false end
if url:match("%W") then return false end
networks[id].dns = networks[id].dns or {}
networks[id].dns[url] = minetest.hash_node_position(pos) .. "/public_pages"
update_file(id)
return true
end
function computers.networks.resolve_url(id, pos, url)
if type(pos) ~= "table" or not networks[id] then return false end
if networks[id].dns[url:split("/")[1]] then
local path = computers.devicepath .. "/" .. networks[id].dns[url:split("/")[1]].."/index.page"
local f = io.open(path)
if not f then return false end
local data = f:read("*all")
f:close()
if data then return true, data else return false end
else
return false
end
end

View File

@ -0,0 +1,18 @@
<global margin=20>
<normal></normal>
<center><bigger>Welcome to ARPANET</bigger></center>
<big>How to use it</big>
<normal>on the bottom of this pane is field input which allows you to enter site urls</normal>
<normal>example: <mono><style color=blue>domain</style></mono></normal>
<normal>example: <mono><style color=blue>domain/demo_page.page</style></mono></normal>
<normal></normal>
<big>Try it out</big>
<action name=demo_link _href=demo_page>test url</action>
<normal></normal>
<big>How can I host my own sites?</big>
<normal>you will need to set up the server node</normal>
<normal>put your webfiles in there in hypertext format in the public_html folder</normal>
<normal>domainname/ and domainname/folder/ will connect to the indexpage file found in the DIR</normal>
<normal></normal>

23
computers/demo.lua Normal file
View File

@ -0,0 +1,23 @@
local old_node = minetest.registered_nodes["computers:shefriendSOO"]
old_node.groups.not_in_creative_inventory = 1
minetest.override_item("computers:shefriendSOO", {
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
computers.load_gui(pos, node, clicker)
end,
groups = old_node.groups
})
old_node = minetest.registered_nodes["computers:shefriendSOO_off"]
old_node.groups.not_in_creative_inventory = nil
minetest.override_item("computers:shefriendSOO_off", {
groups = old_node.groups
})
old_node = minetest.registered_nodes["computers:server_on"]
old_node.groups.computers_server = 1
minetest.override_item("computers:server_on", {
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
computers.load_gui(pos, node, clicker)
end,
groups = old_node.groups
})

7
computers/demo_page.page Normal file
View File

@ -0,0 +1,7 @@
<global margin=20>
<normal></normal>
<center><bigger>Welcome to Demo Page</bigger></center>
<normal></normal>
<action name=default_link _href=default_page>go back</action>
<normal></normal>

View File

@ -1,354 +0,0 @@
local S = minetest.get_translator("computers")
-- Sony PlayStation lookalike
computers.register("computers:slaystation", {
description = S("Pony SlayStation"),
inventory_image = "computers_ps1_inv.png",
tiles_off = { top=true },
node_box = computers.pixelnodebox(32, {
-- X Y Z W H L
{ 0, 0, 11, 32, 6, 21 }, -- Console
{ 1, 0, 1, 4, 2, 9 }, -- Controller 1 L Grip
{ 10, 0, 1, 4, 2, 9 }, -- Controller 1 R Grip
{ 5, 0, 4, 5, 2, 5 }, -- Controller 1 Center
{ 18, 0, 1, 4, 2, 9 }, -- Controller 2 L Grip
{ 27, 0, 1, 4, 2, 9 }, -- Controller 2 R Grip
{ 22, 0, 4, 5, 2, 5 } -- Controller 2 Center
})
})
-- Sony PlayStation 2 lookalike
computers.register("computers:slaystation2", {
description = S("Pony SlayStation 2"),
inventory_image = "computers_ps2_inv.png",
tiles_off = { front=true },
node_box = computers.pixelnodebox(32, {
-- X Y Z W H L
{ 2, 2, 11, 28, 3, 19 }, -- Console (Upper part)
{ 2, 0, 11, 26, 2, 19 }, -- Console (Lower part)
{ 1, 0, 1, 4, 2, 9 }, -- Controller 1 L Grip
{ 10, 0, 1, 4, 2, 9 }, -- Controller 1 R Grip
{ 5, 0, 1, 5, 2, 8 }, -- Controller 1 Center
{ 18, 0, 1, 4, 2, 9 }, -- Controller 2 L Grip
{ 27, 0, 1, 4, 2, 9 }, -- Controller 2 R Grip
{ 22, 0, 1, 5, 2, 8 } -- Controller 2 Center
})
})
-- Nintendo Wii lookalike
computers.register("computers:wee", {
description = S("Nientiendo Wee"),
inventory_image = "computers_wii_inv.png",
tiles_off = { front=true },
node_box = computers.pixelnodebox(32, {
-- X Y Z W H L
{ 11, 0, 3, 10, 6, 26 }, -- Base
{ 12, 6, 4, 8, 22, 24 } -- Top
})
})
-- XBox lookalike
computers.register("computers:hueg_box", {
description = S("HUEG Box"),
tiles_off = { },
node_box = computers.pixelnodebox(16, {
-- X Y Z W H L
{ 0, 0, 7, 16, 6, 9 }, -- Console
{ 2, 0, 1, 11, 3, 6 }, -- Controller
{ 2, 0, 0, 2, 3, 1 },
{ 11, 0, 0, 2, 3, 1 },
})
})
-- Tetris arcade machine
local shapes = {
{ { x = {0, 1, 0, 1}, y = {0, 0, 1, 1} } },
{ { x = {1, 1, 1, 1}, y = {0, 1, 2, 3} },
{ x = {0, 1, 2, 3}, y = {1, 1, 1, 1} } },
{ { x = {0, 0, 1, 1}, y = {0, 1, 1, 2} },
{ x = {1, 2, 0, 1}, y = {0, 0, 1, 1} } },
{ { x = {1, 0, 1, 0}, y = {0, 1, 1, 2} },
{ x = {0, 1, 1, 2}, y = {0, 0, 1, 1} } },
{ { x = {1, 2, 1, 1}, y = {0, 0, 1, 2} },
{ x = {0, 1, 2, 2}, y = {1, 1, 1, 2} },
{ x = {1, 1, 0, 1}, y = {0, 1, 2, 2} },
{ x = {0, 0, 1, 2}, y = {0, 1, 1, 1} } },
{ { x = {1, 1, 1, 2}, y = {0, 1, 2, 2} },
{ x = {0, 1, 2, 0}, y = {1, 1, 1, 2} },
{ x = {0, 1, 1, 1}, y = {0, 0, 1, 2} },
{ x = {0, 1, 2, 2}, y = {1, 1, 1, 0} } },
{ { x = {1, 0, 1, 2}, y = {0, 1, 1, 1} },
{ x = {1, 1, 1, 2}, y = {0, 1, 2, 1} },
{ x = {0, 1, 2, 1}, y = {1, 1, 1, 2} },
{ x = {0, 1, 1, 1}, y = {1, 0, 1, 2} } } }
local colors = { "computers_cyan.png", "computers_magenta.png", "computers_red.png",
"computers_blue.png", "computers_green.png", "computers_orange.png", "computers_yellow.png" }
local background = "image[0,0;3.55,6.66;computers_black.png]"
local buttons = "button[3,4.5;0.6,0.6;left;<]"
.."button[3.6,4.5;0.6,0.6;rotateleft;"..minetest.formspec_escape(S("L")).."]"
.."button[4.2,4.5;0.6,0.6;down;v]"
.."button[4.2,5.3;0.6,0.6;drop;V]"
.."button[4.8,4.5;0.6,0.6;rotateright;"..minetest.formspec_escape(S("R")).."]"
.."button[5.4,4.5;0.6,0.6;right;>]"
.."button[3.5,3;2,2;new;"..minetest.formspec_escape(S("New Game")).."]"
local formsize = "size[5.9,5.7]"
local boardx, boardy = 0, 0
local sizex, sizey, size = 0.29, 0.29, 0.31
local comma = ","
local semi = ";"
local close = "]"
local concat = table.concat
local insert = table.insert
local draw_shape = function(id, x, y, rot, posx, posy)
local d = shapes[id][rot]
local scr = {}
local ins = #scr
for i=1,4 do
local tmp = { "image[",
(d.x[i]+x)*sizex+posx, comma,
(d.y[i]+y)*sizey+posy, semi,
size, comma, size, semi,
colors[id], close }
ins = ins + 1
scr[ins] = concat(tmp)
end
return concat(scr)
end
local function step(pos, fields)
local meta = minetest.get_meta(pos)
local t = minetest.deserialize(meta:get_string("tetris"))
local function new_game(p)
local nex = math.random(7)
t = {
board = {},
boardstring = "",
previewstring = draw_shape(nex, 0, 0, 1, 4, 1),
score = 0,
cur = math.random(7),
nex = nex,
x=4, y=0, rot=1
}
local timer = minetest.get_node_timer(p)
timer:set(0.3, 0)
end
local function update_boardstring()
local scr = {}
local ins = #scr
for i, line in pairs(t.board) do
for _, tile in pairs(line) do
local tmp = { "image[",
tile[1]*sizex+boardx, comma,
i*sizey+boardy, semi,
size, comma, size, semi,
colors[tile[2]], close }
ins = ins + 1
scr[ins] = concat(tmp)
end
end
t.boardstring = concat(scr)
end
local function add()
local d = shapes[t.cur][t.rot]
for i=1,4 do
local l = d.y[i] + t.y
if not t.board[l] then t.board[l] = {} end
insert(t.board[l], {d.x[i] + t.x, t.cur})
end
end
local function scroll(l)
for i=l, 1, -1 do
t.board[i] = t.board[i-1] or {}
end
end
local function check_lines()
for i, line in pairs(t.board) do
if #line >= 10 then
scroll(i)
t.score = t.score + 20
end
end
end
local function check_position(x, y, rot)
local d = shapes[t.cur][rot]
for i=1,4 do
local cx, cy = d.x[i]+x, d.y[i]+y
if cx < 0 or cx > 9 or cy < 0 or cy > 19 then
return false
end
for _, tile in pairs(t.board[ cy ] or {}) do
if tile[1] == cx then return false end
end
end
return true
end
local function stuck()
if check_position(t.x, t.y+1, t.rot) then return false end
return true
end
local function tick()
if stuck() then
if t.y <= 0 then
return false end
add()
check_lines()
update_boardstring()
t.cur, t.nex = t.nex, math.random(7)
t.x, t.y, t.rot = 4, 0, 1
t.previewstring = draw_shape(t.nex, 0, 0, 1, 4.1, 0.6)
else
t.y = t.y + 1
end
return true
end
local function move(dx, dy)
local newx, newy = t.x+dx, t.y+dy
if not check_position(newx, newy, t.rot) then return end
t.x, t.y = newx, newy
end
local function rotate(dr)
local no = #(shapes[t.cur])
local newrot = (t.rot+dr) % no
if newrot<1 then newrot = newrot+no end
if not check_position(t.x, t.y, newrot) then return end
t.rot = newrot
end
local function key()
if fields.left then
move(-1, 0)
end
if fields.rotateleft then
rotate(-1)
end
if fields.down then
t.score = t.score + 1
move(0, 1)
end
if fields.drop then
while not stuck() do
t.score = t.score + 2
move(0, 1)
end
end
if fields.rotateright then
rotate(1)
end
if fields.right then
move(1, 0)
end
end
local run = true
if fields then
if fields.new then
new_game(pos)
elseif t then
key(fields)
end
elseif t then
run = tick()
end
if t then
local scr = { formsize, background,
t.boardstring, t.previewstring,
draw_shape(t.cur, t.x, t.y, t.rot, boardx, boardy),
"label[3.8,0.1;"..S("Next...").."]label[3.8,2.7;"..S("Score: "),
t.score, close, buttons }
meta:set_string("formspec", concat(scr)
..default.gui_bg..default.gui_bg_img..default.gui_slots)
meta:set_string("tetris", minetest.serialize(t))
end
return run
end
minetest.register_node("computers:tetris_arcade", {
description=S("Tetris Arcade"),
drawtype = "mesh",
mesh = "computers_tetris_arcade.obj",
tiles = {"computers_tetris_arcade.png"},
paramtype = "light",
paramtype2 = "facedir",
groups = {snappy=3},
on_rotate = minetest.get_modpath("screwdriver") and screwdriver.rotate_simple or nil,
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
},
collision_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formsize
.."button[2,2.5;2,2;new;"..minetest.formspec_escape(S("New Game")).."]"
..default.gui_bg..default.gui_bg_img..default.gui_slots)
end,
on_timer = function(pos)
return step(pos, nil)
end,
on_receive_fields = function(pos, formanme, fields, sender)
step(pos, fields)
end,
on_place = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.above
if minetest.is_protected(pos, placer:get_player_name()) or
minetest.is_protected({x=pos.x, y=pos.y+1, z=pos.z}, placer:get_player_name()) then
return itemstack
end
if minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name ~= "air" then
minetest.chat_send_player(placer:get_player_name(), S("No room for place the Arcade!"))
return itemstack
end
local dir = placer:get_look_dir()
local node = {name="computers:tetris_arcade", param1=0, param2 = minetest.dir_to_facedir(dir)}
minetest.set_node(pos, node)
itemstack:take_item()
return itemstack
end
})

View File

@ -1,98 +1,20 @@
computers = {}
computers.modpath = minetest.get_modpath("computers")
computers.storagepath = minetest.get_worldpath() .. "/computers"
computers.devicepath = computers.storagepath .. "/devices"
computers.networkpath = computers.storagepath .. "/networks"
minetest.mkdir(computers.storagepath) --make sure it exists
minetest.mkdir(computers.devicepath) --make sure it exists
minetest.mkdir(computers.networkpath) --make sure it exists
computers.register = function (name, def)
if (name:sub(1, 1) == ":") then name = name:sub(2) end
local modname, basename = name:match("^([^:]+):(.*)")
local TEXPFX = modname.."_"..basename.."_"
local ONSTATE = modname..":"..basename
local OFFSTATE = modname..":"..basename.."_off"
local cdef = table.copy(def)
minetest.register_node(ONSTATE, {
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
description = cdef.description,
inventory_image = cdef.inventory_image,
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2},
tiles = {
TEXPFX.."tp.png",
TEXPFX.."bt.png",
TEXPFX.."rt.png",
TEXPFX.."lt.png",
TEXPFX.."bk.png",
TEXPFX.."ft.png"
},
node_box = cdef.node_box,
selection_box = cdef.node_box,
on_rightclick = function (pos, node, clicker, itemstack)
if cdef.on_turn_off and cdef.on_turn_off(pos, node, clicker, itemstack) then
return itemstack
end
node.name = OFFSTATE
minetest.set_node(pos, node)
return itemstack
end
})
minetest.register_node(OFFSTATE, {
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, not_in_creative_inventory=1},
tiles = {
(TEXPFX.."tp"..(cdef.tiles_off.top and "_off" or "")..".png"),
(TEXPFX.."bt"..(cdef.tiles_off.bottom and "_off" or "")..".png"),
(TEXPFX.."rt"..(cdef.tiles_off.right and "_off" or "")..".png"),
(TEXPFX.."lt"..(cdef.tiles_off.left and "_off" or "")..".png"),
(TEXPFX.."bk"..(cdef.tiles_off.back and "_off" or "")..".png"),
(TEXPFX.."ft"..(cdef.tiles_off.front and "_off" or "")..".png")
},
node_box = cdef.node_box_off or cdef.node_box,
selection_box = cdef.node_box_off or cdef.node_box,
on_rightclick = function (pos, node, clicker, itemstack)
if cdef.on_turn_on and cdef.on_turn_on(pos, node, clicker, itemstack) then
return itemstack
end
node.name = ONSTATE
minetest.set_node(pos, node)
return itemstack
end,
drop = ONSTATE
})
end
computers.os = {
version = 0.44,
name = "kuto",
authors = {"wsor", "luk3yx"},
license = "MIT",
}
computers.register_handheld = function (name, def)
if (name:sub(1, 1) == ":") then name = name:sub(2) end
local modname, basename = name:match("^([^:]+):(.*)")
local TEXPFX = modname.."_"..basename.."_inv"
local ONSTATE = modname..":"..basename
minetest.register_craftitem(ONSTATE, {
description = def.description,
inventory_image = TEXPFX..".png",
wield_image = TEXPFX..".png"
})
end
dofile(computers.modpath .. "/core/init.lua")
dofile(computers.modpath .. "/nodes/init.lua")
computers.pixelnodebox = function (size, boxes)
local fixed = { }
for _, box in ipairs(boxes) do
local x, y, z, w, h, l = unpack(box)
fixed[#fixed + 1] = {
(x / size) - 0.5,
(y / size) - 0.5,
(z / size) - 0.5,
((x + w) / size) - 0.5,
((y + h) / size) - 0.5,
((z + l) / size) - 0.5
}
end
return {
type = "fixed",
fixed = fixed
}
end
local MODPATH = minetest.get_modpath("computers")
dofile(MODPATH.."/computers.lua")
dofile(MODPATH.."/recipes.lua")
dofile(MODPATH.."/gaming.lua")
dofile(MODPATH.."/aliases.lua")
dofile(computers.modpath .. "/demo.lua")

View File

@ -1,3 +1,3 @@
name = computers
depends = default, basic_materials
optional_depends = screwdriver
depends = formspec_ast
optional_depends = screwdriver, default, basic_materials

View File

@ -3,11 +3,13 @@ minetest.register_alias("computer:shefriendSOO_off", "computers:shefriendSOO
minetest.register_alias("computer:vanio", "computers:vanio")
minetest.register_alias("computer:vanio_off", "computers:vanio_off")
minetest.register_alias("computer:slaystation", "computers:slaystation")
minetest.register_alias("computer:slaystation_off", "computers:slaystation_off")
minetest.register_alias("computer:slaystation2", "computers:slaystation2")
minetest.register_alias("computer:slaystation2_off", "computers:slaystation2_off")
minetest.register_alias("computer:specter", "computers:specter")
minetest.register_alias("computer:specter_off", "computers:specter_off")
minetest.register_alias("computer:wee", "computers:wee")
minetest.register_alias("computer:wee_off", "computers:wee_off")
minetest.register_alias("computer:piepad", "computers:piepad")
minetest.register_alias("computer:admiral64", "computers:admiral64")
minetest.register_alias("computer:admiral64_off", "computers:admiral64_off")

11
computers/nodes/init.lua Normal file
View File

@ -0,0 +1,11 @@
local path = computers.modpath .. "/nodes"
dofile(path .. "/node_api.lua")
dofile(path .. "/nodes.lua")
dofile(path .. "/router.lua")
dofile(path .. "/tetris.lua")
dofile(path .. "/aliases.lua")
if minetest.get_modpath("default") and minetest.get_modpath("basic_materials") then
dofile(path .. "/recipes.lua")
end

View File

@ -0,0 +1,78 @@
computers.register = function (name, def)
if (name:sub(1, 1) == ":") then name = name:sub(2) end
local modname, basename = name:match("^([^:]+):(.*)")
local TEXPFX = modname.."_"..basename.."_"
local ONSTATE = modname..":"..basename
local OFFSTATE = modname..":"..basename.."_off"
local cdef = table.copy(def)
minetest.register_node(ONSTATE, {
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
description = cdef.description,
inventory_image = cdef.inventory_image,
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2},
tiles = {
TEXPFX.."tp.png",
TEXPFX.."bt.png",
TEXPFX.."rt.png",
TEXPFX.."lt.png",
TEXPFX.."bk.png",
TEXPFX.."ft.png"
},
node_box = cdef.node_box,
selection_box = cdef.node_box,
on_rightclick = function (pos, node, clicker, itemstack)
if cdef.on_turn_off and cdef.on_turn_off(pos, node, clicker, itemstack) then
return itemstack
end
node.name = OFFSTATE
minetest.set_node(pos, node)
return itemstack
end
})
minetest.register_node(OFFSTATE, {
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, not_in_creative_inventory=1},
tiles = {
(TEXPFX.."tp"..(cdef.tiles_off.top and "_off" or "")..".png"),
(TEXPFX.."bt"..(cdef.tiles_off.bottom and "_off" or "")..".png"),
(TEXPFX.."rt"..(cdef.tiles_off.right and "_off" or "")..".png"),
(TEXPFX.."lt"..(cdef.tiles_off.left and "_off" or "")..".png"),
(TEXPFX.."bk"..(cdef.tiles_off.back and "_off" or "")..".png"),
(TEXPFX.."ft"..(cdef.tiles_off.front and "_off" or "")..".png")
},
node_box = cdef.node_box_off or cdef.node_box,
selection_box = cdef.node_box_off or cdef.node_box,
on_rightclick = function (pos, node, clicker, itemstack)
if cdef.on_turn_on and cdef.on_turn_on(pos, node, clicker, itemstack) then
return itemstack
end
node.name = ONSTATE
minetest.set_node(pos, node)
return itemstack
end,
drop = ONSTATE
})
end
computers.pixelnodebox = function (size, boxes)
local fixed = { }
for _, box in ipairs(boxes) do
local x, y, z, w, h, l = unpack(box)
fixed[#fixed + 1] = {
(x / size) - 0.5,
(y / size) - 0.5,
(z / size) - 0.5,
((x + w) / size) - 0.5,
((y + h) / size) - 0.5,
((z + l) / size) - 0.5
}
end
return {
type = "fixed",
fixed = fixed
}
end

View File

@ -80,7 +80,7 @@ minetest.register_node("computers:piepad", {
walkable = false,
groups = {oddly_breakable_by_hand=2},
selection_box = {type = "wallmounted"},
sounds = default.node_sound_wood_defaults()
sounds = default and default.node_sound_wood_defaults() or nil
})
-- Commodore 64 lookalike
@ -116,7 +116,9 @@ minetest.register_node("computers:monitor", {
inventory_image = "computers_monitor_inv.png",
drawtype = "mesh",
mesh = "computers_monitor.obj",
tiles = {"computers_black.png", "computers_monitor_plastic.png", "computers_black.png", "computers_monitor_plastic.png"},
tiles = {
"computers_black.png", "computers_monitor_plastic.png", "computers_black.png", "computers_monitor_plastic.png"
},
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
@ -132,7 +134,12 @@ minetest.register_node("computers:monitor", {
minetest.register_node("computers:monitor_on", {
drawtype = "mesh",
mesh = "computers_monitor.obj",
tiles = {"computers_monitor_display.png^[transformFX", "computers_monitor_plastic.png", "computers_black.png", "computers_monitor_plastic.png"},
tiles = {
"computers_monitor_display.png^[transformFX",
"computers_monitor_plastic.png",
"computers_black.png",
"computers_monitor_plastic.png"
},
paramtype = "light",
paramtype2 = "facedir",
light_source = 9,
@ -147,38 +154,6 @@ minetest.register_node("computers:monitor_on", {
end
})
--WIFI Router (linksys look-a-like)
minetest.register_node("computers:router", {
description = S("WIFI Router"),
inventory_image = "computers_router_inv.png",
tiles = {
"computers_router_t.png",
"computers_router_bt.png",
"computers_router_l.png",
"computers_router_r.png",
"computers_router_b.png",
{
name = "computers_router_f_animated.png",
animation = {type="vertical_frames", aspect_w=32, aspect_h=32, length=1.0}
},
}, --"computers_router_f.png"},
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
groups = {snappy=3},
sound = default.node_sound_wood_defaults(),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.25, -0.5, -0.0625, 0.25, -0.375, 0.3125},
{-0.1875, -0.4375, 0.3125, -0.125, -0.1875, 0.375},
{0.125, -0.4375, 0.3125, 0.1875, -0.1875, 0.375},
{-0.0625, -0.4375, 0.3125, 0.0625, -0.25, 0.375}
}
}
})
local pct_cbox = {
type = "fixed",
fixed = { -0.1875, -0.5, -0.36, 0.1875, 0.34, 0.46 }
@ -194,7 +169,7 @@ minetest.register_node("computers:tower", {
paramtype = "light",
paramtype2 = "facedir",
groups = {snappy=3},
sound = default.node_sound_wood_defaults(),
sound = default and default.node_sound_wood_defaults() or nil,
selection_box = pct_cbox,
collision_box = pct_cbox
})
@ -223,7 +198,7 @@ minetest.register_node("computers:server", {
type = "fixed",
fixed = {-0.5, -0.5, -0.25, 0.5, 1.125, 0.4375}
},
sounds = default.node_sound_wood_defaults(),
sounds = default and default.node_sound_wood_defaults() or nil,
on_rightclick = function(pos, node, clicker, itemstack)
node.name = "computers:server_on"
minetest.set_node(pos, node)
@ -262,7 +237,7 @@ minetest.register_node("computers:server_on", {
type = "fixed",
fixed = {-0.5, -0.5, -0.25, 0.5, 1.125, 0.4375}
},
sounds = default.node_sound_wood_defaults(),
sounds = default and default.node_sound_wood_defaults() or nil,
drop = 'computers:server',
on_rightclick = function(pos, node, clicker, itemstack)
node.name = "computers:server"
@ -282,7 +257,7 @@ minetest.register_node("computers:printer", {
paramtype2 = "facedir",
walkable = true,
groups = {snappy=3},
sound = default.node_sound_wood_defaults(),
sound = default and default.node_sound_wood_defaults() or nil,
drawtype = "nodebox",
node_box = {
type = "fixed",
@ -298,3 +273,63 @@ minetest.register_node("computers:printer", {
},
},
})
-- Sony PlayStation lookalike
computers.register("computers:slaystation", {
description = S("Pony SlayStation"),
inventory_image = "computers_ps1_inv.png",
tiles_off = { top=true },
node_box = computers.pixelnodebox(32, {
-- X Y Z W H L
{ 0, 0, 11, 32, 6, 21 }, -- Console
{ 1, 0, 1, 4, 2, 9 }, -- Controller 1 L Grip
{ 10, 0, 1, 4, 2, 9 }, -- Controller 1 R Grip
{ 5, 0, 4, 5, 2, 5 }, -- Controller 1 Center
{ 18, 0, 1, 4, 2, 9 }, -- Controller 2 L Grip
{ 27, 0, 1, 4, 2, 9 }, -- Controller 2 R Grip
{ 22, 0, 4, 5, 2, 5 } -- Controller 2 Center
})
})
-- Sony PlayStation 2 lookalike
computers.register("computers:slaystation2", {
description = S("Pony SlayStation 2"),
inventory_image = "computers_ps2_inv.png",
tiles_off = { front=true },
node_box = computers.pixelnodebox(32, {
-- X Y Z W H L
{ 2, 2, 11, 28, 3, 19 }, -- Console (Upper part)
{ 2, 0, 11, 26, 2, 19 }, -- Console (Lower part)
{ 1, 0, 1, 4, 2, 9 }, -- Controller 1 L Grip
{ 10, 0, 1, 4, 2, 9 }, -- Controller 1 R Grip
{ 5, 0, 1, 5, 2, 8 }, -- Controller 1 Center
{ 18, 0, 1, 4, 2, 9 }, -- Controller 2 L Grip
{ 27, 0, 1, 4, 2, 9 }, -- Controller 2 R Grip
{ 22, 0, 1, 5, 2, 8 } -- Controller 2 Center
})
})
-- Nintendo Wii lookalike
computers.register("computers:wee", {
description = S("Nientiendo Wee"),
inventory_image = "computers_wii_inv.png",
tiles_off = { front=true },
node_box = computers.pixelnodebox(32, {
-- X Y Z W H L
{ 11, 0, 3, 10, 6, 26 }, -- Base
{ 12, 6, 4, 8, 22, 24 } -- Top
})
})
-- XBox lookalike
computers.register("computers:hueg_box", {
description = S("HUEG Box"),
tiles_off = { },
node_box = computers.pixelnodebox(16, {
-- X Y Z W H L
{ 0, 0, 7, 16, 6, 9 }, -- Console
{ 2, 0, 1, 11, 3, 6 }, -- Controller
{ 2, 0, 0, 2, 3, 1 },
{ 11, 0, 0, 2, 3, 1 },
})
})

View File

@ -1,5 +1,3 @@
-- Copyright (C) 2012-2013 Diego Martínez <kaeza@users.sf.net>
minetest.register_craft({
output = "computers:shefriendSOO",
recipe = {

227
computers/nodes/router.lua Normal file
View File

@ -0,0 +1,227 @@
local S = minetest.get_translator("computers")
minetest.register_node("computers:router", {
description = S("WIFI Router"),
inventory_image = "computers_router_inv.png",
tiles = {
"computers_router_t.png",
"computers_router_bt.png",
"computers_router_l.png",
"computers_router_r.png",
"computers_router_b.png",
{
name = "computers_router_f_animated.png",
animation = {type="vertical_frames", aspect_w=32, aspect_h=32, length=1.0}
},
}, --"computers_router_f.png"},
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
groups = {snappy=3},
sound = default and default.node_sound_wood_defaults() or nil,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.25, -0.5, -0.0625, 0.25, -0.375, 0.3125},
{-0.1875, -0.4375, 0.3125, -0.125, -0.1875, 0.375},
{0.125, -0.4375, 0.3125, 0.1875, -0.1875, 0.375},
{-0.0625, -0.4375, 0.3125, 0.0625, -0.25, 0.375}
}
},
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local formspec = {
formspec_version = 4,
{
type = "size",
w = 10,
h = 5,
},
{
type = "no_prepend"
},
{
type = "bgcolor",
bgcolor = "black",
fullscreen = "neither"
},
{
type = "background9",
x = 0,
y = 0,
w = 10,
h = 5,
texture_name = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70",
auto_clip = false,
middle_x = 4,
middle_y = 4,
},
{
type = "hypertext",
name = "label",
x = 0,
y = 0,
w = 10,
h = 2,
text = "<global valign=middle><center><big>Choose Option Below</big></center>",
},
{
type = "button",
x = 0.5,
y = 3.5,
w = 4,
h = 1,
name = "newnetwork_btn",
label = "New Network",
on_event = function(form, player, element, value, fields)
if element.label == "New Network" then
element.label = "Submit"
table.remove(form, computers.formspec.get_index_by_name(form, "existingnetwork_btn"))
form[computers.formspec.get_index_by_name(form, "label")].text = [[
<global valign=middle><center><big>Enter New Network Name</big></center>
]]
table.insert(
form,
{
type = "box",
x = 3,
y = 1.75,
w = 4,
h = 1,
color = "#ffffff"
}
)
table.insert(
form,
{
type = "field",
x = 3,
y = 1.75,
w = 4,
h = 1,
name = "id_field",
close_on_enter = false,
props = {
border = false,
},
}
)
else
if fields.id_field == "" and not computers.formspec.get_index_by_name(form, "warning") then
table.insert(
form,
{
type = "hypertext",
name = "warning",
x = 5.5,
y = 3.5,
w = 4,
h = 1,
text = [[
<global valign=middle>
<center><style color=red>empty or invalid network name</style></center>
]]
}
)
elseif fields.id_field ~= "" then
local status, id = computers.networks.create(player, fields.id_field)
if status then
form = nil
minetest.get_meta(pos):set_string("net_id", id)
computers.networks.add_device(id, pos)
computers.api.chat_send_player(player, "network " .. fields.id_field .. " created")
computers.formspec.close_formspec(player)
elseif not computers.formspec.get_index_by_name(form, "warning") then
table.insert(
form,
{
type = "hypertext",
name = "warning",
x = 5.5,
y = 3.5,
w = 4,
h = 1,
text = [[
<global valign=middle>
<center><style color=red>empty or invalid network name</style></center>
]]
}
)
end
end
end
--minetest.chat_send_all("test")
if form then return form end
end,
props = {
border = false,
bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70",
bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90",
bgimg_middle = "4,4",
}
},
{
type = "button",
x = 5.5,
y = 3.5,
w = 4,
h = 1,
name = "existingnetwork_btn",
label = "Existing Network",
on_event = function(form, player, element, value, fields)
if element.label == "Existing Network" then
element.label = "Submit"
table.remove(form, computers.formspec.get_index_by_name(form, "newnetwork_btn"))
form[computers.formspec.get_index_by_name(form, "label")].text = [[
<global valign=middle><center>Enter Network</center>
]]
table.insert(
form,
{
type = "box",
x = 3,
y = 1.75,
w = 4,
h = 1,
color = "#ffffff"
}
)
table.insert(
form,
{
type = "field",
x = 3,
y = 1.75,
w = 4,
h = 1,
name = "id_field",
close_on_enter = false,
props = {
border = false,
},
}
)
else
minetest.chat_send_all("submitted")
end
return form
end,
props = {
border = false,
bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70",
bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90",
bgimg_middle = "4,4",
}
},
}
if minetest.get_meta(pos):get("net_id") then
local net_name = computers.networks.get_net_name(minetest.get_meta(pos):get("net_id"))
computers.api.chat_send_player(clicker, "router attached to network: " .. net_name)
else
computers.formspec.show_formspec(clicker, "computers:router", formspec)
end
end
})

289
computers/nodes/tetris.lua Normal file
View File

@ -0,0 +1,289 @@
-- Tetris arcade machine
local S = minetest.get_translator("computers")
local shapes = {
{ { x = {0, 1, 0, 1}, y = {0, 0, 1, 1} } },
{ { x = {1, 1, 1, 1}, y = {0, 1, 2, 3} },
{ x = {0, 1, 2, 3}, y = {1, 1, 1, 1} } },
{ { x = {0, 0, 1, 1}, y = {0, 1, 1, 2} },
{ x = {1, 2, 0, 1}, y = {0, 0, 1, 1} } },
{ { x = {1, 0, 1, 0}, y = {0, 1, 1, 2} },
{ x = {0, 1, 1, 2}, y = {0, 0, 1, 1} } },
{ { x = {1, 2, 1, 1}, y = {0, 0, 1, 2} },
{ x = {0, 1, 2, 2}, y = {1, 1, 1, 2} },
{ x = {1, 1, 0, 1}, y = {0, 1, 2, 2} },
{ x = {0, 0, 1, 2}, y = {0, 1, 1, 1} } },
{ { x = {1, 1, 1, 2}, y = {0, 1, 2, 2} },
{ x = {0, 1, 2, 0}, y = {1, 1, 1, 2} },
{ x = {0, 1, 1, 1}, y = {0, 0, 1, 2} },
{ x = {0, 1, 2, 2}, y = {1, 1, 1, 0} } },
{ { x = {1, 0, 1, 2}, y = {0, 1, 1, 1} },
{ x = {1, 1, 1, 2}, y = {0, 1, 2, 1} },
{ x = {0, 1, 2, 1}, y = {1, 1, 1, 2} },
{ x = {0, 1, 1, 1}, y = {1, 0, 1, 2} } } }
local colors = { "computers_cyan.png", "computers_magenta.png", "computers_red.png",
"computers_blue.png", "computers_green.png", "computers_orange.png", "computers_yellow.png" }
local background = "image[0,0;3.55,6.66;computers_black.png]"
local buttons = "button[3,4.5;0.6,0.6;left;<]"
.."button[3.6,4.5;0.6,0.6;rotateleft;"..minetest.formspec_escape(S("L")).."]"
.."button[4.2,4.5;0.6,0.6;down;v]"
.."button[4.2,5.3;0.6,0.6;drop;V]"
.."button[4.8,4.5;0.6,0.6;rotateright;"..minetest.formspec_escape(S("R")).."]"
.."button[5.4,4.5;0.6,0.6;right;>]"
.."button[3.5,3;2,2;new;"..minetest.formspec_escape(S("New Game")).."]"
local formsize = "size[5.9,5.7]"
local boardx, boardy = 0, 0
local sizex, sizey, size = 0.29, 0.29, 0.31
local comma = ","
local semi = ";"
local close = "]"
local concat = table.concat
local insert = table.insert
local draw_shape = function(id, x, y, rot, posx, posy)
local d = shapes[id][rot]
local scr = {}
local ins = #scr
for i=1,4 do
local tmp = { "image[",
(d.x[i]+x)*sizex+posx, comma,
(d.y[i]+y)*sizey+posy, semi,
size, comma, size, semi,
colors[id], close }
ins = ins + 1
scr[ins] = concat(tmp)
end
return concat(scr)
end
local function step(pos, fields)
local meta = minetest.get_meta(pos)
local t = minetest.deserialize(meta:get_string("tetris"))
local function new_game(p)
local nex = math.random(7)
t = {
board = {},
boardstring = "",
previewstring = draw_shape(nex, 0, 0, 1, 4, 1),
score = 0,
cur = math.random(7),
nex = nex,
x=4, y=0, rot=1
}
local timer = minetest.get_node_timer(p)
timer:set(0.3, 0)
end
local function update_boardstring()
local scr = {}
local ins = #scr
for i, line in pairs(t.board) do
for _, tile in pairs(line) do
local tmp = { "image[",
tile[1]*sizex+boardx, comma,
i*sizey+boardy, semi,
size, comma, size, semi,
colors[tile[2]], close }
ins = ins + 1
scr[ins] = concat(tmp)
end
end
t.boardstring = concat(scr)
end
local function add()
local d = shapes[t.cur][t.rot]
for i=1,4 do
local l = d.y[i] + t.y
if not t.board[l] then t.board[l] = {} end
insert(t.board[l], {d.x[i] + t.x, t.cur})
end
end
local function scroll(l)
for i=l, 1, -1 do
t.board[i] = t.board[i-1] or {}
end
end
local function check_lines()
for i, line in pairs(t.board) do
if #line >= 10 then
scroll(i)
t.score = t.score + 20
end
end
end
local function check_position(x, y, rot)
local d = shapes[t.cur][rot]
for i=1,4 do
local cx, cy = d.x[i]+x, d.y[i]+y
if cx < 0 or cx > 9 or cy < 0 or cy > 19 then
return false
end
for _, tile in pairs(t.board[ cy ] or {}) do
if tile[1] == cx then return false end
end
end
return true
end
local function stuck()
if check_position(t.x, t.y+1, t.rot) then return false end
return true
end
local function tick()
if stuck() then
if t.y <= 0 then
return false end
add()
check_lines()
update_boardstring()
t.cur, t.nex = t.nex, math.random(7)
t.x, t.y, t.rot = 4, 0, 1
t.previewstring = draw_shape(t.nex, 0, 0, 1, 4.1, 0.6)
else
t.y = t.y + 1
end
return true
end
local function move(dx, dy)
local newx, newy = t.x+dx, t.y+dy
if not check_position(newx, newy, t.rot) then return end
t.x, t.y = newx, newy
end
local function rotate(dr)
local no = #(shapes[t.cur])
local newrot = (t.rot+dr) % no
if newrot<1 then newrot = newrot+no end
if not check_position(t.x, t.y, newrot) then return end
t.rot = newrot
end
local function key()
if fields.left then
move(-1, 0)
end
if fields.rotateleft then
rotate(-1)
end
if fields.down then
t.score = t.score + 1
move(0, 1)
end
if fields.drop then
while not stuck() do
t.score = t.score + 2
move(0, 1)
end
end
if fields.rotateright then
rotate(1)
end
if fields.right then
move(1, 0)
end
end
local run = true
if fields then
if fields.new then
new_game(pos)
elseif t then
key(fields)
end
elseif t then
run = tick()
end
if t then
local scr = { formsize, background,
t.boardstring, t.previewstring,
draw_shape(t.cur, t.x, t.y, t.rot, boardx, boardy),
"label[3.8,0.1;"..S("Next...").."]label[3.8,2.7;"..S("Score: "),
t.score, close, buttons }
meta:set_string("formspec", concat(scr))
meta:set_string("tetris", minetest.serialize(t))
end
return run
end
minetest.register_node("computers:tetris_arcade", {
description=S("Tetris Arcade"),
drawtype = "mesh",
mesh = "computers_tetris_arcade.obj",
tiles = {"computers_tetris_arcade.png"},
paramtype = "light",
paramtype2 = "facedir",
groups = {snappy=3},
on_rotate = minetest.get_modpath("screwdriver") and screwdriver.rotate_simple or nil,
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
},
collision_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formsize
.."button[2,2.5;2,2;new;"..minetest.formspec_escape(S("New Game")).."]")
end,
on_timer = function(pos)
return step(pos, nil)
end,
on_receive_fields = function(pos, formanme, fields, sender)
step(pos, fields)
end,
on_place = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.above
if minetest.is_protected(pos, placer:get_player_name()) or
minetest.is_protected({x=pos.x, y=pos.y+1, z=pos.z}, placer:get_player_name()) then
return itemstack
end
if minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name ~= "air" then
minetest.chat_send_player(placer:get_player_name(), S("No room for place the Arcade!"))
return itemstack
end
local dir = placer:get_look_dir()
local node = {name="computers:tetris_arcade", param1=0, param2 = minetest.dir_to_facedir(dir)}
minetest.set_node(pos, node)
itemstack:take_item()
return itemstack
end
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

View File

@ -0,0 +1,59 @@
local registered_currency = {}
function home_vending_machines.register_currency(name, worth)
registered_currency[name] = worth
end
local function reg_simple(name, def)
minetest.register_node(":" .. name, {
description = def.description,
drawtype = "mesh",
mesh = "home_vending_machines_machine.obj",
tiles = def.tiles,
paramtype = "light",
paramtype2 = "facedir",
groups = def.groups or {snappy=3},
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
},
collision_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
},
sounds = def.sounds,
on_rotate = function(pos, node, user, mode, new_param2)
if minetest.get_modpath("screwdriver") then
return screwdriver.rotate_simple(pos, node, user, mode, new_param2)
end
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local pname = clicker:get_player_name()
local iname = itemstack:get_name()
local dpos = vector.add((vector.multiply(minetest.facedir_to_dir(node.param2), -1)), pos)
if registered_currency[iname] and registered_currency[iname] == 1 then
local item = def._vmachine.item
if type(item) == "table" then
item = item[math.random(#item)]
end
minetest.add_item(dpos, item)
if not minetest.is_creative_enabled(pname) then
itemstack:take_item()
return itemstack
end
else
minetest.chat_send_player(pname, "Please insert valid currency.")
end
end
})
end
function home_vending_machines.register_machine(type, name, def)
if type == "simple" then
reg_simple(name, def)
end
--TODO: add more complex machine type with formspec and selections
end

View File

@ -0,0 +1,32 @@
if minetest.get_modpath("default") then
if minetest.get_modpath("dye") then
minetest.register_craft({
output = "home_workshop_misc:soda_machine",
recipe = {
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "dye:red", "default:steel_ingot"},
{"default:steel_ingot", "default:copperblock", "default:steel_ingot"},
},
})
end
if minetest.get_modpath("vessel") then
minetest.register_craft({
output = "home_vending_machines:drink_machine",
recipe = {
{"default:steel_ingot", "group:vessel", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "default:copperblock", "default:steel_ingot"},
},
})
end
if minetest.get_modpath("farming") then
minetest.register_craft({
output = "home_vending_machines:sweet_machine",
recipe = {
{"default:steel_ingot", "group:food_sugar", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "default:copperblock", "default:steel_ingot"},
},
})
end
end

View File

@ -0,0 +1,9 @@
local modpath = minetest.get_modpath("home_vending_machines")
home_vending_machines = {}
dofile(modpath .. "/api.lua")
dofile(modpath .. "/machines.lua")
dofile(modpath .. "/items.lua")
dofile(modpath .. "/crafts.lua")
home_vending_machines.init = true

View File

@ -0,0 +1,12 @@
local function reg_item(name, evalue)
minetest.register_craftitem("home_vending_machines:" .. name, {
description = string.gsub(name, "_", " "),
inventory_image = "home_vending_machines_" .. name .. ".png",
on_use = minetest.item_eat(evalue),
})
end
reg_item("soda_can", 2)
minetest.register_alias("home_workshop_misc:soda_can", "home_vending_machines:soda_can")
reg_item("water_bottle", 3)

View File

@ -0,0 +1,29 @@
home_vending_machines.register_currency("default:gold_ingot", 1)
home_vending_machines.register_currency("currency:minegeld_cent_25", 1)
home_vending_machines.register_machine("simple", "home_workshop_misc:soda_machine", {
description = "Soda vending machine",
tiles = {"home_vending_machines_soda_machine.png"},
sounds = nil,
_vmachine = {
item = "home_vending_machines:soda_can"
}
})
home_vending_machines.register_machine("simple", "home_vending_machines:drink_machine", {
description = "Drinks vending machine",
tiles = {"home_vending_machines_drink_machine.png"},
sounds = nil,
_vmachine = {
item = {"home_vending_machines:soda_can", "home_vending_machines:water_bottle"}
}
})
home_vending_machines.register_machine("simple", "home_vending_machines:sweet_machine", {
description = "Sweets vending machine",
tiles = {"home_vending_machines_sweet_machine.png"},
sounds = nil,
_vmachine = {
item = "home_vending_machines:soda_can"
}
})

View File

@ -0,0 +1 @@
optional_depends = screwdriver

View File

@ -0,0 +1,11 @@
code wsor MIT
VanessaE (cc-by-sa 4.0):
home_vending_machines_machine.obj
home_vending_machines_soda_can.png
home_vending_machines_soda_machine.png
Andrey01 (CC BY-SA 3.0):
home_vending_machines_drimk_machine.png
home_vending_machines_sweet_machine.png
home_vending_machines_water_bottle.png

View File

Before

Width:  |  Height:  |  Size: 155 B

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,2 +0,0 @@
-- nothing to see here :P
-- this is just a stub to provide a few textures

View File

@ -17,7 +17,7 @@ minetest.register_node("home_workshop_machines:3dprinter_bedflinger", {
paramtype = "light",
walkable = true,
groups = {snappy=3, ud_param2_colorable = 1},
sound = default.node_sound_wood_defaults(),
sound = default and default.node_sound_wood_defaults() or nil,
drawtype = "mesh",
mesh = "home_workshop_machines_3dprinter_bedflinger.obj",
paramtype2 = "colorwallmounted",
@ -47,7 +47,7 @@ minetest.register_node("home_workshop_machines:3dprinter_corexy", {
paramtype = "light",
walkable = true,
groups = {snappy=3, ud_param2_colorable = 1},
sound = default.node_sound_wood_defaults(),
sound = default and default.node_sound_wood_defaults() or nil,
drawtype = "mesh",
mesh = "home_workshop_machines_3dprinter_corexy.obj",
paramtype2 = "colorwallmounted",

View File

@ -1,3 +1,4 @@
name = home_workshop_machines
depends = default, unifieddyes
depends = unifieddyes
optional_depends = default
optional_depends = screwdriver

View File

@ -14,13 +14,4 @@ minetest.register_craft({
{ "basic_materials:steel_bar", "default:steel_ingot", "basic_materials:steel_bar" },
{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" }
},
})
minetest.register_craft({
output = "homedecor:soda_machine",
recipe = {
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "dye:red", "default:steel_ingot"},
{"default:steel_ingot", "default:copperblock", "default:steel_ingot"},
},
})
})

View File

@ -14,6 +14,7 @@ minetest.register_node("home_workshop_misc:tool_cabinet", {
"home_workshop_common_generic_metal_bright.png",
"home_workshop_misc_tool_cabinet_misc.png",
},
paramtype = "light",
paramtype2="facedir",
inventory_image = "home_workshop_misc_tool_cabinet_inv.png",
on_rotate = minetest.get_modpath("screwdriver") and screwdriver.rotate_simple or nil,
@ -33,6 +34,7 @@ minetest.register_node("home_workshop_misc:beer_tap", {
{ name = "home_workshop_common_generic_metal.png", color = 0xff303030 }
},
inventory_image = "home_workshop_misc_beertap_inv.png",
paramtype = "light",
paramtype2 = "facedir",
groups = { snappy=3 },
walkable = false,
@ -46,9 +48,9 @@ minetest.register_node("home_workshop_misc:beer_tap", {
local wieldname = itemstack:get_name()
if wieldname == "vessels:drinking_glass" then
if inv:room_for_item("main", "home_workshop_misc:beer_mug 1") then
inv:add_item("main", "home_workshop_misc:beer_mug 1")
itemstack:take_item()
clicker:set_wielded_item(itemstack)
inv:add_item("main", "home_workshop_misc:beer_mug 1")
minetest.chat_send_player(clicker:get_player_name(),
S("Ahh, a frosty cold beer - look in your inventory for it!"))
else
@ -70,77 +72,35 @@ minetest.register_node("home_workshop_misc:beer_mug", {
mesh = "home_workshop_misc_beer_mug.obj",
tiles = { "home_workshop_misc_beer_mug.png" },
inventory_image = "home_workshop_misc_beer_mug_inv.png",
paramtype = "light",
paramtype2 = "facedir",
groups = { snappy=3, oddly_breakable_by_hand=3 },
walkable = false,
sounds = default.node_sound_glass_defaults(),
sounds = default and default.node_sound_glass_defaults() or nil,
selection_box = beer_cbox,
on_use = function(itemstack, user, pointed_thing)
if not creative.is_enabled_for(user:get_player_name()) then
if not minetest.is_creative_enabled(user:get_player_name()) then
minetest.do_item_eat(2, "vessels:drinking_glass 1", itemstack, user, pointed_thing)
return itemstack
end
end
})
local svm_cbox = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}
}
minetest.register_node("home_workshop_misc:soda_machine", {
description = S("Soda vending machine"),
drawtype = "mesh",
mesh = "home_workshop_misc_soda_machine.obj",
tiles = {"home_workshop_misc_soda_machine.png"},
paramtype2 = "facedir",
groups = {snappy=3},
selection_box = svm_cbox,
collision_box = svm_cbox,
expand = { top="placeholder" },
sounds = default.node_sound_wood_defaults(),
on_rotate = minetest.get_modpath("screwdriver") and screwdriver.rotate_simple or nil,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local playername = clicker:get_player_name()
local wielditem = clicker:get_wielded_item()
local wieldname = wielditem:get_name()
local fdir_to_fwd = { {0, -1}, {-1, 0}, {0, 1}, {1, 0} }
local fdir = node.param2
local pos_drop = { x=pos.x+fdir_to_fwd[fdir+1][1], y=pos.y, z=pos.z+fdir_to_fwd[fdir+1][2] }
if wieldname == "currency:minegeld_cent_25" then
minetest.spawn_item(pos_drop, "home_workshop_misc:soda_can")
minetest.sound_play("insert_coin", {
pos=pos, max_hear_distance = 5
})
if not creative.is_enabled_for(playername) then
wielditem:take_item()
clicker:set_wielded_item(wielditem)
return wielditem
end
else
minetest.chat_send_player(playername, S("Please insert a 25 Mg cent coin in the machine."))
end
end
})
minetest.register_craftitem("home_workshop_misc:soda_can", {
description = S("Soda Can"),
inventory_image = "home_workshop_misc_soda_can.png",
on_use = minetest.item_eat(2),
})
if minetest.get_modpath("homedecor_common") then
minetest.register_alias("home_workshop_misc:drawer_small", "homedecor:drawer_small")
else
minetest.register_craftitem("home_workshop_misc:drawer_small", {
description = S("Small Wooden Drawer"),
inventory_image = "home_workshop_machines_drawer_small.png",
inventory_image = "home_workshop_common_drawer_small.png",
})
end
local MODPATH = minetest.get_modpath("home_workshop_misc")
dofile(MODPATH.."/crafts.lua")
if minetest.get_modpath("default") and minetest.get_modpath("basic_materials") then
dofile(MODPATH.."/crafts.lua")
end
minetest.register_alias("homedecor:tool_cabinet", "home_workshop_misc:tool_cabinet")
minetest.register_alias("homedecor:tool_cabinet_bottom", "home_workshop_misc:tool_cabinet")
minetest.register_alias("homedecor:tool_cabinet_top", "air")

View File

@ -1,3 +1,2 @@
name = home_workshop_misc
depends = default, home_workshop_common
optional_depends = currency, screwdriver, homedecor_common
optional_depends = currency, screwdriver, homedecor_common, default

2
license.txt Normal file
View File

@ -0,0 +1,2 @@
LGPL-3.0 for code,
CC-BY-SA-4.0 for media.