mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 01:53:45 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
e0564d5de0
@ -143,6 +143,7 @@ if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE})
|
|||||||
install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
|
install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
|
||||||
install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/")
|
install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/")
|
||||||
install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game")
|
install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game")
|
||||||
|
install(DIRECTORY ${MINETEST_GAME_SOURCE}/menu DESTINATION "${SHAREDIR}/games/minetest_game")
|
||||||
endif()
|
endif()
|
||||||
set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build")
|
set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build")
|
||||||
if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE})
|
if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE})
|
||||||
|
20
README.txt
20
README.txt
@ -30,13 +30,17 @@ This game is not finished
|
|||||||
|
|
||||||
Default Controls
|
Default Controls
|
||||||
-----------------
|
-----------------
|
||||||
- WASD: Move
|
- WASD: move
|
||||||
- Space: Jump
|
- Space: jump/climb
|
||||||
- E: Go down
|
- Shift: sneak/go down
|
||||||
- Shift: Sneak
|
- Q: drop item
|
||||||
- Q: Drop item
|
- I: inventory
|
||||||
- I: Open inventory
|
- Mouse: turn/look
|
||||||
- Mouse: Turn/look
|
- Mouse left: dig/punch
|
||||||
|
- Mouse right: place/use
|
||||||
|
- Mouse wheel: select item
|
||||||
|
- Esc: pause menu
|
||||||
|
- T: chat
|
||||||
- Settable in the configuration file, see the section below.
|
- Settable in the configuration file, see the section below.
|
||||||
|
|
||||||
Paths
|
Paths
|
||||||
@ -277,7 +281,7 @@ the Free Software Foundation; either version 2.1 of the License, or
|
|||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
-- Minetest: builtin/features.lua
|
-- Minetest: builtin/features.lua
|
||||||
|
|
||||||
minetest.features = {
|
minetest.features = {
|
||||||
"glasslike_framed" = true,
|
glasslike_framed = true,
|
||||||
"nodebox_as_selectionbox" = true,
|
nodebox_as_selectionbox = true,
|
||||||
"chat_send_player_param3" = true,
|
chat_send_player_param3 = true,
|
||||||
"get_all_craft_recipes_works" = true,
|
get_all_craft_recipes_works = true,
|
||||||
"use_texture_alpha" = true,
|
use_texture_alpha = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function minetest.has_feature(arg)
|
function minetest.has_feature(arg)
|
||||||
|
@ -120,6 +120,17 @@ depends.txt:
|
|||||||
List of mods that have to be loaded before loading this mod.
|
List of mods that have to be loaded before loading this mod.
|
||||||
A single line contains a single modname.
|
A single line contains a single modname.
|
||||||
|
|
||||||
|
Optional dependencies can be defined by appending a question mark
|
||||||
|
to a single modname. Their meaning is that if the specified mod
|
||||||
|
is missing, that does not prevent this mod from being loaded.
|
||||||
|
|
||||||
|
optdepends.txt:
|
||||||
|
An alternative way of specifying optional dependencies.
|
||||||
|
Like depends.txt, a single line contains a single modname.
|
||||||
|
|
||||||
|
NOTE: This file exists for compatibility purposes only and
|
||||||
|
support for it will be removed from the engine by the end of 2013.
|
||||||
|
|
||||||
init.lua:
|
init.lua:
|
||||||
The main Lua script. Running this script should register everything it
|
The main Lua script. Running this script should register everything it
|
||||||
wants to register. Subsequent execution depends on minetest calling the
|
wants to register. Subsequent execution depends on minetest calling the
|
||||||
|
@ -61,6 +61,9 @@ Run dedicated server
|
|||||||
\-\-speedtests
|
\-\-speedtests
|
||||||
Run speed tests
|
Run speed tests
|
||||||
.TP
|
.TP
|
||||||
|
\-\-videomodes
|
||||||
|
List available video modes
|
||||||
|
.TP
|
||||||
\-\-info
|
\-\-info
|
||||||
Print more information to console
|
Print more information to console
|
||||||
.TP
|
.TP
|
||||||
|
BIN
games/minimal/menu/background.png
Normal file
BIN
games/minimal/menu/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
games/minimal/menu/icon.png
Normal file
BIN
games/minimal/menu/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 397 B |
@ -151,6 +151,8 @@
|
|||||||
#crosshair_color = (255,255,255)
|
#crosshair_color = (255,255,255)
|
||||||
# Cross alpha (opaqueness, between 0 and 255)
|
# Cross alpha (opaqueness, between 0 and 255)
|
||||||
#crosshair_alpha = 255
|
#crosshair_alpha = 255
|
||||||
|
# Sensitivity multiplier
|
||||||
|
#mouse_sensitivity = 0.2
|
||||||
# Sound settings
|
# Sound settings
|
||||||
#enable_sound = true
|
#enable_sound = true
|
||||||
#sound_volume = 0.7
|
#sound_volume = 0.7
|
||||||
|
@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "mapblock.h"
|
#include "mapblock.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "profiler.h"
|
#include "profiler.h"
|
||||||
|
#include "gettext.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nodemetadata.h"
|
#include "nodemetadata.h"
|
||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
@ -2555,6 +2556,9 @@ void Client::inventoryAction(InventoryAction *a)
|
|||||||
Predict some local inventory changes
|
Predict some local inventory changes
|
||||||
*/
|
*/
|
||||||
a->clientApply(this, this);
|
a->clientApply(this, this);
|
||||||
|
|
||||||
|
// Remove it
|
||||||
|
delete a;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientActiveObject * Client::getSelectedActiveObject(
|
ClientActiveObject * Client::getSelectedActiveObject(
|
||||||
@ -2801,7 +2805,10 @@ ClientEvent Client::getClientEvent()
|
|||||||
return m_client_event_queue.pop_front();
|
return m_client_event_queue.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::afterContentReceived()
|
void draw_load_screen(const std::wstring &text,
|
||||||
|
IrrlichtDevice* device, gui::IGUIFont* font,
|
||||||
|
float dtime=0 ,int percent=0, bool clouds=true);
|
||||||
|
void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
|
||||||
{
|
{
|
||||||
infostream<<"Client::afterContentReceived() started"<<std::endl;
|
infostream<<"Client::afterContentReceived() started"<<std::endl;
|
||||||
assert(m_itemdef_received);
|
assert(m_itemdef_received);
|
||||||
@ -2836,13 +2843,23 @@ void Client::afterContentReceived()
|
|||||||
if(g_settings->getBool("preload_item_visuals"))
|
if(g_settings->getBool("preload_item_visuals"))
|
||||||
{
|
{
|
||||||
verbosestream<<"Updating item textures and meshes"<<std::endl;
|
verbosestream<<"Updating item textures and meshes"<<std::endl;
|
||||||
|
wchar_t* text = wgettext("Item textures...");
|
||||||
|
draw_load_screen(text,device,font,0,0);
|
||||||
std::set<std::string> names = m_itemdef->getAll();
|
std::set<std::string> names = m_itemdef->getAll();
|
||||||
|
size_t size = names.size();
|
||||||
|
size_t count = 0;
|
||||||
|
int percent = 0;
|
||||||
for(std::set<std::string>::const_iterator
|
for(std::set<std::string>::const_iterator
|
||||||
i = names.begin(); i != names.end(); ++i){
|
i = names.begin(); i != names.end(); ++i){
|
||||||
// Asking for these caches the result
|
// Asking for these caches the result
|
||||||
m_itemdef->getInventoryTexture(*i, this);
|
m_itemdef->getInventoryTexture(*i, this);
|
||||||
m_itemdef->getWieldMesh(*i, this);
|
m_itemdef->getWieldMesh(*i, this);
|
||||||
|
count++;
|
||||||
|
percent = count*100/size;
|
||||||
|
if (count%50 == 0) // only update every 50 item
|
||||||
|
draw_load_screen(text,device,font,0,percent);
|
||||||
}
|
}
|
||||||
|
delete[] text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start mesh update thread after setting up content definitions
|
// Start mesh update thread after setting up content definitions
|
||||||
|
@ -385,7 +385,7 @@ public:
|
|||||||
bool nodedefReceived()
|
bool nodedefReceived()
|
||||||
{ return m_nodedef_received; }
|
{ return m_nodedef_received; }
|
||||||
|
|
||||||
void afterContentReceived();
|
void afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font);
|
||||||
|
|
||||||
float getRTT(void);
|
float getRTT(void);
|
||||||
|
|
||||||
|
@ -91,10 +91,10 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
|
|||||||
PROTOCOL_VERSION 19:
|
PROTOCOL_VERSION 19:
|
||||||
GENERIC_CMD_SET_PHYSICS_OVERRIDE
|
GENERIC_CMD_SET_PHYSICS_OVERRIDE
|
||||||
PROTOCOL_VERSION 20:
|
PROTOCOL_VERSION 20:
|
||||||
TOCLIENT_HUD_ADD
|
TOCLIENT_HUDADD
|
||||||
TOCLIENT_HUD_RM
|
TOCLIENT_HUDRM
|
||||||
TOCLIENT_HUD_CHANGE
|
TOCLIENT_HUDCHANGE
|
||||||
TOCLIENT_HUD_BUILTIN_ENABLE
|
TOCLIENT_HUD_SET_FLAGS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LATEST_PROTOCOL_VERSION 20
|
#define LATEST_PROTOCOL_VERSION 20
|
||||||
|
@ -117,6 +117,7 @@ void set_default_settings(Settings *settings)
|
|||||||
settings->setDefault("selectionbox_color", "(0,0,0)");
|
settings->setDefault("selectionbox_color", "(0,0,0)");
|
||||||
settings->setDefault("crosshair_color", "(255,255,255)");
|
settings->setDefault("crosshair_color", "(255,255,255)");
|
||||||
settings->setDefault("crosshair_alpha", "255");
|
settings->setDefault("crosshair_alpha", "255");
|
||||||
|
settings->setDefault("mouse_sensitivity", "0.2");
|
||||||
settings->setDefault("enable_sound", "true");
|
settings->setDefault("enable_sound", "true");
|
||||||
settings->setDefault("sound_volume", "0.8");
|
settings->setDefault("sound_volume", "0.8");
|
||||||
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
|
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
|
||||||
|
@ -1084,8 +1084,8 @@ void ServerEnvironment::step(float dtime)
|
|||||||
{
|
{
|
||||||
v3s16 p = *i;
|
v3s16 p = *i;
|
||||||
|
|
||||||
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
|
/* infostream<<"Server: Block " << PP(p)
|
||||||
<<") became inactive"<<std::endl;*/
|
<< " became inactive"<<std::endl; */
|
||||||
|
|
||||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||||
if(block==NULL)
|
if(block==NULL)
|
||||||
@ -1104,9 +1104,6 @@ void ServerEnvironment::step(float dtime)
|
|||||||
i != blocks_added.end(); ++i)
|
i != blocks_added.end(); ++i)
|
||||||
{
|
{
|
||||||
v3s16 p = *i;
|
v3s16 p = *i;
|
||||||
|
|
||||||
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
|
|
||||||
<<") became active"<<std::endl;*/
|
|
||||||
|
|
||||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||||
if(block==NULL){
|
if(block==NULL){
|
||||||
@ -1117,6 +1114,8 @@ void ServerEnvironment::step(float dtime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
activateBlock(block);
|
activateBlock(block);
|
||||||
|
/* infostream<<"Server: Block " << PP(p)
|
||||||
|
<< " became active"<<std::endl; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1850,17 +1849,17 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
|
|||||||
<<" Forcing delete."<<std::endl;
|
<<" Forcing delete."<<std::endl;
|
||||||
force_delete = true;
|
force_delete = true;
|
||||||
} else {
|
} else {
|
||||||
u16 new_id = pending_delete ? id : 0;
|
|
||||||
// If static counterpart already exists, remove it first.
|
// If static counterpart already exists, remove it first.
|
||||||
// This shouldn't happen, but happens rarely for some
|
// This shouldn't happen, but happens rarely for some
|
||||||
// unknown reason. Unsuccessful attempts have been made to
|
// unknown reason. Unsuccessful attempts have been made to
|
||||||
// find said reason.
|
// find said reason.
|
||||||
if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
|
if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
|
||||||
infostream<<"ServerEnv: WARNING: Performing hack #83274"
|
infostream<<"ServerEnv: WARNING: Performing hack #83274"
|
||||||
<<std::endl;
|
<<std::endl;
|
||||||
block->m_static_objects.remove(new_id);
|
block->m_static_objects.remove(id);
|
||||||
}
|
}
|
||||||
block->m_static_objects.insert(new_id, s_obj);
|
//store static data
|
||||||
|
block->m_static_objects.insert(0, s_obj);
|
||||||
|
|
||||||
// Only mark block as modified if data changed considerably
|
// Only mark block as modified if data changed considerably
|
||||||
if(shall_be_written)
|
if(shall_be_written)
|
||||||
|
310
src/game.cpp
310
src/game.cpp
@ -395,11 +395,14 @@ PointedThing getPointedThing(Client *client, v3f player_position,
|
|||||||
/*
|
/*
|
||||||
Draws a screen with a single text on it.
|
Draws a screen with a single text on it.
|
||||||
Text will be removed when the screen is drawn the next time.
|
Text will be removed when the screen is drawn the next time.
|
||||||
|
Additionally, a progressbar can be drawn when percent is set between 0 and 100.
|
||||||
*/
|
*/
|
||||||
/*gui::IGUIStaticText **/
|
/*gui::IGUIStaticText **/
|
||||||
void draw_load_screen(const std::wstring &text,
|
void draw_load_screen(const std::wstring &text,
|
||||||
video::IVideoDriver* driver, gui::IGUIFont* font)
|
IrrlichtDevice* device, gui::IGUIFont* font,
|
||||||
|
float dtime=0 ,int percent=0, bool clouds=true)
|
||||||
{
|
{
|
||||||
|
video::IVideoDriver* driver = device->getVideoDriver();
|
||||||
v2u32 screensize = driver->getScreenSize();
|
v2u32 screensize = driver->getScreenSize();
|
||||||
const wchar_t *loadingtext = text.c_str();
|
const wchar_t *loadingtext = text.c_str();
|
||||||
core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
|
core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
|
||||||
@ -411,7 +414,30 @@ void draw_load_screen(const std::wstring &text,
|
|||||||
loadingtext, textrect, false, false);
|
loadingtext, textrect, false, false);
|
||||||
guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
|
guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
|
||||||
|
|
||||||
driver->beginScene(true, true, video::SColor(255,0,0,0));
|
bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
|
||||||
|
if (cloud_menu_background)
|
||||||
|
{
|
||||||
|
g_menuclouds->step(dtime*3);
|
||||||
|
g_menuclouds->render();
|
||||||
|
driver->beginScene(true, true, video::SColor(255,140,186,250));
|
||||||
|
g_menucloudsmgr->drawAll();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
driver->beginScene(true, true, video::SColor(255,0,0,0));
|
||||||
|
if (percent >= 0 && percent <= 100) // draw progress bar
|
||||||
|
{
|
||||||
|
core::vector2d<s32> barsize(256,32);
|
||||||
|
core::rect<s32> barrect(center-barsize/2, center+barsize/2);
|
||||||
|
driver->draw2DRectangle(video::SColor(255,255,255,255),barrect, NULL); // border
|
||||||
|
driver->draw2DRectangle(video::SColor(255,64,64,64), core::rect<s32> (
|
||||||
|
barrect.UpperLeftCorner+1,
|
||||||
|
barrect.LowerRightCorner-1), NULL); // black inside the bar
|
||||||
|
driver->draw2DRectangle(video::SColor(255,128,128,128), core::rect<s32> (
|
||||||
|
barrect.UpperLeftCorner+1,
|
||||||
|
core::vector2d<s32>(
|
||||||
|
barrect.LowerRightCorner.X-(barsize.X-1)+percent*(barsize.X-2)/100,
|
||||||
|
barrect.LowerRightCorner.Y-1)), NULL); // the actual progress
|
||||||
|
}
|
||||||
guienv->drawAll();
|
guienv->drawAll();
|
||||||
driver->endScene();
|
driver->endScene();
|
||||||
|
|
||||||
@ -789,6 +815,67 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void nodePlacementPrediction(Client &client,
|
||||||
|
const ItemDefinition &playeritem_def,
|
||||||
|
v3s16 nodepos, v3s16 neighbourpos)
|
||||||
|
{
|
||||||
|
std::string prediction = playeritem_def.node_placement_prediction;
|
||||||
|
INodeDefManager *nodedef = client.ndef();
|
||||||
|
ClientMap &map = client.getEnv().getClientMap();
|
||||||
|
|
||||||
|
if(prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable)
|
||||||
|
{
|
||||||
|
verbosestream<<"Node placement prediction for "
|
||||||
|
<<playeritem_def.name<<" is "
|
||||||
|
<<prediction<<std::endl;
|
||||||
|
v3s16 p = neighbourpos;
|
||||||
|
// Place inside node itself if buildable_to
|
||||||
|
try{
|
||||||
|
MapNode n_under = map.getNode(nodepos);
|
||||||
|
if(nodedef->get(n_under).buildable_to)
|
||||||
|
p = nodepos;
|
||||||
|
else if (!nodedef->get(map.getNode(p)).buildable_to)
|
||||||
|
return;
|
||||||
|
}catch(InvalidPositionException &e){}
|
||||||
|
// Find id of predicted node
|
||||||
|
content_t id;
|
||||||
|
bool found = nodedef->getId(prediction, id);
|
||||||
|
if(!found){
|
||||||
|
errorstream<<"Node placement prediction failed for "
|
||||||
|
<<playeritem_def.name<<" (places "
|
||||||
|
<<prediction
|
||||||
|
<<") - Name not known"<<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Predict param2
|
||||||
|
u8 param2 = 0;
|
||||||
|
if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED){
|
||||||
|
v3s16 dir = nodepos - neighbourpos;
|
||||||
|
if(abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))){
|
||||||
|
param2 = dir.Y < 0 ? 1 : 0;
|
||||||
|
} else if(abs(dir.X) > abs(dir.Z)){
|
||||||
|
param2 = dir.X < 0 ? 3 : 2;
|
||||||
|
} else {
|
||||||
|
param2 = dir.Z < 0 ? 5 : 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Facedir prediction
|
||||||
|
// TODO: If predicted node is in attached_node group, check attachment
|
||||||
|
// Add node to client map
|
||||||
|
MapNode n(id, 0, param2);
|
||||||
|
try{
|
||||||
|
// This triggers the required mesh update too
|
||||||
|
client.addNode(p, n);
|
||||||
|
}catch(InvalidPositionException &e){
|
||||||
|
errorstream<<"Node placement prediction failed for "
|
||||||
|
<<playeritem_def.name<<" (places "
|
||||||
|
<<prediction
|
||||||
|
<<") - Position not loaded"<<std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void the_game(
|
void the_game(
|
||||||
bool &kill,
|
bool &kill,
|
||||||
bool random_input,
|
bool random_input,
|
||||||
@ -821,7 +908,11 @@ void the_game(
|
|||||||
Draw "Loading" screen
|
Draw "Loading" screen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
draw_load_screen(L"Loading...", driver, font);
|
{
|
||||||
|
wchar_t* text = wgettext("Loading...");
|
||||||
|
draw_load_screen(text, device, font,0,0);
|
||||||
|
delete[] text;
|
||||||
|
}
|
||||||
|
|
||||||
// Create texture source
|
// Create texture source
|
||||||
IWritableTextureSource *tsrc = createTextureSource(device);
|
IWritableTextureSource *tsrc = createTextureSource(device);
|
||||||
@ -878,7 +969,9 @@ void the_game(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if(address == ""){
|
if(address == ""){
|
||||||
draw_load_screen(L"Creating server...", driver, font);
|
wchar_t* text = wgettext("Creating server....");
|
||||||
|
draw_load_screen(text, device, font,0,25);
|
||||||
|
delete[] text;
|
||||||
infostream<<"Creating server"<<std::endl;
|
infostream<<"Creating server"<<std::endl;
|
||||||
server = new Server(map_dir, configpath, gamespec,
|
server = new Server(map_dir, configpath, gamespec,
|
||||||
simple_singleplayer_mode);
|
simple_singleplayer_mode);
|
||||||
@ -891,7 +984,11 @@ void the_game(
|
|||||||
Create client
|
Create client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
draw_load_screen(L"Creating client...", driver, font);
|
{
|
||||||
|
wchar_t* text = wgettext("Creating client...");
|
||||||
|
draw_load_screen(text, device, font,0,50);
|
||||||
|
delete[] text;
|
||||||
|
}
|
||||||
infostream<<"Creating client"<<std::endl;
|
infostream<<"Creating client"<<std::endl;
|
||||||
|
|
||||||
MapDrawControl draw_control;
|
MapDrawControl draw_control;
|
||||||
@ -901,8 +998,12 @@ void the_game(
|
|||||||
|
|
||||||
// Client acts as our GameDef
|
// Client acts as our GameDef
|
||||||
IGameDef *gamedef = &client;
|
IGameDef *gamedef = &client;
|
||||||
|
|
||||||
draw_load_screen(L"Resolving address...", driver, font);
|
{
|
||||||
|
wchar_t* text = wgettext("Resolving address...");
|
||||||
|
draw_load_screen(text, device, font,0,75);
|
||||||
|
delete[] text;
|
||||||
|
}
|
||||||
Address connect_address(0,0,0,0, port);
|
Address connect_address(0,0,0,0, port);
|
||||||
try{
|
try{
|
||||||
if(address == "")
|
if(address == "")
|
||||||
@ -934,15 +1035,26 @@ void the_game(
|
|||||||
bool could_connect = false;
|
bool could_connect = false;
|
||||||
bool connect_aborted = false;
|
bool connect_aborted = false;
|
||||||
try{
|
try{
|
||||||
float frametime = 0.033;
|
|
||||||
float time_counter = 0.0;
|
float time_counter = 0.0;
|
||||||
input->clear();
|
input->clear();
|
||||||
|
float fps_max = g_settings->getFloat("fps_max");
|
||||||
|
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||||
|
u32 lasttime = device->getTimer()->getTime();
|
||||||
while(device->run())
|
while(device->run())
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
// Update client and server
|
// Update client and server
|
||||||
client.step(frametime);
|
client.step(dtime);
|
||||||
if(server != NULL)
|
if(server != NULL)
|
||||||
server->step(frametime);
|
server->step(dtime);
|
||||||
|
|
||||||
// End condition
|
// End condition
|
||||||
if(client.connectedAndInitialized()){
|
if(client.connectedAndInitialized()){
|
||||||
@ -963,15 +1075,37 @@ void the_game(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display status
|
// Display status
|
||||||
std::wostringstream ss;
|
{
|
||||||
ss<<L"Connecting to server... (press Escape to cancel)\n";
|
wchar_t* text = wgettext("Connecting to server...");
|
||||||
std::wstring animation = L"/-\\|";
|
draw_load_screen(text, device, font, dtime, 100);
|
||||||
ss<<animation[(int)(time_counter/0.2)%4];
|
delete[] text;
|
||||||
draw_load_screen(ss.str(), driver, font);
|
}
|
||||||
|
|
||||||
// Delay a bit
|
// On some computers framerate doesn't seem to be
|
||||||
sleep_ms(1000*frametime);
|
// automatically limited
|
||||||
time_counter += frametime;
|
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);
|
||||||
|
}
|
||||||
|
time_counter += dtime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(con::PeerNotFoundException &e)
|
catch(con::PeerNotFoundException &e)
|
||||||
@ -995,15 +1129,26 @@ void the_game(
|
|||||||
bool got_content = false;
|
bool got_content = false;
|
||||||
bool content_aborted = false;
|
bool content_aborted = false;
|
||||||
{
|
{
|
||||||
float frametime = 0.033;
|
|
||||||
float time_counter = 0.0;
|
float time_counter = 0.0;
|
||||||
input->clear();
|
input->clear();
|
||||||
|
float fps_max = g_settings->getFloat("fps_max");
|
||||||
|
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||||
|
u32 lasttime = device->getTimer()->getTime();
|
||||||
while(device->run())
|
while(device->run())
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
// Update client and server
|
// Update client and server
|
||||||
client.step(frametime);
|
client.step(dtime);
|
||||||
if(server != NULL)
|
if(server != NULL)
|
||||||
server->step(frametime);
|
server->step(dtime);
|
||||||
|
|
||||||
// End condition
|
// End condition
|
||||||
if(client.texturesReceived() &&
|
if(client.texturesReceived() &&
|
||||||
@ -1025,21 +1170,52 @@ void the_game(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display status
|
// Display status
|
||||||
std::wostringstream ss;
|
std::ostringstream ss;
|
||||||
ss<<L"Waiting content... (press Escape to cancel)\n";
|
int progress=0;
|
||||||
|
if (!client.itemdefReceived())
|
||||||
ss<<(client.itemdefReceived()?L"[X]":L"[ ]");
|
{
|
||||||
ss<<L" Item definitions\n";
|
ss << "Item definitions...";
|
||||||
ss<<(client.nodedefReceived()?L"[X]":L"[ ]");
|
progress = 0;
|
||||||
ss<<L" Node definitions\n";
|
}
|
||||||
ss<<L"["<<(int)(client.mediaReceiveProgress()*100+0.5)<<L"%] ";
|
else if (!client.nodedefReceived())
|
||||||
ss<<L" Media\n";
|
{
|
||||||
|
ss << "Node definitions...";
|
||||||
draw_load_screen(ss.str(), driver, font);
|
progress = 25;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << "Media...";
|
||||||
|
progress = 50+client.mediaReceiveProgress()*50+0.5;
|
||||||
|
}
|
||||||
|
wchar_t* text = wgettext(ss.str().c_str());
|
||||||
|
draw_load_screen(text, device, font, dtime, progress);
|
||||||
|
delete[] text;
|
||||||
|
|
||||||
// Delay a bit
|
// On some computers framerate doesn't seem to be
|
||||||
sleep_ms(1000*frametime);
|
// automatically limited
|
||||||
time_counter += frametime;
|
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);
|
||||||
|
}
|
||||||
|
time_counter += dtime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,7 +1232,7 @@ void the_game(
|
|||||||
After all content has been received:
|
After all content has been received:
|
||||||
Update cached textures, meshes and materials
|
Update cached textures, meshes and materials
|
||||||
*/
|
*/
|
||||||
client.afterContentReceived();
|
client.afterContentReceived(device,font);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create the camera node
|
Create the camera node
|
||||||
@ -1885,7 +2061,8 @@ void the_game(
|
|||||||
if(input->isKeyDown(irr::KEY_RIGHT))
|
if(input->isKeyDown(irr::KEY_RIGHT))
|
||||||
dx += dtime * keyspeed;*/
|
dx += dtime * keyspeed;*/
|
||||||
|
|
||||||
float d = 0.2;
|
float d = g_settings->getFloat("mouse_sensitivity");
|
||||||
|
d = rangelim(d, 0.01, 100.0);
|
||||||
camera_yaw -= dx*d;
|
camera_yaw -= dx*d;
|
||||||
camera_pitch += dy*d;
|
camera_pitch += dy*d;
|
||||||
if(camera_pitch < -89.5) camera_pitch = -89.5;
|
if(camera_pitch < -89.5) camera_pitch = -89.5;
|
||||||
@ -2198,17 +2375,15 @@ void the_game(
|
|||||||
- Can it point to liquids?
|
- Can it point to liquids?
|
||||||
*/
|
*/
|
||||||
ItemStack playeritem;
|
ItemStack playeritem;
|
||||||
bool playeritem_usable = false;
|
|
||||||
bool playeritem_liquids_pointable = false;
|
|
||||||
{
|
{
|
||||||
InventoryList *mlist = local_inventory.getList("main");
|
InventoryList *mlist = local_inventory.getList("main");
|
||||||
if(mlist != NULL)
|
if(mlist != NULL)
|
||||||
{
|
{
|
||||||
playeritem = mlist->getItem(client.getPlayerItem());
|
playeritem = mlist->getItem(client.getPlayerItem());
|
||||||
playeritem_usable = playeritem.getDefinition(itemdef).usable;
|
|
||||||
playeritem_liquids_pointable = playeritem.getDefinition(itemdef).liquids_pointable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const ItemDefinition &playeritem_def =
|
||||||
|
playeritem.getDefinition(itemdef);
|
||||||
ToolCapabilities playeritem_toolcap =
|
ToolCapabilities playeritem_toolcap =
|
||||||
playeritem.getToolCapabilities(itemdef);
|
playeritem.getToolCapabilities(itemdef);
|
||||||
|
|
||||||
@ -2267,7 +2442,7 @@ void the_game(
|
|||||||
// input
|
// input
|
||||||
&client, player_position, camera_direction,
|
&client, player_position, camera_direction,
|
||||||
camera_position, shootline, d,
|
camera_position, shootline, d,
|
||||||
playeritem_liquids_pointable, !ldown_for_dig,
|
playeritem_def.liquids_pointable, !ldown_for_dig,
|
||||||
// output
|
// output
|
||||||
hilightboxes,
|
hilightboxes,
|
||||||
selected_object);
|
selected_object);
|
||||||
@ -2327,7 +2502,7 @@ void the_game(
|
|||||||
else
|
else
|
||||||
repeat_rightclick_timer = 0;
|
repeat_rightclick_timer = 0;
|
||||||
|
|
||||||
if(playeritem_usable && input->getLeftState())
|
if(playeritem_def.usable && input->getLeftState())
|
||||||
{
|
{
|
||||||
if(input->getLeftClicked())
|
if(input->getLeftClicked())
|
||||||
client.interact(4, pointed);
|
client.interact(4, pointed);
|
||||||
@ -2534,46 +2709,13 @@ void the_game(
|
|||||||
|
|
||||||
// If the wielded item has node placement prediction,
|
// If the wielded item has node placement prediction,
|
||||||
// make that happen
|
// make that happen
|
||||||
const ItemDefinition &def =
|
nodePlacementPrediction(client,
|
||||||
playeritem.getDefinition(itemdef);
|
playeritem_def,
|
||||||
if(def.node_placement_prediction != ""
|
nodepos, neighbourpos);
|
||||||
&& !nodedef->get(map.getNode(nodepos)).rightclickable)
|
|
||||||
do{ // breakable
|
|
||||||
verbosestream<<"Node placement prediction for "
|
|
||||||
<<playeritem.name<<" is "
|
|
||||||
<<def.node_placement_prediction<<std::endl;
|
|
||||||
v3s16 p = neighbourpos;
|
|
||||||
// Place inside node itself if buildable_to
|
|
||||||
try{
|
|
||||||
MapNode n_under = map.getNode(nodepos);
|
|
||||||
if(nodedef->get(n_under).buildable_to)
|
|
||||||
p = nodepos;
|
|
||||||
}catch(InvalidPositionException &e){}
|
|
||||||
// Find id of predicted node
|
|
||||||
content_t id;
|
|
||||||
bool found =
|
|
||||||
nodedef->getId(def.node_placement_prediction, id);
|
|
||||||
if(!found){
|
|
||||||
errorstream<<"Node placement prediction failed for "
|
|
||||||
<<playeritem.name<<" (places "
|
|
||||||
<<def.node_placement_prediction
|
|
||||||
<<") - Name not known"<<std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MapNode n(id);
|
|
||||||
try{
|
|
||||||
// This triggers the required mesh update too
|
|
||||||
client.addNode(p, n);
|
|
||||||
}catch(InvalidPositionException &e){
|
|
||||||
errorstream<<"Node placement prediction failed for "
|
|
||||||
<<playeritem.name<<" (places "
|
|
||||||
<<def.node_placement_prediction
|
|
||||||
<<") - Position not loaded"<<std::endl;
|
|
||||||
}
|
|
||||||
}while(0);
|
|
||||||
|
|
||||||
// Read the sound
|
// Read the sound
|
||||||
soundmaker.m_player_rightpunch_sound = def.sound_place;
|
soundmaker.m_player_rightpunch_sound =
|
||||||
|
playeritem_def.sound_place;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3193,6 +3335,8 @@ void the_game(
|
|||||||
clouds->drop();
|
clouds->drop();
|
||||||
if (gui_chat_console)
|
if (gui_chat_console)
|
||||||
gui_chat_console->drop();
|
gui_chat_console->drop();
|
||||||
|
if (sky)
|
||||||
|
sky->drop();
|
||||||
clear_particles();
|
clear_particles();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3201,7 +3345,9 @@ void the_game(
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
/*gui::IGUIStaticText *gui_shuttingdowntext = */
|
/*gui::IGUIStaticText *gui_shuttingdowntext = */
|
||||||
draw_load_screen(L"Shutting down stuff...", driver, font);
|
wchar_t* text = wgettext("Shutting down stuff...");
|
||||||
|
draw_load_screen(text, device, font, 0, -1, false);
|
||||||
|
delete[] text;
|
||||||
/*driver->beginScene(true, true, video::SColor(255,0,0,0));
|
/*driver->beginScene(true, true, video::SColor(255,0,0,0));
|
||||||
guienv->drawAll();
|
guienv->drawAll();
|
||||||
driver->endScene();
|
driver->endScene();
|
||||||
|
@ -121,6 +121,7 @@ GUIChatConsole::GUIChatConsole(
|
|||||||
|
|
||||||
GUIChatConsole::~GUIChatConsole()
|
GUIChatConsole::~GUIChatConsole()
|
||||||
{
|
{
|
||||||
|
delete m_font;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUIChatConsole::openConsole(f32 height)
|
void GUIChatConsole::openConsole(f32 height)
|
||||||
|
@ -407,14 +407,26 @@ bool GUIConfigureWorld::OnEvent(const SEvent& event)
|
|||||||
delete[] text;
|
delete[] text;
|
||||||
menu->drop();
|
menu->drop();
|
||||||
|
|
||||||
ModConfiguration modconf(m_wspec.path);
|
try
|
||||||
if(!modconf.isConsistent())
|
|
||||||
{
|
{
|
||||||
wchar_t* text = wgettext("Warning: Configuration not consistent. ");
|
ModConfiguration modconf(m_wspec.path);
|
||||||
|
if(!modconf.isConsistent())
|
||||||
|
{
|
||||||
|
wchar_t* text = wgettext("Warning: Configuration not consistent. ");
|
||||||
|
GUIMessageMenu *menu =
|
||||||
|
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
||||||
|
text );
|
||||||
|
delete[] text;
|
||||||
|
menu->drop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ModError &err)
|
||||||
|
{
|
||||||
|
errorstream<<err.what()<<std::endl;
|
||||||
|
std::wstring text = narrow_to_wide(err.what()) + wgettext("\nCheck debug.txt for details.");
|
||||||
GUIMessageMenu *menu =
|
GUIMessageMenu *menu =
|
||||||
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
||||||
text );
|
text );
|
||||||
delete[] text;
|
|
||||||
menu->drop();
|
menu->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,13 +42,22 @@ GUICreateWorld::GUICreateWorld(gui::IGUIEnvironment* env,
|
|||||||
gui::IGUIElement* parent, s32 id,
|
gui::IGUIElement* parent, s32 id,
|
||||||
IMenuManager *menumgr,
|
IMenuManager *menumgr,
|
||||||
CreateWorldDest *dest,
|
CreateWorldDest *dest,
|
||||||
const std::vector<SubgameSpec> &games
|
const std::vector<SubgameSpec> &games,
|
||||||
|
const std::string &initial_game
|
||||||
):
|
):
|
||||||
GUIModalMenu(env, parent, id, menumgr),
|
GUIModalMenu(env, parent, id, menumgr),
|
||||||
m_dest(dest),
|
m_dest(dest),
|
||||||
m_games(games)
|
m_games(games),
|
||||||
|
m_initial_game_i(0)
|
||||||
{
|
{
|
||||||
assert(games.size() > 0);
|
assert(games.size() > 0);
|
||||||
|
|
||||||
|
for(size_t i=0; i<games.size(); i++){
|
||||||
|
if(games[i].id == initial_game){
|
||||||
|
m_initial_game_i = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GUICreateWorld::~GUICreateWorld()
|
GUICreateWorld::~GUICreateWorld()
|
||||||
@ -151,7 +160,7 @@ void GUICreateWorld::regenerateGui(v2u32 screensize)
|
|||||||
os<<L"]";
|
os<<L"]";
|
||||||
e->addItem(os.str().c_str());
|
e->addItem(os.str().c_str());
|
||||||
}
|
}
|
||||||
e->setSelected(0);
|
e->setSelected(m_initial_game_i);
|
||||||
}
|
}
|
||||||
changeCtype("");
|
changeCtype("");
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,8 @@ public:
|
|||||||
gui::IGUIElement* parent, s32 id,
|
gui::IGUIElement* parent, s32 id,
|
||||||
IMenuManager *menumgr,
|
IMenuManager *menumgr,
|
||||||
CreateWorldDest *dest,
|
CreateWorldDest *dest,
|
||||||
const std::vector<SubgameSpec> &games);
|
const std::vector<SubgameSpec> &games,
|
||||||
|
const std::string &initial_game);
|
||||||
~GUICreateWorld();
|
~GUICreateWorld();
|
||||||
|
|
||||||
void removeChildren();
|
void removeChildren();
|
||||||
@ -56,6 +57,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
CreateWorldDest *m_dest;
|
CreateWorldDest *m_dest;
|
||||||
std::vector<SubgameSpec> m_games;
|
std::vector<SubgameSpec> m_games;
|
||||||
|
int m_initial_game_i;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -53,12 +53,12 @@ const wchar_t *contrib_core_strs[] = {
|
|||||||
L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
|
L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
|
||||||
L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
|
L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
|
||||||
L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
|
L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
|
||||||
L"proller <proler@gmail.com>"
|
L"proller <proler@gmail.com>",
|
||||||
|
L"sfan5 <sfan5@live.de>"
|
||||||
};
|
};
|
||||||
|
|
||||||
const wchar_t *contrib_active_strs[] = {
|
const wchar_t *contrib_active_strs[] = {
|
||||||
L"kahrl <kahrl@gmx.net>",
|
L"kahrl <kahrl@gmx.net>",
|
||||||
L"sfan5 <sfan5@live.de>",
|
|
||||||
L"sapier <sapier@gmx.net>",
|
L"sapier <sapier@gmx.net>",
|
||||||
L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
|
L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
|
||||||
L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
|
L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
|
||||||
@ -162,15 +162,8 @@ enum
|
|||||||
GUI_ID_SERVERLIST_TOGGLE,
|
GUI_ID_SERVERLIST_TOGGLE,
|
||||||
GUI_ID_SERVERLIST_DELETE,
|
GUI_ID_SERVERLIST_DELETE,
|
||||||
GUI_ID_SERVERLIST_TITLE,
|
GUI_ID_SERVERLIST_TITLE,
|
||||||
};
|
GUI_ID_GAME_BUTTON_FIRST = 130,
|
||||||
|
GUI_ID_GAME_BUTTON_MAX = 150,
|
||||||
enum
|
|
||||||
{
|
|
||||||
TAB_SINGLEPLAYER=0,
|
|
||||||
TAB_MULTIPLAYER,
|
|
||||||
TAB_ADVANCED,
|
|
||||||
TAB_SETTINGS,
|
|
||||||
TAB_CREDITS
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
|
GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
|
||||||
@ -255,8 +248,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||||||
{
|
{
|
||||||
core::rect<s32> rect(0, 0, size.X, 40);
|
core::rect<s32> rect(0, 0, size.X, 40);
|
||||||
rect += v2s32(4, 0);
|
rect += v2s32(4, 0);
|
||||||
Environment->addStaticText(narrow_to_wide(
|
std::string t = "Minetest " VERSION_STRING;
|
||||||
"Minetest " VERSION_STRING).c_str(),
|
if(m_data->selected_game_name != "" &&
|
||||||
|
m_data->selected_tab == TAB_SINGLEPLAYER){
|
||||||
|
t += "/";
|
||||||
|
t += m_data->selected_game_name;
|
||||||
|
}
|
||||||
|
Environment->addStaticText(narrow_to_wide(t).c_str(),
|
||||||
rect, false, true, this, -1);
|
rect, false, true, this, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,11 +340,17 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||||||
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
||||||
GUI_ID_WORLD_LISTBOX);
|
GUI_ID_WORLD_LISTBOX);
|
||||||
e->setDrawBackground(true);
|
e->setDrawBackground(true);
|
||||||
for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
|
m_world_indices.clear();
|
||||||
i != m_data->worlds.end(); i++){
|
for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
|
||||||
e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
|
const WorldSpec &spec = m_data->worlds[wi];
|
||||||
|
if(spec.gameid == m_data->selected_game){
|
||||||
|
//e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
|
||||||
|
e->addItem(narrow_to_wide(spec.name).c_str());
|
||||||
|
m_world_indices.push_back(wi);
|
||||||
|
if(m_data->selected_world == (int)wi)
|
||||||
|
e->setSelected(m_world_indices.size()-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e->setSelected(m_data->selected_world);
|
|
||||||
Environment->setFocus(e);
|
Environment->setFocus(e);
|
||||||
}
|
}
|
||||||
// Delete world button
|
// Delete world button
|
||||||
@ -416,6 +420,26 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||||||
delete[] text;
|
delete[] text;
|
||||||
}
|
}
|
||||||
changeCtype("C");
|
changeCtype("C");
|
||||||
|
|
||||||
|
/* Add game selection buttons */
|
||||||
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||||
|
for(size_t i=0; i<m_data->games.size(); i++){
|
||||||
|
const SubgameSpec *spec = &m_data->games[i];
|
||||||
|
v2s32 p(8 + i*(48+8), screensize.Y - (48+8));
|
||||||
|
core::rect<s32> rect(0, 0, 48, 48);
|
||||||
|
rect += p;
|
||||||
|
video::ITexture *bgtexture = NULL;
|
||||||
|
if(spec->menuicon_path != "")
|
||||||
|
bgtexture = driver->getTexture(spec->menuicon_path.c_str());
|
||||||
|
gui::IGUIButton *b = Environment->addButton(rect, this,
|
||||||
|
GUI_ID_GAME_BUTTON_FIRST+i, narrow_to_wide(wrap_rows(spec->id, 4)).c_str());
|
||||||
|
if(bgtexture){
|
||||||
|
b->setImage(bgtexture);
|
||||||
|
b->setText(L"");
|
||||||
|
b->setDrawBorder(false);
|
||||||
|
b->setUseAlphaChannel(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(m_data->selected_tab == TAB_MULTIPLAYER)
|
else if(m_data->selected_tab == TAB_MULTIPLAYER)
|
||||||
{
|
{
|
||||||
@ -471,16 +495,20 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||||||
{
|
{
|
||||||
core::rect<s32> rect(0, 0, 390, 20);
|
core::rect<s32> rect(0, 0, 390, 20);
|
||||||
rect += m_topleft_client + v2s32(50, 10);
|
rect += m_topleft_client + v2s32(50, 10);
|
||||||
Environment->addStaticText(wgettext("Favorites:"),
|
wchar_t* text = wgettext("Favorites:");
|
||||||
|
Environment->addStaticText(text,
|
||||||
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
||||||
|
delete[] text;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_data->servers = ServerList::getOnline();
|
m_data->servers = ServerList::getOnline();
|
||||||
{
|
{
|
||||||
core::rect<s32> rect(0, 0, 390, 20);
|
core::rect<s32> rect(0, 0, 390, 20);
|
||||||
rect += m_topleft_client + v2s32(50, 10);
|
rect += m_topleft_client + v2s32(50, 10);
|
||||||
Environment->addStaticText(wgettext("Public Server List:"),
|
wchar_t* text = wgettext("Public Server List:");
|
||||||
|
Environment->addStaticText(text,
|
||||||
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
||||||
|
delete[] text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -488,8 +516,10 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||||||
{
|
{
|
||||||
core::rect<s32> rect(0, 0, 390, 20);
|
core::rect<s32> rect(0, 0, 390, 20);
|
||||||
rect += m_topleft_client + v2s32(50, 10);
|
rect += m_topleft_client + v2s32(50, 10);
|
||||||
Environment->addStaticText(wgettext("Favorites:"),
|
wchar_t* text = wgettext("Favorites:");
|
||||||
|
Environment->addStaticText(text,
|
||||||
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
||||||
|
delete[] text;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
updateGuiServerList();
|
updateGuiServerList();
|
||||||
@ -714,9 +744,11 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||||||
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
||||||
GUI_ID_WORLD_LISTBOX);
|
GUI_ID_WORLD_LISTBOX);
|
||||||
e->setDrawBackground(true);
|
e->setDrawBackground(true);
|
||||||
for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
|
m_world_indices.clear();
|
||||||
i != m_data->worlds.end(); i++){
|
for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
|
||||||
e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
|
const WorldSpec &spec = m_data->worlds[wi];
|
||||||
|
e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
|
||||||
|
m_world_indices.push_back(wi);
|
||||||
}
|
}
|
||||||
e->setSelected(m_data->selected_world);
|
e->setSelected(m_data->selected_world);
|
||||||
}
|
}
|
||||||
@ -909,7 +941,9 @@ void GUIMainMenu::drawMenu()
|
|||||||
if (!skin)
|
if (!skin)
|
||||||
return;
|
return;
|
||||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||||
|
|
||||||
|
/* Draw menu background */
|
||||||
|
|
||||||
/*video::SColor bgcolor(140,0,0,0);
|
/*video::SColor bgcolor(140,0,0,0);
|
||||||
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
|
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
|
||||||
|
|
||||||
@ -976,6 +1010,8 @@ void GUIMainMenu::drawMenu()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Draw UI elements */
|
||||||
|
|
||||||
gui::IGUIElement::draw();
|
gui::IGUIElement::draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1100,8 +1136,13 @@ void GUIMainMenu::readInput(MainMenuData *dst)
|
|||||||
|
|
||||||
{
|
{
|
||||||
gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
|
gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
|
||||||
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
|
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX){
|
||||||
dst->selected_world = ((gui::IGUIListBox*)e)->getSelected();
|
int list_i = ((gui::IGUIListBox*)e)->getSelected();
|
||||||
|
if(list_i == -1)
|
||||||
|
dst->selected_world = -1;
|
||||||
|
else
|
||||||
|
dst->selected_world = m_world_indices[list_i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ServerListSpec server =
|
ServerListSpec server =
|
||||||
@ -1221,7 +1262,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case GUI_ID_CREATE_WORLD_BUTTON: {
|
case GUI_ID_CREATE_WORLD_BUTTON: {
|
||||||
std::vector<SubgameSpec> games = getAvailableGames();
|
const std::vector<SubgameSpec> &games = m_data->games;
|
||||||
if(games.size() == 0){
|
if(games.size() == 0){
|
||||||
wchar_t* text = wgettext("Cannot create world: No games found");
|
wchar_t* text = wgettext("Cannot create world: No games found");
|
||||||
GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
|
GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
|
||||||
@ -1232,7 +1273,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
|||||||
} else {
|
} else {
|
||||||
CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
|
CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
|
||||||
GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
|
GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
|
||||||
menumgr, dest, games);
|
menumgr, dest, games, m_data->selected_game);
|
||||||
menu->drop();
|
menu->drop();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1308,6 +1349,16 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
/* Game buttons */
|
||||||
|
int eid = event.GUIEvent.Caller->getID();
|
||||||
|
if(eid >= GUI_ID_GAME_BUTTON_FIRST &&
|
||||||
|
eid <= GUI_ID_GAME_BUTTON_MAX){
|
||||||
|
m_data->selected_game =
|
||||||
|
m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].id;
|
||||||
|
m_data->selected_game_name =
|
||||||
|
m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].name;
|
||||||
|
regenerateGui(m_screensize_old);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
|
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
|
||||||
{
|
{
|
||||||
@ -1318,9 +1369,11 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
|||||||
readInput(&cur);
|
readInput(&cur);
|
||||||
if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
|
if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
|
||||||
{
|
{
|
||||||
|
wchar_t* text = wgettext("Address required.");
|
||||||
(new GUIMessageMenu(env, parent, -1, menumgr,
|
(new GUIMessageMenu(env, parent, -1, menumgr,
|
||||||
wgettext("Address required."))
|
text)
|
||||||
)->drop();
|
)->drop();
|
||||||
|
delete[] text;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
acceptInput();
|
acceptInput();
|
||||||
@ -1328,6 +1381,10 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED)
|
||||||
|
{
|
||||||
|
readInput(m_data);
|
||||||
|
}
|
||||||
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
|
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
|
||||||
{
|
{
|
||||||
switch(event.GUIEvent.Caller->getID())
|
switch(event.GUIEvent.Caller->getID())
|
||||||
|
@ -34,11 +34,22 @@ enum {
|
|||||||
SERVERLIST_PUBLIC,
|
SERVERLIST_PUBLIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TAB_SINGLEPLAYER=0,
|
||||||
|
TAB_MULTIPLAYER,
|
||||||
|
TAB_ADVANCED,
|
||||||
|
TAB_SETTINGS,
|
||||||
|
TAB_CREDITS
|
||||||
|
};
|
||||||
|
|
||||||
struct MainMenuData
|
struct MainMenuData
|
||||||
{
|
{
|
||||||
// These are in the native format of the gui elements
|
// These are in the native format of the gui elements
|
||||||
// Generic
|
// Generic
|
||||||
int selected_tab;
|
int selected_tab;
|
||||||
|
std::string selected_game;
|
||||||
|
std::string selected_game_name;
|
||||||
// Client options
|
// Client options
|
||||||
std::string servername;
|
std::string servername;
|
||||||
std::string serverdescription;
|
std::string serverdescription;
|
||||||
@ -78,6 +89,8 @@ struct MainMenuData
|
|||||||
MainMenuData():
|
MainMenuData():
|
||||||
// Generic
|
// Generic
|
||||||
selected_tab(0),
|
selected_tab(0),
|
||||||
|
selected_game("minetest"),
|
||||||
|
selected_game_name("Minetest"),
|
||||||
// Client opts
|
// Client opts
|
||||||
fancy_trees(false),
|
fancy_trees(false),
|
||||||
smooth_lighting(false),
|
smooth_lighting(false),
|
||||||
@ -128,6 +141,8 @@ private:
|
|||||||
s32 id;
|
s32 id;
|
||||||
IMenuManager *menumgr;
|
IMenuManager *menumgr;
|
||||||
|
|
||||||
|
std::vector<int> m_world_indices;
|
||||||
|
|
||||||
bool m_is_regenerating;
|
bool m_is_regenerating;
|
||||||
v2s32 m_topleft_client;
|
v2s32 m_topleft_client;
|
||||||
v2s32 m_size_client;
|
v2s32 m_size_client;
|
||||||
|
@ -168,16 +168,16 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
|
|||||||
core::rect<s32> rect(0, 0, 180, 240);
|
core::rect<s32> rect(0, 0, 180, 240);
|
||||||
rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
|
rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
|
||||||
wchar_t* text = wgettext("Default Controls:\n"
|
wchar_t* text = wgettext("Default Controls:\n"
|
||||||
"- WASD: Walk\n"
|
"- WASD: move\n"
|
||||||
"- Mouse left: dig/hit\n"
|
"- Space: jump/climb\n"
|
||||||
|
"- Shift: sneak/go down\n"
|
||||||
|
"- Q: drop item\n"
|
||||||
|
"- I: inventory\n"
|
||||||
|
"- Mouse: turn/look\n"
|
||||||
|
"- Mouse left: dig/punch\n"
|
||||||
"- Mouse right: place/use\n"
|
"- Mouse right: place/use\n"
|
||||||
"- Mouse wheel: select item\n"
|
"- Mouse wheel: select item\n"
|
||||||
"- 0...9: select item\n"
|
"- T: chat\n"
|
||||||
"- Shift: sneak\n"
|
|
||||||
"- R: Toggle viewing all loaded chunks\n"
|
|
||||||
"- I: Inventory menu\n"
|
|
||||||
"- ESC: This menu\n"
|
|
||||||
"- T: Chat\n"
|
|
||||||
);
|
);
|
||||||
Environment->addStaticText(text, rect, false, true, this, 258);
|
Environment->addStaticText(text, rect, false, true, this, 258);
|
||||||
delete[] text;
|
delete[] text;
|
||||||
|
@ -226,17 +226,11 @@ class CItemDefManager: public IWritableItemDefManager
|
|||||||
public:
|
public:
|
||||||
CItemDefManager()
|
CItemDefManager()
|
||||||
{
|
{
|
||||||
for (std::map<std::string, ItemDefinition*>::iterator iter =
|
|
||||||
m_item_definitions.begin(); iter != m_item_definitions.end();
|
|
||||||
iter ++) {
|
|
||||||
delete iter->second;
|
|
||||||
}
|
|
||||||
m_item_definitions.clear();
|
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
m_main_thread = get_current_thread_id();
|
m_main_thread = get_current_thread_id();
|
||||||
m_driver = NULL;
|
m_driver = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
virtual ~CItemDefManager()
|
virtual ~CItemDefManager()
|
||||||
@ -260,6 +254,12 @@ public:
|
|||||||
}
|
}
|
||||||
m_driver = NULL;
|
m_driver = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
for (std::map<std::string, ItemDefinition*>::iterator iter =
|
||||||
|
m_item_definitions.begin(); iter != m_item_definitions.end();
|
||||||
|
iter ++) {
|
||||||
|
delete iter->second;
|
||||||
|
}
|
||||||
|
m_item_definitions.clear();
|
||||||
}
|
}
|
||||||
virtual const ItemDefinition& get(const std::string &name_) const
|
virtual const ItemDefinition& get(const std::string &name_) const
|
||||||
{
|
{
|
||||||
|
475
src/main.cpp
475
src/main.cpp
@ -76,6 +76,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "subgame.h"
|
#include "subgame.h"
|
||||||
#include "quicktune.h"
|
#include "quicktune.h"
|
||||||
#include "serverlist.h"
|
#include "serverlist.h"
|
||||||
|
#include "sound.h"
|
||||||
|
#include "sound_openal.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Settings.
|
Settings.
|
||||||
@ -88,6 +90,10 @@ Settings *g_settings = &main_settings;
|
|||||||
Profiler main_profiler;
|
Profiler main_profiler;
|
||||||
Profiler *g_profiler = &main_profiler;
|
Profiler *g_profiler = &main_profiler;
|
||||||
|
|
||||||
|
// Menu clouds are created later
|
||||||
|
Clouds *g_menuclouds = 0;
|
||||||
|
irr::scene::ISceneManager *g_menucloudsmgr = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Debug streams
|
Debug streams
|
||||||
*/
|
*/
|
||||||
@ -196,7 +202,49 @@ u32 getTime(TimePrecision prec) {
|
|||||||
return 0;
|
return 0;
|
||||||
return g_timegetter->getTime(prec);
|
return g_timegetter->getTime(prec);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Client side main menu music fetcher
|
||||||
|
#ifndef SERVER
|
||||||
|
class MenuMusicFetcher: public OnDemandSoundFetcher
|
||||||
|
{
|
||||||
|
std::set<std::string> m_fetched;
|
||||||
|
public:
|
||||||
|
|
||||||
|
void 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");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
|
||||||
|
base = porting::path_user + DIR_DELIM + "sounds";
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
|
||||||
|
dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
|
||||||
|
}
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class StderrLogOutput: public ILogOutput
|
class StderrLogOutput: public ILogOutput
|
||||||
@ -612,122 +660,181 @@ private:
|
|||||||
bool rightreleased;
|
bool rightreleased;
|
||||||
};
|
};
|
||||||
|
|
||||||
//Draw the tiled menu background
|
struct MenuTextures
|
||||||
void drawMenuBackground(video::IVideoDriver* driver) {
|
{
|
||||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
std::string current_gameid;
|
||||||
|
bool global_textures;
|
||||||
|
video::ITexture *background;
|
||||||
|
video::ITexture *overlay;
|
||||||
|
video::ITexture *header;
|
||||||
|
video::ITexture *footer;
|
||||||
|
|
||||||
std::string path = getTexturePath("menubg.png");
|
MenuTextures():
|
||||||
if (path[0]) {
|
global_textures(false),
|
||||||
static const video::ITexture *bgtexture =
|
background(NULL),
|
||||||
driver->getTexture(path.c_str());
|
overlay(NULL),
|
||||||
|
header(NULL),
|
||||||
|
footer(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
if (bgtexture) {
|
static video::ITexture* getMenuTexture(const std::string &tname,
|
||||||
s32 scaledsize = 128;
|
video::IVideoDriver* driver, const SubgameSpec *spec)
|
||||||
|
{
|
||||||
// The important difference between destsize and screensize is
|
if(spec){
|
||||||
// that destsize is rounded to whole scaled pixels.
|
std::string path;
|
||||||
// These formulas use component-wise multiplication and division of v2u32.
|
// eg. minetest_menu_background.png (for texture packs)
|
||||||
v2u32 texturesize = bgtexture->getSize();
|
std::string pack_tname = spec->id + "_menu_" + tname + ".png";
|
||||||
v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
|
path = getTexturePath(pack_tname);
|
||||||
v2u32 destsize = scaledsize * sourcesize / texturesize;
|
if(path != "")
|
||||||
|
return driver->getTexture(path.c_str());
|
||||||
// Default texture wrapping mode in Irrlicht is ETC_REPEAT.
|
// eg. games/minetest_game/menu/background.png
|
||||||
driver->draw2DImage(bgtexture,
|
path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
|
||||||
core::rect<s32>(0, 0, destsize.X, destsize.Y),
|
if(path != "")
|
||||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
return driver->getTexture(path.c_str());
|
||||||
NULL, NULL, true);
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Draw the footer at the bottom of the window
|
void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||||
void drawMenuFooter(video::IVideoDriver* driver, bool clouds) {
|
{
|
||||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||||
std::string path = getTexturePath(clouds ?
|
video::ITexture *texture = menutextures.footer;
|
||||||
"menufooter_clouds.png" : "menufooter.png");
|
|
||||||
if (path[0]) {
|
|
||||||
static const video::ITexture *footertexture =
|
|
||||||
driver->getTexture(path.c_str());
|
|
||||||
|
|
||||||
if (footertexture) {
|
/* If no texture, draw nothing */
|
||||||
f32 mult = (((f32)screensize.Width)) /
|
if(!texture)
|
||||||
((f32)footertexture->getOriginalSize().Width);
|
return;
|
||||||
|
|
||||||
v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult,
|
f32 mult = (((f32)screensize.Width)) /
|
||||||
((f32)footertexture->getOriginalSize().Height) * mult);
|
((f32)texture->getOriginalSize().Width);
|
||||||
|
|
||||||
// Don't draw the footer if there isn't enough room
|
v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
|
||||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
((f32)texture->getOriginalSize().Height) * mult);
|
||||||
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(footertexture, rect,
|
// Don't draw the footer if there isn't enough room
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||||
core::dimension2di(footertexture->getSize())),
|
if (free_space > footersize.Y) {
|
||||||
NULL, NULL, true);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the Header over the main menu
|
static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
|
||||||
void drawMenuHeader(video::IVideoDriver* driver) {
|
{
|
||||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
for(size_t i=0; i<menudata.games.size(); i++){
|
||||||
|
if(menudata.games[i].id == menudata.selected_game){
|
||||||
std::string path = getTexturePath("menuheader.png");
|
return &menudata.games[i];
|
||||||
if (path[0]) {
|
|
||||||
static const video::ITexture *splashtexture =
|
|
||||||
driver->getTexture(path.c_str());
|
|
||||||
|
|
||||||
if(splashtexture) {
|
|
||||||
f32 mult = (((f32)screensize.Width / 2)) /
|
|
||||||
((f32)splashtexture->getOriginalSize().Width);
|
|
||||||
|
|
||||||
v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult,
|
|
||||||
((f32)splashtexture->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(splashtexture, splashrect,
|
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
|
||||||
core::dimension2di(splashtexture->getSize())),
|
|
||||||
NULL, NULL, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the Splash over the clouds and under the main menu
|
#endif // !SERVER
|
||||||
void drawMenuSplash(video::IVideoDriver* driver) {
|
|
||||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
|
||||||
std::string path = getTexturePath("menusplash.png");
|
|
||||||
if (path[0]) {
|
|
||||||
static const video::ITexture *splashtexture =
|
|
||||||
driver->getTexture(path.c_str());
|
|
||||||
|
|
||||||
if(splashtexture) {
|
|
||||||
core::rect<s32> splashrect(0, 0, screensize.Width, screensize.Height);
|
|
||||||
|
|
||||||
video::SColor bgcolor(255,50,50,50);
|
|
||||||
|
|
||||||
driver->draw2DImage(splashtexture, splashrect,
|
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
|
||||||
core::dimension2di(splashtexture->getSize())),
|
|
||||||
NULL, NULL, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// These are defined global so that they're not optimized too much.
|
// These are defined global so that they're not optimized too much.
|
||||||
// Can't change them to volatile.
|
// Can't change them to volatile.
|
||||||
@ -900,6 +1007,8 @@ int main(int argc, char *argv[])
|
|||||||
allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
|
allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
|
||||||
_("Set gameid (\"--gameid list\" prints available ones)"))));
|
_("Set gameid (\"--gameid list\" prints available ones)"))));
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
|
allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
|
||||||
|
_("Show available video modes"))));
|
||||||
allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
|
allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
|
||||||
_("Run speed tests"))));
|
_("Run speed tests"))));
|
||||||
allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
|
allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
|
||||||
@ -999,7 +1108,7 @@ int main(int argc, char *argv[])
|
|||||||
print_worldspecs(worldspecs, dstream);
|
print_worldspecs(worldspecs, dstream);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print startup message
|
// Print startup message
|
||||||
infostream<<PROJECT_NAME<<
|
infostream<<PROJECT_NAME<<
|
||||||
" "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
|
" "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
|
||||||
@ -1379,11 +1488,64 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create device and exit if creation failed
|
List video modes if requested
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MyEventReceiver receiver;
|
MyEventReceiver receiver;
|
||||||
|
|
||||||
|
if(cmd_args.getFlag("videomodes")){
|
||||||
|
IrrlichtDevice *nulldevice;
|
||||||
|
|
||||||
|
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
||||||
|
params.DriverType = video::EDT_NULL;
|
||||||
|
params.WindowSize = core::dimension2d<u32>(640, 480);
|
||||||
|
params.Bits = 24;
|
||||||
|
params.AntiAlias = fsaa;
|
||||||
|
params.Fullscreen = false;
|
||||||
|
params.Stencilbuffer = false;
|
||||||
|
params.Vsync = vsync;
|
||||||
|
params.EventReceiver = &receiver;
|
||||||
|
|
||||||
|
nulldevice = createDeviceEx(params);
|
||||||
|
|
||||||
|
if(nulldevice == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
dstream<<_("Available video modes (WxHxD):")<<std::endl;
|
||||||
|
|
||||||
|
video::IVideoModeList *videomode_list =
|
||||||
|
nulldevice->getVideoModeList();
|
||||||
|
|
||||||
|
if(videomode_list == 0){
|
||||||
|
nulldevice->drop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 videomode_count = videomode_list->getVideoModeCount();
|
||||||
|
core::dimension2d<u32> videomode_res;
|
||||||
|
s32 videomode_depth;
|
||||||
|
for (s32 i = 0; i < videomode_count; ++i){
|
||||||
|
videomode_res = videomode_list->getVideoModeResolution(i);
|
||||||
|
videomode_depth = videomode_list->getVideoModeDepth(i);
|
||||||
|
dstream<<videomode_res.Width<<"x"<<videomode_res.Height
|
||||||
|
<<"x"<<videomode_depth<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
dstream<<_("Active video mode (WxHxD):")<<std::endl;
|
||||||
|
videomode_res = videomode_list->getDesktopResolution();
|
||||||
|
videomode_depth = videomode_list->getDesktopDepth();
|
||||||
|
dstream<<videomode_res.Width<<"x"<<videomode_res.Height
|
||||||
|
<<"x"<<videomode_depth<<std::endl;
|
||||||
|
|
||||||
|
nulldevice->drop();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create device and exit if creation failed
|
||||||
|
*/
|
||||||
|
|
||||||
IrrlichtDevice *device;
|
IrrlichtDevice *device;
|
||||||
|
|
||||||
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
||||||
@ -1426,6 +1588,7 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
dstream<<"Running speed tests"<<std::endl;
|
dstream<<"Running speed tests"<<std::endl;
|
||||||
SpeedTests();
|
SpeedTests();
|
||||||
|
device->drop();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1477,6 +1640,19 @@ int main(int argc, char *argv[])
|
|||||||
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
|
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Create the menu clouds
|
||||||
|
if (!g_menucloudsmgr)
|
||||||
|
g_menucloudsmgr = smgr->createNewSceneManager();
|
||||||
|
if (!g_menuclouds)
|
||||||
|
g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
|
||||||
|
g_menucloudsmgr, -1, rand(), 100);
|
||||||
|
g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255));
|
||||||
|
scene::ICameraSceneNode* camera;
|
||||||
|
camera = g_menucloudsmgr->addCameraSceneNode(0,
|
||||||
|
v3f(0,0,0), v3f(0, 60, 100));
|
||||||
|
camera->setFarValue(10000);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GUI stuff
|
GUI stuff
|
||||||
*/
|
*/
|
||||||
@ -1558,6 +1734,10 @@ int main(int argc, char *argv[])
|
|||||||
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
|
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
|
||||||
if(g_settings->exists("selected_serverlist"))
|
if(g_settings->exists("selected_serverlist"))
|
||||||
menudata.selected_serverlist = g_settings->getS32("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.address = narrow_to_wide(address);
|
||||||
menudata.name = narrow_to_wide(playername);
|
menudata.name = narrow_to_wide(playername);
|
||||||
menudata.port = narrow_to_wide(itos(port));
|
menudata.port = narrow_to_wide(itos(port));
|
||||||
@ -1611,6 +1791,17 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
// Copy worldspecs to menu
|
// Copy worldspecs to menu
|
||||||
menudata.worlds = worldspecs;
|
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)
|
if(skip_main_menu == false)
|
||||||
{
|
{
|
||||||
@ -1623,7 +1814,7 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
driver->beginScene(true, true,
|
driver->beginScene(true, true,
|
||||||
video::SColor(255,128,128,128));
|
video::SColor(255,128,128,128));
|
||||||
drawMenuBackground(driver);
|
drawMenuBackground(driver, menutextures);
|
||||||
guienv->drawAll();
|
guienv->drawAll();
|
||||||
driver->endScene();
|
driver->endScene();
|
||||||
// On some computers framerate doesn't seem to be
|
// On some computers framerate doesn't seem to be
|
||||||
@ -1637,22 +1828,6 @@ int main(int argc, char *argv[])
|
|||||||
&g_menumgr, &menudata, g_gamecallback);
|
&g_menumgr, &menudata, g_gamecallback);
|
||||||
menu->allowFocusRemoval(true);
|
menu->allowFocusRemoval(true);
|
||||||
|
|
||||||
// Clouds for the main menu
|
|
||||||
bool cloud_menu_background = false;
|
|
||||||
Clouds *clouds = NULL;
|
|
||||||
if (g_settings->getBool("menu_clouds")) {
|
|
||||||
cloud_menu_background = true;
|
|
||||||
clouds = new Clouds(smgr->getRootSceneNode(),
|
|
||||||
smgr, -1, rand(), 100);
|
|
||||||
clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
|
|
||||||
|
|
||||||
// A camera to see the clouds
|
|
||||||
scene::ICameraSceneNode* camera;
|
|
||||||
camera = smgr->addCameraSceneNode(0,
|
|
||||||
v3f(0,0,0), v3f(0, 60, 100));
|
|
||||||
camera->setFarValue(10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(error_message != L"")
|
if(error_message != L"")
|
||||||
{
|
{
|
||||||
verbosestream<<"error_message = "
|
verbosestream<<"error_message = "
|
||||||
@ -1668,6 +1843,16 @@ int main(int argc, char *argv[])
|
|||||||
// Time is in milliseconds, for clouds
|
// Time is in milliseconds, for clouds
|
||||||
u32 lasttime = device->getTimer()->getTime();
|
u32 lasttime = device->getTimer()->getTime();
|
||||||
|
|
||||||
|
MenuMusicFetcher soundfetcher;
|
||||||
|
ISoundManager *sound = NULL;
|
||||||
|
sound = createOpenALSoundManager(&soundfetcher);
|
||||||
|
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;
|
infostream<<"Created main menu"<<std::endl;
|
||||||
|
|
||||||
while(device->run() && kill == false)
|
while(device->run() && kill == false)
|
||||||
@ -1675,8 +1860,25 @@ int main(int argc, char *argv[])
|
|||||||
if(menu->getStatus() == true)
|
if(menu->getStatus() == true)
|
||||||
break;
|
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
|
// Time calc for the clouds
|
||||||
f32 dtime; // in seconds
|
f32 dtime=0; // in seconds
|
||||||
if (cloud_menu_background) {
|
if (cloud_menu_background) {
|
||||||
u32 time = device->getTimer()->getTime();
|
u32 time = device->getTimer()->getTime();
|
||||||
if(time > lasttime)
|
if(time > lasttime)
|
||||||
@ -1691,15 +1893,16 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (cloud_menu_background) {
|
if (cloud_menu_background) {
|
||||||
// *3 otherwise the clouds would move very slowly
|
// *3 otherwise the clouds would move very slowly
|
||||||
clouds->step(dtime*3);
|
g_menuclouds->step(dtime*3);
|
||||||
clouds->render();
|
g_menuclouds->render();
|
||||||
smgr->drawAll();
|
g_menucloudsmgr->drawAll();
|
||||||
drawMenuSplash(driver);
|
drawMenuOverlay(driver, menutextures);
|
||||||
drawMenuFooter(driver, true);
|
drawMenuHeader(driver, menutextures);
|
||||||
drawMenuHeader(driver);
|
drawMenuFooter(driver, menutextures);
|
||||||
} else {
|
} else {
|
||||||
drawMenuBackground(driver);
|
drawMenuBackground(driver, menutextures);
|
||||||
drawMenuFooter(driver, false);
|
drawMenuHeader(driver, menutextures);
|
||||||
|
drawMenuFooter(driver, menutextures);
|
||||||
}
|
}
|
||||||
|
|
||||||
guienv->drawAll();
|
guienv->drawAll();
|
||||||
@ -1731,14 +1934,15 @@ int main(int argc, char *argv[])
|
|||||||
sleep_ms(25);
|
sleep_ms(25);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sound->stopSound(handle);
|
||||||
|
if(sound != &dummySoundManager){
|
||||||
|
delete sound;
|
||||||
|
sound = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
infostream<<"Dropping main menu"<<std::endl;
|
infostream<<"Dropping main menu"<<std::endl;
|
||||||
|
|
||||||
menu->drop();
|
menu->drop();
|
||||||
if (cloud_menu_background) {
|
|
||||||
clouds->drop();
|
|
||||||
smgr->clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playername = wide_to_narrow(menudata.name);
|
playername = wide_to_narrow(menudata.name);
|
||||||
@ -1755,6 +1959,7 @@ int main(int argc, char *argv[])
|
|||||||
// Save settings
|
// Save settings
|
||||||
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
|
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
|
||||||
g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
|
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("new_style_leaves", itos(menudata.fancy_trees));
|
||||||
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
|
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
|
||||||
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
|
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
|
||||||
@ -1832,6 +2037,7 @@ int main(int argc, char *argv[])
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
g_settings->set("selected_world_path", path);
|
g_settings->set("selected_world_path", path);
|
||||||
|
g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1897,6 +2103,7 @@ int main(int argc, char *argv[])
|
|||||||
gamespec,
|
gamespec,
|
||||||
simple_singleplayer_mode
|
simple_singleplayer_mode
|
||||||
);
|
);
|
||||||
|
smgr->clear();
|
||||||
|
|
||||||
} //try
|
} //try
|
||||||
catch(con::PeerNotFoundException &e)
|
catch(con::PeerNotFoundException &e)
|
||||||
@ -1927,6 +2134,10 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
} // Menu-game loop
|
} // Menu-game loop
|
||||||
|
|
||||||
|
|
||||||
|
g_menuclouds->drop();
|
||||||
|
g_menucloudsmgr->drop();
|
||||||
|
|
||||||
delete input;
|
delete input;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1934,6 +2145,8 @@ int main(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
device->drop();
|
device->drop();
|
||||||
|
|
||||||
|
delete font;
|
||||||
|
|
||||||
#endif // !SERVER
|
#endif // !SERVER
|
||||||
|
|
||||||
// Update configuration file
|
// Update configuration file
|
||||||
|
@ -28,6 +28,14 @@ extern Settings *g_settings;
|
|||||||
class Profiler;
|
class Profiler;
|
||||||
extern Profiler *g_profiler;
|
extern Profiler *g_profiler;
|
||||||
|
|
||||||
|
// Menu clouds
|
||||||
|
class Clouds;
|
||||||
|
extern Clouds *g_menuclouds;
|
||||||
|
|
||||||
|
// Scene manager used for menu clouds
|
||||||
|
namespace irr{namespace scene{class ISceneManager;}}
|
||||||
|
extern irr::scene::ISceneManager *g_menucloudsmgr;
|
||||||
|
|
||||||
// Debug streams
|
// Debug streams
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -1338,12 +1338,20 @@ void MeshCollector::append(const TileSpec &tile,
|
|||||||
const video::S3DVertex *vertices, u32 numVertices,
|
const video::S3DVertex *vertices, u32 numVertices,
|
||||||
const u16 *indices, u32 numIndices)
|
const u16 *indices, u32 numIndices)
|
||||||
{
|
{
|
||||||
|
if(numIndices > 65535)
|
||||||
|
{
|
||||||
|
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PreMeshBuffer *p = NULL;
|
PreMeshBuffer *p = NULL;
|
||||||
for(u32 i=0; i<prebuffers.size(); i++)
|
for(u32 i=0; i<prebuffers.size(); i++)
|
||||||
{
|
{
|
||||||
PreMeshBuffer &pp = prebuffers[i];
|
PreMeshBuffer &pp = prebuffers[i];
|
||||||
if(pp.tile != tile)
|
if(pp.tile != tile)
|
||||||
continue;
|
continue;
|
||||||
|
if(pp.indices.size() + numIndices > 65535)
|
||||||
|
continue;
|
||||||
|
|
||||||
p = &pp;
|
p = &pp;
|
||||||
break;
|
break;
|
||||||
@ -1361,11 +1369,6 @@ void MeshCollector::append(const TileSpec &tile,
|
|||||||
for(u32 i=0; i<numIndices; i++)
|
for(u32 i=0; i<numIndices; i++)
|
||||||
{
|
{
|
||||||
u32 j = indices[i] + vertex_count;
|
u32 j = indices[i] + vertex_count;
|
||||||
if(j > 65535)
|
|
||||||
{
|
|
||||||
dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
|
|
||||||
// NOTE: Fix is to just add an another MeshBuffer
|
|
||||||
}
|
|
||||||
p->indices.push_back(j);
|
p->indices.push_back(j);
|
||||||
}
|
}
|
||||||
for(u32 i=0; i<numVertices; i++)
|
for(u32 i=0; i<numVertices; i++)
|
||||||
|
@ -284,7 +284,6 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture,
|
|||||||
if (img2 != NULL)
|
if (img2 != NULL)
|
||||||
{
|
{
|
||||||
img1->copyTo(img2);
|
img1->copyTo(img2);
|
||||||
img1->drop();
|
|
||||||
|
|
||||||
mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock());
|
mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock());
|
||||||
img2->unlock();
|
img2->unlock();
|
||||||
|
375
src/mods.cpp
375
src/mods.cpp
@ -24,9 +24,74 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "subgame.h"
|
#include "subgame.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "strfnd.h"
|
#include "strfnd.h"
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
std::map<std::string, ModSpec> getModsInPath(std::string path)
|
static bool parseDependsLine(std::istream &is,
|
||||||
|
std::string &dep, std::set<char> &symbols)
|
||||||
{
|
{
|
||||||
|
std::getline(is, dep);
|
||||||
|
dep = trim(dep);
|
||||||
|
symbols.clear();
|
||||||
|
size_t pos = dep.size();
|
||||||
|
while(pos > 0 && !string_allowed(dep.substr(pos-1, 1), MODNAME_ALLOWED_CHARS)){
|
||||||
|
// last character is a symbol, not part of the modname
|
||||||
|
symbols.insert(dep[pos-1]);
|
||||||
|
--pos;
|
||||||
|
}
|
||||||
|
dep = trim(dep.substr(0, pos));
|
||||||
|
return dep != "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseModContents(ModSpec &spec)
|
||||||
|
{
|
||||||
|
// NOTE: this function works in mutual recursion with getModsInPath
|
||||||
|
|
||||||
|
spec.depends.clear();
|
||||||
|
spec.optdepends.clear();
|
||||||
|
spec.is_modpack = false;
|
||||||
|
spec.modpack_content.clear();
|
||||||
|
|
||||||
|
// Handle modpacks (defined by containing modpack.txt)
|
||||||
|
std::ifstream modpack_is((spec.path+DIR_DELIM+"modpack.txt").c_str());
|
||||||
|
if(modpack_is.good()){ //a modpack, recursively get the mods in it
|
||||||
|
modpack_is.close(); // We don't actually need the file
|
||||||
|
spec.is_modpack = true;
|
||||||
|
spec.modpack_content = getModsInPath(spec.path, true);
|
||||||
|
|
||||||
|
// modpacks have no dependencies; they are defined and
|
||||||
|
// tracked separately for each mod in the modpack
|
||||||
|
}
|
||||||
|
else{ // not a modpack, parse the dependencies
|
||||||
|
std::ifstream is((spec.path+DIR_DELIM+"depends.txt").c_str());
|
||||||
|
while(is.good()){
|
||||||
|
std::string dep;
|
||||||
|
std::set<char> symbols;
|
||||||
|
if(parseDependsLine(is, dep, symbols)){
|
||||||
|
if(symbols.count('?') != 0){
|
||||||
|
spec.optdepends.insert(dep);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
spec.depends.insert(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: optdepends.txt is deprecated
|
||||||
|
// remove this code at some point in the future
|
||||||
|
std::ifstream is2((spec.path+DIR_DELIM+"optdepends.txt").c_str());
|
||||||
|
while(is2.good()){
|
||||||
|
std::string dep;
|
||||||
|
std::set<char> symbols;
|
||||||
|
if(parseDependsLine(is2, dep, symbols))
|
||||||
|
spec.optdepends.insert(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, ModSpec> getModsInPath(std::string path, bool part_of_modpack)
|
||||||
|
{
|
||||||
|
// NOTE: this function works in mutual recursion with parseModContents
|
||||||
|
|
||||||
std::map<std::string, ModSpec> result;
|
std::map<std::string, ModSpec> result;
|
||||||
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
|
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
|
||||||
for(u32 j=0; j<dirlist.size(); j++){
|
for(u32 j=0; j<dirlist.size(); j++){
|
||||||
@ -39,38 +104,34 @@ std::map<std::string, ModSpec> getModsInPath(std::string path)
|
|||||||
continue;
|
continue;
|
||||||
std::string modpath = path + DIR_DELIM + modname;
|
std::string modpath = path + DIR_DELIM + modname;
|
||||||
|
|
||||||
// Handle modpacks (defined by containing modpack.txt)
|
ModSpec spec(modname, modpath);
|
||||||
std::ifstream modpack_is((modpath+DIR_DELIM+"modpack.txt").c_str(),
|
spec.part_of_modpack = part_of_modpack;
|
||||||
std::ios_base::binary);
|
parseModContents(spec);
|
||||||
if(modpack_is.good()) //a modpack, recursively get the mods in it
|
result.insert(std::make_pair(modname, spec));
|
||||||
{
|
|
||||||
modpack_is.close(); // We don't actually need the file
|
|
||||||
ModSpec spec(modname,modpath);
|
|
||||||
spec.modpack_content = getModsInPath(modpath);
|
|
||||||
spec.is_modpack = true;
|
|
||||||
result.insert(std::make_pair(modname,spec));
|
|
||||||
}
|
|
||||||
else // not a modpack, add the modspec
|
|
||||||
{
|
|
||||||
std::set<std::string> depends;
|
|
||||||
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
|
|
||||||
std::ios_base::binary);
|
|
||||||
while(is.good())
|
|
||||||
{
|
|
||||||
std::string dep;
|
|
||||||
std::getline(is, dep);
|
|
||||||
dep = trim(dep);
|
|
||||||
if(dep != "")
|
|
||||||
depends.insert(dep);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModSpec spec(modname, modpath, depends);
|
|
||||||
result.insert(std::make_pair(modname,spec));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModSpec findCommonMod(const std::string &modname)
|
||||||
|
{
|
||||||
|
// Try to find in {$user,$share}/games/common/$modname
|
||||||
|
std::vector<std::string> find_paths;
|
||||||
|
find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
|
||||||
|
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
||||||
|
find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
|
||||||
|
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
||||||
|
for(u32 i=0; i<find_paths.size(); i++){
|
||||||
|
const std::string &try_path = find_paths[i];
|
||||||
|
if(fs::PathExists(try_path)){
|
||||||
|
ModSpec spec(modname, try_path);
|
||||||
|
parseModContents(spec);
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Failed to find mod
|
||||||
|
return ModSpec();
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
|
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
|
||||||
{
|
{
|
||||||
std::map<std::string, ModSpec> result;
|
std::map<std::string, ModSpec> result;
|
||||||
@ -109,109 +170,18 @@ std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
|
|||||||
}
|
}
|
||||||
else //not a modpack
|
else //not a modpack
|
||||||
{
|
{
|
||||||
// infostream << "inserting mod " << mod.name << std::endl;
|
|
||||||
result.push_back(mod);
|
result.push_back(mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
|
|
||||||
std::set<std::string> exclude_mod_names)
|
|
||||||
{
|
|
||||||
std::vector<ModSpec> result;
|
|
||||||
for(std::vector<ModSpec>::iterator it = mods.begin();
|
|
||||||
it != mods.end(); ++it)
|
|
||||||
{
|
|
||||||
ModSpec& mod = *it;
|
|
||||||
if(exclude_mod_names.count(mod.name) == 0)
|
|
||||||
result.push_back(mod);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
|
|
||||||
{
|
|
||||||
addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
|
|
||||||
{
|
|
||||||
// Step 1: remove mods in sorted_mods from unmet dependencies
|
|
||||||
// of new_mods. new mods without unmet dependencies are
|
|
||||||
// temporarily stored in satisfied_mods
|
|
||||||
std::vector<ModSpec> satisfied_mods;
|
|
||||||
for(std::vector<ModSpec>::iterator it = m_sorted_mods.begin();
|
|
||||||
it != m_sorted_mods.end(); ++it)
|
|
||||||
{
|
|
||||||
ModSpec mod = *it;
|
|
||||||
for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
|
|
||||||
it_new != new_mods.end(); ++it_new)
|
|
||||||
{
|
|
||||||
ModSpec& mod_new = *it_new;
|
|
||||||
//infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl;
|
|
||||||
mod_new.unsatisfied_depends.erase(mod.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// split new mods into satisfied and unsatisfied
|
|
||||||
for(std::vector<ModSpec>::iterator it = new_mods.begin();
|
|
||||||
it != new_mods.end(); ++it)
|
|
||||||
{
|
|
||||||
ModSpec mod_new = *it;
|
|
||||||
if(mod_new.unsatisfied_depends.empty())
|
|
||||||
satisfied_mods.push_back(mod_new);
|
|
||||||
else
|
|
||||||
m_unsatisfied_mods.push_back(mod_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: mods without unmet dependencies can be appended to
|
|
||||||
// the sorted list.
|
|
||||||
while(!satisfied_mods.empty())
|
|
||||||
{
|
|
||||||
ModSpec mod = satisfied_mods.back();
|
|
||||||
m_sorted_mods.push_back(mod);
|
|
||||||
satisfied_mods.pop_back();
|
|
||||||
for(std::list<ModSpec>::iterator it = m_unsatisfied_mods.begin();
|
|
||||||
it != m_unsatisfied_mods.end(); )
|
|
||||||
{
|
|
||||||
ModSpec& mod2 = *it;
|
|
||||||
mod2.unsatisfied_depends.erase(mod.name);
|
|
||||||
if(mod2.unsatisfied_depends.empty())
|
|
||||||
{
|
|
||||||
satisfied_mods.push_back(mod2);
|
|
||||||
it = m_unsatisfied_mods.erase(it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If failed, returned modspec has name==""
|
|
||||||
static ModSpec findCommonMod(const std::string &modname)
|
|
||||||
{
|
|
||||||
// Try to find in {$user,$share}/games/common/$modname
|
|
||||||
std::vector<std::string> find_paths;
|
|
||||||
find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
|
|
||||||
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
|
||||||
find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
|
|
||||||
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
|
||||||
for(u32 i=0; i<find_paths.size(); i++){
|
|
||||||
const std::string &try_path = find_paths[i];
|
|
||||||
if(fs::PathExists(try_path))
|
|
||||||
return ModSpec(modname, try_path);
|
|
||||||
}
|
|
||||||
// Failed to find mod
|
|
||||||
return ModSpec();
|
|
||||||
}
|
|
||||||
|
|
||||||
ModConfiguration::ModConfiguration(std::string worldpath)
|
ModConfiguration::ModConfiguration(std::string worldpath)
|
||||||
{
|
{
|
||||||
SubgameSpec gamespec = findWorldSubgame(worldpath);
|
SubgameSpec gamespec = findWorldSubgame(worldpath);
|
||||||
|
|
||||||
// Add common mods without dependency handling
|
// Add common mods
|
||||||
|
std::map<std::string, ModSpec> common_mods;
|
||||||
std::vector<std::string> inexistent_common_mods;
|
std::vector<std::string> inexistent_common_mods;
|
||||||
Settings gameconf;
|
Settings gameconf;
|
||||||
if(getGameConfig(gamespec.path, gameconf)){
|
if(getGameConfig(gamespec.path, gameconf)){
|
||||||
@ -225,7 +195,7 @@ ModConfiguration::ModConfiguration(std::string worldpath)
|
|||||||
if(spec.name.empty())
|
if(spec.name.empty())
|
||||||
inexistent_common_mods.push_back(modname);
|
inexistent_common_mods.push_back(modname);
|
||||||
else
|
else
|
||||||
m_sorted_mods.push_back(spec);
|
common_mods.insert(std::make_pair(modname, spec));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,10 +208,11 @@ ModConfiguration::ModConfiguration(std::string worldpath)
|
|||||||
s += " could not be found.";
|
s += " could not be found.";
|
||||||
throw ModError(s);
|
throw ModError(s);
|
||||||
}
|
}
|
||||||
|
addMods(flattenMods(common_mods));
|
||||||
|
|
||||||
// Add all world mods and all game mods
|
// Add all game mods and all world mods
|
||||||
addModsInPath(worldpath + DIR_DELIM + "worldmods");
|
|
||||||
addModsInPath(gamespec.gamemods_path);
|
addModsInPath(gamespec.gamemods_path);
|
||||||
|
addModsInPath(worldpath + DIR_DELIM + "worldmods");
|
||||||
|
|
||||||
// check world.mt file for mods explicitely declared to be
|
// check world.mt file for mods explicitely declared to be
|
||||||
// loaded or not by a load_mod_<modname> = ... line.
|
// loaded or not by a load_mod_<modname> = ... line.
|
||||||
@ -264,7 +235,155 @@ ModConfiguration::ModConfiguration(std::string worldpath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
|
// Collect all mods in gamespec.addon_mods_paths,
|
||||||
i != gamespec.addon_mods_paths.end(); ++i)
|
// excluding those in the set exclude_mod_names
|
||||||
addModsInPathFiltered((*i),exclude_mod_names);
|
std::vector<ModSpec> addon_mods;
|
||||||
|
for(std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin();
|
||||||
|
it_path != gamespec.addon_mods_paths.end(); ++it_path)
|
||||||
|
{
|
||||||
|
std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path));
|
||||||
|
for(std::vector<ModSpec>::iterator it = addon_mods_in_path.begin();
|
||||||
|
it != addon_mods_in_path.end(); ++it)
|
||||||
|
{
|
||||||
|
ModSpec& mod = *it;
|
||||||
|
if(exclude_mod_names.count(mod.name) == 0)
|
||||||
|
addon_mods.push_back(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMods(addon_mods);
|
||||||
|
|
||||||
|
// report on name conflicts
|
||||||
|
if(!m_name_conflicts.empty()){
|
||||||
|
std::string s = "Unresolved name conflicts for mods ";
|
||||||
|
for(std::set<std::string>::const_iterator it = m_name_conflicts.begin();
|
||||||
|
it != m_name_conflicts.end(); ++it)
|
||||||
|
{
|
||||||
|
if(it != m_name_conflicts.begin()) s += ", ";
|
||||||
|
s += std::string("\"") + (*it) + "\"";
|
||||||
|
}
|
||||||
|
s += ".";
|
||||||
|
throw ModError(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the mods in order
|
||||||
|
resolveDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModConfiguration::addModsInPath(std::string path)
|
||||||
|
{
|
||||||
|
addMods(flattenMods(getModsInPath(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
|
||||||
|
{
|
||||||
|
// Maintain a map of all existing m_unsatisfied_mods.
|
||||||
|
// Keys are mod names and values are indices into m_unsatisfied_mods.
|
||||||
|
std::map<std::string, u32> existing_mods;
|
||||||
|
for(u32 i = 0; i < m_unsatisfied_mods.size(); ++i){
|
||||||
|
existing_mods[m_unsatisfied_mods[i].name] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new mods
|
||||||
|
for(int want_from_modpack = 1; want_from_modpack >= 0; --want_from_modpack){
|
||||||
|
// First iteration:
|
||||||
|
// Add all the mods that come from modpacks
|
||||||
|
// Second iteration:
|
||||||
|
// Add all the mods that didn't come from modpacks
|
||||||
|
|
||||||
|
std::set<std::string> seen_this_iteration;
|
||||||
|
|
||||||
|
for(std::vector<ModSpec>::const_iterator it = new_mods.begin();
|
||||||
|
it != new_mods.end(); ++it){
|
||||||
|
const ModSpec &mod = *it;
|
||||||
|
if(mod.part_of_modpack != want_from_modpack)
|
||||||
|
continue;
|
||||||
|
if(existing_mods.count(mod.name) == 0){
|
||||||
|
// GOOD CASE: completely new mod.
|
||||||
|
m_unsatisfied_mods.push_back(mod);
|
||||||
|
existing_mods[mod.name] = m_unsatisfied_mods.size() - 1;
|
||||||
|
}
|
||||||
|
else if(seen_this_iteration.count(mod.name) == 0){
|
||||||
|
// BAD CASE: name conflict in different levels.
|
||||||
|
u32 oldindex = existing_mods[mod.name];
|
||||||
|
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
|
||||||
|
errorstream<<"WARNING: Mod name conflict detected: \""
|
||||||
|
<<mod.name<<"\""<<std::endl
|
||||||
|
<<"Will not load: "<<oldmod.path<<std::endl
|
||||||
|
<<"Overridden by: "<<mod.path<<std::endl;
|
||||||
|
m_unsatisfied_mods[oldindex] = mod;
|
||||||
|
|
||||||
|
// If there was a "VERY BAD CASE" name conflict
|
||||||
|
// in an earlier level, ignore it.
|
||||||
|
m_name_conflicts.erase(mod.name);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// VERY BAD CASE: name conflict in the same level.
|
||||||
|
u32 oldindex = existing_mods[mod.name];
|
||||||
|
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
|
||||||
|
errorstream<<"WARNING: Mod name conflict detected: \""
|
||||||
|
<<mod.name<<"\""<<std::endl
|
||||||
|
<<"Will not load: "<<oldmod.path<<std::endl
|
||||||
|
<<"Will not load: "<<mod.path<<std::endl;
|
||||||
|
m_unsatisfied_mods[oldindex] = mod;
|
||||||
|
m_name_conflicts.insert(mod.name);
|
||||||
|
}
|
||||||
|
seen_this_iteration.insert(mod.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModConfiguration::resolveDependencies()
|
||||||
|
{
|
||||||
|
// Step 1: Compile a list of the mod names we're working with
|
||||||
|
std::set<std::string> modnames;
|
||||||
|
for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
|
||||||
|
it != m_unsatisfied_mods.end(); ++it){
|
||||||
|
modnames.insert((*it).name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: get dependencies (including optional dependencies)
|
||||||
|
// of each mod, split mods into satisfied and unsatisfied
|
||||||
|
std::list<ModSpec> satisfied;
|
||||||
|
std::list<ModSpec> unsatisfied;
|
||||||
|
for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
|
||||||
|
it != m_unsatisfied_mods.end(); ++it){
|
||||||
|
ModSpec mod = *it;
|
||||||
|
mod.unsatisfied_depends = mod.depends;
|
||||||
|
// check which optional dependencies actually exist
|
||||||
|
for(std::set<std::string>::iterator it_optdep = mod.optdepends.begin();
|
||||||
|
it_optdep != mod.optdepends.end(); ++it_optdep){
|
||||||
|
std::string optdep = *it_optdep;
|
||||||
|
if(modnames.count(optdep) != 0)
|
||||||
|
mod.unsatisfied_depends.insert(optdep);
|
||||||
|
}
|
||||||
|
// if a mod has no depends it is initially satisfied
|
||||||
|
if(mod.unsatisfied_depends.empty())
|
||||||
|
satisfied.push_back(mod);
|
||||||
|
else
|
||||||
|
unsatisfied.push_back(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: mods without unmet dependencies can be appended to
|
||||||
|
// the sorted list.
|
||||||
|
while(!satisfied.empty()){
|
||||||
|
ModSpec mod = satisfied.back();
|
||||||
|
m_sorted_mods.push_back(mod);
|
||||||
|
satisfied.pop_back();
|
||||||
|
for(std::list<ModSpec>::iterator it = unsatisfied.begin();
|
||||||
|
it != unsatisfied.end(); ){
|
||||||
|
ModSpec& mod2 = *it;
|
||||||
|
mod2.unsatisfied_depends.erase(mod.name);
|
||||||
|
if(mod2.unsatisfied_depends.empty()){
|
||||||
|
satisfied.push_back(mod2);
|
||||||
|
it = unsatisfied.erase(it);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: write back list of unsatisfied mods
|
||||||
|
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
|
||||||
}
|
}
|
||||||
|
76
src/mods.h
76
src/mods.h
@ -30,6 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
|
||||||
|
|
||||||
class ModError : public std::exception
|
class ModError : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -53,23 +55,32 @@ struct ModSpec
|
|||||||
std::string path;
|
std::string path;
|
||||||
//if normal mod:
|
//if normal mod:
|
||||||
std::set<std::string> depends;
|
std::set<std::string> depends;
|
||||||
|
std::set<std::string> optdepends;
|
||||||
std::set<std::string> unsatisfied_depends;
|
std::set<std::string> unsatisfied_depends;
|
||||||
|
|
||||||
|
bool part_of_modpack;
|
||||||
bool is_modpack;
|
bool is_modpack;
|
||||||
// if modpack:
|
// if modpack:
|
||||||
std::map<std::string,ModSpec> modpack_content;
|
std::map<std::string,ModSpec> modpack_content;
|
||||||
ModSpec(const std::string name_="", const std::string path_="",
|
ModSpec(const std::string name_="", const std::string path_=""):
|
||||||
const std::set<std::string> depends_=std::set<std::string>()):
|
|
||||||
name(name_),
|
name(name_),
|
||||||
path(path_),
|
path(path_),
|
||||||
depends(depends_),
|
depends(),
|
||||||
unsatisfied_depends(depends_),
|
optdepends(),
|
||||||
is_modpack(false),
|
unsatisfied_depends(),
|
||||||
modpack_content()
|
part_of_modpack(false),
|
||||||
|
is_modpack(false),
|
||||||
|
modpack_content()
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::string,ModSpec> getModsInPath(std::string path);
|
// Retrieves depends, optdepends, is_modpack and modpack_content
|
||||||
|
void parseModContents(ModSpec &mod);
|
||||||
|
|
||||||
|
std::map<std::string,ModSpec> getModsInPath(std::string path, bool part_of_modpack = false);
|
||||||
|
|
||||||
|
// If failed, returned modspec has name==""
|
||||||
|
ModSpec findCommonMod(const std::string &modname);
|
||||||
|
|
||||||
// expands modpack contents, but does not replace them.
|
// expands modpack contents, but does not replace them.
|
||||||
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods);
|
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods);
|
||||||
@ -77,10 +88,6 @@ std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mod
|
|||||||
// replaces modpack Modspecs with their content
|
// replaces modpack Modspecs with their content
|
||||||
std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
|
std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
|
||||||
|
|
||||||
// removes Mods mentioned in exclude_mod_names
|
|
||||||
std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
|
|
||||||
std::set<std::string> exclude_mod_names);
|
|
||||||
|
|
||||||
// a ModConfiguration is a subset of installed mods, expected to have
|
// a ModConfiguration is a subset of installed mods, expected to have
|
||||||
// all dependencies fullfilled, so it can be used as a list of mods to
|
// all dependencies fullfilled, so it can be used as a list of mods to
|
||||||
// load when the game starts.
|
// load when the game starts.
|
||||||
@ -89,26 +96,13 @@ class ModConfiguration
|
|||||||
public:
|
public:
|
||||||
ModConfiguration():
|
ModConfiguration():
|
||||||
m_unsatisfied_mods(),
|
m_unsatisfied_mods(),
|
||||||
m_sorted_mods()
|
m_sorted_mods(),
|
||||||
|
m_name_conflicts()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
ModConfiguration(std::string worldpath);
|
ModConfiguration(std::string worldpath);
|
||||||
|
|
||||||
// adds all mods in the given path. used for games, modpacks
|
|
||||||
// and world-specific mods (worldmods-folders)
|
|
||||||
void addModsInPath(std::string path)
|
|
||||||
{
|
|
||||||
addMods(flattenMods(getModsInPath(path)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds all mods in the given path whose name does not appear
|
|
||||||
// in the exclude_mods set.
|
|
||||||
void addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods);
|
|
||||||
|
|
||||||
// adds all mods in the set.
|
|
||||||
void addMods(std::vector<ModSpec> mods);
|
|
||||||
|
|
||||||
// checks if all dependencies are fullfilled.
|
// checks if all dependencies are fullfilled.
|
||||||
bool isConsistent()
|
bool isConsistent()
|
||||||
{
|
{
|
||||||
@ -120,17 +114,27 @@ public:
|
|||||||
return m_sorted_mods;
|
return m_sorted_mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<ModSpec> getUnsatisfiedMods()
|
std::vector<ModSpec> getUnsatisfiedMods()
|
||||||
{
|
{
|
||||||
return m_unsatisfied_mods;
|
return m_unsatisfied_mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// adds all mods in the given path. used for games, modpacks
|
||||||
|
// and world-specific mods (worldmods-folders)
|
||||||
|
void addModsInPath(std::string path);
|
||||||
|
|
||||||
// mods with unmet dependencies. This is a list and not a
|
// adds all mods in the set.
|
||||||
// vector because we want easy removal of elements at every
|
void addMods(std::vector<ModSpec> new_mods);
|
||||||
// position.
|
|
||||||
std::list<ModSpec> m_unsatisfied_mods;
|
// move mods from m_unsatisfied_mods to m_sorted_mods
|
||||||
|
// in an order that satisfies dependencies
|
||||||
|
void resolveDependencies();
|
||||||
|
|
||||||
|
// mods with unmet dependencies. Before dependencies are resolved,
|
||||||
|
// this is where all mods are stored. Afterwards this contains
|
||||||
|
// only the ones with really unsatisfied dependencies.
|
||||||
|
std::vector<ModSpec> m_unsatisfied_mods;
|
||||||
|
|
||||||
// list of mods sorted such that they can be loaded in the
|
// list of mods sorted such that they can be loaded in the
|
||||||
// given order with all dependencies being fullfilled. I.e.,
|
// given order with all dependencies being fullfilled. I.e.,
|
||||||
@ -138,6 +142,16 @@ private:
|
|||||||
// appear earlier in the vector.
|
// appear earlier in the vector.
|
||||||
std::vector<ModSpec> m_sorted_mods;
|
std::vector<ModSpec> m_sorted_mods;
|
||||||
|
|
||||||
|
// set of mod names for which an unresolved name conflict
|
||||||
|
// exists. A name conflict happens when two or more mods
|
||||||
|
// at the same level have the same name but different paths.
|
||||||
|
// Levels (mods in higher levels override mods in lower levels):
|
||||||
|
// 1. common mod in modpack; 2. common mod;
|
||||||
|
// 3. game mod in modpack; 4. game mod;
|
||||||
|
// 5. world mod in modpack; 6. world mod;
|
||||||
|
// 7. addon mod in modpack; 8. addon mod.
|
||||||
|
std::set<std::string> m_name_conflicts;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -76,8 +76,7 @@ bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
|
|||||||
{
|
{
|
||||||
ModNameStorer modnamestorer(L, modname);
|
ModNameStorer modnamestorer(L, modname);
|
||||||
|
|
||||||
if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
|
if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){
|
||||||
"0123456789_")){
|
|
||||||
errorstream<<"Error loading mod \""<<modname
|
errorstream<<"Error loading mod \""<<modname
|
||||||
<<"\": modname does not follow naming conventions: "
|
<<"\": modname does not follow naming conventions: "
|
||||||
<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
|
<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
|
||||||
|
@ -707,11 +707,11 @@ Server::Server(
|
|||||||
|
|
||||||
ModConfiguration modconf(m_path_world);
|
ModConfiguration modconf(m_path_world);
|
||||||
m_mods = modconf.getMods();
|
m_mods = modconf.getMods();
|
||||||
std::list<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
|
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
|
||||||
// complain about mods with unsatisfied dependencies
|
// complain about mods with unsatisfied dependencies
|
||||||
if(!modconf.isConsistent())
|
if(!modconf.isConsistent())
|
||||||
{
|
{
|
||||||
for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
|
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
|
||||||
it != unsatisfied_mods.end(); ++it)
|
it != unsatisfied_mods.end(); ++it)
|
||||||
{
|
{
|
||||||
ModSpec mod = *it;
|
ModSpec mod = *it;
|
||||||
@ -745,7 +745,7 @@ Server::Server(
|
|||||||
for(std::vector<ModSpec>::iterator it = m_mods.begin();
|
for(std::vector<ModSpec>::iterator it = m_mods.begin();
|
||||||
it != m_mods.end(); ++it)
|
it != m_mods.end(); ++it)
|
||||||
load_mod_names.erase((*it).name);
|
load_mod_names.erase((*it).name);
|
||||||
for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
|
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
|
||||||
it != unsatisfied_mods.end(); ++it)
|
it != unsatisfied_mods.end(); ++it)
|
||||||
load_mod_names.erase((*it).name);
|
load_mod_names.erase((*it).name);
|
||||||
if(!load_mod_names.empty())
|
if(!load_mod_names.empty())
|
||||||
@ -2980,12 +2980,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
playersao->setWieldedItem(item);
|
playersao->setWieldedItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If item has node placement prediction, always send the above
|
// If item has node placement prediction, always send the
|
||||||
// node to make sure the client knows what exactly happened
|
// blocks to make sure the client knows what exactly happened
|
||||||
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
|
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
|
||||||
RemoteClient *client = getClient(peer_id);
|
RemoteClient *client = getClient(peer_id);
|
||||||
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
|
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
|
||||||
client->SetBlockNotSent(blockpos);
|
client->SetBlockNotSent(blockpos);
|
||||||
|
v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
|
||||||
|
if(blockpos2 != blockpos){
|
||||||
|
client->SetBlockNotSent(blockpos2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // action == 3
|
} // action == 3
|
||||||
|
|
||||||
@ -5073,6 +5077,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
|
|||||||
getPlayerEffectivePrivs(player->getName()),
|
getPlayerEffectivePrivs(player->getName()),
|
||||||
isSingleplayer());
|
isSingleplayer());
|
||||||
|
|
||||||
|
/* Clean up old HUD elements from previous sessions */
|
||||||
|
player->hud.clear();
|
||||||
|
|
||||||
/* Add object to environment */
|
/* Add object to environment */
|
||||||
m_env->addActiveObject(playersao);
|
m_env->addActiveObject(playersao);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ std::vector<ServerListSpec> getLocal()
|
|||||||
std::string liststring;
|
std::string liststring;
|
||||||
if(fs::PathExists(path))
|
if(fs::PathExists(path))
|
||||||
{
|
{
|
||||||
std::ifstream istream(path.c_str(), std::ios::binary);
|
std::ifstream istream(path.c_str());
|
||||||
if(istream.is_open())
|
if(istream.is_open())
|
||||||
{
|
{
|
||||||
std::ostringstream ostream;
|
std::ostringstream ostream;
|
||||||
|
@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|||||||
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|||||||
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "filesys.h"
|
#include "filesys.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#ifndef SERVER
|
||||||
|
#include "tile.h" // getImagePath
|
||||||
|
#endif
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
|
bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
|
||||||
@ -94,7 +97,12 @@ SubgameSpec findSubgame(const std::string &id)
|
|||||||
std::string game_name = getGameName(game_path);
|
std::string game_name = getGameName(game_path);
|
||||||
if(game_name == "")
|
if(game_name == "")
|
||||||
game_name = id;
|
game_name = id;
|
||||||
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name);
|
std::string menuicon_path;
|
||||||
|
#ifndef SERVER
|
||||||
|
menuicon_path = getImagePath(game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
|
||||||
|
#endif
|
||||||
|
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
|
||||||
|
menuicon_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
SubgameSpec findWorldSubgame(const std::string &world_path)
|
SubgameSpec findWorldSubgame(const std::string &world_path)
|
||||||
|
@ -35,17 +35,20 @@ struct SubgameSpec
|
|||||||
std::string gamemods_path; //path to mods of the game
|
std::string gamemods_path; //path to mods of the game
|
||||||
std::set<std::string> addon_mods_paths; //paths to addon mods for this game
|
std::set<std::string> addon_mods_paths; //paths to addon mods for this game
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string menuicon_path;
|
||||||
|
|
||||||
SubgameSpec(const std::string &id_="",
|
SubgameSpec(const std::string &id_="",
|
||||||
const std::string &path_="",
|
const std::string &path_="",
|
||||||
const std::string &gamemods_path_="",
|
const std::string &gamemods_path_="",
|
||||||
const std::set<std::string> &addon_mods_paths_=std::set<std::string>(),
|
const std::set<std::string> &addon_mods_paths_=std::set<std::string>(),
|
||||||
const std::string &name_=""):
|
const std::string &name_="",
|
||||||
|
const std::string &menuicon_path_=""):
|
||||||
id(id_),
|
id(id_),
|
||||||
path(path_),
|
path(path_),
|
||||||
gamemods_path(gamemods_path_),
|
gamemods_path(gamemods_path_),
|
||||||
addon_mods_paths(addon_mods_paths_),
|
addon_mods_paths(addon_mods_paths_),
|
||||||
name(name_)
|
name(name_),
|
||||||
|
menuicon_path(menuicon_path_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool isValid() const
|
bool isValid() const
|
||||||
|
@ -77,7 +77,7 @@ static bool replace_ext(std::string &path, const char *ext)
|
|||||||
|
|
||||||
If failed, return "".
|
If failed, return "".
|
||||||
*/
|
*/
|
||||||
static std::string getImagePath(std::string path)
|
std::string getImagePath(std::string path)
|
||||||
{
|
{
|
||||||
// A NULL-ended list of possible image extensions
|
// A NULL-ended list of possible image extensions
|
||||||
const char *extensions[] = {
|
const char *extensions[] = {
|
||||||
|
11
src/tile.h
11
src/tile.h
@ -34,6 +34,17 @@ class IGameDef;
|
|||||||
tile.{h,cpp}: Texture handling stuff.
|
tile.{h,cpp}: Texture handling stuff.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find out the full path of an image by trying different filename
|
||||||
|
extensions.
|
||||||
|
|
||||||
|
If failed, return "".
|
||||||
|
|
||||||
|
TODO: Should probably be moved out from here, because things needing
|
||||||
|
this function do not need anything else from this header
|
||||||
|
*/
|
||||||
|
std::string getImagePath(std::string path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets the path to a texture by first checking if the texture exists
|
Gets the path to a texture by first checking if the texture exists
|
||||||
in texture_path and if not, using the data path.
|
in texture_path and if not, using the data path.
|
||||||
|
Loading…
Reference in New Issue
Block a user