kettek2/wiki/games/newsboy/Newsboy_0x00/engine/State_Animator.c

652 lines
21 KiB
C

#include "State_Animator.h"
#include "state.h"
#include "report.h"
#include "globals.h"
#include "render.h"
#include "AnimData.h"
#include "string.h"
#include "fifo.h"
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
/*
+-------------------------------------------+
|List | anim |list|list |
| of | box |of |of |
|Anims |-------+sets+faces |
| |list |tags |
| |of |for |
| |frames |frames |
| | | |
|init|rebuild|delete| | |del|insert|apply| |
+-------------------------------------------+
*/
int openAnimatorState() {
anim_window = newBox(16, 16, g_v_width-32, g_v_height-32, 0);
// left-hand
anim_list = newList(ui_colors);
anim_list->box.x = anim_window->x;
anim_list->box.y = anim_window->y;
anim_list->box.w = anim_window->w/2;
anim_list->box.h = anim_window->h;
anim_init = newButton(&g_medium_glyphs, button_colors, "init");
anim_init->box.x = anim_window->x + 8;
anim_init->box.y = anim_list->box.h - 8;
anim_buttons[0] = anim_init;
anim_rebuild = newButton(&g_medium_glyphs, button_colors, "rebuild");
anim_rebuild->box.x = anim_init->box.x + anim_init->box.w + 8;
anim_rebuild->box.y = anim_list->box.h - 8;
anim_buttons[1] = anim_rebuild;
anim_delete = newButton(&g_medium_glyphs, button_colors, "delete");
anim_delete->box.x = anim_rebuild->box.x + anim_rebuild->box.w + 8;
anim_delete->box.y = anim_list->box.h - 8;
anim_buttons[2] = anim_delete;
//
int offset_x = anim_window->x + anim_window->w/2;
int offset_y = anim_window->y;
// right-hand
anim_box = newBox(offset_x, offset_y, 128, 128, 0);
// animation save, play, and stop controls
anim_play = newButton(&g_medium_glyphs, button_colors, "play");
anim_play->box.x = offset_x + anim_box->w+2;
anim_play->box.y = offset_y;
anim_buttons[3] = anim_play;
offset_y += anim_play->box.h+2;
anim_stop = newButton(&g_medium_glyphs, button_colors, "stop");
anim_stop->box.x = offset_x + anim_box->w+2;
anim_stop->box.y = offset_y;
anim_buttons[4] = anim_stop;
offset_y += anim_stop->box.h+12;
anim_save = newButton(&g_medium_glyphs, button_colors, "save");
anim_save->box.x = offset_x + anim_box->w;
anim_save->box.y = offset_y;
anim_buttons[5] = anim_save;
offset_y = anim_box->h;
//offset_x += anim_box->w + 2;
anim_sets = newList(button_colors);
anim_sets->box.x = offset_x;
anim_sets->box.y = offset_y;
anim_sets->box.w = ((anim_window->w/2) - anim_box->w)/4;
anim_sets->box.h = anim_box->h;
offset_x += anim_sets->box.w + 2;
anim_faces = newList(ui_colors);
anim_faces->box.x = offset_x;
anim_faces->box.y = offset_y;
anim_faces->box.w = anim_sets->box.w;
anim_faces->box.h = anim_box->h;
offset_x += anim_faces->box.w + 2;
anim_frames_box = newBox(offset_x, offset_y, anim_sets->box.w, anim_window->h-anim_box->h, 0);
anim_frames = malloc(1);
anim_frames_count = 0;
anim_frames_offset = 0;
offset_x += anim_frames_box->w;
anim_frames_i_box = newBox(offset_x, offset_y, anim_sets->box.w, anim_frames_box->h, 0);
anim_frames_input = malloc(1);
// NULL out pointers
anim_button = NULL;
anim_list_item = NULL;
anim_sets_item = NULL;
anim_faces_item = NULL;
anim_input = NULL;
anim_input_i = -1;
l_anim_data = newAnimData();
// build a list of *.nba files found in the anim dir
buildAnimList();
return 0;
}
int closeAnimatorState() {
freeBox(anim_window);
// left-hand
freeList(anim_list);
// right-hand
freeBox(anim_box);
freeList(anim_sets);
freeList(anim_faces);
// frames list
freeBox(anim_frames_box);
freeBox(anim_frames_i_box);
int i;
for (i = 0; i < anim_frames_count; i++) {
freeTextt(anim_frames[i]);
freeTextInput(anim_frames_input[i]);
}
free(anim_frames);
free(anim_frames_input);
// buttons
for (i = 0; i < ANIM_BUTTONS; i++) {
freeButton(anim_buttons[i]);
}
// free loaded anim
freeAnimData(l_anim_data);
return 0;
}
int handleAnimatorState(SDL_Event event) {
int i;
switch(event.type) {
case SDL_MOUSEBUTTONDOWN:
if (anim_input != NULL) {
anim_input->flags &= ~UI_ACTIVE;
anim_input = NULL;
SDL_StopTextInput();
syncFrames();
}
// check against buttons
for (i = 0; i < ANIM_BUTTONS; i++) {
if (inBox(anim_buttons[i]->box, event.motion.x, event.motion.y)) {
anim_button = anim_buttons[i];
anim_button->flags |= UI_ACTIVE;
}
}
// check against list items
for (i = 0; i < anim_list->count; i++) {
if (inBox(getListItemBoxAbs(anim_list, i), event.motion.x, event.motion.y)) {
if (anim_list_item != NULL) {
if (anim_list_item != anim_list->items[i]) {
// deselect if not same as before
anim_list_item->flags &= ~UI_ACTIVE;
} else {
// if same item, ignore event
break;
}
}
anim_list_item = anim_list->items[i];
anim_list_item->flags |= UI_ACTIVE;
// build item filename
char *anim_filename = malloc(1);
anim_filename[0] = '\0';
anim_filename = catStringF(anim_filename, "%s%s", ANIM_DIR, anim_list->items[i]->text);
// free old AnimData
freeAnimData(l_anim_data);
l_anim_data = newAnimData();
anim_frames_offset = 0;
// load in new AnimData
if (loadAnimData(l_anim_data, anim_filename) != 0) {
report(ERROR, "handleAnimatorState", "could not load AnimData from %s", anim_filename);
}
updateAnim();
// free filename
free(anim_filename);
}
}
// check against set items
if (inBox(anim_sets->box, event.motion.x, event.motion.y)) {
for (i = 0; i < anim_sets->count; i++) {
if (inBox(getListItemBoxAbs(anim_sets, i), event.motion.x, event.motion.y)) {
// unselect last item
if (anim_sets_item != NULL) {
anim_sets_item->flags &= ~UI_ACTIVE;
}
// select new set item
anim_sets_item = anim_sets->items[i];
anim_sets_item->flags |= UI_ACTIVE;
set_index = i;
refreshData();
updateFaces();
}
}
}
// check against face items
if (inBox(anim_faces->box, event.motion.x, event.motion.y)) {
for (i = 0; i < anim_faces->count; i++) {
if (inBox(getListItemBoxAbs(anim_faces, i), event.motion.x, event.motion.y)) {
if (anim_faces_item != NULL) {
anim_faces_item->flags &= ~UI_ACTIVE;
}
// select new face item
anim_faces_item = anim_faces->items[i];
anim_faces_item->flags |= UI_ACTIVE;
face_index = i;
refreshData();
updateFrames();
}
}
}
// GUH, check against frame tag input items
if (inBox(*anim_frames_i_box, event.motion.x, event.motion.y)) {
for (i = 0; i < anim_frames_count; i++) {
if (inBox(anim_frames_input[i]->box, event.motion.x, event.motion.y)) {
anim_input_i = i;
anim_input = anim_frames_input[i];
anim_input->flags |= UI_ACTIVE;
SDL_Rect rect = { anim_input->box.x, anim_input->box.y, anim_input->box.w, anim_input->box.h };
SDL_SetTextInputRect(&rect);
SDL_StartTextInput();
}
}
}
break;
case SDL_MOUSEBUTTONUP:
if (anim_input != NULL) {
// unfocus input if mouseup is not within its bounds
if (!inBox(anim_input->box, event.motion.x, event.motion.y)) {
anim_input->flags &= ~UI_ACTIVE;
anim_input = NULL;
syncFrames();
}
}
if (anim_button != NULL) {
if (inBox(anim_button->box, event.motion.x, event.motion.y)) {
if (anim_button == anim_init) {
initializeAnims();
buildAnimList();
} else if (anim_button == anim_rebuild) {
} else if (anim_button == anim_delete) {
if (anim_list_item != NULL) {
// create filename
char *anim_filename = malloc(1);
anim_filename[0] = '\0';
anim_filename = catStringF(anim_filename, "%s%s", ANIM_DIR, anim_list_item->text);
// delete file!
deleteFile(anim_filename);
// remove item from list and free it
remListItem(anim_list, anim_list_item);
freeButton(anim_list_item);
anim_list_item = NULL;
// free filename
// BUG: this segfaults on windows... why?
//if (anim_filename != NULL) free(anim_filename);
}
} else if (anim_button == anim_save) {
if (l_anim_data != NULL) {
saveAnimData(l_anim_data);
}
}
}
anim_button->flags &= ~UI_ACTIVE;
anim_button = NULL;
}
break;
case SDL_MOUSEMOTION:
mouse_x = event.motion.x;
mouse_y = event.motion.y;
break;
case SDL_MOUSEWHEEL:
if (inBox(*anim_frames_box, mouse_x, mouse_y)) {
if (event.wheel.y < 0) {
syncFrames();
anim_frames_offset++;
} else if (event.wheel.y > 0) {
if (anim_frames_offset > 0) {
syncFrames();
anim_frames_offset--;
}
}
updateFrames();
}
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) {
popState(g_state_manager);
}
if (anim_input != NULL) {
switch(event.key.keysym.sym) {
// otherwise, control our input and stop the event
case SDLK_LEFT:
if (anim_input->cursor > 0) {
anim_input->cursor--;
}
break;
case SDLK_RIGHT:
if (anim_input->cursor < strlen(anim_input->text)) {
anim_input->cursor++;
}
break;
case SDLK_BACKSPACE:
if (anim_input->cursor != 0) {
deleteTextInput(anim_input, anim_input->cursor-1, 1);
anim_input->cursor--;
}
break;
}
}
break;
case SDL_TEXTINPUT:
if (anim_input != NULL) {
insertTextInput(anim_input, event.text.text);
}
break;
}
return 1;
}
int processAnimatorState() {
return 1;
}
int renderAnimatorState() {
renderSQuad(anim_window->x, anim_window->y, anim_window->w, anim_window->h, ui_colors->bg);
renderList(anim_list);
SDL_Color image_bg = { 0, 0, 0};
renderSQuad(anim_box->x, anim_box->y, anim_box->w, anim_box->h, image_bg);
renderList(anim_sets);
renderList(anim_faces);
int i;
// render frames
for (i = 0; i < anim_frames_count; i++) {
renderTextt(anim_frames[i]);
renderTextInput(anim_frames_input[i]);
}
// render buttons
for (i = 0; i < ANIM_BUTTONS; i++) {
renderButton(anim_buttons[i]);
}
return 1;
}
// ===
int refreshData() {
if (l_anim_data != NULL) {
if (set_index < 0 || set_index > l_anim_data->count-1) {
l_set_data = NULL;
} else {
l_set_data = l_anim_data->sets[set_index];
}
}
if (l_set_data != NULL) {
if (face_index < 0 || face_index > l_set_data->count-1) {
l_face_data = NULL;
} else {
l_face_data = l_set_data->faces[face_index];
}
}
return 0;
}
// === update Animation editor view
int updateAnim() {
refreshData();
freeListItems(anim_sets);
freeListItems(anim_faces);
if (l_anim_data != NULL) {
set_index = 0;
updateSets();
}
return 0;
}
int updateSets() {
freeListItems(anim_sets);
if (l_anim_data != NULL) {
struct SetData *set_data = NULL;
int set_i;
for(set_i = 0; set_i < l_anim_data->count; set_i++) {
struct Ui_Colors *colors;
if (set_i%2 == 1) {
colors = ui_colors;
} else {
colors = button_colors;
}
set_data = l_anim_data->sets[set_i];
struct Button *set_item = newButton(&g_small_glyphs, colors, set_data->name);
addListItem(anim_sets, set_item);
}
// if set_data is not NULL, that means we have at least 1 item
if (set_data != NULL) {
anim_sets_item = anim_sets->items[0];
anim_sets_item->flags |= UI_ACTIVE;
}
face_index = 0;
updateFaces();
}
return 0;
}
int updateFaces() {
freeListItems(anim_faces);
if (l_set_data != NULL) {
struct FaceData *face_data;
int face_i;
for(face_i = 0; face_i < l_set_data->count; face_i++) {
struct Ui_Colors *colors;
if (face_i%2 == 1) {
colors = ui_colors;
} else {
colors = button_colors;
}
face_data = l_set_data->faces[face_i];
struct Button *face_item = newButton(&g_small_glyphs, colors, face_data->name);
addListItem(anim_faces, face_item);
}
// if face_data is not NULL, we have at least 1 item
if (face_data != NULL) {
anim_faces_item = anim_faces->items[0];
anim_faces_item->flags |= UI_ACTIVE;
}
frame_index = 0;
updateFrames();
}
return 0;
}
int syncFrames() {
if (l_face_data != NULL) {
if (anim_input_i >= 0 && anim_input_i < anim_frames_count) {
l_face_data->frames[anim_frames_offset+anim_input_i]->tag = copyString(l_face_data->frames[anim_frames_offset+anim_input_i]->tag, anim_frames_input[anim_input_i]->text);
anim_input_i = -1;
}
}
if (anim_input != NULL) {
anim_input->flags &= ~UI_ACTIVE;
anim_input = NULL;
}
return 0;
}
int updateFrames() {
if (l_face_data != NULL) {
syncFrames();
// free old frames
int i;
for (i = 0; i < anim_frames_count; i++) {
freeTextt(anim_frames[i]);
freeTextInput(anim_frames_input[i]);
}
anim_frames = realloc(anim_frames, 1);
anim_frames_input = realloc(anim_frames_input, 1);
anim_frames_count = 0;
// get new frames
struct FrameData *frame_data;
int offset_x = anim_frames_box->x;
int offset_y = anim_frames_box->y;
int item_limit = anim_frames_box->h / g_small_glyphs.h;
int item_count = 0;
int frame_i;
for(frame_i = anim_frames_offset; frame_i < l_face_data->count; frame_i++) {
if (item_count >= item_limit) break;
struct Ui_Colors *colors;
if (frame_i%2 == 1) {
colors = ui_colors;
} else {
colors = button_colors;
}
frame_data = l_face_data->frames[frame_i];
// create Text
struct Textt *frame_item = newTextt(&g_small_glyphs, colors, frame_data->file);
frame_item->box.x = offset_x;
frame_item->box.y = offset_y;
frame_item->box.w = anim_frames_box->w;
// create TextInput
// use inverse colors
if (frame_i%2 == 0) {
colors = ui_colors;
} else {
colors = button_colors;
}
struct TextInput *frame_input = newTextInput(&g_small_glyphs, colors, anim_frames_i_box->w);
setTextInputText(frame_input, frame_data->tag);
frame_input->box.x = anim_frames_i_box->x;
frame_input->box.y = offset_y;
anim_frames_count++;
// resize anim_frames
anim_frames = realloc(anim_frames, anim_frames_count*(sizeof(struct Textt*)));
anim_frames[anim_frames_count-1] = frame_item;
// now resize input
anim_frames_input = realloc(anim_frames_input, anim_frames_count*(sizeof(struct Textt*)));
anim_frames_input[anim_frames_count-1] = frame_input;
//
offset_y += frame_item->box.h;
item_count++;
}
if (frame_index < 0 || face_index > l_face_data->count-1) {
l_frame_data = NULL;
} else {
l_frame_data = l_face_data->frames[frame_index];
}
}
return 0;
}
// === AnimData initialization and list building
int initializeAnims() {
struct Dir *anim_dir = openDir(ANIM_DIR, SORT_DESCEND);
if (anim_dir == NULL) return 1;
// set up our containing array
struct AnimArray *anim_array = newAnimArray();
struct DirEntry *anim_entry;
while((anim_entry = readDir(anim_dir)) != NULL) {
// skip regular files
if (anim_entry->d_type != F_DIR) continue;
// skip "."/".."
if (strcmp(anim_entry->d_name, ".") == 0 || strcmp(anim_entry->d_name, "..") == 0) continue;
int anim_entry_size = strlen(anim_entry->d_name)+1;
// create full file path to anim_entry
int anim_path_size = strlen(ANIM_DIR)+anim_entry_size;
char anim_path[anim_path_size];
strcpy(anim_path, ANIM_DIR);
memcpy(anim_path+strlen(ANIM_DIR), anim_entry->d_name, anim_entry_size);
// skip anim_entry if a ".anim" file exists for it already
char anim_file[anim_path_size+strlen(ANIM_EXT)];
memcpy(anim_file, anim_path, anim_path_size);
memcpy(anim_file+anim_path_size-1, ANIM_EXT, strlen(ANIM_EXT)+1);
anim_file[anim_path_size+strlen(ANIM_EXT)] = '\0';
if (fileExists(anim_file)) continue;
// okay, seems this is valid, let's create our AnimData
struct AnimData *anim_data = newAnimData();
anim_data->name = copyString(anim_data->name, anim_entry->d_name);
// BEGIN anim traversal
struct Dir *set_dir = openDir(anim_path, SORT_DESCEND);
if (set_dir == NULL) break;
struct DirEntry *set_entry;
while((set_entry = readDir(set_dir)) != NULL) {
// skip regular files
if (set_entry->d_type != F_DIR) continue;
// skip "."/".."
if (strcmp(set_entry->d_name, ".") == 0 || strcmp(set_entry->d_name, "..") == 0) continue;
// create full file path to set_entry
int set_entry_size = strlen(set_entry->d_name)+1;
char set_path[strlen(anim_path)+set_entry_size+1];
strcpy(set_path, anim_path);
set_path[strlen(anim_path)] = '/';
memcpy(set_path+strlen(anim_path)+1, set_entry->d_name, set_entry_size);
// valid so far, create SetData
struct SetData *set_data = newSetData();
set_data->fps = 30; // default 60
set_data->name = copyString(set_data->name, set_entry->d_name);
// BEGIN set traversel
struct Dir *face_dir = openDir(set_path, SORT_DESCEND);
struct DirEntry *face_entry = NULL;
while((face_entry = readDir(face_dir)) != NULL) {
// skip regular files
if (face_entry->d_type != F_DIR) continue;
// skip "."/".."
if (strcmp(face_entry->d_name, ".") == 0 || strcmp(face_entry->d_name, "..") == 0) continue;
// create full file path to face_entry
int face_entry_size = strlen(face_entry->d_name)+1;
char face_path[strlen(set_path)+face_entry_size+1];
strcpy(face_path, set_path);
face_path[strlen(set_path)] = '/';
memcpy(face_path+strlen(set_path)+1, face_entry->d_name, face_entry_size);
// ohhh, it's getting closer, create FaceData
struct FaceData *face_data = newFaceData();
face_data->name = copyString(face_data->name, face_entry->d_name);
// BEGIN face traversal
struct Dir *frame_dir = openDir(face_path, SORT_DESCEND);
if (frame_dir == NULL) continue;
struct DirEntry *frame_entry;
while((frame_entry = readDir(frame_dir)) != NULL) {
// skip regular files
if (frame_entry->d_type != F_REG) continue;
// skip "."/".."
if (strcmp(frame_entry->d_name, ".") == 0 || strcmp(frame_entry->d_name, "..") == 0) continue;
// create full file path to frame_entry
int frame_entry_size = strlen(frame_entry->d_name)+1;
char frame_path[strlen(face_path)+frame_entry_size+1];
strcpy(frame_path, face_path);
frame_path[strlen(face_path)] = '/';
memcpy(frame_path+strlen(face_path)+1, frame_entry->d_name, frame_entry_size);
// hot dogs, we've reached our conclusion
struct FrameData *frame_data = newFrameData();
frame_data->file = copyString(frame_data->file, frame_entry->d_name);
// push onto our face :)
pushFrameData(face_data, frame_data);
}
// push onto our set!
closeDir(frame_dir);
pushFaceData(set_data, face_data);
}
// push onto anim data! :D
closeDir(face_dir);
pushSetData(anim_data, set_data);
}
closeDir(set_dir);
pushAnimData(anim_array, anim_data);
}
closeDir(anim_dir);
// save our AnimData!
int anim_i;
for(anim_i = 0; anim_i < anim_array->count; anim_i++) {
saveAnimData(anim_array->anims[anim_i]);
}
// free
freeAnimArray(anim_array);
return 0;
}
int buildAnimList() {
int i = 0;
struct dirent *entry;
DIR *dir = opendir(ANIM_DIR);
if (dir == NULL) {
return 1;
}
freeListItems(anim_list);
while ((entry = readdir(dir)) != NULL) {
// find extension (reverse search for first '.')
char *extension;
if ((extension = strrchr(entry->d_name, '.')) == NULL) {
continue;
}
if (strcmp(extension, ANIM_EXT) != 0) {
continue;
}
int entry_size = strlen(entry->d_name)+1;
// :S
char file_path[strlen(MAP_DIR)+entry_size];
strcpy(file_path, MAP_DIR);
memcpy(file_path+strlen(MAP_DIR), entry->d_name, entry_size);
//
struct stat file_stat;
stat(file_path, &file_stat);
// ignore directories
switch(file_stat.st_mode & S_IFMT) {
case S_IFDIR:
continue;
break;
}
// TODO: do something with file_path ?
struct Ui_Colors *colors;
if (i%2 == 1) {
colors = ui_colors;
} else {
colors = button_colors;
}
struct Button *anim_item = newButton(&g_small_glyphs, colors, entry->d_name);
addListItem(anim_list, anim_item);
i++;
}
closedir(dir);
return 0;
}