예제 #1
0
void TournamentTeam::reopen_join_window(Player *p) {
    p->joining = false;
    player_join_request(p);
}
예제 #2
0
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);
        }
    }
}