狂~劇情狂 发表于 2015-5-3 10:29:00

PHP SCX Editor 基本教程

事前准备:

第一,下载 PHP 编译程序 5.2.17 版文件(下载位置:https://s000.tinyupload.com/index.php?file_id=08497628607876979134),并解压到 C:\PHP 5.2.17\ 或其他你指定的目录之下。


第二,安装一个 PHP 集成开发环境程序,例如免费的 PHP Designer 2007 个人版(下载位置:https://download.cnet.com/3001-10248_4-10575026.html)


第三,下载 AOHH 原编、 AzZzRu 改编的 PHP SCX Editor 脚本代码(下载位置:https://download.cnet.com/3001-10248_4-10575026.html),并解压到 C:\PHP SCX Editor\ 或其他你指定的目录之下。







狂~劇情狂 发表于 2015-5-3 10:36:14

工具设定:

首先,设定你的 PHP 集成开发环境程序,让它使用 PHP 编译程序 5.2.17 版文件;如果使用 PHP Designer 2007 个人版,就到 Tools > Preferences > Debug & Preview 处,将 PHP Interpreter 设成 C:\PHP 5.2.17\php.exe ,将 Settings - use php.ini 设成 C:\PHP 5.2.17\php.ini 。

其次,修改 C:\PHP SCX Editor\Compiler.php ,将 Aoe2Path="C:\Program Files (x86)\Microsoft Games\Age of Empires II" 的值改成你游戏程序的目录路径(即 Scenario 活页夹所有位置,使用 MOD 版本游戏时要注意)。

最后,修改 C:\PHP SCX Editor\Scripts.php ,将 $V='My Map.scx' 的值改成要读取的 *.scx 文件名,将 $V='My New Map' 改成要储存的 *.scx 文件名。

狂~劇情狂 发表于 2015-5-3 10:36:19

编辑方法:

PHP SCX Editor 脚本主要运作方式是先读取你指定的 *.scx 档案(预设为 My Map.scx),期间会保留原有档案的地形、单位、游戏者、提示文字等信息,但是全部旧有触发事件都会遭到删除;在那以后,它会将你在 Scripts.php 及 Function.php 编写的修改动作施加上去,并且生成另一个新的 *.scx 档案(预设为 My New Map.scx)。

一般来说, Function.php 是用来定义供 Scripts.php 呼叫的函式,而修改动作的内容结构基本都在 Scripts.php 之内。

透过在 PHP Designer 2007 个人版开启 Compiler.php (File > Open 、 CTRL + O)并将其除错执行两次(Tools > Debug 、 F5), PHP SCX Editor 脚本就能够完成上述工作,而于 Scripts.php 及 Function.php 编写修改动作具体作法可以参考后列范例。

狂~劇情狂 发表于 2015-5-3 10:36:25

预设范例:

Scripts.php 预设的内容包含了呼叫五组函式动作范例:
# 修改地形动作范例
Example_Modify_Terrain();

# 增加物件动作范例
Example_Add_Objects();

# 新增触发事件动作范例
Example_Make_Some_Trigs();

# 写入指示讯息动作范例
Example_Write_Instructions();

# 修改设定数据动作范例
Example_Modify_Constants();当然,它们都是已经在 Function.php 预先定义好的函式。在你日后修改自己的 *.scx 档案时,当然有必要将它们全数删除以免它们影响你的作品,但于初学阶段你不妨先参考它们的具体内容来学习如何编写修改动作。

修改地形动作范例的效果是让整张地图的所有格子地形变成泥地 1 (地形编号为 6 ):
function Example_Modify_Terrain (){
   
    global $V;

    # 读取地图大小
    $mapSize = SCX::$data_serial['terrain']['size'];

    # 从 y 轴、 x 轴坐标依次相加将格子地形变成泥地 1
   for($y=0; $y<$mapSize; $y++)
      for($x=0; $x<$mapSize; $x++)
            SCX::$data_serial['terrain']['data'][$x.','.$y]=6;
}增加物件动作范例的效果是在整张地图上随机为不同游戏者放置民兵共 500 人:
function Example_Add_Objects(){
   
    global $V;

    # 读取地图大小
    $mapSize = SCX::$data_serial['terrain']['size'];

    # 根据随机 y 轴、 x 轴坐标、单位方向及所属游戏者放置民兵(物件编号为 74 )
    for($i=0; $i<500; $i++){
      $x = rand(0,$mapSize-1);
      $y = rand(0,$mapSize-1);
      $r = rand(0,360);
      NewObject($i,rand(1,8),74,array($x,$y),$r);
    }
}新增触发事件动作范例的效果是在先建立一个在游戏时间十秒后送出交谈讯息的触发事件,再为每位游戏者建立二十秒后毁灭步兵群组物件的触发事件,最后重写另一个在游戏时间十秒后送出交谈讯息的触发事件:
function Example_Make_Some_Trigs (){
   
    global $V;
   
    # 名称为 Send Message P1 的触发事件,起始状态开启而不循环
    Trig("Send Message P1",1,0);
    Cond_Timer(10); # 状况为定时器 10 秒
    Efft_Chat(1,"Hello World"); # 效果为向游戏者 1 送出交谈讯息 Hello World
   
    # 为每一位游戏者建立相同的触发事件,定时器 20 秒后毁灭该游戏者所有步兵
    foreach($V as $p){
      Trig("Kill Soldiers P$p",1,0); # 名称为 Kill Soldiers P 加上游戏者编号的触发事件,起始状态开启而不循环
      Cond_Timer(20); # 状况为定时器 20 秒
      Efft_KillG($p,SOLDIER);} # 效果为毁灭该游戏者步兵群组物件

    # 第一个触发事件写死硬码的方法,注意名称改为 Send Message P1 (2)
    Trig("Send Message P1 (2)",1,0);
    Cond('Timer',array(
      T => 10
      ));
    Efft('SendChat',array(
      P => 1,
      X => "Hello World"
      ));

}写入指示讯息动作范例的效果是在任务、斥候、提示、胜利、失败栏填上简单文字内容:
function Example_Write_Instructions(){
   
    global $V;

    # 任务栏填上 Hello World
    SCX::$data_serial['message']['directive']='Hello World';
   
    # 斥候栏填上 This is scouts
    SCX::$data_serial['message']['directive']='This is scouts';
   
    # 提示栏填上 This is hints
    SCX::$data_serial['message']['directive']='This is hints';
   
    # 胜利栏填上 This is victory
    SCX::$data_serial['message']['directive']='This is victory';
   
    # 失败栏填上 You lost
    SCX::$data_serial['message']['directive']='You lost';

}修改设定数据动作范例的效果是将游戏者人数设为 8 、启始时代设为黑暗时代、资源数量设为 200 、人口限制设为 75 。
function Example_Modify_Constants(){
   
    global $V;

    # 游戏者人数设为 8
    SCX::$data_serial['players']['count']=8;

    # 替每一位游戏者设定启始时代、资源数量及人口限制
    foreach($V as $p){
      SCX::$data_serial['player'][$p]['boolean']=1; # 启用该游戏者
      SCX::$data_serial['player'][$p]['food']=200;
      SCX::$data_serial['player'][$p]['wood']=200;
      SCX::$data_serial['player'][$p]['rock']=200;
      SCX::$data_serial['player'][$p]['gold']=200;
      SCX::$data_serial['player'][$p]['popl']=75;
      SCX::$data_serial['disabled']['initial'][$p]=0;
    }

}

狂~劇情狂 发表于 2015-5-3 10:36:29

语法基础(触发事件):

新增觸發事件
Trig ( "觸發事件名稱", 觸發事件起始狀態, 觸發事件循環, 任務目標顯示, "觸發事件敘述", 敘述順序);觸發事件名稱:字符。
觸發事件起始狀態:數值。開啟時為 1 ,關閉時為 0 。預設值為 1 。
觸發事件循環:數值。循環時為 1 ,不循環時為 0 。預設值為 0 。
任務目標顯示:數值。顯示時為 1 ,不顯示時為 0 。預設值為 0 。
觸發事件敘述:字符。觸發事件說明文字,任務目標顯示時會顯示在任務欄。預設值為空白。
敘述順序:數值。任務目標顯示時觸發事件敘述在任務欄顯示的次序。預設值為 0 。

觸發事件狀況:基本結構
Cond('狀況條件類型',array(Q=>數量, R=>資源屬性, U=>物件類型編號, P=>來源遊戲者編號, H=>科技編號, T=>計時器秒數, , A=>array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), Z=>人工智慧信號值, K=>逆向判斷));逆向判斷:數值, Userpatch 1.4 版專用。啟用時為 True 或 -256 ,關閉時為 False 或 -1 。

觸發事件狀況:將指定物件帶到指定區域
Cond('BringObjectToArea',array(S=>array(設定物件編號), A=>array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標))));
觸發事件狀況:將指定物件帶給指定物件

現行版本腳本代碼懷疑因為作者失誤,並未容許設定觸發事件狀況的第二個物件,故無法順利編寫將指定物件帶給指定物件狀況。

觸發事件狀況:擁有物件
Cond('OwnObjects',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, Q=>數量));
觸發事件狀況:擁有較少物件
Cond('OwnFewerObjects',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, Q=>數量));
觸發事件狀況:區域中的物件
Cond('ObjectsInArea',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, Q=>數量, A=>array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)));
觸發事件狀況:摧毀物件
Cond_Destroy(array(設定物件編號));或
Cond('DestroyObject',array(S=>array(設定物件編號)));
觸發事件狀況:捕獲物件
Cond('CaptureObject',array(P=>來源遊戲者編號, S=>array(設定物件編號)));
觸發事件狀況:累積屬性
Cond_Accumulate(來源遊戲者編號, 資源屬性, 屬性數量);或
Cond('AccumulateAttribute',array(P=>來源遊戲者編號, R=>資源屬性, Q=>屬性數量));
觸發事件狀況:科技研發
Cond_Researched(來源遊戲者編號, 科技編號);或
Cond('ResearchTechnology',array(P=>來源遊戲者編號, H=>科技編號));
觸發事件狀況:計時器
Cond_Timer(計時器秒數);或
Cond('Timer',array(T=>計時器秒數));
觸發事件狀況:選擇物件
Cond('ObjectSelected',array(S=>array(設定物件編號)));
觸發事件狀況:人工智慧信號
Cond('AISignal',array(Z=>人工智慧信號值));
觸發事件狀況:遊戲者被擊敗
Cond_Died(來源遊戲者編號);或
Cond('PlayerDefeated',array(P=>來源遊戲者編號));
觸發事件狀況:目標物件
Cond('ObjectHasTarget',array(S=>array(設定物件編號)));目標物件狀況雖然容許設定物件類型、群組、類別和下一個物件,但都沒有實際效用。

觸發事件狀況:可視物件
Cond('ObjectVisible',array(S=>array(設定物件編號)));
觸發事件狀況:不可視物件
Cond('ObjectNotVisible',array(S=>array(設定物件編號)));
觸發事件狀況:研發科技
Cond('ResearchingTechnology',array(P=>來源遊戲者編號, H=>科技編號));
觸發事件狀況:駐守單位
Cond('UnitsGarrisoned',array(S=>array(設定物件編號), Q=>數量));
觸發事件狀況:難易度
Cond('DifficultyLevel',array(Q=>難易度等級));難易度等級:最易為 4 ,標準為 3 ,中等為 2 ,困難為 1 ,較難為 0 。

觸發事件效果:基本結構

Efft('效果類型',array(Q=>數量, R=>資源屬性, U=>物件類型編號, P=>來源遊戲者編號, E=>目標遊戲者編號, H=>科技編號, T=>計時器秒數, I=>觸發事件名稱, L=>array(目標位置 X 坐標, 目標位置 Y 坐標), A=>array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), G=>物件群組編號, Y=>物件類別編號, N=>指令欄編號, X=>文字訊息, D=>聲音檔案名稱, S=>array(設定物件編號)));
觸發事件效果:改變外交策略

現行版本腳本代碼懷疑因為作者失誤,未容許設定觸發事件效果的外交狀態,故此無法順利編寫改變外交策略效果。

觸發事件效果:科技研發
Efft_Research(來源遊戲者編號, 科技編號));

Efft('ResearchTechnology',array(P=>來源遊戲者編號, H=>科技編號));
觸發事件效果:送出交談訊息
Efft_Chat(來源遊戲者編號, 文字訊息));

Efft('SendChat',array(P=>來源遊戲者編號, X=>文字訊息, D=>聲音檔案名稱));
觸發事件效果:播放聲音
Efft_PlaySound(來源遊戲者編號, D=>聲音檔案名稱);

Efft('PlaySound',array(P=>來源遊戲者編號, D=>聲音檔案名稱));
觸發事件效果:貢品
Efft('SendTribute',array(P=>來源遊戲者編號, E=>目標遊戲者編號, R=>資源屬性, Q=>屬性數量));
觸發事件效果:開啟城門
Efft('UnlockGate',array(S=>array(設定物件編號)));
觸發事件效果:關閉城門
Efft('LockGate',array(S=>array(設定物件編號)));
觸發事件效果:啟動觸發事件
Efft_Act(觸發事件名稱);

Efft('ActivateTrigger',array(I=>觸發事件名稱));
觸發事件效果:關閉觸發事件
Efft_Deact(觸發事件名稱);

Efft('DeactivateTrigger',array(I=>觸發事件名稱));
觸發事件效果:人工智慧設定目標
Efft('AIScriptGoal',array('P=>來源遊戲者編號, Q=>數量));
觸發事件效果:建立物件
Efft_Create(來源遊戲者編號, 物件類型編號, array(目標位置 X 坐標, 目標位置 Y 坐標));

Efft('CreateObject',array(S=>array(P=>來源遊戲者編號, U=>物件類型編號, L=>array(目標位置 X 坐標, 目標位置 Y 坐標)));
觸發事件效果:指派物件
Efft('TaskObject',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), L=>array(目標位置 X 坐標, 目標位置 Y 坐標)));
觸發事件效果:宣告勝利
Efft('DeclareVictory',array(P=>來源遊戲者編號));
觸發事件效果:毀滅物件
Efft('KillObject',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標))));
觸發事件效果:移除物件
Efft('RemoveObject',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標))));
觸發事件效果:改變視野
Efft_ChangeView(來源遊戲者編號, array(目標位置 X 坐標, 目標位置 Y 坐標));

Efft('ChangeView',array(P=>來源遊戲者編號, L=>array(目標位置 X 坐標, 目標位置 Y 坐標)));
觸發事件效果:卸載
Efft_Unload(來源遊戲者編號, 設定物件編號, array(目標位置 X 坐標, 目標位置 Y 坐標));

Efft('Unload',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), L=>array(目標位置 X 坐標, 目標位置 Y 坐標)));
觸發事件效果:改變所有權
Efft('ChangeOwnership',array(P=>來源遊戲者編號, E=>目標遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標))));
觸發事件效果:巡邏
Efft('Patrol',array(S=>array(設定物件編號), L=>array(目標位置 X 坐標, 目標位置 Y 坐標)));
觸發事件效果:顯示指令
Efft_Chat(計時器秒數, 指令欄編號, 文字訊息));

Efft('DisplayInstructions',array(T=>計時器秒數, N=>指令欄編號, X=>文字訊息, D=>聲音檔案名稱));
觸發事件效果:清除指令
Efft('ClearInstructions',array(N=>指令欄編號));
觸發事件效果:凍結單位
Efft('FreezeUnit',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標))));
觸發事件效果:使用進階選項按鈕
Efft('UseAdvancedButtons');
觸發事件效果:摧毀目標
Efft('DamageObject',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), Q=>數量));
觸發事件效果:設置基礎
Efft('ChangeObjectName',array(S=>array(P=>來源遊戲者編號, U=>物件類型編號, L=>array(目標位置 X 坐標, 目標位置 Y 坐標)));
觸發事件效果:改變目標名字
Efft_Name(設定物件編號, 文字訊息));

Efft('ChangeObjectName',array(S=>array(設定物件編號), X=>文字訊息));
觸發事件效果:改變目標的生命值
Efft('ChangeObjectHP',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), Q=>數量));
觸發事件效果:改變目標的攻擊力
Efft('ChangeObjectAttack',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標)), Q=>數量));
觸發事件效果:停止部隊
Efft('StopUnit',array(P=>來源遊戲者編號, U=>物件類型編號, G=>物件群組編號, Y=>物件類別編號, S=>array(設定物件編號), array(array(設定區域左端 X 坐標, 設定區域左端 Y 坐標),array(設定區域右端 X 坐標, 設定區域右端 Y 坐標))));






狂~劇情狂 发表于 2015-5-3 10:36:37

狂~劇情狂 发表于 2015-5-3 10:36:40

条顿武士 发表于 2015-5-3 12:19:08

atila 发表于 2015-5-3 16:19:30

本帖最后由 atila 于 2015-5-3 16:23 编辑

期待完成施工

troytroytroy 发表于 2015-5-4 09:46:40

請問一下,這樣編輯的優勢是什麼?

狂~劇情狂 发表于 2015-5-4 12:51:04

tryotryotryo 发表于 2015-5-4 09:46
請問一下,這樣編輯的優勢是什麼?
暫時實際應用主要用於設計大規模的聯機場景,國外平台上較受歡迎的幾個聯機場景Roll The Dice 、 Death Crossroads Blood 及 Dynamo Castle Blood Arena 俱以此編輯而成,而其觸發事件數量均超越一萬多--基本都是無法單靠遊戲地圖編輯器完成的數字(觸發事件數量越多,越會增加讀取清單時間,而在一萬多觸發事件時單是滾動輪軸已經顯得相當緩慢,更不用說後期除錯想找出要改動地方難度極大);前兩者的最明顯特性是超大量的隨機事件,後者則是完全自動指派玩家單位動作及在任務欄即時刷新戰況。

此種編輯方法可能表面上對單機戰役作者(尤其是 MOD 戰役)沒什麼明顯的優勢, AOK Heaven 論壇上發佈該工具的帖子亦有類似聲音(https://aok.heavengames.com/cgi-bin/forums/display.cgi?action=ct&f=26,42243,,30),認為編程式的設計工作可能是在扼殺創意。

事實上,單機戰役設計若能好好應用此工具的話亦有相當的價值,比如早前我發佈的密鋪物件集成以及拒馬裝飾即是用它設計而來--由此可見,若將不能夠直接在編輯器進行複製地圖的密鋪物件組合轉化成為代碼(複製地圖功能無法保存幀數,而放置的投射物改變幀數只能夠透過外部修改達致),那就可以簡單讓比較複雜的密鋪物件在多個戰役不同位置進行調用。

除了編寫海量觸發事件、複雜密鋪裝飾,其餘能夠大幅加速完成的工序還有多對多的單位 ID 修改(lo_oj的修改器目標編號只能有一個,亦即僅容許進行單對單或多對單的編輯)、以及去除地圖水域海岸一類地形微調(無岸深水與海域只能用外部修改達致)等等。另外,前引連結內提到的將現實地貌 gif 圖直接轉為遊戲地形功能,對重視再現真實地形的歷史題材戰役作者自然也有一定價值。

總的來說,如此編輯優勢是要快速完成本來就算利用 AoK Trigger Studio 也極具重複性的工序,這點在聯機競技類作品較為明顯,而設計形式相對下顯得單線直接的單機戰役就較小可能應用到它;只是若具編程背景的設計者有需要進行同類工作時,其優勢仍能夠很快突顯出來。

我是谁004 发表于 2015-5-5 19:26:05

也就是说,使用PHP语言来帮助生成SCX,是吧。

标本 发表于 2015-5-7 09:12:10

全部旧有触发会删除?略有失望。
开启关闭触发用触发名称定位?重名改名了怎么办?

狂~劇情狂 发表于 2015-5-7 13:23:00

标本 发表于 2015-5-7 09:12
全部旧有触发会删除?略有失望。
开启关闭触发用触发名称定位?重名改名了怎么办?

此一工具許多功能還沒完善,大概也是其版本號只有0.2的原因,前邊也提到一些觸發事件狀況與效果並未提供寫法,而且它的設計概念明顯預設每一觸發事件名稱不同

至於觸發改名,若果是後續在遊戲地圖編輯器進行的話當然就沒有問題(實際儲存是觸發編號而非名稱),但在編輯代碼時作者便一定要記著取代其他位置了

另外,聽說內測版本具備將舊有觸發轉成代碼的功能,但是等待至今仍未公開

mxzwt2 发表于 2017-3-27 20:03:17

version要怎么填写?
页: [1]
查看完整版本: PHP SCX Editor 基本教程