mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-09-22 22:48:08 +03:00
The following SACT functions now return immediately while message skipping is active: - DrawEffect - DrawEffectAlphaMap - MessageOutput - MessageOutputEx - QuakeSprite - TimerWait - Wait - SoundWaitKey - WaitKeyMessage
568 lines
14 KiB
C
568 lines
14 KiB
C
/*
|
|
* sprite_msg.c: メッセージスプライトの処理
|
|
*
|
|
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
|
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "portab.h"
|
|
#include "system.h"
|
|
#include "ags.h"
|
|
#include "gfx.h"
|
|
#include "nact.h"
|
|
#include "variable.h"
|
|
#include "input.h"
|
|
#include "msgskip.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, vmvar_t *wLength) {
|
|
char *msg;
|
|
sprite_t *sp;
|
|
int len = 0; // 処理した文字数?
|
|
bool has_batched_output = false;
|
|
bool flush_batched_output = wSpeed > 0;
|
|
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 || msgskip_isSkipping()) 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);
|
|
|
|
has_batched_output = 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();
|
|
has_batched_output = 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';
|
|
|
|
// Register the update area after rendering without waits.
|
|
if (has_batched_output) {
|
|
uparea.w = sp->width;
|
|
uparea.h = min(sp->height - uparea.y,
|
|
sp->u.msg.dspcur.y - uparea.y + wSize + wRSize + wRLineSpace);
|
|
sp_updateme_part(sp, uparea.x, uparea.y, uparea.w, uparea.h);
|
|
// Present now if waiting was skipped.
|
|
if (flush_batched_output) {
|
|
sp_update_clipped();
|
|
gfx_updateScreen();
|
|
}
|
|
}
|
|
|
|
// ????
|
|
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 || msgskip_isSkipping()) {
|
|
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);
|
|
}
|
|
}
|