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::spawn_object(Object *obj, identifier_t id, int x, int y, flags_t flags) { GameObject *nobj = new GameObject; nobj->object = obj; nobj->origin_x = x; nobj->origin_y = y; nobj->state.id = id; nobj->state.accel_x = 0.0f; nobj->state.accel_y = 0.0f; nobj->state.x = static_cast<double>(x); nobj->state.y = static_cast<double>(y); nobj->spawned_object = true; game_objects.push_back(nobj); if (!server) { if (flags & PlaceObjectWithAnimation) { const std::string& spawn_animation = obj->get_value("spawn_animation"); if (spawn_animation.length()) { TileGraphic *tg = obj->get_tile()->get_tilegraphic(); add_animation(spawn_animation, 0, 0, 0, x, y, 0.0f, 0.0f, tg->get_width(), tg->get_height()); } } if (flags & PlaceObjectWithSpawnSound) { const std::string& sound_name = obj->get_value("spawn_sound"); if (sound_name.length()) { Sound *sound = resources.get_sound(sound_name); subsystem.play_sound(sound, 0); } } } }
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::add_pick_object(GPickObject *po) { for (GameObjects::iterator it = game_objects.begin(); it != game_objects.end(); it++) { GameObject *obj = *it; if (obj->state.id == po->id) { obj->picked = true; obj->delete_me = obj->object->is_spawnable(); const std::string& pickup_animation = obj->object->get_value("pickup_animation"); if (pickup_animation.length()) { TileGraphic *tg = obj->object->get_tile()->get_tilegraphic(); add_animation(pickup_animation, 0, 0, 0, static_cast<int>(obj->state.x), static_cast<int>(obj->state.y), 0.0f, 0.0f, tg->get_width(), tg->get_height()); } if (!server) { const std::string& pickup_sound = obj->object->get_value("pickup_sound"); if (pickup_sound.length()) { Sound *sound = resources.get_sound(pickup_sound); subsystem.play_sound(sound, 0); } } break; } } /* delete marked objects */ game_objects.erase(std::remove_if(game_objects.begin(), game_objects.end(), erase_element<GameObject>), game_objects.end()); }
void Tournament::add_animation(const std::string& name, identifier_t id, scounter_t duration, identifier_t owner, int x, int y, double accel_x, double accel_y, int width, int height) { GameAnimation *gani = 0; try { Animation *ani = resources.get_animation(name); TileGraphic *tg = ani->get_tile()->get_tilegraphic(); gani = new GameAnimation; gani->animation = ani; gani->state.id = id; /* client side generated are 0 */ gani->state.duration = duration; gani->state.owner = owner; gani->state.x = x + width / 2 - tg->get_width() / 2; gani->state.y = y + height / 2 - tg->get_height() / 2; gani->state.accel_x = accel_x; gani->state.accel_y = accel_y; game_animations.push_back(gani); if (!server) { subsystem.play_sound(ani->get_sound(), ani->get_sound_loops()); } } catch (const ResourcesException& e) { if (gani) delete gani; subsystem << "creating animation failed: " << e.what() << std::endl; } }
void Tournament::add_player_spawn_animation(Player *p) { const std::string& spawn_animation = p->get_characterset()->get_value("spawn_animation"); if (spawn_animation.length()) { Animation *ani = resources.get_animation(spawn_animation); TileGraphic *tg = ani->get_tile()->get_tilegraphic(); int x = static_cast<int>(p->state.client_server_state.x); int y = static_cast<int>(p->state.client_server_state.y) - p->get_characterset()->get_height(); add_animation(spawn_animation, 0, 0, 0, x, y, 0.0f, 0.0f, tg->get_width(), tg->get_height()); subsystem.play_sound(resources.get_sound("respawn"), 0); } }
void Tournament::add_place_object(GPlaceObject *po) { for (GameObjects::iterator it = game_objects.begin(); it != game_objects.end(); it++) { GameObject *obj = *it; if (obj->state.id == po->id) { obj->picked = false; obj->state.x = po->x; obj->state.y = po->y; if (po->flags & PlaceObjectWithAnimation) { const std::string& spawn_animation = obj->object->get_value("spawn_animation"); if (spawn_animation.length()) { TileGraphic *tg = obj->object->get_tile()->get_tilegraphic(); add_animation(spawn_animation, 0, 0, 0, static_cast<int>(obj->state.x), static_cast<int>(obj->state.y), 0.0f, 0.0f, tg->get_width(), tg->get_height()); } } if (po->flags & PlaceObjectResetVelocity) { obj->state.accel_x = 0; obj->state.accel_y = 0; } if (!server) { if (po->flags & PlaceObjectWithSpawnSound) { const std::string& sound_name = obj->object->get_value("spawn_sound"); if (sound_name.length()) { Sound *sound = resources.get_sound(sound_name); subsystem.play_sound(sound, 0); } } if (po->flags & PlaceObjectWithScoredSound) { const std::string& sound_name = obj->object->get_value("score_sound"); if (sound_name.length()) { Sound *sound = resources.get_sound(sound_name); subsystem.play_sound(sound, 0); } } if (po->flags & PlaceObjectWithDropSound) { const std::string& sound_name = obj->object->get_value("drop_sound"); if (sound_name.length()) { Sound *sound = resources.get_sound(sound_name); subsystem.play_sound(sound, 0); } } } break; } } }
void Tournament::spawn_player_base(Player *p, SpawnPoints& spawn_points) { // TODO: better selection of spawn points, maybe order by last spawn point usage GameObject *obj = spawn_points[rand() % spawn_points.size()]; const CollisionBox& colbox = p->get_characterset()->get_colbox(); TileGraphic *tg = obj->object->get_tile()->get_tilegraphic(); int w = tg->get_width(); int h = tg->get_height(); int x = static_cast<int>(obj->state.x); int y = static_cast<int>(obj->state.y); x += w / 2; y += h; x = x - colbox.width / 2 - colbox.x; p->spawn(x, y); }
void TournamentTeam::draw_team_colours() { for (Players::iterator it = players.begin(); it != players.end(); it++) { Player *p = *it; if (p->is_alive_and_playing()) { /* draw team colours */ Tile *t = p->get_characterset()->get_tile( static_cast<Direction>(p->state.client_server_state.direction), static_cast<CharacterAnimation>(p->state.client_state.icon)); TileGraphic *tg = t->get_tilegraphic(); const int bar_width = 32; const int bar_height = 4; int x = static_cast<int>(p->state.client_server_state.x) + (tg->get_width() / 2) - (bar_width / 2); int y = static_cast<int>(p->state.client_server_state.y) - (tg->get_height()) - bar_height + 2; if (p->state.server_state.flags & PlayerServerFlagTeamRed) { subsystem.set_color(1.0f, 0.0f, 0.0f, 1.0f); } else { subsystem.set_color(0.0f, 0.0f, 1.0f, 1.0f); } subsystem.draw_box(x + left, y + top, bar_width, bar_height); subsystem.reset_color(); } } }
GuiTextbox *OptionsMenu::create_field(GuiWindow *parent, int x, int y, const std::string& text, GuiVirtualButton::OnClick on_click, bool erase_pic) { Icon *select = resources.get_icon("select"); TileGraphic *stg = select->get_tile()->get_tilegraphic(); int iw = stg->get_width(); int ih = stg->get_height(); gui.create_label(parent, x, y, text); GuiTextbox *tb = gui.create_textbox(parent, x + 50, y, 80, ""); int tbh = tb->get_height(); if (!erase_pic) { tb->set_locked(true); GuiButton *btn = gui.create_button(parent, x + 129, y, tbh, tbh, "", on_click, this); btn->show_bolts(false); gui.create_picture(btn, tbh / 2 - iw / 2, tbh / 2 - ih / 2, stg); } else { tb->set_locked(false); GuiButton *btn = gui.create_button(parent, x + 129, y, tbh, tbh, "...", on_click, this); btn->show_bolts(false); } return tb; }
void Tournament::update_npc_states(double period_f) { for (SpawnableNPCs::iterator it = spawnable_npcs.begin(); it != spawnable_npcs.end(); it++) { SpawnableNPC *npc = *it; /* update physics */ TileGraphic *tg = npc->npc->get_tile(DirectionLeft, NPCAnimationStanding)->get_tilegraphic(); int width = tg->get_width(); int height = tg->get_height(); const CollisionBox& colbox = npc->npc->get_colbox(); bool killing = false; double y = npc->state.y - height; bool col = render_physics(period_f, false, 0, 0.0f, 0, npc->npc->get_springiness_x(), 0.0f, colbox, npc->state.x, y, npc->state.accel_x, npc->state.accel_y, width, height, npc->npc->get_friction_factor(), false, npc->falling, npc->last_falling_y_pos, &killing, npc->npc->get_name()); npc->state.y = y + height; /* update ownership */ if (server && npc->init_owner) { npc->ignore_owner_counter -= period_f; if (npc->ignore_owner_counter <= 0.0f) { npc->init_owner = 0; } } /* update motion */ Sound *idle_sound = 0; if (!npc->in_idle) { npc->move_counter -= period_f; if (npc->move_counter <= 0.0f) { npc->move_counter = static_cast<double>(npc->npc->get_move_init_randomized()); int action = rand() % 5; switch (action) { case 0: case 1: { /* jump */ if (server) { if (!npc->falling) { npc->state.accel_y = -npc->npc->get_jump_y_impulse_randomized(); npc->state.accel_x = npc->npc->get_jump_x_impulse_randomized() * (static_cast<Direction>(npc->state.direction) == DirectionLeft ? -1 : 1); } } break; } case 2: { /* change direction */ if (server && !col && !npc->falling) { npc->state.direction = static_cast<unsigned char>(rand() % 2 ? DirectionLeft : DirectionRight); } break; } case 3: { /* idle 1 */ if (!server) { npc->in_idle = true; npc->idle_counter_init = npc->npc->get_idle1_counter(); npc->idle_counter = npc->idle_counter_init; npc->icon = NPCAnimationIdle1; npc->iconindex = 0; const std::string& sound = npc->npc->get_value("idle1_sound"); if (sound.length()) { try { idle_sound = resources.get_sound(sound); } catch (const Exception& e) { subsystem << e.what() << std::endl; } } } break; } case 4: { /* idle 2 */ if (!server) { npc->in_idle = true; npc->idle_counter_init = npc->npc->get_idle2_counter(); npc->idle_counter = npc->idle_counter_init; npc->icon = NPCAnimationIdle2; npc->iconindex = 0; const std::string& sound = npc->npc->get_value("idle2_sound"); if (sound.length()) { try { idle_sound = resources.get_sound(sound); } catch (const Exception& e) { subsystem << e.what() << std::endl; } } } break; } } } } /* max x accel? */ double max_accel_x = npc->npc->get_max_accel_x(); if (npc->state.accel_x > max_accel_x) { npc->state.accel_x = max_accel_x; } if (npc->state.accel_x < -max_accel_x) { npc->state.accel_x = -max_accel_x; } if (server) { if (npc->state.y - 100 > map_height * tile_height) { npc->delete_me = true; send_remove_npc(npc); } else if (killing) { npc->delete_me = true; add_npc_remove_animation(npc); send_remove_npc(npc); } } /* update icon */ if (npc->falling) { npc->in_idle = false; npc->icon = NPCAnimationJumping; npc->iconindex = 0; if (col && server) { if (npc->state.direction == static_cast<unsigned char>(DirectionLeft)) { npc->state.direction = static_cast<unsigned char>(DirectionRight); } else { npc->state.direction = static_cast<unsigned char>(DirectionLeft); } } } else { if (!npc->in_idle) { npc->icon = NPCAnimationStanding; npc->iconindex = 0; } } /* update idle */ if (npc->in_idle) { if (idle_sound) { subsystem.play_sound(idle_sound, 0); } npc->idle_counter -= period_f; if (npc->idle_counter <= 0.0f) { npc->iconindex++; npc->idle_counter = npc->idle_counter_init; Tile *t =npc->npc->get_tile(static_cast<Direction>(npc->state.direction), npc->icon); TileGraphic *tg = t->get_tilegraphic(); if (static_cast<size_t>(npc->iconindex) >= tg->get_tile_count()) { npc->in_idle = false; npc->icon = NPCAnimationStanding; npc->iconindex = 0; } } } } remove_marked_npcs(); }
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); } } }