#include #include #include #include #include #include #include "Animal.h" #include "embed.h" using namespace std; SkinsEmbed skins_embed; Animal::Animal(string skin) { move_time = 0; animate_time = 0; random_time = 0; cur = 0; viime_event = 0; state_changed = false; animation_i = 0; // set rand seed SDL_srand(0); readSkin(skin); setSpecialState(drop); } Animal::~Animal() { for (unsigned i = 0; i < states.size(); i++) { if (states[i]) { delete states[i]; } } } SDL_Surface *Animal::readImg(string skin, string state_name, int num) { SDL_Surface *pet; embed_data *img_buf = skins_embed.getSkin(skin, state_name); if (img_buf) { // load from embedded if exists SDL_IOStream *io = SDL_IOFromConstMem(img_buf[num].data, img_buf[num].len); if (io == NULL) { SDL_Log("could not open io for pet: %s\n", SDL_GetError()); } pet = IMG_Load_IO(io, true); if (pet == NULL) { SDL_Log("could not create pet surface: %s\n", SDL_GetError()); } } else { stringstream file_name; file_name << "skin/" << skin << "/" << state_name << "/" << (num + 1) << ".png"; pet = IMG_Load(file_name.str().c_str()); if (pet == NULL) { SDL_Log("could not create pet surface: %s\n", SDL_GetError()); } } // SDL_CreateTexture can't handle palette formats, so check if needs converting if (pet && pet->format <= SDL_PIXELFORMAT_INDEX8) { SDL_Surface *tmp = pet; pet = SDL_ConvertSurface(pet, SDL_GetPixelFormatForMasks(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)); SDL_DestroySurface(tmp); } if (pet && !SDL_ISPIXELFORMAT_ALPHA(pet->format)) { /* Set the colorkey to the top-left pixel */ Uint8 r, g, b, a; SDL_ReadSurfacePixel(pet, 0, 0, &r, &g, &b, &a); SDL_SetSurfaceColorKey(pet, 1, SDL_MapSurfaceRGBA(pet, r, g, b, a)); } return pet; } void Animal::readSkin(string skin) { string buf; string key; State feat; char prev_chr; for (unsigned i = 0; i < states.size(); i++) { if (states[i]) { delete states[i]; } } states.clear(); special_states.clear(); random_states.clear(); special_states.resize(SPECIAL_STATE_LAST); string params = skins_embed.getParams(skin); if (params.length()) { // load from embedded if exists for (string::iterator it = params.begin(); it != params.end(); ++it) { readSkinStep(skin, *it, buf, key, feat, prev_chr); } } else { stringstream file_name; file_name << "skin/" << skin << ".tsk"; FILE *tied = fopen(file_name.str().c_str(), "rb"); if (!tied) { cout << "Skin file fail: " << file_name.str() << endl; } while(!feof(tied)) { char chr = fgetc(tied); readSkinStep(skin, chr, buf, key, feat, prev_chr); } fclose(tied); } if (!feat.name.empty()) { readSkinFill(skin, feat); feat.name.clear(); feat.sound.clear(); feat.sound_data.loop = 0; } } void Animal::readSkinStep(string skin, char chr, string &buf, string &key, State &feat, char &prev_chr) { if (chr == '\n') { if (prev_chr == '\n' && !feat.name.empty()) { readSkinFill(skin, feat); feat.name.clear(); feat.sound.clear(); feat.sound_data.loop = 0; } if (key == "name") feat.name = buf; if (key == "img_name") feat.img_name = buf; if (key == "count") feat.count = atoi(buf.c_str()); if (key == "interval") feat.interval = atoi(buf.c_str()); if (key == "x") feat.x = atoi(buf.c_str()); if (key == "y") feat.y = atoi(buf.c_str()); if (key == "sound") feat.sound = buf.c_str(); if (key == "loop") feat.sound_data.loop = atoi(buf.c_str()); buf.clear(); } else if (chr == '=') { key = buf; buf.clear(); } else { buf.push_back(chr); } prev_chr = chr; } void Animal::readSkinFill(string skin, State &feat) { cur_state = new State(feat); states.push_back(cur_state); // Load images to memory for (int x = 0; x < cur_state->count; x++) { cur_state->imgs.push_back(NULL); cur_state->imgs[x] = readImg(skin, cur_state->img_name, x); } if (feat.sound.empty()) { cur_state->sound_data.len = 0; } else { pair buf = skins_embed.getSound(skin, cur_state->img_name); if (buf.first) { // load from embedded if exists SDL_IOStream *io = SDL_IOFromConstMem(buf.first, buf.second); if (io == NULL) { SDL_Log("could not open io for sound: %s\n", SDL_GetError()); } if (!SDL_LoadWAV_IO(io, true, &cur_state->sound_data.spec, &cur_state->sound_data.buf, &cur_state->sound_data.len)) { SDL_Log("failed to load audio: %s", SDL_GetError()); } } else { stringstream file_name; file_name << "skin/" << skin << "/" << cur_state->img_name << "/" << cur_state->sound; if (!SDL_LoadWAV(file_name.str().c_str(), &cur_state->sound_data.spec, &cur_state->sound_data.buf, &cur_state->sound_data.len)) { SDL_Log("failed to load audio %s: %s", file_name.str().c_str(), SDL_GetError()); } } } // Special state cases cur_state->interruptable = true; if (cur_state->name == "stay") { cur_state->special = stay; random_states.push_back(cur_state); } else if (cur_state->name == "drop") { cur_state->special = drop; cur_state->interruptable = false; } else if (cur_state->name == "jump") { cur_state->special = jump; random_states.push_back(cur_state); cur_state->interruptable = false; } else if (cur_state->name == "jump_end") { cur_state->special = jump_end; cur_state->interruptable = false; } else if (cur_state->name == "flop") { cur_state->special = flop; } else if (cur_state->name == "hang") { cur_state->special = hang; cur_state->interruptable = false; } else if (cur_state->name == "fast_right") { cur_state->special = fast_right; random_states.push_back(cur_state); } else if (cur_state->name == "fast_left") { cur_state->special = fast_left; random_states.push_back(cur_state); } else { cur_state->special = NOTHING; random_states.push_back(cur_state); } if (cur_state->special) { special_states[cur_state->special] = cur_state; } } void Animal::move() { disp_pos.x += cur_state->x; disp_pos.y += cur_state->y; // Don't go out of display on right side if (disp_pos.x + getImage()->w > disp_pos.w) { setSpecialState(stay); if (disp_pos.y < disp_pos.h - 150) { setSpecialState(drop); } disp_pos.x = disp_pos.w - getImage()->w - 2; } // Don't go out of display on left side if (disp_pos.x < 0) { setSpecialState(stay); if (disp_pos.y < disp_pos.h - 150) { setSpecialState(drop); } disp_pos.x = 2; } // Check if we have jumped high enough if (disp_pos.y < disp_pos.h - 300 && cur_state->name == "jump") { setSpecialState(jump_end); } // Check if if hit the ground if (disp_pos.y >= disp_pos.h - 150) { if (cur_state->name == "jump_end") { setSpecialState(stay); } else if (cur_state->name == "drop") { setSpecialState(flop); } } } void Animal::animate(bool reset){ if (reset) { animation_i = 0; } else if (animation_i >= cur_state->count - 1) { animation_i = 0; } else { animation_i++; } } void Animal::shuffle() { // Check if we are in a cur_state where we can't do anything if (cur_state->interruptable) { int lot = SDL_rand(random_states.size()); setState(random_states[lot]); } } void Animal::setState(State *s) { cur_state = s; cur_state->sound_data.is_played = false; animate(true); state_changed = true; } void Animal::setSpecialState(SpecialState s) { //SDL_Log("special_state %d", s); cur_state = special_states[s]; //SDL_Log("cur_state %x", cur_state); cur_state->sound_data.is_played = false; animate(true); state_changed = true; } void Animal::resetTimes(Uint64 t) { move_time = random_time = t; } Uint64 Animal::step(Uint64 cur, bool press) { short move_interval = 25; if (!press && cur >= move_time + move_interval) { move(); move_time += move_interval; // don't allow move_time be too far away from cur if (cur > move_time + move_interval * 5) { move_time = cur; } } if (cur >= animate_time + cur_state->interval) { animate(false); animate_time += cur_state->interval; // don't allow animate_time be too far away from cur if (cur > animate_time + cur_state->interval * 5) { animate_time = cur; } } short random_interval = 1000 * 5; if (!press && cur >= random_time + random_interval) { shuffle(); random_time += random_interval; // don't allow random_time be too far away from cur if (cur >= random_time + random_interval * 5) { random_time = cur; } } Uint64 nearest_time = move_time + move_interval; if (nearest_time > animate_time + cur_state->interval) { nearest_time = animate_time + cur_state->interval; } if (nearest_time > random_time + random_interval) { nearest_time = random_time + random_interval; } if (nearest_time < cur) { nearest_time = cur; } return nearest_time - cur; } SDL_Surface *Animal::getImage() { return cur_state->imgs[animation_i]; } SoundData *Animal::getSound() { return &cur_state->sound_data; } bool Animal::hasStateChanged() { if (state_changed) { state_changed = false; return true; } return false; } void Animal::shouldRunAway(char dir) { // Check if we are in a cur_state where we can't do anything if (cur_state->interruptable) { if (dir == 1) { if (cur_state->special != fast_right) { setSpecialState(fast_right); } } else { if (cur_state->special != fast_left) { setSpecialState(fast_left); } } } } SDL_Surface *Animal::getPreviewSurface() { if (special_states[stay]->imgs.size()) { return special_states[stay]->imgs[0]; } else { return NULL; } }