mg_villages/roads.lua

210 lines
7.6 KiB
Lua

-- helper function for get_path_from_pos_to_plot
mg_villages.next_step_on_road_path = function( p, this_road_xdir, following_road )
if( this_road_xdir == true ) then
if( p.x < following_road.x ) then
p.x = following_road.x;
else
p.x = following_road.x + following_road.bsizex -1;
end
else
if( p.z < following_road.z ) then
p.z = following_road.z;
else
p.z = following_road.z + following_road.bsizez -1;
end
end
return p;
end
-- pos needs to be a position either on a road or at max 1 node away from a road
mg_villages.get_path_from_pos_to_plot_via_roads = function( village_id, pos, target_plot_nr )
if( not( mg_villages.all_villages[ village_id ] )
or not( target_plot_nr )
or not( mg_villages.all_villages[ village_id ].to_add_data.bpos[ target_plot_nr ])
or not( mg_villages.all_villages[ village_id ].to_add_data.bpos[ target_plot_nr ].road_nr)) then
return {};
end
local bpos_list = mg_villages.all_villages[ village_id ].to_add_data.bpos;
-- find out which road is the one next to pos
local standing_on_road = nil;
local roads = mg_villages.get_road_list( village_id, false );
for i,road in ipairs( roads ) do
local r = bpos_list[ road ]; -- road data
-- if this is really a road, and if a parent road exists (or is 0)
if( r and r.btype == "road" and r.parent_road_plot
-- ..and pos is in the area of the road or next to it
and pos.x >= r.x-1 and pos.x <= r.x + r.bsizex + 1
and pos.z >= r.z-1 and pos.z <= r.z + r.bsizez + 1
and pos.y >= r.y-4 and pos.y <= r.y + 4 ) then
standing_on_road = i;
end
end
-- nothing found
if( not( standing_on_road )) then
return;
end
-- walk from pos up to the main road
local start_to_main_road = {};
local next_road_plot = roads[ standing_on_road ];
while( next_road_plot and bpos_list[ next_road_plot ] and bpos_list[ next_road_plot ].btype=="road" ) do
table.insert( start_to_main_road, next_road_plot );
next_road_plot = bpos_list[ next_road_plot ].parent_road_plot;
end
-- walk from the target road up to the main road - until we find a road that is
-- already part of the path from pos to the main road
local target_to_main_road = {};
local next_road_plot = roads[ bpos_list[ target_plot_nr ].road_nr ];
local match_found = -1;
while( next_road_plot and bpos_list[ next_road_plot ] and bpos_list[ next_road_plot ].btype=="road" and match_found==-1) do
-- it may not be necessary to go all the way back to the main road
for i,r in ipairs( start_to_main_road ) do
if( r == next_road_plot ) then
match_found = i;
end
end
if( match_found == -1) then
table.insert( target_to_main_road, next_road_plot );
end
next_road_plot = bpos_list[ next_road_plot ].parent_road_plot;
end
if( match_found == -1 ) then
match_found = #start_to_main_road;
end
-- we may have gone too far up and can take a turn much earlier
local start_to_target = {};
for i=1,match_found do
table.insert( start_to_target, start_to_main_road[i] );
end
-- combine the full walk through the tree-like road structure into one list of roads
for i=#target_to_main_road,1,-1 do
table.insert( start_to_target, target_to_main_road[i] );
end
-- generate a path for travelling on these roads
local path = {};
-- let the mob take the first step onto the road
local first_road = bpos_list[ start_to_target[1] ];
local p = {x=pos.x, y=first_road.y+1, z=pos.z};
p = mg_villages.next_step_on_road_path( p, not(first_road.xdir), first_road );
table.insert( path, {x=p.x, y=p.y, z=p.z} );
-- travel using all the given roads
for i=1,#start_to_target-1 do
local this_road = bpos_list[ start_to_target[i] ];
local following_road = bpos_list[ start_to_target[i+1]];
-- walk on the inside in curves instead of taking longer paths
p = mg_villages.next_step_on_road_path( p, this_road.xdir, following_road );
table.insert( path, {x=p.x, y=p.y, z=p.z} );
end
-- walk on the last road to the target plot
local last_road = bpos_list[ start_to_target[ #start_to_target ] ];
local target = {x=bpos_list[ target_plot_nr ].x + math.floor(bpos_list[ target_plot_nr ].bsizex/2),
y = p.y,
z=bpos_list[ target_plot_nr ].z + math.floor(bpos_list[ target_plot_nr ].bsizez/2),
bsizex = 2, bsizez = 2};
p = mg_villages.next_step_on_road_path( p, last_road.xdir, target);
table.insert( path, {x=p.x, y=p.y, z=p.z} );
-- take the very last step and leave the road
p = mg_villages.next_step_on_road_path( p, not(last_road.xdir), target);
-- make sure we do not walk further than one step into the plot
if( p.x < last_road.x ) then
p.x = last_road.x - 1;
elseif( p.x >= last_road.x + last_road.bsizex ) then
p.x = last_road.x + last_road.bsizex + 1;
elseif( p.z < last_road.z ) then
p.z = last_road.z - 1;
elseif( p.z >= last_road.z + last_road.bsizez ) then
p.z = last_road.z + last_road.bsizez + 1;
end
table.insert( path, {x=p.x, y=p.y, z=p.z} );
--[[
-- if you want to visualize the path with yellow wool blocks for debugging, uncomment this
local str = " path: ";
for i,p in ipairs( path ) do
minetest.set_node( p, {name="wool:yellow"});
str = str.." "..minetest.pos_to_string( p );
end
minetest.chat_send_player("singleplayer","roads to walk on: "..minetest.serialize( start_to_target)..str);
--]]
return path;
end
-- try to reconstruct the tree-like road network structure (the data was
-- not saved completely from the beginning)
mg_villages.get_road_list = function( village_id, force_check )
if( not( mg_villages.all_villages[ village_id ] )) then
return {};
end
local bpos_list = mg_villages.all_villages[ village_id ].to_add_data.bpos;
local roads = {};
for i,pos in ipairs( bpos_list ) do
if( pos.btype and pos.btype=="road" ) then
-- store the plot nr for each road nr
roads[ pos.road_nr ] = i;
-- store weather the road streches in x- or z-direction
if( pos.bsizex >= pos.bsizez) then
pos.xdir = true;
else
pos.xdir = false;
end
end
end
-- a village without roads (i.e. a single house)
if( not( roads[1])) then
return {};
end
-- the parent roads have already been identified
if( not( force_check ) and bpos_list[ roads[ 1 ]].parent_road_plot == 0 ) then
return roads;
end
-- assume that road nr. 1 is the main road (which it is due to the way villages are constructed)
bpos_list[ roads[ 1 ]].parent_road_plot = 0;
-- identify all parent roads
for i=1,#roads do
if( bpos_list[ roads[i] ].parent_road_plot ) then
mg_villages.mark_roads_that_branch_off( bpos_list, roads, roads[i] );
end
end
return roads;
end
-- changes bpos_list and sets bpos_list[ road ].parent_road_plot = plot_nr for those roads where
-- plot_nr contains the road from which road branches off
mg_villages.mark_roads_that_branch_off = function( bpos_list, roads, plot_nr )
-- see which roads branch off from this parent road
local parent_road = bpos_list[ plot_nr ];
for i,road in ipairs( roads ) do
local r = bpos_list[ road ]; -- road data
-- if the road is not yet connected to another one
if( r.parent_road_plot == nil
-- and if it is 90 degree rotated compared to the potential parent road
and( r.xdir ~= parent_road.xdir )
-- and if one end lies inside the parent road
and( (r.x >= parent_road.x and r.x <= parent_road.x + parent_road.bsizex)
or(r.x+r.bsizex >= parent_road.x and r.x+r.bsizex <= parent_road.x + parent_road.bsizex))
and( (r.z >= parent_road.z and r.z <= parent_road.z + parent_road.bsizez)
or(r.z+r.bsizez >= parent_road.z and r.z+r.bsizez <= parent_road.z + parent_road.bsizez))) then
-- store plot_nr instead of road_nr as that is more useful
bpos_list[ road ].parent_road_plot = plot_nr;
end
end
end