remove game state classes

scripting improvements
This commit is contained in:
Eduardo Bart 2011-04-22 10:49:46 -03:00
parent a3901b0251
commit e611734396
32 changed files with 428 additions and 435 deletions

View File

@ -55,8 +55,6 @@ ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug")
SET(SOURCES
# game sources
src/main.cpp
src/menustate.cpp
src/teststate.cpp
# game net
src/protocollogin.cpp
@ -66,6 +64,7 @@ SET(SOURCES
src/framework/core/configs.cpp
src/framework/core/resources.cpp
src/framework/core/engine.cpp
src/framework/core/modules.cpp
# framework script
src/framework/script/luascript.cpp

View File

@ -50,8 +50,8 @@ window#enterGameWindow:
margin.bottom: 10
margin.right: 13
onClick: |
enterGameWindow:destroy()
enterGameWindow = nil
self:getParent():destroy()
self:getParent():getParent():unlock()
textEdit#accountNameTextEdit:
anchors.right: parent.right

View File

@ -57,4 +57,6 @@ window#infoWindow:
anchors.left: parent.left
anchors.top: parent.top
margin.top: 191
margin.left: 188
margin.left: 188
onClick: |
self:getParent():destroy()

View File

@ -1,6 +1,15 @@
-- events
-- main menu methods
function MainMenu_create()
mainMenu = loadUI("modules/mainmenu/mainmenu.yml")
end
function MainMenu_destroy()
mainMenu:destroy()
end
function MainMenu_enterGameClicked()
enterGameWindow = loadUI("modules/mainmenu/entergamewindow.yml")
enterGameWindow:getParent():lock(enterGameWindow)
end
function MainMenu_optionsClicked()
@ -12,12 +21,5 @@ function MainMenu_infoClicked()
end
function MainMenu_exitClicked()
exitGame()
onApplicationClose()
end
-- create main menu
function MainMenu_create()
menuPanel = loadUI("modules/mainmenu/mainmenu.yml")
end
MainMenu_create()

View File

@ -1,41 +1,51 @@
panel#mainMenu:
skin: roundedGridPanel
size: [117, 171]
panel#background:
skin:
image: background.png
antialised: true
size: [500, 500]
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin.left: 60
margin.bottom: 70
button#enterGameButton:
text: Enter Game
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 16
onClick: MainMenu_enterGameClicked()
panel#mainMenu:
skin: roundedGridPanel
size: [117, 171]
anchors.left: parent.left
anchors.bottom: parent.bottom
margin.left: 60
margin.bottom: 70
button#enterGameButton:
text: Enter Game
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 16
onClick: MainMenu_enterGameClicked()
button#accessAccountButton:
text: Access Account
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 46
button#accessAccountButton:
text: Access Account
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 46
button#optionsButton:
text: Options
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 76
onClick: MainMenu_optionsClicked()
button#optionsButton:
text: Options
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 76
onClick: MainMenu_optionsClicked()
button#infoButton:
text: Info
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 106
onClick: MainMenu_infoClicked()
button#infoButton:
text: Info
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 106
onClick: MainMenu_infoClicked()
button#exitGameButton:
text: Exit
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 136
onClick: MainMenu_exitClicked()
button#exitGameButton:
text: Exit
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 136
onClick: MainMenu_exitClicked()

View File

@ -0,0 +1,20 @@
-- menu state
function onEnterMenuState()
MainMenu_create()
end
function onLeaveMenuState()
MainMenu_destroy()
end
function onApplicationClose()
onLeaveMenuState()
exitGame()
end
-- here is where everything starts
if not initialStateLoaded then
onEnterMenuState()
setOnApplicationClose(onApplicationClose)
initialStateLoaded = true
end

View File

@ -0,0 +1,10 @@
title: Main menu
notes: Used to create the main menu
enabled: true
#dependencies
interface: 020
author: edubart
version: 0.2
scripts:
- menustate.lua
- mainmenu.lua

View File

@ -110,5 +110,4 @@ window#optionsWindow:
margin.right: 10
margin.bottom: 13
onClick: |
optionsWindow:destroy()
optionsWindow = nil
self:getParent():destroy()

View File

@ -42,14 +42,23 @@ void Engine::init()
void Engine::terminate()
{
// force last state exit
changeState(NULL);
// terminate stuff
g_fonts.terminate();
g_graphics.terminate();
}
void Engine::poll()
{
// poll platform events
Platform::poll();
// poll network events
Connection::poll();
// poll diaptcher tasks
g_dispatcher.poll();
}
void Engine::run()
{
std::string fpsText;
@ -65,14 +74,7 @@ void Engine::run()
while(!m_stopping) {
m_lastFrameTicks = Platform::getTicks();
// poll platform events
Platform::poll();
// poll network events
Connection::poll();
// poll diaptcher tasks
g_dispatcher.poll();
poll();
// render only when visible
if(Platform::isWindowVisible()) {
@ -93,8 +95,6 @@ void Engine::run()
// render
g_graphics.beginRender();
if(m_currentState)
m_currentState->render();
UIContainer::getRootContainer()->render();
// render fps
@ -117,39 +117,19 @@ void Engine::stop()
m_stopping = true;
}
void Engine::changeState(GameState* newState)
{
if(m_currentState)
m_currentState->onLeave();
m_currentState = newState;
if(m_currentState)
m_currentState->onEnter();
}
void Engine::onClose()
{
if(m_currentState)
m_currentState->onClose();
if(m_onCloseCallback)
g_dispatcher.addTask(m_onCloseCallback);
}
void Engine::onResize(const Size& size)
{
g_graphics.resize(size);
UIContainer::getRootContainer()->setSize(size);
if(m_currentState)
m_currentState->onResize(size);
}
void Engine::onInputEvent(const InputEvent& event)
{
bool eventCaptured = false;
// events goes to the state first
if(m_currentState)
eventCaptured = m_currentState->onInputEvent(event);
// if the state didn't capture the input then goes to the gui
if(!eventCaptured)
UIContainer::getRootContainer()->onInputEvent(event);
UIContainer::getRootContainer()->onInputEvent(event);
}

View File

@ -26,19 +26,20 @@
#define ENGINE_H
#include <prerequisites.h>
#include <core/gamestate.h>
#include <core/input.h>
class Engine
{
public:
Engine() : m_stopping(false),
m_running(false),
m_calculateFps(false),
m_currentState(NULL) { }
m_calculateFps(false) { }
void init();
void terminate();
/// Poll events
void poll();
/// Main loop
void run();
@ -46,8 +47,6 @@ public:
void stop();
/// Change current game state
void changeState(GameState *newState);
bool isRunning() const { return m_running; }
bool isStopping() const { return m_stopping; }
@ -64,13 +63,16 @@ public:
/// Return the current ticks on this frame
int getCurrentFrameTicks() const { return m_lastFrameTicks; }
void setOnClose(Callback onCloseCallback) { m_onCloseCallback = onCloseCallback; }
private:
bool m_stopping;
bool m_running;
bool m_calculateFps;
GameState *m_currentState;
int m_lastFrameTicks;
Callback m_onCloseCallback;
};
extern Engine g_engine;

View File

@ -1,55 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GAMESTATE_H
#define GAMESTATE_H
#include <prerequisites.h>
#include <core/input.h>
struct InputEvent;
class GameState
{
public:
GameState() { }
virtual ~GameState() { }
/// Fired when enter the state
virtual void onEnter() = 0;
/// Fired when leaves the state
virtual void onLeave() = 0;
/// Fired when user tries to close the window
virtual void onClose() = 0;
/// Fired for every user input event, this is called before processing UI input and if it returns false the input is not passed to the UI
virtual bool onInputEvent(const InputEvent& event) = 0;
/// Fired when main window is resized
virtual void onResize(const Size& size) = 0;
/// Fired before redering the UI
virtual void render() = 0;
};
#endif // GAMESTATE_H

View File

@ -22,26 +22,5 @@
*/
#ifndef TESTSTATE_H
#define TESTSTATE_H
#include "modules.h"
#include <prerequisites.h>
#include <core/gamestate.h>
class TestState : public GameState
{
public:
TestState() { }
void onEnter();
void onLeave();
void onClose();
bool onInputEvent(const InputEvent& event);
void onResize(const Size& size);
virtual void render();
virtual void update(int ticks, int elapsedTicks);
};
#endif // TESTSTATE_H

View File

@ -22,41 +22,11 @@
*/
#include <core/engine.h>
#include <graphics/graphics.h>
#include "teststate.h"
#ifndef MODULES_H
#define MODULES_H
void TestState::onEnter()
class Modules
{
};
}
void TestState::onLeave()
{
}
void TestState::onClose()
{
g_engine.stop();
}
bool TestState::onInputEvent(const InputEvent& event)
{
return false;
}
void TestState::onResize(const Size& size)
{
}
void TestState::render()
{
}
void TestState::update(int ticks, int elapsedTicks)
{
}
#endif // MODULES_H

View File

@ -27,9 +27,16 @@
#include <graphics/graphics.h>
#include <graphics/textures.h>
Image::Image(TexturePtr texture)
: m_texture(texture)
{
m_textureCoords = Rect(0, 0, m_texture->getSize());
}
Image::Image(const std::string& texture)
{
m_texture = g_textures.get(texture);
m_textureCoords = Rect(0, 0, m_texture->getSize());
}
Image::Image(const std::string& texture, Rect textureCoords) :

View File

@ -31,7 +31,7 @@
class Image
{
public:
Image(TexturePtr texture) : m_texture(texture) { }
Image(TexturePtr texture);
Image(TexturePtr texture, Rect textureCoords) : m_texture(texture), m_textureCoords(textureCoords) { }
Image(const std::string& texture);
Image(const std::string& texture, Rect textureCoords);

View File

@ -33,6 +33,7 @@ void LuaScript::registerFunctions()
registerGlobalFunction("exitGame", &LuaScript::lua_exitGame);
registerGlobalFunction("loadUI", &LuaScript::lua_loadUI);
registerGlobalFunction("getUIRootContainer", &LuaScript::lua_getUIRootContainer);
registerGlobalFunction("setOnApplicationClose", &LuaScript::lua_setOnApplicationClose);
registerClass("UILayout");
registerClass("UIElement", "UILayout");
@ -42,8 +43,11 @@ void LuaScript::registerFunctions()
registerClass("UIContainer", "UIElement");
registerClass("UIWindow", "UIContainer");
registerMemberFunction("UIElement", "getParent", &LuaScript::lua_UIElement_getParent);
registerMemberFunction("UIElement", "destroy", &LuaScript::lua_UIElement_destroy);
registerMemberFunction("UIContainer", "getChildByID", &LuaScript::lua_UIContainer_getChildByID);
registerMemberFunction("UIContainer", "lock", &LuaScript::lua_UIContainer_lock);
registerMemberFunction("UIContainer", "unlock", &LuaScript::lua_UIContainer_unlock);
registerMemberFunction("UIButton", "setOnClick", &LuaScript::lua_UIButton_setOnClick);
}
@ -74,10 +78,34 @@ int LuaScript::lua_getUIRootContainer()
return 1;
}
int LuaScript::lua_setOnApplicationClose()
{
int funcRef = popFunction();
g_engine.setOnClose([this, funcRef] {
pushFunction(funcRef);
callFunction();
});
return 1;
}
int LuaScript::lua_UIElement_destroy()
{
UIElementPtr element = boost::static_pointer_cast<UIElement>(popClassInstance());
element->destroy();
if(element)
element->destroy();
else
pushNil();
return 1;
}
int LuaScript::lua_UIElement_getParent()
{
UIElementPtr element = boost::static_pointer_cast<UIElement>(popClassInstance());
if(element)
pushClassInstance(element->getParent());
else
pushNil();
return 1;
}
@ -87,8 +115,9 @@ int LuaScript::lua_UIButton_setOnClick()
UIButtonPtr button = boost::static_pointer_cast<UIButton>(popClassInstance());
if(button) {
int funcRef = popFunction();
button->setOnClick([this, funcRef] {
button->setOnClick([this, funcRef](UIButtonPtr button) {
pushFunction(funcRef);
setSelf(button);
callFunction();
});
} else {
@ -100,13 +129,29 @@ int LuaScript::lua_UIButton_setOnClick()
int LuaScript::lua_UIContainer_getChildByID()
{
std::string id = popString();
ScriptablePtr object = popClassInstance();
if(object && strcmp("UIContainer", object->getScriptableName()) == 0) {
UIContainerPtr container = boost::static_pointer_cast<UIContainer>(object);
UIContainerPtr container = boost::static_pointer_cast<UIContainer>(popClassInstance());
if(container)
pushClassInstance(container->getChildById(id));
} else {
else
pushNil();
}
return 1;
}
}
int LuaScript::lua_UIContainer_lock()
{
UIElementPtr element = boost::static_pointer_cast<UIElement>(popClassInstance());
UIContainerPtr container = boost::static_pointer_cast<UIContainer>(popClassInstance());
if(container && element) {
container->lockElement(element);
}
return 1;
}
int LuaScript::lua_UIContainer_unlock()
{
UIContainerPtr container = boost::static_pointer_cast<UIContainer>(popClassInstance());
if(container) {
container->unlockElement();
}
return 1;
}

View File

@ -49,10 +49,22 @@ LuaScript::~LuaScript()
lua_close(L);
}
void LuaScript::loadAllModules()
{
std::list<std::string> modules = g_resources.getDirectoryFiles("modules");
foreach(const std::string& module, modules) {
std::list<std::string> moduleFiles = g_resources.getDirectoryFiles(std::string("modules/") + module);
foreach(const std::string& moduleFile, moduleFiles) {
if(boost::ends_with(moduleFile, ".lua"))
loadFile(std::string("modules/") + module + "/" + moduleFile);
}
}
}
bool LuaScript::loadFile(const std::string& fileName)
{
if(!g_resources.fileExists(fileName)) {
logError("lua script file '%s' doesn't exist", fileName.c_str());
logError("script file '%s' doesn't exist", fileName.c_str());
return false;
}
std::string fileContents = g_resources.loadTextFile(fileName);
@ -64,20 +76,19 @@ bool LuaScript::loadBuffer(const std::string& text, const std::string& what)
// load buffer
int ret = luaL_loadbuffer(L, text.c_str(), text.length(), what.c_str());
if(ret != 0){
logError((std::string("while parsing lua code: ") + popString()).c_str());
reportError(popString());
return false;
}
// check if that is loaded as a function
// check if is loaded as a function
if(lua_isfunction(L, -1) == 0) {
logError("lua code not loaded as function");
return false;
}
// execute it
ret = lua_pcall(L, 0, 0, 0);
if(ret != 0){
logError((std::string("while loading lua code: ") + popString()).c_str());
reportError(popString());
return false;
}
@ -86,21 +97,30 @@ bool LuaScript::loadBuffer(const std::string& text, const std::string& what)
int LuaScript::loadBufferAsFunction(const std::string& text, const std::string& what)
{
int ret = luaL_loadstring(L, text.c_str());
int ret = luaL_loadbuffer(L, text.c_str(), text.length(), what.c_str());
if(ret != 0){
logError((std::string("while parsing lua code: ") + popString()).c_str());
reportError(popString());
return LUA_REFNIL;
}
// check if that is loaded as a function
// check if is loaded as a function
if(lua_isfunction(L, -1) == 0) {
logError("lua code not loaded as function");
return LUA_REFNIL;
}
return popFunction();
}
void LuaScript::reportError(const std::string& errorDesc, const char *funcName)
{
std::stringstream ss;
ss << "LUA script error";
if(funcName)
ss << " in " << funcName << "()";
ss << ": " << errorDesc << std::endl;
logError(ss.str().c_str());
}
int LuaScript::getStackSize()
{
return lua_gettop(L);
@ -120,23 +140,15 @@ bool LuaScript::popBoolean()
int32_t LuaScript::popInteger()
{
double d;
if(lua_istable(L, -1)) {
lua_getfield(L, -1, "__intValue");
d = lua_tonumber(L, -1);
pop(2);
}
else{
d = lua_tonumber(L, -1);
pop(1);
}
double d = lua_tonumber(L, -1);
pop(1);
return (int)d;
}
std::string LuaScript::popString()
{
size_t len;
const char* cstr = lua_tolstring(L, -1, &len);
const char *cstr = lua_tolstring(L, -1, &len);
std::string str(cstr, len);
pop();
return str;
@ -169,29 +181,30 @@ void LuaScript::pushUserdata(void* ptr)
void LuaScript::pushClassInstance(const ScriptablePtr& object)
{
if(object->getScriptableName()) {
if(object && object->getScriptableName()) {
new(lua_newuserdata(L, sizeof(ScriptableWeakPtr))) ScriptableWeakPtr(object);
lua_getfield(L, LUA_REGISTRYINDEX, (std::string(object->getScriptableName()) + "_mt").c_str());
lua_setmetatable(L, -2);
} else {
lua_pushnil(L);
}
}
ScriptablePtr LuaScript::popClassInstance()
{
if(!lua_isuserdata(L, -1)) {
logError("Couldn't pop a class instance, top objet is not a valid type (%s)", luaL_typename(L, -1));
reportError(format("couldn't pop a class instance, top objet is not a valid type (%s)", luaL_typename(L, -1)));
lua_pop(L, 1);
return ScriptablePtr();
}
ScriptableWeakPtr *objectRef = (ScriptableWeakPtr *)lua_touserdata(L, -1);
lua_pop(L, 1);
return objectRef->lock();
}
int LuaScript::popFunction()
{
return luaL_ref(L, LUA_REGISTRYINDEX);
ScriptablePtr object = objectRef->lock();
if(!object)
reportError(format("attempt to retrive class instance from a object that is already expired"));
return object;
}
void LuaScript::pushFunction(int functionRef)
@ -199,9 +212,40 @@ void LuaScript::pushFunction(int functionRef)
lua_rawgeti(L, LUA_REGISTRYINDEX, functionRef);
}
void LuaScript::callFunction(int numArgs, bool itReturnsValue)
int LuaScript::popFunction()
{
lua_call(L, numArgs, itReturnsValue ? 1 : 0);
return luaL_ref(L, LUA_REGISTRYINDEX);
}
void LuaScript::releaseFunction(int functionRef)
{
luaL_unref(L, LUA_REGISTRYINDEX, functionRef);
}
void LuaScript::callFunction(int numArgs)
{
int size = lua_gettop(L);
int errorIndex = size - numArgs;
lua_pushcfunction(L, &LuaScript::luaErrorHandler);
lua_insert(L, errorIndex);
int ret = lua_pcall(L, numArgs, 0, errorIndex);
if(ret != 0) {
reportError(popString());
}
lua_remove(L, errorIndex);
if(lua_gettop(L) + numArgs + 1 != size)
reportError("stack size changed!");
}
void LuaScript::setSelf(const ScriptablePtr& scriptable, int envIndex)
{
lua_getfenv(L, envIndex);
pushClassInstance(scriptable);
lua_setfield(L, -2, "self");
lua_pop(L, 1);
}
void LuaScript::setupPackageLoader()
@ -342,3 +386,20 @@ int LuaScript::luaFunctionCallback(lua_State* L)
return (g_lua.*(g_lua.m_functions[id]))();
}
int LuaScript::luaErrorHandler(lua_State *L)
{
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if(!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if(!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1);
lua_pushinteger(L, 2);
lua_call(L, 2, 1);
return 1;
}

View File

@ -29,15 +29,19 @@
#include <script/scriptable.h>
#include <lua.hpp>
#define reportFuncError(a) reportError(a, __FUNCTION__)
class LuaScript
{
public:
LuaScript();
virtual ~LuaScript();
void loadAllModules();
bool loadFile(const std::string& fileName);
bool loadBuffer(const std::string& text, const std::string& what = "luaBuffer");
int loadBufferAsFunction(const std::string& text, const std::string& what = "luaBuffer");
void reportError(const std::string& errorDesc, const char *funcName = NULL);
int getStackSize();
@ -52,9 +56,12 @@ public:
int32_t popInteger();
std::string popString();
int popFunction();
void pushFunction(int functionRef);
void callFunction(int numArgs = 0, bool itReturnsValue = false);
int popFunction();
void releaseFunction(int functionRef);
void callFunction(int numArgs = 0);
void setSelf(const ScriptablePtr& scriptable, int envIndex = -1);
void pushClassInstance(const ScriptablePtr& object);
ScriptablePtr popClassInstance();
@ -70,20 +77,25 @@ public:
static int luaPackageLoader(lua_State* L);
static int luaCollectClassInstance(lua_State* L);
static int luaCompareClassInstances(lua_State* L);
static int luaErrorHandler(lua_State *L);
void registerFunctions();
int lua_UIButton_setOnClick();
int lua_UIElement_getParent();
int lua_UIElement_destroy();
// container functions
int lua_UIContainer_getChildByID();
int lua_UIContainer_lock();
int lua_UIContainer_unlock();
// global functions
int lua_exitGame();
int lua_loadUI();
int lua_getUIRootContainer();
int lua_setOnApplicationClose();
private:
std::vector<LuaCFunction> m_functions;

View File

@ -33,8 +33,8 @@ void UIButton::onInputEvent(const InputEvent& event)
} else if(event.type == EV_MOUSE_LUP && m_state == UI::ButtonDown) {
m_state = UI::ButtonUp;
if(getRect().contains(event.mousePos)) {
if(m_buttonClickCallback) {
g_dispatcher.addTask(m_buttonClickCallback);
if(m_onClickCallback) {
g_dispatcher.addTask(boost::bind(m_onClickCallback, boost::static_pointer_cast<UIButton>(shared_from_this())));
}
}
}

View File

@ -29,8 +29,13 @@
#include <ui/uielement.h>
#include <graphics/borderedimage.h>
class UIButton;
typedef boost::shared_ptr<UIButton> UIButtonPtr;
class UIButton : public UIElement
{
typedef boost::function<void(UIButtonPtr)> OnClick;
public:
UIButton() :
UIElement(UI::Button),
@ -43,16 +48,14 @@ public:
UI::EButtonState getState() { return m_state; }
void setOnClick(const Callback& callback) { m_buttonClickCallback = callback; }
void setOnClick(const OnClick& callback) { m_onClickCallback = callback; }
virtual const char *getScriptableName() const { return "UIButton"; }
private:
std::string m_text;
UI::EButtonState m_state;
Callback m_buttonClickCallback;
OnClick m_onClickCallback;
};
typedef boost::shared_ptr<UIButton> UIButtonPtr;
#endif // UIBUTTON_H

View File

@ -25,6 +25,7 @@
#include <prerequisites.h>
#include <core/resources.h>
#include <ui/uicontainer.h>
#include <core/dispatcher.h>
UIContainerPtr rootContainer(new UIContainer);
@ -71,6 +72,15 @@ UIElementPtr UIContainer::getChildById(const std::string& id)
return UIElementPtr();
}
UIElementPtr UIContainer::getChildByPos(const Point& pos)
{
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if((*it)->getRect().contains(pos))
return (*it);
}
return UIElementPtr();
}
UIElementPtr UIContainer::recursiveGetChildById(const std::string& id)
{
if(getId() == id)
@ -93,6 +103,21 @@ UIElementPtr UIContainer::recursiveGetChildById(const std::string& id)
return UIElementPtr();
}
void UIContainer::pushChildToTop(const UIElementPtr& child)
{
bool removed = false;
for(auto it = m_children.begin(); it != m_children.end(); ++it) {
if((*it) == child) {
removed = true;
m_children.erase(it);
break;
}
}
if(removed) {
m_children.push_back(child);
}
}
void UIContainer::render()
{
UIElement::render();
@ -119,9 +144,10 @@ void UIContainer::onInputEvent(const InputEvent& event)
}
// mouse events
} else if(event.type & EV_MOUSE) {
// mouse down and wheel events only go to elements that contains the mouse position and are not containers
if((event.type & EV_DOWN || event.type & EV_MOUSE_WHEEL) && !child->asUIContainer()) {
if(child->getRect().contains(event.mousePos)) {
// mouse down and wheel events only go to elements that contains the mouse position
if(event.type & EV_DOWN || event.type & EV_MOUSE_WHEEL) {
// the child must contains the mouse position and be on top
if(child->getRect().contains(event.mousePos) && child == getChildByPos(event.mousePos)) {
// focus it
if(event.type == EV_MOUSE_LDOWN && child->isFocusable())
setFocusedElement(child);
@ -171,9 +197,17 @@ void UIContainer::setFocusedElement(UIElementPtr focusedElement)
m_focusedElement->setFocused(false);
m_focusedElement->onFocusChange();
}
m_focusedElement = focusedElement;
m_focusedElement->setFocused(true);
m_focusedElement->onFocusChange();
if(m_focusedElement) {
m_focusedElement->setFocused(true);
m_focusedElement->onFocusChange();
}
}
// when containers are focused they go to the top
if(focusedElement && focusedElement->asUIContainer()) {
g_dispatcher.addTask(boost::bind(&UIContainer::pushChildToTop, asUIContainer(), m_focusedElement));
}
}

View File

@ -44,8 +44,14 @@ public:
void removeChild(UIElementPtr child);
/// Find an element in this container by id
UIElementPtr getChildById(const std::string& id);
/// Find an element by position
UIElementPtr getChildByPos(const Point& pos);
/// Find an element in this container and in its children by id
UIElementPtr recursiveGetChildById(const std::string& id);
/// Pushs a child to the top
void pushChildToTop(const UIElementPtr& child);
int getChildCount() const { return m_children.size(); }
/// Disable all children except the specified element
bool lockElement(UIElementPtr element);

View File

@ -42,12 +42,21 @@ UIElement::UIElement(UI::EElementType type) :
void UIElement::destroy()
{
// we must always have a parent when destroying
assert(getParent());
// we cant delete now, as this call maybe in a event loop
g_dispatcher.addTask(boost::bind(&UIContainer::removeChild, getParent(), asUIContainer()));
// shared ptr must have 4 refs, (this + removeChild callback + parent + use_count call)
assert(asUIElement().use_count() == 4);
setVisible(false);
setEnabled(false);
if(getParent()) {
// schedule removal from parent
g_dispatcher.addTask(boost::bind(&UIContainer::removeChild, getParent(), asUIElement()));
}
// schedule internal destroy (used to check for leaks)
g_dispatcher.addTask(boost::bind(&UIElement::internalDestroy, asUIElement()));
}
void UIElement::internalDestroy()
{
// check for leaks, the number of references must be always 2 here
assert(asUIElement().use_count() == 2);
}
void UIElement::setSkin(const UIElementSkinPtr& skin)

View File

@ -89,6 +89,8 @@ public:
friend class UIContainer;
private:
void internalDestroy();
UI::EElementType m_type;
UIContainerWeakPtr m_parent;
UIElementSkinPtr m_skin;

View File

@ -50,6 +50,8 @@ void UIElementSkin::draw(UIElement *element)
ImagePtr UIElementSkin::loadImage(const YAML::Node& node)
{
ImagePtr image;
TexturePtr texture;
if(node.FindValue("bordered image")) {
const YAML::Node& child = node["bordered image"];
Rect left, right, top, bottom, topLeft, topRight, bottomLeft, bottomRight, center;
@ -72,7 +74,6 @@ ImagePtr UIElementSkin::loadImage(const YAML::Node& node)
if(child.FindValue("center"))
child["center"] >> center;
TexturePtr texture;
if(child.FindValue("image")) {
std::string textureName;
child["image"] >> textureName;
@ -81,17 +82,34 @@ ImagePtr UIElementSkin::loadImage(const YAML::Node& node)
texture = g_uiSkins.getDefaultTexture();
}
image = ImagePtr(new BorderedImage(texture,
left,
right,
top,
bottom,
topLeft,
topRight,
bottomLeft,
bottomRight,
center));
if(texture) {
image = ImagePtr(new BorderedImage(texture,
left,
right,
top,
bottom,
topLeft,
topRight,
bottomLeft,
bottomRight,
center));
}
} else if(node.FindValue("image")) {
std::string textureName;
node["image"] >> textureName;
texture = g_textures.get(textureName);
if(texture) {
image = ImagePtr(new Image(texture));
}
}
if(texture && node.FindValue("antialised")){
bool antialised;
node["antialised"] >> antialised;
if(antialised)
texture->enableBilinearFilter();
}
return image;
}

View File

@ -37,6 +37,7 @@ public:
UIElementSkin(const std::string& name, UI::EElementType elementType) :
m_name(name),
m_elementType(elementType) { }
UIElementSkin() : m_elementType(UI::Element) { }
virtual ~UIElementSkin() { }
/// Load the skin from a YAML node

View File

@ -133,8 +133,8 @@ void UILayout::recalculateLayout()
m_rect.moveVerticalCenter(m_anchors[ANCHOR_VERTICAL_CENTER].getPos() + m_marginTop - m_marginBottom);
} else {
if(m_anchors[ANCHOR_TOP].isValid() && m_anchors[ANCHOR_BOTTOM].isValid()) {
m_rect.setLeft(m_anchors[ANCHOR_TOP].getPos() + m_marginTop);
m_rect.setRight(m_anchors[ANCHOR_BOTTOM].getPos() - m_marginBottom);
m_rect.setTop(m_anchors[ANCHOR_TOP].getPos() + m_marginTop);
m_rect.setBottom(m_anchors[ANCHOR_BOTTOM].getPos() - m_marginBottom);
} else if(m_anchors[ANCHOR_TOP].isValid()) {
m_rect.moveTop(m_anchors[ANCHOR_TOP].getPos() + m_marginTop);
} else if(m_anchors[ANCHOR_BOTTOM].isValid()) {

View File

@ -67,7 +67,7 @@ UIElementPtr UILoader::loadFile(const std::string& file, const UIContainerPtr& p
{
std::string fileContents = g_resources.loadTextFile(file);
if(!fileContents.size()) {
logFatal("Could not load ui file \"%s", file.c_str());
logError("Could not load ui file \"%s", file.c_str());
return UIElementPtr();
}
@ -101,7 +101,7 @@ UIElementPtr UILoader::loadFile(const std::string& file, const UIContainerPtr& p
return element;
} catch (YAML::Exception& e) {
logFatal("Failed to load ui file \"%s\":\n %s", file.c_str(), e.what());
logError("Failed to load ui file \"%s\":\n %s", file.c_str(), e.what());
}
return UIElementPtr();
@ -168,9 +168,15 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
}
// set element skin
if(node.FindValue("skin"))
element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), node["skin"]));
else // apply default skin
if(node.FindValue("skin")) {
if(node["skin"].GetType() == YAML::CT_SCALAR) {
element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), node["skin"]));
} else {
UIElementSkinPtr skin = UIElementSkinPtr(new UIElementSkin());
skin->load(node["skin"]);
element->setSkin(skin);
}
} else // apply default skin
element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), "default"));
// load elements common proprieties
@ -252,6 +258,8 @@ void UILoader::loadElementAnchor(const UIElementPtr& element, EAnchorType type,
UILayoutPtr relativeElement;
if(relativeElementId == "parent" && element->getParent()) {
relativeElement = element->getParent()->asUILayout();
} else if(relativeElementId == "root") {
relativeElement = UIContainer::getRootContainer();
} else {
UIElementPtr tmp = element->backwardsGetElementById(relativeElementId);
if(tmp)
@ -277,6 +285,8 @@ void UILoader::loadButton(const UIButtonPtr& button, const YAML::Node& node)
g_lua.pushClassInstance(button);
g_lua.pushFunction(funcRef);
g_lua.lua_UIButton_setOnClick();
} else {
throw YAML::Exception(node["onClick"].GetMark(), "failed to parse lua script");
}
}
}

View File

@ -42,6 +42,8 @@ public:
virtual const char *getScriptableName() const { return "UIWindow"; }
virtual bool isFocusable() const { return true; }
private:
std::string m_title;
bool m_moving;

View File

@ -29,9 +29,8 @@
#include <core/platform.h>
#include <core/dispatcher.h>
#include <ui/uiskins.h>
#include "menustate.h"
#include "teststate.h"
#include <script/luascript.h>
#include <ui/uicontainer.h>
/// Catches signals so we can exit nicely
void signal_handler(int sig)
@ -43,7 +42,7 @@ void signal_handler(int sig)
static bool stopping = false;
if(!stopping) {
stopping = true;
g_engine.stop();
g_engine.onClose();
}
break;
}
@ -113,26 +112,30 @@ int main(int argc, const char *argv[])
// init engine
g_engine.init();
g_engine.enableFpsCounter();
// load ui skins
g_uiSkins.load("skins/tibiaskin.yml");
// state scope
{
boost::scoped_ptr<MenuState> initialState(new MenuState);
//boost::scoped_ptr<TestState> initialState(new TestState);
g_dispatcher.addTask(boost::bind(&Engine::changeState, &g_engine, initialState.get()));
// load script modules
g_lua.loadAllModules();
Platform::showWindow();
//Platform::hideMouseCursor();
g_engine.enableFpsCounter();
if(!UIContainer::getRootContainer()->getChildCount())
logFatal("no ui loaded at all, no reason to continue running");
// main loop, run everything
g_engine.run();
Platform::showWindow();
//Platform::hideMouseCursor();
// terminate stuff
g_engine.terminate();
// main loop, run everything
g_engine.run();
g_uiSkins.terminate();
}
// poll remaning events
g_engine.poll();
// terminate stuff
g_engine.terminate();
g_uiSkins.terminate();
// save configurations before exiting
saveConfigs();

View File

@ -1,79 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <prerequisites.h>
#include <core/engine.h>
#include <core/dispatcher.h>
#include <graphics/framebuffer.h>
#include <graphics/graphics.h>
#include <graphics/textures.h>
#include <graphics/borderedimage.h>
#include <graphics/fonts.h>
#include <ui/ui.h>
#include "menustate.h"
#include <script/luascript.h>
void MenuState::onEnter()
{
m_background = g_textures.get("background.png");
m_background->enableBilinearFilter();
g_lua.loadFile("modules/mainmenu/mainmenu.lua");
}
void MenuState::onLeave()
{
}
void MenuState::onClose()
{
g_engine.stop();
}
bool MenuState::onInputEvent(const InputEvent& event)
{
return false;
}
void MenuState::onResize(const Size& size)
{
}
void MenuState::render()
{
// render background
static Size minTexCoordsSize(1240, 880);
const Size& screenSize = g_graphics.getScreenSize();
const Size& texSize = m_background->getSize();
Size texCoordsSize = screenSize;
if(texCoordsSize < minTexCoordsSize)
texCoordsSize.scale(minTexCoordsSize, KEEP_ASPECT_RATIO_BY_EXPANDING);
texCoordsSize = texCoordsSize.boundedTo(texSize);
Rect texCoords(0, 0, texCoordsSize);
texCoords.moveBottomRight(texSize.toPoint());
g_graphics.drawTexturedRect(Rect(0, 0, screenSize), m_background, texCoords);
}

View File

@ -1,59 +0,0 @@
/* The MIT License
*
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MENUSTATE_H
#define MENUSTATE_H
#include <prerequisites.h>
#include <core/gamestate.h>
#include <graphics/texture.h>
#include "protocollogin.h"
class MenuState : public GameState
{
public:
MenuState() { }
void onEnter();
void onLeave();
void onClose();
bool onInputEvent(const InputEvent& event);
void onResize(const Size& size);
void render();
private:
void enterGameButton_clicked();
void infoButton_clicked();
void optionsButton_clicked();
void enterGameWindowOkButton_clicked();
TexturePtr m_background;
ProtocolLoginPtr m_protocolLogin;
};
#endif // MENUSTATE_H