#include "gui.h" #include "m_exceptions.h" #include "dataholder.h" #include "gl_spritecache.h" #include "gl_screen.h" #ifdef WITH_LUA #include "lua_vm.h" #endif #include #include "localplayer.h" extern float screen_gamma; namespace GUI { Object::Object(const SDL_Rect & r) : id(0), rect(), color(), borderColor(), drawBorder(false), manager(ManagerHolder::Instance()) { copyRect(r); color.r = 255; color.g = 255; color.b = 255; color.a = 255; } Object::Object(const size_t Id, const SDL_Rect & r) : id(Id), rect(), color(), borderColor(), drawBorder(false), manager(ManagerHolder::Instance()) { copyRect(r); color.r = 255; color.g = 255; color.b = 255; color.a = 255; } Object::Object(const size_t Id, const SDL_Rect & r, const SDL_Color & c) : id(Id), rect(), color(), borderColor(), drawBorder(false), manager(ManagerHolder::Instance()) { copyRect(r); copyColor(c); } void Object::draw() { glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4ub(color.r, color.g, color.b, color.a); glDisable(GL_TEXTURE_2D); glBegin(GL_QUADS); glVertex2i(rect.x, rect.y); glVertex2i(rect.x + rect.w, rect.y); glVertex2i(rect.x + rect.w, rect.y + rect.h); glVertex2i(rect.x, rect.y + rect.h); glEnd(); if (drawBorder) draw_border(); glDisable(GL_BLEND); } void Object::draw_border() { glColor4ub(borderColor.r, borderColor.g, borderColor.b, borderColor.a); glBegin(GL_LINE_LOOP); glVertex2i(rect.x, rect.y); glVertex2i(rect.x + rect.w, rect.y); glVertex2i(rect.x + rect.w, rect.y + rect.h); glVertex2i(rect.x, rect.y + rect.h); glEnd(); } void Object::copyRect(const SDL_Rect & src) { rect.x = src.x; rect.y = src.y; rect.w = src.w; rect.h = src.h; } void Object::copyColor(const SDL_Color & src) { color.r = src.r; color.g = src.g; color.b = src.b; color.a = src.a; } void TexturedObject::draw() { if (texId == 0) return; const OpenGL::PagedTexture & tex = manager.getCachedImage(texId); glColor4ub(color.r, color.g, color.b, color.a); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tex.inPage); glBegin(GL_QUADS); glTexCoord2f(tex.coords[0].u, tex.coords[1].v); glVertex2i(rect.x, rect.y); glTexCoord2f(tex.coords[1].u, tex.coords[1].v); glVertex2i(rect.x + rect.w, rect.y); glTexCoord2f(tex.coords[1].u, tex.coords[0].v); glVertex2i(rect.x + rect.w, rect.y + rect.h); glTexCoord2f(tex.coords[0].u, tex.coords[0].v); glVertex2i(rect.x, rect.y + rect.h); glEnd(); glDisable(GL_TEXTURE_2D); if (drawBorder) draw_border(); } void AnimatedTextureObject::draw() { if (!animation) animation = manager.findAnimation(animId); assert(animation); size_t texId = animation->getCurrentFrame(); const OpenGL::PagedTexture & tex = manager.getCachedImage(texId); glColor4ub(color.r, color.g, color.b, color.a); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tex.inPage); glBegin(GL_QUADS); glTexCoord2f(tex.coords[0].u, tex.coords[1].v); glVertex2i(rect.x, rect.y); glTexCoord2f(tex.coords[1].u, tex.coords[1].v); glVertex2i(rect.x + rect.w, rect.y); glTexCoord2f(tex.coords[1].u, tex.coords[0].v); glVertex2i(rect.x + rect.w, rect.y + rect.h); glTexCoord2f(tex.coords[0].u, tex.coords[0].v); glVertex2i(rect.x, rect.y + rect.h); glEnd(); glDisable(GL_TEXTURE_2D); if (drawBorder) draw_border(); } void Label::draw() { glPushMatrix(); glColor4ub(color.r, color.g, color.b, color.a); glEnable(GL_TEXTURE_2D); if (align == 0) { glTranslatef(rect.x, rect.y, 0); rect.w = uint16_t(font->drawString(text)); } else if (align == 1) { glTranslatef(rect.x, rect.y, 0); rect.w = uint16_t(font->drawString_r2l(text)); } rect.h = font->getHeight(); glPopMatrix(); /* glDisable(GL_TEXTURE_2D); glBegin(GL_LINE_STRIP); glVertex2i(rect.x, rect.y); glVertex2i(rect.x + rect.w, rect.y); glVertex2i(rect.x + rect.w, rect.y + rect.h); glVertex2i(rect.x, rect.y + rect.h); glVertex2i(rect.x, rect.y); glEnd(); */ glDisable(GL_TEXTURE_2D); if (drawBorder) draw_border(); } void Pager::draw() { const OpenGL::PagedTexture & tex = manager.getCachedImage(texId); glColor4ub(color.r, color.g, color.b, color.a); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tex.inPage); glBegin(GL_QUADS); glTexCoord2f(tex.coords[0].u, tex.coords[1].v); glVertex2i(rect.x, rect.y); glTexCoord2f(tex.coords[1].u, tex.coords[1].v); glVertex2i(rect.x + rect.w, rect.y); glTexCoord2f(tex.coords[1].u, tex.coords[0].v); glVertex2i(rect.x + rect.w, rect.y + rect.h); glTexCoord2f(tex.coords[0].u, tex.coords[0].v); glVertex2i(rect.x, rect.y + rect.h); glEnd(); glScissor(rect.x, rect.y, rect.w, rect.h); glEnable(GL_SCISSOR_TEST); glPushMatrix(); glColor4ub(color.r, color.g, color.b, color.a); glTranslatef(rect.x+offset, rect.y+4, 0); int slen = (int)font->drawString("test - hello world, this is a really long message. it will not end. never... or maybe sometime, but not yet. The end."); if (slen + offset < -10) { color.a = 0; manager.remove(this); } INFO << slen <<" " << offset << std::endl; glPopMatrix(); glDisable(GL_SCISSOR_TEST); glDisable(GL_TEXTURE_2D); if (drawBorder) draw_border(); } void Pager::update(Uint32 ticks) { offset -= 1; //if (offset < -50) // color.unused = 0; } void Manager::draw() { GuiObjectListMap::iterator layer_it = guiLayers.begin(); while (layer_it != guiLayers.end()) { GuiObjectList & inThisLayer = layer_it->second; for (GuiObjectList::iterator obj_it = inThisLayer.begin(); obj_it != inThisLayer.end(); ++obj_it) { Object * obj = *obj_it; obj->draw(); } ++layer_it; } glColor4f(1, 1, 1, 1); glEnable(GL_TEXTURE_2D); } Manager::~Manager() { clearObjects(); clearCache(); } void Manager::clearObjects() { INFO << "clearing gui objects" << std::endl; GuiObjectListMap::iterator layer_it = guiLayers.begin(); while (layer_it != guiLayers.end()) { GuiObjectList & list = layer_it->second; for (GuiObjectList::iterator i = list.begin(); i != list.end(); ++i) { delete *i; } list.clear(); ++layer_it; } guiLayers.clear(); } void Manager::clearCache() { INFO << "clearing gui texture cache" << std::endl; for (GuiTextureCache::iterator i = texCache.begin(); i != texCache.end(); ++i) { OpenGL::PagedTexture & p = i->second; glDeleteTextures(1, &p.inPage); } texCache.clear(); for (AnimationMap::iterator i = guiAnimations.begin(); i != guiAnimations.end(); ++i) { delete i->second; } guiAnimations.clear(); } const OpenGL::PagedTexture & Manager::getCachedImage(size_t Id) { GuiTextureCache::iterator i = findByCacheId(Id); if (i == texCache.end()) { std::ostringstream o; o << "cached texture id " << int(Id); throw E_UNKNOWNKEY(o.str()); } return i->second; } Manager::GuiTextureCache::iterator Manager::findByCacheId(const size_t & Id) { return texCache.find(Id); } Manager::GuiObjectListMap::iterator Manager::findLayer(uint8_t l) { return guiLayers.find(l); } void Manager::cacheImageRAW(const std::string & file, size_t k) { texCache.insert(std::make_pair(k, ImageUtil::loadImageRAW(file))); } void Manager::cacheImageRAT(const std::string & file, const std::string & palette, size_t k) { texCache.insert(std::make_pair(k, ImageUtil::loadImageRATWithPalette(file, palette))); } ImageUtil::WidthHeightPair Manager::cacheStyleArrowSprite(const size_t id, int remap) { OpenGTA::GraphicsBase & graphics = OpenGTA::StyleHolder::Instance().get(); PHYSFS_uint16 t = graphics.spriteNumbers.reIndex(id, OpenGTA::GraphicsBase::SpriteNumbers::ARROW); OpenGTA::GraphicsBase::SpriteInfo * info = graphics.getSprite(t); texCache.insert(std::make_pair( id, OpenGL::SpriteCacheHolder::Instance().createSprite(size_t(t), remap, 0, info) )); return ImageUtil::WidthHeightPair(info->w, info->h); } #ifdef WITH_SDL_IMAGE void Manager::cacheImageSDL(const std::string & file, size_t k) { texCache.insert(std::make_pair(k, ImageUtil::loadImageSDL(file))); } #endif void Manager::add(Object * obj, uint8_t onLevel) { GuiObjectListMap::iterator l = findLayer(onLevel); if (l == guiLayers.end()) { GuiObjectList list; list.push_back(obj); guiLayers.insert(std::make_pair(onLevel, list)); return; } GuiObjectList & list = l->second; list.push_back(obj); } void Manager::remove(Object * obj) { for (GuiObjectListMap::iterator l = guiLayers.begin(); l != guiLayers.end(); ++l) { GuiObjectList & list = l->second; for (GuiObjectList::iterator m = list.begin(); m != list.end(); ++m) { Object * o = *m; if (o == obj) { delete o; list.erase(m); return; } } } throw E_UNKNOWNKEY("not a managed object-ptr"); } Object* Manager::findObject(const size_t id) { for (GuiObjectListMap::iterator l = guiLayers.begin(); l != guiLayers.end(); ++l) { GuiObjectList & list = l->second; for (GuiObjectList::iterator m = list.begin(); m != list.end(); ++m) { Object * o = *m; if (o->id == id) return o; } } std::ostringstream o; o << "object by id " << int(id); throw E_UNKNOWNKEY(o.str()); return 0; } void Manager::removeById(const size_t id) { Object * o = findObject(id); if (o) remove(o); //else // ERROR << "failed to find object " << id << " - cannot remove it" << std::endl; } bool Manager::isInside(Object & obj, Uint16 x, Uint16 y) const { if ((obj.rect.x <= x) && (x <= obj.rect.x + obj.rect.w) && (obj.rect.y <= y) && (y <= obj.rect.y + obj.rect.h)) return true; return false; } void Manager::receive(SDL_MouseButtonEvent & mb_event) { Uint32 sh = OpenGL::ScreenHolder::Instance().getHeight(); GuiObjectListMap::reverse_iterator l = guiLayers.rbegin(); while (l != guiLayers.rend()) { GuiObjectList & list = l->second; for (GuiObjectList::iterator i = list.begin(); i != list.end(); ++i) { Object * obj = *i; if (isInside(*obj, mb_event.x, sh - mb_event.y)) { // std::cout << "mouse inside obj id: " << obj->id << " at " << mb_event.x << "," << mb_event.y << std::endl; obj->receive(mb_event); return; } } ++l; } } void Manager::update(uint32_t nowticks) { for (AnimationMap::iterator i = guiAnimations.begin(); i != guiAnimations.end(); ++i) { i->second->update(nowticks); } GuiObjectListMap::iterator layer_it = guiLayers.begin(); while (layer_it != guiLayers.end()) { GuiObjectList & inThisLayer = layer_it->second; for (GuiObjectList::iterator obj_it = inThisLayer.begin(); obj_it != inThisLayer.end(); ++obj_it) { Object * obj = *obj_it; obj->update(nowticks); } ++layer_it; } } Animation* Manager::findAnimation(uint16_t id) { AnimationMap::iterator i = guiAnimations.find(id); return i->second; } void Manager::createAnimation(const std::vector & indices, uint16_t fps, size_t k) { Animation * anim = new Animation(indices, fps); guiAnimations.insert(std::make_pair(k, anim)); anim->set(Util::Animation::PLAY_FORWARD, Util::Animation::LOOP); } uint16_t Animation::getCurrentFrame() { return indices[getCurrentFrameNumber()]; } const int WEAPON_DISPLAY_ID = 100; const SDL_Rect sdl_rect(size_t a, size_t b, size_t c, size_t d) { SDL_Rect rect; rect.x = a; rect.y = b; rect.w = c; rect.h = d; return rect; } WeaponDisplay::WeaponDisplay(const SDL_Rect & r) : Object(WEAPON_DISPLAY_ID, r), img(sdl_rect(r.x+2, r.y+2, r.w - 4, r.h - 4), 0), label(sdl_rect(r.x+2, r.y+r.h, r.w - 4, r.h - 4), "", "F_MTEXT.FON", 1) { } void WeaponDisplay::draw() { img.draw(); label.draw(); } size_t WeaponDisplay::getWeaponIdx(const size_t wt) { if (wt == 0) return 0; if (wt < 5) return 1000 + wt; return 0; } void WeaponDisplay::setWeapon(const size_t wt) { img.texId = getWeaponIdx(wt); try { manager.getCachedImage(img.texId); } catch (const Util::UnknownKey & ek) { WARN << "GUI image for weapon " << wt << " not loaded - retrying" << std::endl; manager.cacheStyleArrowSprite(img.texId, -1); } } void ScrollBar::draw() { Object::draw(); glColor3ub(innerColor.r, innerColor.g, innerColor.b); glBegin(GL_QUADS); glVertex2i(rect.x+2, rect.y+2); glVertex2i(rect.x + int(rect.w * value) -2, rect.y+2); glVertex2i(rect.x + int(rect.w * value) -2, rect.y + rect.h-2); glVertex2i(rect.x+2, rect.y + rect.h-2); glEnd(); } void ScrollBar::receive(SDL_MouseButtonEvent & mb_event) { value = (mb_event.x - rect.x) / float(rect.w - 4); INFO << value << std::endl; if (changeCB) changeCB(value * 2); else WARN << "No callback function set - I ain't seen nothing" << std::endl; /* SDL_SetGamma(value * 2, value * 2, value * 2); Object * o = ManagerHolder::Instance().findObject(101); if (o) { std::ostringstream os; os << "Gamma: " << value * 2; static_cast(o)->text = os.str(); }*/ } void screen_gamma_callback(float v) { //Xperimental -- Vladislav Khorev vladislav.khorev@fishrungames.com /* screen_gamma = v; SDL_SetGamma(v, v, v); #ifdef WITH_LUA OpenGTA::Script::LuaVM & vm = OpenGTA::Script::LuaVMHolder::Instance(); lua_State *L = vm.getInternalState(); int top = lua_gettop(L); lua_getglobal(L, "config"); if (lua_type(L, -1) != LUA_TTABLE) { lua_pop(L, 1); lua_newtable(L); lua_pushvalue(L, -1); lua_setglobal(L, "config"); } uint8_t sf = OpenGTA::StyleHolder::Instance().get().getFormat(); if (sf) vm.setFloat("screen_gamma_g24", v); else vm.setFloat("screen_gamma_gry", v); lua_settop(L, top); #endif Object * o = ManagerHolder::Instance().findObject(GAMMA_LABEL_ID); if (o) { std::ostringstream os; os << "Gamma: " << v; static_cast(o)->text = os.str(); } */ } AnimStatusDisplay* wantedLevel = NULL; Label* cashLabel = NULL; void create_ingame_gui(bool is32bit) { OpenGL::Screen & screen = OpenGL::ScreenHolder::Instance(); GUI::Manager & gm = ManagerHolder::Instance(); assert(!wantedLevel); { SDL_Rect r; r.h = 32; r.x = screen.getWidth() / 2 - 50; r.y = screen.getHeight() - r.h; r.w = 100; SDL_Rect rs; rs.x = rs.y = 0; rs.w = rs.h = 16; gm.cacheStyleArrowSprite(16, -1); gm.cacheStyleArrowSprite(17, -1); std::vector anim2frames(2); anim2frames[0] = 16; anim2frames[1] = 17; gm.createAnimation(anim2frames, 10, 2); wantedLevel = new AnimStatusDisplay(WANTED_LEVEL_ID, r, rs, 2); /* wantedLevel->borderColor.r = wantedLevel->borderColor.g = wantedLevel->borderColor.b = wantedLevel->borderColor.unused = 255; wantedLevel->drawBorder = 1; */ gm.add(wantedLevel, 50); } assert(!cashLabel); { SDL_Rect r; r.x = screen.getWidth() - 5; r.y = screen.getHeight() - 30; cashLabel = new Label(GUI::CASH_ID, r, "0", "F_MTEXT.FON", 1); cashLabel->align = 1; gm.add(cashLabel, 50); } } void update_ingame_gui_values() { OpenGTA::PlayerController & pc = OpenGTA::LocalPlayer::Instance(); if (wantedLevel) wantedLevel->number = pc.getWantedLevel(); if (cashLabel) { std::ostringstream os; os << pc.getCash(); cashLabel->text = os.str(); } } void remove_ingame_gui() { GUI::Manager & gm = ManagerHolder::Instance(); if (wantedLevel) gm.remove(wantedLevel); wantedLevel = NULL; if (cashLabel) gm.remove(cashLabel); cashLabel = NULL; } }