Files
SDLRousku/Animal.cpp

371 lines
10 KiB
C++

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream>
#include <SDL3/SDL_audio.h>
#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<unsigned char*, unsigned int> 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) {
if (!press && cur >= move_time) {
move();
move_time += 25;
}
if (cur >= animate_time) {
animate(false);
animate_time += cur_state->interval;
}
if (!press && cur >= random_time) {
shuffle();
random_time += 1000 * 5;
}
Uint64 lowest_time = move_time;
if (lowest_time > animate_time) lowest_time = animate_time;
if (lowest_time > random_time) lowest_time = random_time;
if (lowest_time < cur) {
lowest_time = cur;
}
return lowest_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;
}
}