26 Commits

Author SHA1 Message Date
754329fc49 Merge branch 'main' of https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1 2026-03-13 23:23:26 +02:00
5385342272 sprint on ctrl 2026-03-13 23:23:25 +02:00
0db3a547c7 Merge branch 'main' of https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1 2026-03-13 22:15:41 +01:00
94f4317b71 Restore cave generation + adjust ore spawn rates 2026-03-13 22:15:30 +01:00
bf2248063d FEAT: F1 (but why jump doesnt work wtf) 2026-03-13 22:21:53 +02:00
13dcf593d8 fck 2026-03-13 19:09:12 +02:00
b547286e53 Merge branch 'main' of https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1 2026-03-13 19:08:27 +02:00
ce5307aa30 FEAT: Tweaks in options 2026-03-13 19:08:27 +02:00
85224c42b7 Skin layers added 2026-03-13 16:56:27 +01:00
450f0d9ec2 Merge branch 'main' of https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1 2026-03-13 16:26:28 +01:00
12d5835711 Watch out! Skins! 2026-03-13 16:26:19 +01:00
969d6b3ee7 Forgot about view bobick 2026-03-13 17:12:55 +02:00
b0de432339 FIX: Settings save 2026-03-13 17:09:39 +02:00
6f7812293e settings almost fixed 2026-03-13 15:57:16 +02:00
37e28d0fcc added a username label to the title screen 2026-03-13 13:22:47 +01:00
954ec6e505 Game now actually pauses when in a local world and when the server is set to be invisible. 2026-03-13 11:25:20 +01:00
b1cd6c6581 Added a dummy cheats toggle to the world creation screen. 2026-03-13 11:04:58 +01:00
248e9cb69a removed survival kit thing, go mining 2026-03-13 09:22:38 +01:00
caedc293eb Merge branch 'main' of https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1 2026-03-13 08:23:20 +01:00
2e9a9b810c Limit username to 12 chars and adjusted the Done button to go more with the rest of the games UI. 2026-03-13 08:22:54 +01:00
ce4c3e9d93 Update README.md 2026-03-13 09:05:59 +02:00
470509ee52 Add "Quit Game" button 2026-03-12 21:42:20 +01:00
0ea8b87970 Prevent duplicate world IDs by loading existing level list before generating a unique name 2026-03-12 21:31:01 +01:00
b4f54083dc Fixed door destroy logic 2026-03-12 19:17:32 +01:00
f17a11c670 Added mouse wheel support to the world selection screen. 2026-03-12 11:02:06 +01:00
adb23d18c6 Update README.md 2026-03-11 20:41:15 +02:00
67 changed files with 1363 additions and 187 deletions

View File

@@ -79,6 +79,8 @@ file(GLOB SERVER_SOURCES
"src/network/command/CommandServer.cpp" "src/network/command/CommandServer.cpp"
"src/platform/CThread.cpp" "src/platform/CThread.cpp"
"src/platform/HttpClient.cpp"
"src/platform/PngLoader.cpp"
"src/platform/time.cpp" "src/platform/time.cpp"
"src/platform/input/Controller.cpp" "src/platform/input/Controller.cpp"

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
``` ```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -148,6 +148,10 @@ options.group.mojang=Login
options.group.game=Game options.group.game=Game
options.group.controls=Controls options.group.controls=Controls
options.group.graphics=Graphics options.group.graphics=Graphics
options.group.tweaks=Tweaks
options.sprint=Sprint
options.barontop=HUD above inventory
options.autojump=Auto Jump
options.thirdperson=Third Person options.thirdperson=Third Person
options.servervisible=Server Visible options.servervisible=Server Visible
options.sensitivity=Sensitivity options.sensitivity=Sensitivity

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

@@ -71,6 +71,7 @@ public:
virtual void saveScreenshot(const std::string& filename, int glWidth, int glHeight) {} virtual void saveScreenshot(const std::string& filename, int glWidth, int glHeight) {}
virtual TextureData loadTexture(const std::string& filename_, bool textureFolder) { return TextureData(); } virtual TextureData loadTexture(const std::string& filename_, bool textureFolder) { return TextureData(); }
virtual TextureData loadTextureFromMemory(const unsigned char* data, size_t size) { return TextureData(); }
virtual void playSound(const std::string& fn, float volume, float pitch) {} virtual void playSound(const std::string& fn, float volume, float pitch) {}

View File

@@ -3,6 +3,8 @@
#include "AppPlatform.h" #include "AppPlatform.h"
#include "platform/log.h" #include "platform/log.h"
#include "platform/HttpClient.h"
#include "platform/PngLoader.h"
#include "client/renderer/gles.h" #include "client/renderer/gles.h"
#include "world/level/storage/FolderMethods.h" #include "world/level/storage/FolderMethods.h"
#include <png.h> #include <png.h>
@@ -11,6 +13,7 @@
#include <sstream> #include <sstream>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <ctime> #include <ctime>
#include "util/StringUtils.h"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@@ -55,10 +58,19 @@ public:
TextureData loadTexture(const std::string& filename_, bool textureFolder) TextureData loadTexture(const std::string& filename_, bool textureFolder)
{ {
// Support fetching PNG textures via HTTP/HTTPS (for skins, etc)
if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) {
std::vector<unsigned char> body;
if (HttpClient::download(filename_, body) && !body.empty()) {
return loadTextureFromMemory(body.data(), body.size());
}
return TextureData();
}
TextureData out; TextureData out;
std::string filename = textureFolder? "data/images/" + filename_ std::string filename = textureFolder? "data/images/" + filename_
: filename_; : filename_;
std::ifstream source(filename.c_str(), std::ios::binary); std::ifstream source(filename.c_str(), std::ios::binary);
if (source) { if (source) {
@@ -107,7 +119,11 @@ public:
} }
} }
std::string getDateString(int s) { TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override {
return loadPngFromMemory(data, size);
}
virtual std::string getDateString(int s) override {
time_t tm = s; time_t tm = s;
char mbstr[100]; char mbstr[100];

View File

@@ -3,8 +3,11 @@
#include "AppPlatform.h" #include "AppPlatform.h"
#include "platform/log.h" #include "platform/log.h"
#include "platform/HttpClient.h"
#include "platform/PngLoader.h"
#include "client/renderer/gles.h" #include "client/renderer/gles.h"
#include "world/level/storage/FolderMethods.h" #include "world/level/storage/FolderMethods.h"
#include "util/StringUtils.h"
#include <png.h> #include <png.h>
#include <cmath> #include <cmath>
#include <fstream> #include <fstream>
@@ -50,10 +53,19 @@ public:
TextureData loadTexture(const std::string& filename_, bool textureFolder) TextureData loadTexture(const std::string& filename_, bool textureFolder)
{ {
// Support fetching PNG textures via HTTP/HTTPS (for skins, etc).
if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) {
std::vector<unsigned char> body;
if (HttpClient::download(filename_, body) && !body.empty()) {
return loadTextureFromMemory(body.data(), body.size());
}
return TextureData();
}
TextureData out; TextureData out;
std::string filename = textureFolder? "data/images/" + filename_ std::string filename = textureFolder? "data/images/" + filename_
: filename_; : filename_;
std::ifstream source(filename.c_str(), std::ios::binary); std::ifstream source(filename.c_str(), std::ios::binary);
if (source) { if (source) {
@@ -102,7 +114,9 @@ public:
} }
} }
std::string getDateString(int s) { TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override {
return loadPngFromMemory(data, size);
}
time_t tm = s; time_t tm = s;
char mbstr[100]; char mbstr[100];

View File

@@ -1,5 +1,8 @@
#include "Minecraft.h" #include "Minecraft.h"
#include "client/player/input/IBuildInput.h" #include "client/player/input/IBuildInput.h"
#include "platform/input/Keyboard.h"
#include "world/item/Item.h"
#include "world/item/ItemInstance.h"
#include <string> #include <string>
#include <cstdlib> #include <cstdlib>
@@ -10,7 +13,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"
@@ -57,6 +59,8 @@
#include "player/input/XperiaPlayInput.h" #include "player/input/XperiaPlayInput.h"
#endif #endif
#include "renderer/Chunk.h"
#include "player/input/MouseTurnInput.h" #include "player/input/MouseTurnInput.h"
#include "../world/entity/MobFactory.h" #include "../world/entity/MobFactory.h"
#include "../world/level/MobSpawner.h" #include "../world/level/MobSpawner.h"
@@ -113,7 +117,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 +455,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 +594,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();
@@ -723,16 +730,29 @@ void Minecraft::tickInput() {
} }
#endif #endif
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
if (key == Keyboard::KEY_LEFT_CTRL) {
player->setSprinting(true);
}
if (key == Keyboard::KEY_E) { if (key == Keyboard::KEY_E) {
screenChooser.setScreen(SCREEN_BLOCKSELECTION); screenChooser.setScreen(SCREEN_BLOCKSELECTION);
} }
if (!screen && key == Keyboard::KEY_T && level) { if (!screen && key == Keyboard::KEY_T && level) {
setScreen(new ConsoleScreen()); setScreen(new ConsoleScreen());
} }
if (!screen && key == Keyboard::KEY_O || key == 250) { if (!screen && key == Keyboard::KEY_O || key == 250) {
releaseMouse(); releaseMouse();
} }
if (key == Keyboard::KEY_F)
options.viewDistance = (options.viewDistance + 1) % 4;
if (key == Keyboard::KEY_F3) {
options.renderDebug = !options.renderDebug;
}
if (key == Keyboard::KEY_F5) { if (key == Keyboard::KEY_F5) {
options.thirdPersonView = !options.thirdPersonView; options.thirdPersonView = !options.thirdPersonView;
/* /*
@@ -741,12 +761,6 @@ void Minecraft::tickInput() {
printf("%d\t%f\n", i, noise.grad2(i, 3, 8)); printf("%d\t%f\n", i, noise.grad2(i, 3, 8));
*/ */
} }
#endif
#if defined(WIN32)
if (key == Keyboard::KEY_F) {
options.isFlying = !options.isFlying;
player->noPhysics = options.isFlying;
}
if (key == Keyboard::KEY_L) if (key == Keyboard::KEY_L)
@@ -820,9 +834,6 @@ void Minecraft::tickInput() {
if (player->inventory->getItem(i)) if (player->inventory->getItem(i))
player->inventory->dropSlot(i, false); player->inventory->dropSlot(i, false);
} }
if (key == Keyboard::KEY_F3) {
options.renderDebug = !options.renderDebug;
}
if (key == Keyboard::KEY_M) { if (key == Keyboard::KEY_M) {
options.difficulty = (options.difficulty == Difficulty::PEACEFUL)? options.difficulty = (options.difficulty == Difficulty::PEACEFUL)?
Difficulty::NORMAL : Difficulty::PEACEFUL; Difficulty::NORMAL : Difficulty::PEACEFUL;
@@ -1018,6 +1029,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 +1092,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 +1136,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,24 @@ 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::Controls_AutoJump = "ctrl_autojump";
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";
const char* OptionStrings::Tweaks_Sprint = "tweaks_sprint";
const char* OptionStrings::Tweaks_BarOnTop = "tweaks_barontop";

View File

@@ -10,14 +10,27 @@ 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;
static const char* Controls_UseTouchJoypad; static const char* Controls_UseTouchJoypad;
static const char* Controls_IsLefthanded; static const char* Controls_IsLefthanded;
static const char* Controls_FeedbackVibration; static const char* Controls_FeedbackVibration;
static const char* Controls_AutoJump;
static const char* Audio_Music;
static const char* Audio_Sound;
static const char* Game_DifficultyLevel; static const char* Game_DifficultyLevel;
static const char* Tweaks_Sprint;
static const char* Tweaks_BarOnTop;
}; };
#endif /*NET_MINECRAFT_CLIENT__OptionsStrings_H__*/ #endif /*NET_MINECRAFT_CLIENT__OptionsStrings_H__*/

View File

@@ -9,6 +9,8 @@
bool Options::debugGl = false; bool Options::debugGl = false;
void Options::initDefaultValues() { void Options::initDefaultValues() {
F1 = false;
difficulty = Difficulty::NORMAL; difficulty = Difficulty::NORMAL;
hideGui = false; hideGui = false;
thirdPersonView = false; thirdPersonView = false;
@@ -23,9 +25,13 @@ void Options::initDefaultValues() {
useMouseForDigging = false; useMouseForDigging = false;
destroyVibration = true; destroyVibration = true;
isLeftHanded = false; isLeftHanded = false;
autoJump = true;
isJoyTouchArea = false; isJoyTouchArea = false;
useSprinting = true;
barOnTop = false;
music = 1; music = 1;
sound = 1; sound = 1;
sensitivity = 0.5f; sensitivity = 0.5f;
@@ -126,27 +132,30 @@ void Options::initDefaultValues() {
} }
const Options::Option const Options::Option
Options::Option::MUSIC (0, "options.music", true, false), Options::Option::MUSIC (0, "options.music", true, false),
Options::Option::SOUND (1, "options.sound", true, false), Options::Option::SOUND (1, "options.sound", true, false),
Options::Option::INVERT_MOUSE (2, "options.invertMouse", false, true), Options::Option::INVERT_MOUSE (2, "options.invertMouse", false, true),
Options::Option::SENSITIVITY (3, "options.sensitivity", true, false), Options::Option::SENSITIVITY (3, "options.sensitivity", true, false),
Options::Option::RENDER_DISTANCE (4, "options.renderDistance",false, false), Options::Option::RENDER_DISTANCE (4, "options.renderDistance",false, false),
Options::Option::VIEW_BOBBING (5, "options.viewBobbing", false, true), Options::Option::VIEW_BOBBING (5, "options.viewBobbing", false, true),
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),
Options::Option::HIDE_GUI (13, "options.hidegui", false, true), Options::Option::HIDE_GUI (13, "options.hidegui", false, true),
Options::Option::SERVER_VISIBLE (14, "options.servervisible", false, true), Options::Option::SERVER_VISIBLE (14, "options.servervisible", false, true),
Options::Option::LEFT_HANDED (15, "options.lefthanded", false, true), Options::Option::LEFT_HANDED (15, "options.lefthanded", false, true),
Options::Option::USE_TOUCHSCREEN (16, "options.usetouchscreen", false, true), Options::Option::USE_TOUCHSCREEN (16, "options.usetouchscreen", false, true),
Options::Option::USE_TOUCH_JOYPAD (17, "options.usetouchpad", false, true), Options::Option::USE_TOUCH_JOYPAD (17, "options.usetouchpad", false, true),
Options::Option::DESTROY_VIBRATION (18, "options.destroyvibration", false, true), Options::Option::DESTROY_VIBRATION (18, "options.destroyvibration", false, true),
Options::Option::PIXELS_PER_MILLIMETER(19, "options.pixelspermilimeter", true, false), Options::Option::PIXELS_PER_MILLIMETER(19, "options.pixelspermilimeter", true, false),
Options::Option::VSYNC (20, "options.vsync", false, true); Options::Option::VSYNC (20, "options.vsync", false, true),
Options::Option::SPRINTING (21, "options.sprint", false, true),
Options::Option::BARONTOP (22, "options.barontop", false, true),
Options::Option::AUTOJUMP (23, "options.autojump", false, true);
/* private */ /* private */
const float Options::SOUND_MIN_VALUE = 0.0f; const float Options::SOUND_MIN_VALUE = 0.0f;
@@ -207,7 +216,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) {
@@ -216,6 +225,9 @@ void Options::update()
if (key == OptionStrings::Controls_IsLefthanded) { if (key == OptionStrings::Controls_IsLefthanded) {
readBool(value, isLeftHanded); readBool(value, isLeftHanded);
} }
if (key == OptionStrings::Controls_AutoJump) {
readBool(value, autoJump);
}
if (key == OptionStrings::Controls_UseTouchJoypad) { if (key == OptionStrings::Controls_UseTouchJoypad) {
readBool(value, isJoyTouchArea); readBool(value, isJoyTouchArea);
if (!minecraft->useTouchscreen()) if (!minecraft->useTouchscreen())
@@ -238,13 +250,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);
@@ -252,6 +280,14 @@ void Options::update()
if (difficulty != Difficulty::PEACEFUL && difficulty != Difficulty::NORMAL) if (difficulty != Difficulty::PEACEFUL && difficulty != Difficulty::NORMAL)
difficulty = Difficulty::NORMAL; difficulty = Difficulty::NORMAL;
} }
// Enchancements
if (key == OptionStrings::Tweaks_Sprint) {
readBool(value, useSprinting);
}
if (key == OptionStrings::Tweaks_BarOnTop) {
readBool(value, barOnTop);
}
} }
#ifdef __APPLE__ #ifdef __APPLE__
@@ -304,8 +340,10 @@ 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);
@@ -317,8 +355,24 @@ 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);
addOptionToSaveOutput(stringVec, OptionStrings::Controls_AutoJump, autoJump);
// 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);
// Audio
addOptionToSaveOutput(stringVec, OptionStrings::Audio_Music, music);
addOptionToSaveOutput(stringVec, OptionStrings::Audio_Sound, sound);
// Tweaks
addOptionToSaveOutput(stringVec, OptionStrings::Tweaks_Sprint, useSprinting);
addOptionToSaveOutput(stringVec, OptionStrings::Tweaks_BarOnTop, barOnTop);
// //
// static const Option MUSIC; // static const Option MUSIC;
// static const Option SOUND; // static const Option SOUND;

View File

@@ -30,6 +30,7 @@ public:
static const Option SOUND; static const Option SOUND;
static const Option INVERT_MOUSE; static const Option INVERT_MOUSE;
static const Option SENSITIVITY; static const Option SENSITIVITY;
static const Option AUTOJUMP;
static const Option RENDER_DISTANCE; static const Option RENDER_DISTANCE;
static const Option VIEW_BOBBING; static const Option VIEW_BOBBING;
static const Option ANAGLYPH; static const Option ANAGLYPH;
@@ -49,6 +50,8 @@ public:
static const Option PIXELS_PER_MILLIMETER; static const Option PIXELS_PER_MILLIMETER;
static const Option VSYNC; static const Option VSYNC;
static const Option SPRINTING;
static const Option BARONTOP;
/* /*
static Option* getItem(int id) { static Option* getItem(int id) {
@@ -105,6 +108,8 @@ private:
public: public:
static bool debugGl; static bool debugGl;
bool F1;
float music; float music;
float sound; float sound;
//note: sensitivity is transformed in Options::update //note: sensitivity is transformed in Options::update
@@ -120,6 +125,7 @@ public:
bool useMouseForDigging; bool useMouseForDigging;
bool isLeftHanded; bool isLeftHanded;
bool destroyVibration; bool destroyVibration;
bool autoJump;
//std::string skin; //std::string skin;
KeyMapping keyUp; KeyMapping keyUp;
@@ -163,6 +169,10 @@ public:
bool isJoyTouchArea; bool isJoyTouchArea;
bool useTouchScreen; bool useTouchScreen;
float pixelsPerMillimeter; float pixelsPerMillimeter;
bool useSprinting;
bool barOnTop;
Options(Minecraft* minecraft, const std::string& workingDirectory) Options(Minecraft* minecraft, const std::string& workingDirectory)
: minecraft(minecraft) : minecraft(minecraft)
{ {
@@ -246,6 +256,10 @@ public:
ambientOcclusion = !ambientOcclusion; ambientOcclusion = !ambientOcclusion;
//minecraft->levelRenderer.allChanged(); //minecraft->levelRenderer.allChanged();
} }
if (option == &Option::SPRINTING) useSprinting = !useSprinting;
if (option == &Option::BARONTOP) barOnTop = !barOnTop;
if (option == &Option::AUTOJUMP) autoJump = !autoJump;
notifyOptionUpdate(option, getBooleanValue(option)); notifyOptionUpdate(option, getBooleanValue(option));
save(); save();
} }
@@ -275,6 +289,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)
@@ -287,12 +303,18 @@ public:
return serverVisible; return serverVisible;
if (item == &Option::LEFT_HANDED) if (item == &Option::LEFT_HANDED)
return isLeftHanded; return isLeftHanded;
if (item == &Option::AUTOJUMP)
return autoJump;
if (item == &Option::USE_TOUCHSCREEN) if (item == &Option::USE_TOUCHSCREEN)
return useTouchScreen; return useTouchScreen;
if (item == &Option::USE_TOUCH_JOYPAD) if (item == &Option::USE_TOUCH_JOYPAD)
return isJoyTouchArea; return isJoyTouchArea;
if (item == &Option::DESTROY_VIBRATION) if (item == &Option::DESTROY_VIBRATION)
return destroyVibration; return destroyVibration;
if (item == &Option::SPRINTING)
return useSprinting;
if (item == &Option::BARONTOP)
return barOnTop;
return false; return false;
} }

View File

@@ -1,6 +1,7 @@
#include "Gui.h" #include "Gui.h"
#include "Font.h" #include "Font.h"
#include "client/Options.h" #include "client/Options.h"
#include "platform/input/Keyboard.h"
#include "screens/IngameBlockSelectionScreen.h" #include "screens/IngameBlockSelectionScreen.h"
#include "../Minecraft.h" #include "../Minecraft.h"
#include "../player/LocalPlayer.h" #include "../player/LocalPlayer.h"
@@ -87,14 +88,16 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) {
// F: 3 // F: 3
int ySlot = screenHeight - 16 - 3; int ySlot = screenHeight - 16 - 3;
if (minecraft->gameMode->canHurtPlayer()) { if (!minecraft->options.F1) {
minecraft->textures->loadAndBindTexture("gui/icons.png"); if (minecraft->gameMode->canHurtPlayer()) {
Tesselator& t = Tesselator::instance; minecraft->textures->loadAndBindTexture("gui/icons.png");
t.beginOverride(); Tesselator& t = Tesselator::instance;
t.colorABGR(0xffffffff); t.beginOverride();
renderHearts(); t.colorABGR(0xffffffff);
renderBubbles(); renderHearts();
t.endOverrideAndDraw(); renderBubbles();
t.endOverrideAndDraw();
}
} }
if(minecraft->player->getSleepTimer() > 0) { if(minecraft->player->getSleepTimer() > 0) {
@@ -106,7 +109,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) {
glEnable(GL_ALPHA_TEST); glEnable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }
if (!minecraft->options.F1) {
renderToolBar(a, ySlot, screenWidth); renderToolBar(a, ySlot, screenWidth);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@@ -122,6 +125,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) {
if (minecraft->options.renderDebug) if (minecraft->options.renderDebug)
renderDebugInfo(); renderDebugInfo();
#endif #endif
}
glDisable(GL_BLEND); glDisable(GL_BLEND);
glEnable2(GL_ALPHA_TEST); glEnable2(GL_ALPHA_TEST);
@@ -201,6 +205,10 @@ void Gui::handleClick(int button, int x, int y) {
void Gui::handleKeyPressed(int key) void Gui::handleKeyPressed(int key)
{ {
if (key == Keyboard::KEY_F1) {
minecraft->options.F1 = !minecraft->options.F1;
}
if (key == 99) if (key == 99)
{ {
if (minecraft->player->inventory->selected > 0) if (minecraft->player->inventory->selected > 0)
@@ -516,7 +524,7 @@ void Gui::renderProgressIndicator( const bool isTouchInterface, const int screen
ItemInstance* currentItem = minecraft->player->inventory->getSelected(); ItemInstance* currentItem = minecraft->player->inventory->getSelected();
bool bowEquipped = currentItem != NULL ? currentItem->getItem() == Item::bow : false; bool bowEquipped = currentItem != NULL ? currentItem->getItem() == Item::bow : false;
bool itemInUse = currentItem != NULL ? currentItem->getItem() == minecraft->player->getUseItem()->getItem() : false; bool itemInUse = currentItem != NULL ? currentItem->getItem() == minecraft->player->getUseItem()->getItem() : false;
if (!isTouchInterface || minecraft->options.isJoyTouchArea || (bowEquipped && itemInUse)) { if ((!isTouchInterface || minecraft->options.isJoyTouchArea || (bowEquipped && itemInUse)) && !minecraft->options.F1) {
minecraft->textures->loadAndBindTexture("gui/icons.png"); minecraft->textures->loadAndBindTexture("gui/icons.png");
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc2(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR); glBlendFunc2(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
@@ -585,11 +593,14 @@ void Gui::renderHearts() {
int oh = minecraft->player->lastHealth; int oh = minecraft->player->lastHealth;
random.setSeed(tickCount * 312871); random.setSeed(tickCount * 312871);
int xx = 2;//screenWidth / 2 - getNumSlots() * 10; int screenWidth = (int)(minecraft->width * InvGuiScale);
int screenHeight = (int)(minecraft->height * InvGuiScale);
int xx = (minecraft->options.barOnTop) ? screenWidth / 2 - getNumSlots() * 10 - 1 : 2;
int armor = minecraft->player->getArmorValue(); int armor = minecraft->player->getArmorValue();
for (int i = 0; i < Player::MAX_HEALTH / 2; i++) { for (int i = 0; i < Player::MAX_HEALTH / 2; i++) {
int yo = 2; int yo = (minecraft->options.barOnTop) ? screenHeight - 32 : 2;
int ip2 = i + i + 1; int ip2 = i + i + 1;
if (armor > 0) { if (armor > 0) {
@@ -617,11 +628,15 @@ void Gui::renderHearts() {
void Gui::renderBubbles() { void Gui::renderBubbles() {
if (minecraft->player->isUnderLiquid(Material::water)) { if (minecraft->player->isUnderLiquid(Material::water)) {
int yo = 12; int screenWidth = (int)(minecraft->width * InvGuiScale);
int screenHeight = (int)(minecraft->height * InvGuiScale);
int xx = (minecraft->options.barOnTop) ? screenWidth / 2 - getNumSlots() * 10 - 1 : 2;
int yo = (minecraft->options.barOnTop) ? screenHeight - 42 : 12;
int count = (int) std::ceil((minecraft->player->airSupply - 2) * 10.0f / Player::TOTAL_AIR_SUPPLY); int count = (int) std::ceil((minecraft->player->airSupply - 2) * 10.0f / Player::TOTAL_AIR_SUPPLY);
int extra = (int) std::ceil((minecraft->player->airSupply) * 10.0f / Player::TOTAL_AIR_SUPPLY) - count; int extra = (int) std::ceil((minecraft->player->airSupply) * 10.0f / Player::TOTAL_AIR_SUPPLY) - count;
for (int i = 0; i < count + extra; i++) { for (int i = 0; i < count + extra; i++) {
int xo = i * 8 + 2; int xo = i * 8 + xx;
if (i < count) blit(xo, yo, 16, 9 * 2, 9, 9); if (i < count) blit(xo, yo, 16, 9 * 2, 9, 9);
else blit(xo, yo, 16 + 9, 9 * 2, 9, 9); else blit(xo, yo, 16 + 9, 9 * 2, 9, 9);
} }

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

@@ -76,6 +76,7 @@ void OptionsScreen::init() {
categoryButtons.push_back(new Touch::TButton(3, "Game")); categoryButtons.push_back(new Touch::TButton(3, "Game"));
categoryButtons.push_back(new Touch::TButton(4, "Controls")); categoryButtons.push_back(new Touch::TButton(4, "Controls"));
categoryButtons.push_back(new Touch::TButton(5, "Graphics")); categoryButtons.push_back(new Touch::TButton(5, "Graphics"));
categoryButtons.push_back(new Touch::TButton(6, "Tweaks"));
btnChangeUsername = new Touch::TButton(10, "Username"); btnChangeUsername = new Touch::TButton(10, "Username");
btnCredits = new Touch::TButton(11, "Credits"); btnCredits = new Touch::TButton(11, "Credits");
@@ -221,6 +222,7 @@ void OptionsScreen::generateOptionScreens() {
optionPanes.push_back(new OptionsPane()); optionPanes.push_back(new OptionsPane());
optionPanes.push_back(new OptionsPane()); optionPanes.push_back(new OptionsPane());
optionPanes.push_back(new OptionsPane()); optionPanes.push_back(new OptionsPane());
optionPanes.push_back(new OptionsPane());
// Login Pane // Login Pane
optionPanes[0]->createOptionsGroup("options.group.mojang") optionPanes[0]->createOptionsGroup("options.group.mojang")
@@ -235,7 +237,8 @@ void OptionsScreen::generateOptionScreens() {
// Controls Pane // Controls Pane
optionPanes[2]->createOptionsGroup("options.group.controls") optionPanes[2]->createOptionsGroup("options.group.controls")
.addOptionItem(&Options::Option::INVERT_MOUSE, minecraft); .addOptionItem(&Options::Option::INVERT_MOUSE, minecraft)
.addOptionItem(&Options::Option::AUTOJUMP, minecraft);
// Graphics Pane // Graphics Pane
optionPanes[3]->createOptionsGroup("options.group.graphics") optionPanes[3]->createOptionsGroup("options.group.graphics")
@@ -243,10 +246,14 @@ 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);
// Tweaks Pane
optionPanes[4]->createOptionsGroup("options.group.tweaks")
.addOptionItem(&Options::Option::SPRINTING, minecraft)
.addOptionItem(&Options::Option::BARONTOP, minecraft);
} }
void OptionsScreen::mouseClicked(int x, int y, int buttonNum) { void OptionsScreen::mouseClicked(int x, int y, int buttonNum) {

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

@@ -4,34 +4,46 @@
#include "../../world/entity/player/Player.h" #include "../../world/entity/player/Player.h"
#include "../../world/entity/player/Inventory.h" #include "../../world/entity/player/Inventory.h"
HumanoidModel::HumanoidModel( float g /*= 0*/, float yOffset /*= 0*/ ) HumanoidModel::HumanoidModel( float g /*= 0*/, float yOffset /*= 0*/, int texW /*= 64*/, int texH /*= 32*/ )
: holdingLeftHand(false), : holdingLeftHand(false),
holdingRightHand(false), holdingRightHand(false),
sneaking(false), sneaking(false),
bowAndArrow(false), bowAndArrow(false),
head(0, 0), head(0, 0),
hair(32, 0),
//ear (24, 0), //ear (24, 0),
//hair(32, 0),
body(16, 16), body(16, 16),
arm0(24 + 16, 16), arm0(24 + 16, 16),
arm1(24 + 16, 16), arm1(24 + 16, 16),
leg0(0, 16), leg0(0, 16),
leg1(0, 16) leg1(0, 16)
{ {
texWidth = texW;
texHeight = texH;
head.setModel(this); head.setModel(this);
hair.setModel(this);
body.setModel(this); body.setModel(this);
arm0.setModel(this); arm0.setModel(this);
arm1.setModel(this); arm1.setModel(this);
leg0.setModel(this); leg0.setModel(this);
leg1.setModel(this); leg1.setModel(this);
// If the texture is 64x64, use the modern skin layout for arms/legs and add overlay layers.
bool modernSkin = (texWidth == 64 && texHeight == 64);
if (modernSkin) {
// Left arm and left leg are located in the bottom half of a 64x64 skin.
arm1.texOffs(32, 48);
leg1.texOffs(16, 48);
}
head.addBox(-4, -8, -4, 8, 8, 8, g); // Head head.addBox(-4, -8, -4, 8, 8, 8, g); // Head
head.setPos(0, 0 + yOffset, 0); head.setPos(0, 0 + yOffset, 0);
//ear.addBox(-3, -6, -1, 6, 6, 1, g); // Ear if (modernSkin) {
hair.addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Outer head layer (hat)
//hair.addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Head hair.setPos(0, 0 + yOffset, 0);
// hair.setPos(0, 0 + yOffset, 0); }
body.addBox(-4, 0, -2, 8, 12, 4, g); // Body body.addBox(-4, 0, -2, 8, 12, 4, g); // Body
body.setPos(0, 0 + yOffset, 0); body.setPos(0, 0 + yOffset, 0);
@@ -49,6 +61,24 @@ HumanoidModel::HumanoidModel( float g /*= 0*/, float yOffset /*= 0*/ )
leg1.mirror = true; leg1.mirror = true;
leg1.addBox(-2, 0, -2, 4, 12, 4, g); // Leg1 leg1.addBox(-2, 0, -2, 4, 12, 4, g); // Leg1
leg1.setPos(2, 12 + yOffset, 0); leg1.setPos(2, 12 + yOffset, 0);
if (modernSkin) {
// Overlay layers for 64x64 skins (same geometry, different texture regions)
body.texOffs(16, 32);
body.addBox(-4, 0, -2, 8, 12, 4, g + 0.5f);
arm0.texOffs(40, 32);
arm0.addBox(-3, -2, -2, 4, 12, 4, g + 0.5f);
arm1.texOffs(48, 48);
arm1.addBox(-1, -2, -2, 4, 12, 4, g + 0.5f);
leg0.texOffs(0, 32);
leg0.addBox(-2, 0, -2, 4, 12, 4, g + 0.5f);
leg1.texOffs(0, 48);
leg1.addBox(-2, 0, -2, 4, 12, 4, g + 0.5f);
}
} }
void HumanoidModel::render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale ) void HumanoidModel::render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale )
@@ -68,14 +98,24 @@ void HumanoidModel::render(Entity* e, float time, float r, float bob, float yRot
setupAnim(time, r, bob, yRot, xRot, scale); setupAnim(time, r, bob, yRot, xRot, scale);
// Sync overlay with head rotation/position
if (texWidth == 64 && texHeight == 64) {
hair.xRot = head.xRot;
hair.yRot = head.yRot;
hair.zRot = head.zRot;
hair.y = head.y;
}
head.render(scale); head.render(scale);
if (texWidth == 64 && texHeight == 64) {
hair.render(scale);
}
body.render(scale); body.render(scale);
arm0.render(scale); arm0.render(scale);
arm1.render(scale); arm1.render(scale);
leg0.render(scale); leg0.render(scale);
leg1.render(scale); leg1.render(scale);
bowAndArrow = false; bowAndArrow = false;
//hair.render(scale);
} }
void HumanoidModel::render( HumanoidModel* model, float scale ) void HumanoidModel::render( HumanoidModel* model, float scale )
@@ -83,8 +123,12 @@ void HumanoidModel::render( HumanoidModel* model, float scale )
head.yRot = model->head.yRot; head.yRot = model->head.yRot;
head.y = model->head.y; head.y = model->head.y;
head.xRot = model->head.xRot; head.xRot = model->head.xRot;
//hair.yRot = head.yRot; if (texWidth == 64 && texHeight == 64) {
//hair.xRot = head.xRot; hair.yRot = head.yRot;
hair.xRot = head.xRot;
hair.zRot = head.zRot;
hair.y = head.y;
}
arm0.xRot = model->arm0.xRot; arm0.xRot = model->arm0.xRot;
arm0.zRot = model->arm0.zRot; arm0.zRot = model->arm0.zRot;
@@ -96,12 +140,14 @@ void HumanoidModel::render( HumanoidModel* model, float scale )
leg1.xRot = model->leg1.xRot; leg1.xRot = model->leg1.xRot;
head.render(scale); head.render(scale);
if (texWidth == 64 && texHeight == 64) {
hair.render(scale);
}
body.render(scale); body.render(scale);
arm0.render(scale); arm0.render(scale);
arm1.render(scale); arm1.render(scale);
leg0.render(scale); leg0.render(scale);
leg1.render(scale); leg1.render(scale);
//hair.render(scale);
} }
void HumanoidModel::renderHorrible( float time, float r, float bob, float yRot, float xRot, float scale ) void HumanoidModel::renderHorrible( float time, float r, float bob, float yRot, float xRot, float scale )

View File

@@ -9,7 +9,7 @@ class ItemInstance;
class HumanoidModel: public Model class HumanoidModel: public Model
{ {
public: public:
HumanoidModel(float g = 0, float yOffset = 0); HumanoidModel(float g = 0, float yOffset = 0, int texW = 64, int texH = 32);
void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale);
@@ -18,7 +18,7 @@ public:
void renderHorrible(float time, float r, float bob, float yRot, float xRot, float scale); void renderHorrible(float time, float r, float bob, float yRot, float xRot, float scale);
void onGraphicsReset(); void onGraphicsReset();
ModelPart head, /*hair,*/ body, arm0, arm1, leg0, leg1;//, ear; ModelPart head, hair, body, arm0, arm1, leg0, leg1;//, ear;
bool holdingLeftHand; bool holdingLeftHand;
bool holdingRightHand; bool holdingRightHand;
bool sneaking; bool sneaking;

View File

@@ -50,12 +50,12 @@ Cube::Cube(ModelPart* modelPart, int xTexOffs, int yTexOffs, float x0, float y0,
VertexPT* l2 = ++ptr; VertexPT* l2 = ++ptr;
VertexPT* l3 = ++ptr; VertexPT* l3 = ++ptr;
polygons[0] = PolygonQuad(l1, u1, u2, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + d, yTexOffs + d + h); // Right polygons[0] = PolygonQuad(l1, u1, u2, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Right
polygons[1] = PolygonQuad(u0, l0, l3, u3, xTexOffs + 0, yTexOffs + d, xTexOffs + d, yTexOffs + d + h); // Left polygons[1] = PolygonQuad(u0, l0, l3, u3, xTexOffs + 0, yTexOffs + d, xTexOffs + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Left
polygons[2] = PolygonQuad(l1, l0, u0, u1, xTexOffs + d, yTexOffs + 0, xTexOffs + d + w, yTexOffs + d); // Up polygons[2] = PolygonQuad(l1, l0, u0, u1, xTexOffs + d, yTexOffs + 0, xTexOffs + d + w, yTexOffs + d, modelPart->xTexSize, modelPart->yTexSize); // Up
polygons[3] = PolygonQuad(u2, u3, l3, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + w, yTexOffs); // Down polygons[3] = PolygonQuad(u2, u3, l3, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + w, yTexOffs, modelPart->xTexSize, modelPart->yTexSize); // Down
polygons[4] = PolygonQuad(u1, u0, u3, u2, xTexOffs + d, yTexOffs + d, xTexOffs + d + w, yTexOffs + d + h); // Front polygons[4] = PolygonQuad(u1, u0, u3, u2, xTexOffs + d, yTexOffs + d, xTexOffs + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Front
polygons[5] = PolygonQuad(l0, l1, l2, l3, xTexOffs + d + w + d, yTexOffs + d, xTexOffs + d + w + d + w, yTexOffs + d + h); // Back polygons[5] = PolygonQuad(l0, l1, l2, l3, xTexOffs + d + w + d, yTexOffs + d, xTexOffs + d + w + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Back
if (modelPart->mirror) { if (modelPart->mirror) {
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)

View File

@@ -12,15 +12,15 @@ PolygonQuad::PolygonQuad(VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3)
} }
PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3, PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3,
int uu0, int vv0, int uu1, int vv1) int uu0, int vv0, int uu1, int vv1, float texW, float texH)
: _flipNormal(false) : _flipNormal(false)
{ {
const float us = -0.002f / 64.0f;//0.1f / 64.0f; const float us = -0.002f / texW;
const float vs = -0.002f / 32.0f;//0.1f / 32.0f; const float vs = -0.002f / texH;
vertices[0] = v0->remap(uu1 / 64.0f - us, vv0 / 32.0f + vs); vertices[0] = v0->remap(uu1 / texW - us, vv0 / texH + vs);
vertices[1] = v1->remap(uu0 / 64.0f + us, vv0 / 32.0f + vs); vertices[1] = v1->remap(uu0 / texW + us, vv0 / texH + vs);
vertices[2] = v2->remap(uu0 / 64.0f + us, vv1 / 32.0f - vs); vertices[2] = v2->remap(uu0 / texW + us, vv1 / texH - vs);
vertices[3] = v3->remap(uu1 / 64.0f - us, vv1 / 32.0f - vs); vertices[3] = v3->remap(uu1 / texW - us, vv1 / texH - vs);
} }
PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3, PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3,

View File

@@ -11,7 +11,7 @@ class PolygonQuad
public: public:
PolygonQuad() {} PolygonQuad() {}
PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*); PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*);
PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, int u0, int v0, int u1, int v1); PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, int u0, int v0, int u1, int v1, float texW = 64.0f, float texH = 32.0f);
PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, float u0, float v0, float u1, float v1); PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, float u0, float v0, float u1, float v1);
void mirror(); void mirror();

View File

@@ -18,6 +18,20 @@
#include "../../network/packet/SendInventoryPacket.h" #include "../../network/packet/SendInventoryPacket.h"
#include "../../network/packet/EntityEventPacket.h" #include "../../network/packet/EntityEventPacket.h"
#include "../../network/packet/PlayerActionPacket.h" #include "../../network/packet/PlayerActionPacket.h"
#include <vector>
#include <cctype>
#include "../../platform/log.h"
#include "../../platform/HttpClient.h"
#include "../../platform/CThread.h"
#include "../../util/StringUtils.h"
#if defined(_WIN32)
#include <direct.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif
#ifndef STANDALONE_SERVER #ifndef STANDALONE_SERVER
#include "../gui/Screen.h" #include "../gui/Screen.h"
#include "../gui/screens/FurnaceScreen.h" #include "../gui/screens/FurnaceScreen.h"
@@ -32,6 +46,195 @@
#include "../../world/item/ArmorItem.h" #include "../../world/item/ArmorItem.h"
#include "../../network/packet/PlayerArmorEquipmentPacket.h" #include "../../network/packet/PlayerArmorEquipmentPacket.h"
namespace {
static bool isBase64(unsigned char c) {
return (std::isalnum(c) || (c == '+') || (c == '/'));
}
static std::string base64Decode(const std::string& encoded) {
static const std::string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string out;
int in_len = (int)encoded.size();
int i = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
while (in_len-- && (encoded[in_] != '=') && isBase64(encoded[in_])) {
char_array_4[i++] = encoded[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = (unsigned char)base64Chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; i < 3; i++)
out += char_array_3[i];
i = 0;
}
}
if (i) {
for (int j = i; j < 4; j++)
char_array_4[j] = 0;
for (int j = 0; j < 4; j++)
char_array_4[j] = (unsigned char)base64Chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (int j = 0; (j < i - 1); j++)
out += char_array_3[j];
}
return out;
}
static std::string extractJsonString(const std::string& json, const std::string& key) {
std::string search = "\"" + key + "\"";
size_t pos = json.find(search);
if (pos == std::string::npos) return "";
pos = json.find(':', pos + search.size());
if (pos == std::string::npos) return "";
pos++;
while (pos < json.size() && std::isspace((unsigned char)json[pos])) pos++;
if (pos >= json.size() || json[pos] != '"') return "";
pos++;
size_t end = json.find('"', pos);
if (end == std::string::npos) return "";
return json.substr(pos, end - pos);
}
static std::string getSkinUrlForUsername(const std::string& username) {
if (username.empty()) {
LOGI("[Skin] username empty\n");
return "";
}
LOGI("[Skin] resolving UUID for user '%s'...\n", username.c_str());
std::vector<unsigned char> body;
std::string apiUrl = "http://api.mojang.com/users/profiles/minecraft/" + username;
if (!HttpClient::download(apiUrl, body)) {
LOGW("[Skin] failed to download UUID for %s\n", username.c_str());
return "";
}
std::string response(body.begin(), body.end());
std::string uuid = extractJsonString(response, "id");
if (uuid.empty()) {
LOGW("[Skin] no UUID found in Mojang response for %s\n", username.c_str());
return "";
}
LOGI("[Skin] UUID=%s for user %s\n", uuid.c_str(), username.c_str());
std::string profileUrl = "http://sessionserver.mojang.com/session/minecraft/profile/" + uuid;
if (!HttpClient::download(profileUrl, body)) {
LOGW("[Skin] failed to download profile for UUID %s\n", uuid.c_str());
return "";
}
response.assign(body.begin(), body.end());
std::string encoded = extractJsonString(response, "value");
if (encoded.empty()) {
LOGW("[Skin] no value field in profile response for UUID %s\n", uuid.c_str());
return "";
}
std::string decoded = base64Decode(encoded);
size_t skinPos = decoded.find("\"SKIN\"");
if (skinPos == std::string::npos) {
LOGW("[Skin] no SKIN entry in decoded profile for UUID %s\n", uuid.c_str());
return "";
}
size_t urlPos = decoded.find("\"url\"", skinPos);
if (urlPos == std::string::npos) {
LOGW("[Skin] no url field under SKIN for UUID %s\n", uuid.c_str());
return "";
}
// extract the URL value from the substring starting at urlPos
std::string urlFragment = decoded.substr(urlPos);
std::string skinUrl = extractJsonString(urlFragment, "url");
if (skinUrl.empty()) {
LOGW("[Skin] failed to parse skin URL for UUID %s\n", uuid.c_str());
return "";
}
LOGI("[Skin] skin URL for %s: %s\n", username.c_str(), skinUrl.c_str());
return skinUrl;
}
static bool ensureDirectoryExists(const std::string& path) {
#if defined(_WIN32)
return _mkdir(path.c_str()) == 0 || errno == EEXIST;
#else
struct stat st;
if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode))
return true;
return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST;
#endif
}
static bool fileExists(const std::string& path) {
struct stat st;
if (stat(path.c_str(), &st) != 0)
return false;
#if defined(_WIN32)
return (st.st_mode & _S_IFREG) != 0;
#else
return S_ISREG(st.st_mode);
#endif
}
static void* fetchSkinForPlayer(void* param) {
LocalPlayer* player = (LocalPlayer*)param;
if (!player) return NULL;
LOGI("[Skin] starting skin download for %s\n", player->name.c_str());
const std::string cacheDir = "data/images/skins";
if (!ensureDirectoryExists(cacheDir)) {
LOGW("[Skin] failed to create cache directory %s\n", cacheDir.c_str());
}
std::string cacheFile = cacheDir + "/" + player->name + ".png";
if (fileExists(cacheFile)) {
LOGI("[Skin] using cached skin for %s\n", player->name.c_str());
player->setTextureName("skins/" + player->name + ".png");
return NULL;
}
std::string skinUrl = getSkinUrlForUsername(player->name);
if (skinUrl.empty()) {
LOGW("[Skin] skin URL lookup failed for %s\n", player->name.c_str());
return NULL;
}
LOGI("[Skin] downloading skin from %s\n", skinUrl.c_str());
std::vector<unsigned char> skinData;
if (!HttpClient::download(skinUrl, skinData) || skinData.empty()) {
LOGW("[Skin] download failed for %s\n", skinUrl.c_str());
return NULL;
}
// Save to cache
FILE* fp = fopen(cacheFile.c_str(), "wb");
if (fp) {
fwrite(skinData.data(), 1, skinData.size(), fp);
fclose(fp);
LOGI("[Skin] cached skin to %s\n", cacheFile.c_str());
} else {
LOGW("[Skin] failed to write skin cache %s\n", cacheFile.c_str());
}
player->setTextureName("skins/" + player->name + ".png");
return NULL;
}
//@note: doesn't work completely, since it doesn't care about stairs rotation //@note: doesn't work completely, since it doesn't care about stairs rotation
static bool isJumpable(int tileId) { static bool isJumpable(int tileId) {
return tileId != Tile::fence->id return tileId != Tile::fence->id
@@ -43,6 +246,8 @@ static bool isJumpable(int tileId) {
&& (Tile::tiles[tileId] != NULL && Tile::tiles[tileId]->getRenderShape() != Tile::SHAPE_STAIRS); && (Tile::tiles[tileId] != NULL && Tile::tiles[tileId]->getRenderShape() != Tile::SHAPE_STAIRS);
} }
} // anonymous namespace
LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, User* user, int dimension, bool isCreative) LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, User* user, int dimension, bool isCreative)
: Player(level, isCreative), : Player(level, isCreative),
minecraft(minecraft), minecraft(minecraft),
@@ -58,10 +263,10 @@ LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, User* user, int dim
this->dimension = dimension; this->dimension = dimension;
_init(); _init();
if (user != NULL) { if (user != NULL && !user->name.empty()) {
if (user->name.length() > 0) this->name = user->name;
//customTextureUrl = "http://s3.amazonaws.com/MinecraftSkins/" + user.name + ".png"; // Fetch user skin from Mojang servers in the background (avoids blocking the main thread)
this->name = user->name; new CThread(fetchSkinForPlayer, this);
} }
} }
@@ -182,7 +387,7 @@ void LocalPlayer::aiStep() {
// Sprint: detect W double-tap // Sprint: detect W double-tap
{ {
bool forwardHeld = (input->ya > 0); bool forwardHeld = (input->ya > 0);
if (forwardHeld && !prevForwardHeld) { if (forwardHeld && !prevForwardHeld && minecraft->options.useSprinting) {
// leading edge of W press // leading edge of W press
if (sprintDoubleTapTimer > 0) if (sprintDoubleTapTimer > 0)
sprinting = true; sprinting = true;
@@ -271,7 +476,7 @@ void LocalPlayer::move(float xa, float ya, float za) {
float newX = x, newZ = z; float newX = x, newZ = z;
if (autoJumpTime <= 0 && autoJumpEnabled) if (autoJumpTime <= 0 && minecraft->options.autoJump)
{ {
// auto-jump when crossing the middle of a tile, and the tile in the front is blocked // auto-jump when crossing the middle of a tile, and the tile in the front is blocked
bool jump = false; bool jump = false;

View File

@@ -105,6 +105,8 @@ private:
bool sprinting; bool sprinting;
int sprintDoubleTapTimer; int sprintDoubleTapTimer;
bool prevForwardHeld; bool prevForwardHeld;
public:
void setSprinting(bool sprint) { sprinting = sprint; }
}; };
#endif /*NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__*/ #endif /*NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__*/

View File

@@ -137,12 +137,6 @@ void Chunk::rebuild()
Tile* tile = Tile::tiles[tileId]; Tile* tile = Tile::tiles[tileId];
int renderLayer = tile->getRenderLayer(); int renderLayer = tile->getRenderLayer();
// if (renderLayer == l)
// rendered |= tileRenderer.tesselateInWorld(tile, x, y, z);
// else {
// _layerChunks[_layerChunkCount[renderLayer]++] = cindex;
// }
if (renderLayer > l) { if (renderLayer > l) {
renderNextLayer = true; renderNextLayer = true;
doRenderLayer[renderLayer] = true; doRenderLayer[renderLayer] = true;

View File

@@ -373,7 +373,7 @@ void GameRenderer::renderLevel(float a) {
// glDisable2(GL_FOG); // glDisable2(GL_FOG);
setupFog(1); setupFog(1);
if (zoom == 1) { if (zoom == 1 && !mc->options.F1) {
TIMER_POP_PUSH("hand"); TIMER_POP_PUSH("hand");
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
renderItemInHand(a, i); renderItemInHand(a, i);

View File

@@ -354,7 +354,7 @@ void ItemInHandRenderer::render( float a )
glRotatef2(-swing3 * 20, 0, 0, 1); glRotatef2(-swing3 * 20, 0, 0, 1);
// glRotatef2(-swing2 * 80, 1, 0, 0); // glRotatef2(-swing2 * 80, 1, 0, 0);
mc->textures->loadAndBindTexture("mob/char.png"); mc->textures->loadAndBindTexture(player->getTexture());
glTranslatef2(-1.0f, +3.6f, +3.5f); glTranslatef2(-1.0f, +3.6f, +3.5f);
glRotatef2(120, 0, 0, 1); glRotatef2(120, 0, 0, 1);
glRotatef2(180 + 20, 1, 0, 0); glRotatef2(180 + 20, 1, 0, 0);

View File

@@ -16,7 +16,7 @@ typedef struct TextureData {
TextureData() TextureData()
: w(0), : w(0),
h(0), h(0),
data(NULL), data(nullptr),
numBytes(0), numBytes(0),
transparent(true), transparent(true),
memoryHandledExternally(false), memoryHandledExternally(false),

View File

@@ -5,6 +5,7 @@
#include "../Options.h" #include "../Options.h"
#include "../../platform/time.h" #include "../../platform/time.h"
#include "../../AppPlatform.h" #include "../../AppPlatform.h"
#include "../../util/StringUtils.h"
/*static*/ int Textures::textureChanges = 0; /*static*/ int Textures::textureChanges = 0;
/*static*/ bool Textures::MIPMAP = false; /*static*/ bool Textures::MIPMAP = false;
@@ -64,7 +65,8 @@ TextureId Textures::loadTexture( const std::string& resourceName, bool inTexture
if (it != idMap.end()) if (it != idMap.end())
return it->second; return it->second;
TextureData texdata = platform->loadTexture(resourceName, inTextureFolder); bool isUrl = Util::startsWith(resourceName, "http://") || Util::startsWith(resourceName, "https://");
TextureData texdata = platform->loadTexture(resourceName, isUrl ? false : inTextureFolder);
if (texdata.data) if (texdata.data)
return assignTexture(resourceName, texdata); return assignTexture(resourceName, texdata);
else if (texdata.identifier != InvalidId) { else if (texdata.identifier != InvalidId) {

View File

@@ -1,4 +1,5 @@
#include "TileRenderer.h" #include "TileRenderer.h"
#include "Chunk.h"
#include "../Minecraft.h" #include "../Minecraft.h"
#include "Tesselator.h" #include "Tesselator.h"
@@ -56,6 +57,7 @@ bool TileRenderer::tesselateBlockInWorld( Tile* tt, int x, int y, int z, float r
float c2 = 0.8f; float c2 = 0.8f;
float c3 = 0.6f; float c3 = 0.6f;
float r11 = c11 * r; float r11 = c11 * r;
float g11 = c11 * g; float g11 = c11 * g;
float b11 = c11 * b; float b11 = c11 * b;
@@ -128,6 +130,7 @@ bool TileRenderer::tesselateBlockInWorld( Tile* tt, int x, int y, int z, float r
return changed; return changed;
} }
void TileRenderer::tesselateInWorld( Tile* tile, int x, int y, int z, int fixedTexture ) void TileRenderer::tesselateInWorld( Tile* tile, int x, int y, int z, int fixedTexture )
{ {
this->fixedTexture = fixedTexture; this->fixedTexture = fixedTexture;

View File

@@ -51,7 +51,7 @@ EntityRenderDispatcher::EntityRenderDispatcher()
assign( ER_SPIDER_RENDERER, new SpiderRenderer()); assign( ER_SPIDER_RENDERER, new SpiderRenderer());
assign( ER_TNT_RENDERER, new TntRenderer()); assign( ER_TNT_RENDERER, new TntRenderer());
assign( ER_ARROW_RENDERER, new ArrowRenderer()); assign( ER_ARROW_RENDERER, new ArrowRenderer());
assign( ER_PLAYER_RENDERER, new PlayerRenderer(new HumanoidModel(), 0)); assign( ER_PLAYER_RENDERER, new PlayerRenderer(new HumanoidModel(0, 0, 64, 64), 0));
assign( ER_THROWNEGG_RENDERER, new ItemSpriteRenderer(Item::egg->getIcon(0))); assign( ER_THROWNEGG_RENDERER, new ItemSpriteRenderer(Item::egg->getIcon(0)));
assign( ER_SNOWBALL_RENDERER, new ItemSpriteRenderer(Item::snowBall->getIcon(0))); assign( ER_SNOWBALL_RENDERER, new ItemSpriteRenderer(Item::snowBall->getIcon(0)));
assign( ER_PAINTING_RENDERER, new PaintingRenderer()); assign( ER_PAINTING_RENDERER, new PaintingRenderer());

View File

@@ -14,8 +14,8 @@ static const std::string armorFilenames[10] = {
PlayerRenderer::PlayerRenderer( HumanoidModel* humanoidModel, float shadow ) PlayerRenderer::PlayerRenderer( HumanoidModel* humanoidModel, float shadow )
: super(humanoidModel, shadow), : super(humanoidModel, shadow),
armorParts1(new HumanoidModel(1.0f)), armorParts1(new HumanoidModel(1.0f, 0, 64, 64)),
armorParts2(new HumanoidModel(0.5f)) armorParts2(new HumanoidModel(0.5f, 0, 64, 64))
{ {
} }

View File

@@ -27,6 +27,7 @@ int transformKey(int glfwkey) {
case GLFW_KEY_BACKSPACE: return Keyboard::KEY_BACKSPACE; case GLFW_KEY_BACKSPACE: return Keyboard::KEY_BACKSPACE;
case GLFW_KEY_LEFT_SHIFT: return Keyboard::KEY_LSHIFT; case GLFW_KEY_LEFT_SHIFT: return Keyboard::KEY_LSHIFT;
case GLFW_KEY_ENTER: return Keyboard::KEY_RETURN; case GLFW_KEY_ENTER: return Keyboard::KEY_RETURN;
case GLFW_KEY_LEFT_CONTROL: return Keyboard::KEY_LEFT_CTRL;
default: return glfwkey; default: return glfwkey;
} }
} }

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 {

335
src/platform/HttpClient.cpp Normal file
View File

@@ -0,0 +1,335 @@
#include "HttpClient.h"
#include "log.h"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <string>
#include <vector>
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <winhttp.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "winhttp.lib")
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
namespace {
bool startsWith(const std::string& s, const std::string& prefix) {
return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
}
bool parseUrl(const std::string& url, std::string& scheme, std::string& host, int& port, std::string& path);
#if defined(_WIN32)
bool downloadHttpsWinHttp(const std::string& url, std::vector<unsigned char>& outBody) {
outBody.clear();
std::string scheme, host, path;
int port = 0;
if (!parseUrl(url, scheme, host, port, path))
return false;
// WinHTTP expects the path to include the leading '/'.
HINTERNET hSession = WinHttpOpen(L"MinecraftPE/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession)
return false;
HINTERNET hConnect = WinHttpConnect(hSession, std::wstring(host.begin(), host.end()).c_str(), port, 0);
if (!hConnect) {
WinHttpCloseHandle(hSession);
return false;
}
HINTERNET hRequest = WinHttpOpenRequest(
hConnect,
L"GET",
std::wstring(path.begin(), path.end()).c_str(),
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE
);
if (!hRequest) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
DWORD redirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS;
WinHttpSetOption(hRequest, WINHTTP_OPTION_REDIRECT_POLICY, &redirectPolicy, sizeof(redirectPolicy));
BOOL result = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
if (!result) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
result = WinHttpReceiveResponse(hRequest, NULL);
if (!result) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
DWORD bytesAvailable = 0;
while (WinHttpQueryDataAvailable(hRequest, &bytesAvailable) && bytesAvailable > 0) {
std::vector<unsigned char> buffer(bytesAvailable);
DWORD bytesRead = 0;
if (!WinHttpReadData(hRequest, buffer.data(), bytesAvailable, &bytesRead) || bytesRead == 0)
break;
outBody.insert(outBody.end(), buffer.begin(), buffer.begin() + bytesRead);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return !outBody.empty();
}
#endif
std::string toLower(const std::string& s) {
std::string out = s;
std::transform(out.begin(), out.end(), out.begin(), ::tolower);
return out;
}
bool parseUrl(const std::string& url, std::string& scheme, std::string& host, int& port, std::string& path) {
scheme.clear();
host.clear();
path.clear();
port = 0;
// Very simple URL parser.
// url format: scheme://host[:port]/path
auto pos = url.find("://");
if (pos == std::string::npos) return false;
scheme = toLower(url.substr(0, pos));
size_t start = pos + 3;
size_t slash = url.find('/', start);
std::string hostPort = (slash == std::string::npos) ? url.substr(start) : url.substr(start, slash - start);
path = (slash == std::string::npos) ? "/" : url.substr(slash);
size_t colon = hostPort.find(':');
if (colon != std::string::npos) {
host = hostPort.substr(0, colon);
port = atoi(hostPort.c_str() + colon + 1);
} else {
host = hostPort;
}
if (scheme == "http") {
if (port == 0) port = 80;
} else if (scheme == "https") {
if (port == 0) port = 443;
}
return !host.empty() && !scheme.empty();
}
bool readAll(int sockfd, std::vector<unsigned char>& out) {
const int BUF_SIZE = 4096;
unsigned char buffer[BUF_SIZE];
while (true) {
int received = recv(sockfd, (char*)buffer, BUF_SIZE, 0);
if (received <= 0)
break;
out.insert(out.end(), buffer, buffer + received);
}
return true;
}
bool resolveAndConnect(const std::string& host, int port, int& outSock) {
#if defined(_WIN32)
static bool initialized = false;
if (!initialized) {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
initialized = true;
}
#endif
struct addrinfo hints;
struct addrinfo* result = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
const std::string portStr = std::to_string(port);
if (getaddrinfo(host.c_str(), portStr.c_str(), &hints, &result) != 0)
return false;
int sock = -1;
for (struct addrinfo* rp = result; rp != NULL; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0) continue;
if (connect(sock, rp->ai_addr, (int)rp->ai_addrlen) == 0)
break; // success
#if defined(_WIN32)
closesocket(sock);
#else
close(sock);
#endif
sock = -1;
}
freeaddrinfo(result);
if (sock < 0)
return false;
outSock = sock;
return true;
}
std::string getHeaderValue(const std::string& headers, const std::string& key) {
std::string lower = toLower(headers);
std::string lowerKey = toLower(key);
size_t pos = lower.find(lowerKey);
if (pos == std::string::npos) return "";
size_t colon = lower.find(':', pos + lowerKey.size());
if (colon == std::string::npos) return "";
size_t start = colon + 1;
while (start < lower.size() && (lower[start] == ' ' || lower[start] == '\t')) start++;
size_t end = lower.find('\r', start);
if (end == std::string::npos) end = lower.find('\n', start);
if (end == std::string::npos) end = lower.size();
return headers.substr(start, end - start);
}
bool extractStatusCode(const std::string& headers, int& outStatus) {
size_t pos = headers.find(" ");
if (pos == std::string::npos) return false;
size_t pos2 = headers.find(" ", pos + 1);
if (pos2 == std::string::npos) return false;
std::string code = headers.substr(pos + 1, pos2 - pos - 1);
outStatus = atoi(code.c_str());
return true;
}
} // anonymous namespace
namespace HttpClient {
bool download(const std::string& url, std::vector<unsigned char>& outBody) {
outBody.clear();
std::string currentUrl = url;
for (int redirect = 0; redirect < 3; ++redirect) {
std::string scheme, host, path;
int port = 0;
if (!parseUrl(currentUrl, scheme, host, port, path)) {
LOGW("[HttpClient] parseUrl failed for '%s'\n", currentUrl.c_str());
return false;
}
if (scheme == "https") {
#if defined(_WIN32)
LOGI("[HttpClient] using WinHTTP for HTTPS URL %s\n", currentUrl.c_str());
return downloadHttpsWinHttp(currentUrl, outBody);
#else
LOGW("[HttpClient] HTTPS not supported on this platform: %s\n", currentUrl.c_str());
return false;
#endif
}
if (scheme != "http") {
LOGW("[HttpClient] unsupported scheme '%s' for URL '%s'\n", scheme.c_str(), currentUrl.c_str());
return false;
}
int sock = -1;
if (!resolveAndConnect(host, port, sock)) {
LOGW("[HttpClient] resolve/connect failed for %s:%d\n", host.c_str(), port);
return false;
}
std::string request = "GET " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "User-Agent: MinecraftPE\r\n";
request += "Connection: close\r\n";
request += "\r\n";
send(sock, request.c_str(), (int)request.size(), 0);
std::vector<unsigned char> raw;
readAll(sock, raw);
#if defined(_WIN32)
closesocket(sock);
#else
close(sock);
#endif
if (raw.empty()) {
LOGW("[HttpClient] no response data from %s\n", currentUrl.c_str());
return false;
}
// split headers and body
const std::string delim = "\r\n\r\n";
auto it = std::search(raw.begin(), raw.end(), delim.begin(), delim.end());
if (it == raw.end())
return false;
size_t headerLen = it - raw.begin();
std::string headers(reinterpret_cast<const char*>(raw.data()), headerLen);
size_t bodyStart = headerLen + delim.size();
int status = 0;
if (!extractStatusCode(headers, status))
return false;
if (status == 301 || status == 302 || status == 307 || status == 308) {
std::string location = getHeaderValue(headers, "Location");
if (location.empty()) {
LOGW("[HttpClient] redirect without Location header for %s\n", currentUrl.c_str());
return false;
}
LOGI("[HttpClient] redirect %s -> %s\n", currentUrl.c_str(), location.c_str());
currentUrl = location;
continue;
}
if (status != 200) {
std::string bodySnippet;
if (!outBody.empty()) {
size_t len = std::min(outBody.size(), (size_t)1024);
bodySnippet.assign(outBody.begin(), outBody.begin() + len);
}
LOGW("[HttpClient] HTTP status %d for %s\n", status, currentUrl.c_str());
LOGW("[HttpClient] Headers:\n%s\n", headers.c_str());
LOGW("[HttpClient] Body (up to 1024 bytes):\n%s\n", bodySnippet.c_str());
return false;
}
outBody.assign(raw.begin() + bodyStart, raw.end());
return true;
}
return false;
}
} // namespace HttpClient

16
src/platform/HttpClient.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef HTTPCLIENT_H__
#define HTTPCLIENT_H__
#include <string>
#include <vector>
namespace HttpClient {
/// Download the given URL into "outBody".
/// Returns true if the download completed successfully (HTTP 200) and the body is in outBody.
/// This function supports plain HTTP only (no TLS). It will follow up to 3 redirects.
bool download(const std::string& url, std::vector<unsigned char>& outBody);
} // namespace HttpClient
#endif /* HTTPCLIENT_H__ */

View File

@@ -0,0 +1,94 @@
#include "PngLoader.h"
#include <png.h>
#include <cstring>
struct MemoryReader {
const unsigned char* data;
size_t size;
size_t pos;
};
static void pngMemoryRead(png_structp pngPtr, png_bytep outBytes, png_size_t byteCountToRead) {
MemoryReader* reader = (MemoryReader*)png_get_io_ptr(pngPtr);
if (!reader)
return;
if (reader->pos + byteCountToRead > reader->size) {
png_error(pngPtr, "Read past end of buffer");
return;
}
memcpy(outBytes, reader->data + reader->pos, byteCountToRead);
reader->pos += byteCountToRead;
}
TextureData loadPngFromMemory(const unsigned char* data, size_t size) {
TextureData out;
if (!data || size == 0) return out;
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngPtr) return out;
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_read_struct(&pngPtr, NULL, NULL);
return out;
}
if (setjmp(png_jmpbuf(pngPtr))) {
png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
return out;
}
MemoryReader reader;
reader.data = data;
reader.size = size;
reader.pos = 0;
png_set_read_fn(pngPtr, &reader, pngMemoryRead);
png_read_info(pngPtr, infoPtr);
// Convert any color type to 8-bit RGBA
if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(pngPtr);
if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(pngPtr, infoPtr) < 8)
png_set_expand_gray_1_2_4_to_8(pngPtr);
if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pngPtr);
if (png_get_bit_depth(pngPtr, infoPtr) == 16)
png_set_strip_16(pngPtr);
// Ensure we always have RGBA (4 bytes per pixel)
// Only add alpha if the image lacks it (e.g., RGB skin files).
png_set_gray_to_rgb(pngPtr);
// Handle interlaced PNGs properly
int number_passes = png_set_interlace_handling(pngPtr);
png_read_update_info(pngPtr, infoPtr);
int colorType = png_get_color_type(pngPtr, infoPtr);
if (colorType == PNG_COLOR_TYPE_RGB) {
png_set_filler(pngPtr, 0xFF, PNG_FILLER_AFTER);
}
out.w = png_get_image_width(pngPtr, infoPtr);
out.h = png_get_image_height(pngPtr, infoPtr);
png_bytep* rowPtrs = new png_bytep[out.h];
out.data = new unsigned char[4 * out.w * out.h];
out.memoryHandledExternally = false;
int rowStrideBytes = 4 * out.w;
for (int i = 0; i < out.h; i++) {
rowPtrs[i] = (png_bytep)&out.data[i*rowStrideBytes];
}
png_read_image(pngPtr, rowPtrs);
png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
delete[] rowPtrs;
return out;
}

12
src/platform/PngLoader.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef PNGLOADER_H__
#define PNGLOADER_H__
#include "../client/renderer/TextureData.h"
#include <cstddef>
/// Decode a PNG (from memory) into a TextureData.
/// Returns an empty TextureData on failure.
TextureData loadPngFromMemory(const unsigned char* data, size_t size);
#endif // PNGLOADER_H__

View File

@@ -71,9 +71,11 @@ public:
static const int KEY_F11 = 122; static const int KEY_F11 = 122;
static const int KEY_F12 = 123; static const int KEY_F12 = 123;
static const int KEY_ESCAPE = 27; static const int KEY_ESCAPE = 27;
static const int KEY_SPACE = 32; static const int KEY_SPACE = 32;
static const int KEY_LSHIFT = 10; static const int KEY_LSHIFT = 10;
static const int KEY_LEFT_CTRL = 232;
static bool isKeyDown(int keyCode) { static bool isKeyDown(int keyCode) {
return _states[keyCode] == KeyboardAction::KEYDOWN; return _states[keyCode] == KeyboardAction::KEYDOWN;

View File

@@ -124,7 +124,7 @@ static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I l
#pragma warning(disable:4309) // 'initializing' : truncation of constant value #pragma warning(disable:4309) // 'initializing' : truncation of constant value
#endif #endif
// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false // Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false
static const char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78};
struct PacketFollowedByData struct PacketFollowedByData
{ {

View File

@@ -110,6 +110,11 @@ std::string Mob::getTexture()
return textureName; return textureName;
} }
void Mob::setTextureName(const std::string& name)
{
textureName = name;
}
bool Mob::isPickable() bool Mob::isPickable()
{ {
return !removed; return !removed;

View File

@@ -42,6 +42,7 @@ public:
virtual void spawnAnim(); virtual void spawnAnim();
virtual std::string getTexture(); virtual std::string getTexture();
virtual void setTextureName(const std::string& name);
virtual bool isAlive(); virtual bool isAlive();
virtual bool isPickable(); virtual bool isPickable();

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

@@ -287,53 +287,58 @@ void RandomLevelSource::postProcess(ChunkSource* parent, int xt, int zt) {
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
for (int i = 0; i < 20; i++) { // Coal: common, wide Y range, moderate vein size
for (int i = 0; i < 16; i++) {
int x = xo + random.nextInt(16); int x = xo + random.nextInt(16);
int y = random.nextInt(128); int y = random.nextInt(128);
int z = zo + random.nextInt(16); int z = zo + random.nextInt(16);
OreFeature feature(Tile::coalOre->id, 16); OreFeature feature(Tile::coalOre->id, 14);
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
for (int i = 0; i < 20; i++) { // Iron: common, limited to upper underground
for (int i = 0; i < 14; i++) {
int x = xo + random.nextInt(16); int x = xo + random.nextInt(16);
int y = random.nextInt(64); int y = random.nextInt(64);
int z = zo + random.nextInt(16); int z = zo + random.nextInt(16);
OreFeature feature(Tile::ironOre->id, 8); OreFeature feature(Tile::ironOre->id, 10);
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
// Gold: rarer and deeper
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
int x = xo + random.nextInt(16); int x = xo + random.nextInt(16);
int y = random.nextInt(32); int y = random.nextInt(32);
int z = zo + random.nextInt(16); int z = zo + random.nextInt(16);
OreFeature feature(Tile::goldOre->id, 8); OreFeature feature(Tile::goldOre->id, 9);
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
for (int i = 0; i < 8; i++) { // Redstone: somewhat common at low depths
for (int i = 0; i < 6; i++) {
int x = xo + random.nextInt(16); int x = xo + random.nextInt(16);
int y = random.nextInt(16); int y = random.nextInt(16);
int z = zo + random.nextInt(16); int z = zo + random.nextInt(16);
OreFeature feature(Tile::redStoneOre->id, 7); OreFeature feature(Tile::redStoneOre->id, 8);
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
for (int i = 0; i < 1; i++) { // Emerald (diamond-equivalent): still rare but slightly more than vanilla
for (int i = 0; i < 3; i++) {
int x = xo + random.nextInt(16); int x = xo + random.nextInt(16);
int y = random.nextInt(16); int y = random.nextInt(16);
int z = zo + random.nextInt(16); int z = zo + random.nextInt(16);
OreFeature feature(Tile::emeraldOre->id, 7); OreFeature feature(Tile::emeraldOre->id, 6);
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
// lapis ore // Lapis: rare and not in very high Y
for (int i = 0; i < 1; i++) { for (int i = 0; i < 1; i++) {
int x = xo + random.nextInt(16); int x = xo + random.nextInt(16);
int y = random.nextInt(16) + random.nextInt(16); int y = random.nextInt(16) + random.nextInt(16);
int z = zo + random.nextInt(16); int z = zo + random.nextInt(16);
OreFeature feature(Tile::lapisOre->id, 6); OreFeature feature(Tile::lapisOre->id, 6);
feature.place(level, &random, x, y, z); feature.place(level, &random, x, y, z);
} }
const float ss = 0.5f; const float ss = 0.5f;
@@ -504,7 +509,8 @@ LevelChunk* RandomLevelSource::getChunk(int xOffs, int zOffs) {
prepareHeights(xOffs, zOffs, blocks, 0, temperatures);//biomes, temperatures); prepareHeights(xOffs, zOffs, blocks, 0, temperatures);//biomes, temperatures);
buildSurfaces(xOffs, zOffs, blocks, biomes); buildSurfaces(xOffs, zOffs, blocks, biomes);
//caveFeature.apply(this, level, xOffs, zOffs, blocks, LevelChunk::ChunkBlockCount); // Carve caves into the chunk
caveFeature.apply(this, level, xOffs, zOffs, blocks, LevelChunk::ChunkBlockCount);
levelChunk->recalcHeightmap(); levelChunk->recalcHeightmap();
return levelChunk; return levelChunk;

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);