427 lines
16 KiB
Lua
427 lines
16 KiB
Lua
|
|
-- founding of a new city
|
|
citybuilder.cityadmin_start_city_at = function( pos, owner, city_name )
|
|
-- there can be only one city at a given location
|
|
local city_id = minetest.pos_to_string( pos );
|
|
if( citybuilder.cities[ city_id ] ) then
|
|
return "There is already a sattlement registered at "..tostring(city_id)..".";
|
|
end
|
|
|
|
-- players cannot found an infinite number of cities
|
|
local anz = 1;
|
|
for k,v in pairs( citybuilder.cities ) do
|
|
if( v and v.owner and v.owner == owner) then
|
|
anz = anz+1;
|
|
end
|
|
end
|
|
if( anz > citybuilder.max_cities_per_player ) then
|
|
return "You can only have "..tostring( citybuilder.max_cities_per_player ).." cities.";
|
|
end
|
|
|
|
-- make sure cities do not overlap
|
|
local city_start_pos = { x=pos.x-math.ceil(citybuilder.min_intercity_distance/2), y=pos.y- 20, z=pos.z-math.ceil(citybuilder.min_intercity_distance/2) };
|
|
local city_end_pos = { x=pos.x+math.ceil(citybuilder.min_intercity_distance/2), y=pos.y+100, z=pos.z+math.ceil(citybuilder.min_intercity_distance/2) };
|
|
for k,v in pairs( citybuilder.cities ) do
|
|
if( v and v.start_pos and v.end_pos
|
|
and (math.abs( v.pos.x - pos.x )< citybuilder.min_intercity_distance )
|
|
and (math.abs( v.pos.z - pos.z )< citybuilder.min_intercity_distance )
|
|
and (math.abs( v.pos.y - pos.y )< citybuilder.min_intercity_distance )) then
|
|
return "City area would overlap with city at "..tostring( k )..".";
|
|
end
|
|
end
|
|
|
|
-- register the new city
|
|
citybuilder.cities[ city_id ] = {
|
|
pos = pos,
|
|
start_pos = city_start_pos,
|
|
end_pos = city_end_pos,
|
|
owner = owner,
|
|
founded = os.time(),
|
|
city_name = city_name,
|
|
wood = "-unkown-",
|
|
buildings = {},
|
|
inhabitants = {} };
|
|
-- save it
|
|
citybuilder.save_data();
|
|
return;
|
|
end
|
|
|
|
|
|
citybuilder.cityadmin_get_main_menu_formspec = function( city_id )
|
|
local city_data = citybuilder.cities[ city_id ];
|
|
if( not( city_data )) then
|
|
return "label[0.5,0.2;Error: City has not been founded yet.]";
|
|
end
|
|
return "label[3.5,0.2;City administration]"..
|
|
"label[0.2,1.0;Name of settlement:]"..
|
|
"label[3.5,1.0;"..city_data.city_name.."]"..
|
|
"label[3.5,1.3;founded by "..city_data.owner.."]"..
|
|
"label[0.2,2.0;Wood type used:]"..
|
|
"item_image[3.5,1.75;1,1;"..city_data.wood.."]"..
|
|
"label[0.2,3.0;Number of buildings:]"..
|
|
"label[3.5,3.0;"..citybuilder.city_get_anz_buildings( city_id ).."]"..
|
|
"button[4.0,3.0;2.5,0.5;show_buildings;Show buildings]"..
|
|
"label[0.2,4.0;Number of inhabitants:]"..
|
|
"label[3.5,4.0;"..table.getn( city_data.inhabitants ).."]";
|
|
|
|
end
|
|
|
|
|
|
citybuilder.cityadmin_on_receive_fields = function( pos, formname, fields, player)
|
|
if( not( pos ) or fields.OK or fields.abort) then
|
|
return;
|
|
end
|
|
local meta = minetest.get_meta( pos );
|
|
-- these values are duplicated in the city datastructure
|
|
local owner = meta:get_string( "owner" );
|
|
local pname = player:get_player_name();
|
|
local city_id = minetest.pos_to_string( pos );
|
|
local city_data = citybuilder.cities[ city_id ];
|
|
local pos_str = "field[20,20;0.1,0.1;pos2str;Pos;"..city_id.."]";
|
|
local inv = meta:get_inventory();
|
|
|
|
local formspec = "size[10,10]"..
|
|
pos_str..
|
|
"label[3,1;City administration]";
|
|
|
|
-- most functions need to be limited to the founder of the city
|
|
if( owner ~= pname) then
|
|
formspec = "size[8,6]"..
|
|
pos_str..
|
|
citybuilder.cityadmin_get_main_menu_formspec( city_id )..
|
|
"button_exit[3.5,5;1.0,0.5;abort;Exit]";
|
|
|
|
|
|
-- the player provided a city name
|
|
elseif( fields.set_city_name and fields.set_city_name ~= "" and fields.new_city_name and fields.new_city_name ~= "") then
|
|
|
|
if( not( city_data )) then
|
|
-- check if the city can be founded here
|
|
local error_msg = citybuilder.cityadmin_start_city_at( pos, owner, fields.new_city_name );
|
|
if( error_msg ) then
|
|
minetest.show_formspec( pname, "citybuilder:cityadmin",
|
|
"size[8,2]"..
|
|
"label[0,0.0;Founding of this city failed. Reason:]"..
|
|
"label[0.5,0.4;"..error_msg.."]"..
|
|
"button_exit[3.5,1.2;1,0.5;abort;Exit]");
|
|
return;
|
|
end
|
|
end
|
|
meta:set_string( "city_name", fields.new_city_name );
|
|
meta:set_string( "infotext", fields.new_city_name.." (founded by "..tostring( owner )..") city administrator desk");
|
|
|
|
-- store the new name in the citydatastructure
|
|
citybuilder.cities[ city_id ].city_name = fields.new_city_name;
|
|
citybuilder.save_data();
|
|
|
|
formspec = "size[6,2]"..
|
|
pos_str..
|
|
"label[0,0.0;Your settlement is now known as:]"..
|
|
"label[1.5,0.7;"..fields.new_city_name.."]"..
|
|
"button[2.5,1.5;1.5,0.5;proceed;Proceed]";
|
|
|
|
|
|
-- the city needs a name
|
|
elseif( not( city_data ) or fields.rename_city) then
|
|
local city_name = "";
|
|
if( city_data and city_data.city_name ~= "") then
|
|
city_name = city_data.city_name;
|
|
else
|
|
city_name = owner.." City";
|
|
end
|
|
formspec = "size[6,2]"..
|
|
pos_str..
|
|
"label[0,0.0;How shall your settlement be called?]"..
|
|
"label[0,0.7;Name:]"..
|
|
"field[1.5,1;4,0.5;new_city_name;;"..city_name.."]"..
|
|
"button_exit[1.5,1.5;1.5,0.5;abort;Abort]"..
|
|
"button[3.5,1.5;1.5,0.5;set_city_name;Save]";
|
|
|
|
|
|
-- make the node diggable again by abandoning cities
|
|
elseif( fields.confirm_abandon and fields.abandon_city_name and city_data and fields.abandon_city_name==city_data.city_name ) then
|
|
|
|
-- All inventory spaces (saplings, printer, ..) need to be empty
|
|
if( not( inv:is_empty("saplings")) or not( inv:is_empty( "printer_input" )) or not( inv:is_empty( "printer_output"))) then
|
|
formspec = "size[5,2]"..
|
|
"label[0.5,0;Please remove all saplings and ]"..
|
|
"label[0.5,0.5;constructors first!]"..
|
|
"button_exit[2.0,1.3;1.5,0.5;back;Back]";
|
|
-- forbid abandoning of cities that have assigned buildings
|
|
elseif( citybuilder.city_get_anz_buildings( city_id )>0) then
|
|
formspec = "size[5,2]"..
|
|
"label[0.5,0;Please remove all buildings that are]"..
|
|
"label[0.5,0.5;associated with this city first!]"..
|
|
"button_exit[2.0,1.3;1.5,0.5;back;Back]";
|
|
-- ..or those that have inhabitants
|
|
elseif( table.getn( city_data.inhabitants)>0) then
|
|
formspec = "size[5,2]"..
|
|
"label[0.5,0;The city still has some inhabitants.]"..
|
|
"label[0.5,0.5;Get rid of them first!]"..
|
|
"button_exit[2.0,1.3;1.5,0.5;back;Back]";
|
|
else
|
|
-- actually unregister the city from the data structure and make the node diggable
|
|
citybuilder.cities[ city_id ] = nil;
|
|
citybuilder.save_data();
|
|
meta:set_string( "city_name", nil);
|
|
meta:set_string("infotext", "Founding of a city by "..meta:get_string("owner").." (planned)");
|
|
-- return a formspec for confirmation
|
|
formspec = "size[6,2]"..
|
|
"label[0,0;City abandoned. You can now dig the city]"..
|
|
"label[0,0.5;administration desk and use it elsewhere.]"..
|
|
"button_exit[2.0,1.3;1.5,0.5;OK;OK]";
|
|
end
|
|
|
|
elseif( fields.abandon ) then
|
|
formspec = "size[8,2.5]"..
|
|
pos_str..
|
|
"label[0,0.0;Do you really want to abandon your city?]"..
|
|
"label[0,0.5;If so, please enter the name of your city below and confirm:]"..
|
|
"label[0,1.0;Name:]"..
|
|
"field[1.5,1.4;4,0.5;abandon_city_name;;]"..
|
|
"button[1.5,1.9;1.5,0.5;back;Abort]"..
|
|
"button[3.5,1.9;3.5,0.5;confirm_abandon;Confirm - really abandon]";
|
|
|
|
|
|
-- set the type of wood the city will use
|
|
elseif( city_data and (city_data.wood == "" or not(minetest.registered_nodes[city_data.wood]) or fields.change_wood or fields.change_wood_store)) then
|
|
|
|
local stack = inv:get_stack("saplings", 1 );
|
|
if( stack and not( stack:is_empty()) and fields.change_wood_store) then
|
|
local sapling_name = stack:get_name();
|
|
for k,v in pairs( replacements_group['wood'].data ) do
|
|
if( v[6]==sapling_name and k ~= city_data.wood and stack:get_count()>=25) then
|
|
-- take the 25 saplings
|
|
inv:remove_item("saplings", stack:get_name().." 25");
|
|
-- set the new wood type and store it in the city data structure
|
|
citybuilder.cities[ city_id ].wood = k;
|
|
citybuilder.save_data();
|
|
end
|
|
end
|
|
end
|
|
|
|
local show_wood = "";
|
|
if( city_data.wood and minetest.registered_nodes[ city_data.wood ]) then
|
|
show_wood = "item_image[4.4,1.4;1,1;"..city_data.wood.."]"..
|
|
"button_exit[6.0,1.0;1.5,0.5;back;Back]";
|
|
end
|
|
|
|
formspec = "size[8,7.5]"..
|
|
pos_str..
|
|
"label[0,0.0;Which type of trees shall your lumberjacks use?]"..
|
|
"label[0.0,0.5;Please insert 25 saplings of the desired type here:]"..
|
|
"label[1.4,1.4;Saplings:]"..
|
|
"list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";saplings;2.5,1.2;1,1;]"..
|
|
"label[4.0,1.0;Current wood:]"..
|
|
show_wood..
|
|
"button[6.0,2.0;1.5,0.5;change_wood_store;Save]"..
|
|
"list[current_player;main;0,3.0;8,4;]";
|
|
|
|
|
|
-- show a list of all buildings that belong to the city
|
|
elseif( fields.show_buildings ) then
|
|
formspec = "size[10,10]"..
|
|
pos_str..
|
|
"label[0.2,0.2;Buildings that belong to this settlement:]"..
|
|
"tablecolumns[" ..
|
|
"text,align=center;".. -- title of the building
|
|
"text,align=center;".. -- what the building provides
|
|
"text,align=center;".. -- description of building
|
|
"text,align=center;".. -- level of the building
|
|
"text,align=center]".. -- position
|
|
'table[0.2,0.8;9.0,8.0;form_does_not_exist;';
|
|
|
|
for k,v in pairs(city_data.buildings) do
|
|
local building_data = citybuilder.city_get_building_data( v.building_name );
|
|
if( building_data ) then
|
|
formspec = formspec..
|
|
minetest.formspec_escape(building_data.title or building_data.scm or tostring(i))..","..
|
|
minetest.formspec_escape("["..(building_data.provides or "- unknown -").."]")..","..
|
|
minetest.formspec_escape("\""..building_data.descr or "").."\","..
|
|
minetest.formspec_escape("[Level "..(building_data.level or "?").."]")..","..
|
|
minetest.formspec_escape(k)..",";
|
|
end
|
|
end
|
|
formspec = formspec..';]'..
|
|
"button[8.0,9.0;1.0,0.5;back;Back]";
|
|
|
|
|
|
-- add new buildings (by "printing" blueprints on citybuilder:constructor_blank
|
|
elseif( fields.add_building or fields[ "citybuilder:cityadmin"] or fields.print_building) then
|
|
if( fields.print_building and fields.selected_blueprint) then
|
|
local input_stack = inv:get_stack("printer_input", 1 );
|
|
local output_stack = inv:get_stack("printer_output", 1 );
|
|
-- is there a blank constructor available which we can configure?
|
|
if( output_stack:is_empty() and inv:contains_item("printer_input", "citybuilder:constructor_blank")) then
|
|
-- take one blank constructor..
|
|
input_stack:take_item(1);
|
|
-- ..and add a properly configured one
|
|
local new_stack = citybuilder.constructor_get_configured_itemstack(
|
|
citybuilder.full_filename[ citybuilder.starter_buildings[ tonumber(fields.selected_blueprint) ]],
|
|
city_data.owner, city_id, city_data.wood, player );
|
|
-- put the configured constructor in the output field
|
|
output_stack:add_item( new_stack );
|
|
inv:set_stack( "printer_input", 1, input_stack );
|
|
inv:set_stack( "printer_output", 1, output_stack );
|
|
end
|
|
end
|
|
|
|
formspec = "size[10,10]"..
|
|
pos_str..
|
|
"label[0.2,0.2;Please select the type of building you want to add:]"..
|
|
"list[current_player;main;0,6.0;8,4;]"..
|
|
"button[8.5,0.0;1.0,0.5;back;Back]"..
|
|
"label[7.8,1.0;Input:]"..
|
|
"list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";printer_input;8.0,1.5;1,1;]"..
|
|
"item_image[9.0,1.5;1,1;citybuilder:constructor_blank]"..
|
|
"label[7.8,3.5;Output:]"..
|
|
"list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";printer_output;8.0,4.0;1,1;]"..
|
|
"item_image[9.0,4.0;1,1;citybuilder:constructor]"..
|
|
"tablecolumns[" ..
|
|
"text,align=center;".. -- description of building
|
|
"text,align=center]".. -- what the building provides
|
|
'table[0.2,0.8;6.0,5.0;'..formname..';';
|
|
|
|
|
|
for i,v in ipairs( citybuilder.starter_buildings ) do
|
|
local building_data = citybuilder.city_get_building_data( v );
|
|
formspec = formspec..
|
|
(building_data.title or building_data.scm or tostring(i))..","..
|
|
minetest.formspec_escape("["..(building_data.provides or "- unknown -").."]")..",";
|
|
end
|
|
|
|
local clicked = minetest.explode_table_event( fields[ "citybuilder:cityadmin"] );
|
|
if( clicked and clicked.row and clicked.row > 0 and citybuilder.starter_buildings[ clicked.row ]) then
|
|
local building_data = citybuilder.city_get_building_data( citybuilder.starter_buildings[ clicked.row ]);
|
|
formspec = formspec..';'..tostring(clicked.row)..']'..
|
|
"field[20,21;0.1,0.1;selected_blueprint;selected_blueprint_row;"..tostring(clicked.row).."]"..
|
|
"button[7.0,2.8;3.0,0.5;print_building;Print selected blueprint]"..
|
|
"label[7.0,5.0;Selected: "..tostring( building_data.title or "-unkown-").."]";
|
|
else
|
|
formspec = formspec..';]';
|
|
end
|
|
|
|
|
|
-- normal main menu
|
|
else
|
|
formspec = "size[10,6]"..
|
|
pos_str..
|
|
citybuilder.cityadmin_get_main_menu_formspec( city_id )..
|
|
"button[8,1;2.0,0.5;rename_city;Rename city]"..
|
|
"button[8,2;2.0,0.5;change_wood;Change wood]"..
|
|
"button[8,3;2.0,0.5;add_building;Add building]"..
|
|
"button[8,4;2.0,0.5;info_inhabitants;Show details]"..
|
|
"button[0.2,5;2.5,0.5;abandon;Abandon settlement]"..
|
|
"button_exit[8.5,5;1.0,0.5;abort;Exit]";
|
|
end
|
|
minetest.show_formspec( pname, "citybuilder:cityadmin", formspec );
|
|
end
|
|
|
|
|
|
minetest.register_node("citybuilder:cityadmin", {
|
|
description = "Desk of the city administrator",
|
|
tiles = {"default_chest_top.png^default_book_written.png", "default_chest_top.png", "default_chest_side.png", -- TODO: needs a better texture
|
|
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png^default_tool_diamondshovel.png"},
|
|
paramtype2 = "facedir",
|
|
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
|
legacy_facedir_simple = true,
|
|
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.5, 0.5-2/16, -0.5, 0.5, 0.5, 0.5}, -- top of the desk
|
|
{-0.5+2/16, -0.5, -0.5, -0.5, 0.5, 0.5}, -- one side
|
|
{ 0.5-2/16, -0.5, -0.5, 0.5, 0.5, 0.5}, -- another side
|
|
{-0.5, 0.5-6/16, -0.5+2/16, 0.5, 0.5, -0.5}, -- front
|
|
},
|
|
},
|
|
|
|
|
|
after_place_node = function(pos, placer, itemstack)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("owner", placer:get_player_name() or "");
|
|
meta:set_string("infotext", "Founding of a city by "..meta:get_string("owner").." (planned)");
|
|
end,
|
|
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("owner", "")
|
|
local inv = meta:get_inventory()
|
|
-- used for determining which wood type the village will use
|
|
inv:set_size("saplings", 1 * 1)
|
|
-- used for turning citybuilder:constructor_blank into configured citybuilder:constructor
|
|
inv:set_size("printer_input", 1 * 1)
|
|
inv:set_size("printer_output", 1 * 1)
|
|
end,
|
|
|
|
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
|
if( not( citybuilder.can_access_inventory( pos, player))) then
|
|
return 0;
|
|
end
|
|
return count
|
|
end,
|
|
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
if( not( citybuilder.can_access_inventory( pos, player))) then
|
|
return 0;
|
|
end
|
|
if( listname=="printer_output") then
|
|
return 0;
|
|
end
|
|
if( listname=="printer_input" and (not( stack ) or stack:get_name()~="citybuilder:constructor_blank")) then
|
|
return 0;
|
|
end
|
|
return stack:get_count()
|
|
end,
|
|
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
if( not( citybuilder.can_access_inventory( pos, player))) then
|
|
return 0;
|
|
end
|
|
return stack:get_count()
|
|
end,
|
|
|
|
can_dig = function(pos,player)
|
|
local meta = minetest.get_meta( pos );
|
|
local inv = meta:get_inventory();
|
|
local owner_name = meta:get_string( "owner" );
|
|
local name = player:get_player_name();
|
|
|
|
if( not( meta ) or not( owner_name )) then
|
|
return true;
|
|
end
|
|
-- only the owner can dig
|
|
if( owner_name ~= name and owner_name ~= "") then
|
|
minetest.chat_send_player(name,
|
|
"This city administration device belongs to "..tostring( owner_name )..
|
|
". You can't take it.");
|
|
return false;
|
|
end
|
|
|
|
-- cities that have been founded cannot be destroyed by digging the node
|
|
if( citybuilder.cities[ minetest.pos_to_string( pos )]) then
|
|
minetest.chat_send_player(name,
|
|
"This city has been founded already. If you want to abandon it, please "..
|
|
"select the appropriate entry in the menu first.");
|
|
return false;
|
|
end
|
|
|
|
return true;
|
|
end,
|
|
|
|
-- handle formspec manually - not via meta:set_string("formspec") as it is very dynamic
|
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
citybuilder.cityadmin_on_receive_fields(pos, "citybuilder:cityadmin", {}, clicker );
|
|
return itemstack;
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_craft({
|
|
output = "citybuilder:cityadmin",
|
|
recipe = {
|
|
{"default:diamondblock", "default:diamondblock", "default:diamondblock"},
|
|
{"default:diamondblock", "default:diamondblock", "default:diamondblock"},
|
|
{"default:diamondblock", "default:diamondblock", "default:diamondblock"},
|
|
}
|
|
})
|