/* * sprite_msg.c: メッセージスプライトの処理 * * Copyright (C) 1997-1998 Masaki Chikama (Wren) * 1998- * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* $Id: sprite_msg.c,v 1.8 2004/10/31 04:18:02 chikama Exp $ */ #include "config.h" #include #include #include #include "portab.h" #include "system.h" #include "ags.h" #include "nact.h" #include "variable.h" #include "input.h" #include "sact.h" #include "sprite.h" #include "drawtext.h" #include "sactlog.h" // メッセージキー待ちの時、表示するアニメーションに関する情報 struct markinfo { sprite_t *sp; cginfo_t *cg; int interval; }; static bool is_messagesprite(int wNum); static void replacestr_cb(void* data, void* userdata); static char *replacestr(char *msg); static void update_mark(sprite_t *sp, cginfo_t *cg); static int setupmark(int wNum1, int wNum2, struct markinfo *minfo); static int get_linelen(uint8_t *msg); static uint8_t *get_char(uint8_t *msg, char *mbuf, char *rbuf, int bufmax); static void append_to_log(char *msg); static void sactlog_newline(); static void set_align(char *msg, sprite_t *sp, int wSize, int wAlign); // 文字列置き換え用 (表示時にon-the-flyで変換して表示) #define REPLACEBUFSIZE MSGBUFMAX+100 static char repbuf[2][REPLACEBUFSIZE]; static char *replacesrc; static char *replacedst; // 指定の番号のスプライトがメッセージスプライトかどうかをチェック static bool is_messagesprite(int wNum) { // check sprite number is sane if (wNum >= (SPRITEMAX -1) || wNum <= 0) return false; // check sprite is set if (sact.sp[wNum] == NULL) return false; // check sprite is message sprite if (sact.sp[wNum]->type != SPRITE_MSG) return false; return true; } // 文字列の置き換え処理 static void replacestr_cb(void* data, void* userdata) { strexchange_t *ex = (strexchange_t *)data; char *start, *next, *out; if (ex == NULL) return; start = replacesrc; out = replacedst; while (true) { next = strstr(start, ex->src); if (next == NULL) break; strncat(out, start, (size_t)(next - start)); strncat(out, ex->dst, max(0, (REPLACEBUFSIZE - (int)strlen(out)))); start = next + strlen(ex->src); } strncat(out, start, max(0, REPLACEBUFSIZE - (int)strlen(out))); replacedst = replacesrc; replacesrc = out; replacedst[0] = '\0'; } // 文字列の置き換え static char *replacestr(char *msg) { if (sact.strreplace == NULL) return msg; repbuf[0][0] = '\0'; repbuf[1][0] = '\0'; strncpy(repbuf[0], msg, REPLACEBUFSIZE); replacesrc = repbuf[0]; replacedst = repbuf[1]; slist_foreach(sact.strreplace, replacestr_cb, NULL); return (repbuf[0][0] == '\0') ? repbuf[1] : repbuf[0]; } // アニメパターンの描画 static void update_mark(sprite_t *sp, cginfo_t *cg) { bool show = sp->show; cginfo_t *curcg = sp->curcg; sp->show = true; sp->curcg = cg; sp_updateme(sp); sp_update_clipped(); sp->show = show; sp->curcg = curcg; } // アニメパターンの初期化 static int setupmark(int wNum1, int wNum2, struct markinfo *minfo) { sprite_t *sp1, *sp2; int i = 0; if (wNum1 == 0 || wNum2 == 0) return 0; sp1 = sact.sp[wNum1]; sp2 = sact.sp[wNum2]; if (sp1 == NULL || sp2 == NULL) return 0; if (sp1->cg1) { minfo[i].sp = sp1; minfo[i].cg = sp1->cg1; minfo[i].interval = sp1->u.anime.interval; i++; } if (sp1->cg2) { minfo[i].sp = sp1; minfo[i].cg = sp1->cg2; minfo[i].interval = sp1->u.anime.interval; i++; } if (sp1->cg3) { minfo[i].sp = sp1; minfo[i].cg = sp1->cg3; minfo[i].interval = sp1->u.anime.interval; i++; } if (sp2->cg1) { minfo[i].sp = sp2; minfo[i].cg = sp2->cg1; minfo[i].interval = sp2->u.anime.interval; i++; } if (sp2->cg2) { minfo[i].sp = sp2; minfo[i].cg = sp2->cg2; minfo[i].interval = sp2->u.anime.interval; i++; } if (sp2->cg3) { minfo[i].sp = sp2; minfo[i].cg = sp2->cg3; minfo[i].interval = sp2->u.anime.interval; i++; } return i; } /* メッセージをバッファに追加 nact.cのメッセージ・コマンド解析ルーチンから呼ばれる @param msg: 追加する文字列 */ void smsg_add(const char *msg) { int len; if (msg[0] == '\0') return; len = MSGBUFMAX - (int)strlen(sact.msgbuf); if (len < 0) { WARNING("buf shortage (%d)", len); return; } strncat(sact.msgbuf, msg, len); sact.msgbuf[MSGBUFMAX -1] = '\0'; } /* 改行 改行はメッセージ情報内につっこみ、出力時に改行幅を取り出して改行 @param wNum: 改行するスプライト番号 @param size: 改行幅 */ void smsg_newline(int wNum, int size) { uint8_t buf[3]; if (!is_messagesprite(wNum)) return; buf[0] = '\n'; buf[1] = size; buf[2] = '\0'; smsg_add(buf); } /* (ルビつき)メッセージの出力 @param wSpriteNumber: メッセージを表示するメッセージスプライト番号 @param wSize: フォントの大きさ @param wColorR: メッセージの色(Red) @param wColorG: メッセージの色(Green) @param wColorB: メッセージの色(Blue) @param wFont: メッセージのフォント(0:ゴシック, 1:明朝) @param wSpeed: メッセージの表示速度 (msec) @param wLineSpace: 行間スペース @param wAlign: 行そろえ @param wRSize: ルビフォントサイズ @param wRFont: ルビフォント @param wRLineSpace: ルビと本文の文字間隔 @param vLength: ??? */ void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace, int wAlign, int wRSize, int wRFont, int wRLineSpace, int *wLength) { char *msg; sprite_t *sp; int len = 0; // 処理した文字数? bool needupdate = false; SDL_Rect uparea = {0,0,0,0}; // wRSize == 0 -> ルビ無し(SACT.MessageOutputからの呼出) if (sact.msgbuf[0] == '\0') return; if (!is_messagesprite(wNum)) return; // MessageSkip中は文字送り速度を最大に if (sact.waitskiplv > 1) wSpeed = 0; // shortcut sp = sact.sp[wNum]; // update開始Y座標 (X座標は0固定) uparea.y = sp->u.msg.dspcur.y; // 文字列置換 msg = replacestr(sact.msgbuf); // 文字アラインメントの調整 set_align(msg, sp, wSize, wAlign); while (*msg) { char mbuf[20], rbuf[20]; int cw, delta, wcnt; wcnt = sys_get_ticks(); mbuf[0] = rbuf[0] = '\0'; msg = get_char(msg, mbuf, rbuf, sizeof(mbuf) -1); if (mbuf[0] == '\n') { sp->u.msg.dspcur.x = 0; sp->u.msg.dspcur.y += (mbuf[1] + wLineSpace + wRSize + wRLineSpace); set_align(msg, sp, wSize, wAlign); sactlog_newline(); continue; } if (rbuf[0] != '\0') { int mlen = strlen(mbuf) * wSize /2; int rlen = strlen(rbuf) * wRSize /2; int adjx = max(0, (mlen - rlen) /2); dt_setfont(wRFont, wRSize); dt_drawtext_col(sp->u.msg.canvas, sp->u.msg.dspcur.x + adjx, sp->u.msg.dspcur.y, rbuf, wColorR, wColorG, wColorB); } dt_setfont(wFont, wSize); cw = dt_drawtext_col(sp->u.msg.canvas, sp->u.msg.dspcur.x, sp->u.msg.dspcur.y + wRSize + wRLineSpace, mbuf, wColorR, wColorG, wColorB); needupdate = true; append_to_log(mbuf); if (wSpeed > 0) { sp_updateme_part(sp, sp->u.msg.dspcur.x, sp->u.msg.dspcur.y, cw, wSize + wRSize + wRLineSpace); sp_update_clipped(); needupdate = false; // keywait delta = sys_get_ticks() - wcnt; if (delta < wSpeed) { if (sys_keywait(wSpeed - delta, KEYWAIT_NONCANCELABLE)) { wSpeed = 0; } } } sp->u.msg.dspcur.x += cw; } sactlog_newline(); // バッファリング中の文字のクリア sact.msgbuf[0] = '\0'; // Waitなしの出力は最後にupdate if (needupdate) { uparea.w = sp->width; uparea.h = min(sp->height, uparea.y - sp->u.msg.dspcur.y + wLineSpace + wLineSpace + wRSize); sp_updateme_part(sp, uparea.x, uparea.y, uparea.w, uparea.h); } // ???? if (wLength) { *wLength = len; } } /* メッセージ領域のクリア @param wNum: クリアするスプライト番号 */ void smsg_clear(int wNum) { if (!is_messagesprite(wNum)) return; // 表示位置の初期化 sprite_t *sp = sact.sp[wNum]; sp->u.msg.dspcur.x = 0; sp->u.msg.dspcur.y = 0; sact.msgbuf[0] = '\0'; sact.msgbuf2[0] = '\0'; // Clear the canvas SDL_FillRect(sp->u.msg.canvas, NULL, 0); sp_updateme(sp); if (sact.logging) { sact.log = list_append(sact.log, strdup("\n")); } } /* 出力中の文字列があるかチェック @return: なし(1) , あり(0) */ bool smsg_is_empty() { return (sact.msgbuf[0] == '\0'); } int smsg_peek(int nTopStringNum) { char *p = sact.msgbuf; int i; for (i = 0; *p; i++) { char *q = strchr(p, '\n'); if (q) *q = '\0'; svar_set(nTopStringNum + i, p); if (q) { *q = '\n'; p = q + 2; } else { i++; break; } } return i; } /* メッセージキー入力待ち @param wNum1: スプライト番号1(アニメーションスプライト) @param wNum2: スプライト番号2(アニメーションスプライト) @param msglen: ? */ int smsg_keywait(int wNum1, int wNum2, int msglen) { struct markinfo minfo[6]; int i = 0, j, maxstep; if (sact.waitskiplv > 0) { sys_getInputInfo(); return 0; } // アニメパターンの初期化 maxstep = setupmark(wNum1, wNum2, minfo); sact.waittype = KEYWAIT_MESSAGE; sact.waitkey = -1; while (sact.waitkey == -1 && !nact->is_quit) { int st = sys_get_ticks(); int interval = 25; // アニメパターンがある場合、その更新 // Zキーで隠されているとは表示しない // (アニメパターンが隠す対象でない時はどうしよう...) if (maxstep && !sact.zhiding && sact.waittype != KEYWAIT_BACKLOG) { j = i % maxstep; interval = minfo[j].interval; update_mark(minfo[j].sp, minfo[j].cg); i++; } sys_keywait(interval - (sys_get_ticks() - st), KEYWAIT_NONCANCELABLE); } sact.waittype = KEYWAIT_NONE; return sact.waitkey; } /* スプライト再描画のコールバック @param sp: 再描画するスプライト */ void smsg_update(sprite_t *sp) { SDL_Rect sp_rect = {0, 0, sp->width, sp->height}; int sx = 0; int sy = 0; int dx = sp->cur.x; int dy = sp->cur.y; int w = sp->width; int h = sp->height; if (!ags_clipCopyRect(&sp_rect, &sact.updaterect, &sx, &sy, &dx, &dy, &w, &h)) { return; } SDL_SetSurfaceBlendMode(sp->u.msg.canvas, SDL_BLENDMODE_BLEND); SDL_SetSurfaceAlphaMod(sp->u.msg.canvas, sp->blendrate); SDL_BlitSurface(sp->u.msg.canvas, &(SDL_Rect){sx, sy, w, h}, main_surface, &(SDL_Rect){dx, dy, w, h}); 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); } // 改行を含む文字列バッファ中から、改行までの文字列の長さを取り出す static int get_linelen(uint8_t *msg) { int c = 0; while (*msg) { if (*msg == '\n') break; if (0 == strncmp("|RB|", msg, 4)) { msg += 4; while (*msg != '|') { msg++; c++; } msg++; while (*msg != '|') { msg++; } msg++; } else { msg++; c++; } } return c; } // 改行やルビを含む文字列バッファからそれらを取り出す // 改行の場合 : 改行幅もいっしょに // ルビつき文字の場合: メッセージ本体と対応するルビ文字列 // それ以外 : 全角|半角文字1文字 static uint8_t *get_char(uint8_t *msg, char *mbuf, char *rbuf, int bufmax) { if (msg[0] == '\n') { mbuf[0] = '\n'; mbuf[1] = msg[1]; mbuf[2] = '\0'; return msg +2; } // ルビつき文字 if (0 == strncmp("|RB|", msg, 4)) { int i; msg += 4; for (i = 0; *msg != '|' && i < bufmax; i++) { mbuf[i] = *msg++; } msg++; mbuf[i] = '\0'; for (i = 0; *msg != '|' && i < bufmax; i++) { rbuf[i] = *msg++; } msg++; rbuf[i] = '\0'; } else { uint8_t *p = advance_char(msg, nact->encoding); while (msg < p) *mbuf++ = *msg++; *mbuf = '\0'; } return msg; } // バックログ用バッファに追加 static void append_to_log(char *msg) { if (sact.logging) { int len = MSGBUFMAX - (int)strlen(sact.msgbuf2); strncat(sact.msgbuf2, msg, len); sact.msgbuf2[MSGBUFMAX -1] = '\0'; } } // バックログを保存するリストに追加 static void sactlog_newline() { if (sact.logging) { if (sact.msgbuf2[0] == '\0') return; sact.log = list_append(sact.log, toUTF8(sact.msgbuf2)); sact.msgbuf2[0] = '\0'; } } // 書き出し位置がx=0の時のみアラインメントの調整を行う static void set_align(char *msg, sprite_t *sp, int wSize, int wAlign) { if (sp->u.msg.dspcur.x == 0) { int mlen = get_linelen(msg) * wSize/2; int adjx = 0; switch (wAlign) { case 1: // センタリング adjx = (sp->width - mlen) / 2; break; case 2: // 右寄せ adjx = (sp->width - mlen); break; } sp->u.msg.dspcur.x = max(0, adjx); } }