389 lines
11 KiB
C++
389 lines
11 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) {
|
|
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;
|
|
}
|
|
}
|