#include "config.h" #include #include #include #include "portab.h" #include "system.h" #include "sdl_core.h" #include "utfsjis.h" #include "ags.h" #include "nact.h" #include "input.h" #include "ngraph.h" #include "drawtext.h" #include "night.h" #include "nt_msg.h" #include "sprite.h" #include "sactcg.h" #include "sactstring.h" extern int ntsel_dosel(void); #define SNAME_RYO "亮    " #define SNAME_RYO_DEF "亮" // 通常メッセージ表示位置と大きさ #define MSGFRAME_0_X 6 #define MSGFRAME_0_Y 347 #define MSGFRAME_0_WIDTH 628 #define MSGFRAME_0_HEIGHT 125 // 画面全体メッセージ表示位置と大きさ #define MSGFRAME_1_X 0 #define MSGFRAME_1_Y 0 #define MSGFRAME_1_WIDTH 640 #define MSGFRAME_1_HEIGHT 480 // 通常メッセージ(顔つき)表示位置と大きさ #define MSGFRAME_2_X (6+160) #define MSGFRAME_2_Y 347 #define MSGFRAME_2_WIDTH (628-160) #define MSGFRAME_2_HEIGHT 125 // キー入力アニメーション位置 #define HAKANIM_LOC_X 620 #define HAKANIM_LOC_Y 450 // キー入力アニメーション間隔 #define HAKANIM_INTERVAL 250 // デフォルトメッセージフォントサイズ #define MSG_DEFFONTSIZE 24 static void ntmsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace); static void set_align(char *msg, sprite_t *sp, int wSize); static uint8_t *get_char(uint8_t *msg, char *mbuf, int bufmax); static void cb_keyrelease(agsevent_t *e); static void cb_mousemove(agsevent_t *e); static void setup_hakanim(); static void hakanim(int i); static int ntmsg_keywait(); static void ntmsg_clear(int wNum); // 初期化 void ntmsg_init() { sprite_t *sp; // メッセージスプライト sp = night.sp[SPNO_MSGFRAME_FG] = nt_sp_msg_new(SPNO_MSGFRAME_FG, 0, 0, sf0->width, sf0->height); nt_sp_add_updatelist(sp); // メッセージ背景用CG sp = night.sp[SPNO_MSGFRAME_BG] = nt_sp_new(SPNO_MSGFRAME_BG, CGNO_MSGFRAME_LCG, 0, 0, SPRITE_NORMAL); nt_sp_add_updatelist(sp); // 文字背景CG作成 nt_scg_create(CGNO_MSGFR_BG, sf0->width, sf0->height, 0, 0, 0, 255); // night.cg[CGNO_MSGFR_BG]->refcnt++; // 文字背景CGのためのsprite sp = night.sp[SPNO_MSGBG] = nt_sp_new(SPNO_MSGBG, CGNO_MSGFR_BG, 0, 0, SPRITE_NORMAL); nt_sp_add_updatelist(sp); // キー入力アニメーションCG作成 nt_scg_cut(CGNO_MSGHAK_1, CGNO_MSGHAK_LCG, 0, 0, 9, 9); //night.cg[CGNO_MSGHAK_1]->refcnt++; nt_scg_cut(CGNO_MSGHAK_2, CGNO_MSGHAK_LCG, 0, 9, 9, 9); //night.cg[CGNO_MSGHAK_2]->refcnt++; sp = night.sp[SPNO_MSG_KEYANIM] = nt_sp_new(SPNO_MSG_KEYANIM, CGNO_MSGHAK_1, CGNO_MSGHAK_2, 0, SPRITE_ANIME); sp->u.anime.interval = HAKANIM_INTERVAL; nt_sp_add_updatelist(sp); nt_sp_set_loc(sp, HAKANIM_LOC_X, HAKANIM_LOC_Y); nt_sp_set_show(sp, FALSE); sp->update = nt_sp_draw_scg; // 主人公名前 nt_sstr_regist_replace(fromUTF8(SNAME_RYO), fromUTF8(SNAME_RYO_DEF)); night.msg.cbmove = cb_mousemove; night.msg.cbrelease = cb_keyrelease; ntmsg_set_frame(0); ntmsg_set_place(0); night.selmode = -1; } // メッセージ枠の種類 // type=0 消去 // type=1 枠あり // type=2 中央 void ntmsg_set_frame(int type) { surface_t *sf; night.msgframe = type; sf = night.sp[SPNO_MSGBG]->curcg->sf; memset(sf->pixel, 0, sf->bytes_per_line * sf->height); memset(sf->alpha, 0, sf->width * sf->height); switch(type) { case 0: nt_sp_set_show(night.sp[SPNO_MSGBG], FALSE); nt_sp_set_show(night.sp[SPNO_MSGFRAME_BG], FALSE); nt_sp_set_show(night.sp[SPNO_MSGFRAME_FG], FALSE); break; case 1: nt_sp_set_show(night.sp[SPNO_MSGBG], TRUE); nt_sp_set_show(night.sp[SPNO_MSGFRAME_BG], TRUE); nt_sp_set_show(night.sp[SPNO_MSGFRAME_FG], TRUE); gr_fill(sf, MSGFRAME_0_X, MSGFRAME_0_Y, MSGFRAME_0_WIDTH, MSGFRAME_0_HEIGHT, 16, 32, 64); gr_fill_alpha_map(sf, MSGFRAME_0_X, MSGFRAME_0_Y, MSGFRAME_0_WIDTH, MSGFRAME_0_HEIGHT, 192); ntmsg_clear(SPNO_MSGFRAME_FG); nt_sp_update_all(TRUE); break; case 2: nt_sp_set_show(night.sp[SPNO_MSGBG], TRUE); nt_sp_set_show(night.sp[SPNO_MSGFRAME_BG], FALSE); nt_sp_set_show(night.sp[SPNO_MSGFRAME_FG], TRUE); gr_fill(sf, MSGFRAME_1_X, MSGFRAME_1_Y, MSGFRAME_1_WIDTH, MSGFRAME_1_HEIGHT, 32, 32, 32); gr_fill_alpha_map(sf, MSGFRAME_1_X, MSGFRAME_1_Y, MSGFRAME_1_WIDTH, MSGFRAME_1_HEIGHT, 128); ntmsg_clear(SPNO_MSGFRAME_FG); nt_sp_update_all(TRUE); break; } } /* メッセージ領域の設定 type=0: 628x125+6+347 type=1: 640x480+0+0 type=2: (628-120)x125+(6+120)+347 */ void ntmsg_set_place(int type) { night.msgplace = type; } void ntmsg_newline() { uint8_t buf[2] = {'\n', '\0'}; ntmsg_add(buf); } void ntmsg_add(const char *msg) { int len; SACT_DEBUG("len = %d", strlen(msg)); if (msg[0] == '\0') return; len = MSGBUFMAX - (int)strlen(night.msgbuf); if (len < 0) { WARNING("buf shortage (%d)", len); return; } strncat(night.msgbuf, msg, len); night.msgbuf[MSGBUFMAX -1] = '\0'; } int ntmsg_ana(void) { int ret; if (night.selmode == 0) { // sel start ret = ntsel_dosel(); } else { // msg newpage ntmsg_out(SPNO_MSGFRAME_FG, night.fontsize, 255, 255, 255, night.fonttype, 0, 2); nt_sp_update_clipped(); ret = ntmsg_keywait(); // clear msgsprite ntmsg_clear(SPNO_MSGFRAME_FG); //nt_sp_update_clipped(); } night.selmode = -1; return ret; } static void ntmsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace) { char *msg; sprite_t *sp; boolean needupdate = FALSE; MyRectangle uparea = {0,0,0,0}; int savex; if (night.msgbuf[0] == '\0') return; // shortcut sp = night.sp[wNum]; msg = nt_sstr_replacestr(night.msgbuf); // 文字アラインメントの調整 set_align(msg, sp, wSize); savex = sp->u.msg.dspcur.x; while (*msg) { char mbuf[20]; int cw, delta, wcnt; wcnt = sdl_getTicks(); mbuf[0] = '\0'; msg = get_char(msg, mbuf, sizeof(mbuf) -1); if (mbuf[0] == '\n') { sp->u.msg.dspcur.x = savex; sp->u.msg.dspcur.y += (wSize + wLineSpace); continue; } dt_setfont(wFont, wSize); cw = dt_drawtext_col(sp->u.msg.canvas, sp->u.msg.dspcur.x, sp->u.msg.dspcur.y, mbuf, wColorR, wColorG, wColorB); needupdate = TRUE; if (wSpeed > 0) { nt_sp_updateme_part(sp, sp->u.msg.dspcur.x, sp->u.msg.dspcur.y, cw, wSize); nt_sp_update_clipped(); needupdate = FALSE; // keywait delta = sdl_getTicks() - wcnt; if (delta < wSpeed) { if (sys_keywait(wSpeed - delta, KEYWAIT_NONCANCELABLE)) { wSpeed = 0; } } } sp->u.msg.dspcur.x += cw; } // バッファリング中の文字のクリア night.msgbuf[0] = '\0'; // Waitなしの出力は最後にupdate if (needupdate) { uparea.w = sp->cursize.width; uparea.h = min(sp->cursize.height, uparea.y - sp->u.msg.dspcur.y + wLineSpace + wLineSpace); nt_sp_updateme_part(sp, uparea.x, uparea.y, uparea.w, uparea.h); } } /* メッセージキー入力待ち @param: none @return: 入力されたキー */ static int ntmsg_keywait() { int i = 0; if (night.waitskiplv > 0) { sys_getInputInfo(); return 0; } // アニメパターンの初期化 setup_hakanim(); night.waittype = KEYWAIT_MESSAGE; night.waitkey = -1; while (night.waitkey == -1 && !nact->is_quit) { int st = sdl_getTicks(); int interval = 25; if (!night.zhiding) { interval = night.sp[SPNO_MSG_KEYANIM]->u.anime.interval; hakanim(i++); } sys_keywait(interval - (sdl_getTicks() - st), KEYWAIT_NONCANCELABLE); } night.waittype = KEYWAIT_NONE; return night.waitkey; } static void set_align(char *msg, sprite_t *sp, int wSize) { int line, maxchar, c; switch(night.msgplace) { case 0: sp->u.msg.dspcur.x = MSGFRAME_0_X; sp->u.msg.dspcur.y = MSGFRAME_0_Y + 8; break; case 1: // もっとも長い行が中心にくるようにし、 // 他の行はその行の先頭に先頭をあわせる line = 0; maxchar = 0; c = 0; while (*msg) { if (*msg == '\n') { line++; maxchar = max(maxchar, c); c = 0; msg++; continue; } msg++; c++; } maxchar = max(maxchar, c); line++; sp->u.msg.dspcur.x = (sp->cursize.width - (maxchar * wSize/2)) /2; sp->u.msg.dspcur.y = (sp->cursize.height - (line * (wSize+2))) /2; break; case 2: sp->u.msg.dspcur.x = MSGFRAME_2_X; sp->u.msg.dspcur.y = MSGFRAME_2_Y + 8; break; } } static uint8_t *get_char(uint8_t *msg, char *mbuf, int bufmax) { if (msg[0] == '\n') { mbuf[0] = '\n'; mbuf[1] = '\0'; return msg +1; } uint8_t *p = advance_char(msg, nact->encoding); while (msg < p) *mbuf++ = *msg++; *mbuf = '\0'; return msg; } static void is_in_icon() { } static void cb_keyrelease(agsevent_t *e) { int x = e->d1, y = e->d2; switch (e->d3) { case AGSEVENT_BUTTON_LEFT: #if 0 if (is_in_icon()) { do_icon(x, y); break; } #endif // fall through case AGSEVENT_BUTTON_RIGHT: if (night.zhiding) { // unhide(); break; } night.waitkey = e->d3; break; } } static void cb_mousemove(agsevent_t *e) { int x = e->d1, y = e->d2; // 音声mute/メッセージスキップ/メッセージ枠消去の領域に // マウスが移動したら、その部分のアイコンを変化させる } // メッセージ表示時に、キー入力を促すアニメーションの設定 static void setup_hakanim() { } // メッセージ表示時のキー入力を促すアニメーション static void hakanim(int i) { sprite_t *sp; boolean show; sp = night.sp[SPNO_MSG_KEYANIM]; if (sp == NULL) return; show = sp->show; if (i%2) { sp->curcg = sp->cg1; } else { sp->curcg = sp->cg2; } sp->show = TRUE; nt_sp_updateme(sp); nt_sp_update_clipped(); sp->show = show; } int ntmsg_update(sprite_t *sp, MyRectangle *r) { int sx, sy, w, h, dx, dy; surface_t update; // canvas が clean のときはなにもしない // -> 説明スプライトのように、SetShowされたときに対応できないからだめ //if (sact.msgbufempty) return OK; update.width = r->w; update.height = r->h; dx = sp->cur.x - r->x; dy = sp->cur.y - r->y; w = sp->cursize.width; h = sp->cursize.height; sx = 0; sy = 0; if (!gr_clip(sp->u.msg.canvas, &sx, &sy, &w, &h, &update, &dx, &dy)) { return NG; } dx += r->x; dy += r->y; gre_BlendUseAMap(sf0, dx, dy, sf0, dx, dy, sp->u.msg.canvas, sx, sy, w, h, sp->u.msg.canvas, sx, sy, sp->blendrate); SACT_DEBUG("do update no=%d, sx=%d, sy=%d, w=%d, h=%d, dx=%d, dy=%d", sp->no, sx, sy, w, h, dx, dy); return OK; } static void ntmsg_clear(int wNum) { sprite_t *sp = night.sp[wNum]; surface_t *sf; sp->u.msg.dspcur.x = 0; sp->u.msg.dspcur.y = 0; night.msgbuf[0] = '\0'; // キャンバスのクリア sf = sp->u.msg.canvas; memset(sf->pixel, 0, sf->bytes_per_line * sf->height); memset(sf->alpha, 0, sf->width * sf->height); nt_sp_updateme(sp); }