diff --git a/LICENSE.txt b/LICENSE.txt index 1c5bae7..d4562b8 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,6 @@ -License of textures -All textures are by kaeza, licensed under the CC-BY-SA license +License of textures and models +Textures and nodebox of screen are by kaeza, modified by Nore, under the WTFPL license +All other textures are by kaeza, licensed under the CC-BY-SA license ------------------------------ License of code diff --git a/README.txt b/README.txt index fa29dde..9efbcf8 100644 --- a/README.txt +++ b/README.txt @@ -5,4 +5,4 @@ or placing nodes. -------------------- Depends on: pipeworks -digilines (optional) \ No newline at end of file +digilines \ No newline at end of file diff --git a/api.lua b/api.lua index 962c441..31d5546 100644 --- a/api.lua +++ b/api.lua @@ -1,5 +1,5 @@ local MOVE_COST = 100 -local FUEL_EFFICIENCY = 10000 +local FUEL_EFFICIENCY = 1000 tl = {} local function delay(x) diff --git a/depends.txt b/depends.txt index 0b73adb..5459f9b 100644 --- a/depends.txt +++ b/depends.txt @@ -1,2 +1,2 @@ pipeworks - +digilines diff --git a/floppy.lua b/floppy.lua new file mode 100644 index 0000000..b7f3c48 --- /dev/null +++ b/floppy.lua @@ -0,0 +1,162 @@ +floppy = {} + +local floppies = db.read_file("floppies") + +minetest.register_on_shutdown(function() + db.write_file("floppies", floppies) +end) + +local function create_floppy_id() + return #floppies + 1 +end + +local function handle_floppy_meta(stack) + if stack.metadata == "" or stack.metadata == nil then + local id = create_floppy_id() + stack.metadata = tostring(id) + floppies[id] = string.rep(string.char(0), 16384) + return floppies[id], true + elseif string.len(stack.metadata) >= 1000 then + local id = create_floppy_id() + floppies[id] = stack.metadata + stack.metadata = tostring(id) + return floppies[id], true + else + if floppies[tonumber(stack.metadata)] == nil then + floppies[tonumber(stack.metadata)] = string.rep(string.char(0), 16384) + end + return floppies[tonumber(stack.metadata)], false + end +end + +local function set_floppy_contents(name, contents) + floppies[tonumber(name)] = contents +end + +minetest.register_craftitem("turtle:floppy", { + description = "Floppy disk", + inventory_image = "floppy.png", + stack_max = 1, +}) + +local progs = { + ["Empty"] = string.rep(string.char(0), 16536), + ["Forth Boot Disk"] = create_forth_floppy(), +} + +minetest.register_node("turtle:floppy_programmator",{ + description = "Floppy disk programmator", + tiles = {"floppy_programmator_top.png", "floppy_programmator_bottom.png", "floppy_programmator_right.png", + "floppy_programmator_left.png", "floppy_programmator_back.png", "floppy_programmator_front.png"}, + groups = {cracky = 3}, + sounds = default.node_sound_stone_defaults(), + on_construct = function(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + inv:set_size("floppy", 1) + meta:set_int("selected", 1) + local s = "size[8,5.5;]" .. + "dropdown[0,0;5;pselector;" + for key, _ in pairs(progs) do + s = s .. key .. "," + end + s = string.sub(s, 1, -2) + s = s .. ";1]" .. + "button[5,0;2,1;prog;Program]" .. + "list[current_name;floppy;7,0;1,1;]" .. + "list[current_player;main;0,1.5;8,4;]" + meta:set_string("formspec", s) + end, + can_dig = function(pos, player) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + return inv:is_empty("floppy") + end, + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + if stack:get_name() == "turtle:floppy" then return 1 end + return 0 + end, + on_receive_fields = function(pos, formname, fields, sender) + local meta = minetest.get_meta(pos) + if fields.prog then + local inv = meta:get_inventory() + local prog = progs[fields.pselector] + local stack = inv:get_stack("floppy", 1):to_table() + if stack == nil then return end + if stack.name ~= "turtle:floppy" or stack:is_empty() then return end + local contents, update = handle_floppy_meta(stack) + set_floppy_contents(stack.metadata, prog) + if update then + inv:set_stack("floppy", 1, ItemStack(stack)) + end + end + end, +}) + +function floppy.disk_digiline_receive(inv, channel, msg, disk_channel, send_func) + if channel == disk_channel then + if string.len(msg) ~= 1 and string.len(msg) ~= 65 then return end -- Invalid message, it comes probably from the disk itself + local page = string.byte(msg, 1) + if page == nil then return end + local stack = inv:get_stack("floppy", 1):to_table() + if stack == nil then return end + if stack.name ~= "turtle:floppy" then return end + local floppy_contents, update = handle_floppy_meta(stack) + if update then + inv:set_stack("floppy", 1, ItemStack(stack)) + end + msg = string.sub(msg, 2, -1) + if string.len(msg) == 0 then -- read + send_func(string.sub(floppy_contents, page * 64 + 1, page * 64 + 64)) + else -- write + floppy_contents = string.sub(floppy_contents, 1, page * 64) .. + msg .. + string.sub(floppy_contents, page * 64 + 65, -1) + set_floppy_contents(stack.metadata, floppy_contents) + end + end +end + +minetest.register_node("turtle:disk",{ + description = "Disk drive", + paramtype2 = "facedir", + tiles = {"floppy_drive_top.png", "floppy_drive_bottom.png", "floppy_drive_right.png", "floppy_drive_left.png", "floppy_drive_back.png", "floppy_drive_front.png"}, + groups = {cracky = 3}, + sounds = default.node_sound_stone_defaults(), + digiline = + { + receptor = {}, + effector = {action = function(pos, node, channel, msg) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local disk_channel = meta:get_string("channel") + floppy.disk_digiline_receive(inv, channel, msg, disk_channel, function(msg) + digiline:receptor_send(pos, digiline.rules.default, disk_channel, msg) + end) + end}, + }, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + inv:set_size("floppy", 1) + meta:set_string("channel", "") + meta:set_string("formspec", "size[9,5.5;]".. + "field[0,0.5;7,1;channel;Channel:;${channel}]".. + "list[current_name;floppy;8,0;1,1;]".. + "list[current_player;main;0,1.5;8,4;]") + end, + can_dig = function(pos, player) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + return inv:is_empty("floppy") + end, + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + if stack:get_name() == "turtle:floppy" then return 1 end + return 0 + end, + on_receive_fields = function(pos, formname, fields, sender) + local meta = minetest.get_meta(pos) + fields.channel = fields.channel or meta:get_string("channel") + meta:set_string("channel", fields.channel) + end, +}) diff --git a/init.lua b/init.lua index cfd35ae..8668b8c 100644 --- a/init.lua +++ b/init.lua @@ -1,10 +1,24 @@ turtles = {} local modpath = minetest.get_modpath("turtle") ---dofile(modpath.."/turtle.lua") + +-- Database handler (for turtles and floppies) dofile(modpath .. "/db.lua") + +-- Initial computer state: bootloader dofile(modpath .. "/computer_memory.lua") + +-- Data for the forth floppy, created from forth.fth file by f.py dofile(modpath .. "/forth_floppy.lua") + +-- Computer simulation code dofile(modpath .. "/cptr.lua") + +-- Screen handler for formspec dofile(modpath .. "/screen.lua") + +-- Floppy, floppy programmator, and disk +dofile(modpath .. "/floppy.lua") + +-- Turtle code dofile(modpath .. "/t2.lua") diff --git a/screen.lua b/screen.lua index 929dc79..93ae215 100644 --- a/screen.lua +++ b/screen.lua @@ -58,3 +58,125 @@ function screen.create_text_formspec(text, basex, basey) return s:sub(2, -1) end +------------------ +-- Screen nodes -- +------------------ + +local screens = {} + +local function hashpos(pos) + if pos.x == 0 then pos.x = 0 end -- Fix for signed 0 + if pos.y == 0 then pos.y = 0 end -- Fix for signed 0 + if pos.z == 0 then pos.z = 0 end -- Fix for signed 0 + return tostring(pos.x).."\n"..tostring(pos.y).."\n"..tostring(pos.z) +end + +local function dehashpos(str) + local l = lines(str) + return {x = tonumber(l[1]), y = tonumber(l[2]), z = tonumber(l[3])} +end + +local function screen_digiline_receive(pos, node, channel, msg) + local meta = minetest.get_meta(pos) + if channel == meta:get_string("channel") then + local ntext = screen.add_text(meta:get_string("text"), msg) + meta:set_string("text", ntext) + local hash = hashpos(pos) + if not screens[hash] then + screens[hash] = {pos = vector.new(pos), fmodif = true, playernames = {}} + else + screens[hashpos(pos)].fmodif = true + end + end +end + +local function create_screen_formspec(text) + return "size[5,4.5;]" .. screen.create_text_formspec(text, 0, 0) +end + +minetest.register_globalstep(function(dtime) + for screenhash, i in pairs(screens) do + if i.fmodif then + i.fmodif = false + local meta = minetest.get_meta(i.pos) + for pname, _ in pairs(i.playernames) do + minetest.show_formspec(pname, "screen" .. screenhash, + create_screen_formspec(meta:get_string("text"))) + end + end + end +end) + +local MAX_TEXT_SEND = 80 +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname:sub(1,6) ~= "screen" then return end + local hash = formname:sub(7, -1) + local s = screens[hash] + if s == nil then return end + local pos = s.pos + if fields["f"] == nil or fields["f"] == "" then + if fields["quit"] ~= nil then + s.playernames[player:get_player_name()] = nil + end + return + end + if string.len(fields["f"]) > MAX_TEXT_SEND then + fields["f"] = string.sub(fields["f"], 1, MAX_TEXT_SEND) + end + digiline:receptor_send(pos, digiline.rules.default, "screen", fields["f"]) + local meta = minetest.get_meta(pos) + local ntext = screen.add_text(meta:get_string("text"), fields["f"]) + meta:set_string("text", ntext) + minetest.show_formspec(player:get_player_name(), formname, create_screen_formspec(ntext)) +end) + +minetest.register_node("turtle:screen", { + description = "Screen", + tiles = {"screen_top.png", "screen_bottom.png", "screen_right.png", "screen_left.png", "screen_back.png", "screen_front.png"}, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + node_box = { + type = "fixed", + fixed = { + { -16/32, -16/32, 1/32, 16/32, 16/32, 13/32 }, -- Monitor Screen + { -13/32, -13/32, 13/32, 13/32, 13/32, 16/32 }, -- Monitor Tube + { -16/32, -16/32, -16/32, 16/32, -12/32, 1/32 }, -- Keyboard + } + }, + groups = {cracky = 3}, + sounds = default.node_sound_stone_defaults(), + digiline = + { + receptor = {}, + effector = {action = screen_digiline_receive}, + }, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("text", "\n\n\n\n\n\n\n\n\n\n\n\n") + screens[hashpos(pos)] = {pos = pos, fmodif = false, playernames = {}} + meta:set_string("channel", "") + meta:set_string("formspec", "field[channel;Channel;${channel}]") + end, + on_receive_fields = function(pos, formname, fields, sender) + local meta = minetest.get_meta(pos) + fields.channel = fields.channel or "" + meta:set_string("channel", fields.channel) + meta:set_string("formspec", "") + end, + on_destruct = function(pos) + screens[hashpos(pos)] = nil + end, + on_rightclick = function(pos, node, clicker) + local name = clicker:get_player_name() + local meta = minetest.get_meta(pos) + local hash = hashpos(pos) + if screens[hash] == nil then + screens[hash] = {pos = pos, fmodif = false, playernames = {}} + end + screens[hash].playernames[name] = true + minetest.show_formspec(name, "screen" .. hash, + create_screen_formspec(meta:get_string("text"))) + end, +}) + diff --git a/t2.lua b/t2.lua index 1fb0551..a25fbcb 100644 --- a/t2.lua +++ b/t2.lua @@ -5,7 +5,6 @@ local DEBUG = true ------------------------- local turtle_infos = db.read_file("turtle_infos") -local floppies = db.read_file("floppies") minetest.register_on_shutdown(function() for id, info in pairs(turtle_infos) do @@ -13,9 +12,12 @@ minetest.register_on_shutdown(function() info.playernames = {} end db.write_file("turtle_infos", turtle_infos) - db.write_file("floppies", floppies) end) +------------------ +-- Some helpers -- +------------------ + function turtles.get_turtle_info(turtle_id) if turtle_infos[turtle_id] == nil then turtle_infos[turtle_id] = {} @@ -32,17 +34,13 @@ function turtles.create_turtle_id() return #turtle_infos + 1 end -function turtles.create_floppy_id() - return #floppies + 1 -end - function turtles.update_formspec(turtle_id) local info = turtles.get_turtle_info(turtle_id) local pos = info.spos local formspec = "size[13,9]".. screen.create_text_formspec(info.screen, 0, 0).. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;4.8,0;8,4;]".. - "image_button[1,7.6;2.5,1;turtle_execute.png;reboot;]".. + "image_button[1,7.6;2.5,1;turtle_reboot.png;reboot;]".. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";floppy;0,7.6;1,1;]".. "list[current_player;main;4.8,4.6;8,4;]" if info.formspec ~= formspec then @@ -59,120 +57,24 @@ local function on_screen_digiline_receive(turtle, channel, msg) end end -local function handle_floppy_meta(stack) - if stack.metadata == "" or stack.metadata == nil then - local id = turtles.create_floppy_id() - stack.metadata = tostring(id) - floppies[id] = string.rep(string.char(0), 16384) - return floppies[id], true - elseif string.len(stack.metadata) >= 1000 then - local id = turtles.create_floppy_id() - floppies[id] = stack.metadata - stack.metadata = tostring(id) - return floppies[id], true - else - if floppies[tonumber(stack.metadata)] == nil then - floppies[tonumber(stack.metadata)] = string.rep(string.char(0), 16384) - end - return floppies[tonumber(stack.metadata)], false - end +local function on_disk_digiline_receive(turtle, channel, msg) + local inv = turtles.get_turtle_inventory(turtle) + floppy.disk_digiline_receive(inv, channel, msg, "boot", + function(msg) turtle_receptor_send(turtle, "boot", msg) end) end -local function set_floppy_contents(name, contents) - floppies[tonumber(name)] = contents -end - -function on_disk_digiline_receive(turtle_id, channel, msg) - if channel == "boot" then - if string.len(msg) ~= 1 and string.len(msg) ~= 65 then return end -- Invalid message, it comes probably from the disk itself - local page = string.byte(msg, 1) - if page == nil then return end - local inv = turtles.get_turtle_inventory(turtle_id) - local stack = inv:get_stack("floppy", 1):to_table() - if stack == nil then return end - if stack.name ~= "turtle:floppy" then return end - local floppy_contents, update = handle_floppy_meta(stack) - if update then - inv:set_stack("floppy", 1, ItemStack(stack)) - end - msg = string.sub(msg, 2, -1) - if string.len(msg) == 0 then -- read - turtle_receptor_send(turtle_id, channel, - string.sub(floppy_contents, page * 64 + 1, page * 64 + 64)) - else -- write - floppy_contents = string.sub(floppy_contents, 1, page * 64) .. - msg .. - string.sub(floppy_contents, page * 64 + 65, -1) - set_floppy_contents(stack.metadata, floppy_contents) - end - end -end - -minetest.register_craftitem("turtle:floppy", { - description = "Floppy disk", - inventory_image = "floppy.png", - stack_max = 1, -}) - -local progs = { - ["Empty"] = string.rep(string.char(0), 16536), - ["Forth Boot Disk"] = create_forth_floppy(), -} - -minetest.register_node("turtle:floppy_programmator",{ - description = "Floppy disk programmator", - tiles = {"floppy_programmator_top.png", "floppy_programmator_bottom.png", "floppy_programmator_right.png", - "floppy_programmator_left.png", "floppy_programmator_back.png", "floppy_programmator_front.png"}, - groups = {cracky = 3}, - sounds = default.node_sound_stone_defaults(), - on_construct = function(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - inv:set_size("floppy", 1) - meta:set_int("selected", 1) - local s = "size[8,5.5;]" .. - "dropdown[0,0;5;pselector;" - for key, _ in pairs(progs) do - s = s .. key .. "," - end - s = string.sub(s, 1, -2) - s = s .. ";1]" .. - "button[5,0;2,1;prog;Program]" .. - "list[current_name;floppy;7,0;1,1;]" .. - "list[current_player;main;0,1.5;8,4;]" - meta:set_string("formspec", s) - end, - can_dig = function(pos, player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty("floppy") - end, - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - if stack:get_name() == "turtle:floppy" then return 1 end - return 0 - end, - on_receive_fields = function(pos, formname, fields, sender) - local meta = minetest.get_meta(pos) - if fields.prog then - local inv = meta:get_inventory() - local prog = progs[fields.pselector] - local stack = inv:get_stack("floppy", 1):to_table() - if stack == nil then return end - if stack.name ~= "turtle:floppy" then return end - local contents, update = handle_floppy_meta(stack) - set_floppy_contents(stack.metadata, prog) - if update then - inv:set_stack("floppy", 1, ItemStack(stack)) - end - end - end, -}) - function turtle_receptor_send(turtle, channel, msg) on_screen_digiline_receive(turtle, channel, msg) on_computer_digiline_receive(turtle, channel, msg) on_disk_digiline_receive(turtle, channel, msg) - --on_turtle_command_receive(turtle, channel, msg) + local info = turtles.get_turtle_info(turtle) + digiline:receptor_send(info.spos, digiline.rules.default, channel, msg) +end + +local function turtle_receive(turtle, channel, msg) + on_screen_digiline_receive(turtle, channel, msg) + on_computer_digiline_receive(turtle, channel, msg) + on_disk_digiline_receive(turtle, channel, msg) end minetest.register_on_player_receive_fields(function(player, formname, fields) @@ -218,6 +120,15 @@ minetest.register_node("turtle:turtle", { sunlight_propagates = true, walkable = false, pointable = false, + digiline = + { + receptor = {}, + effector = {action = function(pos, node, channel, msg) + local meta = minetest.get_meta(pos) + local turtle = meta:get_int("turtle_id") + turtle_receive(turtle, channel, msg) + end}, + }, after_place_node = function(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() diff --git a/textures/floppy_drive_back.png b/textures/floppy_drive_back.png new file mode 100644 index 0000000..377df5e Binary files /dev/null and b/textures/floppy_drive_back.png differ diff --git a/textures/floppy_drive_bottom.png b/textures/floppy_drive_bottom.png new file mode 100644 index 0000000..8da61de Binary files /dev/null and b/textures/floppy_drive_bottom.png differ diff --git a/textures/floppy_drive_front.png b/textures/floppy_drive_front.png new file mode 100644 index 0000000..1bc6887 Binary files /dev/null and b/textures/floppy_drive_front.png differ diff --git a/textures/floppy_drive_left.png b/textures/floppy_drive_left.png new file mode 100644 index 0000000..ef4e0f7 Binary files /dev/null and b/textures/floppy_drive_left.png differ diff --git a/textures/floppy_drive_right.png b/textures/floppy_drive_right.png new file mode 100644 index 0000000..b02c4b4 Binary files /dev/null and b/textures/floppy_drive_right.png differ diff --git a/textures/floppy_drive_top.png b/textures/floppy_drive_top.png new file mode 100644 index 0000000..51a32a6 Binary files /dev/null and b/textures/floppy_drive_top.png differ diff --git a/textures/screen_back.png b/textures/screen_back.png new file mode 100644 index 0000000..4f179b6 Binary files /dev/null and b/textures/screen_back.png differ diff --git a/textures/screen_bottom.png b/textures/screen_bottom.png new file mode 100644 index 0000000..a052328 Binary files /dev/null and b/textures/screen_bottom.png differ diff --git a/textures/screen_front.png b/textures/screen_front.png new file mode 100644 index 0000000..3dbfbcf Binary files /dev/null and b/textures/screen_front.png differ diff --git a/textures/screen_left.png b/textures/screen_left.png new file mode 100644 index 0000000..193850d Binary files /dev/null and b/textures/screen_left.png differ diff --git a/textures/screen_right.png b/textures/screen_right.png new file mode 100644 index 0000000..7fd2f3a Binary files /dev/null and b/textures/screen_right.png differ diff --git a/textures/screen_top.png b/textures/screen_top.png new file mode 100644 index 0000000..0c34c88 Binary files /dev/null and b/textures/screen_top.png differ