diff --git a/build/linux/Makefile b/build/linux/Makefile index a8a0097..0526762 100644 --- a/build/linux/Makefile +++ b/build/linux/Makefile @@ -18,7 +18,7 @@ OBJ=main.o fio.o Core.o Log.o QMap.o Tile.o Thought.o OBJ+=render/Quat.o render/Mat4.o render/Vec.o render/Mesh.o render/Texture.o render/Program.o render/RenderScene.o render/RenderSet.o render/RenderObject.o render/RenderCamera.o render/RenderView.o render/Sprite.o OBJ+=render/Font.o render/Ui.o render/UiText.o OBJ+=states/GameState.o states/UiSubState.o -OBJ+=Controller.o PlayerController.o +OBJ+=Controller.o PlayerController.o AiController.o OBJ+=things/Thing.o things/Denizen.o things/SightSeer.o things/GnashingGibber.o OBJ+=abilities/Ability.o abilities/GodHand.o abilities/VampiricNom.o OBJ_DIR=obj diff --git a/data/shaders/sprite_outline_fs.glsl b/data/shaders/sprite_outline_fs.glsl index 388c95e..03e9b5a 100644 --- a/data/shaders/sprite_outline_fs.glsl +++ b/data/shaders/sprite_outline_fs.glsl @@ -14,4 +14,4 @@ void main() { gl_FragColor = vec4(0.75, 0.5, 0.5, c); if (gl_FragColor.a == 0.0) discard; //gl_FragColor = vec4(0.5, 0.0, 0.0, 0.5); -} \ No newline at end of file +} diff --git a/data/shaders/ui_fs.glsl b/data/shaders/ui_fs.glsl index 12ba389..312f312 100644 --- a/data/shaders/ui_fs.glsl +++ b/data/shaders/ui_fs.glsl @@ -2,13 +2,34 @@ // uniforms uniform sampler2D texture_sampler; uniform float transparency; +uniform float vertical_fade; // input varying vec2 frag_uv; void main() { gl_FragColor = texture2D(texture_sampler, frag_uv); - gl_FragColor.a -= transparency; - gl_FragColor.a -= 0.10; + // grossly outline the UI element + if (gl_FragColor.a < 0.1 && frag_uv.x >= 0.001 && frag_uv.x <= 0.999 && frag_uv.y >= 0.001 && frag_uv.y <= 0.999) { + vec4 t = texture2D(texture_sampler, frag_uv + vec2( 0.0f, -0.0025f )); + vec4 b = texture2D(texture_sampler, frag_uv + vec2( 0.0f, 0.0025f )); + vec4 l = texture2D(texture_sampler, frag_uv + vec2( -0.0025f, 0.0f )); + float c = 0.0f; + if (t.a > 0.75) { + gl_FragColor = vec4(0.5 - t.r, 0.5 - t.g, 0.5 - t.b, 1.0 - transparency); + } else if (b.a > 0.75) { + gl_FragColor = vec4(0.5 - b.r, 0.5 - b.g, 0.5 - b.b, 1.0 - transparency); + } else if (l.a > 0.75) { + gl_FragColor = vec4(0.5 - l.r, 0.5 - l.g, 0.5 - l.b, 1.0 - transparency); + } + } else { + gl_FragColor.a -= transparency; + gl_FragColor.a -= 0.10; + } + // modify alpha/color based on UV offsets. + if (vertical_fade > 0) { + //gl_FragColor.a -= 0.35 - frag_uv.y; // this increases alpha towards top + gl_FragColor.rgb -= (0.35 - frag_uv.y/2); // this darkens rgb towards top + } if (gl_FragColor.a == 0.0) discard; //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); -} \ No newline at end of file +} diff --git a/data/sprites/ui_time.png b/data/sprites/ui_time.png new file mode 100644 index 0000000..af2258b Binary files /dev/null and b/data/sprites/ui_time.png differ diff --git a/src/AiController.cpp b/src/AiController.cpp new file mode 100644 index 0000000..0639531 --- /dev/null +++ b/src/AiController.cpp @@ -0,0 +1,22 @@ +#include "AiController.hpp" +#include "Thought.hpp" +#include "Thing.hpp" +#include "Core.hpp" +#include "Log.hpp" + +AiController::AiController() { +} +AiController::~AiController() { +} +void AiController::doThink(int ticks) { + std::vector::iterator t_it, t_end; + for (t_it = things.begin(), t_end = things.end(); t_it != t_end; ++t_it) { + Thing *thing = *t_it; + } +} +void AiController::controlThing(Thing *thing_) { + things.push_back(thing_); +} +void AiController::recvMessage(std::string string) { + LOG(LOG_INFO) << string; +} diff --git a/src/AiController.hpp b/src/AiController.hpp new file mode 100644 index 0000000..c95763b --- /dev/null +++ b/src/AiController.hpp @@ -0,0 +1,16 @@ +#ifndef AICONTROLLER_HPP +#define AICONTROLLER_HPP +#include "Controller.hpp" +#include +class AiController : Controller { + friend class GameState; + public: + AiController(); + ~AiController(); + virtual void doThink(int ticks); + virtual void controlThing(Thing *thing_); + virtual void recvMessage(std::string string); + protected: + std::vector things; +}; +#endif diff --git a/src/Controller.cpp b/src/Controller.cpp index dd242c2..5bcfdd2 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -3,7 +3,7 @@ Controller::Controller() { thing = NULL; + tick_rate = 50; } Controller::~Controller() { - } diff --git a/src/Controller.hpp b/src/Controller.hpp index a2ee2eb..572225f 100644 --- a/src/Controller.hpp +++ b/src/Controller.hpp @@ -14,7 +14,10 @@ class Controller { Controller(); ~Controller(); virtual void recvMessage(std::string string) { return; }; + virtual void doThink(int ticks) { return; }; + virtual void controlThing(Thing *thing_) { thing = thing_; }; protected: Thing *thing; + int tick_rate; }; #endif diff --git a/src/Core.cpp b/src/Core.cpp index e52ff34..551dcec 100644 --- a/src/Core.cpp +++ b/src/Core.cpp @@ -47,9 +47,22 @@ int Core::initSystem() { LOG(LOG_ERROR) << TTF_GetError(); return 1; } - // get our displayyyy + // ** Get our display, checking bounding boxes for multi monitor setups + int di = SDL_GetNumVideoDisplays()-1; // display index (monitor) + /*int mx, my; + SDL_GetGlobalMouseState(&mx, &my); // SDL 2.0.4 + LOG(LOG_INFO) << "mouse: " << mx << "x" << my; + int dc = SDL_GetNumVideoDisplays(); + for (di = 0; di < dc; di++) { + SDL_Rect dr; + SDL_GetDisplayBounds(di, &dr); + if ((mx >= dr.x && mx <= dr.x+dr.w) && (my >= dr.y && my <= dr.y+dr.h)) { + break; + } + }*/ + // get our window size from display SDL_DisplayMode dmode; - SDL_GetDesktopDisplayMode(0, &dmode); + SDL_GetCurrentDisplayMode(di, &dmode); v_width = dmode.w - dmode.w/4; // TEMP: just for a smaller window per-default v_height = dmode.h - dmode.h/4; v_flags = SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE; @@ -279,8 +292,8 @@ Font* Core::getFont(const char *font_name) { char *fbuf = NULL; size_t flen = 0; flen = fileToMem(font_name, &fbuf); - font = new Font(font_name, 13, fbuf, flen); + font = new Font(font_name, 11, fbuf, flen); font_table->set(font_name, font); } return font; -} \ No newline at end of file +} diff --git a/src/abilities/GodHand.cpp b/src/abilities/GodHand.cpp index e657bca..e03279f 100644 --- a/src/abilities/GodHand.cpp +++ b/src/abilities/GodHand.cpp @@ -1,4 +1,5 @@ #include "GodHand.hpp" +#include "QMap.hpp" #include "Controller.hpp" #include "Thing.hpp" @@ -11,10 +12,22 @@ GodHand::~GodHand() { delete sprite; } int GodHand::onUse() { - owner->getController()->recvMessage("The nature of Things have changed."); + Vec3 dir = owner->getDirection(); + Thing *othing = owner->getMap()->checkThings(owner, dir.x, dir.y, 24, 24); + if (othing != NULL) { + Controller *ocontroller = othing->getController(); + othing->setController(owner->getController()); + owner->setController(ocontroller); + owner->remAbility(name); + othing->unshiftAbility(this); + owner->getController()->recvMessage("The nature of Things have changed."); + // TODO: haiku-based swap difference, each ability has 5 and 7 syllable versions that create a 5-7-5 message + } else { + owner->getController()->recvMessage("You yearn for a new host."); + } return 0; } int GodHand::onPrompt() { owner->getController()->recvMessage("Soul swap with what?"); return 0; -} \ No newline at end of file +} diff --git a/src/render/Sprite.cpp b/src/render/Sprite.cpp index 9c3f525..2f31166 100644 --- a/src/render/Sprite.cpp +++ b/src/render/Sprite.cpp @@ -64,7 +64,6 @@ void Sprite::doAnimation(unsigned int ticks) { } // void Sprite::setOffset(int col_, int row_) { - LOG(LOG_INFO) << x << "x" << y; col = col_; row = row_; uv_x = col * uvw; @@ -76,4 +75,4 @@ void Sprite::setPosition(float xpos, float ypos) { // NOTE: since we're using orthogonal rendering, we send ypos as the depth as well (so we don't have to sort) object->setTranslation(xpos, ypos, -ypos); object->calcMatrix(); -} \ No newline at end of file +} diff --git a/src/render/Ui.cpp b/src/render/Ui.cpp index 9754b7f..895e647 100644 --- a/src/render/Ui.cpp +++ b/src/render/Ui.cpp @@ -10,6 +10,7 @@ Ui::Ui() { timeout = 0; timeout_elapsed = 0; transparency = 0.0f; + vertical_fade = 0.0f; } Ui::Ui(const char *filename, int width_, int height_) { texture = NULL; @@ -19,6 +20,7 @@ Ui::Ui(const char *filename, int width_, int height_) { timeout = 0; timeout_elapsed = 0; transparency = 0.0f; + vertical_fade = 0.0f; setImage(filename, width_, height_); } Ui::~Ui() { @@ -71,6 +73,11 @@ void Ui::setPosition(float xpos, float ypos) { x = xpos; y = ypos; } +void Ui::setRotation(int angle_) { + angle = angle_; + object->setRotation(0, angle, 0); + object->calcMatrix(); +} void Ui::setOffset(int col_, int row_) { col = col_; row = row_; diff --git a/src/render/Ui.hpp b/src/render/Ui.hpp index f9561fd..2d72dac 100644 --- a/src/render/Ui.hpp +++ b/src/render/Ui.hpp @@ -15,12 +15,14 @@ class Ui { virtual void setPosition(float x, float y); void setOffset(int col, int row); void setTimeout(int time); + void setRotation(int angle); Texture *getTexture() { return texture; }; RenderObject *getObject() { return object; }; int getWidth() { return width; }; int getHeight() { return height; }; void doRefresh(float screen_w, float screen_h); void doProcess(unsigned int ticks); + int getAngle() { return angle; }; int flags; enum Flags { UI_DESTROY = (1 << 1) @@ -32,12 +34,14 @@ class Ui { int width, height; // width and height of ui element int col, row; // column and row in spritesheet float x, y; // x and y in coordinates + int angle; // angle in eular int max_x, max_y; // max x and y coordinates derived from texture float uv_x, uv_y; // uv x and y offsets float uvw, uvh; // sprite size in UV terms float timeout; // time to keep this Ui up before closing it float timeout_elapsed; // elapsed time for ^ float transparency; // alpha of this element, 1.0 = full transparent, 0.0 = full opacity + float vertical_fade; // vertical fading to apply, 0 = none }; bool destroyUi(Ui* ui); diff --git a/src/render/UiText.cpp b/src/render/UiText.cpp index ef62762..55ff1cb 100644 --- a/src/render/UiText.cpp +++ b/src/render/UiText.cpp @@ -4,12 +4,14 @@ #include UiText::UiText() { + vertical_fade = 1.0f; } UiText::UiText(const char *text_, Font *font_, SDL_Color color_) { text = text_; font = font_; color = color_; texture = NULL; + vertical_fade = 1.0f; setText(text_); } UiText::~UiText() { @@ -30,11 +32,15 @@ void UiText::setText(std::string text_) { text = text_; buildText(text); - width = texture->w/2; height = texture->h/2; x = y = 0; row = col = 0; + width = texture->w/2; height = texture->h/2; max_x = texture->w/2 / width; max_y = texture->h/2 / height; + /*width = texture->w; height = texture->h; + max_x = texture->w / width; + max_y = texture->h / height;*/ + object = new RenderObject(); object->setScale(1.0f, 1.0f, 1.0f); object->calcMatrix(); @@ -73,9 +79,7 @@ Texture *UiText::buildText(std::string string) { int width = 0; int height = 0; SDL_Surface *line_surface = NULL; - LOG(LOG_INFO) << "about to build!"; while(std::getline(ss, line, '\n')) { - //LOG(LOG_INFO) << line; line_surface = TTF_RenderUTF8_Blended(font->font, line.c_str(), color); if (line_surface == NULL) { line_surfaces.push_back(NULL); @@ -86,7 +90,6 @@ Texture *UiText::buildText(std::string string) { height += line_surface->h; } } - LOG(LOG_INFO) << "built!"; SDL_Surface *final_surface = SDL_CreateRGBSurface(0, width, height, line_surface->format->BitsPerPixel, line_surface->format->Rmask, line_surface->format->Gmask, line_surface->format->Bmask, line_surface->format->Amask); SDL_Rect offset = { 0, 0, 0, 0 }; std::vector::iterator surface_it, surface_end; @@ -105,4 +108,4 @@ Texture *UiText::buildText(std::string string) { texture->buildTexture(); //SDL_FreeSurface(final_surface); // NOTE: surface is freed by the Texture return texture; -} \ No newline at end of file +} diff --git a/src/states/GameState.cpp b/src/states/GameState.cpp index 24c7239..b580bce 100644 --- a/src/states/GameState.cpp +++ b/src/states/GameState.cpp @@ -13,9 +13,10 @@ GameState::GameState() { camera = new RenderCamera(); camera->setRenderView(new RenderView(core.getWidth(), core.getHeight())); - camera->setPitch(90.0f); + //camera->setPitch(75.0f); + //camera->setPosition(0, 1000, 0); /*camera->setPosition(0, core.getHeight()*2, 0);*/ - camera->setPosition(0, 0, 0); + camera->setPitch(90.0f); camera->setRenderMode(1); camera->setSize(core.getHeight()); camera->updateProjection(core.getWidth(), core.getHeight()); @@ -48,14 +49,20 @@ GameState::GameState() { } } - pc.thing = new GnashingGibber(); - pc.thing->setController(&pc); - pc.thing->unshiftAbility(new GodHand()); - current_map->addThing(pc.thing, 1, 1); + Thing *thing = new GnashingGibber(); + thing->setController(&pc); + thing->unshiftAbility(new GodHand()); + current_map->addThing(thing, 1, 1); - current_map->addThing(new Denizen(), 2, 2); + thing = new Denizen(); + thing->setController(&ac); - current_map->addThing(new SightSeer(), 4, 4); + current_map->addThing(thing, 2, 2); + + thing = new SightSeer(); + thing->setController(&ac); + + current_map->addThing(thing, 4, 4); ui_state = new UiSubState(); @@ -65,6 +72,10 @@ GameState::GameState() { ui_state->pushUi(ui_character); ui_character->setImage("data/sprites/gnashing_gibber.png", 16, 32); + ui_time = new Ui("data/sprites/ui_time.png", 16, 16); + ui_time->setPosition(32, 32); + ui_state->pushUi(ui_time); + SDL_Color color = { 128, 255, 128, 255 }; ui_sheet_name = new UiText("Gnashing Gibber", core.getFont("data/fonts/default.ttf"), color); ui_state->pushUi(ui_sheet_name); @@ -139,8 +150,14 @@ int GameState::onEvent(SDL_Event event) { int GameState::doProcess(unsigned int ticks) { ui_state->doProcess(ticks); // 1. for each AiController, run their doThink + std::vector::iterator c_it, c_end; + for (c_it = controllers.begin(), c_end = controllers.end(); c_it != c_end; ++c_it) { + Controller *controller = *c_it; + controller->doThink(ticks); + } // 2. for each thing, run their doThink if ((pc.thing->thoughts.size() <= 0 && pc.thing->thought_current.type == 0) && pc.thing->velocity.x == 0 && pc.thing->velocity.y == 0) return 0; + ui_time->setRotation(ui_time->getAngle()+1); std::vector::iterator thing_it, thing_end; for (thing_it = current_map->things.begin(), thing_end = current_map->things.end(); thing_it != thing_end; ++thing_it) { Thing *thing = *thing_it; @@ -156,8 +173,8 @@ int GameState::doProcess(unsigned int ticks) { thing->getSprite()->doAnimation(ticks); float x = thing->velocity.x; float y = thing->velocity.y; - if (x != 0) thing->velocity.x += -x/2; - if (y != 0) thing->velocity.y += -y/2; + if (x != 0) thing->velocity.x += -x/4; + if (y != 0) thing->velocity.y += -y/4; if (thing->velocity.x > -0.05 && thing->velocity.x < 0.05) thing->velocity.x = 0; if (thing->velocity.y > -0.05 && thing->velocity.y < 0.05) thing->velocity.y = 0; if (x != 0 || y != 0) { @@ -165,7 +182,6 @@ int GameState::doProcess(unsigned int ticks) { if (othing != NULL) { thing->setVelocity(Vec3(0, 0, 0)); thing->doTouch(othing); - othing->onTouch(thing); } Tile *tile = current_map->checkTiles(thing, x, y, thing->getSprite()->getWidth(), thing->getSprite()->getHeight()/3); if (tile == NULL) { @@ -189,6 +205,7 @@ int GameState::doProcess(unsigned int ticks) { } // 4. Finally, set the camera to the player's current thing camera->setPosition(pc.thing->position.x, camera->getPosition().y, -pc.thing->position.y); + //camera->setPosition(pc.thing->position.x, camera->getPosition().y, -pc.thing->position.y*1.5); camera->doRefresh(); return 0; } @@ -306,6 +323,9 @@ int GameState::doRender() { int GameState::refreshUi() { int width = core.getWidth(); int height = core.getHeight(); + + ui_time->setPosition(16, 16); + ui_time->doRefresh(width, height); ui_sheet->setPosition(width - ui_sheet->getWidth()*2, 0); ui_sheet->doRefresh(width, height); diff --git a/src/states/GameState.hpp b/src/states/GameState.hpp index bf29e45..638ad4a 100644 --- a/src/states/GameState.hpp +++ b/src/states/GameState.hpp @@ -2,6 +2,7 @@ #define GAMESTATE_HPP #include "State.hpp" #include "Controller.hpp" +#include "AiController.hpp" #include "PlayerController.hpp" #include "QMap.hpp" #include "Ui.hpp" @@ -23,6 +24,7 @@ class GameState : public State { std::vector maps; std::vector controllers; PlayerController pc; + AiController ac; // rendering GLuint program; // get our uniforms (move elsewhere) @@ -52,6 +54,7 @@ class GameState : public State { UiText *ui_sheet_name; UiText *ui_map_name; Ui *ui_controls; + Ui *ui_time; UiText *ui_console; UiText *ui_intro; std::vector ui_abilities; diff --git a/src/states/UiSubState.cpp b/src/states/UiSubState.cpp index 36b9bf4..3f2e47a 100644 --- a/src/states/UiSubState.cpp +++ b/src/states/UiSubState.cpp @@ -2,6 +2,7 @@ #include "Core.hpp" #include // remove_if #include +#include "Log.hpp" UiSubState::UiSubState() { // UI camera @@ -21,6 +22,7 @@ UiSubState::UiSubState() { ui_program_texture = glGetUniformLocation(ui_program, "texture_sampler"); ui_program_uv_offset = glGetUniformLocation(ui_program, "uv_offset"); ui_program_transparency = glGetUniformLocation(ui_program, "transparency"); + ui_program_v_fade = glGetUniformLocation(ui_program, "vertical_fade"); // FIXME: should this be a bool? // get our attributes ui_program_vp = glGetAttribLocation(ui_program, "vp"); ui_program_uv = glGetAttribLocation(ui_program, "uv"); @@ -74,8 +76,11 @@ int UiSubState::doRender() { glUniform2f(ui_program_uv_offset, 0.0f, 0.0f); // send alpha glUniform1f(ui_program_transparency, ui->transparency); + // send fade amount + glUniform1f(ui_program_v_fade, ui->vertical_fade); + LOG(LOG_INFO) << "vfade is " << ui->vertical_fade; // draw glDrawArrays(GL_TRIANGLES, 0, mesh->vertices.size()); } return 0; -} \ No newline at end of file +} diff --git a/src/states/UiSubState.hpp b/src/states/UiSubState.hpp index 4e35641..9a73810 100644 --- a/src/states/UiSubState.hpp +++ b/src/states/UiSubState.hpp @@ -24,6 +24,7 @@ class UiSubState { GLuint ui_program_modelview; GLuint ui_program_texture; GLuint ui_program_transparency; + GLuint ui_program_v_fade; // get our attributes GLuint ui_program_vp; GLuint ui_program_uv; diff --git a/src/things/GnashingGibber.cpp b/src/things/GnashingGibber.cpp index f4baa84..0757450 100644 --- a/src/things/GnashingGibber.cpp +++ b/src/things/GnashingGibber.cpp @@ -53,6 +53,8 @@ int GnashingGibber::onThought(Thought thought) { } return 0; } -int GnashingGibber::onTouch(Thing *thing) { +int GnashingGibber::onTouch(Thing *touched) { + controller->recvMessage("You bite "+touched->getName()); + touched->getController()->recvMessage(name+" bites you"); return 0; } diff --git a/src/things/Thing.cpp b/src/things/Thing.cpp index 5d54fd6..a0e4836 100644 --- a/src/things/Thing.cpp +++ b/src/things/Thing.cpp @@ -32,13 +32,8 @@ int Thing::doThink(unsigned int ticks) { return 0; } int Thing::doTouch(Thing *touched) { - if (controller != NULL) controller->recvMessage("You hit "+touched->name); - return 0; -} -int Thing::onTouch(Thing *toucher) { - return 0; -} -int Thing::onThought(Thought thought) { + onTouch(touched); + touched->onTouched(this); return 0; } /* ======== Adding/Access ======== */ @@ -68,6 +63,25 @@ Ability *Thing::getAbility(int number) { } return abilities.at(number); } +Ability* Thing::remAbility(std::string ability_name) { + std::deque::iterator abil_it, abil_end; + for (abil_it = abilities.begin(), abil_end = abilities.end(); abil_it != abil_end; ++abil_it) { + Ability *abil = *abil_it; + if (abil->getName() == ability_name) { + abilities.erase(abil_it); + return abil; + } + } + return NULL; +} +int Thing::hasAbility(std::string ability_name) { + std::deque::iterator abil_it, abil_end; + for (abil_it = abilities.begin(), abil_end = abilities.end(); abil_it != abil_end; ++abil_it) { + Ability *abil = *abil_it; + if (abil->getName() == ability_name) return 1; + } + return 0; +} /* ======== Velocity/Position ======== */ void Thing::addVelocity(Vec3 vel) { velocity += vel; diff --git a/src/things/Thing.hpp b/src/things/Thing.hpp index 46fb339..6191440 100644 --- a/src/things/Thing.hpp +++ b/src/things/Thing.hpp @@ -20,17 +20,20 @@ class Thing { ~Thing(); // interface int doThink(unsigned int ticks); - virtual int doTouch(Thing *touched); - virtual int onTouch(Thing *thing); - virtual int onThought(Thought thought); + int doTouch(Thing *touched); + virtual int onTouch(Thing *touched) { return 0; }; + virtual int onTouched(Thing *toucher) { return 0; }; + virtual int onThought(Thought thought) { return 0; }; // int pushThought(Thought thought); // Ability *getAbility(int number); int pushAbility(Ability *ability); int unshiftAbility(Ability *ability); + int hasAbility(std::string ability); + Ability* remAbility(std::string ability_name); // - void setController(Controller *controller_) { controller = controller_; }; + void setController(Controller *controller_) { controller_->controlThing(this); controller = controller_; }; Controller* getController() { return controller; }; void printMsg(std::string string); std::string getName() { return name; }; @@ -52,9 +55,11 @@ class Thing { // getters for internal stuff Sprite *getSprite() { return sprite; }; // oh boy oh boy, our stats! - int speed; - int health; - int damage; + int health; // total health + int armor; // armor, reduces incoming damage + int speed; // movement speed + int damage; // physical damage + int power; // magical power protected: QMap *map; // the map to which we belong (this is bad) Controller *controller;