large refactoring WIP
windows & osx untested bgm untested too many changes to comment on
This commit is contained in:
parent
25f856a3bc
commit
ca16c30966
44 changed files with 1571 additions and 1339 deletions
|
@ -17,7 +17,6 @@ pkg_check_modules(PNG libpng REQUIRED)
|
|||
|
||||
if(NOT NO_AUDIO)
|
||||
pkg_check_modules(SDL2_MIXER SDL2_mixer REQUIRED)
|
||||
add_definitions(-DHAVE_MIXER)
|
||||
endif()
|
||||
|
||||
set(SRCs
|
||||
|
@ -50,6 +49,7 @@ set(SRCs
|
|||
transition.c
|
||||
color.c
|
||||
difficulty.c
|
||||
audio_common.c
|
||||
menu/menu.c
|
||||
menu/mainmenu.c
|
||||
menu/options.c
|
||||
|
@ -96,9 +96,17 @@ else()
|
|||
endif()
|
||||
|
||||
if(NO_AUDIO)
|
||||
set(SRCs ${SRCs} resource/audio_null.c resource/bgm_null.c)
|
||||
set(SRCs ${SRCs}
|
||||
audio_null.c
|
||||
resource/bgm_null.c
|
||||
resource/sfx_null.c
|
||||
)
|
||||
else()
|
||||
set(SRCs ${SRCs} resource/audio.c resource/bgm.c)
|
||||
set(SRCs ${SRCs}
|
||||
audio_mixer.c
|
||||
resource/bgm_mixer.c
|
||||
resource/sfx_mixer.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
@ -126,7 +134,6 @@ endif()
|
|||
set(LIBs ${LIBs}
|
||||
${SDL2_LIBRARIES}
|
||||
${SDL2_TTF_LIBRARIES}
|
||||
${SDL2_MIXER_LIBRARIES}
|
||||
${PNG_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
|
@ -152,13 +159,19 @@ endif()
|
|||
set(INCs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
${SDL2_MIXER_INCLUDE_DIRS}
|
||||
${SDL2_TTF_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${PNG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# stupid cmake caching SHIT
|
||||
if(NOT NO_AUDIO)
|
||||
set(INCs ${INCs} ${SDL2_MIXER_INCLUDE_DIRS})
|
||||
set(LIBs ${LIBs} ${SDL2_MIXER_LIBRARIES})
|
||||
set(LIBDIRs ${LIBDIRs} ${SDL2_MIXER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES ${INCs})
|
||||
include_directories(${INCs})
|
||||
|
||||
|
|
53
src/audio.h
Normal file
53
src/audio.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include "resource/sfx.h"
|
||||
#include "resource/bgm.h"
|
||||
|
||||
typedef struct CurrentBGM {
|
||||
char *name;
|
||||
char *title;
|
||||
int isboss;
|
||||
int started_at;
|
||||
Music *music;
|
||||
} CurrentBGM;
|
||||
|
||||
extern CurrentBGM current_bgm;
|
||||
|
||||
void audio_backend_init(void);
|
||||
void audio_backend_shutdown(void);
|
||||
bool audio_backend_initialized(void);
|
||||
void audio_backend_set_sfx_volume(float gain);
|
||||
void audio_backend_set_bgm_volume(float gain);
|
||||
bool audio_backend_music_is_paused(void);
|
||||
bool audio_backend_music_is_playing(void);
|
||||
void audio_backend_music_resume(void);
|
||||
void audio_backend_music_stop(void);
|
||||
void audio_backend_music_pause(void);
|
||||
bool audio_backend_music_play(void *impl);
|
||||
bool audio_backend_sound_play(void *impl);
|
||||
|
||||
void audio_init(void);
|
||||
void audio_shutdown(void);
|
||||
|
||||
void play_sound(const char *name);
|
||||
void play_ui_sound(const char *name);
|
||||
void reset_sounds(void);
|
||||
|
||||
Sound* get_sound(const char *name);
|
||||
Music* get_music(const char *music);
|
||||
|
||||
void start_bgm(const char *name);
|
||||
void stop_bgm(void);
|
||||
void resume_bgm(void);
|
||||
void save_bgm(void);
|
||||
void restore_bgm(void);
|
||||
|
||||
#endif
|
193
src/audio_common.c
Normal file
193
src/audio_common.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
|
||||
* Copyright (C) 2012, Alexeyew Andrew <http://akari.thebadasschoobs.org/>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "resource/resource.h"
|
||||
#include "global.h"
|
||||
|
||||
static char *saved_bgm;
|
||||
static Hashtable *bgm_descriptions;
|
||||
CurrentBGM current_bgm = { .name = NULL };
|
||||
|
||||
static void play_sound_internal(const char *name, int unconditional) {
|
||||
if(!audio_backend_initialized() || global.frameskip) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sound *snd = get_sound(name);
|
||||
|
||||
if(!snd || (!unconditional && snd->lastplayframe == global.frames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
snd->lastplayframe = global.frames;
|
||||
audio_backend_sound_play(snd->impl);
|
||||
}
|
||||
|
||||
void play_sound(const char *name) {
|
||||
play_sound_internal(name, false);
|
||||
}
|
||||
|
||||
void play_ui_sound(const char *name) {
|
||||
play_sound_internal(name, true);
|
||||
}
|
||||
|
||||
void reset_sounds(void) {
|
||||
Resource *snd;
|
||||
for(HashtableIterator *i = hashtable_iter(resources.handlers[RES_SFX].mapping);
|
||||
hashtable_iter_next(i, NULL, (void**)&snd);) {
|
||||
snd->sound->lastplayframe = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Sound* get_sound(const char *name) {
|
||||
Resource *res = get_resource(RES_SFX, name, 0);
|
||||
return res ? res->sound : NULL;
|
||||
}
|
||||
|
||||
Music* get_music(const char *name) {
|
||||
Resource *res = get_resource(RES_BGM, name, 0);
|
||||
return res ? res->music : NULL;
|
||||
}
|
||||
|
||||
static void sfx_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
|
||||
audio_backend_set_sfx_volume(config_set_float(idx, v.f));
|
||||
}
|
||||
|
||||
static void bgm_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
|
||||
audio_backend_set_bgm_volume(config_set_float(idx, v.f));
|
||||
}
|
||||
|
||||
static void load_bgm_descriptions(void) {
|
||||
char *fullname = strjoin(get_prefix(), "bgm/bgm.conf", NULL);
|
||||
FILE *fp = fopen(fullname, "rt");
|
||||
free(fullname);
|
||||
|
||||
bgm_descriptions = hashtable_new_stringkeys(16);
|
||||
|
||||
if(fp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
while(fgets(line, sizeof(line), fp)) {
|
||||
char *rem;
|
||||
|
||||
while((rem = strchr(line,'\n')) != NULL) *rem = '\0';
|
||||
while((rem = strchr(line,'\r')) != NULL) *rem = '\0';
|
||||
while((rem = strchr(line,'\t')) != NULL) *rem = ' ';
|
||||
|
||||
if((rem = strchr(line,' ' )) == NULL) {
|
||||
if(strlen(line) > 0)
|
||||
warnx("load_bgm_description(): illegal string format. See README.");
|
||||
continue;
|
||||
}
|
||||
|
||||
*(rem++)='\0';
|
||||
|
||||
char *value = strjoin("BGM: ", rem, NULL);
|
||||
hashtable_set_string(bgm_descriptions, line, value);
|
||||
printf("Music %s is now known as \"%s\".\n", line, value);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline char* get_bgm_desc(char *name) {
|
||||
return (char*)hashtable_get_string(bgm_descriptions, name);
|
||||
}
|
||||
|
||||
void resume_bgm(void) {
|
||||
start_bgm(current_bgm.name); // In most cases it just unpauses existing music.
|
||||
}
|
||||
|
||||
void stop_bgm(void) {
|
||||
if(!current_bgm.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(audio_backend_music_is_playing() && !audio_backend_music_is_paused()) {
|
||||
audio_backend_music_pause(); // Pause, not halt - to be unpaused in continue_bgm() if needed.
|
||||
printf("BGM stopped.\n");
|
||||
} else {
|
||||
printf("stop_bgm(): No BGM was playing.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void save_bgm(void) {
|
||||
// Deal with consequent saves without restore.
|
||||
stralloc(&saved_bgm, current_bgm.name);
|
||||
}
|
||||
|
||||
void restore_bgm(void) {
|
||||
start_bgm(saved_bgm);
|
||||
free(saved_bgm);
|
||||
saved_bgm = NULL;
|
||||
}
|
||||
|
||||
void start_bgm(const char *name) {
|
||||
if(!name || !*name) {
|
||||
stop_bgm();
|
||||
return;
|
||||
}
|
||||
|
||||
// if BGM has changed, change it and start from beginning
|
||||
if(!current_bgm.name || strcmp(name, current_bgm.name)) {
|
||||
audio_backend_music_stop();
|
||||
|
||||
stralloc(¤t_bgm.name, name);
|
||||
|
||||
if((current_bgm.music = get_music(name)) == NULL) {
|
||||
warnx("start_bgm(): BGM '%s' does not not exist", current_bgm.name);
|
||||
stop_bgm();
|
||||
free(current_bgm.name);
|
||||
current_bgm.name = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(audio_backend_music_is_paused()) {
|
||||
audio_backend_music_resume();
|
||||
}
|
||||
|
||||
if(audio_backend_music_is_playing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!audio_backend_music_play(current_bgm.music->impl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Support drawing BGM title in game loop (only when music changed!)
|
||||
if((current_bgm.title = get_bgm_desc(current_bgm.name)) != NULL) {
|
||||
current_bgm.started_at = global.frames;
|
||||
// Boss BGM title color may differ from the one at beginning of stage
|
||||
current_bgm.isboss = strendswith(current_bgm.name, "boss");
|
||||
} else {
|
||||
current_bgm.started_at = -1;
|
||||
}
|
||||
|
||||
printf("Started %s\n", (current_bgm.title ? current_bgm.title : current_bgm.name));
|
||||
}
|
||||
|
||||
void audio_init(void) {
|
||||
audio_backend_init();
|
||||
load_bgm_descriptions();
|
||||
config_set_callback(CONFIG_SFX_VOLUME, sfx_cfg_volume_callback);
|
||||
config_set_callback(CONFIG_BGM_VOLUME, bgm_cfg_volume_callback);
|
||||
}
|
||||
|
||||
void audio_shutdown(void) {
|
||||
audio_backend_shutdown();
|
||||
hashtable_foreach(bgm_descriptions, hashtable_iter_free_data, NULL);
|
||||
hashtable_free(bgm_descriptions);
|
||||
}
|
173
src/audio_mixer.c
Normal file
173
src/audio_mixer.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
|
||||
*/
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "global.h"
|
||||
#include "list.h"
|
||||
#include "taisei_err.h"
|
||||
|
||||
#define AUDIO_FREQ 44100
|
||||
#define AUDIO_FORMAT MIX_DEFAULT_FORMAT
|
||||
#define AUDIO_CHANNELS 100
|
||||
|
||||
static bool mixer_loaded = false;
|
||||
|
||||
const char *mixer_audio_exts[] = { ".wav", ".ogg", ".mp3", ".mod", ".xm", ".s3m",
|
||||
".669", ".it", ".med", ".mid", ".flac", ".aiff", ".voc",
|
||||
NULL };
|
||||
|
||||
void audio_backend_init(void) {
|
||||
if(mixer_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
warnx("audio_backend_init(): SDL_InitSubSystem() failed: %s.\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Mix_Init(MIX_INIT_OGG | MIX_INIT_FLAC | MIX_INIT_MOD | MIX_INIT_MP3)) {
|
||||
Mix_Quit(); // Try to shutdown mixer if it was partly initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if(Mix_OpenAudio(AUDIO_FREQ, AUDIO_FORMAT, 2, config_get_int(CONFIG_MIXER_CHUNKSIZE)) == -1) {
|
||||
warnx("audio_backend_init(): Mix_OpenAudio() failed: %s.\n", Mix_GetError());
|
||||
Mix_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
int channels = Mix_AllocateChannels(AUDIO_CHANNELS);
|
||||
if(!channels) {
|
||||
warnx("audio_backend_init(): unable to allocate any channels.\n");
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if(channels < AUDIO_CHANNELS) {
|
||||
warnx("audio_backend_init(): allocated only %d of %d channels.\n", channels, AUDIO_CHANNELS);
|
||||
}
|
||||
|
||||
mixer_loaded = true;
|
||||
|
||||
audio_backend_set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
|
||||
audio_backend_set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
|
||||
|
||||
int frequency = 0;
|
||||
uint16_t format = 0;
|
||||
|
||||
Mix_QuerySpec(&frequency, &format, &channels);
|
||||
|
||||
if(frequency != AUDIO_FREQ || format != AUDIO_FORMAT) {
|
||||
warnx( "audio_backend_init(): mixer spec doesn't match our request, "
|
||||
"requested (freq=%i, fmt=%u), got (freq=%i, fmt=%u). "
|
||||
"Sound may be distorted.",
|
||||
AUDIO_FREQ, AUDIO_FORMAT, frequency, format);
|
||||
}
|
||||
|
||||
printf("audio_backend_init(): audio subsystem initialized (SDL2_Mixer)\n");
|
||||
}
|
||||
|
||||
void audio_backend_shutdown(void) {
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
mixer_loaded = false;
|
||||
|
||||
printf("audio_backend_shutdown(): audio subsystem uninitialized (SDL2_Mixer)\n");
|
||||
}
|
||||
|
||||
bool audio_backend_initialized(void) {
|
||||
return mixer_loaded;
|
||||
}
|
||||
|
||||
void audio_backend_set_sfx_volume(float gain) {
|
||||
if(mixer_loaded)
|
||||
Mix_Volume(-1, gain * MIX_MAX_VOLUME);
|
||||
}
|
||||
|
||||
void audio_backend_set_bgm_volume(float gain) {
|
||||
if(mixer_loaded)
|
||||
Mix_VolumeMusic(gain * MIX_MAX_VOLUME);
|
||||
}
|
||||
|
||||
char* audio_mixer_sound_path(const char *name) {
|
||||
for(const char **ext = mixer_audio_exts; *ext; ++ext) {
|
||||
char *p = strjoin(get_prefix(), SFX_PATH_PREFIX, name, *ext, NULL);
|
||||
struct stat statbuf;
|
||||
|
||||
if(!stat(p, &statbuf)) {
|
||||
return p;
|
||||
}
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool audio_mixer_check_sound_path(const char *path, bool isbgm) {
|
||||
if(strstartswith(resource_util_filename(path), "bgm_") == isbgm) {
|
||||
return strendswith_any(path, mixer_audio_exts);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void audio_backend_music_stop(void) {
|
||||
if(mixer_loaded)
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
bool audio_backend_music_is_paused(void) {
|
||||
return mixer_loaded && Mix_PausedMusic();
|
||||
}
|
||||
|
||||
bool audio_backend_music_is_playing(void) {
|
||||
return mixer_loaded && Mix_PlayingMusic();
|
||||
}
|
||||
|
||||
void audio_backend_music_resume(void) {
|
||||
if(mixer_loaded)
|
||||
Mix_ResumeMusic();
|
||||
}
|
||||
|
||||
void audio_backend_music_pause(void) {
|
||||
if(mixer_loaded)
|
||||
Mix_PauseMusic();
|
||||
}
|
||||
|
||||
bool audio_backend_music_play(void *impl) {
|
||||
if(!mixer_loaded)
|
||||
return false;
|
||||
|
||||
bool result = (Mix_PlayMusic((Mix_Music*)impl, -1) != -1);
|
||||
|
||||
if(!result) {
|
||||
warnx("audio_backend_music_play(): Mix_PlayMusic() failed: %s", Mix_GetError());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool audio_backend_sound_play(void *impl) {
|
||||
if(!mixer_loaded)
|
||||
return false;
|
||||
|
||||
bool result = (Mix_PlayChannel(-1, (Mix_Chunk*)impl, 0) != -1);
|
||||
|
||||
if(!result) {
|
||||
warnx("audio_backend_sound_play(): Mix_PlayChannel() failed: %s", Mix_GetError());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
15
src/audio_null.c
Normal file
15
src/audio_null.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
#include "audio.h"
|
||||
|
||||
void audio_backend_init(void) {}
|
||||
void audio_backend_shutdown(void) {}
|
||||
bool audio_backend_initialized(void) { return false; }
|
||||
void audio_backend_set_sfx_volume(float gain) {}
|
||||
void audio_backend_set_bgm_volume(float gain) {}
|
||||
bool audio_backend_music_is_paused(void) { return false; }
|
||||
bool audio_backend_music_is_playing(void) { return false; }
|
||||
void audio_backend_music_resume(void) { return; }
|
||||
void audio_backend_music_stop(void) {}
|
||||
void audio_backend_music_pause(void) {}
|
||||
bool audio_backend_music_play(void *impl) { return false; }
|
||||
bool audio_backend_sound_play(void *impl) { return false; }
|
|
@ -70,9 +70,6 @@
|
|||
CONFIGDEF_INT (VID_HEIGHT, "vid_height", RESY) \
|
||||
CONFIGDEF_INT (VID_RESIZABLE, "vid_resizable", 0) \
|
||||
CONFIGDEF_INT (VSYNC, "vsync", 2) \
|
||||
CONFIGDEF_INT (NO_SHADER, "disable_shader", 0) \
|
||||
CONFIGDEF_INT (NO_AUDIO, "disable_audio", 0) \
|
||||
CONFIGDEF_INT (NO_MUSIC, "disable_bgm", 0) \
|
||||
CONFIGDEF_INT (MIXER_CHUNKSIZE, "mixer_chunksize", 1024) \
|
||||
CONFIGDEF_FLOAT (SFX_VOLUME, "sfx_volume", 1.0) \
|
||||
CONFIGDEF_FLOAT (BGM_VOLUME, "bgm_volume", 1.0) \
|
||||
|
|
|
@ -96,11 +96,10 @@ void credits_towerwall_draw(Vector pos) {
|
|||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, get_tex("stage6/towerwall")->gltex);
|
||||
if(!config_get_int(CONFIG_NO_SHADER)) {
|
||||
Shader *s = get_shader("tower_wall");
|
||||
glUseProgram(s->prog);
|
||||
glUniform1i(uniloc(s, "lendiv"), 2800.0 + 300.0 * sin(global.frames / 77.7));
|
||||
}
|
||||
|
||||
Shader *s = get_shader("tower_wall");
|
||||
glUseProgram(s->prog);
|
||||
glUniform1i(uniloc(s, "lendiv"), 2800.0 + 300.0 * sin(global.frames / 77.7));
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(pos[0], pos[1], pos[2]);
|
||||
|
|
87
src/global.c
87
src/global.c
|
@ -243,6 +243,26 @@ bool strendswith(const char *s, const char *e) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool strstartswith(const char *s, const char *p) {
|
||||
int ls = strlen(s);
|
||||
int lp = strlen(p);
|
||||
|
||||
if(ls < lp)
|
||||
return false;
|
||||
|
||||
return !strncmp(s, p, lp);
|
||||
}
|
||||
|
||||
bool strendswith_any(const char *s, const char **earray) {
|
||||
for(const char **e = earray; *e; ++e) {
|
||||
if(strendswith(s, *e)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void stralloc(char **dest, const char *src) {
|
||||
free(*dest);
|
||||
|
||||
|
@ -254,6 +274,73 @@ void stralloc(char **dest, const char *src) {
|
|||
}
|
||||
}
|
||||
|
||||
char* strjoin(const char *first, ...) {
|
||||
va_list args;
|
||||
size_t size = strlen(first) + 1;
|
||||
char *str = malloc(size);
|
||||
|
||||
strcpy(str, first);
|
||||
va_start(args, first);
|
||||
|
||||
for(;;) {
|
||||
char *next = va_arg(args, char*);
|
||||
|
||||
if(!next) {
|
||||
break;
|
||||
}
|
||||
|
||||
size += strlen(next);
|
||||
str = realloc(str, size);
|
||||
strcat(str, next);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return str;
|
||||
}
|
||||
|
||||
char* read_all(const char *filename, int *outsize) {
|
||||
char *text;
|
||||
size_t size;
|
||||
|
||||
FILE *file = fopen(filename, "r");
|
||||
if(file == NULL)
|
||||
errx(-1, "Error opening '%s'", filename);
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
text = malloc(size+1);
|
||||
fread(text, size, 1, file);
|
||||
text[size] = 0;
|
||||
|
||||
fclose(file);
|
||||
|
||||
if(outsize) {
|
||||
*outsize = size;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
char* copy_segment(const char *text, const char *delim, int *size) {
|
||||
char *seg, *beg, *end;
|
||||
|
||||
beg = strstr(text, delim);
|
||||
if(!beg)
|
||||
return NULL;
|
||||
beg += strlen(delim);
|
||||
|
||||
end = strstr(beg, "%%");
|
||||
if(!end)
|
||||
return NULL;
|
||||
|
||||
*size = end-beg;
|
||||
seg = malloc(*size+1);
|
||||
strlcpy(seg, beg, *size+1);
|
||||
|
||||
return seg;
|
||||
}
|
||||
// Inputdevice-agnostic method of checking whether a game control is pressed.
|
||||
// ALWAYS use this instead of SDL_GetKeyState if you need it.
|
||||
bool gamekeypressed(KeyIndex key) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "tscomplex.h"
|
||||
#include "color.h"
|
||||
|
||||
#include "resource/audio.h"
|
||||
#include "resource/sfx.h"
|
||||
#include "resource/bgm.h"
|
||||
#include "resource/shader.h"
|
||||
#include "resource/font.h"
|
||||
|
@ -42,6 +42,7 @@
|
|||
#include "events.h"
|
||||
#include "difficulty.h"
|
||||
#include "color.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include "taisei_err.h"
|
||||
#include "rwops/all.h"
|
||||
|
@ -160,9 +161,14 @@ double clamp(double, double, double);
|
|||
double approach(double v, double t, double d);
|
||||
double psin(double);
|
||||
bool strendswith(const char *s, const char *e);
|
||||
bool strstartswith(const char *s, const char *p);
|
||||
bool strendswith_any(const char *s, const char **earray);
|
||||
void stralloc(char **dest, const char *src);
|
||||
char* strjoin(const char *first, ...);
|
||||
bool gamekeypressed(KeyIndex key);
|
||||
int getenvint(const char *v);
|
||||
char* read_all(const char *filename, int *size);
|
||||
char* copy_segment(const char *text, const char *delim, int *size);
|
||||
|
||||
#define SIGN(x) ((x > 0) - (x < 0))
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
|
@ -30,8 +29,15 @@ struct Hashtable {
|
|||
HTHashFunc hash_func;
|
||||
HTCopyFunc copy_func;
|
||||
HTFreeFunc free_func;
|
||||
ListContainer *deferred_unsets;
|
||||
};
|
||||
|
||||
typedef struct HashtableIterator {
|
||||
Hashtable *hashtable;
|
||||
size_t bucketnum;
|
||||
HashtableElement *elem;
|
||||
} HashtableIterator;
|
||||
|
||||
/*
|
||||
* Generic functions
|
||||
*/
|
||||
|
@ -46,6 +52,9 @@ Hashtable* hashtable_new(size_t size, HTCmpFunc cmp_func, HTHashFunc hash_func,
|
|||
ht->hash_func = hash_func;
|
||||
ht->copy_func = copy_func;
|
||||
ht->free_func = free_func;
|
||||
ht->deferred_unsets = NULL;
|
||||
|
||||
assert(ht->hash_func != NULL);
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
@ -62,6 +71,8 @@ static void hashtable_delete_callback(void **vlist, void *velem, void *vht) {
|
|||
}
|
||||
|
||||
void hashtable_unset_all(Hashtable *ht) {
|
||||
assert(ht != NULL);
|
||||
|
||||
for(size_t i = 0; i < ht->table_size; ++i) {
|
||||
delete_all_elements_witharg((void**)(ht->table + i), hashtable_delete_callback, ht);
|
||||
}
|
||||
|
@ -77,7 +88,7 @@ void hashtable_free(Hashtable *ht) {
|
|||
free(ht);
|
||||
}
|
||||
|
||||
static inline bool hashtable_compare(Hashtable *ht, void *key1, void *key2) {
|
||||
static bool hashtable_compare(Hashtable *ht, void *key1, void *key2) {
|
||||
if(ht->cmp_func) {
|
||||
return ht->cmp_func(key1, key2);
|
||||
}
|
||||
|
@ -86,6 +97,8 @@ static inline bool hashtable_compare(Hashtable *ht, void *key1, void *key2) {
|
|||
}
|
||||
|
||||
void* hashtable_get(Hashtable *ht, void *key) {
|
||||
assert(ht != NULL);
|
||||
|
||||
hash_t hash = ht->hash_func(key);
|
||||
HashtableElement *elems = ht->table[hash % ht->table_size];
|
||||
|
||||
|
@ -99,6 +112,8 @@ void* hashtable_get(Hashtable *ht, void *key) {
|
|||
}
|
||||
|
||||
void hashtable_set(Hashtable *ht, void *key, void *data) {
|
||||
assert(ht != NULL);
|
||||
|
||||
hash_t hash = ht->hash_func(key);
|
||||
size_t idx = hash % ht->table_size;
|
||||
HashtableElement *elems = ht->table[idx], *elem;
|
||||
|
@ -133,7 +148,36 @@ void hashtable_unset(Hashtable *ht, void *key) {
|
|||
hashtable_set(ht, key, NULL);
|
||||
}
|
||||
|
||||
void hashtable_unset_deferred(Hashtable *ht, void *key) {
|
||||
assert(ht != NULL);
|
||||
|
||||
ListContainer *c = create_element((void**)&ht->deferred_unsets, sizeof(ListContainer));
|
||||
|
||||
if(ht->copy_func) {
|
||||
ht->copy_func(&c->data, key);
|
||||
} else {
|
||||
c->data = key;
|
||||
}
|
||||
}
|
||||
|
||||
void hashtable_unset_deferred_now(Hashtable *ht) {
|
||||
ListContainer *next;
|
||||
assert(ht != NULL);
|
||||
|
||||
for(ListContainer *c = ht->deferred_unsets; c; c = next) {
|
||||
next = c->next;
|
||||
hashtable_unset(ht, c->data);
|
||||
delete_element((void**)&ht->deferred_unsets, c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Iteration functions
|
||||
*/
|
||||
|
||||
void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg) {
|
||||
assert(ht != NULL);
|
||||
|
||||
void *ret = NULL;
|
||||
|
||||
for(size_t i = 0; i < ht->table_size; ++i) {
|
||||
|
@ -148,6 +192,44 @@ void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
HashtableIterator* hashtable_iter(Hashtable *ht) {
|
||||
assert(ht != NULL);
|
||||
HashtableIterator *iter = malloc(sizeof(HashtableIterator));
|
||||
iter->hashtable = ht;
|
||||
iter->bucketnum = (size_t)-1;
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool hashtable_iter_next(HashtableIterator *iter, void **out_key, void **out_data) {
|
||||
Hashtable *ht = iter->hashtable;
|
||||
|
||||
if(iter->bucketnum == (size_t)-1) {
|
||||
iter->bucketnum = 0;
|
||||
iter->elem = ht->table[iter->bucketnum];
|
||||
} else {
|
||||
iter->elem = iter->elem->next;
|
||||
}
|
||||
|
||||
while(!iter->elem) {
|
||||
if(++iter->bucketnum == ht->table_size) {
|
||||
free(iter);
|
||||
return false;
|
||||
}
|
||||
|
||||
iter->elem = ht->table[iter->bucketnum];
|
||||
}
|
||||
|
||||
if(out_key) {
|
||||
*out_key = iter->elem->key;
|
||||
}
|
||||
|
||||
if(out_data) {
|
||||
*out_data = iter->elem->data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience functions for hashtables with string keys
|
||||
*/
|
||||
|
@ -206,10 +288,8 @@ void* hashtable_iter_free_data(void *key, void *data, void *arg) {
|
|||
size_t hashtable_get_approx_overhead(Hashtable *ht) {
|
||||
size_t o = sizeof(Hashtable) + sizeof(HashtableElement*) * ht->table_size;
|
||||
|
||||
for(size_t i = 0; i < ht->table_size; ++i) {
|
||||
for(HashtableElement *e = ht->table[i]; e; e = e->next) {
|
||||
o += sizeof(HashtableElement);
|
||||
}
|
||||
for(HashtableIterator *i = hashtable_iter(ht); hashtable_iter_next(i, NULL, NULL);) {
|
||||
o += sizeof(HashtableElement);
|
||||
}
|
||||
|
||||
return o;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
typedef struct Hashtable Hashtable;
|
||||
typedef struct HashtableIterator HashtableIterator;
|
||||
typedef uint32_t hash_t;
|
||||
|
||||
typedef bool (*HTCmpFunc)(void *key1, void *key2);
|
||||
|
@ -27,8 +28,13 @@ void hashtable_free(Hashtable *ht);
|
|||
void* hashtable_get(Hashtable *ht, void *key);
|
||||
void hashtable_set(Hashtable *ht, void *key, void *data);
|
||||
void hashtable_unset(Hashtable *ht, void *key);
|
||||
void hashtable_unset_deferred(Hashtable *ht, void *key);
|
||||
void hashtable_unset_deferred_now(Hashtable *ht);
|
||||
void hashtable_unset_all(Hashtable *ht);
|
||||
|
||||
void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg);
|
||||
HashtableIterator* hashtable_iter(Hashtable *ht);
|
||||
bool hashtable_iter_next(HashtableIterator *iter, void **out_key, void **out_data);
|
||||
|
||||
bool hashtable_cmpfunc_string(void *str1, void *str2);
|
||||
hash_t hashtable_hashfunc_string(void *vstr);
|
||||
|
@ -39,6 +45,7 @@ Hashtable* hashtable_new_stringkeys(size_t size);
|
|||
void* hashtable_get_string(Hashtable *ht, const char *key);
|
||||
void hashtable_set_string(Hashtable *ht, const char *key, void *data);
|
||||
void hashtable_unset_string(Hashtable *ht, const char *key);
|
||||
void hashtable_unset_deferred_string(Hashtable *ht, const char *key);
|
||||
|
||||
void* hashtable_iter_free_data(void *key, void *data, void *arg);
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ void draw_lasers(void) {
|
|||
Laser *laser;
|
||||
|
||||
for(laser = global.lasers; laser; laser = laser->next) {
|
||||
if(laser->shader && !config_get_int(CONFIG_NO_SHADER) && glext.draw_instanced)
|
||||
if(laser->shader && glext.draw_instanced)
|
||||
draw_laser_curve_instanced(laser);
|
||||
else
|
||||
draw_laser_curve(laser);
|
||||
|
|
|
@ -38,6 +38,10 @@ void *create_element(void **dest, int size) {
|
|||
return e;
|
||||
}
|
||||
|
||||
ListContainer* create_container(ListContainer **dest) {
|
||||
return create_element((void**)dest, sizeof(ListContainer));
|
||||
}
|
||||
|
||||
void delete_element(void **dest, void *e) {
|
||||
if(((List *)e)->prev != NULL)
|
||||
((List *)((List *)e)->prev)->next = ((List *)e)->next;
|
||||
|
|
|
@ -17,6 +17,14 @@ void delete_element(void **dest, void *e);
|
|||
void delete_all_elements(void **dest, void (callback)(void **, void *));
|
||||
void delete_all_elements_witharg(void **dest, void (callback)(void **, void *, void *), void *arg);
|
||||
|
||||
typedef struct ListContainer {
|
||||
void *next;
|
||||
void *prev;
|
||||
void *data;
|
||||
} ListContainer;
|
||||
|
||||
ListContainer* create_container(ListContainer **dest);
|
||||
|
||||
typedef struct {
|
||||
void *ptr;
|
||||
int refs;
|
||||
|
|
26
src/main.c
26
src/main.c
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "global.h"
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
#include "stage.h"
|
||||
#include "menu/mainmenu.h"
|
||||
#include "paths/native.h"
|
||||
|
@ -23,11 +24,9 @@ void taisei_shutdown(void) {
|
|||
progress_save();
|
||||
printf("\nshutdown:\n");
|
||||
|
||||
if(!config_get_int(CONFIG_NO_AUDIO)) shutdown_sfx();
|
||||
if(!config_get_int(CONFIG_NO_MUSIC)) shutdown_bgm();
|
||||
|
||||
free_all_refs();
|
||||
free_resources();
|
||||
audio_shutdown();
|
||||
video_shutdown();
|
||||
gamepad_shutdown();
|
||||
stage_free_array();
|
||||
|
@ -137,27 +136,26 @@ int main(int argc, char **argv) {
|
|||
|
||||
printf("initialize:\n");
|
||||
|
||||
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
|
||||
if(SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
errx(-1, "Error initializing SDL: %s", SDL_GetError());
|
||||
printf("-- SDL_Init\n");
|
||||
printf("-- SDL\n");
|
||||
|
||||
init_global();
|
||||
|
||||
video_init();
|
||||
printf("-- Video and OpenGL\n");
|
||||
|
||||
audio_init();
|
||||
printf("-- Audio\n");
|
||||
|
||||
init_resources();
|
||||
draw_loading_screen();
|
||||
|
||||
load_resources();
|
||||
gamepad_init();
|
||||
stage_init_array();
|
||||
progress_load(); // stage_init_array goes first!
|
||||
|
||||
// Order DOES matter: init_global, then sfx/bgm, then load_resources.
|
||||
init_sfx();
|
||||
init_bgm();
|
||||
load_resources();
|
||||
|
||||
set_transition(TransLoader, 0, FADE_TIME*2);
|
||||
|
||||
printf("initialization complete.\n");
|
||||
|
@ -172,13 +170,7 @@ int main(int argc, char **argv) {
|
|||
#ifdef DEBUG
|
||||
|
||||
if(argc >= 2 && argv[1] && !strcmp(argv[1], "dumprestables")) {
|
||||
hashtable_print_stringkeys(resources.textures);
|
||||
hashtable_print_stringkeys(resources.animations);
|
||||
hashtable_print_stringkeys(resources.sounds);
|
||||
hashtable_print_stringkeys(resources.music);
|
||||
hashtable_print_stringkeys(resources.shaders);
|
||||
hashtable_print_stringkeys(resources.models);
|
||||
hashtable_print_stringkeys(resources.bgm_descriptions);
|
||||
print_resource_hashtables();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,17 +36,15 @@ void create_ingame_menu(MenuData *m) {
|
|||
void draw_ingame_menu_bg(float f) {
|
||||
float rad = f*IMENU_BLUR;
|
||||
|
||||
if(!config_get_int(CONFIG_NO_SHADER)) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
Shader *shader = get_shader("ingame_menu");
|
||||
glUseProgram(shader->prog);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
Shader *shader = get_shader("ingame_menu");
|
||||
glUseProgram(shader->prog);
|
||||
|
||||
glUniform1f(uniloc(shader, "rad"), rad);
|
||||
glUniform1f(uniloc(shader, "rad"), rad);
|
||||
|
||||
draw_fbo_viewport(&resources.fsec);
|
||||
draw_fbo_viewport(&resources.fsec);
|
||||
|
||||
glUseProgram(0);
|
||||
}
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void draw_ingame_menu(MenuData *menu) {
|
||||
|
|
|
@ -218,20 +218,20 @@ bool bind_isactive(OptionBinding *b) {
|
|||
|
||||
// --- Shared binding callbacks --- //
|
||||
|
||||
int bind_common_onoffget(void *b) {
|
||||
return !config_get_int(((OptionBinding*)b)->configentry);
|
||||
int bind_common_onoffget(OptionBinding *b) {
|
||||
return !config_get_int(b->configentry);
|
||||
}
|
||||
|
||||
int bind_common_onoffset(void *b, int v) {
|
||||
return !config_set_int(((OptionBinding*)b)->configentry, !v);
|
||||
int bind_common_onoffset(OptionBinding *b, int v) {
|
||||
return !config_set_int(b->configentry, !v);
|
||||
}
|
||||
|
||||
int bind_common_onoffget_inverted(void *b) {
|
||||
return config_get_int(((OptionBinding*)b)->configentry);
|
||||
int bind_common_onoffget_inverted(OptionBinding *b) {
|
||||
return config_get_int(b->configentry);
|
||||
}
|
||||
|
||||
int bind_common_onoffset_inverted(void *b, int v) {
|
||||
return config_set_int(((OptionBinding*)b)->configentry, v);
|
||||
int bind_common_onoffset_inverted(OptionBinding *b, int v) {
|
||||
return config_set_int(b->configentry, v);
|
||||
}
|
||||
|
||||
#define bind_common_intget bind_common_onoffget_inverted
|
||||
|
@ -243,33 +243,29 @@ bool bind_stagebg_fpslimit_dependence(void) {
|
|||
return config_get_int(CONFIG_NO_STAGEBG) == 2;
|
||||
}
|
||||
|
||||
bool bind_sfxvol_dependence(void) {
|
||||
return !config_get_int(CONFIG_NO_AUDIO);
|
||||
}
|
||||
|
||||
bool bind_bgmvol_dependence(void) {
|
||||
return !config_get_int(CONFIG_NO_MUSIC);
|
||||
bool bind_audio_dependence(void) {
|
||||
return audio_backend_initialized();
|
||||
}
|
||||
|
||||
bool bind_resizable_dependence(void) {
|
||||
return !config_get_int(CONFIG_FULLSCREEN);
|
||||
}
|
||||
|
||||
int bind_saverpy_get(void *b) {
|
||||
int v = config_get_int(((OptionBinding*)b)->configentry);
|
||||
int bind_saverpy_get(OptionBinding *b) {
|
||||
int v = config_get_int(b->configentry);
|
||||
|
||||
if(v > 1)
|
||||
return v;
|
||||
return !v;
|
||||
}
|
||||
|
||||
int bind_saverpy_set(void *b, int v) {
|
||||
int bind_saverpy_set(OptionBinding *b, int v) {
|
||||
if(v > 1)
|
||||
return config_set_int(((OptionBinding*)b)->configentry, v);
|
||||
return !config_set_int(((OptionBinding*)b)->configentry, !v);
|
||||
return config_set_int(b->configentry, v);
|
||||
return !config_set_int(b->configentry, !v);
|
||||
}
|
||||
|
||||
int bind_resolution_set(void *b, int v) {
|
||||
int bind_resolution_set(OptionBinding *b, int v) {
|
||||
if(v >= 0) {
|
||||
VideoMode *m = video.modes + v;
|
||||
config_set_int(CONFIG_VID_WIDTH, m->width);
|
||||
|
@ -357,12 +353,6 @@ void options_sub_video(MenuData *parent, void *arg) {
|
|||
|
||||
add_menu_separator(m);
|
||||
|
||||
#ifdef DEBUG
|
||||
add_menu_entry(m, "Shaders", do_nothing,
|
||||
b = bind_option(CONFIG_NO_SHADER, bind_common_onoffget_inverted, bind_common_onoffset_inverted)
|
||||
); bind_onoff(b);
|
||||
#endif
|
||||
|
||||
add_menu_entry(m, "Stage background", do_nothing,
|
||||
b = bind_option(CONFIG_NO_STAGEBG, bind_common_intget, bind_common_intset)
|
||||
); bind_addvalue(b, "on");
|
||||
|
@ -614,27 +604,13 @@ void create_options_menu(MenuData *m) {
|
|||
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Sound effects", do_nothing,
|
||||
b = bind_option(CONFIG_NO_AUDIO, bind_common_onoffget_inverted,
|
||||
bind_common_onoffset_inverted)
|
||||
); bind_onoff(b);
|
||||
|
||||
add_menu_entry(m, "Volume", do_nothing,
|
||||
add_menu_entry(m, "SFX Volume", do_nothing,
|
||||
b = bind_scale(CONFIG_SFX_VOLUME, 0, 1, 0.1)
|
||||
); bind_setdependence(b, bind_sfxvol_dependence);
|
||||
b->pad++;
|
||||
); bind_setdependence(b, bind_audio_dependence);
|
||||
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Background music", do_nothing,
|
||||
b = bind_option(CONFIG_NO_MUSIC, bind_common_onoffget_inverted,
|
||||
bind_common_onoffset_inverted)
|
||||
); bind_onoff(b);
|
||||
|
||||
add_menu_entry(m, "Volume", do_nothing,
|
||||
add_menu_entry(m, "BGM Volume", do_nothing,
|
||||
b = bind_scale(CONFIG_BGM_VOLUME, 0, 1, 0.1)
|
||||
); bind_setdependence(b, bind_bgmvol_dependence);
|
||||
b->pad++;
|
||||
); bind_setdependence(b, bind_audio_dependence);
|
||||
|
||||
add_menu_separator(m);
|
||||
add_menu_entry(m, "Video options…", options_sub_video, NULL);
|
||||
|
|
|
@ -16,8 +16,10 @@ void draw_options_menu(MenuData *m);
|
|||
|
||||
#define OPTIONS_TEXT_INPUT_BUFSIZE 50
|
||||
|
||||
typedef int (*BindingGetter)(void*);
|
||||
typedef int (*BindingSetter)(void*, int);
|
||||
typedef struct OptionBinding OptionBinding;
|
||||
|
||||
typedef int (*BindingGetter)(OptionBinding*);
|
||||
typedef int (*BindingSetter)(OptionBinding*, int);
|
||||
typedef bool (*BindingDependence)(void);
|
||||
|
||||
typedef enum BindingType {
|
||||
|
|
|
@ -196,7 +196,7 @@ int asymptotic(Projectile *p, int t) { // v = a[0]*(a[1] + 1); a[1] -> 0
|
|||
}
|
||||
|
||||
void _ProjDraw(Projectile *proj, int t) {
|
||||
if(proj->clr && !config_get_int(CONFIG_NO_SHADER)) {
|
||||
if(proj->clr) {
|
||||
static GLfloat clr[4];
|
||||
Shader *shader = get_shader("bullet_color");
|
||||
glUseProgram(shader->prog);
|
||||
|
@ -204,16 +204,11 @@ void _ProjDraw(Projectile *proj, int t) {
|
|||
glUniform4fv(uniloc(shader, "color"), 1, clr);
|
||||
}
|
||||
|
||||
if(!proj->clr && config_get_int(CONFIG_NO_SHADER))
|
||||
glColor3f(0,0,0);
|
||||
|
||||
draw_texture_p(0,0, proj->tex);
|
||||
|
||||
if(proj->clr && config_get_int(CONFIG_NO_SHADER))
|
||||
glColor3f(1,1,1);
|
||||
|
||||
if(!config_get_int(CONFIG_NO_SHADER))
|
||||
if(proj->clr) {
|
||||
glUseProgram(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjDraw(Projectile *proj, int t) {
|
||||
|
|
|
@ -6,66 +6,91 @@
|
|||
*/
|
||||
|
||||
#include "animation.h"
|
||||
#include "texture.h"
|
||||
#include "global.h"
|
||||
#include "resource.h"
|
||||
#include "list.h"
|
||||
|
||||
#include "taisei_err.h"
|
||||
|
||||
Animation *init_animation(const char *filename) {
|
||||
Animation *buf = malloc(sizeof(Animation));
|
||||
char* animation_path(const char *name) {
|
||||
// stub
|
||||
// we could scan the gfx/ directory and find the first matching animation here
|
||||
// ...or we could describe animations with simple text files instead of encoding them in texture file names
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *beg = strstr(filename, "gfx/") + 4;
|
||||
char *end = strrchr(filename, '.');
|
||||
bool check_animation_path(const char *path) {
|
||||
char *base = strjoin(get_prefix(), TEX_PATH_PREFIX, "ani_", NULL);
|
||||
bool result = strstartswith(path, base) && strendswith(path, TEX_EXTENSION);
|
||||
free(base);
|
||||
return result;
|
||||
}
|
||||
|
||||
int sz = end - beg + 1;
|
||||
char name[end - beg + 1];
|
||||
strlcpy(name, beg, sz);
|
||||
char* animation_name(const char *filename) {
|
||||
char *name = resource_util_basename(ANI_PATH_PREFIX, filename);
|
||||
char *c = name, *newname;
|
||||
|
||||
char* tok;
|
||||
while(c = strchr(c, '_')) {
|
||||
newname = ++c;
|
||||
}
|
||||
|
||||
char buf[strlen(newname)+1];
|
||||
strcpy(buf, newname);
|
||||
free(name);
|
||||
newname = NULL;
|
||||
stralloc(&newname, buf);
|
||||
|
||||
return newname;
|
||||
}
|
||||
|
||||
void* load_animation(const char *filename) {
|
||||
Animation *ani = malloc(sizeof(Animation));
|
||||
|
||||
char *basename = resource_util_basename(ANI_PATH_PREFIX, filename);
|
||||
char name[strlen(basename) + 1];
|
||||
strcpy(name, basename);
|
||||
|
||||
char *tok;
|
||||
strtok(name, "_");
|
||||
|
||||
if((tok = strtok(NULL, "_")) == NULL)
|
||||
errx(-1, "init_animation():\n!- bad 'rows' in filename '%s'", name);
|
||||
buf->rows = atoi(tok);
|
||||
if((tok = strtok(NULL, "_")) == NULL)
|
||||
errx(-1, "init_animation():\n!- bad 'cols' in filename '%s'", name);
|
||||
buf->cols = atoi(tok);
|
||||
if((tok = strtok(NULL, "_")) == NULL)
|
||||
errx(-1, "init_animation():\n!- bad 'speed' in filename '%s'", name);
|
||||
buf->speed = atoi(tok);
|
||||
|
||||
if(buf->speed == 0)
|
||||
errx(-1, "animation speed of %s == 0. relativity?", name);
|
||||
#define ANIFAIL(what) { warnx("load_animation(): bad '" what "' in filename '%s'", basename); free(basename); return NULL; }
|
||||
|
||||
if((tok = strtok(NULL, "_")) == NULL)
|
||||
errx(-1, "init_animation():\n!- bad 'name' in filename '%s'", name);
|
||||
ANIFAIL("rows")
|
||||
ani->rows = atoi(tok);
|
||||
if((tok = strtok(NULL, "_")) == NULL)
|
||||
ANIFAIL("cols")
|
||||
ani->cols = atoi(tok);
|
||||
if((tok = strtok(NULL, "_")) == NULL)
|
||||
ANIFAIL("speed")
|
||||
ani->speed = atoi(tok);
|
||||
|
||||
char refname[strlen(tok)+1];
|
||||
memset(refname, 0, strlen(tok)+1);
|
||||
strcpy(refname, tok);
|
||||
if(ani->speed == 0) {
|
||||
warnx("load_animation(): animation speed of %s == 0. relativity?", name);
|
||||
free(basename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf->tex = load_texture(filename);
|
||||
buf->w = buf->tex->w/buf->cols;
|
||||
buf->h = buf->tex->h/buf->rows;
|
||||
ani->tex = get_tex(basename);
|
||||
|
||||
hashtable_set_string(resources.animations, refname, buf);
|
||||
if(!ani->tex) {
|
||||
warnx("load_animation(): couldn't get texture '%s'", basename);
|
||||
free(basename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("-- initialized animation '%s'\n", refname);
|
||||
return buf;
|
||||
ani->w = ani->tex->w / ani->cols;
|
||||
ani->h = ani->tex->h / ani->rows;
|
||||
|
||||
free(basename);
|
||||
return (void*)ani;
|
||||
|
||||
#undef ANIFAIL
|
||||
}
|
||||
|
||||
Animation *get_ani(const char *name) {
|
||||
Animation *res = hashtable_get_string(resources.animations, name);
|
||||
|
||||
if(res == NULL)
|
||||
errx(-1,"get_ani():\n!- cannot load animation '%s'", name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void delete_animations(void) {
|
||||
resources_delete_and_unset_all(resources.animations, hashtable_iter_free_data, NULL);
|
||||
return get_resource(RES_ANIM, name, RESF_REQUIRED)->animation;
|
||||
}
|
||||
|
||||
void draw_animation(float x, float y, int row, const char *name) {
|
||||
|
|
|
@ -24,10 +24,16 @@ typedef struct Animation {
|
|||
Texture *tex;
|
||||
} Animation;
|
||||
|
||||
Animation *init_animation(const char *filename);
|
||||
char* animation_path(const char *name);
|
||||
bool check_animation_path(const char *path);
|
||||
void* load_animation(const char *filename);
|
||||
char* animation_name(const char *filename);
|
||||
|
||||
Animation *get_ani(const char *name);
|
||||
void delete_animations(void);
|
||||
|
||||
void draw_animation(float x, float y, int row, const char *name);
|
||||
void draw_animation_p(float x, float y, int row, Animation *ani);
|
||||
|
||||
#define ANI_PATH_PREFIX TEX_PATH_PREFIX
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
|
||||
*/
|
||||
|
||||
#include "audio.h" |