treewars/game.cpp

419 lines
11 KiB
C++
Raw Permalink Normal View History

#include "game.h"
#include "mathutils.h"
#include "drawutils.h"
#include "debug.h"
#include "itos.h"
#include <SDL.h>
Game::Game(stack<GameState*>* state_stack, SDL_Surface* display)
: GameState(state_stack, display)
{
background = NULL;
font = NULL;
}
Game::~Game()
{
2011-06-29 01:44:28 +00:00
if (background != NULL)
SDL_FreeSurface(background);
if (font != NULL)
TTF_CloseFont(font);
for (list<MenuButton*>::iterator cursor = buttons.begin();
cursor != buttons.end(); cursor++)
{
delete *cursor;
}
for (map<VertexType,Label*>::iterator cursor = costs.begin();
cursor != costs.end(); cursor++)
{
delete (*cursor).second;
}
}
bool Game::init()
{
background = DrawUtils::load("background.bmp");
2011-07-03 20:46:16 +00:00
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;
2011-07-04 06:23:20 +00:00
int row2 = display->h - 65;
int row3 = display->h - 35;
int col1 = 155;
2011-07-04 06:23:20 +00:00
int col2 = 255;
int col3 = 355;
int col4 = 455;
ButtonAction action;
action.mode = MODE_MOVE;
buttons.push_back(new MenuButton(action, "Move (m)", font, col1, row1));
action.mode = MODE_BUILD;
buttons.push_back(new MenuButton(action, "Build (b)", font, col1, row2));
action.mode = MODE_ATTACK;
buttons.push_back(new MenuButton(action, "Attack (a)", font, col1, row3));
action.mode = MODE_NONE;
action.type = VERTEX_ATTACKER;
buttons.push_back(new MenuButton(action, "Attacker (t)", font, col2,
row1));
action.type = VERTEX_DEFENDER;
buttons.push_back(new MenuButton(action, "Defender (d)", font, col2,
row2));
action.type = VERTEX_PRODUCER;
buttons.push_back(new MenuButton(action, "Producer (p)", font, col2,
row3));
action.type = VERTEX_NONE;
action.action = BUTTON_END_TURN;
buttons.push_back(new MenuButton(action, "End Turn (e)", font, col4,
row3));
for (list<MenuButton*>::iterator cursor = buttons.begin();
cursor != buttons.end(); cursor++)
{
if (!(*cursor)->init())
{
debug("Game::init(): Failed to initialize a button");
throw StateExit();
}
}
costs[VERTEX_ATTACKER] = new Label("", col3 - 10, row1 + 2, font);
costs[VERTEX_DEFENDER] = new Label("", col3 - 10, row2 + 2, font);
costs[VERTEX_PRODUCER] = new Label("", col3 - 10, row3 + 2, font);
mode_changed();
data.set_first_turn();
return GameState::init();
}
void Game::iterate()
{
// handle the button iterations
for (list<MenuButton*>::iterator cursor = buttons.begin();
cursor != buttons.end(); cursor++)
{
(*cursor)->iterate();
}
// set the costs
Label* tmp_label;
if (data.get_mode() & MODE_BUILD && data.get_turn()->has_played())
{
tmp_label = costs[VERTEX_ATTACKER];
tmp_label->set_text(itos(data.get_energy_cost(VERTEX_ATTACKER))
+ " energy");
tmp_label = costs[VERTEX_DEFENDER];
tmp_label->set_text(itos(data.get_energy_cost(VERTEX_DEFENDER))
+ " energy");
tmp_label = costs[VERTEX_PRODUCER];
tmp_label->set_text(itos(data.get_energy_cost(VERTEX_PRODUCER))
+ " energy");
}
else
{
tmp_label = costs[VERTEX_ATTACKER];
tmp_label->set_text("");
tmp_label = costs[VERTEX_DEFENDER];
tmp_label->set_text("");
tmp_label = costs[VERTEX_PRODUCER];
tmp_label->set_text("");
}
for (map<VertexType,Label*>::iterator cursor = costs.begin();
cursor != costs.end(); cursor++)
{
(*cursor).second->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<Vertex*> vertices = data.get_vertices();
for (list<Vertex*>::iterator cursor = vertices.begin();
cursor != vertices.end(); cursor++)
{
Vertex* v = *cursor;
for (list<Vertex*>::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);
}
}
2011-07-04 05:27:56 +00:00
// 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<Vertex*>::iterator cursor = vertices.begin();
cursor != vertices.end(); cursor++)
{
dynamic_cast<GameVertex*>(*cursor)->render(display);
}
// draw the rest of the bottom menu
draw_menu_bars();
draw_player_info();
2011-07-04 06:23:20 +00:00
for (list<MenuButton*>::iterator cursor = buttons.begin();
cursor != buttons.end(); cursor++)
{
(*cursor)->render(display);
}
for (map<VertexType,Label*>::iterator cursor = costs.begin();
cursor != costs.end(); cursor++)
{
(*cursor).second->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);
}
2011-07-04 05:27:56 +00:00
void Game::draw_player_info()
{
Player* player = data.get_turn();
2011-07-04 06:23:20 +00:00
DrawUtils::draw_text(display, player->get_name(), 10, display->h - 95,
2011-07-04 05:27:56 +00:00
font);
string temp = "Energy: ";
temp += itos(player->get_energy());
2011-07-04 06:23:20 +00:00
DrawUtils::draw_text(display, temp, 10, display->h - 80, font);
2011-07-04 05:27:56 +00:00
}
void Game::handle_button_press(ButtonAction action)
{
if (action.mode != MODE_NONE) data.set_mode(action.mode);
else if (action.type != VERTEX_NONE) data.set_build_type(action.type);
else if (action.action == BUTTON_END_TURN) data.toggle_turn();
}
void Game::draw_stats(GameVertex* v)
{
int line_num = 0;
int line_height = 14;
2011-07-04 06:23:20 +00:00
int x = 550;
int y = display->h - 95;
int adj_y = y;
2011-07-04 06:23:20 +00:00
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;
default:
type = "unknown";
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<MenuButton*>::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<MenuButton*>::iterator cursor = buttons.begin();
cursor != buttons.end(); cursor++)
{
(*cursor)->set_state(data.get_mode(), data.get_build_type(),
data.get_current_vertex(),
data.can_build((*cursor)->get_action().type));
}
}