diff --git a/CMakeLists.txt b/CMakeLists.txt index fba1b2e..665cc58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,6 @@ target_sources(system3 PRIVATE src/sys/ags_text.cpp src/sys/ags_vsp.cpp src/sys/ags_vsp2l.cpp - src/sys/ags_window.cpp src/sys/dri.cpp src/sys/game_id.cpp src/sys/msgskip.cpp diff --git a/src/sys/ags.cpp b/src/sys/ags.cpp index 8ae3c11..2a22b99 100644 --- a/src/sys/ags.cpp +++ b/src/sys/ags.cpp @@ -186,13 +186,10 @@ AGS::AGS(const Config& config, const GameId& game_id) : game_id(game_id) SDL_SetPaletteColors(screen_palette, program_palette->colors, 0, 256); - init_windows(); - // 画面選択 src_screen = dest_screen = 0; // メッセージ表示 - text.reset_pos(text_w[0].sx, text_w[0].sy + 2); text.line_space = 2; text.font_size = 16; if (game_id.sys_ver == 1) { @@ -218,7 +215,6 @@ AGS::AGS(const Config& config, const GameId& game_id) : game_id(game_id) menu.frame_color = 15; menu.back_color = 0; } - menu_fix = false; draw_hankaku = false; draw_menu = false; @@ -406,14 +402,6 @@ bool AGS::save_screenshot(const char* path) return ok; } -int AGS::calculate_menu_max(int window) { - if (game_id.is(GameId::INTRUDER)) - return 6; - if (game_id.is(GameId::GAKUEN_SENKI)) - return (menu_w[window - 1].ey - menu_w[window - 1].sy) / (menu.font_size + 4); - return 11; -} - void AGS::load_censor_list(const char* fname) { censor_list.clear(); diff --git a/src/sys/ags.h b/src/sys/ags.h index 6109b28..1380e58 100644 --- a/src/sys/ags.h +++ b/src/sys/ags.h @@ -16,39 +16,11 @@ #include #include "common.h" #include "game_id.h" +#include "cg.h" #include "dri.h" #include -inline uint8_t* surface_line(SDL_Surface* surface, int y) -{ - return static_cast(surface->pixels) + surface->pitch * y; -} - struct Config; -class FILEIO; - -struct SurfaceDeleter { - void operator()(SDL_Surface* s) const noexcept { - if (s) SDL_FreeSurface(s); - } -}; - -struct CG { - std::unique_ptr surface_; - int x; - int y; - - CG() = default; - CG(int x, int y, int width, int height) - : surface_(SDL_CreateRGBSurfaceWithFormat(0, width, height, 8, SDL_PIXELFORMAT_INDEX8)), - x(x), y(y) {} - explicit operator bool() const noexcept { return static_cast(surface_); } - - SDL_Surface* surface() const { return surface_.get(); } - int width() const { return surface_->w; } - int height() const { return surface_->h; } - SDL_Palette* palette() const { return surface_->format->palette; } -}; enum CgFlags { CG_EXTRACT_CG = 1, @@ -91,7 +63,6 @@ private: void draw_char(int dest, int dest_x, int dest_y, uint16 code, TTF_Font* font, uint8 color); void draw_char_antialias(int dest, int dest_x, int dest_y, uint16 code, TTF_Font* font, uint8 color, uint8 cache[]); - void draw_gaiji(int dest, int dest_x, int dest_y, const uint8_t bitmap[32], int size, uint8 color); uint8_t palR(uint8_t col) const { return screen_palette->colors[col].r; } uint8_t palG(uint8_t col) const { return screen_palette->colors[col].g; } @@ -124,6 +95,8 @@ public: uint8 get_pixel(int dest, int x, int y) const { return vram[dest][y][x]; } void set_pixel(int dest, int x, int y, uint8 color) { vram[dest][y][x] = color; } void invalidate_screen(int sx, int sy, int width, int height); + void draw_gaiji(int dest, int dest_x, int dest_y, const uint8_t bitmap[32], int size, uint8 color); + bool is_faded() const { return fade_level != 0; } void fade_out(int duration_ms, bool white); void fade_in(int duration_ms); @@ -140,33 +113,11 @@ public: void box_line(int dest, int sx, int sy, int ex, int ey, uint8 color); void draw_window(int sx, int sy, int ex, int ey, bool frame, uint8 frame_color, uint8 back_color); + CG save_rect(int x, int y, int width, int height); + void restore_rect(const CG& cg); + void draw_text(std::string_view string, bool text_wait = false); - void clear_text_window(int index, bool erase); - bool return_text_line(int index); - void draw_push(int index); - void set_push_bitmap(const uint8_t* bitmap) { push_bitmap = bitmap; } - void open_text_window(int index, bool erase); - void close_text_window(int index, bool update); - void set_text_window(int index, int sx, int sy, int ex, int ey, bool save) { - text_w[index - 1].reset(sx, sy, ex, ey, text_w[index - 1].frame, save); - } - void set_text_window_frame(int index, bool frame) { - text_w[index - 1].frame = frame; - } - - void clear_menu_window(); - void open_menu_window(int index); - void redraw_menu_window(int index, int selected); - void close_menu_window(int index); - void get_menu_window_rect(int index, int* sx, int* sy, int* ex, int* ey); - void set_menu_window(int index, int sx, int sy, int ex, int ey, bool save) { - menu_w[index - 1].reset(sx, sy, ex, ey, menu_w[index - 1].frame, save); - } - void set_menu_window_frame(int index, bool frame) { - menu_w[index - 1].frame = frame; - } - void load_cursor(int page, uint8_t flags); void select_cursor(); void translate_mouse_coords(int* x, int* y); @@ -174,7 +125,6 @@ public: bool get_scanline_mode() const { return scanline_texture; } void set_scanline_mode(bool enable); bool save_screenshot(const char* path); - int calculate_menu_max(int window); SDL_Rect dirty_rect = {}; @@ -212,7 +162,6 @@ public: // メニュー表示 TextContext menu; - bool menu_fix; bool draw_hankaku; bool draw_menu; @@ -227,28 +176,9 @@ public: uint8 cursor_color; int cursor_index; - // Window (B command) - struct Window { - int sx; - int sy; - int ex; - int ey; - bool frame; - bool save; - CG screen; - CG window; - - void reset(int sx, int sy, int ex, int ey, bool frame, bool save); - }; - Window menu_w[10]; - Window text_w[10]; - private: - void init_windows(); - Dri acg; const char* bmp_prefix = NULL; - const uint8_t* push_bitmap = nullptr; }; extern "C" void ags_setAntialiasedStringMode(int on); diff --git a/src/sys/ags_draw.cpp b/src/sys/ags_draw.cpp index b8e19dd..1ca0382 100644 --- a/src/sys/ags_draw.cpp +++ b/src/sys/ags_draw.cpp @@ -255,3 +255,39 @@ void AGS::box_line(int dest, int sx, int sy, int ex, int ey, uint8 color) } } +void AGS::draw_window(int sx, int sy, int ex, int ey, bool frame, uint8 frame_color, uint8 back_color) +{ + SDL_Rect rect = {sx, sy, ex - sx + 1, ey - sy + 1}; + SDL_FillRect(hBmpScreen[0], &rect, back_color); + + if (frame) { + SDL_Rect rects[] = { + {sx + 1, sy + 1, ex - sx - 1, 2}, + {sx + 1, ey - 2, ex - sx - 1, 2}, + {sx + 1, sy + 1, 2, ey - sy - 1}, + {ex - 2, sy + 1, 2, ey - sy - 1}, + }; + SDL_FillRects(hBmpScreen[0], rects, 4, frame_color); + box_line(0, sx + 4, sy + 4, ex - 4, ey - 4, frame_color); + } + invalidate_screen(sx, sy, ex - sx + 1, ey - sy + 1); +} + +CG AGS::save_rect(int x, int y, int width, int height) +{ + CG cg(x, y, width, height); + SDL_SetPaletteColors(cg.palette(), screen_palette->colors, 0, 256); + + SDL_Rect rect = {x, y, width, height}; + SDL_BlitSurface(hBmpScreen[0], &rect, cg.surface(), NULL); + return cg; +} + +void AGS::restore_rect(const CG& cg) +{ + SDL_SetSurfacePalette(hBmpScreen[0], cg.palette()); + SDL_Rect rect = {cg.x, cg.y, cg.width(), cg.height()}; + SDL_BlitSurface(cg.surface(), NULL, hBmpScreen[0], &rect); + invalidate_screen(cg.x, cg.y, cg.width(), cg.height()); + SDL_SetSurfacePalette(hBmpScreen[0], screen_palette); +} diff --git a/src/sys/ags_window.cpp b/src/sys/ags_window.cpp deleted file mode 100644 index a84fc85..0000000 --- a/src/sys/ags_window.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* - ALICE SOFT SYSTEM 3 for Win32 - - [ AGS - window ] -*/ - -#include "ags.h" -#include - -void AGS::Window::reset(int sx, int sy, int ex, int ey, bool frame, bool save) { - this->sx = sx; - this->sy = sy; - this->ex = ex; - this->ey = ey; - this->frame = frame; - this->save = save; - screen.surface_.reset(); - window.surface_.reset(); -} - -void AGS::init_windows() -{ - for (int i = 0; i < 10; i++) { - // ウィンドウの初期位置はシステムによって異なる - switch (game_id.game) { - case GameId::BUNKASAI: - text_w[i].reset(24, 304, 616, 384, false, false); - menu_w[i].reset(440, 18, 620, 178, true, true); - break; - case GameId::CRESCENT: - text_w[i].reset(24, 288, 616, 378, false, false); - // 本来は横メニュー - menu_w[i].reset(464, 50, 623, 240, true, true); - break; - case GameId::RANCE2: - case GameId::RANCE2_HINT: - text_w[i].reset(8, 285, 502, 396, false, false); - menu_w[i].reset(431, 19, 624, 181, false, true); - break; - case GameId::DPS: - case GameId::DPS_SG_FAHREN: - case GameId::DPS_SG_KATEI: - case GameId::DPS_SG_NOBUNAGA: - case GameId::DPS_SG2_ANTIQUE: - case GameId::DPS_SG2_IKENAI: - case GameId::DPS_SG2_AKAI: - case GameId::DPS_SG3_RABBIT: - case GameId::DPS_SG3_SHINKON: - case GameId::DPS_SG3_SOTSUGYOU: - text_w[i].reset(48, 288, 594, 393, false, false); - //menu_w[i].reset(48, 288, 584, 393, false, true); - menu_w[i].reset(48, 288, 594, 393, false, true); - break; - case GameId::FUKEI: - text_w[i].reset(44, 282, 593, 396, false, false); - menu_w[i].reset(460, 14, 635, 214, false, true); - break; - case GameId::INTRUDER: - text_w[i].reset(8, 280, 629, 393, false, false); - menu_w[i].reset(448, 136, 623, 340, true, true); - break; - case GameId::TENGU: - text_w[i].reset(44, 282, 593, 396, false, false); - menu_w[i].reset(452, 14, 627, 214, false, true); - break; - case GameId::TOUSHIN_HINT: - text_w[i].reset(8, 311, 623, 391, false, false); - menu_w[i].reset(452, 14, 627, 214, true, true); - break; - case GameId::LITTLE_VAMPIRE: - text_w[i].reset(8, 255, 615, 383, false, false); - menu_w[i].reset(448, 11, 615, game_id.language == ENGLISH ? 234 : 224, false, true); - break; - case GameId::YAKATA: - text_w[i].reset(48, 288, 594, 393, false, false); - menu_w[i].reset(452, 14, 627, 214, false, true); - break; - case GameId::DALK_HINT: - text_w[i].reset(24, 308, 376, 386, false, false); - menu_w[i].reset(404, 28, 604, 244, true, true); - break; - case GameId::RANCE3_HINT: - text_w[i].reset(104, 304, 615, 383, false, false); - menu_w[i].reset(464, 24, 623, 200, true, true); - break; - case GameId::YAKATA2: - text_w[i].reset(104, 304, 620, 382, false, false); - menu_w[i].reset(420, 28, 620, 244, true, true); - break; - case GameId::GAKUEN_SENKI: - text_w[i].reset(8, 260, 505, 384, false, false); - if (i == 1) { - menu_w[i].reset(128, 32, 337, 178, true, true); - } else { - menu_w[i].reset(288, 30, 433, 210, true, true); - } - break; - case GameId::GAKUEN_KING: - switch (i) { - default: - case 0: text_w[i].reset(112, 310, 623, 391, false, false); break; - case 1: text_w[i].reset(48, 310, 591, 391, false, false); break; - case 2: text_w[i].reset(0, 0, 639, 391, false, false); break; - case 3: text_w[i].reset(24, 310, 183, 391, false, false); break; - case 4: text_w[i].reset(464, 310, 623, 391, false, false); break; - } - switch (i) { - default: - case 0: menu_w[i].reset(416, 24, 591, 248, true, true); break; - case 1: menu_w[i].reset(32, 16, 191, 216, true, true); break; - case 2: menu_w[i].reset(448, 16, 607, 216, true, true); break; - case 3: menu_w[i].reset(32, 223, 191, 383, true, true); break; - case 4: menu_w[i].reset(448, 223, 607, 383, true, true); break; - } - break; - default: - text_w[i].reset(8, 311, 623, 391, true, false); - menu_w[i].reset(464, 80, 623, 240, true, true); - break; - } - } -} - -void AGS::clear_text_window(int index, bool erase) -{ - Window& w = text_w[index - 1]; - - if(erase) { - if (w.frame && fade_level) { - draw_window(w.sx - 8, w.sy - 8, w.ex + 8, w.ey + 8, true, text.frame_color, text.back_color); - } else { - box_fill(0, w.sx, w.sy, w.ex, w.ey, text.back_color); - } - } - - text.reset_pos(w.sx, w.sy + 2); -} - -bool AGS::return_text_line(int index) -{ - text.newline(); - // Return true if the next line exceeds the bottom of the window - return text.pos.y + text.font_size > text_w[index - 1].ey; -} - -void AGS::draw_push(int index) -{ - static const uint8_t builtin_bitmap[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, - 0x90, 0x00, 0x99, 0x74, 0xe9, 0x44, 0x89, 0x77, - 0x89, 0x15, 0x87, 0x75, 0x00, 0x00, 0xff, 0xff - }; - - Window& w = text_w[index - 1]; - int x = w.ex - 16; - int y = w.ey - 16; - draw_gaiji(0, x, y, push_bitmap ? push_bitmap : builtin_bitmap, 16, text.font_color); - invalidate_screen(x, y, 16, 16); -} - -void AGS::open_text_window(int index, bool erase) -{ - Window& w = text_w[index - 1]; - int sx = w.sx - (w.frame ? 8 : 0); - int sy = w.sy - (w.frame ? 8 : 0); - int ex = w.ex + (w.frame ? 8 : 0); - int ey = w.ey + (w.frame ? 8 : 0); - int width = ex - sx + 1; - int height = ey - sy + 1; - - if (game_id.is(GameId::PROG_CD) && !erase) { - // prostudent G オープニング画面化け対策 - w.screen = CG(); - w.window = CG(); - } - - // 画面退避 - if (w.save) { - w.screen = CG(sx, sy, width, height); - SDL_SetPaletteColors(w.screen.palette(), screen_palette->colors, 0, 256); - - SDL_Rect rect = {sx, sy, width, height}; - SDL_BlitSurface(hBmpScreen[0], &rect, w.screen.surface(), NULL); - } - - if(erase) { - // 窓初期化 - draw_window(sx, sy, ex, ey, w.frame, text.frame_color, text.back_color); - } else if (w.save && w.window) { - // 窓復帰 - sx = w.window.x; - sy = w.window.y; - width = w.window.width(); - height = w.window.height(); - - SDL_SetSurfacePalette(hBmpScreen[0], w.window.palette()); - SDL_Rect rect = {sx, sy, width, height}; - SDL_BlitSurface(w.window.surface(), NULL, hBmpScreen[0], &rect); - invalidate_screen(sx, sy, width, height); - SDL_SetSurfacePalette(hBmpScreen[0], screen_palette); - } - - text.reset_pos(w.sx, w.sy + text.line_space); -} - -void AGS::close_text_window(int index, bool update) -{ - Window& w = text_w[index - 1]; - int sx = w.sx - (w.frame ? 8 : 0); - int sy = w.sy - (w.frame ? 8 : 0); - int ex = w.ex + (w.frame ? 8 : 0); - int ey = w.ey + (w.frame ? 8 : 0); - int width = ex - sx + 1; - int height = ey - sy + 1; - - // 窓退避 - if (w.save) { - w.window = CG(sx, sy, width, height); - SDL_SetPaletteColors(w.window.palette(), screen_palette->colors, 0, 256); - - SDL_Rect rect = {sx, sy, width, height}; - SDL_BlitSurface(hBmpScreen[0], &rect, w.window.surface(), NULL); - } - - // 画面復帰 - if (w.save && w.screen) { - sx = w.screen.x; - sy = w.screen.y; - width = w.screen.width(); - height = w.screen.height(); - - SDL_SetSurfacePalette(hBmpScreen[0], w.screen.palette()); - SDL_Rect rect = {sx, sy, width, height}; - SDL_BlitSurface(w.screen.surface(), NULL, hBmpScreen[0], &rect); - invalidate_screen(sx, sy, width, height); - SDL_SetSurfacePalette(hBmpScreen[0], screen_palette); - } - - if(update) { - text.reset_pos(w.sx, w.sy + text.line_space); - } -} - -void AGS::clear_menu_window() -{ - SDL_Rect rect = {0, 0, 640, 480}; - SDL_FillRect(hBmpScreen[2], &rect, menu.back_color); - menu.reset_pos(2, 2); -} - -void AGS::open_menu_window(int index) -{ - Window &w = menu_w[index - 1]; - int sx = w.sx; - int sy = w.sy; - int ex = w.ex; - int ey = menu_fix ? w.ey : sy + menu.pos.y - 3; - int width = ex - sx + 1; - int height = ey - sy + 1; - int wsx = sx - (w.frame ? 8 : 0); - int wsy = sy - (w.frame ? 8 : 0); - int wex = ex + (w.frame ? 8 : 0); - int wey = ey + (w.frame ? 8 : 0); - int wwidth = wex - wsx + 1; - int wheight = wey - wsy + 1; - - // 画面退避 - if (w.save) { - w.screen = CG(wsx, wsy, wwidth, wheight); - SDL_SetPaletteColors(w.screen.palette(), screen_palette->colors, 0, 256); - - SDL_Rect rect = {wsx, wsy, wwidth, wheight}; - SDL_BlitSurface(hBmpScreen[0], &rect, w.screen.surface(), NULL); - } - - // メニュー表示 - draw_window(wsx, wsy, wex, wey, w.frame, menu.frame_color, menu.back_color); - SDL_Rect src_rect = {0, 0, width, height}; - SDL_Rect dst_rect = {sx, sy, width, height}; - SDL_BlitSurface(hBmpScreen[2], &src_rect, hBmpScreen[0], &dst_rect); - box_line(0, sx, sy, ex, sy + menu.font_size + 3, menu.frame_color); - invalidate_screen(wsx, wsy, wwidth, wheight); -} - -void AGS::redraw_menu_window(int index, int selected) -{ - Window &w = menu_w[index - 1]; - int sx = w.sx; - int sy = w.sy; - int ex = w.ex; - int ey = menu_fix ? w.ey : sy + menu.pos.y - 3; - int width = ex - sx + 1; - int height = ey - sy + 1; - - SDL_Rect src_rect = {0, 0, width, height}; - SDL_Rect dst_rect = {sx, sy, width, height}; - SDL_BlitSurface(hBmpScreen[2], &src_rect, hBmpScreen[0], &dst_rect); - box_line(0, sx, sy + (menu.font_size + 4) * selected, ex, sy + (menu.font_size + 4) * (selected + 1) - 1, menu.frame_color); - invalidate_screen(sx, sy, width, height); -} - -void AGS::close_menu_window(int index) -{ - Window &w = menu_w[index - 1]; - // 画面復帰 - if (w.save && w.screen) { - SDL_SetSurfacePalette(hBmpScreen[0], w.screen.palette()); - SDL_Rect rect = {w.screen.x, w.screen.y, w.screen.width(), w.screen.height()}; - SDL_BlitSurface(w.screen.surface(), NULL, hBmpScreen[0], &rect); - invalidate_screen(w.screen.x, w.screen.y, w.screen.width(), w.screen.height()); - SDL_SetSurfacePalette(hBmpScreen[0], screen_palette); - } -} - -void AGS::get_menu_window_rect(int index, int* sx, int* sy, int* ex, int* ey) -{ - Window &w = menu_w[index - 1]; - if (sx) *sx = w.sx; - if (sy) *sy = w.sy; - if (ex) *ex = w.ex; - if (ey) *ey = w.ey; -} - -void AGS::draw_window(int sx, int sy, int ex, int ey, bool frame, uint8 frame_color, uint8 back_color) -{ - SDL_Rect rect = {sx, sy, ex - sx + 1, ey - sy + 1}; - SDL_FillRect(hBmpScreen[0], &rect, back_color); - - if(frame) { - SDL_Rect rects[] = { - {sx + 1, sy + 1, ex - sx - 1, 2}, - {sx + 1, ey - 2, ex - sx - 1, 2}, - {sx + 1, sy + 1, 2, ey - sy - 1}, - {ex - 2, sy + 1, 2, ey - sy - 1}, - }; - SDL_FillRects(hBmpScreen[0], rects, 4, frame_color); - box_line(0, sx + 4, sy + 4, ex - 4, ey - 4, frame_color); - } - invalidate_screen(sx, sy, ex - sx + 1, ey - sy + 1); -} diff --git a/src/sys/cg.h b/src/sys/cg.h new file mode 100644 index 0000000..193fd9e --- /dev/null +++ b/src/sys/cg.h @@ -0,0 +1,42 @@ +/* + ALICE SOFT SYSTEM 3 for Win32 + + [ CG ] +*/ + +#ifndef _CG_H_ +#define _CG_H_ + +#include +#include +#include "common.h" + +inline uint8_t* surface_line(SDL_Surface* surface, int y) +{ + return static_cast(surface->pixels) + surface->pitch * y; +} + +struct SurfaceDeleter { + void operator()(SDL_Surface* s) const noexcept { + if (s) SDL_FreeSurface(s); + } +}; + +struct CG { + std::unique_ptr surface_; + int x; + int y; + + CG() = default; + CG(int x, int y, int width, int height) + : surface_(SDL_CreateRGBSurfaceWithFormat(0, width, height, 8, SDL_PIXELFORMAT_INDEX8)), + x(x), y(y) {} + explicit operator bool() const noexcept { return static_cast(surface_); } + + SDL_Surface* surface() const { return surface_.get(); } + int width() const { return surface_->w; } + int height() const { return surface_->h; } + SDL_Palette* palette() const { return surface_->format->palette; } +}; + +#endif // _CG_H_ diff --git a/src/sys/nact.cpp b/src/sys/nact.cpp index 270686e..425def5 100644 --- a/src/sys/nact.cpp +++ b/src/sys/nact.cpp @@ -70,6 +70,9 @@ NACT::NACT(const Config& config, const GameId& game_id) mako = new MAKO(config, game_id); msgskip = new MsgSkip(); + init_windows(); + ags->text.reset_pos(text_w[0].sx, text_w[0].sy + 2); + SDL_Init(SDL_INIT_GAMECONTROLLER); for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { @@ -330,7 +333,7 @@ void NACT::cmd_set_menu() TRACE("$"); } else { if (menu_items.empty()) { - ags->clear_menu_window(); + clear_menu_window(); } menu_items.emplace_back(sco.getw()); ags->draw_menu = true; @@ -408,7 +411,7 @@ void NACT::cmd_a() if (msgskip->get_flags() & MSGSKIP_STOP_ON_CLICK && get_key()) msgskip->activate(false); } else if (show_push) { - ags->draw_push(text_window); + draw_push(text_window); } get_wheel(); // clear wheel input @@ -429,7 +432,7 @@ void NACT::cmd_a() } // ウィンドウ更新 - ags->clear_text_window(text_window, true); + clear_text_window(text_window, true); if (game_id.is_system1_dps()) { text_refresh = true; @@ -449,8 +452,8 @@ void NACT::cmd_r() TRACE("R"); - // ウィンドウの表示範囲外の場合は改ページ - if(ags->return_text_line(text_window)) { + // If the text is outside the window, do a page break + if (return_text_line(text_window)) { cmd_a(); } } @@ -612,11 +615,11 @@ int NACT::menu_select(int num_items) msgskip->activate(false); // メニュー表示 - ags->open_menu_window(menu_window); + open_menu_window(menu_window); // マウス移動 int sx, sy, ex; - ags->get_menu_window_rect(menu_window, &sx, &sy, &ex, nullptr); + get_menu_window_rect(menu_window, &sx, &sy, &ex, nullptr); int mx = ex - 16; int my = sy + 10; int height = ags->menu.font_size + 4; @@ -654,7 +657,7 @@ int NACT::menu_select(int num_items) int index = (my - sy) / height; if(sx <= mx && mx <= ex && 0 <= index && index < num_items) { current_index = index; - ags->redraw_menu_window(menu_window, current_index); + redraw_menu_window(menu_window, current_index); selectable = true; } else { selectable = false; @@ -669,7 +672,7 @@ int NACT::menu_select(int num_items) } else if(val == 8) { current_index = num_items - 1; } - ags->redraw_menu_window(menu_window, current_index); + redraw_menu_window(menu_window, current_index); selectable = true; } else if(val == 16 && selectable) { break; @@ -680,9 +683,9 @@ int NACT::menu_select(int num_items) } // 画面更新 - ags->close_menu_window(menu_window); + close_menu_window(menu_window); if(clear_text) { - ags->clear_text_window(text_window, true); + clear_text_window(text_window, true); } return current_index; diff --git a/src/sys/nact.h b/src/sys/nact.h index fcb6890..31bdc13 100644 --- a/src/sys/nact.h +++ b/src/sys/nact.h @@ -14,6 +14,7 @@ #include #include "common.h" #include "config.h" +#include "cg.h" #include "dri.h" #include "game_id.h" #include "scenario.h" @@ -136,6 +137,38 @@ protected: uint8_t cg_flags; void load_cg(int page, int transparent); + // Window (B command) + struct Window { + int sx; + int sy; + int ex; + int ey; + bool frame; + bool save; + CG screen; + CG window; + + void reset(int sx, int sy, int ex, int ey, bool frame, bool save); + }; + Window menu_w[10]; + Window text_w[10]; + bool menu_fix = false; + const uint8_t* push_bitmap = nullptr; + void init_windows(); + + void clear_text_window(int index, bool erase); + bool return_text_line(int index); + void draw_push(int index); + void open_text_window(int index, bool erase); + void close_text_window(int index, bool update); + + void clear_menu_window(); + void open_menu_window(int index); + void redraw_menu_window(int index, int selected); + void close_menu_window(int index); + void get_menu_window_rect(int index, int* sx, int* sy, int* ex, int* ey); + int calculate_menu_max(int window); + struct MenuItem { uint16_t addr; uint8_t verb; diff --git a/src/sys/nact_gakuenking.cpp b/src/sys/nact_gakuenking.cpp index 0a96a3e..d4cf048 100644 --- a/src/sys/nact_gakuenking.cpp +++ b/src/sys/nact_gakuenking.cpp @@ -99,7 +99,7 @@ private: ags->menu.back_color = ags->text.back_color = (param == 1 || param == 2) ? 0 : 1; text_window = param + 1; - ags->open_text_window(text_window, false); + open_text_window(text_window, false); } break; case 26: @@ -1368,16 +1368,16 @@ private: static constexpr int PUSH_BITMAP_OFFSET = 0x08aa; static constexpr int NR_PUSH_BITMAPS = 4; - uint8_t push_bitmap[NR_PUSH_BITMAPS][32] = {}; + uint8_t push_bitmaps[NR_PUSH_BITMAPS][32] = {}; void load_push_bitmaps(const std::vector& exe) { - memcpy(push_bitmap, &exe[PUSH_BITMAP_OFFSET], sizeof(push_bitmap)); + memcpy(push_bitmaps, &exe[PUSH_BITMAP_OFFSET], sizeof(push_bitmaps)); } void set_push_bitmap() { int r = random(100); r = r < NR_PUSH_BITMAPS ? r : 0; - ags->set_push_bitmap(fixed_shapes ? nullptr : push_bitmap[r]); + push_bitmap = fixed_shapes ? nullptr : push_bitmaps[r]; } // --- I grid selection ------------------------------------------------- diff --git a/src/sys/nact_sys1.cpp b/src/sys/nact_sys1.cpp index 874444f..88ab966 100644 --- a/src/sys/nact_sys1.cpp +++ b/src/sys/nact_sys1.cpp @@ -181,7 +181,7 @@ void NACT_Sys1::cmd_open_verb() if (game_id.is(GameId::GAKUEN_SENKI)) menu_window = 1; - int menu_max = ags->calculate_menu_max(menu_window); + int menu_max = calculate_menu_max(menu_window); for (const MenuItem& item : menu_items) { chk[item.verb] = true; @@ -192,7 +192,7 @@ top: // メニュー項目の準備 int id[32], index = 0; - ags->clear_menu_window(); + clear_menu_window(); ags->draw_menu = true; if(cnt <= menu_max) { @@ -245,7 +245,7 @@ top2: void NACT_Sys1::cmd_open_obj(int verb) { - int menu_max = ags->calculate_menu_max(menu_window); + int menu_max = calculate_menu_max(menu_window); // 目的語メニューの表示 verb_obj = false; @@ -274,7 +274,7 @@ top: // メニュー項目の準備 int id[32], index = 0; - ags->clear_menu_window(); + clear_menu_window(); ags->draw_menu = true; if(cnt <= menu_max - 1) { @@ -408,7 +408,7 @@ void NACT_Sys1::exec_y(int cmd, int param) { switch(cmd) { case 0: - ags->clear_text_window(text_window, true); + clear_text_window(text_window, true); break; case 1: { @@ -728,11 +728,11 @@ public: switch (cmd) { case 0: - ags->clear_text_window(text_window, true); + clear_text_window(text_window, true); break; case 1: if (show_push) { - ags->draw_push(text_window); + draw_push(text_window); } for (;;) { if (terminate) { @@ -745,7 +745,7 @@ public: } sys_sleep(100); wait_key_release(0x1f); - ags->clear_text_window(text_window, true); + clear_text_window(text_window, true); break; case 2: if (param == 0) { diff --git a/src/sys/nact_sys2.cpp b/src/sys/nact_sys2.cpp index 8bf917d..6e6155b 100644 --- a/src/sys/nact_sys2.cpp +++ b/src/sys/nact_sys2.cpp @@ -274,7 +274,7 @@ void NACT_Sys2::cmd_open_verb() // メニュー項目の準備 int id[32], index = 0; - ags->clear_menu_window(); + clear_menu_window(); ags->draw_menu = true; for(int i = 0; i < MAX_VERB; i++) { @@ -325,7 +325,7 @@ void NACT_Sys2::cmd_open_obj(int verb) // メニュー項目の準備 int id[32], index = 0; - ags->clear_menu_window(); + clear_menu_window(); ags->draw_menu = true; for(int i = 0; i < MAX_OBJ; i++) { @@ -369,7 +369,7 @@ void NACT_Sys2::cmd_b() if (game_id.is(GameId::AYUMI_FD) || game_id.is(GameId::AYUMI_HINT) || game_id.is(GameId::DRSTOP)) { p5 = 1; } - ags->set_menu_window(index, p1 * 8, p2, p3 * 8 - 1, p4, p5); + menu_w[index - 1].reset(p1 * 8, p2, p3 * 8 - 1, p4, menu_w[index - 1].frame, p5); break; case 2: if (game_id.is(GameId::AYUMI_FD) || game_id.is(GameId::AYUMI_HINT) || game_id.is(GameId::DRSTOP)) { @@ -378,11 +378,11 @@ void NACT_Sys2::cmd_b() // if (game_id.is(GameId::PROG_FD)) { // p1 = (index == 1 || index == 3); // } - ags->set_menu_window_frame(index, p1); + menu_w[index - 1].frame = p1; menu_window = index; break; case 3: - ags->set_text_window(index, p1 * 8, p2, p3 * 8 - 1, p4, p5); + text_w[index - 1].reset(p1 * 8, p2, p3 * 8 - 1, p4, text_w[index - 1].frame, p5); break; case 4: if (game_id.is(GameId::AYUMI_FD) || game_id.is(GameId::AYUMI_HINT)) { @@ -397,12 +397,12 @@ void NACT_Sys2::cmd_b() // if (game_id.is(GameId::PROG_FD)) { // p1 = (index == 1 || index == 3); // } else - ags->set_text_window_frame(index, p1); - ags->open_text_window(index, p4 ? false : true); + text_w[index - 1].frame = p1; + open_text_window(index, p4 ? false : true); text_window = index; } else { // ウィンドウ復帰 - ags->close_text_window(index, text_window == index ? true : false); + close_text_window(index, text_window == index ? true : false); } break; default: @@ -693,7 +693,7 @@ void NACT_Sys2::cmd_y() switch(cmd) { case 1: - ags->clear_text_window(text_window, (param == 0) ? true : false); + clear_text_window(text_window, (param == 0) ? true : false); break; case 2: if(param == 0) { diff --git a/src/sys/nact_sys3.cpp b/src/sys/nact_sys3.cpp index b537732..c3b7d8e 100644 --- a/src/sys/nact_sys3.cpp +++ b/src/sys/nact_sys3.cpp @@ -104,7 +104,7 @@ void NACT_Sys3::cmd_open_verb() // メニュー項目の準備 int id[32], index = 0; - ags->clear_menu_window(); + clear_menu_window(); ags->draw_menu = true; for(int i = 0; i < MAX_VERB; i++) { @@ -155,7 +155,7 @@ void NACT_Sys3::cmd_open_obj(int verb) // メニュー項目の準備 int id[32], index = 0; - ags->clear_menu_window(); + clear_menu_window(); ags->draw_menu = true; for(int i = 0; i < MAX_OBJ; i++) { @@ -200,24 +200,26 @@ void NACT_Sys3::cmd_b() if (game_id.is(GameId::AYUMI_CD) || game_id.is(GameId::AYUMI_LIVE_256) || game_id.is(GameId::AYUMI_LIVE_FULL)) { p5 = 1; } - ags->set_menu_window(index, column ? p1 * 8 : p1 & 0xfff8, p2, column ? p3 * 8 - 1 : (p3 & 0xfff8) - 1, p4, p5); + menu_w[index - 1].reset(column ? p1 * 8 : p1 & 0xfff8, p2, column ? p3 * 8 - 1 : (p3 & 0xfff8) - 1, p4, + menu_w[index - 1].frame, p5); break; case 2: - ags->set_menu_window_frame(index, p1); + menu_w[index - 1].frame = p1; menu_window = index; break; case 3: - ags->set_text_window(index, column ? p1 * 8 : p1 & 0xfff8, p2, column ? p3 * 8 - 1 : (p3 & 0xfff8) - 1, p4, p5); + text_w[index - 1].reset(column ? p1 * 8 : p1 & 0xfff8, p2, column ? p3 * 8 - 1 : (p3 & 0xfff8) - 1, p4, + text_w[index - 1].frame, p5); break; case 4: if(p5 == 0) { // ウィンドウ退避 - ags->set_text_window_frame(index, p1); - ags->open_text_window(index, p4 ? false : true); + text_w[index - 1].frame = p1; + open_text_window(index, p4 ? false : true); text_window = index; } else { // ウィンドウ復帰 - ags->close_text_window(index, text_window == index ? true : false); + close_text_window(index, text_window == index ? true : false); } break; default: @@ -673,7 +675,7 @@ void NACT_Sys3::exec_y(int cmd, int param) { switch(cmd) { case 1: - ags->clear_text_window(text_window, (param == 0) ? true : false); + clear_text_window(text_window, (param == 0) ? true : false); break; case 2: if(param == 0) { @@ -932,7 +934,7 @@ void NACT_Sys3::exec_y(int cmd, int param) case 233: break; case 234: - ags->menu_fix = param ? true : false; + menu_fix = param ? true : false; break; case 235: break; diff --git a/src/sys/nact_window.cpp b/src/sys/nact_window.cpp index a3b743e..efa1629 100644 --- a/src/sys/nact_window.cpp +++ b/src/sys/nact_window.cpp @@ -8,6 +8,283 @@ #include "ags.h" #include "fileio.h" +void NACT::Window::reset(int sx, int sy, int ex, int ey, bool frame, bool save) { + this->sx = sx; + this->sy = sy; + this->ex = ex; + this->ey = ey; + this->frame = frame; + this->save = save; + screen.surface_.reset(); + window.surface_.reset(); +} + +void NACT::init_windows() +{ + for (int i = 0; i < 10; i++) { + // ウィンドウの初期位置はシステムによって異なる + switch (game_id.game) { + case GameId::BUNKASAI: + text_w[i].reset(24, 304, 616, 384, false, false); + menu_w[i].reset(440, 18, 620, 178, true, true); + break; + case GameId::CRESCENT: + text_w[i].reset(24, 288, 616, 378, false, false); + // 本来は横メニュー + menu_w[i].reset(464, 50, 623, 240, true, true); + break; + case GameId::RANCE2: + case GameId::RANCE2_HINT: + text_w[i].reset(8, 285, 502, 396, false, false); + menu_w[i].reset(431, 19, 624, 181, false, true); + break; + case GameId::DPS: + case GameId::DPS_SG_FAHREN: + case GameId::DPS_SG_KATEI: + case GameId::DPS_SG_NOBUNAGA: + case GameId::DPS_SG2_ANTIQUE: + case GameId::DPS_SG2_IKENAI: + case GameId::DPS_SG2_AKAI: + case GameId::DPS_SG3_RABBIT: + case GameId::DPS_SG3_SHINKON: + case GameId::DPS_SG3_SOTSUGYOU: + text_w[i].reset(48, 288, 594, 393, false, false); + //menu_w[i].reset(48, 288, 584, 393, false, true); + menu_w[i].reset(48, 288, 594, 393, false, true); + break; + case GameId::FUKEI: + text_w[i].reset(44, 282, 593, 396, false, false); + menu_w[i].reset(460, 14, 635, 214, false, true); + break; + case GameId::INTRUDER: + text_w[i].reset(8, 280, 629, 393, false, false); + menu_w[i].reset(448, 136, 623, 340, true, true); + break; + case GameId::TENGU: + text_w[i].reset(44, 282, 593, 396, false, false); + menu_w[i].reset(452, 14, 627, 214, false, true); + break; + case GameId::TOUSHIN_HINT: + text_w[i].reset(8, 311, 623, 391, false, false); + menu_w[i].reset(452, 14, 627, 214, true, true); + break; + case GameId::LITTLE_VAMPIRE: + text_w[i].reset(8, 255, 615, 383, false, false); + menu_w[i].reset(448, 11, 615, game_id.language == ENGLISH ? 234 : 224, false, true); + break; + case GameId::YAKATA: + text_w[i].reset(48, 288, 594, 393, false, false); + menu_w[i].reset(452, 14, 627, 214, false, true); + break; + case GameId::DALK_HINT: + text_w[i].reset(24, 308, 376, 386, false, false); + menu_w[i].reset(404, 28, 604, 244, true, true); + break; + case GameId::RANCE3_HINT: + text_w[i].reset(104, 304, 615, 383, false, false); + menu_w[i].reset(464, 24, 623, 200, true, true); + break; + case GameId::YAKATA2: + text_w[i].reset(104, 304, 620, 382, false, false); + menu_w[i].reset(420, 28, 620, 244, true, true); + break; + case GameId::GAKUEN_SENKI: + text_w[i].reset(8, 260, 505, 384, false, false); + if (i == 1) { + menu_w[i].reset(128, 32, 337, 178, true, true); + } else { + menu_w[i].reset(288, 30, 433, 210, true, true); + } + break; + case GameId::GAKUEN_KING: + switch (i) { + default: + case 0: text_w[i].reset(112, 310, 623, 391, false, false); break; + case 1: text_w[i].reset(48, 310, 591, 391, false, false); break; + case 2: text_w[i].reset(0, 0, 639, 391, false, false); break; + case 3: text_w[i].reset(24, 310, 183, 391, false, false); break; + case 4: text_w[i].reset(464, 310, 623, 391, false, false); break; + } + switch (i) { + default: + case 0: menu_w[i].reset(416, 24, 591, 248, true, true); break; + case 1: menu_w[i].reset(32, 16, 191, 216, true, true); break; + case 2: menu_w[i].reset(448, 16, 607, 216, true, true); break; + case 3: menu_w[i].reset(32, 223, 191, 383, true, true); break; + case 4: menu_w[i].reset(448, 223, 607, 383, true, true); break; + } + break; + default: + text_w[i].reset(8, 311, 623, 391, true, false); + menu_w[i].reset(464, 80, 623, 240, true, true); + break; + } + } +} + +void NACT::clear_text_window(int index, bool erase) +{ + Window& w = text_w[index - 1]; + + if (erase) { + if (w.frame && ags->is_faded()) { + ags->draw_window(w.sx - 8, w.sy - 8, w.ex + 8, w.ey + 8, true, ags->text.frame_color, ags->text.back_color); + } else { + ags->box_fill(0, w.sx, w.sy, w.ex, w.ey, ags->text.back_color); + } + } + + ags->text.reset_pos(w.sx, w.sy + 2); +} + +bool NACT::return_text_line(int index) +{ + ags->text.newline(); + // Return true if the next line exceeds the bottom of the window + return ags->text.pos.y + ags->text.font_size > text_w[index - 1].ey; +} + +void NACT::draw_push(int index) +{ + static const uint8_t builtin_bitmap[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, + 0x90, 0x00, 0x99, 0x74, 0xe9, 0x44, 0x89, 0x77, + 0x89, 0x15, 0x87, 0x75, 0x00, 0x00, 0xff, 0xff + }; + + Window& w = text_w[index - 1]; + int x = w.ex - 16; + int y = w.ey - 16; + ags->draw_gaiji(0, x, y, push_bitmap ? push_bitmap : builtin_bitmap, 16, ags->text.font_color); + ags->invalidate_screen(x, y, 16, 16); +} + +void NACT::open_text_window(int index, bool erase) +{ + Window& w = text_w[index - 1]; + int sx = w.sx - (w.frame ? 8 : 0); + int sy = w.sy - (w.frame ? 8 : 0); + int ex = w.ex + (w.frame ? 8 : 0); + int ey = w.ey + (w.frame ? 8 : 0); + int width = ex - sx + 1; + int height = ey - sy + 1; + + if (game_id.is(GameId::PROG_CD) && !erase) { + // prostudent G オープニング画面化け対策 + w.screen = CG(); + w.window = CG(); + } + + if (w.save) { + w.screen = ags->save_rect(sx, sy, width, height); + } + + if (erase) { + ags->draw_window(sx, sy, ex, ey, w.frame, ags->text.frame_color, ags->text.back_color); + } else if (w.save && w.window) { + ags->restore_rect(w.window); + } + + ags->text.reset_pos(w.sx, w.sy + ags->text.line_space); +} + +void NACT::close_text_window(int index, bool update) +{ + Window& w = text_w[index - 1]; + int sx = w.sx - (w.frame ? 8 : 0); + int sy = w.sy - (w.frame ? 8 : 0); + int ex = w.ex + (w.frame ? 8 : 0); + int ey = w.ey + (w.frame ? 8 : 0); + int width = ex - sx + 1; + int height = ey - sy + 1; + + if (w.save) { + w.window = ags->save_rect(sx, sy, width, height); + } + + if (w.save && w.screen) { + ags->restore_rect(w.screen); + } + + if (update) { + ags->text.reset_pos(w.sx, w.sy + ags->text.line_space); + } +} + +void NACT::clear_menu_window() +{ + ags->box_fill(2, 0, 0, 639, 479, ags->menu.back_color); + ags->menu.reset_pos(2, 2); +} + +void NACT::open_menu_window(int index) +{ + Window &w = menu_w[index - 1]; + int sx = w.sx; + int sy = w.sy; + int ex = w.ex; + int ey = menu_fix ? w.ey : sy + ags->menu.pos.y - 3; + int width = ex - sx + 1; + int height = ey - sy + 1; + int wsx = sx - (w.frame ? 8 : 0); + int wsy = sy - (w.frame ? 8 : 0); + int wex = ex + (w.frame ? 8 : 0); + int wey = ey + (w.frame ? 8 : 0); + int wwidth = wex - wsx + 1; + int wheight = wey - wsy + 1; + + if (w.save) { + w.screen = ags->save_rect(wsx, wsy, wwidth, wheight); + } + + ags->draw_window(wsx, wsy, wex, wey, w.frame, ags->menu.frame_color, ags->menu.back_color); + ags->copy_screen(2, 0, 0, 0, width - 1, height - 1, sx, sy); + ags->box_line(0, sx, sy, ex, sy + ags->menu.font_size + 3, ags->menu.frame_color); + ags->invalidate_screen(wsx, wsy, wwidth, wheight); +} + +void NACT::redraw_menu_window(int index, int selected) +{ + Window &w = menu_w[index - 1]; + int sx = w.sx; + int sy = w.sy; + int ex = w.ex; + int ey = menu_fix ? w.ey : sy + ags->menu.pos.y - 3; + int width = ex - sx + 1; + int height = ey - sy + 1; + + ags->copy_screen(2, 0, 0, 0, width - 1, height - 1, sx, sy); + ags->box_line(0, sx, sy + (ags->menu.font_size + 4) * selected, ex, sy + (ags->menu.font_size + 4) * (selected + 1) - 1, ags->menu.frame_color); + ags->invalidate_screen(sx, sy, width, height); +} + +void NACT::close_menu_window(int index) +{ + Window &w = menu_w[index - 1]; + if (w.save && w.screen) { + ags->restore_rect(w.screen); + } +} + +void NACT::get_menu_window_rect(int index, int* sx, int* sy, int* ex, int* ey) +{ + Window &w = menu_w[index - 1]; + if (sx) *sx = w.sx; + if (sy) *sy = w.sy; + if (ex) *ex = w.ex; + if (ey) *ey = w.ey; +} + +int NACT::calculate_menu_max(int window) +{ + if (game_id.is(GameId::INTRUDER)) + return 6; + if (game_id.is(GameId::GAKUEN_SENKI)) + return (menu_w[window - 1].ey - menu_w[window - 1].sy) / (ags->menu.font_size + 4); + return 11; +} + void NACT::load_display_state(FILEIO* fio) { ags->menu.font_size = fio->getw(); @@ -29,7 +306,7 @@ void NACT::load_display_state(FILEIO* fio) int ey = fio->getw(); bool save = fio->getw() ? true : false; bool frame = fio->getw() ? true : false; - ags->menu_w[i].reset(sx, sy, ex, ey, frame, save); + menu_w[i].reset(sx, sy, ex, ey, frame, save); fio->getw(); fio->getw(); } @@ -40,7 +317,7 @@ void NACT::load_display_state(FILEIO* fio) int ey = fio->getw(); bool save = fio->getw() ? true : false; bool frame = fio->getw() ? true : false; - ags->text_w[i].reset(sx, sy, ex, ey, frame, save); + text_w[i].reset(sx, sy, ex, ey, frame, save); fio->getw(); fio->getw(); } @@ -58,22 +335,22 @@ void NACT::save_display_state(FILEIO* fio) fio->putw(ags->text.frame_color); fio->putw(ags->text.back_color); for (int i = 0; i < 10; i++) { - fio->putw(ags->menu_w[i].sx); - fio->putw(ags->menu_w[i].sy); - fio->putw(ags->menu_w[i].ex); - fio->putw(ags->menu_w[i].ey); - fio->putw(ags->menu_w[i].save ? 1 : 0); - fio->putw(ags->menu_w[i].frame ? 1 : 0); + fio->putw(menu_w[i].sx); + fio->putw(menu_w[i].sy); + fio->putw(menu_w[i].ex); + fio->putw(menu_w[i].ey); + fio->putw(menu_w[i].save ? 1 : 0); + fio->putw(menu_w[i].frame ? 1 : 0); fio->putw(0); fio->putw(0); } for (int i = 0; i < 10; i++) { - fio->putw(ags->text_w[i].sx); - fio->putw(ags->text_w[i].sy); - fio->putw(ags->text_w[i].ex); - fio->putw(ags->text_w[i].ey); - fio->putw(ags->text_w[i].save ? 1 : 0); - fio->putw(ags->text_w[i].frame ? 1 : 0); + fio->putw(text_w[i].sx); + fio->putw(text_w[i].sy); + fio->putw(text_w[i].ex); + fio->putw(text_w[i].ey); + fio->putw(text_w[i].save ? 1 : 0); + fio->putw(text_w[i].frame ? 1 : 0); fio->putw(0); fio->putw(0); }