コード例 #1
0
ファイル: satori_tool.cpp プロジェクト: yagi/satori
bool Satori::calc_argument(const string &iExpression, int &oResult, bool for_non_talk)
{
	string exp = UnKakko(iExpression.c_str(), true, for_non_talk);
	if ( !calc(exp) ){
		return false;
	}
	oResult = zen2int(exp);
	return true;
}
コード例 #2
0
ファイル: satori.cpp プロジェクト: legokichi/satoriya-shiori
// 式を評価し、結果を真偽値として解釈する
bool Satori::evalcate_to_bool(const Condition& i_cond)
{
	string r;
	if ( !calculate(i_cond.c_str(), r) )
	{
		// 計算失敗
		return false;
	}
	return  ( zen2int(r) != 0 );
}
コード例 #3
0
bool	Satori::load(const string& iBaseFolder)
{
	GetSender().next_event();

	setlocale(LC_ALL, "Japanese");
#ifdef _WINDOWS
	_setmbcp(_MB_CP_LOCALE);
#endif

	mBaseFolder = iBaseFolder;
	GetSender().sender() << "■SATORI::Load on " << mBaseFolder << "" << std::endl;

#if POSIX
	// 「/」で終わっていなければ付ける。
	if (mBaseFolder[mBaseFolder.size() - 1] != '/') {
	    mBaseFolder += '/';
	}
#else
	// 「\」で終わっていなければ付ける。
	if (mBaseFolder[mBaseFolder.size() - 1] != '\\') {
	    mBaseFolder += '\\';
	}
#endif


#ifdef	_MSC_VER
	// 本体のあるフォルダをサーチ
	{
		TCHAR	buf[MAX_PATH+1];
		::GetModuleFileName(NULL, buf, MAX_PATH);
		char*	p = FindFinalChar(buf, DIR_CHAR);
		if ( p==NULL )
			mExeFolder = "";
		else {
			*(++p) = '\0';
			mExeFolder = buf;
		}
	}
	GetSender().sender() << "本体の所在: " << mExeFolder << "" << std::endl;
#endif // _MSC_VER

	// メンバ初期化
	InitMembers();

#ifdef	_MSC_VER
	// システムの設定を読んでおく
    OSVERSIONINFO	ovi;
    ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	::GetVersionEx(&ovi);
	string	os;
	if ( ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
		if ( ovi.dwMinorVersion == 0 ) { mOSType=SATORI_OS_WIN95; os="Windows 95"; }
		else if ( ovi.dwMinorVersion == 10 ) { mOSType=SATORI_OS_WIN98; os="Windows 98"; }
		else if ( ovi.dwMinorVersion == 90 ) { mOSType=SATORI_OS_WINME; os="Windows Me"; }
		else { mOSType = SATORI_OS_UNDEFINED; os="undefined"; }
	} else {
		if ( ovi.dwMinorVersion == 0 ) {
			if ( ovi.dwMajorVersion == 4 ) { mOSType=SATORI_OS_WINNT; os="Windows NT"; }
			else if ( ovi.dwMajorVersion == 5 ) { mOSType=SATORI_OS_WIN2K; os="Windows 2000"; }
		}
		else { mOSType = SATORI_OS_WINXP; os="Windows XP or later"; }
	}
	GetSender().sender() << "OS種別: " << os << std::endl;
	if ( mOSType==SATORI_OS_WIN95 ) {
		is_single_monitor = true;
	} else {
		BOOL (WINAPI* pEnumDisplayMonitors)(HDC,LPRECT,MONITORENUMPROC,LPARAM);
		(FARPROC&)pEnumDisplayMonitors = ::GetProcAddress(::GetModuleHandle("user32.dll"), "EnumDisplayMonitors");
		if ( pEnumDisplayMonitors==NULL ) {
			is_single_monitor = true;
		}
		else {
			RECT rectData[2];
			memset(rectData,0,sizeof(rectData));
			(*pEnumDisplayMonitors)(NULL,NULL,(MONITORENUMPROC)MonitorEnumFunc,(LPARAM)(rectData));

			max_screen_rect = rectData[0];
			desktop_rect = rectData[1];

			RECT*	rect;
			rect = &desktop_rect;
			GetSender().sender() << "プライマリデスクトップ: (" << 
				rect->left << "," << rect->top << "," << rect->right << "," << rect->bottom << ")" << std::endl;
			rect = &max_screen_rect;
			GetSender().sender() << "仮想デスクトップ: (" << 
				rect->left << "," << rect->top << "," << rect->right << "," << rect->bottom << ")" << std::endl;
			is_single_monitor = ( ::EqualRect(&max_screen_rect, &desktop_rect)!=FALSE );
			GetSender().sender() << (is_single_monitor ? 
				"モニタは一つだけと判断、見切れ判定を呼び出し元に任せます。" : 
				"複数のモニタが接続されていると判断、見切れ判定は里々が行います。") << std::endl;
		}
	}
#endif // _MSC_VER

	// 置換辞書読み取り
	strmap_from_file(replace_before_dic, mBaseFolder+"replace.txt", "\t");
	strmap_from_file(replace_after_dic, mBaseFolder+"replace_after.txt", "\t");

	// キャラデータ読み込み
	mCharacters.load(mBaseFolder + "characters.ini");
	for ( inimap::const_iterator i=mCharacters.begin() ; i!=mCharacters.end() ; ++i ) {
		const strmap& m = i->second;
		strmap::const_iterator j;

		// 置換辞書に追加
		j = m.find("popular-name");
		if ( j != m.end() && j->second.size()>0 ) 
			replace_before_dic[j->second + ":"] = string("\xff\x01") + zen2han(i->first); //0xff0x01はあとで変換
		j = m.find("initial-letter");
		if ( j != m.end() && j->second.size()>0 ) 
			replace_before_dic[j->second + ":"] = string("\xff\x01") + zen2han(i->first); //0xff0x01はあとで変換

		j = m.find("base-surface");
		if ( j != m.end() && j->second.size()>0 )
			system_variable_operation( string("サーフェス加算値") + i->first, j->second);
	}

	//for ( strmap::const_iterator j=replace_before_dic.begin() ; j!=replace_before_dic.end() ; ++j )
	//	cout << j->first << ": " << j->second << endl;

	// ランダマイズ
	randomize();


	//------------------------------------------

	// コンフィグ読み込み
	LoadDictionary(mBaseFolder + "satori_conf.txt", false);

	// 変数初期化実行
	GetSentence("初期化");	

	// SAORI読み込み
	Family<Word>* f = words.get_family("SAORI");

	mShioriPlugins->load(mBaseFolder);
	
	if ( f != NULL )
	{
		std::vector<const Word*> els;
		f->get_elements_pointers(els);

		for (std::vector<const Word*>::const_iterator i=els.begin(); i!=els.end() ; ++i)
		{
			if ( (*i)->size()>0 && !mShioriPlugins->load_a_plugin(**i) )
			{
				GetSender().sender() << "SAORI読み込み中にエラーが発生: " << **i << std::endl;
			}
		}

	}
	mShioriPlugins->load_default_entry();

	talks.clear();
	words.clear();

	//------------------------------------------

	// セーブデータ読み込み
	//bool oldConf = fEncodeSavedata;

	bool loadResult = LoadDictionary(mBaseFolder + "satori_savedata." + (fEncodeSavedata?"sat":"txt"), false);
	GetSentence("セーブデータ");
	bool execResult = talks.get_family("セーブデータ") != NULL;

	if ( ! loadResult || ! execResult ) {
		loadResult = LoadDictionary(mBaseFolder + "satori_savebackup." + (fEncodeSavedata?"sat":"txt"), false);
		GetSentence("セーブデータ");
		execResult = talks.get_family("セーブデータ") != NULL;
	}

	talks.clear();
	
	reload_flag = false;

	if ( variables.find("ゴースト起動時間累計秒") != variables.end() ) {
		sec_count_total = zen2ul(variables["ゴースト起動時間累計秒"]);
	}
	else if ( variables.find("ゴースト起動時間累計ミリ秒") != variables.end() ) {
		sec_count_total = zen2ul(variables["ゴースト起動時間累計ミリ秒"]) / 1000;
	}
	else {
		sec_count_total = zen2ul(variables["ゴースト起動時間累計(ms)"]) / 1000;
	}
	variables["起動回数"] = itos( zen2int(variables["起動回数"])+1 );

	// 「単語の追加」で登録された単語を覚えておく
	const std::map< string, Family<Word> >& m = words.compatible();
	for (std::map< string, Family<Word> >::const_iterator it = m.begin() ; it != m.end() ; ++it )
	{
		std::vector<const Word*> v;
		it->second.get_elements_pointers(v);
		for (std::vector<const Word*>::const_iterator itx = v.begin() ; itx < v.end() ; ++itx ) {
			mAppendedWords[it->first].push_back(**itx);
		}
	}

	//------------------------------------------

	// 指定フォルダの辞書を読み込み
	int loadcount = 0;
	strvec::iterator i = dic_folder.begin();
	if ( i==dic_folder.end() ) {
		loadcount += LoadDicFolder(mBaseFolder);	// ルートフォルダの辞書
	} else {
		for ( ; i!=dic_folder.end() ; ++i )
			loadcount += LoadDicFolder(mBaseFolder + *i + DIR_CHAR);	// サブフォルダの辞書
	}

	is_dic_loaded = loadcount != 0;

	//------------------------------------------

	secure_flag = true;

	system_variable_operation("単語群「*」の重複回避", "有効、トーク中");
	system_variable_operation("文「*」の重複回避", "有効");
	//system_variable_operation("単語群「季節の食べ物」の重複回避", "有効、トーク中");

	GetSentence("OnSatoriLoad");
	on_loaded_script = GetSentence("OnSatoriBoot");
	diet_script(on_loaded_script);

	GetSender().sender() << "loaded." << std::endl;

	GetSender().flush();
	return	true;
}
コード例 #4
0
ファイル: satori_tool.cpp プロジェクト: yagi/satori
bool	Satori::system_variable_operation(string key, string value, string* result)
{
	// mapにしようよ。
	
	if ( key == "喋り間隔" ) {
		talk_interval = zen2int(value);
		if ( talk_interval<3 ) talk_interval=0; // 3未満は喋らない
		
		// 喋りカウント初期化
		int	dist = static_cast<int>(talk_interval*(talk_interval_random/100.0));
		talk_interval_count = ( dist==0 ) ? talk_interval : (talk_interval-dist)+(random(dist*2));
		
		return true;
	}
	
	if ( key == "喋り間隔誤差" ) {
		talk_interval_random = zen2int(value);
		if ( talk_interval_random>100 ) talk_interval_random=100;
		if ( talk_interval_random<0 ) talk_interval_random=0;
		
		// 喋りカウント初期化
		int	dist = int(talk_interval*(talk_interval_random/100.0));
		talk_interval_count = ( dist==0 ) ? talk_interval : 
		(talk_interval-dist)+(random(dist*2));
		
		return true;
	}
	
	if ( key =="見切れてても喋る" ) {
		is_call_ontalk_at_mikire= (value=="有効");
		return true;
	}

	if ( key == "今回は喋らない" ) {
		return_empty=(value=="有効");
		return true;
	}
	
	if ( key == "スクリプトの一番頭" ) {
		header_script = value;
		return true;
	}

	if ( key == "呼び出し回数制限" ) {
		m_nest_limit = zen2int(value);
		if ( m_nest_limit < 0 ) { m_nest_limit = 0; }
		return true;
	}

	if ( key == "ジャンプ回数制限" ) {
		m_jump_limit = zen2int(value);
		if ( m_jump_limit < 0 ) { m_jump_limit = 0; }
		return true;
	}
	
	if ( key == "スコープ切り換え時" ) {
		append_at_scope_change = zen2han(value);
		return true;
	}

	if ( key == "さくらスクリプトによるスコープ切り換え時" ) {
		append_at_scope_change_with_sakura_script = zen2han(value);
		return true;
	}

	if ( key == "トーク開始時" ) {
		append_at_talk_start = zen2han(value);
		return true;
	}

	if ( key == "トーク終了時" ) {
		append_at_talk_end = zen2han(value);
		return true;
	}

	if ( key == "選択肢開始時" ) {
		append_at_choice_start = zen2han(value);
		return true;
	}
	
	if ( key == "選択肢終了時" ) {
		append_at_choice_end = zen2han(value);
		return true;
	}

	if ( key == "会話時サーフェス戻し" || key == "会話時サーフィス戻し" ) {
		if ( value == "有効" ) {
			surface_restore_at_talk = SR_NORMAL;
		}
		else if ( value == "強制" ) {
			surface_restore_at_talk = SR_FORCE;
		}
		else {
			surface_restore_at_talk = SR_NONE;
		}
		return true;
	}

	if ( key == "今回は会話時サーフェス戻し" || key == "今回は会話時サーフィス戻し" ) {
		if ( value == "有効" ) {
			surface_restore_at_talk_onetime = SR_NORMAL;
		}
		else if ( value == "強制" ) {
			surface_restore_at_talk_onetime = SR_FORCE;
		}
		else {
			surface_restore_at_talk_onetime = SR_NONE;
		}
		return true;
	}
	
	if ( key == "自動アンカー" ) {
		if ( value == "有効" ) {
			auto_anchor_enable = true;
			auto_anchor_enable_onetime = auto_anchor_enable;
		}
		else {
			auto_anchor_enable = false;
			auto_anchor_enable_onetime = auto_anchor_enable;
		}
		return true;
	}

	if ( key == "今回は自動アンカー" ) {
		if ( value == "有効" ) {
			auto_anchor_enable_onetime = true;
		}
		else {
			auto_anchor_enable_onetime = false;
		}
		return true;
	}
	
	if ( compare_head(key,  "サーフェス加算値") && aredigits(key.c_str() + const_strlen("サーフェス加算値")) ) {
		int n = zen2int(key.c_str() + const_strlen("サーフェス加算値"));
		surface_add_value[n]= zen2int(value);
		
		variables[string()+"デフォルトサーフェス"+itos(n)] = value;
		next_default_surface[n] = zen2int(value);
		if ( !is_speaked_anybody() )
			default_surface[n]=next_default_surface[n];
		return true;
	}

	if ( compare_head(key,  "デフォルトサーフェス") && aredigits(key.c_str() + const_strlen("デフォルトサーフェス")) ) {
		int n = zen2int(key.c_str() + const_strlen("デフォルトサーフェス"));
		next_default_surface[n]= zen2int(value);
		if ( !is_speaked_anybody() )
			default_surface[n]=next_default_surface[n];
		return true;
	}

	if ( compare_head(key,  "BalloonOffset") && aredigits(key.c_str() + const_strlen("BalloonOffset")) ) {
		int n = stoi(key.c_str() + const_strlen("BalloonOffset"));
		BalloonOffset[n] = value;
		validBalloonOffset[n] = true;
		return true;
	}

	if ( key == "トーク中のなでられ反応") {
		insert_nade_talk_at_other_talk= (value=="有効");
		return true;
	}
	
	if ( key == "なでられ持続秒数") {
		nade_valid_time_initializer = zen2int(value);
		return true;
	}

	if ( key == "なでられ反応回数") {
		nade_sensitivity = zen2int(value);
		return true;
	}

	if ( key == "デバッグ" ) {
		fDebugMode = (value=="有効");
		return true;
	}

	if ( key == "Log" ) {
		Sender::validate(value=="有効");
		return true;
	}

	if ( key == "RequestLog" ) {
		fRequestLog = (value=="有効");
		return true;
	}

	if ( key == "OperationLog" ) {
		fOperationLog = (value=="有効");
		return true;
	}
	
	if ( key == "ResponseLog" ) {
		fResponseLog = (value=="有効");
		return true;
	}
	
	if ( key == "自動挿入ウェイトの倍率" ) {
		rate_of_auto_insert_wait= zen2int(value);
		rate_of_auto_insert_wait = min(1000, max(0, rate_of_auto_insert_wait));
		variables["自動挿入ウェイトの倍率"] = int2zen(rate_of_auto_insert_wait);
		return true;
	}
	
	if ( key == "自動挿入ウェイトタイプ" ) {
		if ( value == "一般" ) {
			type_of_auto_insert_wait = 2;
			variables["自動挿入ウェイトタイプ"] = "一般";
		}
		else if ( value == "無効" ) {
			type_of_auto_insert_wait = 0;
			variables["自動挿入ウェイトタイプ"] = "無効";
		}
		else /* if ( value == "里々" ) */ {
			type_of_auto_insert_wait = 1;
			variables["自動挿入ウェイトタイプ"] = "里々";
		}
		return true;
	}
	
	if ( key == "辞書フォルダ" ) {
		strvec	words;
		split(value, ",",dic_folder);
		reload_flag=true;
		return true;
	}
	
	if ( key == "セーブデータ暗号化" ) {
		fEncodeSavedata = (value=="有効");
		return true;
	}
	
	if ( compare_head(key,"単語群「") && compare_tail(key,"」の重複回避") ) {
		variables.erase(key);
		words.setOC( string(key.c_str()+8, key.length()-8-12), value );
		return true;
	}

	if ( compare_head(key,"文「") && compare_tail(key,"」の重複回避") ) {
		variables.erase(key);
		talks.setOC( string(key.c_str()+4, key.length()-4-12), value );
		return true;
	}
	
	if ( key == "次のトーク" ) {
		variables.erase(key);
		int	count=1;
		while ( reserved_talk.find(count) != reserved_talk.end() )
			++count;
		reserved_talk[count] = value;
		sender << "次回のランダムトークが「" << value << "」に予\x96\xf1されました。" << endl;
		return true;
	}
	
	if ( compare_head(key,"次から") && compare_tail(key,"回目のトーク") ) {
		variables.erase(key);
		int	count = zen2int( string(key.c_str()+6, key.length()-6-12) );
		if ( count<=0 ) {
			sender << "トーク予\x96\xf1、設定値がヘンです。" << endl;
		}
		else {
			while ( reserved_talk.find(count) != reserved_talk.end() )
				++count;
			reserved_talk[count] = value;
			sender << count << "回後のランダムトークが「" << value << "」に予\x96\xf1されました。" << endl;
		}
		return true;
	}

	if ( key=="トーク予\x96\xf1のキャンセル" ) {
		if ( value=="*" )
			reserved_talk.clear();
		else
			for (map<int, string>::iterator it=reserved_talk.begin(); it!=reserved_talk.end() ; )
				if ( value == it->second )
					reserved_talk.erase(it++);
				else
					++it;
		return true;
	}

	if ( key == "SAORI引数の計算" ) {
		if (value=="有効")
			mSaoriArgumentCalcMode = SACM_ON;
		else if (value=="無効")
			mSaoriArgumentCalcMode = SACM_OFF;
		else
			mSaoriArgumentCalcMode = SACM_AUTO;
		return true;
	}
	
	if ( key == "辞書リロード" && value=="実行") {
		variables.erase(key);
		reload_flag=true;
		return true;
	}
	
	if ( key == "手動セーブ" && value=="実行") {
		variables.erase(key);
		if ( is_dic_loaded ) {
			this->Save();
		}
		return true;
	}
	
	if ( key == "自動セーブ間隔" ) {
		mAutoSaveInterval = zen2int(value);
		mAutoSaveCurrentCount = mAutoSaveInterval;
		if ( mAutoSaveInterval > 0 )
			sender << ""  << itos(mAutoSaveInterval) << "秒間隔で自動セーブを行います。" << endl;
		else
			sender << "自動セーブは行いません。" << endl;
		return true;
	}
	
	if ( key == "全タイマ解除" && value=="実行") {
		variables.erase(key);
		for (strintmap::const_iterator i=timer.begin();i!=timer.end();++i)
			variables.erase(i->first + "タイマ");
		timer.clear();
		return true;
	}
	
	if ( key == "教わること" ) {
		variables.erase(key);
		teach_genre=value;
		if ( result != NULL )
			*result += "\\![open,teachbox]";
		return true;
	}
	
	if ( key.size()>6 && compare_tail(key, "タイマ") ) {
		string	name(key.c_str(), strlen(key.c_str())-6);
		/*if ( sentences.find(name) == sentences.end() ) {
		result = string("※ タイマ終了時のジャンプ先 *")+name+" がありません ※";
		// セーブデータ復帰時を考慮
		}
		else */{
		int sec = zen2int(value);
		if ( sec < 1 ) {
			variables.erase(key);
			if ( timer.find(name)!=timer.end() ) {
				timer.erase(name);
				sender << "タイマ「"  << name << "」の予\x96\xf1がキャンセルされました。" << endl;
			} else
				sender << "タイマ「"  << name << "」は元から予\x96\xf1されていません。" << endl;
		} else {
			timer[name] = sec;
			sender << "タイマ「"  << name << "」が" << sec << "秒後に予\x96\xf1されました。" << endl;
		}
		}
		return true;
	}

	if ( key == "引数区切り追加" && value.size()>0 ) {
		variables.erase(key);
		mDelimiters.insert(value);
		return true;
	}
	
	if ( key == "引数区切り削除" && value.size()>0 ) {
		variables.erase(key);
		mDelimiters.erase(value);
		return true;
	}
	
	if ( compare_head(key, "Value") && aredigits(key.c_str() + 5) )
	{
		variables.erase(key);
		if(value!=""){
			mResponseMap[string()+"Reference"+key.substr(5)] = value;
		}else{
			mResponseMap.erase(string()+"Reference"+key.substr(5));
		}
		return true;
	}

	return	false;
}
コード例 #5
0
ファイル: satori_sentence.cpp プロジェクト: yagi/satori
int Satori::SentenceToSakuraScriptInternal(const strvec &vec,string &result,string &jump_to,ptrdiff_t &ip)
{
    // 再帰管理
    static	int nest_count=0;
    ++nest_count;
    //DBG(sender << "enter SentenceToSakuraScriptInternal, nest-count: " << nest_count << ", vector_size: " << vec.size() << endl);

    if ( m_nest_limit > 0 && nest_count > m_nest_limit ) {
        sender << "呼び出し回数超過" << endl;
        --nest_count;
        return 0;
    }

    static const int basewait=3;

    strvec::const_iterator it = vec.begin();
    std::advance(it,ip);

    for ( ; it != vec.end() ; ++it) {
        const char*	p = it->c_str();
        //DBG(sender << nest_count << " '" << p << "'" << endl);

        if ( it==vec.begin() && strncmp(p, "→", 2)==0 ) {
            p+=2;
            updateGhostsInfo();	// ゴースト情報を更新

            if ( ghosts_info.size()>=2 ) {	// そもそも自分以外にゴーストはいるのか。
                string	temp = p;
                vector<strmap>::iterator i=ghosts_info.begin();
                ++i; // 自分は飛ばす
                for ( ; i!=ghosts_info.end() ; ++i ) {
                    string	name = (*i)["name"];
                    sender << "ghost: " << name <<endl;
                    if ( compare_head(temp, name) ) {// 相手を特定
                        mCommunicateFor = name;
                        p += mCommunicateFor.size();
                        break;
                    }
                }


                if ( i==ghosts_info.end() ) {	// 特定しなかった場合
                    // ランダム
                    //int n = random(ghosts_info.size()-1))+1;
                    //assert( n>=1 && n < ghosts_info.size());
                    mCommunicateFor = (ghosts_info[1])["name"];

                    // あかん、隣で起動している〜〜にならん
                }
            }
        }

        // 選択肢	\q?[id,string]
        if ( strncmp(p, "_", 2)==0 ) {
            if ( strlen(p)>1023 )
                continue;
            char	buf[1024];
            strncpy(buf, p+2, sizeof(buf) / sizeof(buf[0]));

            char*	choiced = buf;
            char*	id = (char*)strstr_hz(buf, "\t"); // 選択肢ラベルとジャンプ先の区切り

            result += append_at_choice_start;
            if ( id == NULL ) {
                string	str=UnKakko(choiced);
                //result += string("\\q")+itos(question_num++)+"["+str+"]["+str+"]";
                result += "\\q["+str+","+str+"]";
            } else {
                *id++='\0';
                while ( *id=='\t' ) ++id; // 選択肢ラベルとジャンプ先の区切り
                //result += string("\\q")+itos(question_num++)+"["+UnKakko(id)+"]["+UnKakko(choiced)+"]";
                result += "\\q["+UnKakko(choiced)+","+UnKakko(id)+"]";
            }
            result += append_at_choice_end;

            continue;
        }

        // ちょっと微妙な存在意義。
        if ( strncmp(p, "\\s", 2)==0 ) {
            if ( !is_speaked(speaker) ) {
                if ( surface_changed_before_speak.find(speaker) == surface_changed_before_speak.end() ) {
                    surface_changed_before_speak.insert(map<int,bool>::value_type(speaker,is_speaked_anybody()) );
                }
            }
        }

        // ジャンプ
        if ( strncmp(p, ">", 2)==0 || strncmp(p, "≫", 2)==0 ) {
            strvec	words;
            split(p+2, "\t", words, 2); // ジャンプ先とジャンプ条件の区切り

            if ( words.size()>=2 ) {
                string	r;
                if ( !calculate(words[1], r) )
                    break;
                if ( zen2int(r) == 0 ) {
                    sender << "*計算結果が0だったため、続行します。" << endl;
                    continue;
                }
            }

            if ( words.size() >= 1 ) {
                jump_to = UnKakko(words[0].c_str(),false,true);
            }
            else {
                jump_to.erase();
            }

            if ( strncmp(p, "≫", 2)==0 ) {
                ip = std::distance(vec.begin(),it) + 1;
                --nest_count;
                return 2;
            }
            else {
                ip = std::distance(vec.begin(),it) + 1;
                --nest_count;
                return 1;
            }
        }

        // 変数を設定
        if ( strncmp(p, "$", 2)==0 ) {
            const char* v;
            string	value;
            bool	do_calc=false;
            p+=2;
            if ( (v=strstr_hz(p, "\t"))!=NULL ) { // 変数名と変数に設定する内容の区切り
                value = UnKakko(v+1,false,true);
            }
            else if ( (v=strstr_hz(p, "="))!=NULL || (v=strstr_hz(p, "="))!=NULL ) {
                value = UnKakko(v+((*v=='=') ? 1 : 2),false,true);
                do_calc=true;
            }
            else {
                //v = p+strlen(p);
                //value="";
                result += "\\n※ $による変数代入文には、タブによる区切りか、=による計算式が必要です ※\\n\\n[half]'" + value +"'";
                break;
            }

            string	key(p, v-p);
            key = UnKakko(key.c_str(),false,true);

            if ( key=="" ) {
                result += "$"; // $そのまま表示
                speaked_speaker.insert(speaker);
            }
            else if ( aredigits(zen2han(key)) ) {
                sender << "$" << key << " 数字のみの変数名は扱えません." << endl;
                erase_var(key);	// 存在抹消
            }
            else if ( value=="" ) {
                sender << "$" << key << "/cleared." << endl;
                erase_var(key);	// 存在抹消
                system_variable_operation(key, "", &result);//存在抹消したものがシステム変数かも!
            }
            else {
                bool isOverwritten;
                bool isSysValue;

                // "0"は代入先を先に参照する時、エラーを返さないように。
                string *pstr = GetValue(key,isSysValue,true,&isOverwritten,"0");

                if ( do_calc ) {
                    if ( !calculate(value, value) )
                        break;
                    if ( aredigits(value) ) {
                        value = int2zen(stoi(value));
                    }
                }

                sender << "$" << key << "=" << value << "/" <<
                       (isOverwritten ? "written." : "overwritten.")<< endl;

                if ( pstr ) {
                    *pstr = value;
                }
                system_variable_operation(key, value, &result);
            }
            continue;
        }

        // 通常処理
        while ( p[0] != '\0' ) {
            string	c=get_a_chr(p);	// 全角半角問わず一文字取得し、pを一文字すすめる

            if ( c=="(" ) {	// 何かを取得・挿入
                character_wait_exec;
                result += KakkoSection(p);
            }
            else if ( c=="\xff" ) {	//内部特殊表現
                c=get_a_chr(p);
                if ( c=="\x01" ) { //0xff0x01=スコープ切り替え 後に半角数値が1文字続く
                    c=get_a_chr(p);
                    int speaker_tmp = stoi(c.c_str());
                    if ( is_speaked(speaker) && speaker != speaker_tmp ) {
                        result += append_at_scope_change;
                        chars_spoken += 2;
                    }
                    speaker = speaker_tmp;
                    character_wait_clear(2);
                    if ( speaker == 0 ) {
                        result += "\\0";
                    }
                    else if ( speaker == 1 ) {
                        result += "\\1";
                    }
                    else {
                        result += "\\p[" + c + "]";
                    }
                }
            }
            else if ( c==":" ) {	// スコープ切り替え - ここは二人を想定。
                if ( is_speaked(speaker) ) {
                    result += append_at_scope_change;
                    chars_spoken += 2;
                }
                speaker = (speaker==0) ? 1 : 0;
                character_wait_clear(2);
                result += (speaker ? "\\1" : "\\0");
            }
            else if ( c=="\\" || c=="%" ) {	// さくらスクリプトの解釈、というか解釈のスキップ。

                if (*p=='\\'||*p=='%') {	// エスケープされた\, %
                    result += c + *p++;
                    continue;
                }

                const char*	start=p;
                string	cmd="",opt="";

                while (!_ismbblead(*p) && (isalpha(*p)||isdigit(*p)||*p=='!'||*p=='-'||*p=='*'||*p=='&'||*p=='?'||*p=='_'))
                    ++p;
                cmd.assign(start, p-start);

                if ( cmd == "_?" || cmd == "_!" ) { //エスケープ処理 この間に自動タグ挿入はしない
                    const char* e1 = strstr(p,"\\_?");
                    if ( ! e1 ) {
                        e1 = strstr(p,"\\_!");
                    }

                    if ( e1 ) {
                        character_wait_exec;
                        result += c;
                        result += cmd;

                        opt.assign(p,e1-p+3);
                        opt = UnKakko(opt.c_str());

                        p = e1 + 3; //endtag

                        result += opt;
                        continue;
                    }
                }

                if (*p=='[') {
                    const char* opt_start = ++p;
                    while (*p!=']') {
                        if (p[0]=='\\' && p[1]==']')	// エスケープされた]
                            ++p;
                        p += _ismbblead(*p) ? 2 : 1;
                    }
                    opt.assign(opt_start, p++ -opt_start);
                    opt=UnKakko(opt.c_str());
                }

                if ( cmd=="n" ) {
                    // 改行
                    character_wait_clear(2);
                }
                else if ( ( (cmd=="0" || cmd=="h") && speaker==1) || ( (cmd=="1" || cmd=="u") && speaker==0) ) {
                    // スコープ切り替え
                    if ( is_speaked(speaker) ) {
                        result += append_at_scope_change_with_sakura_script;
                        chars_spoken += 2;
                    }
                    speaker = stoi(cmd);
                    character_wait_clear(2);
                }
                else if ( cmd=="p" && aredigits(opt) ) {
                    int spktmp = stoi(opt);
                    if ( speaker != spktmp ) {
                        // スコープ切り替えonSSP/CROW
                        if ( is_speaked(speaker) ) {
                            result += append_at_scope_change_with_sakura_script;
                            chars_spoken += 2;
                        }
                        speaker = spktmp;
                        character_wait_clear(2);
                    }
                }
                else if ( cmd=="s" ) {
                    //サーフィス切り替えの前にウェイトは済ませておくこと
                    character_wait_exec;
                }
                else if ( cmd=="_q" ) {
                    if ( ! is_quick_section ) { //これからクイックセクションなのでウエイトを全部消化
                        character_wait_exec;
                    }
                    is_quick_section = ! is_quick_section;
                }

                if ( opt!="" ) {
                    //sender << "ss_cmd: " << c << "," << cmd << "," << opt << endl;
                    result += c + cmd + "[" + opt + "]";
                } else {
                    //sender << "ss_cmd: " << c << "," << cmd << endl;
                    result += c + cmd;
                }


                //result += string(start, p-start);
            }
            else {	// 通常の一文字
                character_wait_exec;

                speaked_speaker.insert(speaker);
                result += c;
                chars_spoken += c.size();
                if ( c=="。" || c=="、" ) {
                    character_wait_clear(c=="、"?1:2);
                }
            }

        }
        character_wait_clear(2);
        result += "\\n";
    }

    //DBG(sender << "leave SentenceToSakuraScriptInternal, nest-count: " << nest_count << endl);
    --nest_count;
    return 0;
}