#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 #include #include #include /* +-------------------------------------------+ |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; }