view scn2k/scn2k_textimpl.cc @ 66:d112357a0ec1

Fix a bug with savegames introduced with changeset c7bcc0ec2267. Warning: savegames created since c7bcc0ec2267 are probably corrupted, you may have to start the game over. If you chose not to do so, you should replace all occurrences of 'TextWindow' by 'TextImplWindow', and 'Text Window' by 'TextImpl Window' in your save files.
author Thibaut Girka <thib@sitedethib.com>
date Sat, 11 Dec 2010 18:36:20 +0100
parents 36d92d21300f
children 043d5db57474
line wrap: on
line source

/*
 * Copyright (c) 2009 Thibaut GIRKA
 * Copyright (c) 2004-2006  Kazunori "jagarl" Ueno
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "scn2k_text.h"

void Text::impl_txtClear(Cmd& cmd) {
	if (text != NULL) {
		text->ResetFace();
		if (cmd.cmd2 == 3 && cmd.cmd3 == 151)
			text->wid->Clear();
	}
	cur_backlog_item.face = "";
	if (cmd.cmd2 == 3 && cmd.cmd3 == 151)
		text_stream.Clear();
	hide();
}

void Text::impl_logKoe(Cmd& cmd) {
	// PlayKoe ; 声出力コマンドをチェックする */
	cur_backlog_item.koe = cmd.args[0].value;
}

void Text::impl_pause(Cmd& cmd) {
	if (text != NULL) {
		eprintf("start\n");
		text->StartText(text_stream);
		if (skip_mode & SKIP_TEXT) text->wid->Flush();
		else if (kcursor) kcursor->show();
		status = WAIT_TEXT;
		text_parsing = false;
	}
	backlog_item = cur_backlog_item;
	if (cur_backlog_item.scn == 0 && cur_backlog_item.pos == -1) backlog_item.text = text_stream;
	cur_backlog_item.Clear();

	cmd.clear();
	cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
}

void Text::impl_PauseCursor(Cmd& cmd)
{
	/* なんかよくわからないけどカーソル形状変更にしとく */
	SetCursor(cmd.args[0].value);
	cmd.clear();
}

void Text::impl_br(Cmd& cmd) {
	text_stream.AddReturn();
	cur_backlog_item.DeleteTextPos();
	cmd.clear();
}

void Text::impl_FaceOpen(Cmd& cmd) {
	if (text == NULL)
		show();
	string s = cmd.Str(cmd.args[0]);
	s += ".g00";
	if (text != NULL)
		text->ShowFace(s.c_str());
	cur_backlog_item.face = s;
	cmd.cmd_type = CMD_SAVECMD_ONCE;
}

void Text::impl_FaceClear(Cmd& cmd) {
	if (text == NULL)
		show();
	if (text)
		text->ResetFace();
	cur_backlog_item.face = "";
	cmd.cmd_type = CMD_SAVECMD_ONCE;
}

void Text::impl_doRuby(Cmd& cmd) {
	if (text == NULL) {
		show();
	}
	if (cmd.cmd4 == 1) {
		ruby_text_flag = true;
		eprintf("SetRubyText.");
		cmd.clear();
	} else if (cmd.cmd4 == 0) {
		if (ruby_text.length() == 0) { // ルビを振るテキストがない
			eprintf("Cannot find ruby text.\n");
			return;
		}
		if (cmd.args.size() != 1) return;
		char debug1[1024], debug2[1024];
		kconv( (unsigned char*)ruby_text.c_str(), (unsigned char*)debug1);
		kconv( (unsigned char*)cmd.Str(cmd.args[0]), (unsigned char*)debug2);
		eprintf("SetRuby. %s, %s",debug1, debug2);
		text_stream.AddRuby(ruby_text.c_str(), cmd.Str(cmd.args[0]));
		cur_backlog_item.DeleteTextPos();
		cmd.clear();
	}
}

void Text::impl_TextWindow(Cmd& cmd) {
	if (cmd.cmd4 == 0) {
		eprintf("set text window <- %d\n",cmd.args[0].value);
		if (text != NULL) show(cmd.args[0].value);
		else text_window_number = cmd.args[0].value;
	} else if (cmd.cmd4 == 1) { // default value
		eprintf("set text window <- default\n");
		if (text != NULL) show(0);
		else text_window_number = 0;
	}
	cmd.clear();
}

void Text::impl_FastText(Cmd& cmd) {
	//I think it's broken. For now, it's disabled.
	if (cmd.cmd3 == 103) {
		// テキストウィンドウ表示?
		show();
// 表示の際はテキストをクリアしない?
//		if (text) text->wid->Clear();
//		text_stream.Clear();
		cmd.clear();
	}
	else if (cmd.cmd3 == 104) { // テキスト表示?
		// 全テキスト表示
		if (text != NULL) {
			text->StartText(text_stream);
			text->wid->Flush();
		}
		cmd.clear();
	}
}

void Text::impl_msgClear(Cmd& cmd) {
	show();
	if (text != NULL) text->wid->Clear();
	text_stream.Clear();
	cmd.clear();
}

void Text::impl_createSelect(Cmd& cmd) {
	if (cmd.cmd4 == 0) {
		// 選択肢
		CreateSelect(cmd);
		//FIXME: Check if it's really clean
		if (text_parsing) {
			show();
			text->StartText(text_stream);
			if (skip_mode & SKIP_TEXT) text->wid->Flush();
			else if (kcursor) kcursor->hide();
			text_parsing = false;
				text_stream.Clear();
		}
		cmd.cmd_type = CMD_ROLLBACKPOINT; /* 選択肢はセーブ位置 / シナリオ巻き戻し位置 */
		// cmd.clear();
	}
	else
		PrintCmd(cmd); //FIXME
}

void Text::impl_ShowBackground(Cmd& cmd) {
	status_mask = Status(CLEARSCR_MASK | status_mask);
	cmd.clear();
}

void Text::impl_SetSkipMode(Cmd& cmd) {
	status_mask = Status(SKIPMASK | status_mask);
	cmd.clear();
}

void Text::impl_Wait(Cmd& cmd) {
	eprintf("wait %dmsec\n",cmd.args[0].value);

	if (cmd.cmd3 == 100 && text != NULL) {
		/* 0x64 だと文字描画中の待ちに使うことがある */
		text->StartText(text_stream);
		text->wid->Flush();
	}

	if (cmd.cmd3 == 111 || cmd.cmd3 == 112 || cmd.cmd3 == 121)
	{
		int index;
		if (cmd.cmd4 == 1)
			index = 0;
		else
			index = cmd.args[1].value;
		wait_time = timer_var[index].start_time + cmd.args[0].value;
	}
	else
		wait_time = old_time + cmd.args[0].value;

	if (cmd.cmd3 == 101 || cmd.cmd3 == 112)
		status = WAIT_CLICK;
	else
		status = WAIT;

	cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
}

void Text::impl_GetClick(Cmd& cmd) {
	eprintf("wait and get mouse pos at click\n");
	wait_time = old_time + 1000 * 1000;
	status = WAIT_CLICK_MOUSEPOS;
	wait_savedvar[0] = cmd.args[0];
	wait_savedvar[1] = cmd.args[1];
	cmd.clear();
}

void Text::impl_ResetTimer(Cmd& cmd) {
	int index;

	if (cmd.cmd4 == 1)
		index = 0;
	else
		index = cmd.args[0].value;

	eprintf("set basetime (%d)\n",index);
	//TODO: Handle EX timer set
	TimerAtom& atom = timer_var[index];
	atom.start_time = old_time;

	cmd.clear();
}

void Text::impl_Timer(Cmd& cmd) {
	int index;

	if (cmd.cmd4 == 1)
		index = 0;
	else
		index = cmd.args[0].value;

	eprintf("get time %dth\n",index);
	if (timer_var.find(index) == timer_var.end())
		cmd.SetSysvar(0);
	else
		cmd.SetSysvar(old_time - timer_var[index].start_time);
}

void Text::impl_ReadFrame(Cmd& cmd) {
	eprintf("get timer value[%d]\n", cmd.args[0].value);
	if (frame_var.find(cmd.args[0].value) == frame_var.end())
		cmd.SetSysvar(0);
	else {
		FrameTimerAtom& atom = frame_var[cmd.args[0].value];
		if (atom.total_time <= 0) atom.total_time = 1;
		int cur_tm = old_time - atom.start_time;
		if (cur_tm < 0) cur_tm = atom.total_time; // エラーなら最終時間に合わせる
		if (cur_tm > atom.total_time) cur_tm = atom.total_time;
		// use 'long long'(64bit) or 'double'(80bit) type, since total_time, to and from is 32 bit.
		int v = atom.from + (long long)(atom.to - atom.from)*cur_tm/int(atom.total_time);
		cmd.SetSysvar(v);
	}
}

void Text::impl_InitFrame(Cmd& cmd) {
	FrameTimerAtom& atom = frame_var[cmd.args[0].value];
	atom.from = cmd.args[1].value;
	atom.to = cmd.args[2].value;
	atom.total_time = cmd.args[3].value;
	atom.start_time = old_time;
	cmd.clear();
}

void Text::impl_InitFrames(Cmd& cmd) {
	int i, j = 0;
	for (i = 0; i < cmd.argc; i++)
	{
		int cnt = cmd.args[j++].value; // £³€Ê€Î€Ç̵»ë
		int num = cmd.args[j++].value;
		FrameTimerAtom& atom = frame_var[num];
		atom.from = cmd.args[j++].value;
		atom.to = cmd.args[j++].value;
		atom.total_time = cmd.args[j++].value;
		atom.start_time = old_time;
	}
	cmd.clear();
}

void Text::impl_ReadFrames(Cmd& cmd) {
	vector<VarInfo> args = cmd.args;
	vector<VarInfo>::iterator it = args.begin();
	int argc = cmd.argc;
	int timers_active = 0;
	int i;
	for (i=0; i < argc; i++)
	{
		int cnt = (it++)->value;
		int num = (it++)->value;

		if (frame_var.find(num) == frame_var.end()) {
			cmd.SetFlagvar(*it++, 0);
		}
		else
		{
			FrameTimerAtom& atom = frame_var[num];
			if (atom.total_time <= 0)
				atom.total_time = 1;
			int cur_tm = old_time - atom.start_time;
			if (cur_tm < 0)
				cur_tm = atom.total_time; // ¥š¥é¡Œ€Ê€éºÇœª»þŽÖ€Ë¹ç€ï€»€ë
			if (cur_tm > atom.total_time)
				cur_tm = atom.total_time;
			// use 'long long'(64bit) or 'double'(80bit) type, since total_time, to and from is 32 bit.
			int v = atom.from + (long long)(atom.to-atom.from)*cur_tm/int(atom.total_time);
			cmd.SetFlagvar(*it++, v);
			if (atom.total_time != -1 && cur_tm < atom.total_time)
				timers_active = 1;
		}
	}
	cmd.SetSysvar(timers_active);
}

#include "math.h"

void Text::impl_rnd(Cmd& cmd)
{
	/* rand() */
	int min, max;
	if (cmd.args.size() == 2)
	{
		min = cmd.args[0].value;
		max = cmd.args[1].value;
	}
	else
	{
		min = 0;
		max = cmd.args[0].value;
	}
	if (min > max)
	{
		int tmp = max;
		max = min;
		min = tmp;
	}
	int r = random();
	if (min == max)
		r = min;
	else
		r = (r % (max-min)) + min;
	cmd.SetSysvar(r);
}

void Text::impl_pcnt(Cmd& cmd)
{
	cmd.SetSysvar(100 * cmd.args[0].value / cmd.args[1].value);
}

void Text::impl_abs(Cmd& cmd)
{
	cmd.SetSysvar(abs(cmd.args[0].value));
}

void Text::impl_power(Cmd& cmd)
{
	cmd.SetSysvar(pow(cmd.args[0].value, cmd.args[1].value));
}

void Text::impl_sin(Cmd& cmd)
{
	cmd.SetSysvar(sin(cmd.args[0].value * M_PI / 180) * 32640 / cmd.args[1].value);
}

void Text::impl_min(Cmd& cmd)
{
	int a = cmd.args[0].value;
	int b = cmd.args[1].value;
	cmd.SetSysvar((a < b) ? a : b);
}

void Text::impl_max(Cmd& cmd)
{
	int a = cmd.args[0].value;
	int b = cmd.args[1].value;
	cmd.SetSysvar((a > b) ? a : b);
}

void Text::impl_index_series(Cmd& cmd)
{
	//TODO: This one is not fully documented in
	// http://dev.haeleth.net/rldev/manual.html
	// Try to figure out what's it...

	/* range conversion : 比率に丸める */
	// アルゴリズムは間違えてるような気がする
	// 
	if (cmd.args.size() >= 7)
	{
		int val = cmd.args[0].value;
		int offset = cmd.args[1].value;
		int r_min = cmd.args[2].value;
		int v_min = cmd.args[3].value;
		int v_max = cmd.args[4].value;
		int r_max = cmd.args[5].value;
		int mode = cmd.args[6].value;
		// rldev : mode == 1,3 : 'acceralating curve', 2,3: 'decelerating curve'
		// 複数の引数リスト(r_minからmodeまでのリスト)もつこともあり、その場合は
		// "cancel out in some way" らしい
		if (mode == 1 || mode == 3)
			val += offset;
		else if (mode == 2 || mode == 4)
			val -= offset;
if (cmd.args.size() != 7)
	fprintf(stderr,"\n%d/%d: cmd 01-04:0320 : XXXX NOT SUPPORTED LIST : DOUBLE RANGE CONVERSION!   XXXXXXXXXXX\n",cmd.scn,cmd.pos);
		if (val < v_min)
			val = v_min;
		if (val > v_max)
			val = v_max;
		val = (r_max-r_min)*(val-v_min)/(v_max-v_min) + r_min;
		cmd.SetSysvar(val);
	}
}

void Text::impl_constrain(Cmd& cmd)
{
	/* range 内に丸める */
	int min = cmd.args[0].value;
	int val = cmd.args[1].value;
	int max = cmd.args[2].value;
	if (min > max) {
		max = cmd.args[0].value;
		min = cmd.args[2].value;
	}
	if (val < min)
		val = min;
	if (val > max)
		val = max;
	cmd.SetSysvar(val);
}


void Text::impl_load(Cmd& cmd)
{
	// メニューからのロード
	cmd.cmd_type = CMD_LOADREQ;
}

void Text::impl_GetWindowAttr(Cmd& cmd)
{
	// テキストウィンドウの色設定
	int r, g, b, a, flag;

	if (cmd.cmd3 == 2617) // 元設定を取り出す
		config->GetOriginalParam("#WINDOW_ATTR", 5, &r, &g, &b, &a, &flag);
	else
		config->GetParam("#WINDOW_ATTR", 5, &r, &g, &b, &a, &flag);

	if (cmd.args.size() != 5) {
		fprintf(stderr,"cmd 01-04:%4d : invalid arg size\n", cmd.cmd3);
	} else {
		vector<VarInfo> args(cmd.args);
		cmd.SetFlagvar(args[0], r);
		cmd.SetFlagvar(args[1], g);
		cmd.SetFlagvar(args[2], b);
		cmd.SetFlagvar(args[3], a);
		cmd.SetFlagvar(args[4], flag);
	}
}

void Text::impl_SetWindowAttr(Cmd& cmd)
{
	int r, g, b, a, flag;
	config->GetParam("#WINDOW_ATTR", 5, &r, &g, &b, &a, &flag);

	switch(cmd.cmd3) {
		case 2260:
			r = cmd.args[0].value;
			break;
		case 2261:
			g = cmd.args[0].value;
			break;
		case 2262:
			b = cmd.args[0].value;
			break;
		case 2263:
			a = cmd.args[0].value;
			break;
		case 2264:
			flag = cmd.args[0].value;
			break;
		case 2267: 
			r = cmd.args[0].value;
			g = cmd.args[1].value;
			b = cmd.args[2].value;
			a = cmd.args[3].value;
			flag = cmd.args[4].value;
			break;
	}
	config->SetParam("#WINDOW_ATTR", 5, r, g, b, a, flag);
	SetWindowColor(r, g, b, a, flag);
	cmd.clear();
}

void Text::impl_GetDefConfig(Cmd& cmd)
{
	int v = 0;
	switch(cmd.cmd3) {
		case 2600:
		case 2605:
			config->GetOriginalParam("#INIT_MESSAGE_SPEED", 1, &v);
			break;
		case 2601:
			config->GetOriginalParam("#INIT_MESSAGE_SPEED_MOD", 1, &v);
			break;
		case 2604:
			config->GetOriginalParam("#MESSAGE_KEY_WAIT_USE", 1, &v);
			break;
		case 2606:
			config->GetOriginalParam("#MESSAGE_KEY_WAIT_TIME", 1, &v);
			break;
	}
	cmd.SetSysvar(v);
}

void Text::impl_GetConfig(Cmd& cmd)
{
	int v;
	switch (cmd.cmd3)
	{
		case 2323:
		case 2351:
			config->GetParam("#INIT_MESSAGE_SPEED", 1, &v);
			break;
		case 2324:
			config->GetParam("#INIT_MESSAGE_SPEED_MOD", 1, &v);
			break;
		case 2350:
			config->GetParam("#MESSAGE_KEY_WAIT_USE", 1, &v);
			break;
		case 2352:
			config->GetParam("#MESSAGE_KEY_WAIT_TIME", 1, &v);
			break;
	}

	cmd.SetSysvar(v);
}

void Text::impl_SetConfig(Cmd& cmd)
{
	int speed, use_speed_mod, wait, use_wait_mod;
	config->GetParam("#INIT_MESSAGE_SPEED", 1, &speed);
	config->GetParam("#INIT_MESSAGE_SPEED_MOD", 1, &use_speed_mod);
	config->GetParam("#MESSAGE_KEY_WAIT_USE", 1, &use_wait_mod);
	config->GetParam("#MESSAGE_KEY_WAIT_TIME", 1, &wait);
	switch (cmd.cmd3)
	{
		case 2223:
		case 2251:
			speed = cmd.args[0].value;
			if (speed < 10) //FIXME: ??? 0 ???
				speed = 10;
			if (speed > 10000) //FIXME: ??? 255 ???
				speed = 10000;
			config->SetParam("#INIT_MESSAGE_SPEED", 1, speed);
			break;
		case 2224:
			use_speed_mod = cmd.args[0].value;
			config->SetParam("#INIT_MESSAGE_SPEED_MOD", 1, use_speed_mod);
			break;
		case 2250:
			use_wait_mod = cmd.args[0].value;
			config->SetParam("#MESSAGE_KEY_WAIT_USE", 1, use_wait_mod);
			break;
		case 2252:
			int wait = cmd.args[0].value;
			if (wait < 0)
				wait = 0;
			else if (wait > 60000)
				wait = 60000;
			config->SetParam("#MESSAGE_KEY_WAIT_TIME", 1, wait);
			break;
	}

	if (use_speed_mod) // FIXME: Not the other way around?
		SetTextSpeed(-1);
	else
		SetTextSpeed(speed);

	if (use_wait_mod)
		SetTextWait(wait);
	else
		SetTextWait(-1);

	cmd.clear();
}

void Text::impl_GetName(Cmd& cmd)
{
	// replace_name を得る
	int n = cmd.args[0].value;
	if (n >= 0 && n < 26) // FIXME: Should go up to 702, but otakunoraifu is not ready for that yet
		cmd.SetStrvar(cmd.args[1], replace_name[n]);
	else
		cmd.SetStrvar(cmd.args[1], "");
}

void Text::impl_SetName(Cmd& cmd)
{
	// replace_name を得る
	int n = cmd.args[0].value;
	if (n >= 0 && n < 26) // FIXME: Should go up to 702, but otakunoraifu is not ready for that yet
		replace_name[n] = cmd.Str(cmd.args[1]);
}

void Text::impl_GetLocalName(Cmd& cmd)
{
	// replace_name2 を得る
	int n = cmd.args[0].value;
	if (n >= 0 && n < 26) // FIXME: Should go up to 702, but otakunoraifu is not ready for that yet
		cmd.SetStrvar(cmd.args[1], replace_name2[n]);
	else
		cmd.SetStrvar(cmd.args[1], "");
}

void Text::impl_SetLocalName(Cmd& cmd)
{
	// replace_name2 の設定
	int n = cmd.args[0].value;
	if (n >= 0 && n < 26) // FIXME: Should go up to 702, but otakunoraifu is not ready for that yet
		replace_name2[n] = cmd.Str(cmd.args[1]);

	cmd.clear();
}