aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking/route_ui.lua
blob: 1999941a1db0c7248f70a95c2dc157dcb705d6ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
-- route_ui.lua
-- User interface for showing and editing routes

local atil = advtrains.interlocking
local ildb = atil.db

-- TODO duplicate
local lntrans = { "A", "B" }
local function sigd_to_string(sigd)
	return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
end



function atil.show_route_edit_form(pname, sigd, routeid)

	if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then
		minetest.chat_send_player(pname, "Insufficient privileges to use this!")
		return
	end
	
	local tcbs = atil.db.get_tcbs(sigd)
	if not tcbs then return end
	local route = tcbs.routes[routeid]
	if not route then return end
	
	local form = "size[9,10]label[0.5,0.2;Route overview]"
	form = form.."field[0.8,1.2;6.5,1;name;Route name;"..minetest.formspec_escape(route.name).."]"
	form = form.."button[7.0,0.9;1.5,1;setname;Set]"
	
	-- construct textlist for route information
	local tab = {}
	local function itab(t)
		tab[#tab+1] = minetest.formspec_escape(string.gsub(t, ",", " "))
	end
	itab("TCB "..sigd_to_string(sigd).." ("..tcbs.signal_name..") Route #"..routeid)
	
	-- this code is partially copy-pasted from routesetting.lua
	-- we start at the tc designated by signal
	local c_sigd = sigd
	local i = 1
	local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp
	while c_sigd and i<=#route do
		c_tcbs = ildb.get_tcbs(c_sigd)
		if not c_tcbs then
			itab("-!- No TCBS at "..sigd_to_string(c_sigd)..". Please reconfigure route!")
			break
		end
		c_ts_id = c_tcbs.ts_id
		if not c_ts_id then
			itab("-!- No track section adjacent to "..sigd_to_string(c_sigd)..". Please reconfigure route!")
			break
		end
		c_ts = ildb.get_ts(c_ts_id)
		
		c_rseg = route[i]
		c_lckp = {}
		
		itab(""..i.." Entry "..sigd_to_string(c_sigd).." -> Sec. "..(c_ts and c_ts.name or "-").." -> Exit "..(c_rseg.next and sigd_to_string(c_rseg.next) or "END"))
		
		if c_rseg.locks then
			for pts, state in pairs(c_rseg.locks) do
				
				local pos = minetest.string_to_pos(pts)
				itab("  Lock: "..pts.." -> "..state)
				if not advtrains.is_passive(pos) then
					itab("-!- No passive component at "..pts..". Please reconfigure route!")
					break
				end
			end
		end
		-- advance
		c_sigd = c_rseg.next
		i = i + 1
	end
	if c_sigd then
		local e_tcbs = ildb.get_tcbs(c_sigd)
		itab("Route end: "..sigd_to_string(c_sigd).." ("..(e_tcbs and e_tcbs.signal_name or "-")..")")
	else
		itab("Route ends on dead-end")
	end
	
	form = form.."textlist[0.5,2;7.75,3.9;rtelog;"..table.concat(tab, ",").."]"
	
	form = form.."button[0.5,6;3,1;back;<<< Back to signal]"
	form = form.."button[4.5,6;2,1;aspect;Signal Aspect]"
	form = form.."button[6.5,6;2,1;delete;Delete Route]"
	
	--atdebug(route.ars)
	form = form.."style[ars;font=mono]"
	form = form.."textarea[0.8,7.3;5,3;ars;ARS Rule List;"..atil.ars_to_text(route.ars).."]"
	form = form.."button[5.5,7.23;3,1;savears;Save ARS List]"
	
	minetest.show_formspec(pname, "at_il_routeedit_"..minetest.pos_to_string(sigd.p).."_"..sigd.s.."_"..routeid, form)

end


minetest.register_on_player_receive_fields(function(player, formname, fields)
	local pname = player:get_player_name()
	if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then
		return
	end
	
	local pts, connids, routeids = string.match(formname, "^at_il_routeedit_([^_]+)_(%d)_(%d+)$")
	local pos, connid, routeid
	if pts then
		pos = minetest.string_to_pos(pts)
		connid = tonumber(connids)
		routeid = tonumber(routeids)
		if not connid or connid<1 or connid>2 then return end
		if not routeid then return end
	end
	if pos and connid and routeid and not fields.quit then
		local sigd = {p=pos, s=connid}
		local tcbs = ildb.get_tcbs(sigd)
		if not tcbs then return end
		local route = tcbs.routes[routeid]
		if not route then return end
		
		if fields.setname and fields.name then
			route.name = fields.name
		end
		
		if fields.aspect then
			local suppasp = advtrains.interlocking.signal_get_supported_aspects(tcbs.signal)
			
			local callback = function(pname, asp)
				route.aspect = asp
				advtrains.interlocking.show_route_edit_form(pname, sigd, routeid)
			end
			
			advtrains.interlocking.show_signal_aspect_selector(pname, suppasp, route.name, callback, route.aspect or advtrains.interlocking.GENERIC_FREE)
			return
		end
		if fields.delete then
			-- if something set the route in the meantime, make sure this doesn't break.
			atil.route.update_route(sigd, tcbs, nil, true)
			table.remove(tcbs.routes, routeid)
			advtrains.interlocking.show_signalling_form(sigd, pname)
		end
		
		if fields.ars and fields.savears then
			route.ars = atil.text_to_ars(fields.ars)
			--atdebug(route.ars)
		end
		
		if fields.back then
			advtrains.interlocking.show_signalling_form(sigd, pname)
		end
		
	end
end)
: m_to_delete) { const char *tname = texture_to_delete.c_str(); video::ITexture *texture = m_driver->getTexture(tname); m_driver->removeTexture(texture); } } /******************************************************************************/ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id) { if (id) *id = 0; if (name.empty()) return NULL; m_to_delete.insert(name); #if ENABLE_GLES video::ITexture *retval = m_driver->findTexture(name.c_str()); if (retval) return retval; video::IImage *image = m_driver->createImageFromFile(name.c_str()); if (!image) return NULL; image = Align2Npot2(image, m_driver); retval = m_driver->addTexture(name.c_str(), image); image->drop(); return retval; #else return m_driver->getTexture(name.c_str()); #endif } /******************************************************************************/ /** MenuMusicFetcher */ /******************************************************************************/ void MenuMusicFetcher::fetchSounds(const std::string &name, std::set<std::string> &dst_paths, std::set<std::string> &dst_datas) { if(m_fetched.count(name)) return; m_fetched.insert(name); std::string base; base = porting::path_share + DIR_DELIM + "sounds"; dst_paths.insert(base + DIR_DELIM + name + ".ogg"); int i; for(i=0; i<10; i++) dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg"); base = porting::path_user + DIR_DELIM + "sounds"; dst_paths.insert(base + DIR_DELIM + name + ".ogg"); for(i=0; i<10; i++) dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg"); } /******************************************************************************/ /** GUIEngine */ /******************************************************************************/ GUIEngine::GUIEngine(JoystickController *joystick, gui::IGUIElement *parent, IMenuManager *menumgr, MainMenuData *data, bool &kill) : m_parent(parent), m_menumanager(menumgr), m_smgr(RenderingEngine::get_scene_manager()), m_data(data), m_kill(kill) { //initialize texture pointers for (image_definition &texture : m_textures) { texture.texture = NULL; } // is deleted by guiformspec! m_buttonhandler = new TextDestGuiEngine(this); //create texture source m_texture_source = new MenuTextureSource(RenderingEngine::get_video_driver()); //create soundmanager MenuMusicFetcher soundfetcher; #if USE_SOUND if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) m_sound_manager = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher); #endif if (!m_sound_manager) m_sound_manager = &dummySoundManager; //create topleft header m_toplefttext = L""; core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()), g_fontengine->getTextHeight()); rect += v2s32(4, 0); m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(), m_toplefttext, rect, false, true, 0, -1); //create formspecsource m_formspecgui = new FormspecFormSource(""); /* Create menu */ m_menu = new GUIFormSpecMenu(joystick, m_parent, -1, m_menumanager, NULL /* &client */, m_texture_source, m_formspecgui, m_buttonhandler, "", false); m_menu->allowClose(false); m_menu->lockSize(true,v2u32(800,600)); // Initialize scripting infostream << "GUIEngine: Initializing Lua" << std::endl; m_script = new MainMenuScripting(this); try { m_script->setMainMenuData(&m_data->script_data); m_data->script_data.errormessage = ""; if (!loadMainMenuScript()) { errorstream << "No future without main menu!" << std::endl; abort(); } run(); } catch (LuaError &e) { errorstream << "Main menu error: " << e.what() << std::endl; m_data->script_data.errormessage = e.what(); } m_menu->quitMenu(); m_menu->drop(); m_menu = NULL; } /******************************************************************************/ bool GUIEngine::loadMainMenuScript() { // Set main menu path (for core.get_mainmenu_path()) m_scriptdir = g_settings->get("main_menu_path"); if (m_scriptdir.empty()) { m_scriptdir = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM + "mainmenu"; } // Load builtin (which will load the main menu script) std::string script = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "init.lua"; try { m_script->loadScript(script); // Menu script loaded return true; } catch (const ModError &e) { errorstream << "GUIEngine: execution of menu script failed: " << e.what() << std::endl; } return false; } /******************************************************************************/ void GUIEngine::run() { // Always create clouds because they may or may not be // needed based on the game selected video::IVideoDriver *driver = RenderingEngine::get_video_driver(); cloudInit(); unsigned int text_height = g_fontengine->getTextHeight(); irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"), g_settings->getU16("screen_h")); static const video::SColor sky_color(255, 140, 186, 250); // Reset fog color { video::SColor fog_color; video::E_FOG_TYPE fog_type = video::EFT_FOG_LINEAR; f32 fog_start = 0; f32 fog_end = 0; f32 fog_density = 0; bool fog_pixelfog = false; bool fog_rangefog = false; driver->getFog(fog_color, fog_type, fog_start, fog_end, fog_density, fog_pixelfog, fog_rangefog); driver->setFog(sky_color, fog_type, fog_start, fog_end, fog_density, fog_pixelfog, fog_rangefog); } while (RenderingEngine::run() && (!m_startgame) && (!m_kill)) { const irr::core::dimension2d<u32> &current_screen_size = RenderingEngine::get_video_driver()->getScreenSize(); // Verify if window size has changed and save it if it's the case // Ensure evaluating settings->getBool after verifying screensize // First condition is cheaper if (previous_screen_size != current_screen_size && current_screen_size != irr::core::dimension2d<u32>(0,0) && g_settings->getBool("autosave_screensize")) { g_settings->setU16("screen_w", current_screen_size.Width); g_settings->setU16("screen_h", current_screen_size.Height); previous_screen_size = current_screen_size; } //check if we need to update the "upper left corner"-text if (text_height != g_fontengine->getTextHeight()) { updateTopLeftTextSize(); text_height = g_fontengine->getTextHeight(); } driver->beginScene(true, true, sky_color); if (m_clouds_enabled) { cloudPreProcess(); drawOverlay(driver); } else drawBackground(driver); drawHeader(driver); drawFooter(driver); RenderingEngine::get_gui_env()->drawAll(); driver->endScene(); if (m_clouds_enabled) cloudPostProcess(); else sleep_ms(25); m_script->step(); #ifdef __ANDROID__ m_menu->getAndroidUIInput(); #endif } } /******************************************************************************/ GUIEngine::~GUIEngine() { if (m_sound_manager != &dummySoundManager){ delete m_sound_manager; m_sound_manager = NULL; } infostream<<"GUIEngine: Deinitializing scripting"<<std::endl; delete m_script; m_irr_toplefttext->setText(L""); //clean up texture pointers for (image_definition &texture : m_textures) { if (texture.texture) RenderingEngine::get_video_driver()->removeTexture(texture.texture); } delete m_texture_source; if (m_cloud.clouds) m_cloud.clouds->drop(); } /******************************************************************************/ void GUIEngine::cloudInit() { m_cloud.clouds = new Clouds(m_smgr, -1, rand()); m_cloud.clouds->setHeight(100.0f); m_cloud.clouds->update(v3f(0, 0, 0), video::SColor(255,240,240,255)); m_cloud.camera = m_smgr->addCameraSceneNode(0, v3f(0,0,0), v3f(0, 60, 100)); m_cloud.camera->setFarValue(10000); m_cloud.lasttime = RenderingEngine::get_timer_time(); } /******************************************************************************/ void GUIEngine::cloudPreProcess() { u32 time = RenderingEngine::get_timer_time(); if(time > m_cloud.lasttime) m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0; else m_cloud.dtime = 0; m_cloud.lasttime = time; m_cloud.clouds->step(m_cloud.dtime*3); m_cloud.clouds->render(); m_smgr->drawAll(); } /******************************************************************************/ void GUIEngine::cloudPostProcess() { float fps_max = g_settings->getFloat("pause_fps_max"); // Time of frame without fps limit u32 busytime_u32; // not using getRealTime is necessary for wine u32 time = RenderingEngine::get_timer_time(); if(time > m_cloud.lasttime) busytime_u32 = time - m_cloud.lasttime; else busytime_u32 = 0; // FPS limiter u32 frametime_min = 1000./fps_max; if (busytime_u32 < frametime_min) { u32 sleeptime = frametime_min - busytime_u32; RenderingEngine::get_raw_device()->sleep(sleeptime); } } /******************************************************************************/ void GUIEngine::setFormspecPrepend(const std::string &fs) { if (m_menu) { m_menu->setFormspecPrepend(fs); } } /******************************************************************************/ void GUIEngine::drawBackground(video::IVideoDriver *driver) { v2u32 screensize = driver->getScreenSize(); video::ITexture* texture = m_textures[TEX_LAYER_BACKGROUND].texture; /* If no texture, draw background of solid color */ if(!texture){ video::SColor color(255,80,58,37); core::rect<s32> rect(0, 0, screensize.X, screensize.Y); driver->draw2DRectangle(color, rect, NULL); return; } v2u32 sourcesize = texture->getOriginalSize(); if (m_textures[TEX_LAYER_BACKGROUND].tile) { v2u32 tilesize( MYMAX(sourcesize.X,m_textures[TEX_LAYER_BACKGROUND].minsize), MYMAX(sourcesize.Y,m_textures[TEX_LAYER_BACKGROUND].minsize)); for (unsigned int x = 0; x < screensize.X; x += tilesize.X ) { for (unsigned int y = 0; y < screensize.Y; y += tilesize.Y ) { draw2DImageFilterScaled(driver, texture, core::rect<s32>(x, y, x+tilesize.X, y+tilesize.Y), core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), NULL, NULL, true); } } return; } /* Draw background texture */ draw2DImageFilterScaled(driver, texture, core::rect<s32>(0, 0, screensize.X, screensize.Y), core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), NULL, NULL, true); } /******************************************************************************/ void GUIEngine::drawOverlay(video::IVideoDriver *driver) { v2u32 screensize = driver->getScreenSize(); video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY].texture; /* If no texture, draw nothing */ if(!texture) return; /* Draw background texture */ v2u32 sourcesize = texture->getOriginalSize(); draw2DImageFilterScaled(driver, texture, core::rect<s32>(0, 0, screensize.X, screensize.Y), core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), NULL, NULL, true); } /******************************************************************************/ void GUIEngine::drawHeader(video::IVideoDriver *driver) { core::dimension2d<u32> screensize = driver->getScreenSize(); video::ITexture* texture = m_textures[TEX_LAYER_HEADER].texture; /* If no texture, draw nothing */ if(!texture) return; f32 mult = (((f32)screensize.Width / 2.0)) / ((f32)texture->getOriginalSize().Width); v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult, ((f32)texture->getOriginalSize().Height) * mult); // Don't draw the header if there isn't enough room s32 free_space = (((s32)screensize.Height)-320)/2; if (free_space > splashsize.Y) { core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y); splashrect += v2s32((screensize.Width/2)-(splashsize.X/2), ((free_space/2)-splashsize.Y/2)+10); video::SColor bgcolor(255,50,50,50); draw2DImageFilterScaled(driver, texture, splashrect, core::rect<s32>(core::position2d<s32>(0,0), core::dimension2di(texture->getOriginalSize())), NULL, NULL, true); } } /******************************************************************************/ void GUIEngine::drawFooter(video::IVideoDriver *driver) { core::dimension2d<u32> screensize = driver->getScreenSize(); video::ITexture* texture = m_textures[TEX_LAYER_FOOTER].texture; /* If no texture, draw nothing */ if(!texture) return; f32 mult = (((f32)screensize.Width)) / ((f32)texture->getOriginalSize().Width); v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult, ((f32)texture->getOriginalSize().Height) * mult); // Don't draw the footer if there isn't enough room s32 free_space = (((s32)screensize.Height)-320)/2; if (free_space > footersize.Y) { core::rect<s32> rect(0,0,footersize.X,footersize.Y); rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y); rect -= v2s32(footersize.X/2, 0); draw2DImageFilterScaled(driver, texture, rect, core::rect<s32>(core::position2d<s32>(0,0), core::dimension2di(texture->getOriginalSize())), NULL, NULL, true); } } /******************************************************************************/ bool GUIEngine::setTexture(texture_layer layer, const std::string &texturepath, bool tile_image, unsigned int minsize) { video::IVideoDriver *driver = RenderingEngine::get_video_driver(); if (m_textures[layer].texture) { driver->removeTexture(m_textures[layer].texture); m_textures[layer].texture = NULL; } if (texturepath.empty() || !fs::PathExists(texturepath)) { return false; } m_textures[layer].texture = driver->getTexture(texturepath.c_str()); m_textures[layer].tile = tile_image; m_textures[layer].minsize = minsize; if (!m_textures[layer].texture) { return false; } return true; } /******************************************************************************/ bool GUIEngine::downloadFile(const std::string &url, const std::string &target) { #if USE_CURL std::ofstream target_file(target.c_str(), std::ios::out | std::ios::binary); if (!target_file.good()) { return false; } HTTPFetchRequest fetch_request; HTTPFetchResult fetch_result; fetch_request.url = url; fetch_request.caller = HTTPFETCH_SYNC; fetch_request.timeout = g_settings->getS32("curl_file_download_timeout"); httpfetch_sync(fetch_request, fetch_result); if (!fetch_result.succeeded) { target_file.close(); fs::DeleteSingleFileOrEmptyDirectory(target); return false; } target_file << fetch_result.data; return true; #else return false; #endif } /******************************************************************************/ void GUIEngine::setTopleftText(const std::string &text) { m_toplefttext = translate_string(utf8_to_wide(text)); updateTopLeftTextSize(); } /******************************************************************************/ void GUIEngine::updateTopLeftTextSize() { core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()), g_fontengine->getTextHeight()); rect += v2s32(4, 0); m_irr_toplefttext->remove(); m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(), m_toplefttext, rect, false, true, 0, -1); } /******************************************************************************/ s32 GUIEngine::playSound(const SimpleSoundSpec &spec, bool looped) { s32 handle = m_sound_manager->playSound(spec, looped); return handle; } /******************************************************************************/ void GUIEngine::stopSound(s32 handle) { m_sound_manager->stopSound(handle); } /******************************************************************************/ unsigned int GUIEngine::queueAsync(const std::string &serialized_func, const std::string &serialized_params) { return m_script->queueAsync(serialized_func, serialized_params); }