aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--advtrains/couple.lua2
-rw-r--r--advtrains/pseudoload.lua2
-rw-r--r--advtrains/trainlogic.lua190
3 files changed, 83 insertions, 111 deletions
diff --git a/advtrains/couple.lua b/advtrains/couple.lua
index a846c8b..675bed5 100644
--- a/advtrains/couple.lua
+++ b/advtrains/couple.lua
@@ -140,7 +140,7 @@ minetest.register_entity("advtrains:couple", {
else
tp2=advtrains.get_real_index_position(train2.path, advtrains.get_train_end_index(train2))
end
- if not tp1 or not tp2 or not (vector.distance(tp1,tp2)<0.5) then
+ if not tp1 or not tp2 or not (vector.distance(tp1,tp2)<1.5) then
self.object:remove()
return
else
diff --git a/advtrains/pseudoload.lua b/advtrains/pseudoload.lua
index 3f4f321..677cd14 100644
--- a/advtrains/pseudoload.lua
+++ b/advtrains/pseudoload.lua
@@ -176,7 +176,7 @@ function advtrains.reset_trackdb_position(pos)
if not advtrains.trackdb[rdp.y] then advtrains.trackdb[rdp.y]={} end
if not advtrains.trackdb[rdp.y][rdp.x] then advtrains.trackdb[rdp.y][rdp.x]={} end
advtrains.trackdb[rdp.y][rdp.x][rdp.z]=nil
- advtrains.get_rail_info_at(pos)--to restore it.
+ advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)--to restore it.
end
diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua
index bb38fce..1050d62 100644
--- a/advtrains/trainlogic.lua
+++ b/advtrains/trainlogic.lua
@@ -2,6 +2,7 @@
--controls train entities stuff about connecting/disconnecting/colliding trains and other things
local print=function(t, ...) minetest.log("action", table.concat({t, ...}, " ")) minetest.chat_send_all(table.concat({t, ...}, " ")) end
+local sid=function(id) return string.sub(id, -4) end
--local print=function() end
local benchmark=false
@@ -130,15 +131,7 @@ minetest.register_globalstep(function(dtime)
advtrains.save_and_audit_timer=advtrains.save_and_audit_timer-dtime
if advtrains.save_and_audit_timer<=0 then
local t=os.clock()
- --print("[advtrains] audit step")
- --clean up orphaned trains
- for k,v in pairs(advtrains.trains) do
- --advtrains.update_trainpart_properties(k)
- if #v.trainparts==0 then
- print("[advtrains][train "..k.."] has empty trainparts, removing.")
- advtrains.trains[k]=nil
- end
- end
+
--save
advtrains.save()
advtrains.save_and_audit_timer=advtrains.audit_interval
@@ -244,26 +237,17 @@ function advtrains.train_step(id, train, dtime)
train.detector_old_index = math.floor(train.index)
train.detector_old_end_index = math.floor(train_end_index)
+ --remove?
+ if #train.trainparts==0 then
+ print("[advtrains][train "..sid(id).."] has empty trainparts, removing.")
+ advtrains.detector.leave_node(path[train.detector_old_index], id)
+ advtrains.trains[id]=nil
+ return
+ end
+
if train_moves then
--check for collisions by finding objects
- --front
- local search_radius=4
- --coupling
- local couple_outward=1
- local posfront=advtrains.get_real_index_position(path, train.index+couple_outward)
- local posback=advtrains.get_real_index_position(path, train_end_index-couple_outward)
- for _,pos in ipairs({posfront, posback}) do
- if pos then
- local objrefs=minetest.get_objects_inside_radius(pos, search_radius)
- for _,v in pairs(objrefs) do
- local le=v:get_luaentity()
- if le and le.is_wagon and le.initialized and le.train_id~=id then
- advtrains.try_connect_trains(id, le.train_id)
- end
- end
- end
- end
--heh, new collision again.
--this time, based on NODES and the advtrains.detector.on_node table.
local collpos
@@ -277,24 +261,29 @@ function advtrains.train_step(id, train, dtime)
local rcollpos=advtrains.round_vector_floor_y(collpos)
for x=-1,1 do
for z=-1,1 do
- local testpts=minetest.pos_to_string(vector.add(rcollpos, {x=x, y=0, z=z}))
+ local testpos=vector.add(rcollpos, {x=x, y=0, z=z})
+ local testpts=minetest.pos_to_string(testpos)
if advtrains.detector.on_node[testpts] and advtrains.detector.on_node[testpts]~=id then
--collides
+ advtrains.spawn_couple_on_collide(id, testpos, advtrains.detector.on_node[testpts], train.movedir==-1)
+
train.recently_collided_with_env=true
train.velocity=0.5*train.velocity
train.movedir=train.movedir*-1
train.tarvelocity=0
+
end
end
end
end
end
--check for any trainpart entities if they have been unloaded. do this only if train is near a player, to not spawn entities into unloaded areas
+ --todo function will be taken by update_trainpart_properties
train.check_trainpartload=(train.check_trainpartload or 0)-dtime
local node_range=(math.max((minetest.setting_get("active_block_range") or 0),1)*16)
if train.check_trainpartload<=0 then
local ori_pos=advtrains.get_real_index_position(path, train.index) --not much to calculate
- print("[advtrains][train "..id.."] at "..minetest.pos_to_string(vector.round(ori_pos)))
+ --print("[advtrains][train "..id.."] at "..minetest.pos_to_string(vector.round(ori_pos)))
local should_check=false
for _,p in ipairs(minetest.get_connected_players()) do
@@ -664,38 +653,7 @@ end
--->backpos's will match
--4. R<->F F<->R flip one of these trains and take it as new parent
--->frontpos's will match
-function advtrains.try_connect_trains(id1, id2)
- local train1=advtrains.trains[id1]
- local train2=advtrains.trains[id2]
- if not train1 or not train2 then return end
- if not train1.path or not train2.path then return end
- if #train1.trainparts==0 or #train2.trainparts==0 then return end
-
- local frontpos1=advtrains.get_real_index_position(train1.path, train1.index)
- local backpos1=advtrains.get_real_index_position(train1.path, advtrains.get_train_end_index(train1))
- --couple logic
- --if train1.traintype==train2.traintype then
- local frontpos2=advtrains.get_real_index_position(train2.path, train2.index)
- local backpos2=advtrains.get_real_index_position(train2.path, advtrains.get_train_end_index(train2))
-
- if not frontpos1 or not frontpos2 or not backpos1 or not backpos2 then return end
-
- local couple_spawnradius=0.7
- --case 1 (first train is front)
- if vector.distance(frontpos2, backpos1)<couple_spawnradius then
- advtrains.spawn_couple_if_neccessary(backpos1, frontpos2, id1, id2, true, false)
- --case 2 (second train is front)
- elseif vector.distance(frontpos1, backpos2)<couple_spawnradius then
- advtrains.spawn_couple_if_neccessary(backpos2, frontpos1, id2, id1, true, false)
- --case 3
- elseif vector.distance(backpos2, backpos1)<couple_spawnradius then
- advtrains.spawn_couple_if_neccessary(backpos1, backpos2, id1, id2, true, true)
- --case 4
- elseif vector.distance(frontpos2, frontpos1)<couple_spawnradius then
- advtrains.spawn_couple_if_neccessary(frontpos1, frontpos2, id1, id2, false, false)
- end
- --end
-end
+
--true when trains are facing each other. needed on colliding.
-- check done by iterating paths and checking their direction
--returns nil when not on the same track at all OR when required path items are not generated. this distinction may not always be needed.
@@ -713,72 +671,86 @@ function advtrains.trains_facing(train1, train2)
return nil
end
---order of trains may be irrelevant in some cases. check opposite cases. TODO does this work?
---pos1 and pos2 are just needed to form a median.
-function advtrains.spawn_couple_if_neccessary(pos1, pos2, tid1, tid2, train1_is_backpos, train2_is_backpos)
- --print("spawn_couple_if_neccessary..."..dump({pos1=pos1, pos2=pos2, train1_is_backpos=train1_is_backpos, train2_is_backpos=train2_is_backpos}))
- local train1=advtrains.trains[tid1]
- local train2=advtrains.trains[tid2]
+function advtrains.spawn_couple_on_collide(id1, pos, id2, t1_is_backpos)
+ print("COLLISION: "..sid(id1).." and "..sid(id2).." at "..minetest.pos_to_string(pos)..", t1_is_backpos="..(t1_is_backpos and "true" or "false"))
+ --TODO:
+ local train1=advtrains.trains[id1]
+ local train2=advtrains.trains[id2]
+
+ local found
+ for i=advtrains.minN(train1.path), advtrains.maxN(train1.path) do
+ if vector.equals(train1.path[i], pos) then
+ found=true
+ end
+ end
+ if not found then
+ print("Err: pos not in path")
+ return
+ end
+
+ local frontpos2=train2.path[math.floor(train2.detector_old_index)]
+ local backpos2=train2.path[math.floor(train2.detector_old_end_index)]
+ local t2_is_backpos
+ print("End positions: "..minetest.pos_to_string(frontpos2)..minetest.pos_to_string(backpos2))
+
+ if vector.equals(frontpos2, pos) then
+ t2_is_backpos=false
+ elseif vector.equals(backpos2, pos) then
+ t2_is_backpos=true
+ else
+ print("Err: not a endpos")
+ return --the collision position is not the end position.
+ end
+ print("t2_is_backpos="..(t2_is_backpos and "true" or "false"))
+
local t1_has_couple
- if train1_is_backpos then
+ if t1_is_backpos then
t1_has_couple=train1.couple_eid_back
else
t1_has_couple=train1.couple_eid_front
end
local t2_has_couple
- if train2_is_backpos then
+ if t2_is_backpos then
t2_has_couple=train2.couple_eid_back
else
t2_has_couple=train2.couple_eid_front
end
- if t1_has_couple and t2_has_couple then
- if t1_has_couple~=t2_has_couple then--what the hell
- if minetest.object_refs[t2_has_couple] then minetest.object_refs[t2_has_couple]:remove() end
- if train2_is_backpos then
- train2.couple_eid_back=t1_has_couple
+ if t1_has_couple then
+ if minetest.object_refs[t1_has_couple] then minetest.object_refs[t1_has_couple]:remove() end
+ end
+ if t2_has_couple then
+ if minetest.object_refs[t2_has_couple] then minetest.object_refs[t2_has_couple]:remove() end
+ end
+ local obj=minetest.add_entity(pos, "advtrains:couple")
+ if not obj then print("failed creating object") return end
+ local le=obj:get_luaentity()
+ le.train_id_1=id1
+ le.train_id_2=id2
+ le.train1_is_backpos=t1_is_backpos
+ le.train2_is_backpos=t2_is_backpos
+ --find in object_refs
+ for aoi, compare in pairs(minetest.object_refs) do
+ if compare==obj then
+ if t1_is_backpos then
+ train1.couple_eid_back=aoi
else
- train2.couple_eid_front=t1_has_couple
+ train1.couple_eid_front=aoi
end
- end
- --[[elseif t1_has_couple and not t2_has_couple then
- if train2_is_backpos then
- train2.couple_eid_back=t1_has_couple
- else
- train2.couple_eid_front=t1_has_couple
- end
- elseif not t1_has_couple and t2_has_couple then
- if train1_is_backpos then
- train1.couple_eid_back=t2_has_couple
- else
- train1.couple_eid_front=t2_has_couple
- end]]
- else
- local pos=advtrains.pos_median(pos1, pos2)
- local obj=minetest.add_entity(pos, "advtrains:couple")
- if not obj then print("failed creating object") return end
- local le=obj:get_luaentity()
- le.train_id_1=tid1
- le.train_id_2=tid2
- le.train1_is_backpos=train1_is_backpos
- le.train2_is_backpos=train2_is_backpos
- --find in object_refs
- for aoi, compare in pairs(minetest.object_refs) do
- if compare==obj then
- if train1_is_backpos then
- train1.couple_eid_back=aoi
- else
- train1.couple_eid_front=aoi
- end
- if train2_is_backpos then
- train2.couple_eid_back=aoi
- else
- train2.couple_eid_front=aoi
- end
+ if t2_is_backpos then
+ train2.couple_eid_back=aoi
+ else
+ train2.couple_eid_front=aoi
end
end
end
+ print("Couple entity:"..dump(le))
+
+ --also TODO: integrate check_trainpartload into update_trainpart_properties.
end
+--order of trains may be irrelevant in some cases. check opposite cases. TODO does this work?
+--pos1 and pos2 are just needed to form a median.
+
function advtrains.do_connect_trains(first_id, second_id)
local first_wagoncnt=#advtrains.trains[first_id].trainparts
ude "xCGUITTFont.h" #endif inline u32 clamp_u8(s32 value) { return (u32) MYMIN(MYMAX(value, 0), 255); } GUIChatConsole::GUIChatConsole( gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, ChatBackend* backend, Client* client ): IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, core::rect<s32>(0,0,100,100)), m_chat_backend(backend), m_client(client), m_screensize(v2u32(0,0)), m_animate_time_old(0), m_open(false), m_height(0), m_desired_height(0), m_desired_height_fraction(0.0), m_height_speed(5.0), m_open_inhibited(0), m_cursor_blink(0.0), m_cursor_blink_speed(0.0), m_cursor_height(0.0), m_background(NULL), m_background_color(255, 0, 0, 0), m_font(NULL), m_fontsize(0, 0) { m_animate_time_old = getTimeMs(); // load background settings s32 console_alpha = g_settings->getS32("console_alpha"); m_background_color.setAlpha(clamp_u8(console_alpha)); // load the background texture depending on settings ITextureSource *tsrc = client->getTextureSource(); if (tsrc->isKnownSourceImage("background_chat.jpg")) { m_background = tsrc->getTexture("background_chat.jpg"); m_background_color.setRed(255); m_background_color.setGreen(255); m_background_color.setBlue(255); } else { v3f console_color = g_settings->getV3F("console_color"); m_background_color.setRed(clamp_u8(myround(console_color.X))); m_background_color.setGreen(clamp_u8(myround(console_color.Y))); m_background_color.setBlue(clamp_u8(myround(console_color.Z))); } m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); if (m_font == NULL) { errorstream << "GUIChatConsole: Unable to load mono font "; } else { core::dimension2d<u32> dim = m_font->getDimension(L"M"); m_fontsize = v2u32(dim.Width, dim.Height); m_font->grab(); } m_fontsize.X = MYMAX(m_fontsize.X, 1); m_fontsize.Y = MYMAX(m_fontsize.Y, 1); // set default cursor options setCursor(true, true, 2.0, 0.1); } GUIChatConsole::~GUIChatConsole() { if (m_font) m_font->drop(); } void GUIChatConsole::openConsole(f32 height) { m_open = true; m_desired_height_fraction = height; m_desired_height = height * m_screensize.Y; reformatConsole(); } bool GUIChatConsole::isOpen() const { return m_open; } bool GUIChatConsole::isOpenInhibited() const { return m_open_inhibited > 0; } void GUIChatConsole::closeConsole() { m_open = false; } void GUIChatConsole::closeConsoleAtOnce() { m_open = false; m_height = 0; recalculateConsolePosition(); } f32 GUIChatConsole::getDesiredHeight() const { return m_desired_height_fraction; } void GUIChatConsole::setCursor( bool visible, bool blinking, f32 blink_speed, f32 relative_height) { if (visible) { if (blinking) { // leave m_cursor_blink unchanged m_cursor_blink_speed = blink_speed; } else { m_cursor_blink = 0x8000; // on m_cursor_blink_speed = 0.0; } } else { m_cursor_blink = 0; // off m_cursor_blink_speed = 0.0; } m_cursor_height = relative_height; } void GUIChatConsole::draw() { if(!IsVisible) return; video::IVideoDriver* driver = Environment->getVideoDriver(); // Check screen size v2u32 screensize = driver->getScreenSize(); if (screensize != m_screensize) { // screen size has changed // scale current console height to new window size if (m_screensize.Y != 0) m_height = m_height * screensize.Y / m_screensize.Y; m_desired_height = m_desired_height_fraction * m_screensize.Y; m_screensize = screensize; reformatConsole(); } // Animation u32 now = getTimeMs(); animate(now - m_animate_time_old); m_animate_time_old = now; // Draw console elements if visible if (m_height > 0) { drawBackground(); drawText(); drawPrompt(); } gui::IGUIElement::draw(); } void GUIChatConsole::reformatConsole() { s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better) s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt if (cols <= 0 || rows <= 0) cols = rows = 0; m_chat_backend->reformat(cols, rows); } void GUIChatConsole::recalculateConsolePosition() { core::rect<s32> rect(0, 0, m_screensize.X, m_height); DesiredRect = rect; recalculateAbsolutePosition(false); } void GUIChatConsole::animate(u32 msec) { // animate the console height s32 goal = m_open ? m_desired_height : 0; if (m_height != goal) { s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0); if (max_change == 0) max_change = 1; if (m_height < goal) { // increase height if (m_height + max_change < goal) m_height += max_change; else m_height = goal; } else { // decrease height if (m_height > goal + max_change) m_height -= max_change; else m_height = goal; } recalculateConsolePosition(); } // blink the cursor if (m_cursor_blink_speed != 0.0) { u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0); if (blink_increase == 0) blink_increase = 1; m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff); } // decrease open inhibit counter if (m_open_inhibited > msec) m_open_inhibited -= msec; else m_open_inhibited = 0; } void GUIChatConsole::drawBackground() { video::IVideoDriver* driver = Environment->getVideoDriver(); if (m_background != NULL) { core::rect<s32> sourcerect(0, -m_height, m_screensize.X, 0); driver->draw2DImage( m_background, v2s32(0, 0), sourcerect, &AbsoluteClippingRect, m_background_color, false); } else { driver->draw2DRectangle( m_background_color, core::rect<s32>(0, 0, m_screensize.X, m_height), &AbsoluteClippingRect); } } void GUIChatConsole::drawText() { if (m_font == NULL) return; ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); for (u32 row = 0; row < buf.getRows(); ++row) { const ChatFormattedLine& line = buf.getFormattedLine(row); if (line.fragments.empty()) continue; s32 line_height = m_fontsize.Y; s32 y = row * line_height + m_height - m_desired_height; if (y + line_height < 0) continue; for (u32 i = 0; i < line.fragments.size(); ++i) { const ChatFormattedFragment& fragment = line.fragments[i]; s32 x = (fragment.column + 1) * m_fontsize.X; core::rect<s32> destrect( x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y); m_font->draw( fragment.text.c_str(), destrect, video::SColor(255, 255, 255, 255), false, false, &AbsoluteClippingRect); } } } void GUIChatConsole::drawPrompt() { if (m_font == NULL) return; u32 row = m_chat_backend->getConsoleBuffer().getRows(); s32 line_height = m_fontsize.Y; s32 y = row * line_height + m_height - m_desired_height; ChatPrompt& prompt = m_chat_backend->getPrompt(); std::wstring prompt_text = prompt.getVisiblePortion(); // FIXME Draw string at once, not character by character // That will only work with the cursor once we have a monospace font for (u32 i = 0; i < prompt_text.size(); ++i) { wchar_t ws[2] = {prompt_text[i], 0}; s32 x = (1 + i) * m_fontsize.X; core::rect<s32> destrect( x, y, x + m_fontsize.X, y + m_fontsize.Y);