view scn2k/scn2k_grpimpl.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 bdd8a5ff8f46
children 1fd20d231376
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_grp.h"
#include "system/system_config.h"
#include "music2/music.h"
#include "window/render.h"

void Grp::impl_stackClear (Cmd& cmd) {
	cmd.cmd_type = CMD_SAVECMDGRP_START;
}

void Grp::impl_grpBuffer (Cmd& cmd) {
	const char* name = cmd.Str(cmd.args[0]);
	int pdt = cmd.args[1].value;
	eprintf("load surface %s pdt %d\n",name, pdt);
	if (pdt == 0)
		reserved_load_surface0 = name; // 画像読み込みは 01-1f:0000 まで待つ
	else if (pdt == 1)
		LoadSurface(name); // 背景絵読み込み?
	else
		LoadSurface(name, pdt);
	cmd.cmd_type = CMD_SAVECMDGRP;
}

void Grp::impl_grpMulti(Cmd& cmd) {
	int pos = cmd.args[0].value;
	const char* name = cmd.Str(cmd.args[1]);
	int sel = cmd.args[2].value;
	eprintf("set foreground %s sel %d pos %d\n",name, sel, pos);
	AddSurface(name);
	StartAnm(sel);
	event.RegisterGlobalPressFunc(&Pressed, (void*)this);
	status = WAIT_ANM;
	cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
}

void Grp::impl_grpOpen(Cmd& cmd) {
	const char* name = cmd.Str(cmd.args[0]);
	int sel = cmd.args[1].value;

	if (name[0] == '?')
		LoadSurface();
	else if(cmd.cmd3 == 73)
		LoadSurface(name, 1);
	else
		LoadSurface(name);

	StartAnm(sel);
	status = WAIT_ANM;
	event.RegisterGlobalPressFunc(&Pressed, (void*)this);

	if (name[0] == '?')
		cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
	else
		cmd.cmd_type = CMD_SAVECMDGRP_START;
}

void Grp::impl_shake(Cmd& cmd) {
	// shake screen
	char key[11];
	sprintf(key, "#SHAKE.%03d", cmd.args[0].value);
	if (config->SearchParam(key) != 2) {
		fprintf(stderr,"Cannot find shake pattern %d; use default pattern\n",cmd.args[0].value);
		strcpy(key, "#SHAKE.000"); // default key
	}
	int num;
	const int* pattern;
	pattern = config->GetParamArray(key, num);
	if (pattern) {
		StartShake(num, pattern);
		status = WAIT_SHAKE;
	}
	cmd.clear();
}

void Grp::impl_grpCopy(Cmd& cmd) {
	if (cmd.cmd4 == 2) { // copy (KANOGI)
		int sx = cmd.args[0].value;
		int sy = cmd.args[1].value;
		int w = cmd.args[2].value - sx;
		int h = cmd.args[3].value - sy;
		Rect rect(sx, sy, sx+w, sy+h);
		int src = cmd.args[4].value;
		int dx = cmd.args[5].value;
		int dy = cmd.args[6].value;
		int dest = cmd.args[7].value;
		unsigned char alpha;
		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
		printf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
		if (src == dest) {
			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
			src = WORKPDT;
		}
		parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
		if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
		cmd.clear();
	}
}

void Grp::impl_recFill(Cmd& cmd) {
	int x = cmd.args[0].value;
	int y = cmd.args[1].value;
	int w = cmd.args[2].value;
	int h = cmd.args[3].value;
	Rect rect(x, y, x+w, y+w);
	int pdt = cmd.args[4].value;
	int r = cmd.args[5].value;
	int g = cmd.args[6].value;
	int b = cmd.args[7].value;

	if (cmd.cmd4 == 2) {
		eprintf("clear %d:(%d,%d) size (%d,%d) r %d g %d b %d\n",pdt,x,y,w,h,r,g,b);
		DSurfaceFill(Dsurface(pdt), rect, r, g, b);
		// if (pdt == 0) screen->ReBlit(rect);
		cmd.cmd_type = CMD_SAVECMDGRP;
	}
	else if (cmd.cmd4 == 3) { // alpha つきfill
		int a = cmd.args[8].value;
		eprintf("alpha-clear %d:(%d,%d) size (%d,%d) r %d g %d b %d a %d\n",pdt,x,y,w,h,r,g,b,a);
		if (a <= 0) ;
		else if (a >= 255) DSurfaceFill(Dsurface(pdt), rect, r, g, b);
		else {
			DSurfaceFill(Dsurface(WORKPDT), rect, r, g, b, a);
			parent.Root().BlitSurface(Dsurface(WORKPDT), rect, Dsurface(pdt), rect);
		}
		// if (pdt == 0) screen->ReBlit(rect);
		cmd.clear();
	}
}

void Grp::impl_recCopy(Cmd& cmd) {
	//TODO: Handle forms 0 and 1
	int sx = cmd.args[0].value;
	int sy = cmd.args[1].value;
	int w = cmd.args[2].value;
	int h = cmd.args[3].value;
	Rect rect(sx, sy, sx + w, sy + h);
	int src = cmd.args[4].value;
	int dx = cmd.args[5].value;
	int dy = cmd.args[6].value;
	int dest = cmd.args[7].value;

	if (cmd.cmd4 == 2) {
		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
		parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
		//DSurfaceMove(Ssurface(src), Rect(sx,sy,sx+w,sy+h), Dsurface(dest), Rect(dx,dy));
		// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
		cmd.cmd_type = CMD_SAVECMDGRP;
	}
	else if (cmd.cmd4 == 3) { // alpha つきcopy
		unsigned char alpha;
		if (cmd.args[8].value < 0) alpha = 0;
		else if (cmd.args[8].value > 255) alpha = 255;
		else alpha = cmd.args[8].value;
		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
		if (src == dest) {
			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
			src = WORKPDT;
		}
		if (alpha != 0)
			parent.Root().BlitSurface(Ssurface(src), rect, &alpha, Rect(0,0,1,1), Dsurface(dest), Rect(dx,dy), 0);
		// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
		cmd.clear();
	}
}

void Grp::impl_recAdd(Cmd& cmd) {
	if (cmd.cmd4 == 3) { // add mode で alpha 付き copy
		int sx = cmd.args[0].value;
		int sy = cmd.args[1].value;
		int w = cmd.args[2].value;
		int h = cmd.args[3].value;
		Rect rect(sx, sy, sx+w, sy+h);
		int src = cmd.args[4].value;
		int dx = cmd.args[5].value;
		int dy = cmd.args[6].value;
		int dest = cmd.args[7].value;
		unsigned char alpha;
		if (cmd.args[8].value < 0) alpha = 0;
		else if (cmd.args[8].value > 255) alpha = 255;
		else alpha = cmd.args[8].value;
		eprintf("copy surface w/ add %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
		if (src == dest) {
			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
			src = WORKPDT;
		}
		if (alpha != 0) {
			// add mode : screen (picture) を一時的に作成
			PicBase* screen_tmp = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
			screen_tmp->SetSurface(Ssurface(src), 0, 0, PicBase::BLIT_ADD);
			screen_tmp->SetSurfaceRect(rect);
			screen_tmp->Move(dx, dy);
			screen_tmp->SetSurfaceAlpha(&alpha, Rect(0,0,1,1));
			screen_tmp->SimpleBlit(Dsurface(dest));
			delete screen_tmp;
		}
		cmd.clear();
	}
}

void Grp::impl_grpPan(Cmd& cmd) {
	if (cmd.cmd4 == 0) {
		Rect r_from(cmd.args[0].value, cmd.args[1].value);
		Rect r_to(cmd.args[2].value, cmd.args[3].value);
		int src_pdt = cmd.args[4].value;
		Rect r(cmd.args[5].value,cmd.args[6].value,cmd.args[7].value+1,cmd.args[8].value+1);
		int tm = cmd.args[9].value;
		fprintf(stderr,"??? cmd time %d\n",tm);
		// anm1 = new ScnGrpMove(event, screen, parent.Root(), surface, r, Ssurface(2), r_from, r_to, tm);
		// status = WAIT_ANM;
	}
}

void Grp::impl_snmBgScroll(Cmd& cmd) {
	if (cmd.cmd4 == 0) { // スクロールする画像効果(Princess Bride)
		if (anm2 != NULL) {
			anm2->Abort();
			delete anm2;
			anm2 = NULL;
		}
		PicBase* pic; Surface* s;
		Rect r(cmd.args[1].value, cmd.args[2].value, cmd.args[3].value+1, cmd.args[4].value+1);
		const char* name = cmd.Str(cmd.args[5]);
		Rect sr_start(cmd.args[6].value,cmd.args[7].value);
		Rect sr_end(cmd.args[8].value,cmd.args[9].value);
		int tm = cmd.args[10].value;
		LoadSurface(name, 2); /* PDT2 に読み込み、と決め打ち */

		anm2 = new ScnGrpMove(event, screen, parent.Root(), Dsurface(1), r, Ssurface(2), sr_start, sr_end, tm);
		cmd.cmd_type = CMD_SAVECMDGRP;
	}
}

void Grp::impl_snmPlay(Cmd& cmd) {
	if (cmd.cmd4 == 0) {
		// カードが落ちるアニメーション
		int i;
		ScnGrpAnm* new_anm = new ScnGrpAnm(event, screen, *this);
		if (cmd.cmd3 == 0x834 || cmd.cmd3 == 0x835) {
			AbortAnm();
			anm1 = new_anm;
			if (cmd.cmd3 == 0x835) {
				status = WAIT_ANM;
				event.RegisterGlobalPressFunc(&Pressed, (void*)this);
			}
		} else {
 			anm2 = new_anm;
		}
		for (i=0; i<cmd.argc; i++) {
			const char* name = cmd.Str(cmd.args[i*3+1]);
			int tm = cmd.args[i*3+2].value;
			new_anm->push_back(ScnGrpAnmAtom(name,tm));
		}
		new_anm->CalcTotal();
		cmd.clear();
	}
}

void Grp::impl_cgGet(Cmd& cmd) {
	if (cmd.cmd3 == 0x5dc) // Total number of CG
		cmd.SetSysvar(cgm_size);

	if (cmd.cmd3 == 0x5dd) // Number of CG viewed
		cmd.SetSysvar(cgm_data.size());

	if (cmd.cmd3 == 0x5de) // Percentage of CG viewed
		cmd.SetSysvar(cgm_data.size() * 100 / cgm_size);
}

void Grp::impl_cgStatus(Cmd& cmd) {
	string s = cmd.Str(cmd.args[0]);
	if (cgm_info.find(s) == cgm_info.end()) {
		fprintf(stderr,"cmd 01-04:05e0 : cannot find cgm-info of '%s'\n",s.c_str());
		cmd.SetSysvar(-1);
	}
	else {
		int n = cgm_info[s];
		if (cmd.cmd3 == 1503) cmd.SetSysvar(n);
		else {
			if (cgm_data.find(n) == cgm_data.end()) cmd.SetSysvar(0);
			else cmd.SetSysvar(1);
		}
	}
}

void Grp::impl_objClear(Cmd& cmd) { //FIXME: may be broken (doesn't reflect what Haeleth says)
	if (cmd.cmd1 == 1)
		DeleteObj(cmd.args[0].value);
	if (cmd.cmd1 == 2)
		DeleteSubObj(cmd.args[0].value, cmd.args[1].value);
	cmd.clear();
}

void Grp::impl_createObj(Cmd& cmd) {
	/**************:
	0x47 : オブジェクト内容の設定
			1100: G00 file
			1003: GAN file
			1100: rect
			1200: string
			1300: weather effects
			1400: number
	*/
	int base_argc = 0;

	if (cmd.cmd1 == 1) { // 1: group object
		DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
		if (cmd.cmd2 == 71)
			DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
	}
	else { // 2: single object in group
		DeleteSubObjPic(cmd.args[0].value, cmd.args[1].value); // 旧ファイル名のsurfaceを削除
		if (cmd.cmd2 == 71)
			DeleteSubObjPic(cmd.args[0].value, cmd.args[1].value); // 旧ファイル名のsurfaceを削除
	}

	GrpObj* g = (cmd.cmd2 == 71) ? &grpobj[cmd.args[0].value] : &bs_obj[cmd.args[0].value];
	if (cmd.cmd1 == 2) // 2: single object in a group
		g = &g->children_obj[cmd.args[1].value];

	if (cmd.cmd1 == 2)
		base_argc = 1;

	if (cmd.cmd3 == 1000) { /* ファイル名設定 */
		g->gtype = GrpObj::FILE;
		string name = cmd.Str(cmd.args[base_argc + 1]);
		if (name.find('?') != -1) {//TODO
			//Used for shading, with DAT/tcdata.tcc or other filename provided by #TONECURVE_FILENAME
			printf("Warning: the part after the '?' was removed: '%s'\n", name.c_str());
			name.erase(name.find('?')); // '?' 以降の意味がわからない
		}
		g->name = name;
	} else if (cmd.cmd3 == 1003) { /* ファイル名設定(GAN含む) */
		g->gtype = GrpObj::GAN;
		if (cmd.Str(cmd.args[base_argc + 1]) == string("???"))
			g->name = cmd.Str(cmd.args[base_argc + 2]);
		else
			g->name = cmd.Str(cmd.args[base_argc + 1]);
		g->gan_name = cmd.Str(cmd.args[base_argc + 2]);

		if (cmd.cmd4 >= 1 && cmd.args[base_argc + 3].value == 0)
			g->attr =  GrpObj::Attribute(g->attr | GrpObj::HIDDEN);
		else
			g->attr =  GrpObj::Attribute(g->attr & ~(GrpObj::HIDDEN));

		if (cmd.argc >= base_argc + 5)
			g->SetPos(1, cmd.args[base_argc + 4].value, -cmd.args[base_argc + 5].value);

		if (g->name.find('?') != -1) {
			g->name.erase(g->name.find('?'));
			g->gan_name = cmd.Str(cmd.args[base_argc + 2]);
		}
	} else if (cmd.cmd3 == 1200) { // 画像を文字列として指定
		g->gtype = GrpObj::MOJI;
		g->print_moji = cmd.Str(cmd.args[base_argc + 1]);
		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN)); // 常に表示がデフォルト?
		cmd.clear();
	} else if (cmd.cmd3 == 1400) { // 数値を画像として表示
		g->gtype = GrpObj::DIGIT;
		g->name = cmd.Str(cmd.args[base_argc + 1]);
	}

	CreateObj(cmd.args[0].value);
	if (cmd.cmd1 == 2)
		CreateSubObj(cmd.args[0].value, cmd.args[1].value);

	if (cmd.cmd3 == 1000 || cmd.cmd3 == 1003 || cmd.cmd3 == 1200 || cmd.cmd3 == 1400) {
		// FILE, GAN, MOJI, DIGIT ならば座標等の設定を行う
		if (cmd.cmd4 >= 1) {
			if (cmd.args[2+base_argc].value == 0) {
				g->attr = GrpObj::Attribute(g->attr | GrpObj::HIDDEN);
			} else {
				g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN));
			}
			SetObjChanged(cmd.args[0].value);
		}
		if (cmd.cmd4 >= 2) { // 座標等も設定
			g->SetPos(0, cmd.args[3+base_argc].value, cmd.args[4+base_argc].value);
		}
		if ( (cmd.cmd3 == 1000 || cmd.cmd3 == 1003) && cmd.cmd4 >= 3) { // pattern 番号も設定
			g->SetSurfaceNum(cmd.args[5+base_argc].value);
			base_argc++; // 1000 (FILE) / 1003 (GAN) の場合のみこのオプションは存在する
		}
		cmd.clear();
	} else {
		fprintf(stderr,"CreateObj : cmd.cmd3 = %04x ; not supported!\n",cmd.cmd3);
	}
}

void Grp::impl_gan(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg);

	if (cmd.cmd3 == 3) { // ganIsPlaying
		if (g->anm == NULL || g->anm->IsEnd())
			cmd.SetSysvar(0);
		else
			cmd.SetSysvar(1);
	}
	else if (cmd.cmd3 == 1000) { // ganStop
		if (g->anm == NULL || g->anm->IsEnd())
			g->SetSurfaceNum(cmd.args[1].value);
		else {
			g->anm->Abort();
			g->SetSurfaceNum(cmd.args[1].value);
		}
		SetObjChanged(cmd.args[0].value);
		cmd.clear();
	}
	else if (cmd.cmd3 == 2003) { // objPlay
		g->CreateGanSpecial(event, 0, cmd.args[1].value);
		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
		SetObjChanged(cmd.args[0].value);
		cmd.clear();
	}
	else if (cmd.cmd3 == 3001 || cmd.cmd3 == 3003 || cmd.cmd3 == 3005 ||
			 cmd.cmd3 == 1001 || cmd.cmd3 == 1003 || cmd.cmd3 == 1005) { // ganPlay*
		g->CreateGan(event, cmd.args[1].value);
		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
		SetObjChanged(cmd.args[0].value);
		cmd.clear();
	}
}

void Grp::impl_objSetPos(Cmd& cmd) {
	//obj or objBg
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	int index, x, y;
	if (cmd.cmd3 == 1006 || cmd.cmd3 == 2006) { //objAdjust
		index = cmd.args[1+base_arg].value + 1;
		x = cmd.args[2+base_arg].value;
		y = cmd.args[3+base_arg].value;
	}
	else {
		index = 0;
		if (cmd.cmd3 == 1000) {
			x = cmd.args[1+base_arg].value;
			y = cmd.args[2+base_arg].value;
		}
		else {
			g->GetPos(index, x, y);
			if (cmd.cmd3 == 1001)
				x = cmd.args[1+base_arg].value;
			else
				y = cmd.args[1+base_arg].value;
		}
	}

	g->SetPos(index, x, y);
	cmd.clear();
}

void Grp::impl_objAlpha(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	g->SetAlpha(cmd.args[base_arg + 1].value);
	cmd.clear();
}

void Grp::impl_objShow(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	if (cmd.args[base_arg + 1].value)
		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN));
	else
		g->attr = GrpObj::Attribute(g->attr | GrpObj::HIDDEN);

	g->attr = GrpObj::Attribute(g->attr | GrpObj::UPDATE_VISIBLE);
		// グループ単位で次の RefreshObj で表示・消去
	if (cmd.cmd2 == 0x51) //not Bg
		SetObjChanged(cmd.args[0].value);
	cmd.clear();
}

void Grp::impl_objColour(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	g->print_r = cmd.args[base_arg+1].value;
	g->print_g = cmd.args[base_arg+2].value;
	g->print_b = cmd.args[base_arg+3].value;
	g->SetUpdate();
	cmd.clear();
}

void Grp::impl_objComposite(Cmd& cmd) {//FIXME
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
	if (cmd.args[base_arg + 1].value == 1)
	{
		g->attr = GrpObj::Attribute(g->attr | GrpObj::BLIT_ADD);
		cmd.clear();
	}
	else if (cmd.args[base_arg + 1].value == 0)
	{
		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::BLIT_ADD));
		cmd.clear();
	}
	g->SetUpdate();
}

void Grp::impl_objSetText(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	g->print_moji = cmd.Str(cmd.args[base_arg + 1]);
	g->SetUpdate();
	cmd.clear();
}

void Grp::impl_objTextOpts(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	// 画像を文字列として設定:文字の大きさなど
	g->print_size = cmd.args[base_arg + 1].value;
	/* 前景色を得る */
	int cr, cg, cb;
	char key[17];
	sprintf(key, "#COLOR_TABLE.%03d", cmd.args[base_arg + 5].value);
	if (config->GetParam(key, 3, &cr, &cg, &cb)) { // color not found
		cr = cg = cb = 0;
	}
	g->print_r = cr;
	g->print_g = cg;
	g->print_b = cb;
	g->SetUpdate();
	cmd.clear();
}

void Grp::impl_objOrder(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	int order = cmd.args[base_arg + 1].value;
	g->order = order;
	ZMoveObj(cmd.args[0].value);
	cmd.clear();
}

void Grp::impl_objDispArea(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	// オブジェクトのどの部分を画面に表示するか(クリップ領域)の設定
	int rx, ry, w, h;
	if (cmd.args.size() == base_arg + 5) {
		int rx = cmd.args[base_arg + 1].value;
		int ry = cmd.args[base_arg + 2].value;
		int w = cmd.args[base_arg + 3].value;
		int h = cmd.args[base_arg + 4].value;
		if (cmd.cmd3 == 1005) {
			w -= rx;
			h -= ry;
		}
	}
	else {
		rx = ry = 0;
		w = screen->Width();
		h = screen->Height();
	}
	g->SetClipArea(rx, ry, w, h); //TODO: case when cmd.args.size() == 1
	cmd.clear();
}

void Grp::impl_objSetDigits(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	g->dig_number = cmd.args[base_arg + 1].value;
	g->SetUpdate();
	cmd.clear();
}

void Grp::impl_objNumOpts(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	g->dig_digit = cmd.args[base_arg + 1].value;
	int attr = g->attr;
	attr &= ~(GrpObj::DIG_ZERO | GrpObj::DIG_SIGN | GrpObj::DIG_PACK);
	if (cmd.args[base_arg + 2].value) attr |= GrpObj::DIG_ZERO;
	if (cmd.args[base_arg + 3].value) attr |= GrpObj::DIG_SIGN;
	if (cmd.args[base_arg + 4].value) attr |= GrpObj::DIG_PACK;
	g->attr = GrpObj::Attribute(attr);
	g->SetUpdate();
	cmd.clear();
}

void Grp::impl_objPattNo(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	g->SetSurfaceNum(cmd.args[base_arg + 1].value);
	cmd.clear();
}

void Grp::impl_objScale(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	int zoom = (cmd.args[base_arg + 1].value + cmd.args[base_arg + 2].value)/2; //FIXME: eurk
	zoom = zoom*256/100;
	g->SetZoomRotate(zoom, -1);
	cmd.clear();
}

void Grp::impl_objRotate(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));

	int angle = cmd.args[base_arg + 1].value;
	angle /= 10;
	if (angle < 0) {
		angle %= 360;
		angle += 360;
	}
	angle %= 360;
	g->SetZoomRotate(-1, angle);
	cmd.clear();
}

void Grp::impl_objPosDims(Cmd& cmd) {
	int base_arg = 0;
	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, true);

	VarInfo arg1 = cmd.args[base_arg + 1];
	VarInfo arg2 = cmd.args[base_arg + 2];

	int val1, val2;

	if (cmd.cmd3 == 1000)
		g->GetPos(0, val1, val2);
	else if (cmd.cmd3 == 1100)
		g->GetSrcGeom(val1, val2);

	cmd.SetFlagvar(arg1, val1);
	cmd.SetFlagvar(arg2, val2);
}

void Grp::impl_refresh(Cmd& cmd) {
	// 本来は grpstack clear らしい
	RefreshObj();
	// Princess Bride の中途 Staff roll
	// このタイミングで描画するのが都合がいいので、
	//シナリオループを抜けて描画を起動
	cmd.cmd_type = CMD_WAITFRAMEUPDATE;
}

void Grp::impl_bgmLoop(Cmd& cmd) {
	if (cmd.cmd4 == 0 || cmd.cmd4 == 2) {
		int count = 8000;
		if (cmd.cmd3 == 2)
			count = 0; //bgmPlay, play once
		music->PlayCDROM((char*)cmd.Str(cmd.args[0]), count);
		cmd.cmd_type = CMD_SAVECMD_ONCE;
	}
}

void Grp::impl_bgmStop(Cmd& cmd) {
	if (cmd.cmd4 == 0) {
		if (cmd.cmd3 == 5)
			music->StopCDROM(0);
		else if (cmd.cmd3 == 105)
			music->StopCDROM(cmd.args[0].value);
		cmd.cmd_type = CMD_SAVECMD_ONCE;
	}
}

void Grp::impl_playWav(Cmd& cmd) {
	if (cmd.cmd3 == 2) {
		music->PlaySE(cmd.Str(cmd.args[0]), 1); //loop
		cmd.cmd_type = CMD_SAVECMD_ONCE;
	}
	else {
		music->PlaySE(cmd.Str(cmd.args[0]));
		cmd.clear();
	}
	if (cmd.cmd3 == 1)
		status = WAIT_SE;
}

void Grp::impl_playSE(Cmd& cmd) {
	music->PlaySE(cmd.args[0].value);
	cmd.clear();
}

void Grp::impl_stopWav(Cmd& cmd) {
	if (cmd.cmd3 == 5)
		music->StopSE();
	else if (cmd.cmd3 == 105)
		music->StopSE(cmd.args[0].value);

	cmd.cmd_type = CMD_SAVECMD_ONCE;
}

void Grp::impl_SetVolMod(Cmd& cmd) {
	music->volmod[cmd.cmd3-0x8b6] = cmd.args[0].value;
	config->SetParam("#VOLMOD", 4, music->volmod[0], music->volmod[1], music->volmod[2], music->volmod[3]);
	cmd.clear();
}

void Grp::impl_GetVolMod(Cmd& cmd) {
	cmd.SetSysvar(music->volmod[cmd.cmd3-0x91a]);
}

void Grp::impl_koePlay(Cmd& cmd) {
	eprintf("play koe %d",cmd.args[0].value);
	if (cmd.cmd4 == 1) {
		eprintf(", para? %d",cmd.args[1].value);
	}
	eprintf("\n");
	char buf[1024]; sprintf(buf, "%d",cmd.args[0].value);
	if ( !(skip_mode & SKIP_TEXT)) music->PlayKoe(buf);
	cmd.clear();
}

/*It may be useful... or not.
void Grp::impl_objSwap(Cmd& cmd) {
	if (cmd.cmd1 == 1 && cmd.args.size() == 2) {
		SwapObj(cmd.args[0].value, cmd.args[1].value);
	}
	cmd.clear();
}*/

void Grp::impl_movPlay(Cmd& cmd) {
	if ( cmd.cmd4 == 0) {
		const char* str = cmd.Str(cmd.args[0]);
		int x = cmd.args[1].value;
		int y = cmd.args[2].value;
		int x2 = cmd.args[3].value;
		int y2 = cmd.args[4].value;
		eprintf("play movie ; name %s pos %d,%d - %d,%d\n",str,x,y,x2,y2);
		music->PlayMovie(str, x, y, x2, y2,1);
		status = WAIT_MOVIE;
		event.RegisterGlobalPressFunc(&Pressed, (void*)this);
		cmd.clear();
	}
}