mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Replace C++ mainmenu by formspec powered one
This commit is contained in:
parent
fe4ce03d52
commit
967121a34b
37 changed files with 8058 additions and 3949 deletions
463
src/main.cpp
463
src/main.cpp
|
@ -78,6 +78,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "serverlist.h"
|
||||
#include "sound.h"
|
||||
#include "sound_openal.h"
|
||||
#include "guiEngine.h"
|
||||
|
||||
/*
|
||||
Settings.
|
||||
|
@ -660,180 +661,6 @@ private:
|
|||
bool rightreleased;
|
||||
};
|
||||
|
||||
struct MenuTextures
|
||||
{
|
||||
std::string current_gameid;
|
||||
bool global_textures;
|
||||
video::ITexture *background;
|
||||
video::ITexture *overlay;
|
||||
video::ITexture *header;
|
||||
video::ITexture *footer;
|
||||
|
||||
MenuTextures():
|
||||
global_textures(false),
|
||||
background(NULL),
|
||||
overlay(NULL),
|
||||
header(NULL),
|
||||
footer(NULL)
|
||||
{}
|
||||
|
||||
static video::ITexture* getMenuTexture(const std::string &tname,
|
||||
video::IVideoDriver* driver, const SubgameSpec *spec)
|
||||
{
|
||||
if(spec){
|
||||
std::string path;
|
||||
// eg. minetest_menu_background.png (for texture packs)
|
||||
std::string pack_tname = spec->id + "_menu_" + tname + ".png";
|
||||
path = getTexturePath(pack_tname);
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
// eg. games/minetest_game/menu/background.png
|
||||
path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
} else {
|
||||
std::string path;
|
||||
// eg. menu_background.png
|
||||
std::string pack_tname = "menu_" + tname + ".png";
|
||||
path = getTexturePath(pack_tname);
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
|
||||
{
|
||||
if(tab == TAB_SINGLEPLAYER){
|
||||
if(spec->id == current_gameid)
|
||||
return;
|
||||
current_gameid = spec->id;
|
||||
global_textures = false;
|
||||
background = getMenuTexture("background", driver, spec);
|
||||
overlay = getMenuTexture("overlay", driver, spec);
|
||||
header = getMenuTexture("header", driver, spec);
|
||||
footer = getMenuTexture("footer", driver, spec);
|
||||
} else {
|
||||
if(global_textures)
|
||||
return;
|
||||
current_gameid = "";
|
||||
global_textures = true;
|
||||
background = getMenuTexture("background", driver, NULL);
|
||||
overlay = getMenuTexture("overlay", driver, NULL);
|
||||
header = getMenuTexture("header", driver, NULL);
|
||||
footer = getMenuTexture("footer", driver, NULL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.background;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Draw background texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
|
||||
void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.overlay;
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
/* Draw overlay texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
|
||||
void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.header;
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
f32 mult = (((f32)screensize.Width / 2)) /
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the header is 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);
|
||||
|
||||
driver->draw2DImage(texture, splashrect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.footer;
|
||||
|
||||
/* 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);
|
||||
|
||||
driver->draw2DImage(texture, rect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
|
||||
{
|
||||
for(size_t i=0; i<menudata.games.size(); i++){
|
||||
if(menudata.games[i].id == menudata.selected_game){
|
||||
return &menudata.games[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // !SERVER
|
||||
|
||||
// These are defined global so that they're not optimized too much.
|
||||
|
@ -1730,83 +1557,37 @@ int main(int argc, char *argv[])
|
|||
|
||||
// Initialize menu data
|
||||
MainMenuData menudata;
|
||||
if(g_settings->exists("selected_mainmenu_tab"))
|
||||
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
|
||||
if(g_settings->exists("selected_serverlist"))
|
||||
menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
|
||||
if(g_settings->exists("selected_mainmenu_game")){
|
||||
menudata.selected_game = g_settings->get("selected_mainmenu_game");
|
||||
menudata.selected_game_name = findSubgame(menudata.selected_game).name;
|
||||
}
|
||||
menudata.address = narrow_to_wide(address);
|
||||
menudata.name = narrow_to_wide(playername);
|
||||
menudata.port = narrow_to_wide(itos(port));
|
||||
menudata.kill = kill;
|
||||
menudata.address = address;
|
||||
menudata.name = playername;
|
||||
menudata.port = itos(port);
|
||||
menudata.errormessage = wide_to_narrow(error_message);
|
||||
error_message = L"";
|
||||
if(cmd_args.exists("password"))
|
||||
menudata.password = narrow_to_wide(cmd_args.get("password"));
|
||||
menudata.fancy_trees = g_settings->getBool("new_style_leaves");
|
||||
menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
|
||||
menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
|
||||
menudata.opaque_water = g_settings->getBool("opaque_water");
|
||||
menudata.mip_map = g_settings->getBool("mip_map");
|
||||
menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
|
||||
menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
|
||||
menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
|
||||
menudata.enable_shaders = g_settings->getS32("enable_shaders");
|
||||
menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
|
||||
menudata.enable_particles = g_settings->getBool("enable_particles");
|
||||
menudata.liquid_finite = g_settings->getBool("liquid_finite");
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
|
||||
menudata.creative_mode = g_settings->getBool("creative_mode");
|
||||
menudata.enable_damage = g_settings->getBool("enable_damage");
|
||||
menudata.password = cmd_args.get("password");
|
||||
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
|
||||
|
||||
menudata.enable_public = g_settings->getBool("server_announce");
|
||||
// Default to selecting nothing
|
||||
menudata.selected_world = -1;
|
||||
// Get world listing for the menu
|
||||
|
||||
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
|
||||
// If there is only one world, select it
|
||||
if(worldspecs.size() == 1){
|
||||
menudata.selected_world = 0;
|
||||
}
|
||||
// Otherwise try to select according to selected_world_path
|
||||
else if(g_settings->exists("selected_world_path")){
|
||||
std::string trypath = g_settings->get("selected_world_path");
|
||||
for(u32 i=0; i<worldspecs.size(); i++){
|
||||
if(worldspecs[i].path == trypath){
|
||||
menudata.selected_world = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a world was commanded, append and select it
|
||||
if(commanded_world != ""){
|
||||
|
||||
std::string gameid = getWorldGameId(commanded_world, true);
|
||||
std::string name = _("[--world parameter]");
|
||||
if(gameid == ""){
|
||||
gameid = g_settings->get("default_game");
|
||||
name += " [new]";
|
||||
}
|
||||
WorldSpec spec(commanded_world, name, gameid);
|
||||
worldspecs.push_back(spec);
|
||||
menudata.selected_world = worldspecs.size()-1;
|
||||
//TODO find within worldspecs and set config
|
||||
}
|
||||
// Copy worldspecs to menu
|
||||
menudata.worlds = worldspecs;
|
||||
// Get game listing
|
||||
menudata.games = getAvailableGames();
|
||||
// If selected game doesn't exist, take first from list
|
||||
if(findSubgame(menudata.selected_game).id == "" &&
|
||||
!menudata.games.empty()){
|
||||
menudata.selected_game = menudata.games[0].id;
|
||||
}
|
||||
const SubgameSpec *menugame = getMenuGame(menudata);
|
||||
|
||||
MenuTextures menutextures;
|
||||
menutextures.update(driver, menugame, menudata.selected_tab);
|
||||
|
||||
if(skip_main_menu == false)
|
||||
{
|
||||
video::IVideoDriver* driver = device->getVideoDriver();
|
||||
float fps_max = g_settings->getFloat("fps_max");
|
||||
|
||||
infostream<<"Waiting for other menus"<<std::endl;
|
||||
while(device->run() && kill == false)
|
||||
{
|
||||
|
@ -1814,7 +1595,6 @@ int main(int argc, char *argv[])
|
|||
break;
|
||||
driver->beginScene(true, true,
|
||||
video::SColor(255,128,128,128));
|
||||
drawMenuBackground(driver, menutextures);
|
||||
guienv->drawAll();
|
||||
driver->endScene();
|
||||
// On some computers framerate doesn't seem to be
|
||||
|
@ -1823,170 +1603,40 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
infostream<<"Waited for other menus"<<std::endl;
|
||||
|
||||
GUIMainMenu *menu =
|
||||
new GUIMainMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, &menudata, g_gamecallback);
|
||||
menu->allowFocusRemoval(true);
|
||||
GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata);
|
||||
|
||||
delete temp;
|
||||
//once finished you'll never end up here
|
||||
smgr->clear();
|
||||
kill = menudata.kill;
|
||||
|
||||
if(error_message != L"")
|
||||
{
|
||||
verbosestream<<"error_message = "
|
||||
<<wide_to_narrow(error_message)<<std::endl;
|
||||
|
||||
GUIMessageMenu *menu2 =
|
||||
new GUIMessageMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, error_message.c_str());
|
||||
menu2->drop();
|
||||
error_message = L"";
|
||||
}
|
||||
|
||||
// Time is in milliseconds, for clouds
|
||||
u32 lasttime = device->getTimer()->getTime();
|
||||
|
||||
MenuMusicFetcher soundfetcher;
|
||||
ISoundManager *sound = NULL;
|
||||
#if USE_SOUND
|
||||
sound = createOpenALSoundManager(&soundfetcher);
|
||||
#endif
|
||||
if(!sound)
|
||||
sound = &dummySoundManager;
|
||||
SimpleSoundSpec spec;
|
||||
spec.name = "main_menu";
|
||||
spec.gain = 1;
|
||||
s32 handle = sound->playSound(spec, true);
|
||||
|
||||
infostream<<"Created main menu"<<std::endl;
|
||||
|
||||
while(device->run() && kill == false)
|
||||
{
|
||||
if(menu->getStatus() == true)
|
||||
break;
|
||||
|
||||
// Game can be selected in the menu
|
||||
menugame = getMenuGame(menudata);
|
||||
menutextures.update(driver, menugame, menu->getTab());
|
||||
// Clouds for the main menu
|
||||
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||
if(menugame){
|
||||
// If game has regular background and no overlay, don't use clouds
|
||||
if(cloud_menu_background && menutextures.background &&
|
||||
!menutextures.overlay){
|
||||
cloud_menu_background = false;
|
||||
}
|
||||
// If game game has overlay and no regular background, always draw clouds
|
||||
else if(menutextures.overlay && !menutextures.background){
|
||||
cloud_menu_background = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Time calc for the clouds
|
||||
f32 dtime=0; // in seconds
|
||||
if (cloud_menu_background) {
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
dtime = (time - lasttime) / 1000.0;
|
||||
else
|
||||
dtime = 0;
|
||||
lasttime = time;
|
||||
}
|
||||
|
||||
//driver->beginScene(true, true, video::SColor(255,0,0,0));
|
||||
driver->beginScene(true, true, video::SColor(255,140,186,250));
|
||||
|
||||
if (cloud_menu_background) {
|
||||
// *3 otherwise the clouds would move very slowly
|
||||
g_menuclouds->step(dtime*3);
|
||||
g_menuclouds->render();
|
||||
g_menucloudsmgr->drawAll();
|
||||
drawMenuOverlay(driver, menutextures);
|
||||
drawMenuHeader(driver, menutextures);
|
||||
drawMenuFooter(driver, menutextures);
|
||||
} else {
|
||||
drawMenuBackground(driver, menutextures);
|
||||
drawMenuHeader(driver, menutextures);
|
||||
drawMenuFooter(driver, menutextures);
|
||||
}
|
||||
|
||||
guienv->drawAll();
|
||||
|
||||
driver->endScene();
|
||||
|
||||
// On some computers framerate doesn't seem to be
|
||||
// automatically limited
|
||||
if (cloud_menu_background) {
|
||||
// Time of frame without fps limit
|
||||
float busytime;
|
||||
u32 busytime_u32;
|
||||
// not using getRealTime is necessary for wine
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
busytime_u32 = time - lasttime;
|
||||
else
|
||||
busytime_u32 = 0;
|
||||
busytime = busytime_u32 / 1000.0;
|
||||
|
||||
// FPS limiter
|
||||
u32 frametime_min = 1000./fps_max;
|
||||
|
||||
if(busytime_u32 < frametime_min) {
|
||||
u32 sleeptime = frametime_min - busytime_u32;
|
||||
device->sleep(sleeptime);
|
||||
}
|
||||
} else {
|
||||
sleep_ms(25);
|
||||
}
|
||||
}
|
||||
sound->stopSound(handle);
|
||||
if(sound != &dummySoundManager){
|
||||
delete sound;
|
||||
sound = NULL;
|
||||
}
|
||||
|
||||
// Save controls status
|
||||
menu->readInput(&menudata);
|
||||
|
||||
infostream<<"Dropping main menu"<<std::endl;
|
||||
|
||||
menu->drop();
|
||||
}
|
||||
|
||||
playername = wide_to_narrow(menudata.name);
|
||||
if (playername == "")
|
||||
playername = std::string("Guest") + itos(myrand_range(1000,9999));
|
||||
password = translatePassword(playername, menudata.password);
|
||||
//update worldspecs (necessary as new world may have been created)
|
||||
worldspecs = getAvailableWorlds();
|
||||
|
||||
if (menudata.name == "")
|
||||
menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
|
||||
else
|
||||
playername = menudata.name;
|
||||
|
||||
password = translatePassword(playername, narrow_to_wide(menudata.password));
|
||||
//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
|
||||
|
||||
address = wide_to_narrow(menudata.address);
|
||||
int newport = stoi(wide_to_narrow(menudata.port));
|
||||
address = menudata.address;
|
||||
int newport = stoi(menudata.port);
|
||||
if(newport != 0)
|
||||
port = newport;
|
||||
|
||||
simple_singleplayer_mode = menudata.simple_singleplayer_mode;
|
||||
|
||||
// Save settings
|
||||
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
|
||||
g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
|
||||
g_settings->set("selected_mainmenu_game", menudata.selected_game);
|
||||
g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
|
||||
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
|
||||
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
|
||||
g_settings->set("opaque_water", itos(menudata.opaque_water));
|
||||
|
||||
g_settings->set("mip_map", itos(menudata.mip_map));
|
||||
g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
|
||||
g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
|
||||
g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
|
||||
|
||||
g_settings->setS32("enable_shaders", menudata.enable_shaders);
|
||||
g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
|
||||
g_settings->set("enable_particles", itos(menudata.enable_particles));
|
||||
g_settings->set("liquid_finite", itos(menudata.liquid_finite));
|
||||
|
||||
g_settings->set("creative_mode", itos(menudata.creative_mode));
|
||||
g_settings->set("enable_damage", itos(menudata.enable_damage));
|
||||
g_settings->set("server_announce", itos(menudata.enable_public));
|
||||
g_settings->set("name", playername);
|
||||
g_settings->set("address", address);
|
||||
g_settings->set("port", itos(port));
|
||||
if(menudata.selected_world != -1)
|
||||
|
||||
if((menudata.selected_world >= 0) &&
|
||||
(menudata.selected_world < worldspecs.size()))
|
||||
g_settings->set("selected_world_path",
|
||||
worldspecs[menudata.selected_world].path);
|
||||
|
||||
|
@ -2010,8 +1660,8 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
ServerListSpec server;
|
||||
server["name"] = menudata.servername;
|
||||
server["address"] = wide_to_narrow(menudata.address);
|
||||
server["port"] = wide_to_narrow(menudata.port);
|
||||
server["address"] = menudata.address;
|
||||
server["port"] = menudata.port;
|
||||
server["description"] = menudata.serverdescription;
|
||||
ServerList::insert(server);
|
||||
}
|
||||
|
@ -2022,30 +1672,7 @@ int main(int argc, char *argv[])
|
|||
infostream<<"Selected world: "<<worldspec.name
|
||||
<<" ["<<worldspec.path<<"]"<<std::endl;
|
||||
}
|
||||
|
||||
// Only refresh if so requested
|
||||
if(menudata.only_refresh){
|
||||
infostream<<"Refreshing menu"<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new world if requested
|
||||
if(menudata.create_world_name != L"")
|
||||
{
|
||||
std::string path = porting::path_user + DIR_DELIM
|
||||
"worlds" + DIR_DELIM
|
||||
+ wide_to_narrow(menudata.create_world_name);
|
||||
// Create world if it doesn't exist
|
||||
if(!initializeWorld(path, menudata.create_world_gameid)){
|
||||
error_message = wgettext("Failed to initialize world");
|
||||
errorstream<<wide_to_narrow(error_message)<<std::endl;
|
||||
continue;
|
||||
}
|
||||
g_settings->set("selected_world_path", path);
|
||||
g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If local game
|
||||
if(current_address == "")
|
||||
{
|
||||
|
@ -2085,8 +1712,10 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
// Break out of menu-game loop to shut down cleanly
|
||||
if(device->run() == false || kill == true)
|
||||
if(device->run() == false || kill == true) {
|
||||
g_settings->updateConfigFile(configpath.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Run game
|
||||
|
@ -2138,11 +1767,11 @@ int main(int argc, char *argv[])
|
|||
break;
|
||||
}
|
||||
} // Menu-game loop
|
||||
|
||||
|
||||
|
||||
|
||||
g_menuclouds->drop();
|
||||
g_menucloudsmgr->drop();
|
||||
|
||||
|
||||
delete input;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue