/*
	PScript file for zmj AI Engine by ZMJ_123
	        流星蝴蝶剑任务增强补丁 v3.15
	   	( 真武林江湖群侠传 出品)

	 该版本以下功能适用于 1.07/1.08/9.07/9.08 版本
	     请不要修改该文件,避免运行崩溃!		  


增加的API 功能函数:
1. 支持 任务PST和地图PST 通信，任务PST通过函数可直接控制地图机关变化。 
2. 支持 任务PST直接函数控制 语音对白，播放语音文件。每句对白是单独语音文件,自由控制播放时间点。
3. 支持 任务等级进度等保存功能, 不限Meteor.exe版本, 比如9.07 也支持保存功能. （提供2种保存函数）
3. 支持 任务PST直接切换玩家的武器。(EnableSwitchWeapon后, 小键盘 4/8/6/2 默认也可切换)
4. 支持 任务PST直接移动玩家到任意地点 
5. 支持 任务PST直接指定玩家使用任意招式。
6. 支持 任务PST直接显示当前使用武器的招式表, 游戏中出现半透明窗口 (EnableSwitchWeapon后 小键盘 5 默认也可切换)
7. 支持 任务PST接收游戏中键盘1-9按键事件, 方便任务PST获得用户选择（1/2..../9)进行多条件开放剧情编写 (键盘事件 9001-9009)
8. 支持 任务PST直接切换当前玩家使用的人物模型。
9. 支持 任务PST禁用ESC键，防止玩家跳过某些桥段。
10.支持 语音自动合成, 对白直接转声音. (声音质量取决于采用的TTS语音引擎，TBD)

基本函数使用简介:
ZMJAI_SendEvent(1, 0, 0, 0);	//播放第1句话， 立即播放
ZMJAI_SendEvent(1, 1, 7, 1);	//播放第2句话，7秒后播放, 1表示结束此次声音事件请求(使用1表示AI引擎会立即处理)
ZMJAI_SendEvent(0, 10, 3, 1);	//启动机关10, 3秒后启动, 1表示结束此次机关事件请求(使用1表示AI引擎会立即处理)
ZMJAI_GameSave(int score, int scence, int val1, int val2, int val3, int val4) //保存本关得分, 以及其余5个用户自定义意义参数 到 pst文件
ZMJAI_SwitchWeapon(8);		//切换或通知SDK, 主角主武器为8（刀）
ZMJAI_MakePose(239);		//让主角使用 239这一招，双刺 左右A
ZMJAI_MoveLocat(8, 9, 10);	//把主角移动到 坐标为8,9,10的地点去。
ZMJAI_SwitchHero(19);		//使用p19 model 代替当前的 model(一般默认是p0 model)
ZMJAI_EnableSwitchWeapon(0);	//0 : 不允许使用小键盘(4/8/6/2 和 5) 切换武器和显示招式表,  1: 允许切换 (按 F12+小键盘5 也可以开关)
ZMJAI_EnableDumpstate(0);	//仅供调试bug使用， 0 ： 不生成dumpstate文件, 1: 生成, （会自动记录日志供调试，可能会导致系统卡顿）

ZMJAI_ShowSkillBook(6);		//游戏中出现半透明窗口显示主角当前使用武器的招式秘笈, -1则表示AI猜测当前武器(用过ZMJAI_SwitchWeapon或小键盘) 
//小键盘5 也可以弹出秘笈窗口，（该功能默认关闭。 调试可用 F11+小键盘5 来弹出， 依然需要先用 F12+小键盘5 先使能）
//低版本图像补丁与该功能冲突，高版本图像补丁则无问题(2019年6月后)

ZMJAI_EnableESCkey(0);		//0：不允许玩家使用ESC按键跳过剧情  1：允许玩家使用ESC按键跳过剧情

ZMJAI_SpeakVoice("Liang", "真武林"，2, 1);//播放这句话，Liang表示男声/Liang (Lily表示女声Lily， ...), 1表示女声, 2秒后开始发声, 1表示结束此次声音事件请求, 
//使用TTS语音引擎合成来说话,必须安装TTS引擎,默认是 NeoSpeech, 质量比较高.默认该功能关闭， 可通过ZMJAI_SendEvent 播放高质量音频替代。


########################### MOD 制作简介 ###########################
再也不用设计NPC吃道具来控制地图机关了，
再也不用全程看哑巴字幕了，
再也不用全程使用同一个主角了，
再也不用全程使用同一把武器了，
再也不用切换出来看招式表了，
再也不能强制ESC跳过剧情桥段了，

添加相关文件到流星目录:
1. AIHero32.dll
2. UIHero32.dll
3. PScriptLib\zmjeve.pst  
4. default.res

如何使能补丁:
二进制打开Meteor.exe, 搜索AVIFile32.dll, 替换成AIHero32.dll 即可


######################## 如何编写剧本：
为了保证游戏效率，只有任务PST和地图PST 都添加了如下代码，
补丁才工作起来。

  
任务PST: sn01_.pst
///////////////////////////////////////////////////////////////////
ZMJAI_OnEvent(int zmjEvt)
{
	//Say(1, "Recv event : ", zmjEvt);
	return zmjEvt-0;
}
#include "..\PScriptLib\zmjeve.pst"

OnUpdate()
{
	int evt = ZMJAI_OnHandler();
	
	// ... 收到事件后该干嘛干嘛
	// ... evt 9001-9009 是专指用户按了键盘 1-9
	// ... 其他事件主要是自己编写的任务和地图互传事件
	// ... 调用 ZMJAI_XXX 做你想做的吧 ！
}
///////////////////////////////////////////////////////////////////



地图PST：sn01.pst
///////////////////////////////////////////////////////////////////
ZMJAI_OnEvent(int zmjEvt)
{
	if(zmjEvt == -1)
	{
		return -1;
	}

	if(zmjEvt == 10)
	{
		Misc("transfer", 1, "D_wpBBox01"); // ... 做你想做的吧 ！
		return 11;						   // 11会传给 任务PST
	}

	if(zmjEvt == 12)
	{
		int i;
		for(i=1; i<=32; i++)
		{
			StoneOnAttack(i, 1000);		// ... 做你想做的吧 ！
		}

		return 13;						// 13会传给 任务PST
	}
	
	return -1;
}
#include "..\PScriptLib\zmjeve.pst"
///////////////////////////////////////////////////////////////////


########################  对白语音定义：sound\zmjAIvoice.lst
///////////////////////////////////////////////////////////////////
zmj1.mp3
zmj2.mp3
zmj3.mp3
zmj4.mp3
zmj5.mp3
...

音频名是可以随意指定编写,
ZMJAI_SendEvent(1, 0, 0, 0); //播放第1句话（zmj1.mp3）， 立即播放
///////////////////////////////////////////////////////////////////


########################   如何保存存档: 
ZMJAI_GameSave 或 ZMJAI_GameEncryptSave/ZMJAI_GameEncryptLoad
1. PScriptLib\zmjcfg.pst 该文件自动生成,可以直接包含在剧情PST里，函数调用保存的数据。 
2. Level\usrhero.sav     该文件是加密保存存档,最多保存120个数据，需要调用save/load函数


有问题可发信到：zhangmj_123@163.com。
常年不在线，勿怪！

*/

int g_iMagic[128];
int g_iVoice[128];
int g_iParam[128];
int g_iQuick[128];

int g_iLoadMap = 0;
int g_iLoadEvt = -1;

Scene_Printf(string tag, int val)
{
	if(0 == val)
	{
		GameCallBack(tag, 0);
	}
	
	if(1 == val)
	{
		AddNPC(tag);
	}
}

Scene_InitZmjEvent(int id)
{
	int i;

	if(id == 0)
	{	
		for(i=0; i<128; i++)
		{
			g_iMagic[i] = 0;
			g_iVoice[i] = 0;
			g_iParam[i] = 0;
			g_iQuick[i] = 0;
		}

		g_iMagic[0] = -1295592268;
		g_iMagic[1] = -1175011923;
		g_iMagic[2] = -906308894;
		g_iMagic[3] = -791952735;
		g_iMagic[4] = -1093807881;
		g_iMagic[5] =  1398227280;
		g_iMagic[6] =  0;
		g_iMagic[7] =  0;	
		
		Scene_Printf("<[ZMJAIv3]>", 0);
	}
	
	if(id == 1)
	{
		for(i=0; i<128; i++)
		{
			g_iMagic[i] = 0;
			g_iVoice[i] = 0;
			g_iParam[i] = 0;
			g_iQuick[i] = 0;			
		}

		g_iMagic[0] = -1180184654;
		g_iMagic[1] = -1214074170;
		g_iMagic[2] = -1040530198;
		g_iMagic[3] = -1259094870;
		g_iMagic[4] = -1093807881;
		g_iMagic[5] =  1398227280;
		g_iMagic[6] =  0;
		g_iMagic[7] =  0;

		g_iVoice[0] = -772619030;
		g_iVoice[1] = -1259094830;
		g_iVoice[2] = -907162395;
		g_iVoice[3] = -441404737;
		g_iVoice[4] = -1093807881;
		g_iVoice[5] =  1398227280;
		g_iVoice[6] =  0;
		g_iVoice[7] =  0;

		g_iParam[0] = -843144259;
		g_iParam[1] = -758200374;
		g_iParam[2] = -843132210;
		g_iParam[3] = -1547843638;	
		g_iParam[4] = -1093807881;
		g_iParam[5] =  1398227280;
		g_iParam[6] =  0;
		g_iParam[7] =  0;

		g_iQuick[0] = -1075000359;
		g_iQuick[1] = -1046299468;
		g_iQuick[2] = -1024603697;
		g_iQuick[3] = -1245394238;
		g_iQuick[4] = -1093807881;
		g_iQuick[5] =  1398227280;
		g_iQuick[6] =  0;
		g_iQuick[7] =  0;			

		Scene_Printf("<[ZMJAIv3]>", 1);
	}
		
	return 0;
}

Scene_SetZmjEvent(int evt, int waitime, int complete)
{
	int m = g_iMagic[7];
	int n = m*2+8;

	g_iMagic[n] = evt;
	g_iMagic[n+1] = waitime;

	g_iMagic[7] = g_iMagic[7]+1;
	if(1 == complete)
	{
		g_iMagic[4] = -1214067763;
	}

	return g_iMagic[7]-0;	
}

Scene_GetZmjEvent()
{
	if(g_iMagic[5] == 1346459475)
	{
		g_iMagic[6] = g_iMagic[6]+1;
	}

	if(g_iMagic[4] != -1110193451)
	{
		return -1;
	}

	int evt = g_iMagic[7];
	
	g_iMagic[7] = 0;
	g_iMagic[4] = -1093807881;
	
	return evt-0;
}

Game_GetZmjEvent()
{
	int val = 0;

	if(g_iParam[5] == 1346459475)
	{
		g_iParam[6] = g_iParam[6]+1;
	}

	if(g_iParam[4] != -1110193451)
	{
		return -1;
	}

	val = g_iParam[7];

	g_iParam[7] = 0;
	g_iParam[4] = -1093807881;
	
	return val-0;
}

Voice_SetZmjEvent(int sndID, int waitime, int complete)
{
	int m = g_iVoice[7];
	int n = m*2+8;

	g_iVoice[n] = sndID;
	g_iVoice[n+1] = waitime;

	g_iVoice[7] = g_iVoice[7]+1;
	if(1 == complete)
	{
		g_iVoice[4] = -1214067763;
	}

	return g_iVoice[7]-0;	
}

Voice_GetZmjEvent()
{
	if(g_iVoice[5] == 1346459475)
	{
		g_iVoice[6] = g_iVoice[6]+1;
	}

	if(g_iVoice[4] != -1110193451)
	{
		return -1;
	}
	
	g_iVoice[7] = 0;
	g_iVoice[4] = -1093807881;
	
	return 0;	
}

Realtime_GetZmjEvent()
{
	if(g_iQuick[5] == 1346459475)
	{
		g_iQuick[6] = g_iQuick[6]+1;
	}

	if(g_iQuick[4] != -1110193451)
	{
		return -1;
	}

	int evt = g_iQuick[7];
	
	g_iQuick[7] = 0;
	g_iQuick[4] = -1093807881;
	
	return evt-0;			
}	

ZMJAI_SendEvent(int stype, int evt, int waitime, int complete)
{
	//map = 0, sound = 1
	if(stype == 0)//"map")
	{
		Scene_SetZmjEvent(evt, waitime, complete);
	}
	
	if(stype == 1)//"sound")
	{
		Voice_SetZmjEvent(evt, waitime, complete);
	}
	
	return 1;
}

ZMJAI_GameSave(int score, int scence, int val1, int val2, int val3, int val4)
{
	g_iParam[8]  = score;
	g_iParam[9]  = scence;
	g_iParam[10] = val1;
	g_iParam[11] = val2;
	g_iParam[12] = val3;
	g_iParam[13] = val4;

	g_iParam[7]  = 6;

	Scene_Printf("<[CFGSAVE]>", 1);

	return 0;
}

ZMJAI_SetSaveData(int idx, int val)
{
	if(idx >= 120)
	{
		return 0;
	}

	g_iParam[8+idx] = val;
	return 0;
}

ZMJAI_GameEncryptSave(int paramCount)
{
	g_iParam[7]  = paramCount;

	//调用者在调用该函数前调用ZMJAI_SetSaveData自行赋值先, paramCount最大120

	Scene_Printf("<[SAFESAVE]>", 1);

	return 0;
}

ZMJAI_GameEncryptLoad()
{
	int paramCount = 0;
	g_iParam[7]  = 1;

	Scene_Printf("<[SAFELOAD]>", 1);

	paramCount = Game_GetZmjEvent();

	//调用者在调用该函数后需立刻调用ZMJAI_GetSaveData取出数值, paramCount最大120
	return paramCount-0;
}

ZMJAI_GetSaveData(int idx)
{
	if(idx >= 120)
	{
		return 0;
	}

	return g_iParam[8+idx]-0;
}

ZMJAI_SwitchWeapon(int wid)
{
	g_iParam[8]  = wid;
	g_iParam[7]  = 1;

	Scene_Printf("<[WEAPON]>", 1);

	return 0;
}

ZMJAI_EnableSwitchWeapon(int enable)
{
	g_iParam[8]  = enable;
	g_iParam[7]  = 1;

	Scene_Printf("<[SWITCHFUNC]>", 1);

	return 0;
}

ZMJAI_SwitchHero(int hid)
{
	g_iParam[8]  = hid;
	g_iParam[7]  = 1;

	Scene_Printf("<[HERO]>", 1);

	return 0;
}

ZMJAI_MakePose(int pid)
{
	g_iParam[8]  = pid;
	g_iParam[7]  = 1;

	Scene_Printf("<[POSE]>", 1);

	return 0;
}

ZMJAI_MoveLocate(int x, int y, int z)
{
	g_iParam[8]  = x;
	g_iParam[9]  = y;
	g_iParam[10] = z;

	g_iParam[7]  = 3;

	Scene_Printf("<[MOVE]>", 1); 

	return 0;
}

ZMJAI_ShowSkillBook(int wid)
{
	g_iParam[8]  = wid;
	g_iParam[7]  = 1;
	
	Scene_Printf("<[SKILL]>", 1);

	return 0;
}

ZMJAI_EnableESCkey(int use)
{
	g_iParam[8]  = use;
	g_iParam[7]  = 1;

	Scene_Printf("<[ESC]>", 1);

	return 0;
}

ZMJAI_SpeakVoice(string speaker, string words, int waitime, int complete)
{
	g_iParam[8]  = waitime;
	g_iParam[9]  = complete;

	string tags = "<[TTS:"+speaker+" | "+words+"]>";

	Scene_Printf(tags, 1);

	return 0;
}

ZMJAI_EnableDumpstate(int enable)
{
	g_iParam[8]  = enable;

	Scene_Printf("<[DUMPSTATE]>", 1);

	return 0;
}

ZMJAI_NotifyEventDone()
{
	g_iLoadEvt = -1;
}

Scene_OnIdle()
{
	if(0 == g_iLoadMap)
	{
		g_iLoadMap = 1;
		Scene_InitZmjEvent(0);		
	}

	int zmjEvt = Scene_GetZmjEvent();
	
	if(zmjEvt == -1)
	{
		return 0;
	}
	
	zmjEvt = ZMJAI_OnEvent(zmjEvt);
	
	if(zmjEvt != -1)
	{
		Scene_SetZmjEvent(zmjEvt, 0, 1);
	}
	
	return 0;
}

ZMJAI_OnHandler()
{
	if(0 == g_iLoadMap)
	{
		g_iLoadMap = 1;
		Scene_InitZmjEvent(1);		
	}

	int zmjEvt = -1;

	zmjEvt = Realtime_GetZmjEvent();
	if(zmjEvt != -1)
	{
		g_iLoadEvt = ZMJAI_OnEvent(zmjEvt);
		return g_iLoadEvt-0;
	}

	zmjEvt = Scene_GetZmjEvent();
	Voice_GetZmjEvent();
	Game_GetZmjEvent();

	if(zmjEvt != -1)
	{
		g_iLoadEvt = ZMJAI_OnEvent(zmjEvt);
		return g_iLoadEvt-0;
	}

	return g_iLoadEvt-0;
}