Subversion Repositories battle

[/] [trunk/] [Battle/] [GameInput.cpp] - Rev 116

Compare with Previous | Blame | View Log

#include "SDL/SDL.h"

#include <iostream>

#include "Main.h"

#include "GameInput.h"

// The value where we make the difference between a press and a non-press
const int GameInput::JOYSTICK_AXIS_THRESHOLD = 0x3fff;

// Input handling for keyboard and joystick
GameInput::GameInput() {
        joystick = NULL;

        reset();

        delay = 0;
        interval = 0;

        keyboard_enabled = true;
        joystick_enabled = false;

        keybinds = new std::vector<GameInputKeyBind>();
        joybuttonbinds = new std::vector<GameInputJoyButtonBind>();
        joyaxisbinds = new std::vector<GameInputJoyAxisBind>();
        joyhatbinds = new std::vector<GameInputJoyHatBind>();
}

GameInput::~GameInput() {
        if(joystick != NULL)
                SDL_JoystickClose(joystick);

        delete keybinds;
        delete joybuttonbinds;
        delete joyaxisbinds;
        delete joyhatbinds;
}

GameInput * GameInput::clone(bool clone_binds) {
        GameInput * gi = new GameInput();
        
        gi->enable_keyboard(keyboard_enabled);
        gi->enable_joystick(joystick_enabled);
        gi->open_joystick(get_joystick_idx());

        if(clone_binds) {
                for(unsigned int i = 0; i < keybinds->size(); i++) {
                        gi->bind_key(keybinds->at(i).key, keybinds->at(i).action);
                }

                for(unsigned int i = 0; i < joybuttonbinds->size(); i++) {
                        gi->bind_joybutton(joybuttonbinds->at(i).button, joybuttonbinds->at(i).action);
                }

                for(unsigned int i = 0; i < joyaxisbinds->size(); i++) {
                        gi->bind_joyaxis(joyaxisbinds->at(i).axis, joyaxisbinds->at(i).threshold, joyaxisbinds->at(i).action);
                }

                for(unsigned int i = 0; i < joyhatbinds->size(); i++) {
                        gi->bind_joyhat(joyhatbinds->at(i).hat, joyhatbinds->at(i).direction, joyhatbinds->at(i).action);
                }
        }

        return gi;
}

void GameInput::copy_from(GameInput * gi) {
        enable_keyboard(gi->keyboard_enabled);
        enable_joystick(gi->joystick_enabled);
        open_joystick(gi->get_joystick_idx());

        flush_keybinds();
        flush_joybuttons();
        flush_joyaxes();
        flush_joyhats();

        for(unsigned int i = 0; i < gi->keybinds->size(); i++) {
                bind_key(gi->keybinds->at(i).key, gi->keybinds->at(i).action);
        }

        for(unsigned int i = 0; i < gi->joybuttonbinds->size(); i++) {
                bind_joybutton(gi->joybuttonbinds->at(i).button, gi->joybuttonbinds->at(i).action);
        }

        for(unsigned int i = 0; i < gi->joyaxisbinds->size(); i++) {
                bind_joyaxis(gi->joyaxisbinds->at(i).axis, gi->joyaxisbinds->at(i).threshold, gi->joyaxisbinds->at(i).action);
        }

        for(unsigned int i = 0; i < gi->joyhatbinds->size(); i++) {
                bind_joyhat(gi->joyhatbinds->at(i).hat, gi->joyhatbinds->at(i).direction, gi->joyhatbinds->at(i).action);
        }
}

// Enable keyboard
void GameInput::enable_keyboard(bool enable) {
        keyboard_enabled = enable;
}

// Enable joystick
void GameInput::enable_joystick(bool enable) {
        joystick_enabled = enable;
}

// Open a joystick
bool GameInput::open_joystick(int index) {
        if(joystick != NULL) {
                SDL_JoystickClose(joystick);
        }

        joystick = SDL_JoystickOpen(index);

        if(joystick == NULL)
                return false;

        joystick_idx = index;
        joystick_enabled = true;

        return true;
}

int GameInput::get_joystick_idx() {
        return joystick_idx;
}

int GameInput::num_buttons() {
        return SDL_JoystickNumButtons(joystick);
}

int GameInput::num_axes() {
        return SDL_JoystickNumAxes(joystick);
}

int GameInput::num_hats() {
        return SDL_JoystickNumHats(joystick);
}

// Bind a keyboard key to an action
void GameInput::bind_key(int key, int action) {
        GameInputKeyBind bind;
        bind.key = key;
        bind.action = action;
        keybinds->push_back(bind);
}

// Bind a joystick button to an action
void GameInput::bind_joybutton(int button, int action) {
        GameInputJoyButtonBind bind;
        bind.button = button;
        bind.action = action;
        joybuttonbinds->push_back(bind);
}

// Bind an joystick axis button to an action
void GameInput::bind_joyaxis(int axis, bool positive, int action) {
        int threshold;
        threshold = positive ? JOYSTICK_AXIS_THRESHOLD : -JOYSTICK_AXIS_THRESHOLD;
        bind_joyaxis(axis, threshold, action);
}

// Bind an joystick axis button to an action
void GameInput::bind_joyaxis(int axis, int threshold, int action) {
        GameInputJoyAxisBind bind;
        bind.axis = axis;
        bind.threshold = threshold;
        bind.action = action;
        joyaxisbinds->push_back(bind);
}

// Bind a joystick POV-hat button to an action
void GameInput::bind_joyhat(int hat, int direction, int action) {
        GameInputJoyHatBind bind;
        bind.hat = hat;
        bind.direction = direction;
        bind.action = action;
        joyhatbinds->push_back(bind);
}

// Check if a binded button for an action is pressed
bool GameInput::is_pressed(int action) {
        if(delay == 0)
                return pressed[action];

        if(!pressed[action])
                return false;

        int frames;

        frames = Main::frame - press_start[action];

        if(frames == 0)
                return true;
        
        if(frames < delay)
                return false;

        frames -= delay;
        frames = frames % interval;

        if(frames == 0)
                return true;
        else
                return false;
}

void GameInput::reset() {
        for(int i = 0; i < ACTION_COUNT; i++) {
                pressed[i] = false;
        }
}

void GameInput::set_delay(int d, int i) {
        delay = d;
        interval = i;
}

void GameInput::unset_delay() {
        delay = 0;
        interval = 0;
}

void GameInput::handle_event(SDL_Event *event) {
        unsigned int idx;

        if(keyboard_enabled) {
                // Keyboard button
                if(event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) {
                        GameInputKeyBind bind;

                        for(idx = 0; idx < keybinds->size(); idx++) {
                                bind = keybinds->at(idx);
                                if(bind.key == event->key.keysym.sym) {
                                        if(event->type == SDL_KEYDOWN && !pressed[bind.action]) {
                                                pressed[bind.action] = true;
                                                press_start[bind.action] = Main::frame;
                                        }
                                        if(event->type == SDL_KEYUP && pressed[bind.action]) {
                                                pressed[bind.action] = false;
                                                press_start[bind.action] = 0;
                                        }
                                }
                        }
                }
        }

        if(joystick_enabled) {
                // Joystick button
                if(event->type == SDL_JOYBUTTONDOWN || event->type == SDL_JOYBUTTONUP) {
                        GameInputJoyButtonBind bind;

                        if(event->jbutton.which != joystick_idx)
                                return;

                        for(idx = 0; idx < joybuttonbinds->size(); idx++) {
                                bind = joybuttonbinds->at(idx);
                                if(bind.button == event->jbutton.button) {
                                        if(event->type == SDL_JOYBUTTONDOWN && !pressed[bind.action]) {
                                                pressed[bind.action] = true;
                                                press_start[bind.action] = Main::frame;
                                        }
                                        if(event->type == SDL_JOYBUTTONUP && pressed[bind.action]) {
                                                pressed[bind.action] = false;
                                                press_start[bind.action] = 0;
                                        }
                                }
                        }
                }

                // Joystick axis
                if(event->type == SDL_JOYAXISMOTION) {
                        GameInputJoyAxisBind bind;

                        if(event->jaxis.which != joystick_idx)
                                return;

                        for(idx = 0; idx < joyaxisbinds->size(); idx++) {
                                bind = joyaxisbinds->at(idx);
                                if(bind.axis == event->jaxis.axis) {
                                        if(!pressed[bind.action]) {
                                                if(bind.threshold < 0 && bind.threshold > event->jaxis.value) {
                                                        pressed[bind.action] = true;
                                                        press_start[bind.action] = Main::frame;
                                                }
                                                else if(bind.threshold >= 0 && bind.threshold < event->jaxis.value) {
                                                        pressed[bind.action] = true;
                                                        press_start[bind.action] = Main::frame;
                                                }
                                        }
                                        else {
                                                if(bind.threshold < 0 && bind.threshold < event->jaxis.value) {
                                                        pressed[bind.action] = false;
                                                        press_start[bind.action] = 0;
                                                }
                                                if(bind.threshold >= 0 && bind.threshold > event->jaxis.value) {
                                                        pressed[bind.action] = false;
                                                        press_start[bind.action] = 0;
                                                }
                                        }
                                }
                        }
                }

                // Joystick hat
                if(event->type == SDL_JOYHATMOTION) {
                        GameInputJoyHatBind bind;

                        if(event->jhat.which != joystick_idx)
                                return;

                        for(idx = 0; idx < joyhatbinds->size(); idx++) {
                                bind = joyhatbinds->at(idx);

                                if(bind.hat == event->jhat.hat) {
                                        if((event->jhat.value & bind.direction) == bind.direction && !pressed[bind.action]) {
                                                pressed[bind.action] = true;
                                                press_start[bind.action] = Main::frame;
                                        }
                                        if((event->jhat.value & bind.direction) == 0 && pressed[bind.action]) {
                                                pressed[bind.action] = false;
                                                press_start[bind.action] = 0;
                                        }
                                }
                        }
                }
        }
}

// Get the current position of a joystick axis
int GameInput::get_joyaxis(int axis) {
        if(joystick_enabled || joystick != NULL) {
                SDL_JoystickUpdate();
                return SDL_JoystickGetAxis(joystick, axis);
        }
        else {
                return 0;
        }
}

// Wait until a key is pressed
int GameInput::keyboard_wait_event() {
        SDL_Event event;
        int retval;
        
        retval = 0;
        while(Main::instance->running) {
                SDL_WaitEvent(&event);
                
                Main::instance->handle_event(&event);

                if(event.type == SDL_KEYDOWN) {
                        retval = event.key.keysym.sym;
                        break;
                }
        }

        return retval;
}

// Wait until a button has been pressed on the joystick
void GameInput::joystick_wait_event(GameInputJoystickEvent * event) {
        SDL_Event sdlevent;
        int old_state;
        int jindex;

        event->type = event->NONE;
        event->button = 0;
        event->axis_idx = 0;
        event->axis_value = 0;
        event->hat_idx = 0;
        event->hat_direction = 0;

        if(!joystick_enabled || joystick == NULL)
                return;

        joystick_wait_released();

        old_state = SDL_JoystickEventState(SDL_QUERY);
        SDL_JoystickEventState(SDL_ENABLE);

        jindex = SDL_JoystickIndex(joystick);

        while(Main::instance->running) {
                SDL_WaitEvent(&sdlevent);
                
                Main::instance->handle_event(&sdlevent);

                if(sdlevent.type == SDL_JOYBUTTONDOWN && sdlevent.jbutton.which == jindex) {
                        event->type = event->BUTTON;
                        event->button = sdlevent.jbutton.button;
                        break;
                }
                if(sdlevent.type == SDL_JOYAXISMOTION && sdlevent.jaxis.which == jindex) {
                        if(sdlevent.jaxis.value < -JOYSTICK_AXIS_THRESHOLD || sdlevent.jaxis.value > JOYSTICK_AXIS_THRESHOLD) {
                                event->type = event->AXIS;
                                event->axis_idx = sdlevent.jaxis.axis;
                                if(sdlevent.jaxis.value < -JOYSTICK_AXIS_THRESHOLD) {
                                        event->axis_value = -JOYSTICK_AXIS_THRESHOLD;
                                } else {
                                        event->axis_value = JOYSTICK_AXIS_THRESHOLD;
                                }
                                break;
                        }
                }
                if(sdlevent.type == SDL_JOYHATMOTION && sdlevent.jhat.which == jindex) {
                        event->type = event->HAT;
                        event->hat_idx = sdlevent.jhat.hat;
                        event->hat_direction = sdlevent.jhat.value;
                        break;
                }
        }

        SDL_JoystickEventState(old_state);
}

// Wait until all the joystick buttons are released and all the axes and hats are centered
void GameInput::joystick_wait_released() {
        int buttons, axes, hats;
        int i;

        if(!joystick_enabled || joystick == NULL)
                return;
        
        bool released;

        buttons = SDL_JoystickNumButtons(joystick);
        axes = SDL_JoystickNumAxes(joystick);
        hats = SDL_JoystickNumHats(joystick);
        
        do {
                released = true;

                SDL_JoystickUpdate();

                for(i = 0; i < buttons; i++) {
                        if(SDL_JoystickGetButton(joystick, i) == 1)
                                released = false;
                }

                for(i = 0; i < axes; i++) {
                        if(SDL_JoystickGetAxis(joystick, i) < -JOYSTICK_AXIS_THRESHOLD ||
                                SDL_JoystickGetAxis(joystick, i) > JOYSTICK_AXIS_THRESHOLD)
                                released = false;
                }

                for(i = 0; i < hats; i++) {
                        if(SDL_JoystickGetHat(joystick, i) != SDL_HAT_CENTERED)
                                released = false;
                }
        } while(!released);
}

// Poll for a key and bind it
void GameInput::keyboard_wait_event_bind(int action) {
        int key;
        key = keyboard_wait_event();
        if(key != 0) {
                bind_key(key, action);
        }
}

// Poll for a joystick button, axis or hat press and bind it
void GameInput::joystick_wait_event_bind(int action) {
        GameInputJoystickEvent event;

        joystick_wait_event(&event);

        if(event.type == event.BUTTON) {
                bind_joybutton(event.button, action);
        }
        if(event.type == event.AXIS) {
                bind_joyaxis(event.axis_idx, event.axis_value, action);
        }
        if(event.type == event.HAT) {
                bind_joyhat(event.hat_idx, event.hat_direction, action);
        }
}

void GameInput::load_options(std::istream * stream) {
        int i;
        short count;

        count = 0;

        stream->read((char *)&joystick_idx, sizeof(int));
        open_joystick(joystick_idx);

        stream->read((char*)&count, sizeof(short));
        for(i = 0; i < count; i++) {
                GameInputKeyBind bind;
                stream->read((char *)&bind, sizeof(GameInputKeyBind));
                keybinds->push_back(bind);
        }

        stream->read((char*)&count, sizeof(short));
        for(i = 0; i < count; i++) {
                GameInputJoyButtonBind bind;
                stream->read((char *)&bind, sizeof(GameInputJoyButtonBind));
                joybuttonbinds->push_back(bind);
        }

        stream->read((char*)&count, sizeof(short));
        for(i = 0; i < count; i++) {
                GameInputJoyAxisBind bind;
                stream->read((char *)&bind, sizeof(GameInputJoyAxisBind));
                joyaxisbinds->push_back(bind);
        }

        stream->read((char*)&count, sizeof(short));
        for(i = 0; i < count; i++) {
                GameInputJoyHatBind bind;
                stream->read((char *)&bind, sizeof(GameInputJoyHatBind));
                joyhatbinds->push_back(bind);
        }
}

void GameInput::save_options(std::ostream * stream) {
        short count;

        stream->write((char *)&joystick_idx, sizeof(int));

        count = (short)keybinds->size();
        stream->write((char *)&count, sizeof(short));
        for(unsigned int i = 0; i < keybinds->size(); i++) {
                stream->write((char *)&keybinds->at(i), sizeof(GameInputKeyBind));
        }

        count = (short)joybuttonbinds->size();
        stream->write((char *)&count, sizeof(short));
        for(unsigned int i = 0; i < joybuttonbinds->size(); i++) {
                stream->write((char *)&joybuttonbinds->at(i), sizeof(GameInputJoyButtonBind));
        }

        count = (short)joyaxisbinds->size();
        stream->write((char *)&count, sizeof(short));
        for(unsigned int i = 0; i < joyaxisbinds->size(); i++) {
                stream->write((char *)&joyaxisbinds->at(i), sizeof(GameInputJoyAxisBind));
        }

        count = (short)joyhatbinds->size();
        stream->write((char *)&count, sizeof(short));
        for(unsigned int i = 0; i < joyhatbinds->size(); i++) {
                stream->write((char *)&joyhatbinds->at(i), sizeof(GameInputJoyHatBind));
        }
}

void GameInput::flush_keybinds() {
        keybinds->clear();
}

void GameInput::flush_joybuttons() {
        joybuttonbinds->clear();
}

void GameInput::flush_joyaxes() {
        joyaxisbinds->clear();
}

void GameInput::flush_joyhats() {
        joyhatbinds->clear();
}

Compare with Previous | Blame