diff --git a/assets/fs/shaders/shot-ring.f.glsl b/assets/fs/shaders/shot-ring.f.glsl index 660872d..a494189 100644 --- a/assets/fs/shaders/shot-ring.f.glsl +++ b/assets/fs/shaders/shot-ring.f.glsl @@ -1,5 +1,5 @@ void main(void) { - gl_FragColor = vec4(1.0, 0.1, 0.1, 1.0); + gl_FragColor = vec4(1.0, 0.1, 0.1, 0.5); } diff --git a/assets/fs/shaders/shot-ring.v.glsl b/assets/fs/shaders/shot-ring.v.glsl index 2d2fb41..3023d87 100644 --- a/assets/fs/shaders/shot-ring.v.glsl +++ b/assets/fs/shaders/shot-ring.v.glsl @@ -2,12 +2,13 @@ uniform mat4 projection; uniform mat4 modelview; uniform float scale; +uniform float width; -/* pos.xyz is position, pos.w is offset */ +/* pos.xyz is position, pos.w is 0/1 for inner/outer ring */ attribute vec4 pos; void main(void) { - vec3 pos3 = pos.xyz * (scale + pos.w); + vec3 pos3 = pos.xyz * (scale + width * pos.w); gl_Position = projection * modelview * vec4(pos3, 1); } diff --git a/src/client/Client-gl.cc b/src/client/Client-gl.cc index c1ade35..c5ec6c6 100644 --- a/src/client/Client-gl.cc +++ b/src/client/Client-gl.cc @@ -17,7 +17,7 @@ using namespace std; #define SKY_DIST 2000 #define NUM_SKY_STEPS 9 #define LAVA_SIZE 100 -#define SHOT_RING_WIDTH 20.0f +#define SHOT_RING_WIDTH 10.0f #define NUM_SHOT_RING_STEPS 24 /* points of a horizontal hexagon 1.0 units high */ @@ -125,6 +125,12 @@ bool Client::initgl() "pos", 0, "tex_coord", 1, NULL, "projection", "modelview", "tex", "shift", NULL)) return false; + if (!m_shot_ring_program.create( + CFS.get_file("shaders/shot-ring.v.glsl"), + CFS.get_file("shaders/shot-ring.f.glsl"), + "pos", 0, NULL, + "projection", "modelview", "scale", "width", NULL)) + return false; if (!m_tank_obj.load("models/tank.obj", load_file)) { cerr << "Error loading tank model" << endl; @@ -199,7 +205,7 @@ bool Client::initgl() shot_ring_attributes[idx++] = x; shot_ring_attributes[idx++] = y; shot_ring_attributes[idx++] = 0.0f; - shot_ring_attributes[idx++] = SHOT_RING_WIDTH; + shot_ring_attributes[idx++] = 1.0f; } if (!m_shot_ring_attributes.create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, &shot_ring_attributes[0], @@ -221,7 +227,7 @@ bool Client::initgl() cerr << "Error creating lava texture" << endl; return false; } - m_obj_program.use(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); return true; } @@ -240,27 +246,31 @@ void Client::redraw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - double dir_x = cos(m_players[current_player]->direction); - double dir_y = sin(m_players[current_player]->direction); - m_modelview.load_identity(); - m_modelview.look_at( - m_players[current_player]->x - dir_x * 25, m_players[current_player]->y - dir_y * 25, 30, - m_players[current_player]->x, m_players[current_player]->y, 20, - 0, 0, 1); - - // TODO: call draw_player() for each networked player - for(std::map >::iterator piter = m_players.begin(); piter != m_players.end(); piter++) + if (m_players.size() > 0) { - draw_player(piter->second); + m_modelview.load_identity(); + m_modelview.look_at( + m_players[m_current_player]->x - m_player_dir_x * 25, + m_players[m_current_player]->y - m_player_dir_y * 25, + 30, + m_players[m_current_player]->x, + m_players[m_current_player]->y, + 20, + 0, 0, 1); + + for(std::map >::iterator piter = m_players.begin(); piter != m_players.end(); piter++) + { + draw_player(piter->second); + } + + draw_map(); + draw_sky(); + draw_lava(); + draw_shot_ring(); + + draw_overlay(); } - - draw_map(); - draw_sky(); - draw_lava(); - - draw_overlay(); - m_window->display(); } @@ -379,15 +389,15 @@ void Client::draw_overlay() overlay_size, overlay_size); glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_overlay_program.use(); GLMatrix proj; const float span = 50 * 8; proj.ortho(-span, span, -span, span, -1, 1); proj.to_uniform(m_overlay_program.uniform("projection")); GLMatrix modelview; - modelview.rotate(90 - m_players[current_player]->direction * 180 / M_PI, 0, 0, 1); - modelview.translate(-m_players[current_player]->x, -m_players[current_player]->y, 0); + modelview.rotate(90 - m_players[m_current_player]->direction * 180 / M_PI, 0, 0, 1); + modelview.translate(-m_players[m_current_player]->x, + -m_players[m_current_player]->y, 0); m_overlay_hex_attributes.bind(); m_overlay_hex_indices.bind(); glEnableVertexAttribArray(0); @@ -438,8 +448,8 @@ void Client::draw_overlay() GLMatrix::Identity.to_uniform( m_overlay_hover_program.uniform("projection")); modelview.load_identity(); - modelview.translate(m_players[current_player]->hover - 1, 0, 0); - modelview.scale(m_players[current_player]->hover * 2, 2.0, 1.0); + modelview.translate(m_players[m_current_player]->hover - 1, 0, 0); + modelview.scale(m_players[m_current_player]->hover * 2, 2.0, 1.0); modelview.to_uniform(m_overlay_hover_program.uniform("modelview")); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -479,8 +489,10 @@ void Client::draw_sky() m_sky_attributes.bind(); m_projection.to_uniform(m_sky_program.uniform("projection")); m_modelview.push(); - m_modelview.translate(m_players[current_player]->x, m_players[current_player]->y, 0); - m_modelview.rotate(m_players[current_player]->direction * 180.0 / M_PI, 0, 0, 1); + m_modelview.translate(m_players[m_current_player]->x, + m_players[m_current_player]->y, 0); + m_modelview.rotate(m_players[m_current_player]->direction * 180.0 / M_PI, + 0, 0, 1); m_modelview.to_uniform(m_sky_program.uniform("modelview")); m_modelview.pop(); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, @@ -528,3 +540,49 @@ void Client::draw_lava() glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); } + +void Client::draw_shot_ring() +{ + if (m_drawing_shot) + { + m_shot_ring_program.use(); + m_shot_ring_attributes.bind(); + glEnableVertexAttribArray(0); + m_modelview.push(); + m_modelview.translate(m_players[m_current_player]->x, + m_players[m_current_player]->y, 0.4); + m_projection.to_uniform(m_shot_ring_program.uniform("projection")); + m_modelview.to_uniform(m_shot_ring_program.uniform("modelview")); + glUniform1f(m_shot_ring_program.uniform("scale"), + m_drawing_shot_distance); + glUniform1f(m_shot_ring_program.uniform("width"), SHOT_RING_WIDTH); + draw_shot_ring_instance(); + float mid_dist = m_drawing_shot_distance + SHOT_RING_WIDTH / 2.0; + m_modelview.translate(mid_dist * m_player_dir_x, + mid_dist * m_player_dir_y, 0.01); + m_modelview.to_uniform(m_shot_ring_program.uniform("modelview")); + glUniform1f(m_shot_ring_program.uniform("scale"), + 0.45 * SHOT_RING_WIDTH); + glUniform1f(m_shot_ring_program.uniform("width"), + 0.05 * SHOT_RING_WIDTH); + draw_shot_ring_instance(); + m_modelview.pop(); + glDisableVertexAttribArray(0); + } +} + +void Client::draw_shot_ring_instance() +{ + glEnable(GL_BLEND); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, + 4 * sizeof(GLfloat), NULL); + glDrawArrays(GL_TRIANGLE_STRIP, 0, (NUM_SHOT_RING_STEPS + 1) * 2); + + glDisable(GL_BLEND); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, + 8 * sizeof(GLfloat), NULL); + glDrawArrays(GL_LINE_STRIP, 0, NUM_SHOT_RING_STEPS + 1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, + 8 * sizeof(GLfloat), (void *) (4 * sizeof(GLfloat))); + glDrawArrays(GL_LINE_STRIP, 0, NUM_SHOT_RING_STEPS + 1); +} diff --git a/src/client/Client.cc b/src/client/Client.cc index b66a391..dfe02d7 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -3,13 +3,19 @@ #include "Types.h" #include "Timer.h" +/* TODO: this should be moved to common somewhere */ +#define MAX_SHOT_DISTANCE 250.0 +#define SHOT_EXPAND_SPEED 75.0 + Client::Client() { m_net_client = new Network(); m_net_client->Create(59243, "127.0.0.1"); // Just connect to local host for now - testing - client_has_focus = true; + m_client_has_focus = true; m_players.clear(); - current_player = 0; + m_current_player = 0; + m_left_button_pressed = false; + m_drawing_shot = false; } Client::~Client() @@ -23,7 +29,7 @@ Client::~Client() client_timer.Init(); client_packet.clear(); client_packet << packet_type; - client_packet << current_player; + client_packet << m_current_player; m_net_client->sendData(client_packet, true); // No time out needed here, since the @@ -51,7 +57,7 @@ Client::~Client() // This completely removes the player from the game // Deletes member from the player list client_packet >> player_index; - if(player_index == current_player) + if(player_index == m_current_player) { connection_closed = true; } @@ -82,7 +88,7 @@ void Client::run(bool fullscreen, int width, int height, std::string pname) { Timer client_timer; client_timer.Init(); - current_player_name = pname; + m_current_player_name = pname; if (!create_window(fullscreen, width, height)) return; m_clock.restart(); @@ -112,14 +118,26 @@ void Client::run(bool fullscreen, int width, int height, std::string pname) break; } break; + case sf::Event::MouseButtonPressed: + if (event.mouseButton.button == sf::Mouse::Left) + m_left_button_pressed = true; + break; + case sf::Event::MouseButtonReleased: + if (event.mouseButton.button == sf::Mouse::Left) + { + m_drawing_shot = false; + m_left_button_pressed = false; + /* TODO: trigger shot network message */ + } + break; case sf::Event::Resized: resize_window(event.size.width, event.size.height); break; case sf::Event::LostFocus: - client_has_focus = false; + m_client_has_focus = false; break; case sf::Event::GainedFocus: - client_has_focus = true; + m_client_has_focus = true; break; default: break; @@ -132,10 +150,7 @@ void Client::run(bool fullscreen, int width, int height, std::string pname) client_timer.Update(); update(elapsed_time); - if(m_players.size() > 0) - { - redraw(); - } + redraw(); last_time = current_time; // temporary for now. otherwise this thread consumed way too processing @@ -148,7 +163,6 @@ void Client::update(double elapsed_time) static bool registered_player = false; sf::Packet client_packet; - m_net_client->Receive(); client_packet.clear(); // Handle all received data (only really want the latest) @@ -168,10 +182,9 @@ void Client::update(double elapsed_time) client_packet >> players_port; // Should be a much better way of doing this. // Perhaps generate a random number - if((name == current_player_name) && - (m_net_client->getLocalPort() == players_port)) + if(name == m_current_player_name) { - current_player = pindex; + m_current_player = pindex; } // Create a new player if one does not exist. @@ -235,7 +248,7 @@ void Client::update(double elapsed_time) // This is a fix so that the mouse will not move outside the window and // cause the user to click on another program. // Note: Does not work well with fast movement. - if(client_has_focus) + if(m_client_has_focus) { if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) { @@ -255,19 +268,46 @@ void Client::update(double elapsed_time) } rel_mouse_movement = sf::Mouse::getPosition(*m_window).x - m_width / 2; sf::Mouse::setPosition(sf::Vector2i(m_width / 2, m_height / 2), *m_window); + + if (m_left_button_pressed) + { + if (m_drawing_shot) + { + m_drawing_shot_distance += SHOT_EXPAND_SPEED * elapsed_time; + if (m_drawing_shot_distance > MAX_SHOT_DISTANCE) + m_drawing_shot_distance = MAX_SHOT_DISTANCE; + } + else + { + m_drawing_shot = true; + m_drawing_shot_distance = 0.0f; + } + } } + /* decrease player hover when not over a tile */ + if (m_map.get_tile_at(m_players[m_current_player]->x, + m_players[m_current_player]->y).isNull()) + { + m_players[m_current_player]->hover -= elapsed_time / 10; + if (m_players[m_current_player]->hover < 0) + m_players[m_current_player]->hover = 0; + } + + m_player_dir_x = cos(m_players[m_current_player]->direction); + m_player_dir_y = sin(m_players[m_current_player]->direction); + // Send an update to the server if something has changed - if((m_players[current_player]->w_pressed != w_pressed) || - (m_players[current_player]->a_pressed != a_pressed) || - (m_players[current_player]->s_pressed != s_pressed) || - (m_players[current_player]->d_pressed != d_pressed) || - (m_players[current_player]->rel_mouse_movement != rel_mouse_movement)) + if((m_players[m_current_player]->w_pressed != w_pressed) || + (m_players[m_current_player]->a_pressed != a_pressed) || + (m_players[m_current_player]->s_pressed != s_pressed) || + (m_players[m_current_player]->d_pressed != d_pressed) || + (m_players[m_current_player]->rel_mouse_movement != rel_mouse_movement)) { sf::Uint8 packet_type = PLAYER_UPDATE; client_packet.clear(); client_packet << packet_type; - client_packet << current_player; + client_packet << m_current_player; client_packet << w_pressed; client_packet << a_pressed; client_packet << s_pressed; @@ -276,11 +316,11 @@ void Client::update(double elapsed_time) m_net_client->sendData(client_packet); - m_players[current_player]->w_pressed = w_pressed; - m_players[current_player]->a_pressed = a_pressed; - m_players[current_player]->s_pressed = s_pressed; - m_players[current_player]->d_pressed = d_pressed; - m_players[current_player]->rel_mouse_movement = rel_mouse_movement; + m_players[m_current_player]->w_pressed = w_pressed; + m_players[m_current_player]->a_pressed = a_pressed; + m_players[m_current_player]->s_pressed = s_pressed; + m_players[m_current_player]->d_pressed = d_pressed; + m_players[m_current_player]->rel_mouse_movement = rel_mouse_movement; } } else if(!registered_player) @@ -290,8 +330,8 @@ void Client::update(double elapsed_time) sf::Uint8 packet_type = PLAYER_CONNECT; client_packet.clear(); client_packet << packet_type; - client_packet << current_player; - client_packet << current_player_name; + client_packet << m_current_player; + client_packet << m_current_player_name; // Send the players port. This will server as a unique // identifier and prevent users with the same name from controlling // each other. diff --git a/src/client/Client.h b/src/client/Client.h index 0c967c4..6c13c27 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -29,12 +29,16 @@ class Client void draw_overlay(); void draw_sky(); void draw_lava(); + void draw_shot_ring(); + void draw_shot_ring_instance(); + double m_player_dir_x; + double m_player_dir_y; refptr m_window; sf::Clock m_clock; Map m_map; - sf::Uint8 current_player; - std::string current_player_name; + sf::Uint8 m_current_player; + std::string m_current_player_name; std::map > m_players; int m_width; int m_height; @@ -43,6 +47,7 @@ class Client GLProgram m_overlay_hover_program; GLProgram m_sky_program; GLProgram m_lava_program; + GLProgram m_shot_ring_program; WFObj m_tank_obj; WFObj m_tile_obj; GLMatrix m_projection; @@ -54,8 +59,11 @@ class Client GLBuffer m_quad_attributes; GLBuffer m_shot_ring_attributes; refptr m_net_client; - bool client_has_focus; + bool m_client_has_focus; sf::Texture m_lava_texture; + bool m_left_button_pressed; + bool m_drawing_shot; + float m_drawing_shot_distance; }; #endif diff --git a/src/common/Map.cc b/src/common/Map.cc index 4953752..dd2238d 100644 --- a/src/common/Map.cc +++ b/src/common/Map.cc @@ -3,6 +3,15 @@ using namespace std; +/* + * The center of the tile at m_grid[i][j] is (x, y) where + * x = j * m_span_x + m_offset_x + * y = (i + ((j & 1) ? 0.5 : 0.0) * m_tile_size + m_offset_y + * + * The rectangular bounding box for a hex tile centered at (x, y) + * are ((x - HEX_WIDTH_TO_HEIGHT * m_tile_size / 2, y - m_tile_size / 2), + * (x + HEX_WIDTH_TO_HEIGHT * m_tile_size / 2, y + m_tile_size / 2)) + */ Map::Map(int width, int height, float tile_size) { m_width = width; @@ -10,13 +19,13 @@ Map::Map(int width, int height, float tile_size) m_tile_size = tile_size; /* construction of default map */ - float span_x = HEX_WIDTH_TO_HEIGHT * 0.75 * tile_size; - float offset_x = -span_x * (width / 2.0); - float offset_y = -tile_size * (height / 2.0); + m_span_x = HEX_WIDTH_TO_HEIGHT * 0.75 * tile_size; + m_offset_x = -m_span_x * (width / 2.0); + m_offset_y = -tile_size * (height / 2.0); - HexTile outer_out(0, 0, 0.85 * span_x * width); - HexTile outer_in(0, 0, 0.5 * span_x * width); - HexTile inner_out(0, 0, 0.30 * span_x * width); + HexTile outer_out(0, 0, 0.85 * m_span_x * width); + HexTile outer_in(0, 0, 0.5 * m_span_x * width); + HexTile inner_out(0, 0, 0.30 * m_span_x * width); for (int i = 0; i < height; i++) { @@ -24,13 +33,14 @@ Map::Map(int width, int height, float tile_size) for (int j = 0; j < width; j++) { refptr ht; - float x = j * span_x + offset_x; - float y = (i + ((j & 1) ? 0.5 : 0.0)) * tile_size + offset_y; + float x = j * m_span_x + m_offset_x; + float y = (i + ((j & 1) ? 0.5 : 0.0)) * tile_size + m_offset_y; if (inner_out.point_within(y, x) || (outer_out.point_within(y, x) && !outer_in.point_within(y, x))) { + refptr get_tile_at(float x, float y); ht = new HexTile(x, y, tile_size); } @@ -38,3 +48,28 @@ Map::Map(int width, int height, float tile_size) } } } + +/* + * Given a point (x, y) return the HexTile, if any, that the point is on. + * A NULL refptr is returned if the point is not on any tile. + */ +refptr Map::get_tile_at(float x, float y) +{ + int i_base = (int) ((y - m_offset_y) / m_tile_size); + int j_base = (int) ((x - m_offset_x) / m_span_x); + for (int i_offset = 0; i_offset <= 1; i_offset++) + { + for (int j_offset = 0; j_offset <= 1; j_offset++) + { + int i = i_base + i_offset; + int j = j_base + j_offset; + if (i >= 0 && i < m_height && j >= 0 && j < m_width) + { + if (!m_grid[i][j].isNull()) + if (m_grid[i][j]->point_within(x, y)) + return m_grid[i][j]; + } + } + } + return NULL; +} diff --git a/src/common/Map.h b/src/common/Map.h index b3050ba..52c6d16 100644 --- a/src/common/Map.h +++ b/src/common/Map.h @@ -11,12 +11,16 @@ class Map Map(int width=21, int height=21, float tile_size=50); bool tile_present(int x, int y) { return !m_grid[y][x].isNull(); } refptr get_tile(int x, int y) { return m_grid[y][x]; } + refptr get_tile_at(float x, float y); int get_width() { return m_width; } int get_height() { return m_height; } protected: int m_width; int m_height; float m_tile_size; + float m_span_x; + float m_offset_x; + float m_offset_y; std::vector< std::vector< refptr< HexTile > > > m_grid; };