15 Commits

35 changed files with 406 additions and 76 deletions

View File

@@ -32,6 +32,12 @@ mkdir build && cd build
cmake .. -B . cmake .. -B .
make -j4 make -j4
``` ```
or
```
mkdir build && cd build
cmake --build . --config Release -j 10
```
## Visual Studio ## Visual Studio
1. Open the repository folder in **Visual Studio**. 1. Open the repository folder in **Visual Studio**.
@@ -40,17 +46,28 @@ make -j4
4. Press **Run** (or F5) to build and launch the game. 4. Press **Run** (or F5) to build and launch the game.
## Android ## Android
Download [r14b Android NDK](http://dl.google.com/android/repository/android-ndk-r14b-windows-x86_64.zip) and run `build.ps1`:
``` 1. Download **Android NDK r14b**:
http://dl.google.com/android/repository/android-ndk-r14b-windows-x86_64.zip
2. Extract it to the root of your `C:` drive so the path becomes:
```
C:\android-ndk-r14b
```
3. Run the build script:
```powershell
# Full build (NDK + Java + APK + install) # Full build (NDK + Java + APK + install)
.\build.ps1 .\build.ps1
# Skip NDK recompile (Java/assets changed only) # Skip C++ compilation (Java/assets changed only)
.\build.ps1 -NoJava
# Skip Java recompile (C++ changed only)
.\build.ps1 -NoCpp .\build.ps1 -NoCpp
# Only repackage + install (no recompile at all) # Skip Java compilation (C++ changed only)
.\build.ps1 -NoJava
# Only repackage + install (no compilation)
.\build.ps1 -NoBuild .\build.ps1 -NoBuild
``` ```

View File

@@ -155,5 +155,5 @@ set(CompileFiles ../../src/main.cpp
add_executable(mcpe_server ${CompileFiles}) add_executable(mcpe_server ${CompileFiles})
target_link_libraries(mcpe_server raknet ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(mcpe_server raknet ${CMAKE_THREAD_LIBS_INIT})
target_include_directories(mcpe_server PUBLIC target_include_directories(mcpe_server PUBLIC
"minecraft-pe-0.6.1/src/" "../../src/"
) )

View File

@@ -10,7 +10,6 @@
#if defined(RPI) #if defined(RPI)
#define CREATORMODE #define CREATORMODE
#endif #endif
#include "../network/RakNetInstance.h" #include "../network/RakNetInstance.h"
#include "../network/ClientSideNetworkHandler.h" #include "../network/ClientSideNetworkHandler.h"
#include "../network/ServerSideNetworkHandler.h" #include "../network/ServerSideNetworkHandler.h"
@@ -113,7 +112,7 @@ static void checkGlError(const char* tag) {
} }
#endif /*GLDEBUG*/ #endif /*GLDEBUG*/
} }
#include <fstream>
/*static*/ /*static*/
const char* Minecraft::progressMessages[] = { const char* Minecraft::progressMessages[] = {
"Locating server", "Locating server",
@@ -451,20 +450,21 @@ void Minecraft::update() {
// } // }
//} //}
if (pause && level != NULL) { // If we're paused (local world / invisible server), freeze gameplay and
float lastA = timer.a; // networking and only keep UI responsive.
timer.advanceTime(); bool freezeGame = pause;
timer.a = lastA;
} else { if (!freezeGame) {
timer.advanceTime(); timer.advanceTime();
} }
if (raknetInstance) { if (raknetInstance && !freezeGame) {
raknetInstance->runEvents(netCallback); raknetInstance->runEvents(netCallback);
} }
TIMER_PUSH("tick"); TIMER_PUSH("tick");
int toTick = timer.ticks; int toTick = freezeGame ? 1 : timer.ticks;
if (!freezeGame) timer.ticks = 0;
for (int i = 0; i < toTick; ++i, ++ticks) for (int i = 0; i < toTick; ++i, ++ticks)
tick(i, toTick-1); tick(i, toTick-1);
@@ -589,7 +589,9 @@ void Minecraft::tick(int nTick, int maxTick) {
#endif #endif
} }
TIMER_POP_PUSH("particles"); TIMER_POP_PUSH("particles");
particleEngine->tick(); if (!pause) {
particleEngine->tick();
}
if (screen) { if (screen) {
screenMutex = true; screenMutex = true;
screen->tick(); screen->tick();
@@ -1018,6 +1020,17 @@ bool Minecraft::isOnline()
} }
void Minecraft::pauseGame(bool isBackPaused) { void Minecraft::pauseGame(bool isBackPaused) {
// Only freeze gameplay when running a local server and it is not accepting
// incoming connections (invisible server), which includes typical single-
// player/lobby mode. If the server is visible, the game should keep ticking.
bool canFreeze = false;
if (raknetInstance && raknetInstance->isServer() && netCallback) {
ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback;
if (!ss->allowsIncomingConnections())
canFreeze = true;
}
pause = canFreeze;
#ifndef STANDALONE_SERVER #ifndef STANDALONE_SERVER
if (screen != NULL) return; if (screen != NULL) return;
screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE); screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE);
@@ -1070,6 +1083,8 @@ void Minecraft::setScreen( Screen* screen )
//noRender = false; //noRender = false;
} else { } else {
// Closing a screen and returning to the game should unpause.
pause = false;
grabMouse(); grabMouse();
} }
#endif #endif
@@ -1112,6 +1127,7 @@ void Minecraft::init()
{ {
options.minecraft = this; options.minecraft = this;
options.initDefaultValues(); options.initDefaultValues();
#ifndef STANDALONE_SERVER #ifndef STANDALONE_SERVER
checkGlError("Init enter"); checkGlError("Init enter");

View File

@@ -6,13 +6,21 @@ const char* OptionStrings::Multiplayer_ServerVisible = "mp_server_visible_defa
const char* OptionStrings::Graphics_Fancy = "gfx_fancygraphics"; const char* OptionStrings::Graphics_Fancy = "gfx_fancygraphics";
const char* OptionStrings::Graphics_LowQuality = "gfx_lowquality"; const char* OptionStrings::Graphics_LowQuality = "gfx_lowquality";
const char* OptionStrings::Graphics_Vsync = "gfx_vsync"; const char* OptionStrings::Graphics_Vsync = "gfx_vsync";
const char* OptionStrings::Graphics_GUIScale = "gfx_guiscale"; const char* OptionStrings::Graphics_GUIScale = "gfx_guiscale";
const char* OptionStrings::Graphics_SmoothLightning = "gfx_smoothlightning";
const char* OptionStrings::Graphics_Anaglyph = "gfx_anaglyph";
const char* OptionStrings::Graphics_ViewBobbing = "gfx_viewbobbing";
const char* OptionStrings::Controls_Sensitivity = "ctrl_sensitivity"; const char* OptionStrings::Controls_Sensitivity = "ctrl_sensitivity";
const char* OptionStrings::Controls_InvertMouse = "ctrl_invertmouse"; const char* OptionStrings::Controls_InvertMouse = "ctrl_invertmouse";
const char* OptionStrings::Controls_UseTouchScreen = "ctrl_usetouchscreen"; const char* OptionStrings::Controls_UseTouchScreen = "ctrl_usetouchscreen";
const char* OptionStrings::Controls_UseTouchJoypad = "ctrl_usetouchjoypad"; const char* OptionStrings::Controls_UseTouchJoypad = "ctrl_usetouchjoypad";
const char* OptionStrings::Controls_IsLefthanded = "ctrl_islefthanded"; const char* OptionStrings::Controls_IsLefthanded = "ctrl_islefthanded";
// why it isnt ctrl_feedback_vibration? i dont want touch it because compatibility with older versions
const char* OptionStrings::Controls_FeedbackVibration = "feedback_vibration"; const char* OptionStrings::Controls_FeedbackVibration = "feedback_vibration";
const char* OptionStrings::Game_DifficultyLevel = "game_difficulty"; const char* OptionStrings::Audio_Music = "audio_music";
const char* OptionStrings::Audio_Sound = "audio_sound";
const char* OptionStrings::Game_DifficultyLevel = "game_difficulty";

View File

@@ -10,6 +10,10 @@ public:
static const char* Graphics_LowQuality; static const char* Graphics_LowQuality;
static const char* Graphics_GUIScale; static const char* Graphics_GUIScale;
static const char* Graphics_Vsync; static const char* Graphics_Vsync;
static const char* Graphics_SmoothLightning;
static const char* Graphics_Anaglyph;
static const char* Graphics_ViewBobbing;
static const char* Controls_Sensitivity; static const char* Controls_Sensitivity;
static const char* Controls_InvertMouse; static const char* Controls_InvertMouse;
static const char* Controls_UseTouchScreen; static const char* Controls_UseTouchScreen;
@@ -17,7 +21,12 @@ public:
static const char* Controls_IsLefthanded; static const char* Controls_IsLefthanded;
static const char* Controls_FeedbackVibration; static const char* Controls_FeedbackVibration;
static const char* Audio_Music;
static const char* Audio_Sound;
static const char* Game_DifficultyLevel; static const char* Game_DifficultyLevel;
}; };
#endif /*NET_MINECRAFT_CLIENT__OptionsStrings_H__*/ #endif /*NET_MINECRAFT_CLIENT__OptionsStrings_H__*/

View File

@@ -135,7 +135,7 @@ const Options::Option
Options::Option::ANAGLYPH (6, "options.anaglyph", false, true), Options::Option::ANAGLYPH (6, "options.anaglyph", false, true),
Options::Option::LIMIT_FRAMERATE (7, "options.limitFramerate",false, true), Options::Option::LIMIT_FRAMERATE (7, "options.limitFramerate",false, true),
Options::Option::DIFFICULTY (8, "options.difficulty", false, false), Options::Option::DIFFICULTY (8, "options.difficulty", false, false),
Options::Option::GRAPHICS (9, "options.graphics", false, false), Options::Option::GRAPHICS (9, "options.graphics", false, true),
Options::Option::AMBIENT_OCCLUSION (10, "options.ao", false, true), Options::Option::AMBIENT_OCCLUSION (10, "options.ao", false, true),
Options::Option::GUI_SCALE (11, "options.guiScale", false, false), Options::Option::GUI_SCALE (11, "options.guiScale", false, false),
Options::Option::THIRD_PERSON (12, "options.thirdperson", false, true), Options::Option::THIRD_PERSON (12, "options.thirdperson", false, true),
@@ -207,7 +207,7 @@ void Options::update()
if (readFloat(value, sens)) { if (readFloat(value, sens)) {
// sens is in range [0,1] with default/center at 0.5 (for aesthetics) // sens is in range [0,1] with default/center at 0.5 (for aesthetics)
// We wanna map it to something like [0.3, 0.9] BUT keep 0.5 @ ~0.5... // We wanna map it to something like [0.3, 0.9] BUT keep 0.5 @ ~0.5...
sensitivity = 0.3f + std::pow(1.1f * sens, 1.3f) * 0.42f; sensitivity = sens;
} }
} }
if (key == OptionStrings::Controls_InvertMouse) { if (key == OptionStrings::Controls_InvertMouse) {
@@ -238,13 +238,29 @@ void Options::update()
fancyGraphics = false; fancyGraphics = false;
} }
} }
if (key == OptionStrings::Graphics_SmoothLightning) {
bool isLow;
readBool(value, ambientOcclusion);
}
// Graphics extras // Graphics extras
if (key == OptionStrings::Graphics_Vsync) if (key == OptionStrings::Graphics_Vsync)
readBool(value, vsync); readBool(value, vsync);
if (key == OptionStrings::Graphics_Anaglyph)
readBool(value, anaglyph3d);
if (key == OptionStrings::Graphics_ViewBobbing)
readBool(value, bobView);
if (key == OptionStrings::Graphics_GUIScale) { if (key == OptionStrings::Graphics_GUIScale) {
int v; int v;
if (readInt(value, v)) guiScale = v % 5; if (readInt(value, v)) guiScale = v % 5;
} }
// Audio
if (key == OptionStrings::Audio_Music)
readFloat(value, music);
if (key == OptionStrings::Audio_Sound)
readFloat(value, sound);
// Game // Game
if (key == OptionStrings::Game_DifficultyLevel) { if (key == OptionStrings::Game_DifficultyLevel) {
readInt(value, difficulty); readInt(value, difficulty);
@@ -304,12 +320,12 @@ void Options::load()
void Options::save() void Options::save()
{ {
StringVector stringVec; StringVector stringVec;
// Login // Login
addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_Username, username); addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_Username, username);
// Game // Game
addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_ServerVisible, serverVisible); addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_ServerVisible, serverVisible);
addOptionToSaveOutput(stringVec, OptionStrings::Game_DifficultyLevel, difficulty); addOptionToSaveOutput(stringVec, OptionStrings::Game_DifficultyLevel, difficulty);
// Input // Input
addOptionToSaveOutput(stringVec, OptionStrings::Controls_InvertMouse, invertYMouse); addOptionToSaveOutput(stringVec, OptionStrings::Controls_InvertMouse, invertYMouse);
addOptionToSaveOutput(stringVec, OptionStrings::Controls_Sensitivity, sensitivity); addOptionToSaveOutput(stringVec, OptionStrings::Controls_Sensitivity, sensitivity);
@@ -317,8 +333,20 @@ void Options::save()
addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchScreen, useTouchScreen); addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchScreen, useTouchScreen);
addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchJoypad, isJoyTouchArea); addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchJoypad, isJoyTouchArea);
addOptionToSaveOutput(stringVec, OptionStrings::Controls_FeedbackVibration, destroyVibration); addOptionToSaveOutput(stringVec, OptionStrings::Controls_FeedbackVibration, destroyVibration);
// Graphics
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_Vsync, vsync); addOptionToSaveOutput(stringVec, OptionStrings::Graphics_Vsync, vsync);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_GUIScale, guiScale); addOptionToSaveOutput(stringVec, OptionStrings::Graphics_GUIScale, guiScale);
addOptionToSaveOutput(stringVec, OptionStrings::Game_DifficultyLevel, difficulty);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_Fancy, fancyGraphics);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_SmoothLightning, ambientOcclusion);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_Anaglyph, anaglyph3d);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_ViewBobbing, bobView);
//addOptionToSaveOutput(stringVec, OptionStrings::VIEW_BOBBING, fancyGraphics);
// Audio
addOptionToSaveOutput(stringVec, OptionStrings::Audio_Music, music);
addOptionToSaveOutput(stringVec, OptionStrings::Audio_Sound, sound);
// //
// static const Option MUSIC; // static const Option MUSIC;
// static const Option SOUND; // static const Option SOUND;

View File

@@ -275,6 +275,8 @@ public:
return limitFramerate; return limitFramerate;
if (item == &Option::VSYNC) if (item == &Option::VSYNC)
return vsync; return vsync;
if (item == &Option::GRAPHICS)
return fancyGraphics;
if (item == &Option::AMBIENT_OCCLUSION) if (item == &Option::AMBIENT_OCCLUSION)
return ambientOcclusion; return ambientOcclusion;
if (item == &Option::THIRD_PERSON) if (item == &Option::THIRD_PERSON)

View File

@@ -78,6 +78,14 @@ void Screen::updateEvents()
void Screen::mouseEvent() void Screen::mouseEvent()
{ {
const MouseAction& e = Mouse::getEvent(); const MouseAction& e = Mouse::getEvent();
// forward wheel events to subclasses
if (e.action == MouseAction::ACTION_WHEEL) {
int xm = e.x * width / minecraft->width;
int ym = e.y * height / minecraft->height - 1;
mouseWheel(e.dx, e.dy, xm, ym);
return;
}
if (!e.isButton()) if (!e.isButton())
return; return;

View File

@@ -57,6 +57,9 @@ protected:
virtual void mouseClicked(int x, int y, int buttonNum); virtual void mouseClicked(int x, int y, int buttonNum);
virtual void mouseReleased(int x, int y, int buttonNum); virtual void mouseReleased(int x, int y, int buttonNum);
// mouse wheel movement (dx/dy are wheel deltas, xm/ym are GUI coords)
virtual void mouseWheel(int dx, int dy, int xm, int ym) {}
virtual void keyPressed(int eventKey); virtual void keyPressed(int eventKey);
virtual void keyboardNewChar(char inputChar) {} virtual void keyboardNewChar(char inputChar) {}
public: public:

View File

@@ -548,6 +548,14 @@ void ScrollingPane::stepThroughDecelerationAnimation(bool noAnimation) {
} }
} }
void ScrollingPane::scrollBy(float dx, float dy) {
// adjust the translation offsets fpx/fpy by the requested amount
float nfpx = fpx + dx;
float nfpy = fpy + dy;
// convert back to content offset (fpx = -contentOffset.x)
setContentOffset(Vec3(-nfpx, -nfpy, 0));
}
void ScrollingPane::setContentOffset(float x, float y) { void ScrollingPane::setContentOffset(float x, float y) {
this->setContentOffsetWithAnimation(Vec3(x, y, 0), false); this->setContentOffsetWithAnimation(Vec3(x, y, 0), false);
} }

View File

@@ -51,6 +51,10 @@ public:
void tick(); void tick();
void render(int xm, int ym, float alpha); void render(int xm, int ym, float alpha);
// scroll the content by the given amount (dx horizontal, dy vertical)
// positive values move content downward/rightward
void scrollBy(float dx, float dy);
bool getGridItemFor_slow(int itemIndex, GridItem& out); bool getGridItemFor_slow(int itemIndex, GridItem& out);
void setSelected(int id, bool selected); void setSelected(int id, bool selected);

View File

@@ -202,6 +202,25 @@ void IngameBlockSelectionScreen::keyPressed(int eventKey)
#endif #endif
} }
//------------------------------------------------------------------------------
// wheel support for creative inventory; scroll moves selection vertically
void IngameBlockSelectionScreen::mouseWheel(int dx, int dy, int xm, int ym)
{
if (dy == 0) return;
// just move selection up/down one row; desktop UI doesn't have a pane
int cols = InventoryCols;
int maxIndex = InventorySize - 1;
int idx = selectedItem;
if (dy > 0) {
// wheel up -> previous row
if (idx >= cols) idx -= cols;
} else {
// wheel down -> next row
if (idx + cols <= maxIndex) idx += cols;
}
selectedItem = idx;
}
int IngameBlockSelectionScreen::getSelectedSlot(int x, int y) int IngameBlockSelectionScreen::getSelectedSlot(int x, int y)
{ {
int left = width / 2 - InventoryCols * 10; int left = width / 2 - InventoryCols * 10;

View File

@@ -23,6 +23,9 @@ protected:
virtual void buttonClicked(Button* button); virtual void buttonClicked(Button* button);
// wheel input for creative inventory scrolling
virtual void mouseWheel(int dx, int dy, int xm, int ym) override;
virtual void keyPressed(int eventKey); virtual void keyPressed(int eventKey);
private: private:
void renderSlots(); void renderSlots();

View File

@@ -243,7 +243,6 @@ void OptionsScreen::generateOptionScreens() {
.addOptionItem(&Options::Option::VIEW_BOBBING, minecraft) .addOptionItem(&Options::Option::VIEW_BOBBING, minecraft)
.addOptionItem(&Options::Option::AMBIENT_OCCLUSION, minecraft) .addOptionItem(&Options::Option::AMBIENT_OCCLUSION, minecraft)
.addOptionItem(&Options::Option::ANAGLYPH, minecraft) .addOptionItem(&Options::Option::ANAGLYPH, minecraft)
.addOptionItem(&Options::Option::LIMIT_FRAMERATE, minecraft)
.addOptionItem(&Options::Option::VSYNC, minecraft) .addOptionItem(&Options::Option::VSYNC, minecraft)
.addOptionItem(&Options::Option::MUSIC, minecraft) .addOptionItem(&Options::Option::MUSIC, minecraft)
.addOptionItem(&Options::Option::SOUND, minecraft); .addOptionItem(&Options::Option::SOUND, minecraft);

View File

@@ -356,7 +356,7 @@ void SelectWorldScreen::render( int xm, int ym, float a )
worldsList->setComponentSelected(bWorldView.selected); worldsList->setComponentSelected(bWorldView.selected);
// #ifdef PLATFORM_DESKTOP // #ifdef PLATFORM_DESKTOP
// We should add scrolling with mouse wheel but currently i dont know how to implement it // desktop: render the list normally (mouse wheel handled separately below)
if (_mouseHasBeenUp) if (_mouseHasBeenUp)
worldsList->render(xm, ym, a); worldsList->render(xm, ym, a);
else { else {
@@ -412,6 +412,28 @@ std::string SelectWorldScreen::getUniqueLevelName( const std::string& level )
bool SelectWorldScreen::isInGameScreen() { return true; } bool SelectWorldScreen::isInGameScreen() { return true; }
void SelectWorldScreen::mouseWheel(int dx, int dy, int xm, int ym)
{
if (!worldsList)
return;
if (dy == 0)
return;
int num = worldsList->getNumberOfItems();
int idx = worldsList->selectedItem;
if (dy > 0) {
if (idx > 0) {
idx--;
worldsList->stepLeft();
}
} else {
if (idx < num - 1) {
idx++;
worldsList->stepRight();
}
}
worldsList->selectedItem = idx;
}
void SelectWorldScreen::keyPressed( int eventKey ) void SelectWorldScreen::keyPressed( int eventKey )
{ {
if (bWorldView.selected) { if (bWorldView.selected) {

View File

@@ -90,6 +90,9 @@ public:
void render(int xm, int ym, float a); void render(int xm, int ym, float a);
// mouse wheel scroll (new in desktop implementation)
virtual void mouseWheel(int dx, int dy, int xm, int ym);
bool isInGameScreen(); bool isInGameScreen();
private: private:
void loadLevelSource(); void loadLevelSource();

View File

@@ -12,11 +12,13 @@
SimpleChooseLevelScreen::SimpleChooseLevelScreen(const std::string& levelName) SimpleChooseLevelScreen::SimpleChooseLevelScreen(const std::string& levelName)
: bHeader(0), : bHeader(0),
bGamemode(0), bGamemode(0),
bCheats(0),
bBack(0), bBack(0),
bCreate(0), bCreate(0),
levelName(levelName), levelName(levelName),
hasChosen(false), hasChosen(false),
gamemode(GameType::Survival), gamemode(GameType::Survival),
cheatsEnabled(false),
tLevelName(0, "World name"), tLevelName(0, "World name"),
tSeed(1, "World seed") tSeed(1, "World seed")
{ {
@@ -26,12 +28,20 @@ SimpleChooseLevelScreen::~SimpleChooseLevelScreen()
{ {
if (bHeader) delete bHeader; if (bHeader) delete bHeader;
delete bGamemode; delete bGamemode;
delete bCheats;
delete bBack; delete bBack;
delete bCreate; delete bCreate;
} }
void SimpleChooseLevelScreen::init() void SimpleChooseLevelScreen::init()
{ {
// make sure the base class loads the existing level list; the
// derived screen uses ChooseLevelScreen::getUniqueLevelName(), which
// depends on `levels` being populated. omitting this used to result
// in duplicate IDs ("creating the second world would load the
// first") when the name already existed.
ChooseLevelScreen::init();
tLevelName.text = "New world"; tLevelName.text = "New world";
// header + close button // header + close button
@@ -48,18 +58,22 @@ void SimpleChooseLevelScreen::init()
} }
if (minecraft->useTouchscreen()) { if (minecraft->useTouchscreen()) {
bGamemode = new Touch::TButton(1, "Survival mode"); bGamemode = new Touch::TButton(1, "Survival mode");
bCheats = new Touch::TButton(4, "Cheats: Off");
bCreate = new Touch::TButton(3, "Create"); bCreate = new Touch::TButton(3, "Create");
} else { } else {
bGamemode = new Button(1, "Survival mode"); bGamemode = new Button(1, "Survival mode");
bCheats = new Button(4, "Cheats: Off");
bCreate = new Button(3, "Create"); bCreate = new Button(3, "Create");
} }
buttons.push_back(bHeader); buttons.push_back(bHeader);
buttons.push_back(bBack); buttons.push_back(bBack);
buttons.push_back(bGamemode); buttons.push_back(bGamemode);
buttons.push_back(bCheats);
buttons.push_back(bCreate); buttons.push_back(bCreate);
tabButtons.push_back(bGamemode); tabButtons.push_back(bGamemode);
tabButtons.push_back(bCheats);
tabButtons.push_back(bBack); tabButtons.push_back(bBack);
tabButtons.push_back(bCreate); tabButtons.push_back(bCreate);
@@ -94,16 +108,26 @@ void SimpleChooseLevelScreen::setupPositions()
tSeed.x = tLevelName.x; tSeed.x = tLevelName.x;
tSeed.y = tLevelName.y + 30; tSeed.y = tLevelName.y + 30;
bGamemode->width = 140; const int buttonWidth = 120;
bGamemode->x = centerX - bGamemode->width / 2; const int buttonSpacing = 10;
// compute vertical centre for gamemode in remaining space const int totalButtonWidth = buttonWidth * 2 + buttonSpacing;
bGamemode->width = buttonWidth;
bCheats->width = buttonWidth;
bGamemode->x = centerX - totalButtonWidth / 2;
bCheats->x = bGamemode->x + buttonWidth + buttonSpacing;
// compute vertical centre for buttons in remaining space
{ {
int bottomPad = 20; int bottomPad = 20;
int availTop = buttonHeight + 20 + 30 + 10; // just below seed int availTop = buttonHeight + 20 + 30 + 10; // just below seed
int availBottom = height - bottomPad - bCreate->height - 10; // leave some gap before create int availBottom = height - bottomPad - bCreate->height - 10; // leave some gap before create
int availHeight = availBottom - availTop; int availHeight = availBottom - availTop;
if (availHeight < 0) availHeight = 0; if (availHeight < 0) availHeight = 0;
bGamemode->y = availTop + (availHeight - bGamemode->height) / 2; int y = availTop + (availHeight - bGamemode->height) / 2;
bGamemode->y = y;
bCheats->y = y;
} }
bCreate->width = 100; bCreate->width = 100;
@@ -124,14 +148,14 @@ void SimpleChooseLevelScreen::render( int xm, int ym, float a )
renderDirtBackground(0); renderDirtBackground(0);
glEnable2(GL_BLEND); glEnable2(GL_BLEND);
const char* str = NULL; const char* modeDesc = NULL;
if (gamemode == GameType::Survival) { if (gamemode == GameType::Survival) {
str = "Mobs, health and gather resources"; modeDesc = "Mobs, health and gather resources";
} else if (gamemode == GameType::Creative) { } else if (gamemode == GameType::Creative) {
str = "Unlimited resources and flying"; modeDesc = "Unlimited resources and flying";
} }
if (str) { if (modeDesc) {
drawCenteredString(minecraft->font, str, width/2, bGamemode->y + bGamemode->height + 4, 0xffcccccc); drawCenteredString(minecraft->font, modeDesc, width / 2, bGamemode->y + bGamemode->height + 4, 0xffcccccc);
} }
drawString(minecraft->font, "World name:", tLevelName.x, tLevelName.y - Font::DefaultLineHeight - 2, 0xffcccccc); drawString(minecraft->font, "World name:", tLevelName.x, tLevelName.y - Font::DefaultLineHeight - 2, 0xffcccccc);
@@ -188,6 +212,12 @@ void SimpleChooseLevelScreen::buttonClicked( Button* button )
return; return;
} }
if (button == bCheats) {
cheatsEnabled = !cheatsEnabled;
bCheats->msg = cheatsEnabled ? "Cheats: On" : "Cheats: Off";
return;
}
if (button == bCreate && !tLevelName.text.empty()) { if (button == bCreate && !tLevelName.text.empty()) {
int seed = getEpochTimeS(); int seed = getEpochTimeS();
if (!tSeed.text.empty()) { if (!tSeed.text.empty()) {
@@ -200,7 +230,7 @@ void SimpleChooseLevelScreen::buttonClicked( Button* button )
} }
} }
std::string levelId = getUniqueLevelName(tLevelName.text); std::string levelId = getUniqueLevelName(tLevelName.text);
LevelSettings settings(seed, gamemode); LevelSettings settings(seed, gamemode, cheatsEnabled);
minecraft->selectLevel(levelId, levelId, settings); minecraft->selectLevel(levelId, levelId, settings);
minecraft->hostMultiplayer(); minecraft->hostMultiplayer();
minecraft->setScreen(new ProgressScreen()); minecraft->setScreen(new ProgressScreen());

View File

@@ -29,12 +29,14 @@ public:
private: private:
Touch::THeader* bHeader; Touch::THeader* bHeader;
Button* bGamemode; Button* bGamemode;
Button* bCheats;
ImageButton* bBack; ImageButton* bBack;
Button* bCreate; Button* bCreate;
bool hasChosen; bool hasChosen;
std::string levelName; std::string levelName;
int gamemode; int gamemode;
bool cheatsEnabled;
TextBox tLevelName; TextBox tLevelName;
TextBox tSeed; TextBox tSeed;

View File

@@ -6,6 +6,7 @@
#include "OptionsScreen.h" #include "OptionsScreen.h"
#include "PauseScreen.h" #include "PauseScreen.h"
#include "PrerenderTilesScreen.h" // test button #include "PrerenderTilesScreen.h" // test button
#include "../components/ImageButton.h"
#include "../../../util/Mth.h" #include "../../../util/Mth.h"
@@ -25,7 +26,8 @@
StartMenuScreen::StartMenuScreen() StartMenuScreen::StartMenuScreen()
: bHost( 2, 0, 0, 160, 24, "Start Game"), : bHost( 2, 0, 0, 160, 24, "Start Game"),
bJoin( 3, 0, 0, 160, 24, "Join Game"), bJoin( 3, 0, 0, 160, 24, "Join Game"),
bOptions( 4, 0, 0, 78, 22, "Options") bOptions( 4, 0, 0, 78, 22, "Options"),
bQuit( 5, "")
{ {
} }
@@ -54,10 +56,18 @@ void StartMenuScreen::init()
tabButtons.push_back(&bOptions); tabButtons.push_back(&bOptions);
#endif #endif
#ifdef DEMO_MODE // add quit button (top right X icon) match OptionsScreen style
buttons.push_back(&bBuy); {
tabButtons.push_back(&bBuy); ImageDef def;
#endif def.name = "gui/touchgui.png";
def.width = 34;
def.height = 26;
def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height));
bQuit.setImageDef(def, true);
bQuit.scaleWhenPressed = false;
buttons.push_back(&bQuit);
// don't include in tab navigation
}
copyright = "\xffMojang AB";//. Do not distribute!"; copyright = "\xffMojang AB";//. Do not distribute!";
@@ -100,8 +110,9 @@ void StartMenuScreen::setupPositions() {
bJoin.x = (width - bJoin.width) / 2; bJoin.x = (width - bJoin.width) / 2;
bOptions.x = (width - bJoin.width) / 2; bOptions.x = (width - bJoin.width) / 2;
copyrightPosX = width - minecraft->font->width(copyright) - 1; // position quit icon at top-right (use image-defined size)
versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2; bQuit.x = width - bQuit.width;
bQuit.y = 0;
} }
void StartMenuScreen::tick() { void StartMenuScreen::tick() {
@@ -130,6 +141,10 @@ void StartMenuScreen::buttonClicked(Button* button) {
{ {
minecraft->setScreen(new OptionsScreen()); minecraft->setScreen(new OptionsScreen());
} }
if (button == &bQuit)
{
minecraft->quit();
}
} }
bool StartMenuScreen::isInGameScreen() { return false; } bool StartMenuScreen::isInGameScreen() { return false; }
@@ -138,6 +153,10 @@ void StartMenuScreen::render( int xm, int ym, float a )
{ {
renderBackground(); renderBackground();
// Show current username in the top-left corner
std::string username = minecraft->options.username.empty() ? "unknown" : minecraft->options.username;
drawString(font, std::string("Username: ") + username, 2, 2, 0xffffffff);
#if defined(RPI) #if defined(RPI)
TextureId id = minecraft->textures->loadTexture("gui/pi_title.png"); TextureId id = minecraft->textures->loadTexture("gui/pi_title.png");
#else #else

View File

@@ -3,6 +3,7 @@
#include "../Screen.h" #include "../Screen.h"
#include "../components/Button.h" #include "../components/Button.h"
#include "../components/ImageButton.h"
class StartMenuScreen: public Screen class StartMenuScreen: public Screen
{ {
@@ -25,6 +26,7 @@ private:
Button bHost; Button bHost;
Button bJoin; Button bJoin;
Button bOptions; Button bOptions;
ImageButton bQuit; // X button in top-right corner
std::string copyright; std::string copyright;
int copyrightPosX; int copyrightPosX;

View File

@@ -33,15 +33,16 @@ void UsernameScreen::setupPositions()
int cx = width / 2; int cx = width / 2;
int cy = height / 2; int cy = height / 2;
_btnDone.width = 120; // Make the done button match the touch-style option tabs
_btnDone.height = 20; _btnDone.width = 66;
_btnDone.height = 26;
_btnDone.x = (width - _btnDone.width) / 2; _btnDone.x = (width - _btnDone.width) / 2;
_btnDone.y = height / 2 + 52; _btnDone.y = height / 2 + 52;
tUsername.x = _btnDone.x;
tUsername.y = _btnDone.y - 60;
tUsername.width = 120; tUsername.width = 120;
tUsername.height = 20; tUsername.height = 20;
tUsername.x = (width - tUsername.width) / 2;
tUsername.y = _btnDone.y - 60;
} }
void UsernameScreen::tick() void UsernameScreen::tick()
@@ -58,14 +59,20 @@ void UsernameScreen::keyPressed(int eventKey)
} }
// deliberately do NOT call super::keyPressed — that would close the screen on Escape // deliberately do NOT call super::keyPressed — that would close the screen on Escape
_btnDone.active = !tUsername.text.empty();
Screen::keyPressed(eventKey); Screen::keyPressed(eventKey);
// enable the Done button only when there is some text (and ensure it updates after backspace)
_btnDone.active = !tUsername.text.empty();
} }
void UsernameScreen::keyboardNewChar(char inputChar) void UsernameScreen::keyboardNewChar(char inputChar)
{ {
for (auto* tb : textBoxes) tb->handleChar(inputChar); // limit username length to 12 characters
if (tUsername.text.size() < 12) {
for (auto* tb : textBoxes) tb->handleChar(inputChar);
}
_btnDone.active = !tUsername.text.empty();
} }
void UsernameScreen::mouseClicked(int x, int y, int button) void UsernameScreen::mouseClicked(int x, int y, int button)

View File

@@ -30,7 +30,7 @@ protected:
virtual void buttonClicked(Button* button); virtual void buttonClicked(Button* button);
private: private:
Button _btnDone; Touch::TButton _btnDone;
TextBox tUsername; TextBox tUsername;
std::string _input; std::string _input;
int _cursorBlink; int _cursorBlink;

View File

@@ -153,6 +153,11 @@ int IngameBlockSelectionScreen::getSlotPosY(int slotY) {
return height - 16 - 3 - 22 * 2 - 22 * slotY; return height - 16 - 3 - 22 * 2 - 22 * slotY;
} }
int IngameBlockSelectionScreen::getSlotHeight() {
// same as non-touch implementation
return 22;
}
void IngameBlockSelectionScreen::mouseClicked(int x, int y, int buttonNum) { void IngameBlockSelectionScreen::mouseClicked(int x, int y, int buttonNum) {
_pendingClose = _blockList->_clickArea->isInside((float)x, (float)y); _pendingClose = _blockList->_clickArea->isInside((float)x, (float)y);
if (!_pendingClose) if (!_pendingClose)
@@ -166,6 +171,24 @@ void IngameBlockSelectionScreen::mouseReleased(int x, int y, int buttonNum) {
super::mouseReleased(x, y, buttonNum); super::mouseReleased(x, y, buttonNum);
} }
void IngameBlockSelectionScreen::mouseWheel(int dx, int dy, int xm, int ym)
{
if (dy == 0) return;
if (_blockList) {
float amount = -dy * getSlotHeight();
_blockList->scrollBy(0, amount);
}
int cols = InventoryColumns;
int maxIndex = InventorySize - 1;
int idx = selectedItem;
if (dy > 0) {
if (idx >= cols) idx -= cols;
} else {
if (idx + cols <= maxIndex) idx += cols;
}
selectedItem = idx;
}
bool IngameBlockSelectionScreen::addItem(const InventoryPane* pane, int itemId) bool IngameBlockSelectionScreen::addItem(const InventoryPane* pane, int itemId)
{ {
Inventory* inventory = minecraft->player->inventory; Inventory* inventory = minecraft->player->inventory;

View File

@@ -39,12 +39,16 @@ public:
protected: protected:
virtual void mouseClicked(int x, int y, int buttonNum); virtual void mouseClicked(int x, int y, int buttonNum);
virtual void mouseReleased(int x, int y, int buttonNum); virtual void mouseReleased(int x, int y, int buttonNum);
// also support wheel scrolling
virtual void mouseWheel(int dx, int dy, int xm, int ym) override;
private: private:
void renderDemoOverlay(); void renderDemoOverlay();
//int getLinearSlotId(int x, int y); //int getLinearSlotId(int x, int y);
int getSlotPosX(int slotX); int getSlotPosX(int slotX);
int getSlotPosY(int slotY); int getSlotPosY(int slotY);
int getSlotHeight();
private: private:
int selectedItem; int selectedItem;

View File

@@ -389,6 +389,26 @@ static char ILLEGAL_FILE_CHARACTERS[] = {
'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'
}; };
void SelectWorldScreen::mouseWheel(int dx, int dy, int xm, int ym)
{
if (!worldsList) return;
if (dy == 0) return;
int num = worldsList->getNumberOfItems();
int idx = worldsList->selectedItem;
if (dy > 0) {
if (idx > 0) {
idx--;
worldsList->stepLeft();
}
} else {
if (idx < num - 1) {
idx++;
worldsList->stepRight();
}
}
worldsList->selectedItem = idx;
}
void SelectWorldScreen::tick() void SelectWorldScreen::tick()
{ {
#if 0 #if 0

View File

@@ -97,6 +97,9 @@ public:
virtual void buttonClicked(Button* button); virtual void buttonClicked(Button* button);
virtual void keyPressed(int eventKey); virtual void keyPressed(int eventKey);
// support for mouse wheel when desktop code uses touch variant
virtual void mouseWheel(int dx, int dy, int xm, int ym) override;
bool isInGameScreen(); bool isInGameScreen();
private: private:
void loadLevelSource(); void loadLevelSource();

View File

@@ -30,7 +30,8 @@ namespace Touch {
StartMenuScreen::StartMenuScreen() StartMenuScreen::StartMenuScreen()
: bHost( 2, "Start Game"), : bHost( 2, "Start Game"),
bJoin( 3, "Join Game"), bJoin( 3, "Join Game"),
bOptions( 4, "Options") bOptions( 4, "Options"),
bQuit( 5, "")
{ {
ImageDef def; ImageDef def;
bJoin.width = 75; bJoin.width = 75;
@@ -58,8 +59,18 @@ void StartMenuScreen::init()
buttons.push_back(&bHost); buttons.push_back(&bHost);
buttons.push_back(&bJoin); buttons.push_back(&bJoin);
buttons.push_back(&bOptions); buttons.push_back(&bOptions);
// add quit icon (same look as options header)
{
ImageDef def;
def.name = "gui/touchgui.png";
def.width = 34;
def.height = 26;
def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height));
bQuit.setImageDef(def, true);
bQuit.scaleWhenPressed = false;
buttons.push_back(&bQuit);
}
tabButtons.push_back(&bHost); tabButtons.push_back(&bHost);
tabButtons.push_back(&bJoin); tabButtons.push_back(&bJoin);
@@ -108,6 +119,10 @@ void StartMenuScreen::setupPositions() {
bHost.x = 1*buttonWidth + (int)(2*spacing); bHost.x = 1*buttonWidth + (int)(2*spacing);
bOptions.x = 2*buttonWidth + (int)(3*spacing); bOptions.x = 2*buttonWidth + (int)(3*spacing);
// quit icon top-right (use size assigned in init)
bQuit.x = width - bQuit.width;
bQuit.y = 0;
copyrightPosX = width - minecraft->font->width(copyright) - 1; copyrightPosX = width - minecraft->font->width(copyright) - 1;
versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2; versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2;
} }
@@ -135,6 +150,10 @@ void StartMenuScreen::buttonClicked(::Button* button) {
{ {
minecraft->setScreen(new OptionsScreen()); minecraft->setScreen(new OptionsScreen());
} }
if (button == &bQuit)
{
minecraft->quit();
}
} }
bool StartMenuScreen::isInGameScreen() { return false; } bool StartMenuScreen::isInGameScreen() { return false; }
@@ -142,6 +161,10 @@ bool StartMenuScreen::isInGameScreen() { return false; }
void StartMenuScreen::render( int xm, int ym, float a ) void StartMenuScreen::render( int xm, int ym, float a )
{ {
renderBackground(); renderBackground();
// Show current username in the top-left corner
std::string username = minecraft->options.username.empty() ? "unknown" : minecraft->options.username;
drawString(font, std::string("Username: ") + username, 2, 2, 0xffffffff);
glEnable2(GL_BLEND); glEnable2(GL_BLEND);

View File

@@ -3,6 +3,7 @@
#include "../../Screen.h" #include "../../Screen.h"
#include "../../components/LargeImageButton.h" #include "../../components/LargeImageButton.h"
#include "../../components/ImageButton.h"
#include "../../components/TextBox.h" #include "../../components/TextBox.h"
namespace Touch { namespace Touch {
@@ -27,6 +28,7 @@ private:
LargeImageButton bHost; LargeImageButton bHost;
LargeImageButton bJoin; LargeImageButton bJoin;
LargeImageButton bOptions; LargeImageButton bOptions;
ImageButton bQuit; // X close icon
std::string copyright; std::string copyright;
int copyrightPosX; int copyrightPosX;

View File

@@ -113,6 +113,14 @@ LRESULT WINAPI windowProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
Multitouch::feed(0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); Multitouch::feed(0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
break; break;
} }
case WM_MOUSEWHEEL: {
// wheel delta is multiples of WHEEL_DELTA (120); convert to +/-1
int delta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
short x = GET_X_LPARAM(lParam);
short y = GET_Y_LPARAM(lParam);
Mouse::feed(MouseAction::ACTION_WHEEL, 0, x, y, 0, delta);
break;
}
default: default:
if (uMsg == WM_NCDESTROY) g_running = false; if (uMsg == WM_NCDESTROY) g_running = false;
else { else {

View File

@@ -255,16 +255,16 @@ void Inventory::setupDefault() {
} else { } else {
#if defined(WIN32) #if defined(WIN32)
// Survival // Survival
addItem(new ItemInstance(Item::ironIngot, 64)); // addItem(new ItemInstance(Item::ironIngot, 64));
addItem(new ItemInstance(Item::ironIngot, 34)); // addItem(new ItemInstance(Item::ironIngot, 34));
addItem(new ItemInstance(Tile::stonecutterBench)); // addItem(new ItemInstance(Tile::stonecutterBench));
addItem(new ItemInstance(Tile::workBench)); // addItem(new ItemInstance(Tile::workBench));
addItem(new ItemInstance(Tile::furnace)); // addItem(new ItemInstance(Tile::furnace));
addItem(new ItemInstance(Tile::wood, 54)); // addItem(new ItemInstance(Tile::wood, 54));
addItem(new ItemInstance(Item::stick, 14)); // addItem(new ItemInstance(Item::stick, 14));
addItem(new ItemInstance(Item::coal, 31)); // addItem(new ItemInstance(Item::coal, 31));
addItem(new ItemInstance(Tile::sand, 6)); // addItem(new ItemInstance(Tile::sand, 6));
addItem(new ItemInstance(Item::dye_powder, 23, DyePowderItem::PURPLE)); // addItem(new ItemInstance(Item::dye_powder, 23, DyePowderItem::PURPLE));
#endif #endif
} }
#endif #endif

View File

@@ -14,13 +14,14 @@ namespace GameType {
class LevelSettings class LevelSettings
{ {
public: public:
LevelSettings(long seed, int gameType) LevelSettings(long seed, int gameType, bool allowCheats = false)
: seed(seed), : seed(seed),
gameType(gameType) gameType(gameType),
allowCheats(allowCheats)
{ {
} }
static LevelSettings None() { static LevelSettings None() {
return LevelSettings(-1,-1); return LevelSettings(-1,-1,false);
} }
long getSeed() const { long getSeed() const {
@@ -31,6 +32,10 @@ public:
return gameType; return gameType;
} }
bool getAllowCheats() const {
return allowCheats;
}
// //
// Those two should actually not be here // Those two should actually not be here
// @todo: Move out when we add LevelSettings.cpp :p // @todo: Move out when we add LevelSettings.cpp :p
@@ -53,6 +58,7 @@ public:
private: private:
const long seed; const long seed;
const int gameType; const int gameType;
const bool allowCheats;
}; };
#endif /*NET_MINECRAFT_WORLD_LEVEL__LevelSettings_H__*/ #endif /*NET_MINECRAFT_WORLD_LEVEL__LevelSettings_H__*/

View File

@@ -12,8 +12,8 @@ LevelData::LevelData()
dimension(Dimension::NORMAL), dimension(Dimension::NORMAL),
playerDataVersion(-1), playerDataVersion(-1),
storageVersion(0), storageVersion(0),
gameType(GameType::Default), gameType(GameType::Default), spawnMobs(false),
loadedPlayerTag(NULL) allowCheats(false), loadedPlayerTag(NULL)
{ {
//LOGI("ctor 1: %p\n", this); //LOGI("ctor 1: %p\n", this);
spawnMobs = (gameType == GameType::Survival); spawnMobs = (gameType == GameType::Survival);
@@ -21,8 +21,7 @@ LevelData::LevelData()
LevelData::LevelData( const LevelSettings& settings, const std::string& levelName, int generatorVersion /*= -1*/ ) LevelData::LevelData( const LevelSettings& settings, const std::string& levelName, int generatorVersion /*= -1*/ )
: seed(settings.getSeed()), : seed(settings.getSeed()),
gameType(settings.getGameType()), gameType(settings.getGameType()), allowCheats(settings.getAllowCheats()), levelName(levelName),
levelName(levelName),
xSpawn(128), xSpawn(128),
ySpawn(64), ySpawn(64),
zSpawn(128), zSpawn(128),
@@ -62,6 +61,7 @@ LevelData::LevelData( const LevelData& rhs )
playerDataVersion(rhs.playerDataVersion), playerDataVersion(rhs.playerDataVersion),
generatorVersion(rhs.generatorVersion), generatorVersion(rhs.generatorVersion),
spawnMobs(rhs.spawnMobs), spawnMobs(rhs.spawnMobs),
allowCheats(rhs.allowCheats),
loadedPlayerTag(NULL), loadedPlayerTag(NULL),
playerData(rhs.playerData) playerData(rhs.playerData)
{ {
@@ -84,6 +84,7 @@ LevelData& LevelData::operator=( const LevelData& rhs )
time = rhs.time; time = rhs.time;
dimension = rhs.dimension; dimension = rhs.dimension;
spawnMobs = rhs.spawnMobs; spawnMobs = rhs.spawnMobs;
allowCheats = rhs.allowCheats;
playerData = rhs.playerData; playerData = rhs.playerData;
playerDataVersion = rhs.playerDataVersion; playerDataVersion = rhs.playerDataVersion;
generatorVersion = rhs.generatorVersion; generatorVersion = rhs.generatorVersion;
@@ -161,6 +162,7 @@ void LevelData::setTagData( CompoundTag* tag, CompoundTag* playerTag )
if (!tag) return; if (!tag) return;
tag->putLong("RandomSeed", seed); tag->putLong("RandomSeed", seed);
tag->putInt("GameType", gameType); tag->putInt("GameType", gameType);
tag->putBoolean("AllowCommands", allowCheats);
tag->putInt("SpawnX", xSpawn); tag->putInt("SpawnX", xSpawn);
tag->putInt("SpawnY", ySpawn); tag->putInt("SpawnY", ySpawn);
tag->putInt("SpawnZ", zSpawn); tag->putInt("SpawnZ", zSpawn);
@@ -181,6 +183,7 @@ void LevelData::getTagData( const CompoundTag* tag )
if (!tag) return; if (!tag) return;
seed = (long)tag->getLong("RandomSeed"); seed = (long)tag->getLong("RandomSeed");
gameType = tag->getInt("GameType"); gameType = tag->getInt("GameType");
allowCheats = tag->getBoolean("AllowCommands");
xSpawn = tag->getInt("SpawnX"); xSpawn = tag->getInt("SpawnX");
ySpawn = tag->getInt("SpawnY"); ySpawn = tag->getInt("SpawnY");
zSpawn = tag->getInt("SpawnZ"); zSpawn = tag->getInt("SpawnZ");
@@ -362,3 +365,13 @@ void LevelData::setSpawnMobs( bool doSpawn )
{ {
spawnMobs = doSpawn; spawnMobs = doSpawn;
} }
bool LevelData::getAllowCheats() const
{
return allowCheats;
}
void LevelData::setAllowCheats( bool allow )
{
allowCheats = allow;
}

View File

@@ -72,6 +72,9 @@ public:
bool getSpawnMobs() const; bool getSpawnMobs() const;
void setSpawnMobs(bool doSpawn); void setSpawnMobs(bool doSpawn);
bool getAllowCheats() const;
void setAllowCheats(bool allow);
public: public:
PlayerData playerData; PlayerData playerData;
int playerDataVersion; int playerDataVersion;
@@ -89,6 +92,7 @@ private:
int gameType; int gameType;
int storageVersion; int storageVersion;
bool spawnMobs; bool spawnMobs;
bool allowCheats;
//@note: This version is never written or loaded to disk. The only purpose //@note: This version is never written or loaded to disk. The only purpose
// is to use it in the level generator on server and clients. // is to use it in the level generator on server and clients.
int generatorVersion; int generatorVersion;

View File

@@ -165,7 +165,8 @@ void DoorTile::neighborChanged(Level* level, int x, int y, int z, int type) {
} }
if (spawn) { if (spawn) {
if (!level->isClientSide) { if (!level->isClientSide) {
spawnResources(level, x, y, z, data, 0); // use default chance (1.0) so the drop always occurs
spawnResources(level, x, y, z, data);
} }
} else { } else {
bool signal = level->hasNeighborSignal(x, y, z) || level->hasNeighborSignal(x, y + 1, z); bool signal = level->hasNeighborSignal(x, y, z) || level->hasNeighborSignal(x, y + 1, z);
@@ -174,13 +175,12 @@ void DoorTile::neighborChanged(Level* level, int x, int y, int z, int type) {
} }
} }
} else { } else {
// upper half: removal should not drop a second door. the
// lower half neighbour handler takes care of spawning the item
// whenever the door is broken from either end.
if (level->getTile(x, y - 1, z) != id) { if (level->getTile(x, y - 1, z) != id) {
level->setTile(x, y, z, 0); level->setTile(x, y, z, 0);
if(material == Material::metal) { // no resource spawn here
popResource(level, x, y, z, ItemInstance(Item::door_iron));
} else {
popResource(level, x, y, z, ItemInstance(Item::door_wood));
}
} }
if (type > 0 && type != id) { if (type > 0 && type != id) {
neighborChanged(level, x, y - 1, z, type); neighborChanged(level, x, y - 1, z, type);
@@ -189,7 +189,11 @@ void DoorTile::neighborChanged(Level* level, int x, int y, int z, int type) {
} }
int DoorTile::getResource(int data, Random* random) { int DoorTile::getResource(int data, Random* random) {
if ((data & 8) != 0) return 0; // only the lower half should return a resource ID; the upper half
// itself never drops anything and playerDestroy suppresses spawning
// from the top. This prevents duplicate drops if the bottom half is
// mined.
if ((data & UPPER_BIT) != 0) return 0;
if (material == Material::metal) return Item::door_iron->id; if (material == Material::metal) return Item::door_iron->id;
return Item::door_wood->id; return Item::door_wood->id;
} }
@@ -199,6 +203,14 @@ HitResult DoorTile::clip(Level* level, int xt, int yt, int zt, const Vec3& a, co
return super::clip(level, xt, yt, zt, a, b); return super::clip(level, xt, yt, zt, a, b);
} }
// override to prevent double-dropping when top half is directly mined
void DoorTile::playerDestroy(Level* level, Player* player, int x, int y, int z, int data) {
if ((data & UPPER_BIT) == 0) {
// only let the lower half handle the actual spawning
super::playerDestroy(level, player, x, y, z, data);
}
}
int DoorTile::getDir(LevelSource* level, int x, int y, int z) { int DoorTile::getDir(LevelSource* level, int x, int y, int z) {
return getCompositeData(level, x, y, z) & C_DIR_MASK; return getCompositeData(level, x, y, z) & C_DIR_MASK;
} }

View File

@@ -49,6 +49,9 @@ public:
int getResource(int data, Random* random); int getResource(int data, Random* random);
// override to avoid duplicate drops when upper half is mined directly
void playerDestroy(Level* level, Player* player, int x, int y, int z, int data) override;
HitResult clip(Level* level, int xt, int yt, int zt, const Vec3& a, const Vec3& b); HitResult clip(Level* level, int xt, int yt, int zt, const Vec3& a, const Vec3& b);
int getDir(LevelSource* level, int x, int y, int z); int getDir(LevelSource* level, int x, int y, int z);