XS脚本指南 + 4则实用函数(2021.4.27大更新)
目录第〇部分 - 导语
第一部分 - 要素说明
1、
2、
3、[地图] 选项卡 - [脚本文件名] 输入栏
4、[条件-脚本调用]
5、[效果-脚本调用]
第二部分 - XS脚本语法
1、决定版内部XS函数表
2、XS脚本通用语法
第三部分 - 实用XS函数 四则
1、实用XS函数四则
2、使用例
>>第〇部分 - 导语<<
决定版 Build 42848~43210 的编辑器更新了一个全新的模块:XS脚本。
(XS 或许代表 eXternal Subroutine,外部子程序)
你会发现游戏目录里多了点东西:
[*][个人数据文件夹]:C:\Users\<用户名>\Games\Age of Empires 2 DE\<长串数字>\resources\_common\xs\default0.xs
[*][游戏根目录]:C:\Program Files\Steam\steamapps\common\AoE2DE\resources\_common\xs\Constants.xs
编辑器里也多了这些东西:
[*][地图] 选项卡:[脚本文件名] 输入栏
[*][触发器] 选项卡:[条件-脚本调用]、[效果-脚本调用]
它们的作用和使用方法是:
>>第一部分 - 要素说明<<
1、:
正如上面所示,你可以在两个xs文件夹中放置.xs文件,编辑器读取.xs文件时会去这两个文件夹里找。
[*]其中 [个人数据文件夹] 下的xs文件夹,如果你切换游戏账号就不能用了。
[*]而 [游戏根目录] 下的,则是所有账号共用的。
2、:
存放在上述任意 下的.xs文件。
你可以在其中编写一些函数,供触发条件效果调用。函数编写指南见下文。
官方的 有两个,它们的作用分别是:
[*]default0.xs:临时储存xs函数/语句的文件。当你在 [条件-脚本调用] 里编写函数文本,在点击任意触发/条件/效果后,编辑器会检查语法错误,无误则会写入到default0.xs中。所以,不要在这里编写内容,否则会被实时覆盖掉的。
[*]Constants.xs:官方定义的通用常数文件。这里定义了整个游戏都能用的常数。目前只囊括了大部分资源编号。可能之后会添加别的常数进来。你可以在任意xs脚本里调用这些常数。
注意事项:
[*]xs脚本文件最好是 UTF-8 编码。否则,xsChatData指令(后面会讲) 将无法正常发送文字。
[*]xs脚本文件中,除了注释内容外的正文代码中,必须 全程使用ASCII字符 (即英文字母、英文标点符号、空格等),不能用全角、汉字等非ASCII字符,即使是用英文双引号括起的字符串。
函数语法规则 见 第二部分。
3、[地图] 选项卡 - [脚本文件名] 输入栏:
在这里写上你要调用的.xs文件名(不需要包括拓展名“.xs”)。
输入栏最多容纳21个字符(中文/英文字符都只算1个字符)。
写上内容后,需要到 [触发器] 选项卡里点选任意 触发/条件/效果,编辑器才会尝试去 中读取对应的文件,并检查语法错误。
我们不妨把这里指定的xs脚本文件称为 [场景xs脚本]。
4、[条件-脚本调用]:
你可以在它的输入框中编写自定义函数,就像在 中一样。
在点击任意触发/条件/效果后,会检查语法错误,无误则写入到 中。而且!即使触发是关闭的,也能正常写入。
条件特性:
[*]这个条件是恒成立的。
[*]这个条件会在触发处于开启状态时 (注意不是激活状态),不断自行调用其中的函数。
[*]即使触发关闭,这个条件也能正常定义函数,并写入到 中。
这个条件可以用于:
[*]①先调用函数做一次修改,再让其他条件检测资源量或变量(要注意条件执行顺序的先后哦)。
[*]②见下图。默默定义一些函数,供效果调用(最好把它放在一个永久关闭的触发里,避免自行调用)。
输入框规范 见 [效果-脚本调用] ↓↓↓。
函数语法 见 [第二部分] ↓↓↓。
5、[效果-脚本调用]:
你可以在这里 写上一个自定义函数名来调用运行该函数,也可以直接 自己定义一个函数并自行调用。
这个效果会尝试去调用 [场景xs脚本] 或 或 其自身输入框 中的函数。
脚本输入框内容的规范——
[*] 调用已有函数:
[*]假设我们已经在别处定义好了一个函数名为“chatFood()",那么你在这个效果的输入框里写“chatFood” 或 “chatFood()” 都可以调用它。
[*] 定义函数并自行调用:
[*]你也可以在其中定义函数,比如:
void chatStone()
{
int stone = xsPlayerAttribute(1,2);
xsChatData("P1's Stone = %d", stone);
}
这样这个函数会写入到 中,而且效果也会自行调用该函数。
[*] 允许注释:
[*]chatFood()//这是一句注释。
[*]void chatStone()
{
int stone = xsPlayerAttribute(1,2);
}
// 这是一句注释(只能放在函数本体后面,不能在函数前或函数中插入注释)。
[*] 不允许:
[*]chatFood() //这是一句注释。
[*]chatFood()//这 是一句注释。
[*]chatFood()
//这是一句注释。
[*]chatFood() (←注:末尾有个空格)
(换而言之,如果你只想调用一个函数,那输入框内绝对不要出现空格或换行符(字符串内的除外)!否则内容会被视为自定义函数并分析语法错误。)[*] 允许在框内调用多个函数,但只有后一行生效,前一行会被忽略(建议分开放到多个效果里):
[*]chatFood()
chatStone()
注意!不要尝试在 [效果-脚本调用] 中调用一个带参数的函数,比如用getAB调用函数getAB(int a=1, int b=0){}。否则,有概率发生:①游戏崩溃。②该效果执行一次后,本局任何XS函数都将无法工作。你可以用一个无参数的函数来调用getAB函数。
注意!不要在第一次进入编辑器时在 [效果-脚本调用] 输入栏中连续写入超过6个字符,否则会导致游戏闪退(截至2021年4月27日)。可以尝试先进入场景测试一次后返回编辑器,再开始编辑 [效果-脚本调用]。
>>第二部分 - XS脚本语法<<
1、决定版内部XS函数:
函数说明与教程整理自:
(决定版 遗朝制作组 官方网站上的XS脚本简短指南与函数对照表)
(帝国时代3 ESOC论坛 XS脚本详细指南)
(divy1211的Github 的 AoE2DE_UGC_Guide - XS部分)
“勉强够用级别”的符号说明:
float xsPlayerAttribute(int playerNumber, int resourceID)
float 表示 xsPlayerAttribute函数 会返回一个浮点数,
int playerNumber 表示 参数playerNumber 应该为一个整数。
(void 表示 空,即没有返回内容或数据没有类型)
(string 表示 字符串)
以上只是小部分,更多XS符号及语法请看 [2、XS脚本通用语法]。
## 普通函数
// 获取指定玩家的指定资源ID的资源量(浮点数)
float xsPlayerAttribute(int playerNumber, int resourceID)
// 设置指定玩家的指定资源ID的资源量
void xsSetPlayerAttribute(int playerNumber, int resourceID, float value)
// 示例:
// 获取玩家1的食物储量(资源#0),乘以2倍后添加到玩家1的木材储量(资源#1)上。
// cAttributeFood = 0,cAttributeWood = 1(来自官方常数Constants.xs)
void food_x2_add_to_wood(){
float food_amount = xsPlayerAttribute(1, cAttributeFood);
float wood_amount = xsPlayerAttribute(1, cAttributeWood);
wood_amount = wood_amount + food_amount * 2;
xsSetPlayerAttribute(1, cAttributeWood, wood_amount);
}
// 获取触发变量 ID 的值
int xsTriggerVariable(int variableID)
// 设置触发变量 ID 的值
void xsSetTriggerVariable(int variableID, int value)
// 示例:
// 获取触发变量#9的值,然后复制给触发变量#10。
void copy_variable(){
int var_value = xsTriggerVariable(9);
int var_value_new = var_value;
xsSetTriggerVariable(10, var_value_new);
}
// 返回游戏中的玩家数量(不包括盖亚女神)
int xsGetNumPlayers()
// 在游戏左上角的聊天框中显示一条消息,类似于 [效果-发送聊天],不过此函数的消息可被全体玩家接收。
// 这通常用于验证xs脚本是否正常运作、输出运行结果,对测试很有帮助。
// 你可以在字符串中用 %d 引用 value。如 xsChatData("A = %d.", 5) 会显示为“A = 5”。
// 当然,你也可以直接将字符串与常数或变量相连。如 xsChatData("A = " + num_a + "."),
// 这样你可以传入多个数值,而且也支持浮点数等其他数据类型。
// 注意,当前xs脚本的字符串只能使用 ASCII 字符(英文字母、数字、英文符号等)
// value 是可选参数,可以不填。
void xsChatData(string message, int value)
// 发送玩家1的食物量到聊天。
void chatFood()
{
int food = xsPlayerAttribute(1,0);
xsChatData("Player 1's food = %d", food);
xsChatData("Player 1's food = " + food);
}
// 返回当前游戏时间(以秒为单位)
int xsGetTime()
// 修改指定单位或科技的指定属性为指定值
// 对于特定的玩家,effectID 为要使用的效果id
// 这基本相当于 [修改属性] 效果
// 更多相关信息,请参考 [UserPatchup-effect 触发效果拾遗 (附使用例)]
// effectID=科技类型,unitOrTechnologyID=科技对象,attributeOrOperation=科技属性,
// value=科技数值,playerNumber=游戏者编号(设为-1作用于全体)
// 至于up-effect的最后一位“格式”,或许可以借助XS的浮点数实现其对应的百分比功能。
void xsEffectAmount(int effectID, int unitOrTechnologyID, int attributeOrOperation, int value, int playerNumber)
// 示例:
void upgrade()
{
// 升级(3) 单位83(男村民) 为 单位4(步弓手) 数值0(参数忽略) 作用于玩家1
xsEffectAmount(3,83,4,0,1);
// 升级(3) 单位280(轻型投石车)为 单位8(长弓兵) 数值0(参数忽略) 作用于全体玩家-1
xsEffectAmount(3,280,8,0,-1);
}
## 规则
// 禁用给定规则
void xsDisableRule(string ruleName)
// 禁用规则自身(在规则内调用此函数,即可作用于当前规则)
// 这可以使规则只执行一次而不自动循环
void xsDisableSelf()
// 启用给定规则
void xsEnableRule(string ruleName)
// 如果给定规则已启用,则返回true
bool xsIsRuleEnabled(string ruleName)
// 设置给定规则的优先级(高优先级的规则会先执行)
void xsSetRulePriority(string ruleName, int priority)
// 设置规则自身的优先级(在规则内调用此函数,即可作用于当前规则)
void xsSetRulePrioritySelf(int priority)
// 设置给定规则的最小(执行)间隔
void xsSetRuleMinInterval(string ruleName, int interval)
// 设置规则自身的最小(执行)间隔(在规则内调用此函数,即可作用于当前规则)
void xsSetRuleMinIntervalSelf(int interval)
// 设置给定规则的最大(执行)间隔
void xsSetRuleMaxInterval(string ruleName, int interval)
// 设置规则自身的最大(执行)间隔(在规则内调用此函数,即可作用于当前规则)
void xsSetRuleMaxIntervalSelf(int interval)
// 启用给定规则组中的所有规则
void xsEnableRuleGroup(string ruleGroupName)
// 禁用给定规则组中的所有规则
void xsDisableRuleGroup(string ruleGroupName)
// 如果给定规则组中的所有规则都已启用,则返回true
bool xsIsRuleGroupEnabled(string ruleGroupName)
## 向量处理
// 返回给定向量的x、y或z分量
float xsVectorGetX(vector v)
float xsVectorGetY(vector v)
float xsVectorGetZ(vector v)
// 读取给定向量,将x、y或z分量更改为给定值后,作为一个新的向量返回
vector xsVectorSetX(vector v, float x)
vector xsVectorSetY(vector v, float y)
vector xsVectorSetZ(vector v, float z)
// 返回一个向量,其x, y和z分量分别为指定值
vector xsVectorSet(float x, float y, float z)
// 返回给定向量的长度(模长)
float xsVectorLength(vector v)
// 返回给定向量的标准化(normalized)版本(即方向不变,长度变为1)
vector xsVectorNormalize(vector v)
## 数组处理
// 创建一个给定大小(项数)的、指定数据类型的数组
// 所有值都会被初始化为 defaultValue(默认值)
// 返回一个唯一地标识这个数组的 arrayID。请务必储存到一个变量中,因为对数组进行各种操作都得借助 arrayID。
// 数组名(uniqueName)无实际意义,但必须是唯一的,不能与其他数组冲突
int xsArrayCreateInt(int size, int defaultValue, string uniqueName)
int xsArrayCreateFloat(int size, float defaultValue, string uniqueName)
int xsArrayCreateBool(int size, bool defaultValue, string uniqueName)
int xsArrayCreateString(int size, string defaultValue, string uniqueName)
int xsArrayCreateVector(int size, vector defaultValue, string uniqueName)
// 设置给定数组中指定索引处的值
// index 是数组中元素的索引。它从0开始,这意味着数组的第一个元素为索引0,第二个元素为索引1,以此类推。
// 例如,一个数组为 ,那么 xsArraySetInt(arrayID, 2, 100) 后,数组将变为
// 返回 1
int xsArraySetInt(int arrayID, int index, int value)
int xsArraySetFloat(int arrayID, int index, float value)
int xsArraySetBool(int arrayID, int index, bool value)
int xsArraySetString(int arrayID, int index, string value)
int xsArraySetVector(int arrayID, int index, vector value)
// 获取给定数组中指定索引处的值
// 返回获取到的值
// 如果你试图访问不存在的数组(无效的arrayID)或不存在的索引(负索引或超过数组长度的索引)中的值,则返回数据类型的默认值:
// int defaultInt = -1
// float defaultFloat = -1.0
// bool defaultBool = false
// string defaultString = ""
// vector defaultVector = vector(0, 0, 0)
int xsArrayGetInt(int arrayID, int index)
float xsArrayGetFloat(int arrayID, int index)
bool xsArrayGetBool(int arrayID, int index)
string xsArrayGetString(int arrayID, int index)
vector xsArrayGetVector(int arrayID, int index)
// 调整指定数组的大小
// 返回 1
int xsArrayResizeInt(int arrayID, int newSize)
int xsArrayResizeFloat(int arrayID, int newSize)
int xsArrayResizeBool(int arrayID, int newSize)
int xsArrayResizeString(int arrayID, int newSize)
int xsArrayResizeVector(int arrayID, int newSize)
// 获取指定数组的大小
int xsArrayGetSize(int arrayID)
## 数学运算
// 返回数字的绝对值(大小)
float abs(float x)
// 返回数字的平方根
float sqrt(float x)
// 返回 x 的 y 次方
float pow(float x, float y)
// 返回以弧度表示的角度的正弦值
float sin(float x)
// 返回以弧度表示的角度的余弦值
float cos(float x)
// 返回以弧度表示的角度的正切值
float tan(float x)
// 返回给定长度的反正弦(arcsin)
float asin(float x)
// 返回给定长度的反余弦(arccos)
float acos(float x)
// 返回给定长度的反正切(arctan)
float atan(float x)
// 这推测是 atan2(y, x) 函数,但它却只需要一个输入?
// thxDE
float atan2(float x)
## 未知函数
// 丢弃(?Blogs out) 所有XS数组
// 现在还不知道这个函数是做什么的
int xsDumpArrays()
// 返回当前的情境(context)玩家ID(情境玩家ID在帝国时代3的AI脚本函数中,表示一局游戏中正在使用该脚本的AI玩家ID)
// 现在还不知道这个函数是做什么的
int xsGetContextPlayer()
// 设置当前的情境(context)玩家ID (!!如果你不知道你在做什么,那就不要做)
// 现在还不知道这个函数是做什么的
void xsSetContextPlayer(int playerID)
// 会导致游戏崩溃... thxDE
??? xsBreakPoint()
// 安设一个运行时间(runtime)事件。不要使用这个
// 现在还不知道这个函数是做什么的
bool xsAddRuntimeEvent(string foo, string bar, int something)
// 运行函数的隐藏XSFID。!!谨慎使用
// 现在还不知道这个函数是做什么的
int xsGetFuntionID(string functionName)
2、XS脚本通用语法:
/*
XS代表外部子程序(eXternal Subroutine)。
在帝国时代3中,XS是c++的一个极其简化的版本,用于编写AI脚本、随机地图脚本、触发器、UI和modding中使用的某些命令。
在帝国时代2决定版中,XS脚本的功能应该与帝国时代3的类似,具体有待探索。
本文大部分来自于 https://eso-community.net/viewtopic.php?p=436182
*/
// 行注释:从左边双斜杠开始,直到本行末,都是注释。
/* 块注释。
首尾标志之间的都是注释内容。
*/
———— 变量 ————
语法:
<数据类型> <变量名称> = <值>;
例如:
int c_Variable_1 = 100;
// 定义整数变量“c_Variable_1”值为100
float c_Percent_random = 0.33;
// 定义浮点数变量“c_Percent_random”值为0.33
格式要求:
1、变量名称必须是唯一的,只能使用 A~Z、a~z、0~9、下划线(_)组合而成,且首个字符不能是数字。
2、xs语言区分大小写,所以 Resource 和 ReSourCe 和 resource 可以标识不同的变量。
3、变量名称不能跟xs语言的保留字相冲突(比如int是保留字,所以我们不能定义一个叫“int”的变量)
4、值必须符合数据类型的格式要求(比如我们不能给整数变量设一个浮点数值:int c_Variable_1 = 5.2)
5、末尾必须带一个英文分号,作为一句代码的结尾标识(会有无需分号的特例,但暂时不考虑)
特殊说明:
1、为了方便管理,浮点数值为0.0时,将被视为一个正数
2、由于xs语言是用分号作为分隔,因此我们可以在同一行内塞下多条语句(如:int A = 1;int B = 2; )
3、等号(=)是赋值标志,表示把 右侧的值 赋值给 左侧的变量
数据类型一览:
整数(int):
int c_Variable_1 = 100;
int zeroCelcius = 0;
浮点数(float):
float AbsoluteZero = -273.15;
float PI = 3.141592;
float c_Percent_random = 0.33;
字符串(string):
string myUnit = "Archer";
string chat_text = "这是一段文字";
string descript = "字符串内使用双引号,要用反斜杠转义:\"Hello\"。";
// 【\"Hello\"】若打印,则会显示为【"Hello"】
string descript = "字符串内使用反斜杠,要用反斜杠转义:\\Yes/。";
// 【\\Yes/】若打印,则会显示为【\Yes/】
布尔值(bool):
bool aBoolVariable = true;
bool anotherBoolVariable = false;
向量(vector):
暂无示例,在帝国时代3的xs脚本中用于储存XYZ三轴坐标。
更多类型:
待补充。
数据类型之间的转换:
并不能将原有的变量本身转为另一种类型,只能通过新定义一个变量、赋值来转换类型。
比如:
浮点数→整数:
float a = 23.945;
int b = a; // 结果是 b = 23(不舍入,只保留整数部分)
整数→浮点数:
int a = 23;
float b = a; // 结果是 b = 23.0
布尔值→整数:
bool a = true;
int b = a; // 结果是 b = 1(true→1,false→0)
任何类型→字符串:
bool a = true;
string b = ""+a; // 结果是 b = "true"(必须如此连接才行)
任何类型→布尔值:
int a = 0;
bool b = a; // 结果是 b = flase(0→flase,非0→true)
———— 算术运算符 ————
· 赋值=
· 加+ (也用于字符串连接)
· 减-
· 加1++
· 减1--
· 乘*
· 除/
· 取余 %
示例:
int a = 0;
int b = 1;
int c = a + b; // 定义变量时也可以用算术符。这样 c 的值为 1。
float d = 3.2 * 0.5;// 定义变量时也可以用算术符。这样 d 的值为 1.6。
c = 101; // c ← 101
a = b; // a ← b
a = a + 10; // a 加 10
a ++; // 等效于 a = a + 1;
a --; // 等效于 a = a - 1;
a = a / 10; // a 除 10
// b = -b; // 这是错误的语法
b = 0 - b; // 这是正确的语法:取相反数
b = -1 * b; // 这是正确的语法:取相反数
string word1 = "Hello";
string word2 = "World";
string fullWord = word1 + " " + word2 + "!"; // fullWorld连接成为了"Hello World!"
———— 比较运算符 ————
· 等于==
· 不等于!=
· 大于>
· 小于<
· 大于或等于>=
· 小于或等于<=
示例:
bool b = ( 0 == 1 );// b = false
b = ( 0 < 1 ); // b = true
———— 逻辑运算符 ————
· 与&&(二者都为true时,整体才为true。否则为false)
· 或||(二者只要有一个为true,整体就为true。二者全为false则整体为false)
示例:
bool a = ( 0 == 1 );// a = false
bool b = ( 0 <= 1 );// b = true
bool c = a && b; // c = false
c = a || b; // c = true
———— 常数 ————
为了方便使用,我们可以给数字定义常数名称,要用的时候就通过名称来调用即可。
语法:
const <数据类型> <常数名称> = <值>;
示例:
const float PI = 3.141592;
float r = 2.0;
float S = 0.0;
S = PI * r * r; // 圆面积 = πr^2
———— if条件判断结构 ————
语法:
// 下面的语句表示:
// 当布尔值1为true时,执行指令A,否则:
// 再判断,当布尔值2为true时,执行B和C,再否则:
// 无条件执行指令D。
if ( 布尔值1 )
{
指令 A;
}
else if ( 布尔值2 )
{
指令 B;
指令 C;
}
else
指令 D;
要点:
1、语法中的“(布尔值)”表示判断语句,用于返回true或false。
2、花括号用于标识代码块。如果有多条指令(如指令B和C),一定要用花括号。如果只有一条指令(如指令A和D),则花括号可有可无。
3、if{}是必需的,else if{}和else{}是可选的。
示例:
// (1) 如果a与b不等,则将a的值设为b。如果a与b相等,则什么也不做。
if (a != b)
a = b;
// (2) 如果a大于等于0,则绝对值取a。反之(即a小于0),则绝对值取-a。
if (a >= 0)
Abs = a;
else
Abs = 0 - a;
———— while循环结构 ————
while循环用于在某个条件为真时重复一组指令。一旦它变为false,循环就会中断,指令就不再执行了。
语法:
while( 布尔值 )
{
指令组;
}
示例:
int i = 0;
string message = "正在从0数到9:";
while( i < 10 )
{
message = message + i + ",";
i++;
}
message = message + "完成!";
示例详解:
i从0开始
第一次判定:i==0 小于 10,所以进入循环。循环末尾i++使得i==1。
第二次判定:i==1 小于 10,所以继续循环。循环末尾i++使得i==2。
……
第九次判定:i==8 小于 10,所以继续循环。循环末尾i++使得i==9!
第十次判定:i==9 小于 10,所以继续循环。循环末尾i++使得i==10!
第十一次判定:i==10 等于 10,所以跳出循环。执行后面的指令。
总共执行了10次循环体。
循环结束后i为10,message为"正在从0数到9:0,1,2,3,4,5,6,7,8,9,完成!"。
重要事项:
务必避免死循环(永远无法跳出的循环)!因为那样的话,游戏或电脑可能会卡死,有时甚至连任务管理器也救不了你。
———— for循环结构 ————
for循环用于重复一组指令一定次数。每轮循环结束时变量都会+1或-1,会循环直到不再满足比较条件。
语法:
for( 变量名称 = 初始值; 比较运算符 最终值 )
{
指令组;
}
要点:
1、[变量] [初始值] [最终值]都必须为整数型。
2、递增方向 & 循环次数:
· 如果 [比较运算符] 为 <, [变量] 将逐次递增+1,最终会循环 [最终值-初始值] 次。
· 如果 [比较运算符] 为 <=,[变量] 将逐次递增+1,最终会循环 [最终值-初始值+1] 次。
· 如果 [比较运算符] 为 >, [变量] 将逐次递减-1,最终会循环 [初始值-最终值] 次。
· 如果 [比较运算符] 为 >=,[变量] 将逐次递减-1,最终会循环 [初始值-最终值+1] 次。
3、[变量] 的值在for循环内可以调用,但无法修改。
示例:
string message = "正在从0数到9:";
for( i = 0; < 10 )
{
message = message + i + ",";
}
message = message + "完成!";
示例详解:
i从0开始
第一次判定:i==0 小于 10,所以进入循环。循环末尾i递增使得i==1。
第二次判定:i==1 小于 10,所以继续循环。循环末尾i递增使得i==2。
……
第九次判定:i==8 小于 10,所以继续循环。循环末尾i递增使得i==9!
第十次判定:i==9 小于 10,所以继续循环。循环末尾i递增使得i==10!
第十一次判定:i==10 等于 10,所以跳出循环。执行后面的指令。
总共执行了10次循环体。
循环结束后i为10,message为"正在从0数到9:0,1,2,3,4,5,6,7,8,9,完成!"。
———— break中断 & continue跳过 ————
循环体中遇到break,将会强制跳出循环体
示例:
int i = 0;
while( i < 50 )
{
if ( i == 10 )
break;
i++;
}
示例详解:
i从0开始
i为0~9时,循环会正常进行
i==9时,循环正常进行,i++变为i==10
i==10时,满足if条件,于是执行if结构里的break,跳出循环。
11~49都不会再执行了。
循环体中遇到continue,将会强制跳过后续指令,直接开始下一循环
示例:
string message = "正在从0数到9:";
for( i = 0; < 10 )
{
if ( i == 5 )
{
message = message + "跳过,";
continue;
}
message = message + i + ",";// i==5时,这句会被跳过
}
示例详解:
i从0开始
i为0~4时,循环会正常进行
i==4时,循环正常进行,i递增变为i==5
i==5时,满足if条件,于是执行if结构里的continue,跳过修改message那一句进入下一循环,i递增变为i==6。
i为6~9时,循环会正常进行。
整个循环体结束后,message为"正在从0数到9:0,1,2,3,4,跳过,6,7,8,9"
———— switch结构 ————
(待补充,C++的switch教程详见 https://www.runoob.com/cplusplus/cpp-switch.html)
(xs语言的switch可能有所不同,以下是一段语法示例:)
switch( 要评估的值/表达式 )
{
case 值A:
{
指令组 A;
break;
}
case 值B:
{
指令组 B;
break;
}
case 值C:
{
指令组 C;
break;
}
// 还可以添加更多case。
default:
{
默认执行的指令组;
break;
}
}
———— 函数 ————
一个函数可包括以下部分:
1、返回值的类型:类似于变量的定义。除 int,float,string,bool 等之外,还可以用 void 表示 <没有任何返回内容>。
2、自定义函数名:类似于变量的定义。名称必须唯一,只能使用 A~Z、a~z、0~9、下划线(_),且首个字符不能是数字。
3、0个或若干个参数:类似于变量的定义。(类型 名称=默认值)。在调用函数时,输入不同的值可以改变参数的值。
参数只能在函数内部使用,函数执行结束就会被销毁。
4、一些语句:函数被调用时执行。这些语句可以使用参数作为变量。语句内也可以调用其他函数。
5、return 语句:可选。可让函数立即结束执行,并使函数返回一个值。如果不写 return 语句,则默认返回 void。
语法(方括号内是可选内容):
返回值的类型 自定义函数名( [参数1类型 参数1名称=参数1默认值,参数2, 参数3... ] )
{
各语句;
[return (返回值);]
}
示例:
1、无参数、无实质语句、无返回值的函数:
void doNothing()
{
return (void);
}
2、给传入的两个整数相加,并返回:
int addTwoNumber(int a=0, int b=0)
{
return (a + b);
}
3、给传入的整数取绝对值,并返回:
int abs(int a=0)
{
if (a < 0)
{
return (0 - a);
}
return (a);
}
调用函数:
1、使用 函数名() 即可调用。
2、如果函数有返回值,则可以 变量=函数名(参数),把返回值赋值给变量。需注意,数据类型最好相匹配
3、如果函数的返回值为 void ,则不能赋值给变量。
4、传参数时,只能按顺序写: addTwoNumber(5,3)。不能像 addTwoNumber(b=3,a=5) 这样指定传给某个参数。
———— 变量作用域(本地 / 全局变量) ————
基本:
每个变量都有一个作用域,即变量可被使用的“区域”。
例如,你不能在变量被定义之前使用它,这是没有意义的!
void main()
{
a++; // 等等!变量“a”尚未存在。你还不能访问并修改它
int a = 10;
a++; // 现在可以了。
}
局部变量 / 全局变量:
[局部变量]- 在函数内部定义的变量。它只能在这个函数内部访问:
void function_A()
{
int a = -1;
// 变量a只能在这个函数中访问,
// 所以它在这个函数以外的地方是不存在的
}
void function_B()
{
int a = 0;
// 这特别意味着你可以在另一个函数里,定义一个名称完全相同的变量。
// 它们并不会互相占用、干扰,可以拥有不同的值。
}
[全局变量]- 在函数外部定义的变量。它在定义之后的代码 (而不是之前) 的任何地方都可以访问:
// 下面这个变量“a”就是一个全局变量。
// 任何函数都可以 访问或修改 它的值,
// 但其名称已被占用,在定义新变量或函数参数时,无论是在何处,都不可以与之同名。
int a = -1;
void function()
{
// int a = 100;
// 即使在函数内部,你也不能像上面这样定义一个名为“a”的变量
a = 0; // 但是你可以在任何地方修改全局变量“a”的值
}
void anotherFunction()
{
// 作为一个[全局变量],“a”的值可以被任何函数访问:
int b = a + 10; // 访问 a 的值,并加上10
// 或者被修改:
a = 0;
}
———— 静态变量(被保护的局部变量) ————
静态变量是局部变量的一个变种。
但与函数用完就被系统随手销毁的普通局部变量不同,静态变量在函数执行结束后会保留数值,用于下一次使用。
而且静态变量在首次被定义初始化后,就不会再被定义语句初始化了。
void test()
{
int local_var = 0; // 普通局部变量,由于用完就丢,所以每次启动函数都会定义(创建)一次
static int static_var = 0; // 静态变量,由于用完还留着,所以定义(创建)只需要进行一次
local_var++;
static_var++;
xsChatData("Local = " + local_var);
xsChatData("Static = " + static_var);
}
void main()
{
test();
test();
test();
}
上述代码的运行结果为:
Local = 1
Static = 1
Local = 1
Static = 2
Local = 1
Static = 3
可见,静态变量不会在函数执行完毕后被清掉,而是留下来被不断累加了。
静态变量的用法:
1、记录一个函数被调用过多少次
2、作为一个阉割版的全局变量使用
3、……
———— 数组(Array) ————
数组是一种数据结构,它是一种将多个数据有序放置的带编号列表。
下面是一个简单的整数数组(里以表格展示):
| 索引| 数据|
| 0 | 23|
| 1 | 4 |
| 2 | 42|
| 3 | 32|
| 4 | 92|
数组内的每个元素(数据)都由一个数字编号来标识。这个数字被称为[索引]。
索引从0开始。所以第一个元素索引为1,第二个元素索引为2,以此类推。
接下来拿[整数数组]举例。
1、创建数组
使用函数为:
int xsArrayCreateInt(int 项数, int 默认值, String 唯一名称)
示例:
int array_A = xsArrayCreateInt(5, 0, "A");
// 创建一个 最多容纳5项、每项初始值为0、名为A 的整数数组,
// 然后将其数组ID赋值给 整数变量 array_A。
2、修改数组内数据
使用函数为:
int xsArraySetInt(int 数组ID, int 索引, int 值)
示例:
xsArraySetInt(array_A, 3, 100);
// 将 ID为array_A 的数组的索引3(第4项) 上的数据设置为100。
3、访问数组内数据
// 使用函数为:
int xsArrayGetInt(int 数组ID, int 索引)
// 示例:
int index_3 = xsArrayGetInt(array_A, 3);
// 获取 ID为array_A 的数组的索引3(第4项) 上的数据,将其值赋值给 整数变量index_3
4、修改数组的大小(略)
5、获取数组的大小(略)
当然数组不是只能容纳 int,还能容纳 float、bool、string、vector。
类型为 X 的数组只能包含类型为 X 的数据。
储存 int 时为整数数组,储存 float 时为浮点数数组,以此类推。
每种类型的数组有各自的一套函数用于创建、修改、访问数据和修改大小(详见xs函数详解部分)
———— 规则(Rule) ————
规则是会以特定间隔周期性地被自动调用的的void函数(无返回值)。
规则应该在函数以外的地方定义和初始化。
语法:
rule 规则名 // 规则的唯一性名称。遵循与变量相同的命名规则。而且不能与其他函数名重复。
active/inactive // 设置规则的初始状态。active表示初始开启,inactive表示初始关闭。
// 这与触发器的“初始开启/初始关闭”工作方式类似。
// 开启的规则会被定期调用,可以被禁用而停止循环。
// 关闭的规则会被游戏忽略,可以被启用而开始循环。
// 无论是开启的还是关闭的,规则都可以像void函数一样被调用。
// 以下附加参数都可省略。
minInterval <int> // 倒计时模式。在代码块再次执行之前 必须经过的最小间隔(游戏秒)
maxInterval <int> // 倒计时模式。在代码块再次执行之前 可能经过的最大间隔(游戏秒)(该参数可忽略)
highFrequency // 高频模式(所有规则的默认模式)。
// 每现实秒循环17~60次规则(这与游戏速度无关,但受性能影响)
//[ highFrequency ] 和 [ minInterval 与 maxInterval ] 冲突。
// 上述两种模式不能同时使用。拥有高频标志时,间隔标志将强制无效。
runImmediately// 具有这个标志时,规则一旦开启则立即执行一次,随后周期性计时器开始运转。
// 若没有这个标志,则倒计时模式开始,倒计时到 0 时规则被调用执行,循环往复。
group 规则组名// 该规则所属的组。且不需要用双引号括起。遵循与变量相同的命名规则。
// 可以将多个规则分配给一个组,如此就可以通过简单地启用/禁用整个组来同时操作它们。
priority <int>// 规则会按照优先级高低依次执行
{
// 要执行的代码块
}
示例:
// 每隔 5 游戏秒执行一次对静态变量+1的简单规则。
rule Rule_Every5second
active
minInterval 5
{
static int static_var = 0;
static_var++;
xsChatData("Static Variable = " + static_var);
}
规则的启用与禁用:
// 可以在其他地方操作规则的开关。
void main()
{
xsDisableRule("Rule_Every5second"); // 禁用规则
xsEnableRule("Rule_Every5second"); // 启用规则
}
可以使用xsDisableSelf来让规则只执行一次就禁用掉自身:
// 这个“激活器”规则会在第10秒时激活Rule_Every5second,随后自我禁用。
rule activatorOnlyOnce
active
minInterval 10
{
xsEnableRule("Rule_Every5second");
xsDisableSelf();
}
规则组(group):
// 在定义规则时,可以借助 来创建一个组。
// 把多条规则划分给同一个规则组,就可以借助下面两个函数来统一启用/禁用了。
rule rule1
inactive
minInterval 3
group timingRules
{
}
rule rule2
inactive
minInterval 5
group timingRules
{
}
void main()
{
xsEnableRuleGroup("timingRules"); // 启用规则组中所有规则
xsDisableRuleGroup("timingRules"); // 禁用规则组中所有规则
}
———— 模块化程序设计 ————
当你正在创建一个大型脚本项目(上千行)时,将你的大型脚本文件分割成几个较小的文件(上百行),将极大方便后期维护修改工作。
假如,社区里已有人写出一个现成的常用数学函数集合(放到一个math.xs里),
你当然可以把里面的函数尽数复制到你的主xs文件里,但难免会过于冗长,
这时你可以在主xs文件里使用 include 引用 math.xs 里的所有内容:
[XS文件夹/new2_math.xs]:
//这里是new2专用的数学函数xs文件。
float max(float a=0.0, float b=0.0)
// 函数1:返回两个浮点数中的较大的一个值
{
if ( a >= b )
return ( a );
return ( b );
}
// (省略其他大量数学函数)
[XS文件夹/new2_main.xs]:
// 这里是给游戏使用的主xs文件。
include "./new2_math.xs";
// 注:这是相对路径的表示方法。也可以使用绝对路径,但不同玩家的路径往往不一致,所以基本用不到。
void main()
{
float num_1 = 5.0;
float num_2 = 10.0;
float num_larger = max(num_1, num_2);
xsChatData( "Maximum of " +num_1 +" and " +num_2 +" is " + num_larger);
}
>>第三部分 - 实用XS函数 四则<<
1、实用XS函数四则(使用例在下文)
/*【newtonerdai设计的XS函数 · 根据资源修改变量】
以 [玩家X] 的 [资源Y] 修改 [变量Z]。有5种模式:
mode = "=" —— 变量Z 设为 资源Y
mode = "+" —— 变量Z 加上 资源Y
mode = "-" —— 变量Z 减去 资源Y
mode = "*" —— 变量Z 乘以 资源Y
mode = "/" —— 变量Z 除以 资源Y(向下取整)
各参数的含义:
variable_id —— 触发变量ID,范围0~255,默认为0
mode —— 修改模式,默认为 =(即设置)
player_id —— 玩家ID,范围1~8,默认为1(即玩家1)
resource_id —— 资源ID,范围0~250,默认为0(即食物)
*/
void modify_Variable_by_Resource(int variable_id=0, string mode="=", int player_id=1, int resource_id=0)
{
float resource_amount = xsPlayerAttribute(player_id, resource_id); // 获取玩家的资源值
int var_value = xsTriggerVariable(variable_id); // 获取触发变量数值
if (mode == "=")
{
var_value = resource_amount;
xsSetTriggerVariable(variable_id, var_value);
}
if (mode == "+")
{
var_value = var_value + resource_amount;
xsSetTriggerVariable(variable_id, var_value);
}
if (mode == "-")
{
var_value = var_value - resource_amount;
xsSetTriggerVariable(variable_id, var_value);
}
if (mode == "*")
{
var_value = var_value * resource_amount;
xsSetTriggerVariable(variable_id, var_value);
}
if (mode == "/")
{
var_value = var_value / resource_amount;
xsSetTriggerVariable(variable_id, var_value);
}
}
/*【newtonerdai设计的XS函数 · 根据资源修改资源】
以[玩家A] 的 [资源A] 修改 [玩家B] 的 [资源B]。有5种模式:
mode = "=" —— 资源B 设为 资源A
mode = "+" —— 资源B 加上 资源A
mode = "-" —— 资源B 减去 资源A
mode = "*" —— 资源B 乘以 资源A
mode = "/" —— 资源B 除以 资源A
(以上所有操作都保留小数,不取整。不过游戏里显示时只显示整数部分)
各参数的含义:
p_B_id —— 玩家B ID,范围1~8,默认为1(即玩家1)
rsc_B_id —— 资源B ID,范围0~250,默认为0(即食物)
mode —— 修改模式,默认为 =(即设置)
p_A_id —— 玩家A ID,范围1~8,默认为1(即玩家1)
rsc_A_id —— 资源A ID,范围0~250,默认为0(即食物)
*/
void modify_Resource_by_Resource(int p_B_id=1, int rsc_B_id=0, string mode="=", int p_A_id=1, int rsc_A_id=0)
{
float rsc_A_amount = xsPlayerAttribute(p_A_id, rsc_A_id); // 获取玩家A 的资源值
float rsc_B_amount = xsPlayerAttribute(p_B_id, rsc_B_id); // 获取玩家B 的资源值
if (mode == "=")
{
xsSetPlayerAttribute(p_B_id, rsc_B_id, rsc_A_amount);
}
if (mode == "+")
{
rsc_B_amount = rsc_B_amount + rsc_A_amount;
xsSetPlayerAttribute(p_B_id, rsc_B_id, rsc_B_amount);
}
if (mode == "-")
{
rsc_B_amount = rsc_B_amount - rsc_A_amount;
xsSetPlayerAttribute(p_B_id, rsc_B_id, rsc_B_amount);
}
if (mode == "*")
{
rsc_B_amount = rsc_B_amount * rsc_A_amount;
xsSetPlayerAttribute(p_B_id, rsc_B_id, rsc_B_amount);
}
if (mode == "/")
{
rsc_B_amount = rsc_B_amount / rsc_A_amount;
xsSetPlayerAttribute(p_B_id, rsc_B_id, rsc_B_amount);
}
}
/*【newtonerdai设计的XS函数 · 根据变量修改变量】
以 [变量A] 修改 [变量B]。有5种模式:
mode = "=" —— 变量B 设为 变量A
mode = "+" —— 变量B 加上 变量A
mode = "-" —— 变量B 减去 变量A
mode = "*" —— 变量B 乘以 变量A
mode = "/" —— 变量B 除以 变量A(向下取整)
各参数的含义:
var_B_id —— 触发变量B ID,范围0~255,默认为0
mode —— 修改模式,默认为 =(即设置)
var_A_id —— 触发变量A ID,范围0~255,默认为0
*/
void modify_Variable_by_Variable(int var_B_id=0, string mode="=", int var_A_id=0)
{
int var_A_value = xsTriggerVariable(var_A_id); // 获取触发变量A 数值
int var_B_value = xsTriggerVariable(var_B_id); // 获取触发变量B 数值
if (mode == "=")
{
xsSetTriggerVariable(var_B_id, var_A_value);
}
if (mode == "+")
{
var_B_value = var_B_value + var_A_value;
xsSetTriggerVariable(var_B_id, var_B_value);
}
if (mode == "-")
{
var_B_value = var_B_value - var_A_value;
xsSetTriggerVariable(var_B_id, var_B_value);
}
if (mode == "*")
{
var_B_value = var_B_value * var_A_value;
xsSetTriggerVariable(var_B_id, var_B_value);
}
if (mode == "/")
{
var_B_value = var_B_value / var_A_value;
xsSetTriggerVariable(var_B_id, var_B_value);
}
}
/*【newtonerdai设计的XS函数 · 根据变量修改资源】
以 [变量A] 修改 [玩家X] 的 [资源Y]。有5种模式:
mode = "=" —— 资源Y 设为 变量A
mode = "+" —— 资源Y 加上 变量A
mode = "-" —— 资源Y 减去 变量A
mode = "*" —— 资源Y 乘以 变量A
mode = "/" —— 资源Y 除以 变量A
(以上所有操作都保留小数,不取整。不过游戏里显示时只显示整数部分)
各参数的含义:
player_id —— 玩家ID,范围1~8,默认为1(即玩家1)
resource_id —— 资源ID,范围0~250,默认为0(即食物)
mode —— 修改模式,默认为 =(即设置)
variable_id —— 触发变量ID,范围0~255,默认为0
*/
void modify_Resource_by_Variable(int player_id=1, int resource_id=0, string mode="=", int variable_id=0)
{
float resource_amount = xsPlayerAttribute(player_id, resource_id); // 获取玩家的资源值
int var_value = xsTriggerVariable(variable_id); // 获取触发变量数值
if (mode == "=")
{
resource_amount = var_value;
xsSetPlayerAttribute(player_id, resource_id, resource_amount);
}
if (mode == "+")
{
resource_amount = resource_amount + var_value;
xsSetPlayerAttribute(player_id, resource_id, resource_amount);
}
if (mode == "-")
{
resource_amount = resource_amount - var_value;
xsSetPlayerAttribute(player_id, resource_id, resource_amount);
}
if (mode == "*")
{
resource_amount = resource_amount * var_value;
xsSetPlayerAttribute(player_id, resource_id, resource_amount);
}
if (mode == "/")
{
resource_amount = resource_amount / var_value;
xsSetPlayerAttribute(player_id, resource_id, resource_amount);
}
}
2、使用例
[*]首先把上面四则函数,放到你的 里,存为UTF-8编码,然后到编辑器 [地图]-[脚本文件名] 里指定xs脚本文件。
[*]接着去 [触发器] 选项卡里,新建一个触发,创建一个 [效果-脚本调用],在下面的使用例里任选一个函数来调用。至于怎么调用,上面已经说得很清楚了,在此不再赘述。
[*]顺带一提,你可以修改相关变量值、资源量来测试,还可以按这个帖子的方法,在右上角简短任务栏里显示变量值、资源量。比如:
触发1 || 初始开启不循环
简短介绍 || 显示在屏幕上 = 是,内容为:
变量0 = <Variable 0>
变量100 = <Variable 100>
资源198 = <Unused Resource 198>
条件0 || 机会/随机百分比 = 0
/*根据 P1资源 修改 变量#0*/
void set_V0_by_P1_R0()
{
modify_Variable_by_Resource(0, "=", 1, 0);
xsChatData("Variable_0 = Resource_0");
}
/*根据 P1资源 修改 P1资源#198*/
void set_P1_R198_by_P1_R0()
{
modify_Resource_by_Resource(1, 198, "=", 1, 0);
xsChatData("Resource_198 = Resource_0");
}
/*根据 变量#100 修改 变量#0*/
void set_V100_by_V0()
{
modify_Variable_by_Variable(100, "=", 0);
xsChatData("Variable_100 = Variable_0");
}
/*根据 变量#0 修改 P1资源#198*/
void set_P1_R198_by_V0()
{
modify_Resource_by_Variable(1, 198, "=", 0);
xsChatData("Resource_198 = Variable_0");
}
拓展阅读:
编辑器Build 43210新功能、半年来新发现大汇总!
用于AI-触发联动的 [资源法] 现已全面升级!
决定版精品贴大索引
决定版资源对照表(包含ID 英文 中文 扩充中文 预设值 作用)
{:155:}又是一种新的途径 看了论坛那么多帖子,就觉得你特别专业,求教这个单位驻扎在单位内怎么做的,我用AOK驻扎半天,两个都没了,还有这个技能效果是不是只能AI设置,有缓冲时间的,能否一起赐教下 (2021.4.27大更新)
1、大幅扩充帝国2DE官方函数库
2、[效果-脚本调用] 的注释方式+2、闪退BUG记录+1
3、XS脚本通用语法 增加 变量作用域、静态变量、数组、规则&规则组、模块化编程。
【重磅消息】
借助官方 xsEffectAmount 函数(第二部分→1、决定版内部XS函数→普通函数),我们可以在DE编辑器里实现 up-effect 语句了!
比如强制升级物件:
// 示例:
void upgrade()
{
// 升级(3) 单位83(男村民) 为 单位4(步弓手) 数值0(参数忽略) 作用于玩家1
xsEffectAmount(3,83,4,0,1);
// 升级(3) 单位280(轻型投石车)为 单位8(长弓兵) 数值0(参数忽略) 作用于全体玩家-1
xsEffectAmount(3,280,8,0,-1);
}
太好了{:10_437:}终于有显性的效果了 newtonerdai 发表于 2021-4-27 21:24
(2021.4.27大更新)
1、大幅扩充帝国2DE官方函数库
2、[效果-脚本调用] 的注释方式+2、闪退BUG记录+1
c里面的几个比如冒泡排序,比如虚类、指针能否运行,交给new2了,指针跟数组是c里面的两大组成部分哦
條件的腳本應該是條件函數必定執行
條件回傳值是true時才會執行效果
條件回傳值是false時不會執行效果 秋一个xs 文件我想实现我的模组中,一个触发
当城堡兵杀死一个敌军事单位就变成英雄状态
页:
[1]