void Tournament::add_npc_remove_animation(SpawnableNPC *npc) { const std::string& animation_name = npc->npc->get_value("kill_animation"); const std::string& sound_name = npc->npc->get_value("kill_sound"); if (animation_name.length()) { try { Animation *ani = resources.get_animation(animation_name); TileGraphic *tg = npc->npc->get_tile(DirectionLeft, NPCAnimationStanding)->get_tilegraphic(); int width = tg->get_width(); int height = tg->get_height(); GAnimation *sgani = new GAnimation; memset(sgani, 0, GAnimationLen); strncpy(sgani->animation_name, ani->get_name().c_str(), NameLength - 1); strncpy(sgani->sound_name, sound_name.c_str(), NameLength - 1); sgani->id = npc->state.id; sgani->duration = ani->get_duration(); TileGraphic *anitg = ani->get_tile()->get_tilegraphic(); sgani->x = npc->state.x + width / 2 - anitg->get_width() / 2; sgani->y = npc->state.y - height / 2 - anitg->get_height() / 2; sgani->accel_x = 0.0f; sgani->accel_y = 0.0f; sgani->to_net(); add_state_response(GPCAddAnimation, GAnimationLen, sgani); } catch (const Exception& e) { subsystem << e.what() << std::endl; } } }
void Tournament::player_dies(Player *p, const std::string& die_message) { player_died(p); p->state.server_state.health = 0; p->state.server_state.flags |= PlayerServerFlagDead; p->state.server_state.kills++; if (die_message.length()) { add_msg_response(die_message.c_str()); } try { Animation *tempani = resources.get_animation(properties.get_value("die_animation")); GAnimation *ani = new GAnimation; memset(ani, 0, sizeof(GAnimation)); strncpy(ani->animation_name, tempani->get_name().c_str(), NameLength - 1); strncpy(ani->sound_name, properties.get_value("die_sound").c_str(), NameLength - 1); TileGraphic *tg = p->get_characterset()->get_tile(DirectionLeft, CharacterAnimationStanding)->get_tilegraphic(); TileGraphic *tga = tempani->get_tile()->get_tilegraphic(); int x = static_cast<int>(p->state.client_server_state.x) + tg->get_width() / 2 - tga->get_width() / 2; int y = static_cast<int>(p->state.client_server_state.y) - tg->get_height() / 2 - tga->get_height() / 2; ani->id = ++animation_id; ani->x = x; ani->y = y; ani->to_net(); add_state_response(GPCAddAnimation, sizeof(GAnimation), ani); } catch (const Exception& e) { subsystem << e.what() << std::endl; } }
void Tournament::player_npc_collision(Player *p, SpawnableNPC *npc) { if (npc->init_owner != p->state.id) { const std::string& explosion_animation = npc->npc->get_value("explosion_animation"); if (explosion_animation.length()) { Animation *ani = resources.get_animation(explosion_animation); GAnimation *sgani = new GAnimation; memset(sgani, 0, GAnimationLen); strncpy(sgani->animation_name, ani->get_name().c_str(), NameLength - 1); strncpy(sgani->sound_name, ani->get_value("sound_name").c_str(), NameLength - 1); sgani->id = npc->state.id; sgani->duration = ani->get_duration(); sgani->x = npc->state.x + ani->get_x_offset(); sgani->y = npc->state.y + ani->get_y_offset(); sgani->accel_x = 0.0f; sgani->accel_y = 0.0f; sgani->to_net(); add_state_response(GPCAddAnimation, GAnimationLen, sgani); check_killing_animation(static_cast<int>(npc->state.x), static_cast<int>(npc->state.y), ani, npc->state.owner, true, npc->npc); } } }
void Tournament::integrate(ns_t ns) { double period_f = ns / static_cast<double>(ns_fc); /* show stats, if game is over? */ if (!game_state.seconds_remaining) { if (!warmup) { game_over = true; show_statistics = true; } return; } /* update time counter */ if (server) { second_counter += ns; if (second_counter >= ns_sec) { second_counter -= ns_sec; if (game_state.seconds_remaining) { game_state.seconds_remaining--; if (!game_state.seconds_remaining) { if (!warmup) { add_state_response(GPCGameOver, 0, 0); if (logger) { logger->log(ServerLogger::LogTypeGameOver, "game over"); write_stats_in_server_log(); logger->log(ServerLogger::LogTypeEndOfStats, "end of stats"); } } } } } } /* update game objects -> respawning */ if (server) { for (GameObjects::iterator it = game_objects.begin(); it != game_objects.end(); it++) { GameObject *obj = *it; int spawn_time = obj->object->get_spawning_time(); if (obj->picked && spawn_time) { obj->spawn_counter += period_f; if (obj->spawn_counter >= spawn_time) { obj->picked = false; obj->spawn_counter = 0.0f; GPlaceObject *gpo = new GPlaceObject; gpo->id = obj->state.id; gpo->flags = PlaceObjectWithAnimation | PlaceObjectWithSpawnSound; gpo->x = static_cast<pos_t>(obj->state.x); gpo->y = static_cast<pos_t>(obj->state.y); gpo->to_net(); add_state_response(GPCPlaceObject, GPlaceObjectLen, gpo); } } } } /* update typing animation */ player_afk_counter += period_f * AnimationMultiplier; if (player_afk_counter > player_afk->get_animation_speed()) { player_afk_counter = 0.0f; player_afk_index = player_afk_index + 1; if (player_afk_index >= player_afk->get_tile()->get_tilegraphic()->get_tile_count()) { player_afk_index = 0; } } /* update animation states and its physics */ for (GameAnimations::iterator it = game_animations.begin(); it != game_animations.end(); it++) { GameAnimation *gani = *it; gani->animation_counter += period_f * AnimationMultiplier; double speed = static_cast<double>(gani->animation->get_animation_speed()); bool finished = false; if (gani->animation_counter > speed) { TileGraphic *tg = gani->animation->get_tile()->get_tilegraphic(); gani->animation_counter = 0.0f; gani->index++; if (gani->index >= static_cast<int>(tg->get_tile_count())) { if (gani->state.duration) { gani->state.duration--; if (!gani->state.duration) { finished = true; } } else { finished = true; } } } if (gani->animation->get_physics()) { TileGraphic *tg = gani->animation->get_tile()->get_tilegraphic(); int width = tg->get_width(); int height = tg->get_height(); double springiness = gani->animation->get_springiness(); bool projectile = gani->animation->is_projectile(); double recoil = gani->animation->get_recoil(); int damage = gani->animation->get_damage(); const CollisionBox& colbox = gani->animation->get_physics_colbox(); bool col = render_physics(period_f, projectile, damage, recoil, gani->state.owner, springiness, springiness, colbox, gani->state.x, gani->state.y, gani->state.accel_x, gani->state.accel_y, width, height, 1.0f, true, gani->falling, gani->last_falling_y_pos, 0, gani->animation->get_name()); if (col && projectile) { finished = true; } } if (finished) { gani->delete_me = true; if (server) { const std::string& finished_animation = gani->animation->get_value("finished_animation"); if (finished_animation.length()) { Animation *ani = resources.get_animation(finished_animation); GAnimation *sgani = new GAnimation; memset(sgani, 0, GAnimationLen); strncpy(sgani->animation_name, ani->get_name().c_str(), NameLength - 1); strncpy(sgani->sound_name, ani->get_value("sound_name").c_str(), NameLength - 1); sgani->id = gani->state.id; sgani->duration = ani->get_duration(); sgani->x = gani->state.x + ani->get_x_offset(); sgani->y = gani->state.y + ani->get_y_offset(); sgani->accel_x = 0.0f; sgani->accel_y = 0.0f; sgani->to_net(); add_state_response(GPCAddAnimation, GAnimationLen, sgani); check_killing_animation( static_cast<int>(gani->state.x + gani->animation->get_tile()->get_tilegraphic()->get_width() / 2), static_cast<int>(gani->state.y + gani->animation->get_tile()->get_tilegraphic()->get_height() / 2), ani, gani->state.owner, false, 0 ); } } } } game_animations.erase(std::remove_if(game_animations.begin(), game_animations.end(), erase_element<GameAnimation>), game_animations.end()); /* update text animations */ for (GameTextAnimations::iterator it = game_text_animations.begin(); it != game_text_animations.end(); it++) { GameTextAnimation *gani = *it; gani->animation_counter += period_f * AnimationMultiplier; if (gani->animation_counter > TextAnimationSpeed) { gani->animation_counter = 0.0f; gani->y--; gani->rise_counter++; if (gani->rise_counter > gani->max_rise_counter) { gani->delete_me = true; } } } game_text_animations.erase(std::remove_if(game_text_animations.begin(), game_text_animations.end(), erase_element<GameTextAnimation>), game_text_animations.end()); /* update frog respawns */ if (server && has_frogs) { frog_respawn_counter -= period_f; if (frog_respawn_counter <= 0.0f) { reset_frog_spawn_counter(); spawn_frog(); } } /* update npcs */ update_npc_states(period_f); /* update tile states */ if (!server) { resources.update_tile_index(period_f * AnimationMultiplier, tileset); } /* update object physics */ for (GameObjects::iterator it = game_objects.begin(); it != game_objects.end(); it++) { GameObject *obj = *it; if (!obj->picked) { if (obj->object->get_physics()) { TileGraphic *tg = obj->object->get_tile()->get_tilegraphic(); int width = tg->get_width(); int height = tg->get_height(); double springiness = obj->object->get_springiness(); const CollisionBox& colbox = (obj->object->has_physics_colbox() ? obj->object->get_physics_colbox() : obj->object->get_colbox()); render_physics(period_f, false, 0, 0.0f, 0, springiness, springiness, colbox, obj->state.x, obj->state.y, obj->state.accel_x, obj->state.accel_y, width, height, 1.0f, true, obj->falling, obj->last_falling_y_pos, 0, obj->object->get_name()); } } } /* subclassed integration */ subintegrate(ns); /* player update cycle */ Player *following_player = 0; Player *me = get_me(); /* player wants to join or to respawn */ if (!server && me) { bool button_state = ((me->state.client_server_state.key_states & PlayerKeyStateJump) != 0); if (button_state != last_button_a_state) { last_button_a_state = button_state; if (me->state.server_state.flags & PlayerServerFlagSpectating) { if (!me->joining && button_state) { player_join_request(me); } } else if (!me->is_alive_and_playing()) { if (!me->respawning && button_state) { this->spawn_player(me); me->respawning = true; add_state_response(GPSRespawnRequest, 0, 0); me->state.client_state.flags &= ~PlayerClientFlagJumpReleased; } } } } /* control spectator */ control_spectator(me, period_f); /* update all player states */ for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->is_alive_and_playing()) { const CollisionBox& colbox = p->get_characterset()->get_colbox(); /* check player name width */ if (!p->font) { p->font = resources.get_font("normal"); p->player_name_width = p->font->get_text_width(p->get_player_name()); } /* set following player */ if (p->state.id == following_id) { following_player = p; } /* action key triggers */ bool move_left = ((p->state.client_server_state.key_states & PlayerKeyStateLeft) != 0) | ((p->state.client_server_state.jaxis & PlayerKeyStateLeft) != 0); bool move_right = ((p->state.client_server_state.key_states & PlayerKeyStateRight) != 0) | ((p->state.client_server_state.jaxis & PlayerKeyStateRight) != 0); bool move_up = ((p->state.client_server_state.key_states & PlayerKeyStateUp) != 0) | ((p->state.client_server_state.jaxis & PlayerKeyStateUp) != 0); bool move_down = ((p->state.client_server_state.key_states & PlayerKeyStateDown) != 0) | ((p->state.client_server_state.jaxis & PlayerKeyStateDown) != 0); bool move_jump = ((p->state.client_server_state.key_states & PlayerKeyStateJump) != 0); /* prevent locking slidings */ if (move_left && move_right) { move_left = move_right = false; } if (move_up && move_down) { move_up = move_down = false; } /* horizontal acceleration */ if (move_left) { p->state.client_server_state.accel_x -= (XAccel * period_f); } if (move_right) { p->state.client_server_state.accel_x += (XAccel * period_f); } if (p->state.client_server_state.accel_x < -XMaxAccel) { p->state.client_server_state.accel_x = -XMaxAccel; } if (p->state.client_server_state.accel_x > XMaxAccel) { p->state.client_server_state.accel_x = XMaxAccel; } /* horizontal deceleration */ if (!move_left && !move_right) { if (p->state.client_server_state.accel_x > -Epsilon && p->state.client_server_state.accel_x < Epsilon) { p->state.client_server_state.accel_x = 0.0f; } else if (p->state.client_server_state.accel_x < -Epsilon) { p->state.client_server_state.accel_x += (XDecel * period_f); if (p->state.client_server_state.accel_x > -Epsilon) { p->state.client_server_state.accel_x = 0.0f; } } else if (p->state.client_server_state.accel_x > Epsilon) { p->state.client_server_state.accel_x -= (XDecel * period_f); if (p->state.client_server_state.accel_x < Epsilon) { p->state.client_server_state.accel_x = 0.0f; } } } /* jump */ if (move_jump && p->state.client_state.flags & PlayerClientFlagJumpReleased) { p->state.client_state.flags &= ~PlayerClientFlagJumpReleased; if (!(p->state.client_state.flags & PlayerClientFlagFalling) && p->state.client_server_state.jump_accel_y > -Epsilon && p->state.client_server_state.jump_accel_y < Epsilon) { if (p == me) { p->state.client_server_state.jump_accel_y = -YInitialJumpImpulse; if (!server) { p->force_broadcast = true; const std::string& jump_sound = p->get_characterset()->get_value("jump_sound"); if (jump_sound.length()) { subsystem.play_sound(resources.get_sound(jump_sound), 0); } } } } } else if (!move_jump) { p->state.client_state.flags |= PlayerClientFlagJumpReleased; } /* gravity */ p->state.client_server_state.accel_y += YAccelGravity * period_f; if (p->state.client_server_state.accel_y > YMaxAccel) { p->state.client_server_state.accel_y = YMaxAccel; } if (p->state.client_server_state.accel_y + p->state.client_server_state.jump_accel_y < -Epsilon) { p->state.client_server_state.jump_accel_y += (move_jump ? YDecelJump : YDecelJumpNormal) * period_f; } else { p->state.client_server_state.jump_accel_y += YDecelJumpNormal * period_f; } if (p->state.client_server_state.jump_accel_y > -Epsilon) { p->state.client_server_state.jump_accel_y = 0.0f; } /* new movement vector */ double movy = p->state.client_server_state.accel_y + p->state.client_server_state.jump_accel_y; /* new position */ double newx = p->state.client_server_state.x + (p->state.client_server_state.accel_x * period_f); double newy = p->state.client_server_state.y + (movy * period_f); /* map boundary checks */ if (newx < -colbox.x) { newx = -colbox.x; p->state.client_server_state.accel_x = 0.0f; } if (newx + colbox.x + colbox.width > map_width * tile_width) { newx = map_width * tile_width - (colbox.x + colbox.width); p->state.client_server_state.accel_x = 0.0f; } /* tile collision detection */ bool bailout; int colmax; /* AABB tile collision detection in x direction */ do { /* left side */ if (p->state.client_server_state.accel_x < 0.0f) { /* left upper stepped down */ bailout = false; colmax = ((colbox.height - 1) / tile_height) + 1; for (int i = 0; i < colmax; i++) { if (collide_with_tile(TestTypeNormal, p, p->last_falling_y_pos, newx + colbox.x, p->state.client_server_state.y - colbox.y - colbox.height + (i * tile_height), 0, 0)) { newx = (tilex + 1) * tile_width - colbox.x; p->state.client_server_state.accel_x = 0.0f; bailout = true; break; } } if (bailout) { break; } /* left lower */ if (collide_with_tile(TestTypeNormal, p, p->last_falling_y_pos, newx + colbox.x, p->state.client_server_state.y - colbox.y - 1.0f, 0, 0)) { newx = (tilex + 1) * tile_width - colbox.x; p->state.client_server_state.accel_x = 0.0f; break; } } if (p->state.client_server_state.accel_x > 0.0f) { /* right upper stepped down */ bailout = false; colmax = ((colbox.height - 1) / tile_height) + 1; for (int i = 0; i < colmax; i++) { if (collide_with_tile(TestTypeNormal, p, p->last_falling_y_pos, newx + colbox.width + colbox.x, p->state.client_server_state.y - colbox.height - colbox.y + (i * tile_height), 0, 0)) { newx = tilex * tile_width - colbox.width - colbox.x; p->state.client_server_state.accel_x = 0.0f; bailout = true; break; } } if (bailout) { break; } /* right lower */ if (collide_with_tile(TestTypeNormal, p, p->last_falling_y_pos, newx + colbox.width + colbox.x, p->state.client_server_state.y - colbox.y - 1.0f, 0, 0)) { newx = tilex * tile_width - colbox.width - colbox.x; p->state.client_server_state.accel_x = 0.0f; } } } while (false); /* set player's falling flag */ p->state.client_state.flags |= PlayerClientFlagFalling; /* save last falling y before y correction for falling tile collision tests */ if (static_cast<int>(newy) < p->last_falling_y_pos) { p->last_falling_y_pos = static_cast<int>(newy); } /* AABB tile collision detection in y direction */ double ground_friction = 0.0f; do { /* test if jumping only */ if (movy < 0.0f) { /* reset after jump */ p->last_falling_y_pos = Player::PlayerFallingTestMaxY; /* top edge */ bailout = false; colmax = ((colbox.width - 1) / tile_width) + 1; for (int i = 0; i < colmax; i++) { if (collide_with_tile(TestTypeNormal, p, p->last_falling_y_pos, p->state.client_server_state.x + colbox.x + (i * tile_width), newy - colbox.y - colbox.height, 0, 0)) { newy = (tiley + 1) * tile_height + colbox.y + colbox.height; p->state.client_server_state.jump_accel_y = 0.0f; p->state.client_server_state.accel_y = 0.0f; p->state.client_state.flags &= ~PlayerClientFlagFalling; bailout = true; break; } } if (bailout) { break; } /* top edge right point */ if (collide_with_tile(TestTypeNormal, p, p->last_falling_y_pos, p->state.client_server_state.x + colbox.x + colbox.width - 1.0f, newy - colbox.y - colbox.height, 0, 0)) { newy = (tiley + 1) * tile_height + colbox.y + colbox.height; p->state.client_server_state.jump_accel_y = 0.0f; p->state.client_server_state.accel_y = 0.0f; p->state.client_state.flags &= ~PlayerClientFlagFalling; break; } } /* test if falling only */ TestType test_type; if (move_down) { p->last_falling_y_pos = Player::PlayerFallingTestMaxY; test_type = TestTypeFallingThrough; } else { test_type = TestTypeFalling; } if (movy > 0.0f) { /* bottom edge */ bool found = false; colmax = ((colbox.width - 1) / tile_width) + 1; for (int i = 0; i < colmax; i++) { double tile_friction = 0.0f; if (collide_with_tile(test_type, p, p->last_falling_y_pos, p->state.client_server_state.x + colbox.x + (i * tile_width), newy - colbox.y, &tile_friction, 0)) { if (!found) { if (p->state.client_server_state.accel_y > YVeloLanding) { p->state.client_state.flags |= PlayerClientFlagLanded; } newy = tiley * tile_height + colbox.y; p->state.client_server_state.jump_accel_y = 0.0f; p->state.client_server_state.accel_y = 0.0f; p->state.client_state.flags &= ~PlayerClientFlagFalling; found = true; } } if (tile_friction > ground_friction) { ground_friction = tile_friction; } } /* bottom edge right point */ double tile_friction = 0.0f; if (collide_with_tile(test_type, p, p->last_falling_y_pos, p->state.client_server_state.x + colbox.x + colbox.width - 1.0f, newy - colbox.y, &tile_friction, 0)) { if (!found) { if (p->state.client_server_state.accel_y > YVeloLanding) { p->state.client_state.flags |= PlayerClientFlagLanded; } newy = tiley * tile_height + colbox.y; p->state.client_server_state.jump_accel_y = 0.0f; p->state.client_server_state.accel_y = 0.0f; p->state.client_state.flags &= ~PlayerClientFlagFalling; } } if (tile_friction > ground_friction) { ground_friction = tile_friction; } } } while (false); /* save last falling y after y correction for falling tile collision tests */ if (static_cast<int>(newy) > p->last_falling_y_pos) { p->last_falling_y_pos = static_cast<int>(newy); } /* if player was killed by tile, continue to next player here */ if (p->state.server_state.flags & PlayerServerFlagDead) { continue; } /* collision with object and NPC */ if (server) { CollisionBox p_colbox = colbox; p_colbox.x += static_cast<int>(newx); p_colbox.y = static_cast<int>(newy) - p_colbox.height - p_colbox.y; /* object collision? */ for (GameObjects::iterator oit = game_objects.begin(); oit != game_objects.end(); oit++) { GameObject *obj = *oit; if (!obj->picked) { TileGraphic *tg = obj->object->get_tile()->get_tilegraphic(); CollisionBox obj_colbox = obj->object->get_colbox(); obj_colbox.x += static_cast<int>(obj->state.x); obj_colbox.y = static_cast<int>(obj->state.y) + tg->get_height() - obj_colbox.height - obj_colbox.y; /* intersection? */ if (p_colbox.intersects(obj_colbox)) { bool proceed = false; if (!pick_item(p, obj)) { proceed = Tournament::pick_item(p, obj); } else { proceed = true; } if (proceed) { GPickObject *po = new GPickObject; po->id = obj->state.id; po->to_net(); add_state_response(GPCPickObject, sizeof(GPickObject), po); obj->picked = true; obj->delete_me = obj->object->is_spawnable(); } } } } /* delete marked objects */ game_objects.erase(std::remove_if(game_objects.begin(), game_objects.end(), erase_element<GameObject>), game_objects.end()); /* NPC collision */ for (SpawnableNPCs::iterator nit = spawnable_npcs.begin(); nit != spawnable_npcs.end(); nit++) { SpawnableNPC *npc = *nit; CollisionBox npc_colbox = npc->npc->get_colbox(); npc_colbox.x += static_cast<int>(npc->state.x); npc_colbox.y = static_cast<int>(npc->state.y) - npc_colbox.height - npc_colbox.y; /* intersection? */ if (p_colbox.intersects(npc_colbox)) { player_npc_collision(p, npc); } } /* delete marked objects */ remove_marked_npcs(); } /* if player is sliding -> more friction */ if (!move_left && !move_right && !(p->state.client_state.flags & PlayerClientFlagFalling)) { if (p->state.client_server_state.accel_x > -Epsilon && p->state.client_server_state.accel_x < Epsilon) { p->state.client_server_state.accel_x = 0.0f; } else if (p->state.client_server_state.accel_x < -Epsilon) { p->state.client_server_state.accel_x += (ground_friction * period_f); if (p->state.client_server_state.accel_x > -Epsilon) { p->state.client_server_state.accel_x = 0.0f; } } else if (p->state.client_server_state.accel_x > Epsilon) { p->state.client_server_state.accel_x -= (ground_friction * period_f); if (p->state.client_server_state.accel_x < Epsilon) { p->state.client_server_state.accel_x = 0.0f; } } } /* fell off the screen */ if (server) { if (newy - 100 > map_height * tile_height) { std::string msg(p->get_player_name() + " fell off the stage"); player_dies(p, msg); if (logger) { logger->log(ServerLogger::LogTypeKill, msg, p, p, "void"); } } } /* update player position */ p->state.client_server_state.x = newx; p->state.client_server_state.y = newy; /* setup character icon and its animation */ unsigned char icon = p->state.client_state.icon; p->animation_counter += period_f * AnimationMultiplier; if (p->state.client_state.flags & PlayerClientFlagDoShotAnimation) { if (p->state.client_state.flags & PlayerClientFlagOneShotFinished) { p->state.client_state.flags &= ~PlayerClientFlagDoShotAnimation; } icon = static_cast<unsigned char>(CharacterAnimationShooting); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; if (move_right && !move_left) { p->state.client_server_state.direction = DirectionRight; } if (move_left && !move_right) { p->state.client_server_state.direction = DirectionLeft; } } else if (p->state.client_server_state.jump_accel_y < -YVeloJumpToLanding) { icon = static_cast<unsigned char>(CharacterAnimationJumping); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; if (move_right && !move_left) { p->state.client_server_state.direction = DirectionRight; } if (move_left && !move_right) { p->state.client_server_state.direction = DirectionLeft; } } else if (p->state.client_state.flags & PlayerClientFlagFalling) { icon = static_cast<unsigned char>(CharacterAnimationFalling); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; if (move_right && !move_left) { p->state.client_server_state.direction = DirectionRight; } if (move_left && !move_right) { p->state.client_server_state.direction = DirectionLeft; } } else if (move_right && p->state.client_server_state.accel_x < Epsilon) { icon = static_cast<unsigned char>(CharacterAnimationSliding); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; } else if (move_left && p->state.client_server_state.accel_x > -Epsilon) { icon = static_cast<unsigned char>(CharacterAnimationSliding); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; } else if (move_right || move_left) { icon = static_cast<unsigned char>(CharacterAnimationRunning); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; if (move_right && !move_left) { p->state.client_server_state.direction = DirectionRight; } if (move_left && !move_right) { p->state.client_server_state.direction = DirectionLeft; } } else { if (p->state.client_state.flags & PlayerClientFlagLanded) { if (p->state.client_state.flags & PlayerClientFlagOneShotFinished) { p->state.client_state.flags &= ~PlayerClientFlagLanded; } icon = static_cast<unsigned char>(CharacterAnimationLanding); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; } else if (p->state.client_server_state.accel_x < -Epsilon || p->state.client_server_state.accel_x > Epsilon) { icon = static_cast<unsigned char>(CharacterAnimationSliding); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; } else { icon = static_cast<unsigned char>(CharacterAnimationStanding); if (icon != p->state.client_state.icon) { p->state.client_state.flags &= ~PlayerClientFlagOneShotFinished; p->animation_counter = 0.0f; } p->state.client_state.icon = icon; if (move_right && !move_left) { p->state.client_server_state.direction = DirectionRight; } if (move_left && !move_right) { p->state.client_server_state.direction = DirectionLeft; } } } Tile *t = p->get_characterset()->get_tile(DirectionLeft, static_cast<CharacterAnimation>(p->state.client_state.icon)); if (p->animation_counter >= static_cast<double>(t->get_animation_speed())) { p->animation_counter = 0.0f; int index = p->state.client_state.iconindex + 1; int sz = static_cast<int>(t->get_tilegraphic()->get_tile_count()); if (index >= sz) { if (t->is_one_shot()) { index = sz - 1; p->state.client_state.flags |= PlayerClientFlagOneShotFinished; } else { index = 0; } } p->state.client_state.iconindex = static_cast<unsigned char>(index); } /* check attacks */ if (p == me) { check_attack(p, colbox, move_up, move_down, following_player); } } } /* post actions */ players_post_actions(); /* set top/left */ if (following_player) { const CollisionBox& colbox = following_player->get_characterset()->get_colbox(); spectator_x = following_player->state.client_server_state.x + colbox.x + colbox.width / 2; spectator_y = following_player->state.client_server_state.y; } int view_width = subsystem.get_view_width(); int view_height = subsystem.get_view_height(); int origin_x = static_cast<int>(spectator_x); int origin_y = static_cast<int>(spectator_y); if (map_width * tile_width < view_width) { left = (view_width / 2) - (map_width * tile_width / 2); } else { left = -(origin_x - (view_width / 2)); if (left > 0) left = 0; if (left < -((map_width * tile_width) - view_width)) { left = -((map_width * tile_width) - view_width); } } if (map_height * tile_height < view_height) { top = (view_height / 2) - (map_height * tile_height / 2); } else { top = -(origin_y - (view_height / 2)); if (top > 0) top = 0; if (top < -((map_height * tile_height) - view_height)) { top = -((map_height * tile_height) - view_height); } } }
void Client::sevt_data(ServerEvent& evt) { GTransport *t = reinterpret_cast<GTransport *>(evt.data); while (true) { t->from_net(); switch (t->cmd) { case GPCServerMessage: { size_t sz = 0; std::string msg(reinterpret_cast<char *>(t->data), t->len); std::vector<std::string> lines; while (true) { sz++; size_t pos = msg.find("|"); if (pos == std::string::npos) { lines.push_back(msg); break; } lines.push_back(msg.substr(0, pos)); msg = msg.substr(pos + 1); } int vw = subsystem.get_view_width(); int vh = subsystem.get_view_height(); int ww = 200; int wh = 130; int x = 10; int y = 10; GuiWindow *window = push_window(x, y, ww, wh, "Server Message"); Font *f = get_font(); int text_height = 0; int ly = Spc; int maxw = 0; for (size_t i = 0; i < sz; i++) { create_label(window, Spc, ly, lines[i]); ly += f->get_font_height(); text_height += f->get_font_height(); int tw = f->get_text_width(lines[i]) + 2 * Spc; maxw = (tw > maxw ? tw : maxw); } int maxh = text_height + 2 * Spc; int bw = 46; int bh = 18; create_button(window, maxw / 2 - bw / 2, maxh, bw, bh, "Okay", static_window_close_click, this); maxh += bh + Spc; window->set_width(window->get_width() - window->get_client_width() + maxw); window->set_height(window->get_height() - window->get_client_height() + maxh); window->set_x(vw / 2 - window->get_width() / 2); window->set_y(vh / 2 - window->get_height() / 2); break; } case GPCMapState: { GTournament *tour = reinterpret_cast<GTournament *>(t->data); tour->from_net(); if (tournament) { delete tournament; } /* reload resources? */ if (reload_resources) { resources.reload_resources(); load_resources(); reload_resources = false; } /* setup tournament */ bool warmup = ((tour->flags & TournamentFlagWarmup) != 0); GamePlayType type = static_cast<GamePlayType>(tour->gametype); MapConfiguration config(type, tour->map_name, tour->duration, tour->warmup); tournament = factory.create_tournament(config, false, warmup, players, 0); factory.set_tournament_id(tour->tournament_id); tournament->set_following_id(my_id); tournament->set_player_configuration(&player_config); tournament->set_team_names(team_red_name, team_blue_name); /* reopen, if join request window is already open */ if (me && me->joining) { tournament->reopen_join_window(me); } break; } case GPCIdentifyPlayer: { player_id_t *nid = reinterpret_cast<player_id_t *>(t->data); my_id = ntohs(*nid); if (tournament) { tournament->set_following_id(my_id); } break; } case GPCReady: { if (tournament) { tournament->set_ready(); } if (me) { me->state.client_server_state.key_states = 0; } break; } case GPCAddPlayer: { GPlayerInfo *info = reinterpret_cast<GPlayerInfo *>(t->data); info->from_net(); Player *p = new Player(resources, 0, info->id, info->desc.player_name, info->desc.characterset_name); players.push_back(p); p->state.server_state = info->server_state; p->state.client_server_state = info->client_server_state; p->state.client_state = info->client_state; if (p->state.id == my_id) { me = p; p->mark_as_me(); } if (tournament) { tournament->player_added(p); } break; } case GPCResetPlayer: { GPlayerInfo *info = reinterpret_cast<GPlayerInfo *>(t->data); info->from_net(); for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->state.id == info->id) { p->state.server_state = info->server_state; p->state.client_server_state = info->client_server_state; p->state.client_state = info->client_state; if (tournament) { tournament->reset_player(p); } break; } } break; } case GPCRemovePlayer: { player_id_t *nid = reinterpret_cast<player_id_t *>(t->data); player_id_t id = ntohs(*nid); for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->state.id == id) { if (id == my_id) { me = 0; } if (tournament) { tournament->player_removed(p); } players.erase(it); delete p; break; } } break; } case GPCSpawnPlayer: { GPlayerState *state = reinterpret_cast<GPlayerState *>(t->data); state->from_net(); for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->state.id == state->id) { if (p == me) { p->state.server_state = state->server_state; } else { p->state = *state; } p->respawning = false; if (tournament) { tournament->add_player_spawn_animation(p); } break; } } break; } case GPCUpdateGameState: { if (t->tournament_id == factory.get_tournament_id()) { GGameState *state = reinterpret_cast<GGameState *>(t->data); state->from_net(); if (tournament) { tournament->get_game_state() = *state; } } break; } case GPCUpdatePlayerState: { if (t->tournament_id== factory.get_tournament_id()) { GPTAllStates *state = reinterpret_cast<GPTAllStates *>(t->data); state->from_net(); for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->state.id == state->id) { p->state.server_state = state->server_state; if (p != me) { p->state.client_server_state = state->client_server_state; } break; } } } break; } case GPCUpdateObject: { if (tournament) { if (t->tournament_id == factory.get_tournament_id()) { GObjectState *state = reinterpret_cast<GObjectState *>(t->data); state->from_net(); Tournament::GameObjects& objects = tournament->get_game_objects(); for (Tournament::GameObjects::iterator it = objects.begin(); it != objects.end(); it++) { GameObject *obj = *it; if (obj->state.id == state->id) { obj->state = *state; break; } } } } break; } case GPCUpdateAnimation: { if (tournament) { if (t->tournament_id == factory.get_tournament_id()) { GAnimationState *state = reinterpret_cast<GAnimationState *>(t->data); state->from_net(); Tournament::GameAnimations& animations = tournament->get_game_animations(); for (Tournament::GameAnimations::iterator it = animations.begin(); it != animations.end(); it++) { GameAnimation *ani = *it; if (ani->state.id == state->id) { ani->state = *state; break; } } } } break; } case GPCUpdateSpawnableNPC: { if (tournament) { if (t->tournament_id == factory.get_tournament_id()) { GNPCState *state = reinterpret_cast<GNPCState *>(t->data); state->from_net(); Tournament::SpawnableNPCs& spawnable_npcs = tournament->get_spawnable_npcs(); for (Tournament::SpawnableNPCs::iterator it = spawnable_npcs.begin(); it != spawnable_npcs.end(); it++) { SpawnableNPC *npc = *it; if (npc->state.id == state->id) { npc->state = *state; break; } } } } break; } case GPCSpawnNPC: { if (tournament) { if (t->tournament_id == factory.get_tournament_id()) { GSpawnNPC *state = reinterpret_cast<GSpawnNPC *>(t->data); state->from_net(); tournament->add_spawnable_npc(state); } } break; } case GPCRemoveNPC: { if (tournament) { if (t->tournament_id == factory.get_tournament_id()) { GRemoveNPC *rnpc = reinterpret_cast<GRemoveNPC *>(t->data); rnpc->from_net(); tournament->remove_spawnable_npc(rnpc); } } break; } case GPCTextMessage: { std::string msg(reinterpret_cast<char *>(t->data), t->len); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = msg; client_text_messages.push_back(cmsg); break; } case GPCPlaySound: { GGenericName *name = reinterpret_cast<GGenericName *>(t->data); try { subsystem.play_sound(resources.get_sound(name->name), 0); } catch (...) { /* chomp */ } break; } case GPCChatMessage: { std::string msg(reinterpret_cast<char *>(t->data), t->len); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = msg; client_text_messages.push_back(cmsg); subsystem.play_sound(resources.get_sound("chat"), 0); break; } case GPCAddAnimation: { GAnimation *ani = reinterpret_cast<GAnimation *>(t->data); ani->from_net(); if (tournament) { tournament->add_animation(ani); } break; } case GPCAddTextAnimation: { GTextAnimation *ani = reinterpret_cast<GTextAnimation *>(t->data); ani->from_net(); if (tournament) { tournament->add_text_animation(ani); } break; } case GPCPlayerRecoil: { GPlayerRecoil *prec = reinterpret_cast<GPlayerRecoil *>(t->data); prec->from_net(); for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->state.id == prec->id) { p->state.client_server_state.accel_x += prec->x_recoil; break; } } break; } case GPCPlayerHurt: { if (tournament) { if (t->tournament_id == factory.get_tournament_id()) { subsystem.play_sound(resources.get_sound("hurt"), 0); } } break; } case GPCPickObject: { GPickObject *po = reinterpret_cast<GPickObject *>(t->data); po->from_net(); if (tournament) { tournament->add_pick_object(po); } break; } case GPCPlaceObject: { GPlaceObject *po = reinterpret_cast<GPlaceObject *>(t->data); po->from_net(); if (tournament) { tournament->add_place_object(po); } break; } case GPCSpawnObject: { GSpawnObject *so = reinterpret_cast<GSpawnObject *>(t->data); so->from_net(); if (tournament) { tournament->spawn_object(so); } break; } case GPCJoinAccepted: { if (tournament) { tournament->join_accepted(); } break; } case GPCJoinRefused: { if (tournament) { tournament->join_refused(); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = "YOUR JOIN REQUEST WAS REFUSED"; client_text_messages.push_back(cmsg); subsystem.play_sound(resources.get_sound("error"), 0); } break; } case GPCTeamScore: { GTeamScore *ts = reinterpret_cast<GTeamScore *>(t->data); ts->from_net(); if (tournament) { tournament->add_team_score(ts); } break; } case GPCTimeRemaining: { GTimeRemaining *remain = reinterpret_cast<GTimeRemaining *>(t->data); remain->from_net(); if (tournament) { tournament->update_wearable_remaining(remain); } break; } case GPCFriendlyFire: { GFriendyFireAlarm *alarm = reinterpret_cast<GFriendyFireAlarm *>(t->data); alarm->from_net(); if (tournament) { if (tournament->friendly_fire_alarm(alarm)) { subsystem.play_controlled_sound(resources.get_sound("friendly_fire"), 0); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = "FRIENDLY FIRE: WATCH OUT!!!"; client_text_messages.push_back(cmsg); } } break; } case GPCGamePlayUnbalanced: { if (tournament) { subsystem.play_controlled_sound(resources.get_sound("unbalanced"), 0); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = "GAMEPLAY IS UNBALANCED"; client_text_messages.push_back(cmsg); } break; } case GPCWarmUp: { if (tournament) { subsystem.play_system_sound(resources.get_sound("warm_up")); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = "please warm up"; client_text_messages.push_back(cmsg); } break; } case GPCGameBegins: { if (tournament) { subsystem.play_system_sound(resources.get_sound("ready")); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = "game begins"; client_text_messages.push_back(cmsg); } break; } case GPCGameOver: { if (tournament) { subsystem.play_system_sound(resources.get_sound("game_over")); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = "GAME IS OVER"; client_text_messages.push_back(cmsg); } break; } case GPCPlayerChanged: { GPlayerDescription *pdesc = reinterpret_cast<GPlayerDescription *>(t->data); pdesc->from_net(); for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->state.id == pdesc->id) { std::string old_name = p->get_player_name(); std::string new_name(pdesc->player_name); std::string old_skin(p->get_characterset()->get_name()); std::string new_skin(pdesc->characterset_name); /* change player name */ if (old_name != new_name) { p->set_player_name(new_name); p->font = 0; ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = old_name + " is now known as " + new_name; client_text_messages.push_back(cmsg); } /* change skin */ if (old_skin != new_skin) { try { p->set_characterset(new_skin); ClientTextMessage *cmsg = new ClientTextMessage; cmsg->text = p->get_player_name() + " changed the skin to " + new_skin; client_text_messages.push_back(cmsg); } catch (const Exception& e) { subsystem << e.what() << std::endl; } } break; } } break; } case GPCScoreTransportRaw: { if (tournament) { tournament->score_transport_raw(t->data); } break; } case GPCClanNames: { GClanNames *names = reinterpret_cast<GClanNames *>(t->data); names->from_net(); team_red_name = names->red_name; team_blue_name = names->blue_name; if (tournament) { tournament->set_team_names(team_red_name, team_blue_name); } break; } case GPCXferHeader: { reload_resources = true; GXferHeader *header = reinterpret_cast<GXferHeader *>(t->data); header->from_net(); if (!fhnd) { xfer_filename = header->filename; current_download_filename = get_home_directory() + dir_separator + UserDirectory + dir_separator + header->filename; fhnd = fopen(current_download_filename.c_str(), "wb"); if (fhnd) { total_xfer_sz = remaining_xfer_sz = header->filesize; } else { subsystem << "WARNING: cannot open file " << strerror(errno) << std::endl; } } else { subsystem << "WARNING: cannot receive " << xfer_filename << ". a file is already opened." << std::endl; } break; } case GPCXferDataChunk: { GXferDataChunk *chunk = reinterpret_cast<GXferDataChunk *>(t->data); chunk->from_net(); if (fhnd) { fwrite(chunk->data, chunk->chunksize, 1, fhnd); remaining_xfer_sz -= chunk->chunksize; if (remaining_xfer_sz < 1) { fclose(fhnd); fhnd = 0; current_download_filename = ""; } } { ScopeMutex lock(mtx); send_data(evt.c, 0, GPSPakSyncAck, NetFlagsReliable, 0, 0); } break; } case GPCGenericData: { if (tournament) { tournament->generic_data_delivery(t->data); } break; } case GPCServerQuit: { exception_msg.assign(reinterpret_cast<char *>(t->data), t->len); throw_exception = true; return; /* not necessary */ break; } } /* advance to next element */ if (t->flags & TransportFlagMorePackets) { unsigned char *tpb = reinterpret_cast<unsigned char *>(t); tpb += GTransportLen; tpb += t->len; t = reinterpret_cast<GTransport *>(tpb); } else { break; } } }