mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-09-28 17:58:09 +03:00
Define a dedicated `vmvar_t` type for values stored in variable pages and propagate it through interfaces that expose those values. Keep the type as int for now so this commit does not change runtime behavior. This separates VM storage from ordinary int buffers and makes a later conversion to 16-bit storage mechanically checkable.
559 lines
14 KiB
C
559 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 "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, vmvar_t *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);
|
|
}
|
|
}
|