From 1a9021e1edb6fe1700f58e3162a2a2ee8b2d99a4 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 27 Jul 2024 18:12:48 +0900 Subject: [PATCH] Add DALKDemo and DALKEDemo HLLs DALKDemo is responsible not only for the opening demo but also for the UI of the title menu. This change fully implements DALKDemo. DALKEDemo is stubbed out. --- CMakeLists.txt | 2 + src/ffi.c | 4 + src/hll/DALKDemo.c | 417 ++++++++++++++++++++++++++++++++++++++++++++ src/hll/DALKEDemo.c | 27 +++ src/meson.build | 2 + 5 files changed, 452 insertions(+) create mode 100644 src/hll/DALKDemo.c create mode 100644 src/hll/DALKEDemo.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 457f1b6..65d0cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,8 @@ target_sources(xsystem4 PRIVATE src/hll/Confirm3.c src/hll/CrayfishLogViewer.c src/hll/Cursor.c + src/hll/DALKDemo.c + src/hll/DALKEDemo.c src/hll/Data.c src/hll/DataFile.c src/hll/DrawDungeon.c diff --git a/src/ffi.c b/src/ffi.c index 4699d50..fedc90c 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -320,6 +320,8 @@ extern struct static_library lib_Confirm2; extern struct static_library lib_Confirm3; extern struct static_library lib_CrayfishLogViewer; extern struct static_library lib_Cursor; +extern struct static_library lib_DALKDemo; +extern struct static_library lib_DALKEDemo; extern struct static_library lib_Data; extern struct static_library lib_DataFile; extern struct static_library lib_DrawDungeon; @@ -417,6 +419,8 @@ static struct static_library *static_libraries[] = { &lib_Confirm3, &lib_CrayfishLogViewer, &lib_Cursor, + &lib_DALKDemo, + &lib_DALKEDemo, &lib_Data, &lib_DataFile, &lib_DrawDungeon, diff --git a/src/hll/DALKDemo.c b/src/hll/DALKDemo.c new file mode 100644 index 0000000..df6b82d --- /dev/null +++ b/src/hll/DALKDemo.c @@ -0,0 +1,417 @@ +/* Copyright (C) 2024 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include + +#include "system4.h" +#include "system4/alk.h" +#include "system4/archive.h" +#include "system4/cg.h" + +#include "audio.h" +#include "effect.h" +#include "gfx/gfx.h" +#include "hll.h" +#include "input.h" +#include "vm.h" +#include "xsystem4.h" + +#define NR_PARTICLES 30 + +enum { + DD_CG_PARTICLE = 12, + DD_CG_LOGO = 13, + DD_CG_BACKGROUND = 14, + DD_CG_BACKGROUND_SHADOWED = 15, + DD_CG_MAGIC_CIRCLE = 16, + DD_CG_INITIALIZE_WARNING = 17, + DD_CG_YES = 18, + DD_CG_NO = 19, + DD_CG_GAMESTART = 20, + DD_CG_GAMESTART_SELECTED = 21, + DD_CG_INITIALIZE = 22, + DD_CG_INITIALIZE_SELECTED = 23, + DD_CG_MAX +}; + +enum { + DD_SOUND_SELCHANGE = 451, + DD_SOUND_CLICK = 454, +}; + +struct dalkdemo { + Texture tex[DD_CG_MAX]; + Texture tmp; + float rotate_deg; + struct { + int x; + int y; + int speed; + } particles[NR_PARTICLES]; +}; +static struct dalkdemo *dd; + +static bool dalkdemo_run_effect(int (*update)(float), int time) +{ + uint32_t start = SDL_GetTicks(); + bool prev_keydown = false; + uint32_t t = SDL_GetTicks() - start; + while (t < time) { + update((float)t / (float)time); + handle_events(); + bool keydown = key_is_down(VK_LBUTTON) || key_is_down(VK_RBUTTON) || key_is_down(VK_RETURN) || key_is_down(VK_SPACE); + if (prev_keydown && !keydown) + return true; + prev_keydown = keydown; + + uint32_t t2 = SDL_GetTicks() - start; + if (t2 < t + 16) { + SDL_Delay(t + 16 - t2); + t += 16; + } else { + t = t2; + } + } + return false; +} + +static bool dalkdemo_effect_copy(Texture *tex, int x, int y, int w, int h, enum effect effect, int time) +{ + if (!effect_init(effect)) + return true; + effect_record_old(); + if (tex) + gfx_copy(gfx_main_surface(), x, y, tex, x, y, w, h); + else + gfx_clear(); + effect_record_new(); + + bool interrupted = dalkdemo_run_effect(effect_update, time); + if (!interrupted) + effect_update(1.0); + effect_fini(); + return interrupted; +} + +static bool dalkdemo_wait(int time) +{ + return dalkdemo_effect_copy(&dd->tex[0], 0, 0, 0, 0, EFFECT_CROSSFADE, time); +} + +static void dalkdemo_draw_title(Texture *dst, float circle_mag, float logo_opacity) +{ + Texture *src = &dd->tex[DD_CG_MAGIC_CIRCLE]; + gfx_copy_rot_zoom2(dst, dst->w / 2, dst->h / 2, src, src->w / 2, src->h / 2, dd->rotate_deg, circle_mag); + dd->rotate_deg -= 0.25f; + + if (logo_opacity <= 0.f) { + src = &dd->tex[DD_CG_BACKGROUND]; + } else if (logo_opacity >= 1.f) { + src = &dd->tex[DD_CG_BACKGROUND_SHADOWED]; + } else { + src = &dd->tex[DD_CG_BACKGROUND]; + gfx_copy(&dd->tmp, 0, 0, src, 0, 0, src->w, src->h); + src = &dd->tex[DD_CG_BACKGROUND_SHADOWED]; + gfx_blend(&dd->tmp, 0, 0, src, 0, 0, src->w, src->h, logo_opacity * 255); + src = &dd->tmp; + } + gfx_blend_screen(dst, 0, 0, src, 0, 0, src->w, src->h); + + src = &dd->tex[DD_CG_LOGO]; + gfx_blend_amap_alpha(dst, 0, 0, src, 0, 0, src->w, src->h, logo_opacity * 255); + + src = &dd->tex[DD_CG_PARTICLE]; + for (int i = 0; i < NR_PARTICLES; i++) { + gfx_blend_screen(dst, dd->particles[i].x - src->w / 2, dd->particles[i].y - src->h / 2, src, 0, 0, src->w, src->h); + dd->particles[i].x = (dd->particles[i].x - dd->particles[i].speed + dst->w) % dst->w; + dd->particles[i].y = (dd->particles[i].y + dd->particles[i].speed) % dst->h; + } +} + +static int dalkdemo_title_effect(float progress) +{ + float t = progress * 15.f; + gfx_clear(); + float logo_opacity = max(0.f, min(1.f, (t - 5.f) / 7.f)); + dalkdemo_draw_title(gfx_main_surface(), 1.75f - t * 0.05f, logo_opacity); + if (t < 5.f) { + int b = t * 255 / 5; + gfx_fill_multiply(gfx_main_surface(), 0, 0, 640, 480, b, b, b); + } + gfx_swap(); + return 1; +} + +static void dalkdemo_intro(void) +{ + int bgm_channel = bgm_get_unused_channel(); + bgm_prepare(bgm_channel, 1); + bgm_play(bgm_channel); + + gfx_clear(); + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 1500)) + goto skipped; + if (dalkdemo_wait(1000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[1], 0, 0, 640, 480, EFFECT_CROSSFADE, 1500)) + goto skipped; + // FIXME: DALKDemo.dll uses a steeper gradient + if (dalkdemo_effect_copy(&dd->tex[2], 585, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[2], 545, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[2], 505, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[2], 60, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[2], 20, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 4000)) + goto skipped; + if (dalkdemo_wait(1000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[3], 340, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[3], 300, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[3], 260, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + + if (dalkdemo_effect_copy(&dd->tex[4], 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[5], 190, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[5], 150, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[5], 110, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[5], 70, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[5], 30, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 4000)) + goto skipped; + if (dalkdemo_wait(1000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[6], 320, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[6], 280, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + + if (dalkdemo_effect_copy(&dd->tex[7], 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[8], 560, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[8], 520, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[8], 480, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[8], 440, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[8], 400, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 4000)) + goto skipped; + if (dalkdemo_wait(1000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[9], 360, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[9], 320, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[9], 280, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[9], 240, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[0], 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + + if (dalkdemo_effect_copy(&dd->tex[10], 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[11], 580, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[11], 540, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(&dd->tex[11], 60, 0, 40, 480, EFFECT_UP_DOWN_CROSSFADE, 3000)) + goto skipped; + if (dalkdemo_effect_copy(NULL, 0, 0, 640, 480, EFFECT_CROSSFADE, 3000)) + goto skipped; + + dalkdemo_run_effect(dalkdemo_title_effect, 15000); + + skipped: + bgm_unprepare(bgm_channel); +} + +static int dalkdemo_main_menu() +{ + Texture *dst = gfx_main_surface(); + + enum { + START_MENU, + INITIALIZE_MENU, + } state = START_MENU; + + bool sel = false; + bool prev_lbutton = false; + bool prev_rbutton = false; + for (;;) { + // Input handling + handle_events(); + int mx, my; + mouse_get_pos(&mx, &my); + bool next_sel = state == START_MENU ? my >= 430 : mx >= 320; + if (sel != next_sel) { + sel = next_sel; + audio_play_sound(DD_SOUND_SELCHANGE); + } + + bool lbutton = key_is_down(VK_LBUTTON); + if (prev_lbutton && !lbutton) { + audio_play_sound(DD_SOUND_CLICK); + if (state == START_MENU) { + if (!sel) + return 1; + else + state = INITIALIZE_MENU; + } else { + if (!sel) + return 2; + else + state = START_MENU; + } + } + prev_lbutton = lbutton; + bool rbutton = key_is_down(VK_RBUTTON); + if (prev_rbutton && !rbutton && state == INITIALIZE_MENU) { + state = START_MENU; + } + prev_rbutton = rbutton; + + // Draw + gfx_clear(); + dalkdemo_draw_title(dst, 1.f, 1.f); + + if (state == START_MENU) { + Texture *src = &dd->tex[!sel ? DD_CG_GAMESTART_SELECTED : DD_CG_GAMESTART]; + gfx_blend_amap(dst, (dst->w - src->w) / 2, 412 - src->h, src, 0, 0, src->w, src->h); + src = &dd->tex[sel ? DD_CG_INITIALIZE_SELECTED : DD_CG_INITIALIZE]; + gfx_blend_amap(dst, (dst->w - src->w) / 2, 473 - src->h, src, 0, 0, src->w, src->h); + } else { + gfx_copy_grayscale(dst, 0, 0, dst, 0, 0, dst->w, dst->h); + Texture *src = &dd->tex[DD_CG_INITIALIZE_WARNING]; + gfx_blend_amap(dst, 0, dst->h - src->h, src, 0, 0, src->w, src->h); + if (!sel) { + src = &dd->tex[DD_CG_YES]; + gfx_blend_amap(dst, 223, dst->h - src->h, src, 0, 0, src->w, src->h); + } else { + src = &dd->tex[DD_CG_NO]; + gfx_blend_amap(dst, 330, dst->h - src->h, src, 0, 0, src->w, src->h); + } + } + + gfx_swap(); + audio_update(); + vm_sleep(16); + } +} + +static void DALKDemo_Release(void) +{ + for (int i = 0; i < DD_CG_MAX; i++) { + gfx_delete_texture(&dd->tex[i]); + } + gfx_delete_texture(&dd->tmp); + free(dd); + dd = NULL; +} + +static int DALKDemo_Init(possibly_unused void *imainsystem) +{ + char *alk_path = gamedir_path("daDemo.alk"); + int error = ARCHIVE_SUCCESS; + struct alk_archive *alk = alk_open(alk_path, MMAP_IF_64BIT, &error); + if (error == ARCHIVE_FILE_ERROR) { + WARNING("%s: %s", display_utf0(alk_path), strerror(errno)); + } else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) { + WARNING("%s: invalid .alk file", display_utf0(alk_path)); + } + free(alk_path); + if (!alk) + return 0; + + dd = xcalloc(1, sizeof(struct dalkdemo)); + for (int i = 0; i < DD_CG_MAX; i++) { + struct archive_data *dfile = archive_get(&alk->ar, i); + if (!dfile) { + WARNING("daDemo.alk: cannot load CG %d", i); + DALKDemo_Release(); + return 0; + } + struct cg *cg = cg_load_data(dfile); + archive_free_data(dfile); + if (!cg) { + WARNING("daDemo.alk: cannot decode CG %d", i); + DALKDemo_Release(); + return 0; + } + gfx_init_texture_with_cg(&dd->tex[i], cg); + cg_free(cg); + } + archive_free(&alk->ar); + gfx_init_texture_blank(&dd->tmp, 640, 480); + + dd->rotate_deg = 0.f; + for (int i = 0; i < NR_PARTICLES; i++) { + dd->particles[i].x = rand() % 640; + dd->particles[i].y = rand() % 480; + dd->particles[i].speed = rand() % 2 ? 1 : 2; + } + + return 1; +} + +static int DALKDemo_Run(void) +{ + if (!dd) + return 0; + + dalkdemo_intro(); + return dalkdemo_main_menu(); +} + +static int DALKDemo_Run2(void) +{ + if (!dd) + return 0; + + return dalkdemo_main_menu(); +} + +HLL_LIBRARY(DALKDemo, + HLL_EXPORT(Init, DALKDemo_Init), + HLL_EXPORT(Run, DALKDemo_Run), + HLL_EXPORT(Run2, DALKDemo_Run2), + HLL_EXPORT(Release, DALKDemo_Release) + ); diff --git a/src/hll/DALKEDemo.c b/src/hll/DALKEDemo.c new file mode 100644 index 0000000..f057fda --- /dev/null +++ b/src/hll/DALKEDemo.c @@ -0,0 +1,27 @@ +/* Copyright (C) 2024 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "hll.h" + +HLL_WARN_UNIMPLEMENTED(0, int, DALKEDemo, Init, void *pIMainSystem); +//void DALKEDemo_SetKeyCancelFlag(int nCencel); +//int DALKEDemo_Run(void); + +HLL_LIBRARY(DALKEDemo, + HLL_EXPORT(Init, DALKEDemo_Init), + HLL_TODO_EXPORT(SetKeyCancelFlag, DALKEDemo_SetKeyCancelFlag), + HLL_TODO_EXPORT(Run, DALKEDemo_Run) + ); diff --git a/src/meson.build b/src/meson.build index 6e64e81..019fd93 100644 --- a/src/meson.build +++ b/src/meson.build @@ -81,6 +81,8 @@ xsystem4 = [version_h, 'hll/Confirm3.c', 'hll/CrayfishLogViewer.c', 'hll/Cursor.c', + 'hll/DALKDemo.c', + 'hll/DALKEDemo.c', 'hll/Data.c', 'hll/DataFile.c', 'hll/DrawDungeon.c',