#include "game.h" #include "mathutils.h" #include "drawutils.h" #include "debug.h" #include "itos.h" #include Game::Game(stack* state_stack, SDL_Surface* display) : GameState(state_stack, display) { background = NULL; font = NULL; } Game::~Game() { if (background != NULL) SDL_FreeSurface(background); if (font != NULL) TTF_CloseFont(font); for (list::iterator cursor = buttons.begin(); cursor != buttons.end(); cursor++) { delete *cursor; } } bool Game::init() { background = DrawUtils::load("background.bmp"); font = TTF_OpenFont("res/LiberationSans-Regular.ttf", 12); if (background == NULL || font == NULL) { debug("Game::init(): error: Couldn't load some resource(s)"); return false; } int row1 = display->h - 95; int row2 = display->h - 65; int row3 = display->h - 35; int col1 = 155; int col2 = 255; int col3 = 355; int col4 = 455; buttons.push_back(new MenuButton("Move (m)", font, col1, row1, BUTTON_MOVE)); buttons.push_back(new MenuButton("Build (b)", font, col1, row2, BUTTON_BUILD)); buttons.push_back(new MenuButton("Attack (a)", font, col1, row3, BUTTON_ATTACK)); buttons.push_back(new MenuButton("Attacker (t)", font, col2, row1, BUTTON_BUILD_ATTACKER)); buttons.push_back(new MenuButton("Defender (d)", font, col2, row2, BUTTON_BUILD_DEFENDER)); buttons.push_back(new MenuButton("Producer (p)", font, col2, row3, BUTTON_BUILD_PRODUCER)); buttons.push_back(new MenuButton("End Turn (e)", font, col4, row3, BUTTON_END_TURN)); for (list::iterator cursor = buttons.begin(); cursor != buttons.end(); cursor++) { if (!(*cursor)->init()) { debug("Failed to initialize a button"); throw StateExit(); } } mode_changed(); return GameState::init(); } void Game::iterate() { // handle the button iterations for (list::iterator cursor = buttons.begin(); cursor != buttons.end(); cursor++) { (*cursor)->iterate(); } } void Game::render() { int range = data.get_range(); int range_colour = 0x888888; switch(data.get_mode()) { case MODE_BUILD: range_colour = 0x0000ff; break; case MODE_MOVE: range_colour = 0x00ff00; break; case MODE_ATTACK: range_colour = 0xff0000; break; } // Background image first DrawUtils::draw(display, background, 0, 0); // Now paint on the targeting circle if (data.get_current_vertex(true) != NULL) { GameVertex* v = data.get_current_vertex(); DrawUtils::draw_circle_filled(display, v->x, v->y, range, range_colour); } // First draw the edges, so that they don't obscure any data list vertices = data.get_vertices(); for (list::iterator cursor = vertices.begin(); cursor != vertices.end(); cursor++) { Vertex* v = *cursor; for (list::iterator subcursor = v->neighbors.begin(); subcursor != v->neighbors.end(); subcursor++) { Vertex* v1 = *subcursor; DrawUtils::draw_line(display, v->x, v->y, v1->x, v1->y, 2, v->colour); } } // highlight the selected vertex and write info about it if (data.get_current_vertex() != NULL) { GameVertex* v = data.get_current_vertex(); DrawUtils::draw_circle(display, v->x, v->y, v->r + 5, 0x000000); draw_stats(v); } // If applicable, draw a 'ghost node'... maybe at some point we can make // this semi-transparent GameVertex* current = data.get_current_vertex(true); if (current != NULL && ((data.get_mode() == MODE_BUILD && data.get_build_type() != VERTEX_NONE) || (data.get_mode() == MODE_MOVE)) && MathUtils::distance(current->x, current->y, 0, cursor_x, cursor_y, 0) < range && MathUtils::distance(current->x, current->y, 0, cursor_x, cursor_y, 0) > current->r * 2) { GameVertex v(cursor_x, cursor_y, 0, current->r,current->colour, 0, VERTEX_NONE, current->player); DrawUtils::draw_line(display, v.x, v.y, current->x, current->y, 2, v.colour); v.render(display); } // Draw each node for (list::iterator cursor = vertices.begin(); cursor != vertices.end(); cursor++) { dynamic_cast(*cursor)->render(display); } // draw the rest of the bottom menu draw_menu_bars(); draw_player_info(); for (list::iterator cursor = buttons.begin(); cursor != buttons.end(); cursor++) { (*cursor)->render(display); } SDL_Flip(display); } void Game::draw_menu_bars() { // horizontal line across the whole thing DrawUtils::draw_line(display, 0, display->h - 100, display->w, display->h - 100, 2, 0x000000); // vertical lines to separate info panes from button pane DrawUtils::draw_line(display, 150, display->h - 100, 150, display->h, 2, 0x000000); DrawUtils::draw_line(display, 540, display->h - 100, 540, display->h, 2, 0x000000); DrawUtils::draw_line(display, display->w - 100, display->h - 100, display->w - 100, display->h, 2, 0x000000); } void Game::draw_player_info() { Player* player = data.get_turn(); DrawUtils::draw_text(display, player->get_name(), 10, display->h - 95, font); string temp = "Energy: "; temp += itos(player->get_energy()); DrawUtils::draw_text(display, temp, 10, display->h - 80, font); } void Game::handle_button_press(ButtonAction action) { switch (action) { case BUTTON_BUILD: data.set_mode(MODE_BUILD); break; case BUTTON_ATTACK: data.set_mode(MODE_ATTACK); break; case BUTTON_MOVE: data.set_mode(MODE_MOVE); break; case BUTTON_BUILD_ATTACKER: data.set_build_type(VERTEX_ATTACKER); break; case BUTTON_BUILD_DEFENDER: data.set_build_type(VERTEX_DEFENDER); break; case BUTTON_BUILD_PRODUCER: data.set_build_type(VERTEX_PRODUCER); break; case BUTTON_END_TURN: data.toggle_turn(); break; } } void Game::draw_stats(GameVertex* v) { int line_num = 0; int line_height = 14; int x = 550; int y = display->h - 95; int adj_y = y; DrawUtils::draw_text(display, "owner:", x, adj_y, font); DrawUtils::draw_text(display, v->player->get_name(), x + 50, adj_y, font); line_num++; adj_y = y + line_num * line_height; DrawUtils::draw_text(display, "type:", x, adj_y, font); string type; switch(v->type) { case VERTEX_ATTACKER: type = "attacker"; break; case VERTEX_DEFENDER: type = "defender"; break; case VERTEX_PRODUCER: type = "producer"; break; } DrawUtils::draw_text(display, type, x + 50, adj_y, font); line_num++; adj_y = y + line_num * line_height; DrawUtils::draw_text(display, "atk:", x, adj_y, font); DrawUtils::draw_text(display, itos(v->calculate_attack()), x + 50, adj_y, font); line_num++; adj_y = y + line_num * line_height; DrawUtils::draw_text(display, "armor:", x, adj_y, font); DrawUtils::draw_text(display, itos(v->calculate_armor()), x + 50, adj_y, font); line_num++; adj_y = y + line_num * line_height; DrawUtils::draw_text(display, "hp:", x, adj_y, font); DrawUtils::draw_text(display, itos(v->score), x + 50, adj_y, font); if (v->type == VERTEX_PRODUCER) { line_num++; adj_y = y + line_num * line_height; DrawUtils::draw_text(display, "energy:", x, adj_y, font); DrawUtils::draw_text(display, "25", x + 50, adj_y, font); } } void Game::on_lbutton_down(int x, int y) { for (list::iterator cursor = buttons.begin(); cursor != buttons.end(); cursor++) { MenuButton* button = *cursor; if (button->is_at(x, y)) { handle_button_press(button->get_action()); mode_changed(); return; } } if (y > display->h - 110) return; if (!data.endgame()) data.handle_click(x, y); mode_changed(); } void Game::on_rbutton_down(int mX, int mY) { data.clear_current_vertex(); mode_changed(); } void Game::on_key_down(SDLKey sym, SDLMod mod, Uint16 unicode) { if (sym == SDLK_q && mod & KMOD_CTRL) throw StateExit(); else if (sym == SDLK_a) data.set_mode(MODE_ATTACK); else if (sym == SDLK_m) data.set_mode(MODE_MOVE); else if (sym == SDLK_b) data.set_mode(MODE_BUILD); else if (sym == SDLK_d) data.set_build_type(VERTEX_DEFENDER); else if (sym == SDLK_p) data.set_build_type(VERTEX_PRODUCER); else if (sym == SDLK_t) data.set_build_type(VERTEX_ATTACKER); else if (sym == SDLK_s || sym == SDLK_ESCAPE) data.set_mode(MODE_SELECT); else if (sym == SDLK_e) data.toggle_turn(); mode_changed(); #ifdef DEBUG if (sym == SDLK_d && mod & (KMOD_ALT | KMOD_CTRL)) print_debug_info(); #endif } void Game::on_mouse_move(int mX, int mY, int relX, int relY, bool left, bool right, bool middle) { cursor_x = mX; cursor_y = mY; } #ifdef DEBUG void Game::print_debug_info() { fprintf(stderr, "Mode: %d\n", data.get_mode()); fprintf(stderr, "Turn: %s\n", data.get_turn()->get_name().c_str()); } #endif void Game::mode_changed() { // Set the state for each button for (list::iterator cursor = buttons.begin(); cursor != buttons.end(); cursor++) { (*cursor)->set_state(data.get_mode(), data.get_build_type(), data.get_current_vertex()); } }