Files
minecraft-pe-0.6.1/src/client/gui/Gui.cpp

970 lines
28 KiB
C++
Executable File

#include "Gui.h"
#include "Font.h"
#include "client/Options.h"
#include "screens/IngameBlockSelectionScreen.h"
#include "../Minecraft.h"
#include "../player/LocalPlayer.h"
#include "../renderer/Tesselator.h"
#include "../renderer/TileRenderer.h"
#include "../renderer/LevelRenderer.h"
#include "../renderer/GameRenderer.h"
#include "../renderer/entity/ItemRenderer.h"
#include "../player/input/IInputHolder.h"
#include "../gamemode/GameMode.h"
#include "../gamemode/CreativeMode.h"
#include "../renderer/Textures.h"
#include "../../AppConstants.h"
#include "../../world/entity/player/Inventory.h"
#include "../../world/level/material/Material.h"
#include "../../world/item/Item.h"
#include "../../world/item/ItemInstance.h"
#include "../../platform/input/Mouse.h"
#include "../../world/level/Level.h"
#include "../../world/PosTranslator.h"
#include "../../platform/time.h"
#include <cmath>
float Gui::InvGuiScale = 1.0f / 3.0f;
float Gui::GuiScale = 1.0f / Gui::InvGuiScale;
const float Gui::DropTicks = 40.0f;
//#include <android/log.h>
Gui::Gui(Minecraft* minecraft)
: minecraft(minecraft),
tickCount(0),
progress(0),
overlayMessageTime(0),
animateOverlayMessageColor(false),
tbr(1),
_inventoryNeedsUpdate(true),
_flashSlotId(-1),
_flashSlotStartTime(-1),
_slotFont(NULL),
_numSlots(4),
_currentDropTicks(-1),
_currentDropSlot(-1),
MAX_MESSAGE_WIDTH(240),
itemNameOverlayTime(2)
{
glGenBuffers2(1, &_inventoryRc.vboId);
glGenBuffers2(1, &rcFeedbackInner.vboId);
glGenBuffers2(1, &rcFeedbackOuter.vboId);
//Gui::InvGuiScale = 1.0f / (int) (3 * Minecraft::width / 854);
}
Gui::~Gui()
{
if (_slotFont)
delete _slotFont;
glDeleteBuffers(1, &_inventoryRc.vboId);
}
void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) {
if (!minecraft->level || !minecraft->player)
return;
//minecraft->gameRenderer->setupGuiScreen();
Font* font = minecraft->font;
#ifdef PLATFORM_DESKTOP
const bool isTouchInterface = false;
#else
const bool isTouchInterface = minecraft->useTouchscreen();
#endif
const int screenWidth = (int)(minecraft->width * InvGuiScale);
const int screenHeight = (int)(minecraft->height * InvGuiScale);
blitOffset = -90;
renderProgressIndicator(isTouchInterface, screenWidth, screenHeight, a);
glColor4f2(1, 1, 1, 1);
// H: 4
// T: 7
// L: 6
// F: 3
int ySlot = screenHeight - 16 - 3;
if (minecraft->gameMode->canHurtPlayer()) {
minecraft->textures->loadAndBindTexture("gui/icons.png");
Tesselator& t = Tesselator::instance;
t.beginOverride();
t.colorABGR(0xffffffff);
renderHearts();
renderBubbles();
t.endOverrideAndDraw();
}
if(minecraft->player->getSleepTimer() > 0) {
glDisable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
renderSleepAnimation(screenWidth, screenHeight);
glEnable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
}
renderToolBar(a, ySlot, screenWidth);
glEnable(GL_BLEND);
unsigned int max = 10;
bool isChatting = false;
renderChatMessages(screenHeight, max, isChatting, font);
#if !defined(RPI)
renderOnSelectItemNameText(screenWidth, font, ySlot);
#endif
#if defined(RPI)
renderDebugInfo();
#elif defined(PLATFORM_DESKTOP)
if (minecraft->options.renderDebug)
renderDebugInfo();
#endif
glDisable(GL_BLEND);
glEnable2(GL_ALPHA_TEST);
}
int Gui::getSlotIdAt(int x, int y) {
int screenWidth = (int)(minecraft->width * InvGuiScale);
int screenHeight = (int)(minecraft->height * InvGuiScale);
x = (int)(x * InvGuiScale);
y = (int)(y * InvGuiScale);
if (y < (screenHeight - 16 - 3) || y > screenHeight)
return -1;
int xBase = 2 + screenWidth / 2 - getNumSlots() * 10;
int xRel = (x - xBase);
if (xRel < 0)
return -1;
int slot = xRel / 20;
return (slot >= 0 && slot < getNumSlots())? slot : -1;
}
bool Gui::isInside(int x, int y) {
return getSlotIdAt(x, y) != -1;
}
int Gui::getNumSlots() {
return _numSlots;
}
void Gui::flashSlot(int slotId) {
_flashSlotId = slotId;
_flashSlotStartTime = getTimeS();
}
void Gui::getSlotPos(int slot, int& posX, int& posY) {
int screenWidth = (int)(minecraft->width * InvGuiScale);
int screenHeight = (int)(minecraft->height * InvGuiScale);
posX = screenWidth / 2 - getNumSlots() * 10 + slot * 20,
posY = screenHeight - 22;
}
RectangleArea Gui::getRectangleArea(int extendSide) {
const int Spacing = 3;
const float pCenterX = 2.0f + (float)(minecraft->width / 2);
const float pHalfWidth = (1.0f + (getNumSlots() * 10 + Spacing)) * Gui::GuiScale;
const float pHeight = (22 + Spacing) * Gui::GuiScale;
if (extendSide < 0)
return RectangleArea(0, (float)minecraft->height-pHeight, pCenterX+pHalfWidth+2, (float)minecraft->height);
if (extendSide > 0)
return RectangleArea(pCenterX-pHalfWidth, (float)minecraft->height-pHeight, (float)minecraft->width, (float)minecraft->height);
return RectangleArea(pCenterX-pHalfWidth, (float)minecraft->height-pHeight, pCenterX+pHalfWidth+2, (float)minecraft->height);
}
void Gui::handleClick(int button, int x, int y) {
if (button != MouseAction::ACTION_LEFT) return;
int slot = getSlotIdAt(x, y);
if (slot != -1)
{
#ifndef PLATFORM_DESKTOP
if (slot == (getNumSlots()-1))
{
minecraft->screenChooser.setScreen(SCREEN_BLOCKSELECTION);
}
else
#endif
{
minecraft->player->inventory->selectSlot(slot);
itemNameOverlayTime = 0;
}
}
}
void Gui::handleKeyPressed(int key)
{
if (key == 99)
{
if (minecraft->player->inventory->selected > 0)
{
minecraft->player->inventory->selected--;
}
}
else if (key == 4)
{
if (minecraft->player->inventory->selected < (getNumSlots() - 2))
{
minecraft->player->inventory->selected++;
}
}
else if (key == 100)
{
minecraft->screenChooser.setScreen(SCREEN_BLOCKSELECTION);
}
else if (key == minecraft->options.keyDrop.key)
{
minecraft->player->inventory->dropSlot(minecraft->player->inventory->selected, false);
}
}
void Gui::tick() {
if (overlayMessageTime > 0) overlayMessageTime--;
tickCount++;
if(itemNameOverlayTime < 2)
itemNameOverlayTime += 1.0f / SharedConstants::TicksPerSecond;
for (unsigned int i = 0; i < guiMessages.size(); i++) {
guiMessages.at(i).ticks++;
}
if (!minecraft->isCreativeMode())
tickItemDrop();
}
void Gui::addMessage(const std::string& _string) {
if (!minecraft->font)
return;
std::string string = _string;
while (minecraft->font->width(string) > MAX_MESSAGE_WIDTH) {
unsigned int i = 1;
while (i < string.length() && minecraft->font->width(string.substr(0, i + 1)) <= MAX_MESSAGE_WIDTH) {
i++;
}
addMessage(string.substr(0, i));
string = string.substr(i);
}
GuiMessage message;
message.message = string;
message.ticks = 0;
guiMessages.insert(guiMessages.begin(), message);
while (guiMessages.size() > 30) {
guiMessages.pop_back();
}
}
void Gui::setNowPlaying(const std::string& string) {
overlayMessageString = "Now playing: " + string;
overlayMessageTime = 20 * 3;
animateOverlayMessageColor = true;
}
void Gui::displayClientMessage(const std::string& messageId) {
//Language language = Language.getInstance();
//std::string languageString = language.getElement(messageId);
addMessage(std::string("Client message: ") + messageId);
}
void Gui::renderVignette(float br, int w, int h) {
br = 1 - br;
if (br < 0) br = 0;
if (br > 1) br = 1;
tbr += (br - tbr) * 0.01f;
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
glBlendFunc2(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
glColor4f2(tbr, tbr, tbr, 1);
minecraft->textures->loadAndBindTexture("misc/vignette.png");
Tesselator& t = Tesselator::instance;
t.begin();
t.vertexUV(0, (float)h, -90, 0, 1);
t.vertexUV((float)w, (float)h, -90, 1, 1);
t.vertexUV((float)w, 0, -90, 1, 0);
t.vertexUV(0, 0, -90, 0, 0);
t.draw();
glDepthMask(true);
glEnable(GL_DEPTH_TEST);
glColor4f2(1, 1, 1, 1);
glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void Gui::renderSlot(int slot, int x, int y, float a) {
ItemInstance* item = minecraft->player->inventory->getItem(slot);
if (!item) {
//LOGW("Warning: item @ Gui::renderSlot is NULL\n");
return;
}
const bool fancy = true;
ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, (float)x, (float)y, fancy);
}
void Gui::renderSlotText( const ItemInstance* item, float x, float y, bool hasFinite, bool shadow )
{
//if (!item || item->getItem()->getMaxStackSize() <= 1) {
if (item->count <= 1) {
return;
}
int c = item->count;
char buffer[4] = {0,0,0,0};
if (hasFinite)
itemCountItoa(buffer, c);
else
buffer[0] = (char)157;
//LOGI("slot: %d - %s\n", slot, buffer);
if (shadow)
minecraft->font->drawShadow(buffer, x, y, item->count>0?0xffcccccc:0x60cccccc);
else
minecraft->font->draw(buffer, x, y, item->count>0?0xffcccccc:0x60cccccc);
}
void Gui::inventoryUpdated() {
_inventoryNeedsUpdate = true;
}
void Gui::onGraphicsReset() {
inventoryUpdated();
}
void Gui::texturesLoaded( Textures* textures ) {
//_slotFont = new Font(&minecraft->options, "gui/gui_blocks.png", textures, 0, 504, 10, 1, '0');
}
void Gui::onConfigChanged( const Config& c ) {
Tesselator& t = Tesselator::instance;
t.begin();
//
// Create outer feedback circle
//
#ifdef ANDROID
const float mm = 12;
#else
const float mm = 12;
#endif
const float maxRadius = minecraft->pixelCalcUi.millimetersToPixels(mm);
const float radius = Mth::Min(80.0f/2, maxRadius);
//LOGI("radius, maxradius: %f, %f\n", radius, maxRadius);
const float radiusInner = radius * 0.95f;
const int steps = 24;
const float fstep = Mth::TWO_PI / steps;
for (int i = 0; i < steps; ++i) {
float a = i * fstep;;
float b = a + fstep;
float aCos = Mth::cos(a);
float bCos = Mth::cos(b);
float aSin = Mth::sin(a);
float bSin = Mth::sin(b);
float x00 = radius * aCos;
float x01 = radiusInner * aCos;
float x10 = radius * bCos;
float x11 = radiusInner * bCos;
float y00 = radius * aSin;
float y01 = radiusInner * aSin;
float y10 = radius * bSin;
float y11 = radiusInner * bSin;
t.vertexUV(x01, y01, 0, 0, 1);
t.vertexUV(x11, y11, 0, 1, 1);
t.vertexUV(x10, y10, 0, 1, 0);
t.vertexUV(x00, y00, 0, 0, 0);
}
rcFeedbackOuter = t.end(true, rcFeedbackOuter.vboId);
//
// Create the inner feedback ring
//
t.begin(GL_TRIANGLE_FAN);
t.vertex(0, 0, 0);
for (int i = 0; i < steps + 1; ++i) {
float a = -i * fstep;
float xx = radiusInner * Mth::cos(a);
float yy = radiusInner * Mth::sin(a);
t.vertex(xx, yy, 0);
//LOGI("x,y: %f, %f\n", xx, yy);
}
rcFeedbackInner = t.end(true, rcFeedbackInner.vboId);
if (c.minecraft->useTouchscreen()) {
// I'll bump this up to 6.
int num = 6; // without "..." dots
if (!c.minecraft->options.isJoyTouchArea && c.width > 480) {
while (num < Inventory::MAX_SELECTION_SIZE - 1) {
int x0, x1, y;
getSlotPos(0, x0, y);
getSlotPos(num, x1, y);
int width = x1 - x0;
float leftoverPixels = c.width - c.guiScale*width;
if (c.pixelCalc.pixelsToMillimeters(leftoverPixels) < 80)
break;
num++;
}
}
_numSlots = num;
#if defined(__APPLE__)
_numSlots = Mth::Min(7, _numSlots);
#endif
} else {
_numSlots = Inventory::MAX_SELECTION_SIZE; // Xperia Play
}
MAX_MESSAGE_WIDTH = c.guiWidth;
}
float Gui::floorAlignToScreenPixel(float v) {
return (int)(v * Gui::GuiScale) * Gui::InvGuiScale;
}
int Gui::itemCountItoa( char* buffer, int count )
{
if (count < 0)
return 0;
if (count < 10) { // 1 digit
buffer[0] = '0' + count;
buffer[1] = 0;
return 1;
} else if (count < 100) { // 2 digits
int digit = count/10;
buffer[0] = '0' + digit;
buffer[1] = '0' + count - digit*10;
buffer[2] = 0;
} else { // 3 digits -> "99+"
buffer[0] = buffer[1] = '9';
buffer[2] = '+';
buffer[3] = 0;
return 3;
}
return 2;
}
void Gui::tickItemDrop()
{
// Handle item drop
static bool isCurrentlyActive = false;
isCurrentlyActive = false;
int slots = getNumSlots();
#ifndef PLATFORM_DESKTOP
slots--;
#endif
if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) {
int slot = getSlotIdAt(Mouse::getX(), Mouse::getY());
if (slot >= 0 && slot < slots) {
if (slot != _currentDropSlot) {
_currentDropTicks = 0;
_currentDropSlot = slot;
}
isCurrentlyActive = true;
if ((_currentDropTicks += 1.0f) >= DropTicks) {
minecraft->player->inventory->dropSlot(slot, false);
minecraft->level->playSound(minecraft->player, "random.pop", 0.3f, 1);
isCurrentlyActive = false;
}
}
}
if (!isCurrentlyActive) {
_currentDropSlot = -1;
_currentDropTicks = -1;
}
}
void Gui::postError( int errCode )
{
static std::set<int> posted;
if (posted.find(errCode) != posted.end())
return;
posted.insert(errCode);
std::stringstream s;
s << "Something went wrong! (errcode " << errCode << ")\n";
addMessage(s.str());
}
void Gui::setScissorRect( const IntRectangle& bbox )
{
GLuint x = (GLuint)(GuiScale * bbox.x);
GLuint y = minecraft->height - (GLuint)(GuiScale * (bbox.y + bbox.h));
GLuint w = (GLuint)(GuiScale * bbox.w);
GLuint h = (GLuint)(GuiScale * bbox.h);
glScissor(x, y, w, h);
}
float Gui::cubeSmoothStep(float percentage, float min, float max) {
//percentage = percentage * percentage;
//return (min * percentage) + (max * (1 - percentage));
return (percentage) * (percentage) * (3 - 2 * (percentage));
}
void Gui::renderProgressIndicator( const bool isTouchInterface, const int screenWidth, const int screenHeight, float a ) {
ItemInstance* currentItem = minecraft->player->inventory->getSelected();
bool bowEquipped = currentItem != NULL ? currentItem->getItem() == Item::bow : false;
bool itemInUse = currentItem != NULL ? currentItem->getItem() == minecraft->player->getUseItem()->getItem() : false;
if (!isTouchInterface || minecraft->options.isJoyTouchArea || (bowEquipped && itemInUse)) {
minecraft->textures->loadAndBindTexture("gui/icons.png");
glEnable(GL_BLEND);
glBlendFunc2(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
blit(screenWidth/2 - 8, screenHeight/2 - 8, 0, 0, 16, 16);
glDisable(GL_BLEND);
} else if(!bowEquipped) {
const float tprogress = minecraft->gameMode->destroyProgress;
const float alpha = Mth::clamp(minecraft->inputHolder->alpha, 0.0f, 1.0f);
//LOGI("alpha: %f\n", alpha);
if (tprogress <= 0 && minecraft->inputHolder->alpha >= 0) {
glDisable2(GL_TEXTURE_2D);
glEnable2(GL_BLEND);
glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (minecraft->hitResult.isHit())
glColor4f2(1, 1, 1, 0.8f * alpha);
else
glColor4f2(1, 1, 1, Mth::Min(0.4f, alpha*0.4f));
//LOGI("alpha2: %f\n", alpha);
const float x = InvGuiScale * minecraft->inputHolder->mousex;
const float y = InvGuiScale * minecraft->inputHolder->mousey;
glTranslatef2(x, y, 0);
drawArrayVT(rcFeedbackOuter.vboId, rcFeedbackOuter.vertexCount, 24);
glTranslatef2(-x, -y, 0);
glEnable2(GL_TEXTURE_2D);
glDisable(GL_BLEND);
} else if (tprogress > 0) {
const float oProgress = minecraft->gameMode->oDestroyProgress;
const float progress = 0.5f * (oProgress + (tprogress - oProgress) * a);
//static Stopwatch w;
//w.start();
glDisable2(GL_TEXTURE_2D);
glColor4f2(1, 1, 1, 0.8f * alpha);
glEnable(GL_BLEND);
glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
const float x = InvGuiScale * minecraft->inputHolder->mousex;
const float y = InvGuiScale * minecraft->inputHolder->mousey;
glPushMatrix2();
glTranslatef2(x, y, 0);
drawArrayVT(rcFeedbackOuter.vboId, rcFeedbackOuter.vertexCount, 24);
glScalef2(0.5f + progress, 0.5f + progress, 1);
//glDisable2(GL_CULL_FACE);
glColor4f2(1, 1, 1, 1);
glBlendFunc2(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
drawArrayVT(rcFeedbackInner.vboId, rcFeedbackInner.vertexCount, 24, GL_TRIANGLE_FAN);
glPopMatrix2();
glDisable(GL_BLEND);
glEnable2(GL_TEXTURE_2D);
//w.stop();
//w.printEvery(100, "feedback-r ");
}
}
}
void Gui::renderHearts() {
bool blink = (minecraft->player->invulnerableTime / 3) % 2 == 1;
if (minecraft->player->invulnerableTime < 10) blink = false;
int h = minecraft->player->health;
int oh = minecraft->player->lastHealth;
random.setSeed(tickCount * 312871);
int xx = 2;//screenWidth / 2 - getNumSlots() * 10;
int armor = minecraft->player->getArmorValue();
for (int i = 0; i < Player::MAX_HEALTH / 2; i++) {
int yo = 2;
int ip2 = i + i + 1;
if (armor > 0) {
int xo = xx + 80 + i * 8 + 4;
if (ip2 < armor) blit(xo, yo, 16 + 2 * 9, 9 * 1, 9, 9);
else if (ip2 == armor) blit(xo, yo, 16 + 4 * 9, 9 * 1, 9, 9);
else if (ip2 > armor) blit(xo, yo, 16 + 0 * 9, 9 * 1, 9, 9);
}
int bg = 0;
if (blink) bg = 1;
int xo = xx + i * 8;
if (h <= 4) {
yo = yo + random.nextInt(2) - 1;
}
blit(xo, yo, 16 + bg * 9, 9 * 0, 9, 9);
if (blink) {
if (ip2 < oh) blit(xo, yo, 16 + 6 * 9, 9 * 0, 9, 9);
else if (ip2 == oh) blit(xo, yo, 16 + 7 * 9, 9 * 0, 9, 9);
}
if (ip2 < h) blit(xo, yo, 16 + 4 * 9, 9 * 0, 9, 9);
else if (ip2 == h) blit(xo, yo, 16 + 5 * 9, 9 * 0, 9, 9);
}
}
void Gui::renderBubbles() {
if (minecraft->player->isUnderLiquid(Material::water)) {
int yo = 12;
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;
for (int i = 0; i < count + extra; i++) {
int xo = i * 8 + 2;
if (i < count) blit(xo, yo, 16, 9 * 2, 9, 9);
else blit(xo, yo, 16 + 9, 9 * 2, 9, 9);
}
}
}
static OffsetPosTranslator posTranslator;
void Gui::onLevelGenerated() {
if (Level* level = minecraft->level) {
Pos p = level->getSharedSpawnPos();
posTranslator = OffsetPosTranslator((float)-p.x, (float)-p.y, (float)-p.z);
}
}
void Gui::renderDebugInfo() {
// FPS counter (updates once per second)
static float fps = 0.0f;
static float fpsLastTime = 0.0f;
static int fpsFrames = 0;
float now = getTimeS();
fpsFrames++;
if (now - fpsLastTime >= 1.0f) {
fps = fpsFrames / (now - fpsLastTime);
fpsFrames = 0;
fpsLastTime = now;
}
LocalPlayer* p = minecraft->player;
Level* lvl = minecraft->level;
// Position
float px = p->x, py = p->y - p->heightOffset, pz = p->z;
posTranslator.to(px, py, pz);
int bx = (int)floorf(px), by = (int)floorf(py), bz = (int)floorf(pz);
int cx = bx >> 4, cz = bz >> 4;
// Facing direction
float yMod = fmodf(p->yRot, 360.0f);
if (yMod < 0) yMod += 360.0f;
const char* facing;
const char* axis;
if (yMod < 45 || yMod >= 315) { facing = "South"; axis = "+Z"; }
else if (yMod < 135) { facing = "West"; axis = "-X"; }
else if (yMod < 225) { facing = "North"; axis = "-Z"; }
else { facing = "East"; axis = "+X"; }
// Biome
const char* biomeName = "unknown";
if (lvl) {
Biome* biome = lvl->getBiome(bx, bz);
if (biome) biomeName = biome->name.c_str();
}
// Time
long worldTime = lvl ? lvl->getTime() : 0;
long dayTime = worldTime % Level::TICKS_PER_DAY;
long day = worldTime / Level::TICKS_PER_DAY;
long seed = lvl ? lvl->getSeed() : 0;
// Build lines (NULL entry = blank gap)
static char ln[8][96];
sprintf(ln[0], "Minecraft PE 0.6.1 alpha (mcpe64)");
sprintf(ln[1], "%.1f fps", fps);
ln[2][0] = '\0'; // blank separator
sprintf(ln[3], "XYZ: %.3f / %.3f / %.3f", px, py, pz);
sprintf(ln[4], "Block: %d %d %d Chunk: %d %d", bx, by, bz, cx, cz);
sprintf(ln[5], "Facing: %s (%s) (%.1f / %.1f)", facing, axis, p->yRot, p->xRot);
sprintf(ln[6], "Biome: %s", biomeName);
sprintf(ln[7], "Day %ld Time: %ld Seed: %ld", day, dayTime, seed);
const int N = 8;
const float LH = (float)Font::DefaultLineHeight; // 10 font-pixels
const float MGN = 2.0f; // left/top margin in font-pixels
const float PAD = 2.0f; // horizontal padding for background
Font* font = minecraft->font;
// 1) Draw semi-transparent background boxes behind each line
for (int i = 0; i < N; i++) {
if (ln[i][0] == '\0') continue;
float w = (float)font->width(ln[i]);
float x0 = MGN - PAD;
float y0 = MGN + i * LH - 1.0f;
float x1 = MGN + w + PAD;
float y1 = MGN + (i + 1) * LH - 1.0f;
fill(x0, y0, x1, y1, 0x90000000);
}
// 2) Draw text (no extra scale — font coords are in GUI units, same as fill)
Tesselator& t = Tesselator::instance;
t.beginOverride();
for (int i = 0; i < N; i++) {
if (ln[i][0] == '\0') continue;
float y = MGN + i * LH;
int col = (i == 0) ? 0xffFFFF55 : 0xffffffff; // title yellow, rest white
font->draw(ln[i], MGN, y, col);
}
t.endOverrideAndDraw();
}
void Gui::renderSleepAnimation( const int screenWidth, const int screenHeight ) {
int timer = minecraft->player->getSleepTimer();
float amount = (float) timer / (float) Player::SLEEP_DURATION;
if (amount > 1) {
// waking up
amount = 1.0f - ((float) (timer - Player::SLEEP_DURATION) / (float) Player::WAKE_UP_DURATION);
}
int color = (int) (220.0f * amount) << 24 | (0x101020);
fill(0, 0, screenWidth, screenHeight, color);
}
void Gui::renderOnSelectItemNameText( const int screenWidth, Font* font, int ySlot ) {
if(itemNameOverlayTime < 1.0f) {
ItemInstance* item = minecraft->player->inventory->getSelected();
if(item != NULL) {
float x = float(screenWidth / 2 - font->width(item->getName()) / 2);
float y = float(ySlot - 22);
int alpha = 255;
if(itemNameOverlayTime > 0.75) {
float time = 0.25f - (itemNameOverlayTime - 0.75f);
float percentage = cubeSmoothStep(time * 4, 0.0f, 1.0f);
alpha = int(percentage * 255);
}
if(alpha != 0)
font->drawShadow(item->getName(), x, y, 0x00ffffff + (alpha << 24));
}
}
}
// helper structure used by drawColoredString
struct ColorSegment {
std::string text;
uint32_t color;
};
// parse [tag] and [/tag] markers; tags may contain a color name (gold, green, etc.)
static void parseColorTags(const std::string& in, std::vector<ColorSegment>& out) {
uint32_t curColor = 0xffffff;
size_t pos = 0;
while (pos < in.size()) {
size_t open = in.find('[', pos);
if (open == std::string::npos) {
out.push_back({in.substr(pos), curColor});
break;
}
if (open > pos) {
out.push_back({in.substr(pos, open - pos), curColor});
}
size_t close = in.find(']', open);
if (close == std::string::npos) {
out.push_back({in.substr(open), curColor});
break;
}
std::string tag = in.substr(open + 1, close - open - 1);
if (!tag.empty() && tag[0] == '/') {
curColor = 0xffffff;
} else {
std::string lower;
lower.resize(tag.size());
std::transform(tag.begin(), tag.end(), lower.begin(), ::tolower);
if (lower.find("gold") != std::string::npos) curColor = 0xffd700;
else if (lower.find("green") != std::string::npos) curColor = 0x00ff00;
else if (lower.find("yellow") != std::string::npos) curColor = 0xffff00;
else if (lower.find("red") != std::string::npos) curColor = 0xff0000;
else if (lower.find("blue") != std::string::npos) curColor = 0x0000ff;
}
pos = close + 1;
}
}
void Gui::drawColoredString(Font* font, const std::string& text, float x, float y, int alpha) {
std::vector<ColorSegment> segs;
parseColorTags(text, segs);
float cx = x;
for (auto &s : segs) {
int color = s.color + (alpha << 24);
font->drawShadow(s.text, cx, y, color);
cx += font->width(s.text);
}
}
float Gui::getColoredWidth(Font* font, const std::string& text) {
std::vector<ColorSegment> segs;
parseColorTags(text, segs);
float w = 0;
for (auto &s : segs) {
w += font->width(s.text);
}
return w;
}
void Gui::renderChatMessages( const int screenHeight, unsigned int max, bool isChatting, Font* font ) {
// if (minecraft.screen instanceof ChatScreen) {
// max = 20;
// isChatting = true;
// }
//
// glEnable(GL_BLEND);
// glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// glDisable(GL_ALPHA_TEST);
//
// glPushMatrix2();
// glTranslatef2(0, screenHeight - 48, 0);
// // glScalef2(1.0f / ssc.scale, 1.0f / ssc.scale, 1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
int baseY = screenHeight - 48;
for (unsigned int i = 0; i < guiMessages.size() && i < max; i++) {
if (guiMessages.at(i).ticks < 20 * 10 || isChatting) {
float t = guiMessages.at(i).ticks / (20 * 10.0f);
t = 1 - t;
t = t * 10;
if (t < 0) t = 0;
if (t > 1) t = 1;
t = t * t;
int alpha = (int) (255 * t);
if (isChatting) alpha = 255;
if (alpha > 0) {
const float x = 2;
const float y = (float)(baseY - i * 9);
std::string msg = guiMessages.at(i).message;
this->fill(x, y - 1, x + MAX_MESSAGE_WIDTH, y + 8, (alpha / 2) << 24);
glEnable(GL_BLEND);
// special-case join/leave announcements
int baseColor = 0xffffff;
if (msg.find(" joined the game") != std::string::npos ||
msg.find(" left the game") != std::string::npos) {
baseColor = 0xffff00; // yellow
}
// replace previous logic; allow full colour tags now
Gui::drawColoredString(font, msg, x, y, alpha);
}
}
}
}
void Gui::renderToolBar( float a, int ySlot, const int screenWidth ) {
glColor4f2(1, 1, 1, .5);
minecraft->textures->loadAndBindTexture("gui/gui.png");
Inventory* inventory = minecraft->player->inventory;
int xBase, yBase;
getSlotPos(0, xBase, yBase);
const float baseItemX = (float)xBase + 3;
const int slotsWidth = 20 * getNumSlots();
// Left + right side of the selection bar
blit(xBase, yBase, 0, 0, slotsWidth, 22);
blit(xBase + slotsWidth, yBase, 180, 0, 2, 22);
if (_currentDropSlot >= 0 && inventory->getItem(_currentDropSlot)) {
int x = xBase + 3 + _currentDropSlot * 20;
int color = 0x8000ff00;
int yy = (int)(17.0f * (_currentDropTicks + a) / DropTicks);
if (_currentDropTicks >= 3) {
glColor4f2(0, 1, 0, 0.5f);
}
fill(x, ySlot+16-yy, x+16, ySlot+16, color);
}
blit(xBase-1 + 20*inventory->selected, yBase - 1, 0, 22, 24, 22);
glColor4f2(1, 1, 1, 1);
// Flash a slot background
if (_flashSlotId >= 0) {
const float since = getTimeS() - _flashSlotStartTime;
if (since > 0.2f) _flashSlotId = -1;
else {
int x = screenWidth / 2 - getNumSlots() * 10 + _flashSlotId * 20 + 2;
int color = 0xffffff + (((int)(/*0x80 * since +*/ 0x51 - 0x50 * Mth::cos(10 * 6.28f * since))) << 24);
//LOGI("Color: %.8x\n", color);
fill(x, ySlot, x+16, ySlot+16, color);
}
}
glColor4f2(1, 1, 1, 1);
//static Stopwatch w;
//w.start();
Tesselator& t = Tesselator::instance;
t.beginOverride();
float x = baseItemX;
int slots = getNumSlots();
// TODO: if using touchscreen
#ifndef PLATFORM_DESKTOP
slots--;
#endif
for (int i = 0; i < slots; i++) {
renderSlot(i, (int)x, ySlot, a);
x += 20;
}
_inventoryNeedsUpdate = false;
#ifndef PLATFORM_DESKTOP
blit(screenWidth / 2 + 10 * getNumSlots() - 20 + 4, ySlot + 6, 242, 252, 14, 4, 14, 4);
#endif
minecraft->textures->loadAndBindTexture("gui/gui_blocks.png");
t.endOverrideAndDraw();
// Render damaged items (@todo: investigate if it's faster by drawing in same batch)
glDisable2(GL_DEPTH_TEST);
glDisable2(GL_TEXTURE_2D);
t.beginOverride();
x = baseItemX;
for (int i = 0; i < slots; i++) {
ItemRenderer::renderGuiItemDecorations(minecraft->player->inventory->getItem(i), x, (float)ySlot);
x += 20;
}
t.endOverrideAndDraw();
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
//w.stop();
//w.printEvery(100, "gui-slots");
// Draw count
//Tesselator& t = Tesselator::instance;
glPushMatrix2();
glScalef2(InvGuiScale + InvGuiScale, InvGuiScale + InvGuiScale, 1);
const float k = 0.5f * GuiScale;
t.beginOverride();
if (minecraft->gameMode->isSurvivalType()) {
x = baseItemX;
for (int i = 0; i < slots; i++) {
ItemInstance* item = minecraft->player->inventory->getItem(i);
if (item && item->count >= 0)
renderSlotText(item, k*x, k*ySlot + 1, true, true);
x += 20;
}
}
minecraft->textures->loadAndBindTexture("font/default8.png");
t.endOverrideAndDraw();
glPopMatrix2();
}