Compare commits
15 Commits
0.6.1-alph
...
969d6b3ee7
| Author | SHA1 | Date | |
|---|---|---|---|
| 969d6b3ee7 | |||
| b0de432339 | |||
| 6f7812293e | |||
| 37e28d0fcc | |||
| 954ec6e505 | |||
| b1cd6c6581 | |||
| 248e9cb69a | |||
| caedc293eb | |||
| 2e9a9b810c | |||
| ce4c3e9d93 | |||
| 470509ee52 | |||
| 0ea8b87970 | |||
| b4f54083dc | |||
| f17a11c670 | |||
| adb23d18c6 |
31
README.md
31
README.md
@@ -32,6 +32,12 @@ mkdir build && cd build
|
||||
cmake .. -B .
|
||||
make -j4
|
||||
```
|
||||
or
|
||||
```
|
||||
mkdir build && cd build
|
||||
cmake --build . --config Release -j 10
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## 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)
|
||||
.\build.ps1
|
||||
|
||||
# Skip NDK recompile (Java/assets changed only)
|
||||
.\build.ps1 -NoJava
|
||||
|
||||
# Skip Java recompile (C++ changed only)
|
||||
# Skip C++ compilation (Java/assets changed only)
|
||||
.\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
|
||||
```
|
||||
@@ -155,5 +155,5 @@ set(CompileFiles ../../src/main.cpp
|
||||
add_executable(mcpe_server ${CompileFiles})
|
||||
target_link_libraries(mcpe_server raknet ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_include_directories(mcpe_server PUBLIC
|
||||
"minecraft-pe-0.6.1/src/"
|
||||
"../../src/"
|
||||
)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#if defined(RPI)
|
||||
#define CREATORMODE
|
||||
#endif
|
||||
|
||||
#include "../network/RakNetInstance.h"
|
||||
#include "../network/ClientSideNetworkHandler.h"
|
||||
#include "../network/ServerSideNetworkHandler.h"
|
||||
@@ -113,7 +112,7 @@ static void checkGlError(const char* tag) {
|
||||
}
|
||||
#endif /*GLDEBUG*/
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
/*static*/
|
||||
const char* Minecraft::progressMessages[] = {
|
||||
"Locating server",
|
||||
@@ -451,20 +450,21 @@ void Minecraft::update() {
|
||||
// }
|
||||
//}
|
||||
|
||||
if (pause && level != NULL) {
|
||||
float lastA = timer.a;
|
||||
timer.advanceTime();
|
||||
timer.a = lastA;
|
||||
} else {
|
||||
// If we're paused (local world / invisible server), freeze gameplay and
|
||||
// networking and only keep UI responsive.
|
||||
bool freezeGame = pause;
|
||||
|
||||
if (!freezeGame) {
|
||||
timer.advanceTime();
|
||||
}
|
||||
|
||||
if (raknetInstance) {
|
||||
if (raknetInstance && !freezeGame) {
|
||||
raknetInstance->runEvents(netCallback);
|
||||
}
|
||||
|
||||
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)
|
||||
tick(i, toTick-1);
|
||||
|
||||
@@ -589,7 +589,9 @@ void Minecraft::tick(int nTick, int maxTick) {
|
||||
#endif
|
||||
}
|
||||
TIMER_POP_PUSH("particles");
|
||||
particleEngine->tick();
|
||||
if (!pause) {
|
||||
particleEngine->tick();
|
||||
}
|
||||
if (screen) {
|
||||
screenMutex = true;
|
||||
screen->tick();
|
||||
@@ -1018,6 +1020,17 @@ bool Minecraft::isOnline()
|
||||
}
|
||||
|
||||
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
|
||||
if (screen != NULL) return;
|
||||
screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE);
|
||||
@@ -1070,6 +1083,8 @@ void Minecraft::setScreen( Screen* screen )
|
||||
|
||||
//noRender = false;
|
||||
} else {
|
||||
// Closing a screen and returning to the game should unpause.
|
||||
pause = false;
|
||||
grabMouse();
|
||||
}
|
||||
#endif
|
||||
@@ -1112,6 +1127,7 @@ void Minecraft::init()
|
||||
{
|
||||
options.minecraft = this;
|
||||
options.initDefaultValues();
|
||||
|
||||
#ifndef STANDALONE_SERVER
|
||||
checkGlError("Init enter");
|
||||
|
||||
|
||||
@@ -6,13 +6,21 @@ const char* OptionStrings::Multiplayer_ServerVisible = "mp_server_visible_defa
|
||||
const char* OptionStrings::Graphics_Fancy = "gfx_fancygraphics";
|
||||
const char* OptionStrings::Graphics_LowQuality = "gfx_lowquality";
|
||||
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_InvertMouse = "ctrl_invertmouse";
|
||||
const char* OptionStrings::Controls_UseTouchScreen = "ctrl_usetouchscreen";
|
||||
const char* OptionStrings::Controls_UseTouchJoypad = "ctrl_usetouchjoypad";
|
||||
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::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";
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ public:
|
||||
static const char* Graphics_LowQuality;
|
||||
static const char* Graphics_GUIScale;
|
||||
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_InvertMouse;
|
||||
static const char* Controls_UseTouchScreen;
|
||||
@@ -17,7 +21,12 @@ public:
|
||||
static const char* Controls_IsLefthanded;
|
||||
static const char* Controls_FeedbackVibration;
|
||||
|
||||
static const char* Audio_Music;
|
||||
static const char* Audio_Sound;
|
||||
|
||||
|
||||
static const char* Game_DifficultyLevel;
|
||||
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_CLIENT__OptionsStrings_H__*/
|
||||
|
||||
@@ -135,7 +135,7 @@ const Options::Option
|
||||
Options::Option::ANAGLYPH (6, "options.anaglyph", false, true),
|
||||
Options::Option::LIMIT_FRAMERATE (7, "options.limitFramerate",false, true),
|
||||
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::GUI_SCALE (11, "options.guiScale", false, false),
|
||||
Options::Option::THIRD_PERSON (12, "options.thirdperson", false, true),
|
||||
@@ -207,7 +207,7 @@ void Options::update()
|
||||
if (readFloat(value, sens)) {
|
||||
// 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...
|
||||
sensitivity = 0.3f + std::pow(1.1f * sens, 1.3f) * 0.42f;
|
||||
sensitivity = sens;
|
||||
}
|
||||
}
|
||||
if (key == OptionStrings::Controls_InvertMouse) {
|
||||
@@ -238,13 +238,29 @@ void Options::update()
|
||||
fancyGraphics = false;
|
||||
}
|
||||
}
|
||||
if (key == OptionStrings::Graphics_SmoothLightning) {
|
||||
bool isLow;
|
||||
readBool(value, ambientOcclusion);
|
||||
}
|
||||
|
||||
// Graphics extras
|
||||
if (key == OptionStrings::Graphics_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) {
|
||||
int v;
|
||||
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
|
||||
if (key == OptionStrings::Game_DifficultyLevel) {
|
||||
readInt(value, difficulty);
|
||||
@@ -304,12 +320,12 @@ void Options::load()
|
||||
void Options::save()
|
||||
{
|
||||
StringVector stringVec;
|
||||
|
||||
// Login
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_Username, username);
|
||||
// Game
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_ServerVisible, serverVisible);
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Game_DifficultyLevel, difficulty);
|
||||
|
||||
// Input
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Controls_InvertMouse, invertYMouse);
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Controls_Sensitivity, sensitivity);
|
||||
@@ -317,8 +333,20 @@ void Options::save()
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchScreen, useTouchScreen);
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchJoypad, isJoyTouchArea);
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Controls_FeedbackVibration, destroyVibration);
|
||||
// Graphics
|
||||
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_Vsync, vsync);
|
||||
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 SOUND;
|
||||
|
||||
@@ -275,6 +275,8 @@ public:
|
||||
return limitFramerate;
|
||||
if (item == &Option::VSYNC)
|
||||
return vsync;
|
||||
if (item == &Option::GRAPHICS)
|
||||
return fancyGraphics;
|
||||
if (item == &Option::AMBIENT_OCCLUSION)
|
||||
return ambientOcclusion;
|
||||
if (item == &Option::THIRD_PERSON)
|
||||
|
||||
@@ -78,6 +78,14 @@ void Screen::updateEvents()
|
||||
void Screen::mouseEvent()
|
||||
{
|
||||
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())
|
||||
return;
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ protected:
|
||||
virtual void mouseClicked(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 keyboardNewChar(char inputChar) {}
|
||||
public:
|
||||
|
||||
@@ -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) {
|
||||
this->setContentOffsetWithAnimation(Vec3(x, y, 0), false);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ public:
|
||||
void tick();
|
||||
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);
|
||||
|
||||
void setSelected(int id, bool selected);
|
||||
|
||||
@@ -202,6 +202,25 @@ void IngameBlockSelectionScreen::keyPressed(int eventKey)
|
||||
#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 left = width / 2 - InventoryCols * 10;
|
||||
|
||||
@@ -23,6 +23,9 @@ protected:
|
||||
|
||||
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);
|
||||
private:
|
||||
void renderSlots();
|
||||
|
||||
@@ -243,7 +243,6 @@ void OptionsScreen::generateOptionScreens() {
|
||||
.addOptionItem(&Options::Option::VIEW_BOBBING, minecraft)
|
||||
.addOptionItem(&Options::Option::AMBIENT_OCCLUSION, minecraft)
|
||||
.addOptionItem(&Options::Option::ANAGLYPH, minecraft)
|
||||
.addOptionItem(&Options::Option::LIMIT_FRAMERATE, minecraft)
|
||||
.addOptionItem(&Options::Option::VSYNC, minecraft)
|
||||
.addOptionItem(&Options::Option::MUSIC, minecraft)
|
||||
.addOptionItem(&Options::Option::SOUND, minecraft);
|
||||
|
||||
@@ -356,7 +356,7 @@ void SelectWorldScreen::render( int xm, int ym, float a )
|
||||
worldsList->setComponentSelected(bWorldView.selected);
|
||||
// #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)
|
||||
worldsList->render(xm, ym, a);
|
||||
else {
|
||||
@@ -412,6 +412,28 @@ std::string SelectWorldScreen::getUniqueLevelName( const std::string& level )
|
||||
|
||||
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 )
|
||||
{
|
||||
if (bWorldView.selected) {
|
||||
|
||||
@@ -90,6 +90,9 @@ public:
|
||||
|
||||
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();
|
||||
private:
|
||||
void loadLevelSource();
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
SimpleChooseLevelScreen::SimpleChooseLevelScreen(const std::string& levelName)
|
||||
: bHeader(0),
|
||||
bGamemode(0),
|
||||
bCheats(0),
|
||||
bBack(0),
|
||||
bCreate(0),
|
||||
levelName(levelName),
|
||||
hasChosen(false),
|
||||
gamemode(GameType::Survival),
|
||||
cheatsEnabled(false),
|
||||
tLevelName(0, "World name"),
|
||||
tSeed(1, "World seed")
|
||||
{
|
||||
@@ -26,12 +28,20 @@ SimpleChooseLevelScreen::~SimpleChooseLevelScreen()
|
||||
{
|
||||
if (bHeader) delete bHeader;
|
||||
delete bGamemode;
|
||||
delete bCheats;
|
||||
delete bBack;
|
||||
delete bCreate;
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
// header + close button
|
||||
@@ -48,18 +58,22 @@ void SimpleChooseLevelScreen::init()
|
||||
}
|
||||
if (minecraft->useTouchscreen()) {
|
||||
bGamemode = new Touch::TButton(1, "Survival mode");
|
||||
bCheats = new Touch::TButton(4, "Cheats: Off");
|
||||
bCreate = new Touch::TButton(3, "Create");
|
||||
} else {
|
||||
bGamemode = new Button(1, "Survival mode");
|
||||
bCheats = new Button(4, "Cheats: Off");
|
||||
bCreate = new Button(3, "Create");
|
||||
}
|
||||
|
||||
buttons.push_back(bHeader);
|
||||
buttons.push_back(bBack);
|
||||
buttons.push_back(bGamemode);
|
||||
buttons.push_back(bCheats);
|
||||
buttons.push_back(bCreate);
|
||||
|
||||
tabButtons.push_back(bGamemode);
|
||||
tabButtons.push_back(bCheats);
|
||||
tabButtons.push_back(bBack);
|
||||
tabButtons.push_back(bCreate);
|
||||
|
||||
@@ -94,16 +108,26 @@ void SimpleChooseLevelScreen::setupPositions()
|
||||
tSeed.x = tLevelName.x;
|
||||
tSeed.y = tLevelName.y + 30;
|
||||
|
||||
bGamemode->width = 140;
|
||||
bGamemode->x = centerX - bGamemode->width / 2;
|
||||
// compute vertical centre for gamemode in remaining space
|
||||
const int buttonWidth = 120;
|
||||
const int buttonSpacing = 10;
|
||||
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 availTop = buttonHeight + 20 + 30 + 10; // just below seed
|
||||
int availBottom = height - bottomPad - bCreate->height - 10; // leave some gap before create
|
||||
int availHeight = availBottom - availTop;
|
||||
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;
|
||||
@@ -124,14 +148,14 @@ void SimpleChooseLevelScreen::render( int xm, int ym, float a )
|
||||
renderDirtBackground(0);
|
||||
glEnable2(GL_BLEND);
|
||||
|
||||
const char* str = NULL;
|
||||
const char* modeDesc = NULL;
|
||||
if (gamemode == GameType::Survival) {
|
||||
str = "Mobs, health and gather resources";
|
||||
modeDesc = "Mobs, health and gather resources";
|
||||
} else if (gamemode == GameType::Creative) {
|
||||
str = "Unlimited resources and flying";
|
||||
modeDesc = "Unlimited resources and flying";
|
||||
}
|
||||
if (str) {
|
||||
drawCenteredString(minecraft->font, str, width/2, bGamemode->y + bGamemode->height + 4, 0xffcccccc);
|
||||
if (modeDesc) {
|
||||
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);
|
||||
@@ -188,6 +212,12 @@ void SimpleChooseLevelScreen::buttonClicked( Button* button )
|
||||
return;
|
||||
}
|
||||
|
||||
if (button == bCheats) {
|
||||
cheatsEnabled = !cheatsEnabled;
|
||||
bCheats->msg = cheatsEnabled ? "Cheats: On" : "Cheats: Off";
|
||||
return;
|
||||
}
|
||||
|
||||
if (button == bCreate && !tLevelName.text.empty()) {
|
||||
int seed = getEpochTimeS();
|
||||
if (!tSeed.text.empty()) {
|
||||
@@ -200,7 +230,7 @@ void SimpleChooseLevelScreen::buttonClicked( Button* button )
|
||||
}
|
||||
}
|
||||
std::string levelId = getUniqueLevelName(tLevelName.text);
|
||||
LevelSettings settings(seed, gamemode);
|
||||
LevelSettings settings(seed, gamemode, cheatsEnabled);
|
||||
minecraft->selectLevel(levelId, levelId, settings);
|
||||
minecraft->hostMultiplayer();
|
||||
minecraft->setScreen(new ProgressScreen());
|
||||
|
||||
@@ -29,12 +29,14 @@ public:
|
||||
private:
|
||||
Touch::THeader* bHeader;
|
||||
Button* bGamemode;
|
||||
Button* bCheats;
|
||||
ImageButton* bBack;
|
||||
Button* bCreate;
|
||||
bool hasChosen;
|
||||
|
||||
std::string levelName;
|
||||
int gamemode;
|
||||
bool cheatsEnabled;
|
||||
|
||||
TextBox tLevelName;
|
||||
TextBox tSeed;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "OptionsScreen.h"
|
||||
#include "PauseScreen.h"
|
||||
#include "PrerenderTilesScreen.h" // test button
|
||||
#include "../components/ImageButton.h"
|
||||
|
||||
#include "../../../util/Mth.h"
|
||||
|
||||
@@ -25,7 +26,8 @@
|
||||
StartMenuScreen::StartMenuScreen()
|
||||
: bHost( 2, 0, 0, 160, 24, "Start 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);
|
||||
#endif
|
||||
|
||||
#ifdef DEMO_MODE
|
||||
buttons.push_back(&bBuy);
|
||||
tabButtons.push_back(&bBuy);
|
||||
#endif
|
||||
// add quit button (top right X icon) – match OptionsScreen style
|
||||
{
|
||||
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);
|
||||
// don't include in tab navigation
|
||||
}
|
||||
|
||||
copyright = "\xffMojang AB";//. Do not distribute!";
|
||||
|
||||
@@ -100,8 +110,9 @@ void StartMenuScreen::setupPositions() {
|
||||
bJoin.x = (width - bJoin.width) / 2;
|
||||
bOptions.x = (width - bJoin.width) / 2;
|
||||
|
||||
copyrightPosX = width - minecraft->font->width(copyright) - 1;
|
||||
versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2;
|
||||
// position quit icon at top-right (use image-defined size)
|
||||
bQuit.x = width - bQuit.width;
|
||||
bQuit.y = 0;
|
||||
}
|
||||
|
||||
void StartMenuScreen::tick() {
|
||||
@@ -130,6 +141,10 @@ void StartMenuScreen::buttonClicked(Button* button) {
|
||||
{
|
||||
minecraft->setScreen(new OptionsScreen());
|
||||
}
|
||||
if (button == &bQuit)
|
||||
{
|
||||
minecraft->quit();
|
||||
}
|
||||
}
|
||||
|
||||
bool StartMenuScreen::isInGameScreen() { return false; }
|
||||
@@ -138,6 +153,10 @@ void StartMenuScreen::render( int xm, int ym, float a )
|
||||
{
|
||||
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)
|
||||
TextureId id = minecraft->textures->loadTexture("gui/pi_title.png");
|
||||
#else
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "../Screen.h"
|
||||
#include "../components/Button.h"
|
||||
#include "../components/ImageButton.h"
|
||||
|
||||
class StartMenuScreen: public Screen
|
||||
{
|
||||
@@ -25,6 +26,7 @@ private:
|
||||
Button bHost;
|
||||
Button bJoin;
|
||||
Button bOptions;
|
||||
ImageButton bQuit; // X button in top-right corner
|
||||
|
||||
std::string copyright;
|
||||
int copyrightPosX;
|
||||
|
||||
@@ -33,15 +33,16 @@ void UsernameScreen::setupPositions()
|
||||
int cx = width / 2;
|
||||
int cy = height / 2;
|
||||
|
||||
_btnDone.width = 120;
|
||||
_btnDone.height = 20;
|
||||
// Make the done button match the touch-style option tabs
|
||||
_btnDone.width = 66;
|
||||
_btnDone.height = 26;
|
||||
_btnDone.x = (width - _btnDone.width) / 2;
|
||||
_btnDone.y = height / 2 + 52;
|
||||
|
||||
tUsername.x = _btnDone.x;
|
||||
tUsername.y = _btnDone.y - 60;
|
||||
tUsername.width = 120;
|
||||
tUsername.height = 20;
|
||||
tUsername.x = (width - tUsername.width) / 2;
|
||||
tUsername.y = _btnDone.y - 60;
|
||||
}
|
||||
|
||||
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
|
||||
_btnDone.active = !tUsername.text.empty();
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -30,7 +30,7 @@ protected:
|
||||
virtual void buttonClicked(Button* button);
|
||||
|
||||
private:
|
||||
Button _btnDone;
|
||||
Touch::TButton _btnDone;
|
||||
TextBox tUsername;
|
||||
std::string _input;
|
||||
int _cursorBlink;
|
||||
|
||||
@@ -153,6 +153,11 @@ int IngameBlockSelectionScreen::getSlotPosY(int 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) {
|
||||
_pendingClose = _blockList->_clickArea->isInside((float)x, (float)y);
|
||||
if (!_pendingClose)
|
||||
@@ -166,6 +171,24 @@ void IngameBlockSelectionScreen::mouseReleased(int x, int y, int 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)
|
||||
{
|
||||
Inventory* inventory = minecraft->player->inventory;
|
||||
|
||||
@@ -39,12 +39,16 @@ public:
|
||||
protected:
|
||||
virtual void mouseClicked(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:
|
||||
void renderDemoOverlay();
|
||||
|
||||
//int getLinearSlotId(int x, int y);
|
||||
int getSlotPosX(int slotX);
|
||||
int getSlotPosY(int slotY);
|
||||
int getSlotHeight();
|
||||
|
||||
private:
|
||||
int selectedItem;
|
||||
|
||||
@@ -389,6 +389,26 @@ static char ILLEGAL_FILE_CHARACTERS[] = {
|
||||
'/', '\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()
|
||||
{
|
||||
#if 0
|
||||
|
||||
@@ -97,6 +97,9 @@ public:
|
||||
virtual void buttonClicked(Button* button);
|
||||
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();
|
||||
private:
|
||||
void loadLevelSource();
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace Touch {
|
||||
StartMenuScreen::StartMenuScreen()
|
||||
: bHost( 2, "Start Game"),
|
||||
bJoin( 3, "Join Game"),
|
||||
bOptions( 4, "Options")
|
||||
bOptions( 4, "Options"),
|
||||
bQuit( 5, "")
|
||||
{
|
||||
ImageDef def;
|
||||
bJoin.width = 75;
|
||||
@@ -58,8 +59,18 @@ void StartMenuScreen::init()
|
||||
buttons.push_back(&bHost);
|
||||
buttons.push_back(&bJoin);
|
||||
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(&bJoin);
|
||||
@@ -108,6 +119,10 @@ void StartMenuScreen::setupPositions() {
|
||||
bHost.x = 1*buttonWidth + (int)(2*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;
|
||||
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());
|
||||
}
|
||||
if (button == &bQuit)
|
||||
{
|
||||
minecraft->quit();
|
||||
}
|
||||
}
|
||||
|
||||
bool StartMenuScreen::isInGameScreen() { return false; }
|
||||
@@ -142,6 +161,10 @@ bool StartMenuScreen::isInGameScreen() { return false; }
|
||||
void StartMenuScreen::render( int xm, int ym, float a )
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "../../Screen.h"
|
||||
#include "../../components/LargeImageButton.h"
|
||||
#include "../../components/ImageButton.h"
|
||||
#include "../../components/TextBox.h"
|
||||
|
||||
namespace Touch {
|
||||
@@ -27,6 +28,7 @@ private:
|
||||
LargeImageButton bHost;
|
||||
LargeImageButton bJoin;
|
||||
LargeImageButton bOptions;
|
||||
ImageButton bQuit; // X close icon
|
||||
|
||||
std::string copyright;
|
||||
int copyrightPosX;
|
||||
|
||||
@@ -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);
|
||||
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:
|
||||
if (uMsg == WM_NCDESTROY) g_running = false;
|
||||
else {
|
||||
|
||||
@@ -255,16 +255,16 @@ void Inventory::setupDefault() {
|
||||
} else {
|
||||
#if defined(WIN32)
|
||||
// Survival
|
||||
addItem(new ItemInstance(Item::ironIngot, 64));
|
||||
addItem(new ItemInstance(Item::ironIngot, 34));
|
||||
addItem(new ItemInstance(Tile::stonecutterBench));
|
||||
addItem(new ItemInstance(Tile::workBench));
|
||||
addItem(new ItemInstance(Tile::furnace));
|
||||
addItem(new ItemInstance(Tile::wood, 54));
|
||||
addItem(new ItemInstance(Item::stick, 14));
|
||||
addItem(new ItemInstance(Item::coal, 31));
|
||||
addItem(new ItemInstance(Tile::sand, 6));
|
||||
addItem(new ItemInstance(Item::dye_powder, 23, DyePowderItem::PURPLE));
|
||||
// addItem(new ItemInstance(Item::ironIngot, 64));
|
||||
// addItem(new ItemInstance(Item::ironIngot, 34));
|
||||
// addItem(new ItemInstance(Tile::stonecutterBench));
|
||||
// addItem(new ItemInstance(Tile::workBench));
|
||||
// addItem(new ItemInstance(Tile::furnace));
|
||||
// addItem(new ItemInstance(Tile::wood, 54));
|
||||
// addItem(new ItemInstance(Item::stick, 14));
|
||||
// addItem(new ItemInstance(Item::coal, 31));
|
||||
// addItem(new ItemInstance(Tile::sand, 6));
|
||||
// addItem(new ItemInstance(Item::dye_powder, 23, DyePowderItem::PURPLE));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,13 +14,14 @@ namespace GameType {
|
||||
class LevelSettings
|
||||
{
|
||||
public:
|
||||
LevelSettings(long seed, int gameType)
|
||||
LevelSettings(long seed, int gameType, bool allowCheats = false)
|
||||
: seed(seed),
|
||||
gameType(gameType)
|
||||
gameType(gameType),
|
||||
allowCheats(allowCheats)
|
||||
{
|
||||
}
|
||||
static LevelSettings None() {
|
||||
return LevelSettings(-1,-1);
|
||||
return LevelSettings(-1,-1,false);
|
||||
}
|
||||
|
||||
long getSeed() const {
|
||||
@@ -31,6 +32,10 @@ public:
|
||||
return gameType;
|
||||
}
|
||||
|
||||
bool getAllowCheats() const {
|
||||
return allowCheats;
|
||||
}
|
||||
|
||||
//
|
||||
// Those two should actually not be here
|
||||
// @todo: Move out when we add LevelSettings.cpp :p
|
||||
@@ -53,6 +58,7 @@ public:
|
||||
private:
|
||||
const long seed;
|
||||
const int gameType;
|
||||
const bool allowCheats;
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_WORLD_LEVEL__LevelSettings_H__*/
|
||||
|
||||
@@ -12,8 +12,8 @@ LevelData::LevelData()
|
||||
dimension(Dimension::NORMAL),
|
||||
playerDataVersion(-1),
|
||||
storageVersion(0),
|
||||
gameType(GameType::Default),
|
||||
loadedPlayerTag(NULL)
|
||||
gameType(GameType::Default), spawnMobs(false),
|
||||
allowCheats(false), loadedPlayerTag(NULL)
|
||||
{
|
||||
//LOGI("ctor 1: %p\n", this);
|
||||
spawnMobs = (gameType == GameType::Survival);
|
||||
@@ -21,8 +21,7 @@ LevelData::LevelData()
|
||||
|
||||
LevelData::LevelData( const LevelSettings& settings, const std::string& levelName, int generatorVersion /*= -1*/ )
|
||||
: seed(settings.getSeed()),
|
||||
gameType(settings.getGameType()),
|
||||
levelName(levelName),
|
||||
gameType(settings.getGameType()), allowCheats(settings.getAllowCheats()), levelName(levelName),
|
||||
xSpawn(128),
|
||||
ySpawn(64),
|
||||
zSpawn(128),
|
||||
@@ -62,6 +61,7 @@ LevelData::LevelData( const LevelData& rhs )
|
||||
playerDataVersion(rhs.playerDataVersion),
|
||||
generatorVersion(rhs.generatorVersion),
|
||||
spawnMobs(rhs.spawnMobs),
|
||||
allowCheats(rhs.allowCheats),
|
||||
loadedPlayerTag(NULL),
|
||||
playerData(rhs.playerData)
|
||||
{
|
||||
@@ -84,6 +84,7 @@ LevelData& LevelData::operator=( const LevelData& rhs )
|
||||
time = rhs.time;
|
||||
dimension = rhs.dimension;
|
||||
spawnMobs = rhs.spawnMobs;
|
||||
allowCheats = rhs.allowCheats;
|
||||
playerData = rhs.playerData;
|
||||
playerDataVersion = rhs.playerDataVersion;
|
||||
generatorVersion = rhs.generatorVersion;
|
||||
@@ -161,6 +162,7 @@ void LevelData::setTagData( CompoundTag* tag, CompoundTag* playerTag )
|
||||
if (!tag) return;
|
||||
tag->putLong("RandomSeed", seed);
|
||||
tag->putInt("GameType", gameType);
|
||||
tag->putBoolean("AllowCommands", allowCheats);
|
||||
tag->putInt("SpawnX", xSpawn);
|
||||
tag->putInt("SpawnY", ySpawn);
|
||||
tag->putInt("SpawnZ", zSpawn);
|
||||
@@ -181,6 +183,7 @@ void LevelData::getTagData( const CompoundTag* tag )
|
||||
if (!tag) return;
|
||||
seed = (long)tag->getLong("RandomSeed");
|
||||
gameType = tag->getInt("GameType");
|
||||
allowCheats = tag->getBoolean("AllowCommands");
|
||||
xSpawn = tag->getInt("SpawnX");
|
||||
ySpawn = tag->getInt("SpawnY");
|
||||
zSpawn = tag->getInt("SpawnZ");
|
||||
@@ -362,3 +365,13 @@ void LevelData::setSpawnMobs( bool doSpawn )
|
||||
{
|
||||
spawnMobs = doSpawn;
|
||||
}
|
||||
|
||||
bool LevelData::getAllowCheats() const
|
||||
{
|
||||
return allowCheats;
|
||||
}
|
||||
|
||||
void LevelData::setAllowCheats( bool allow )
|
||||
{
|
||||
allowCheats = allow;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ public:
|
||||
bool getSpawnMobs() const;
|
||||
void setSpawnMobs(bool doSpawn);
|
||||
|
||||
bool getAllowCheats() const;
|
||||
void setAllowCheats(bool allow);
|
||||
|
||||
public:
|
||||
PlayerData playerData;
|
||||
int playerDataVersion;
|
||||
@@ -89,6 +92,7 @@ private:
|
||||
int gameType;
|
||||
int storageVersion;
|
||||
bool spawnMobs;
|
||||
bool allowCheats;
|
||||
//@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.
|
||||
int generatorVersion;
|
||||
|
||||
@@ -165,7 +165,8 @@ void DoorTile::neighborChanged(Level* level, int x, int y, int z, int type) {
|
||||
}
|
||||
if (spawn) {
|
||||
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 {
|
||||
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 {
|
||||
// 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) {
|
||||
level->setTile(x, y, z, 0);
|
||||
if(material == Material::metal) {
|
||||
popResource(level, x, y, z, ItemInstance(Item::door_iron));
|
||||
} else {
|
||||
popResource(level, x, y, z, ItemInstance(Item::door_wood));
|
||||
}
|
||||
// no resource spawn here
|
||||
}
|
||||
if (type > 0 && type != id) {
|
||||
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) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return getCompositeData(level, x, y, z) & C_DIR_MASK;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
int getDir(LevelSource* level, int x, int y, int z);
|
||||
|
||||
Reference in New Issue
Block a user