共计 13584 个字符,预计需要花费 34 分钟才能阅读完成。
在教程 01 和 02 中介绍了如何新建一个技能、新建增益状态以及技能属性的详细解释。但是只依靠这些基本只能做到与游戏中已存在技能相同或相似的机制,很难做出更独特的技能机制。要做到这一点,就需要使用故事编辑器来编写 Osiris 脚本做到。本节教程会介绍故事编辑器的简单用法、Osiris 脚本的基本语法以及几个我 Mod 中的实例讲解。官方的介绍。
故事编辑器
首先点击图标打开故事编辑器,在里边可以查看管理所有的脚本:

打开后左边列出了所有的故事脚本,其中灰色高亮的是游戏自带的。这里每一个脚本被称为一个“Goal”。可以看到有些 Goal 有子项(左侧有加号)。在左侧选择脚本后,右侧会显示其内容。最下边会显示编译后的报错信息,双击报错信息自动定位到出错位置。左上角 File 中有一些常用功能,例如保存、编译、生成定义头文件等。项目中 第一次使用故事编辑器时需要先生成定义(Generate Definitions)。另外生成成功(没有报错)的脚本要立即生效需要执行一次 Reload。
Osiris 基本语法
完成父 Goal 目标
前面提到,这里每一个脚本被称为一个 Goal,子 Goal 如果想要运行生效,就需要先完成父 Goal 的目标。完成目标可以理解成脚本加载的时机。完成目标需要调用函数 GoalCompleted;
。这里你可以把你的脚本新建在__Start
下,成为它的子 Goal,你也可以自己新建一个父 Goal 并完成它。下面我以自建父 Goal 为例。
方法一:
直接在父 Goal 的 KB 区域填写如下代码:
IF
GameStarted(_,_)
THEN
GoalCompleted;
这段代码的意思是在游戏开始后加载子脚本。
方法二:
在 INIT 区域填入
DB_Mytest_ModStarted(1);
这里必须以 DB_
开头,名字独一无二即可,具体原因后边会讲到。
随后在 KB 区域填入:
IF
DB_Mytest_ModStarted(1)
THEN
GoalCompleted;
然后子 Goal 就会在父 Goal 完成后被加载了。
Osiris 脚本基本结构和语法
你可能注意到了在查看脚本的编辑器界面有三个区域:INIT、KB、EXIT。INIT 区域的代码会在 Goal 初始化时运行。KB(knowledge base)区域的代码在 Goal 初始化就生效,也就是说 KB 的代码也可以响应 INIT 区域的变化。EXIT 的代码会在 Goa 完成时运行。
Osiris 的数据类型
在 Osiris 中有以下数据类型:
- INTEGER,32 位整型变量,例如 -10,-5,1,2,3,4
- INTEGER64,64 位整型变量,例如 -99999999999, -4, 0, 10, 12345678901
- REAL,实数,例如 -10.0, -0.1, 0.0, 0.5, 100.123
- STRING,字符串,例如 "A", "ABC", "_This is a string"
- GUIDSTRING,一个对象的 GUID,例如音效资源、物品、人物等,例如 123e4567-e89b-12d3-a456-426655440000
- CHARACTERGUID,一个游戏中人物的 GUID,例如(CHARACTERGUID)123e4567-e89b-12d3-a456-426655440000
- ITEMGUID,物品的 GUID
- TRIGGERGUID,触发器 GUID
- SPLINEGUID,spline GUID
- LEVELTEMPLATEGUID,关卡模板的 GUID
数据库 Databases
Osiris 的 DB 类型必须以 DB_
开头,括号内是你要添加的数据,数据类型参考上文。通常推荐写一个前缀YourPrefix
,这样可以保证 DB 名字的独一无二。这是因为所有的 DB 都共享一个命名空间,包括官方的 DB 变量和其他加载的 Mod 的 DB 变量。
DB_YourPrefix_DatabaseName(TypedValue1[,TypedValue2..]);
每一个添加的数据被称为一个fact
,同一个 DB 变量下可以有多个fact
。
// Type: String
DB_Overview_StringDB("SomeString");
DB_Overview_StringDB("AnotherString");
// Type: float
DB_Overview_FloatDB(1.0);
// Type: Integer, String
DB_Overview_IntegerStringDB(0, "String0");
DB_Overview_IntegerStringDB(1, "String1");
DB_Overview_IntegerStringDB(1, "String1");
// Type: GUIDSTRING (not CHARACTERGUID, because no typecast)
DB_Overview_Origins(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f);
DB_Overview_Origins(CHARACTERGUID_S_Player_Beast_f25ca124-a4d2-427b-af62-df66df41a978);
DB_Overview_Origins(CHARACTERGUID_S_Player_Lohse_bb932b13-8ebf-4ab4-aac0-83e6924e4295);
DB_Overview_Origins(CHARACTERGUID_S_Player_RedPrince_a26a1efb-cdc8-4cf3-a7b2-b2f9544add6f);
DB_Overview_Origins(CHARACTERGUID_S_Player_Sebille_c8d55eaf-e4eb-466a-8f0d-6a9447b5b24c);
DB_Overview_Origins(CHARACTERGUID_S_Player_Fane_02a77f1f-872b-49ca-91ab-32098c443beb);
// Type: CHARACTERGUID, String
DB_Overview_Origins((CHARACTERGUID)CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f, "IFAN");
DB_Overview_Origins(CHARACTERGUID_S_Player_Beast_f25ca124-a4d2-427b-af62-df66df41a978, "BEAST");
DB_Overview_Origins(CHARACTERGUID_S_Player_Lohse_bb932b13-8ebf-4ab4-aac0-83e6924e4295, "LOHSE");
DB_Overview_Origins(CHARACTERGUID_S_Player_RedPrince_a26a1efb-cdc8-4cf3-a7b2-b2f9544add6f, "RED PRINCE");
DB_Overview_Origins(CHARACTERGUID_S_Player_Sebille_c8d55eaf-e4eb-466a-8f0d-6a9447b5b24c, "SEBILLE");
DB_Overview_Origins(CHARACTERGUID_S_Player_Fane_02a77f1f-872b-49ca-91ab-32098c443beb, "FANE");
从第三类和第四类例子中可以看到 DB 变量是可以重载的,只需要它们有不同的变量数量。
删除 fact
需要使用 NOT 指令
NOT DB_Overview_StringDB("SomeString");
NOT DB_Overview_StringDB("AnotherString");
DB 可以作为全局变量使用,可以保存一些临时信息、作为判断的标志状态等。
基本规则 Rules
一条规则 Rule 由一个或多个触发条件开始,随后是 0 个或多个额外条件,最后是 1 个或多个执行动作语句。当触发条件和额外条件都满足时执行动作语句。
IF
触发条件
[
AND
触发条件 或 额外条件
]
[
AND
触发条件 或 额外条件
..
]
THEN
动作语句 1;
[
动作语句 2;
..
]
注意:触发条件和额外条件结尾没有分号,而动作语句后要加英文分号。
触发条件 有两种:
-
Osiris Event 事件 :游戏定义的事件,例如角色死亡、受到攻击等,注意 Event 条件只能放在 第一个 触发条件位置,也就是开头。
-
Database 触发:当一个 Database fact 被添加时(可以是第一个触发条件)或者删除 fact。例如:
IF DB_IsPlayer(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件 1 AND NOT DB_Dead(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件 2 THEN DB_IfanIsAliveAsAPlayer(1);
这个例子中在两种情况会触发:
- 当
DB_IsPlayer()
添加伊凡 并且DB_Dead()
中没有伊凡的 fact 时 - 或者
DB_IsPlayer()
和DB_Dead()
都添加了伊凡,不过随后DB_Dead()
中的伊凡 fact 会被删除
- 当
额外条件 包含以下几种形式:
- Osiris Query:Osiris 定义的查询条件,查看引擎提供的 Query 有哪些,点我。
- 自定义 Query:详情见此,点我。。
- 数据比较:使用比较符号:判断相等
==
、不等!=
、大于>
、小于<
、大于等于>=
、小于等于<=
IF
DB_IsPlayer(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件
AND
CharacterIsDead(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f, 0) // 额外条件
AND
DB_Avatars(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件 (!)
THEN
DB_IfanIsAnAvatarPlayerAndNotDead(1);
CharacterIsDead
是一个查询角色是否死亡的 Query,它有两个参数,角色 GUID 和一个整数。这个整数代表是否死亡(0 没死,1 死了)。这个查询会在所有参数都匹配上时成立,即结果为真。所以这个 Query 就是询问伊凡死了没有。这个 Query 也可以写成以下形式,只不过更繁琐:
IF
DB_IsPlayer(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件
AND
CharacterIsDead(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f, _Result) // 额外条件
AND
_Result == 0 // 额外条件
AND
DB_Avatars(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件 (!)
THEN
DB_IfanIsAnAvatarPlayerAndNotDead(1);
这段规则的意思是在 DB_IsPlayer
和DB_Avatars
中第一次出现伊凡后,并且伊凡活着,那么就给 DB_IfanIsAnAvatarPlayerAndNotDead
添加一个 fact
1。注意,这里只有两个 DB 变量 DB_IsPlayer
和DB_Avatars
都是第一次出现伊凡 GUID 的 fact
才会执行动作。
动作语句 Actions:
动作语句 Actions 负责执行一些函数来改变游戏状态或更新数据库,它包含以下形式:
- Osiris Call:Osiris 定义的函数,用来改变某个游戏对象的状态。查看引擎提供的 Call 有哪些,点我
- Procedure:自定义的流程,与其他编程语言的函数概念类似。如何定义点 这里。
- Database 操作 :例如添加或删除某个 DB 的
fact
。删除fact
使用 NOT 关键字。
IF
DB_IsPlayer(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f) // 触发条件
AND
CharacterIsDead(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f, 1) // 额外条件
THEN
CharacterResurrect(CHARACTERGUID_S_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f); // 动作语句
这段代码的作用是在 DB_IsPlayer
中第一次出现伊凡 GUID 并且伊凡是死亡状态时,复活他。注意动作语句后以英文分号结尾,而条件语句不用。
定义变量:
有时候你不知道具体的值或者想获取某个值,这时候就需要使用变量来实现。在 Osiris 中你不需要定义变量的类型,变量的生命周期被限定在一条规则 Rule、流程 Procedure 或者查询 Query 中。
示例:
IF
DB_IsPlayer(_Player)
AND
CharacterIsDead(_Player, 1)
THEN
CharacterResurrect(_Player)
与前面专注于复活伊凡不同,这里的代码实现复活死亡的任意角色,只要他的 GUID 第一次出现在 DB_IsPlayer
中。
More powerful uses of variables are demonstrated in the section on Rule Matching below.
自定义查询 Query
使用自定义的 Query 语句可以很方便地实现或条件(是的,规则 Rule 中只能 AND 做衔接,不能是其他的)。格式为:
QRY
QRY_MyPrefix_QueryName((TYPE1)_Param1[,(TYPE2)_Param2..])
[
AND
ExtraCondition1
..
]
THEN
Action1;
[
Action2;
..
]
第二行 QRY_MyPrefix_QueryName
是自定义的 Query 名字,如同 DB 一样。紧跟着的括号内是本条 Query 用到的参数。
并且 Query 和 Database 一样可以通过设置不同的参数数量进行重载。并且相同名字的 Query 们在被调用时会全部执行。举例:
QRY
QRY_Overview_CharacterIsIncapacitated((CHARACTERGUID)_Char)
AND
HasActiveStatus(_Char,"FROZEN",1)
THEN
DB_NOOP(1);
QRY
QRY_Overview_CharacterIsIncapacitated(_Char)
AND
HasActiveStatus(_Char,"PETRIFIED",1)
THEN
DB_NOOP(1);
QRY
QRY_Overview_CharacterIsIncapacitated(_Char)
AND
HasActiveStatus(_Char,"KNOCKED_DOWN",1)
THEN
DB_NOOP(1);
IF
CharacterReceivedDamage(_Char)
AND
// check whether _Char is FROZEN, PETRIFIED or KNOCKED_DOWN
QRY_Overview_CharacterIsIncapacitated(_Char)
THEN
DB_Overview_CowardAttackingIncapacitatedCharacter(1);
在这段代码中定义了三个 Query,名字为 QRY_Overview_CharacterIsIncapacitated
,分别判断是否有冻结状态、石化状态和击倒状态并什么也不做(DB_NOOP(1);
意思往这个数据库中插入一条值为 1 的 fact
,没有实际效果,常被用来占位,类似 Python 中的 pass)。在随后的规则语句中如果满足三个QRY_Overview_CharacterIsIncapacitated
的其中一个条件,那么就会执行一个插入 fact 的操作。
另外你可能注意到了只有第一个 Query 指定了类型,这点也是和 DB 类似的。
自定义流程 Procedure
流程 Procedure 的概念很类似于其他编程语言中的函数概念。这里的 Procedure 就是把一些关联的动作语句或者满足某个条件而执行的语句合并起来。
Procedure 的格式如下:
PROC
PROC_MyPrefix_ProcName((TYPE1)_Param1[,(TYPE2)_Param2..])
[
AND
ExtraCondition1
..
]
THEN
Action1;
[
Action2;
..
]
Procedure 的定义和 Query 很相似,不过多阐述了。举例:
PROC
PROC_Overview_TeleportAlive((CHARATERGUID)_Char)
AND
CharacterIsDead(_Char, 1)
THEN
CharacterResurrect(_Char);
IF
CharacterReceivedDamage(_Char)
THEN
PROC_Overview_TeleportAlive(_Char);
这段代码定义了一个名为 PROC_Overview_TeleportAlive
的流程,流程用于复活死亡角色。并在角色受到伤害后执行该流程。注意和 Query 使用在额外条件处不同,流程 Procedure 应在使用在动作语句 Action 位置,也就是在 THEN 关键字后边,并且因为是 Action 语句,要以英文分号结尾。
如何查看有哪些 Osiris 定义的 event、query、call
游戏引擎为我们预先定义了游戏事件、游戏状态的查询函数和改变游戏状态的函数。在生成了头文件定义(CTRL+F7
)后,就可以查看可以使用的全部函数。位置在Divinity Original Sin 2\DefEd\Data\Mods\ 你的 Mod 名称 \Story\RawFiles\story_header.div
。可以使用 Vs Code、Notepad++ 等文本查看器打开。打开后就可以看到所有的可用函数,例如:
query IntegerSum([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Sum) (2,0,0,1)
query Real([in](INTEGER)_I, [out](REAL)_R) (2,0,14,1)
call TransferItemsToUser((CHARACTERGUID)_Character) (1,0,44,1)
event CharacterUsedLadder((CHARACTERGUID)_Character) (3,0,305,1)
在函数名前标记了它是属于哪一类,括号内说明了参数类型和参数名。最后一个括号不用管,像(2,0,0,1)之类的。通过这些信息很容易判断函数的功能,如果你还是拿不准,可以尝试 在此 查询。
另外如果安装了 Norbyte’s ositools,并且定义使用了其函数,那么除了引擎定义的函数外,还可以在文件末尾看到 Norbyte 定义的额外函数。里边有很多扩展的函数,其 API文档在此。Norbyte 定义的额外函数都以 NRD 开头。
实例讲解
在 Mod 增强盾战中,我想要达到一个盾击的技能,在对敌人造成伤害的同时也对自己造成伤害。要达到这一效果就可以使用 Osiris 脚本来实现。
IF
CharacterUsedSkill(_, "Target_ShieldAttack", "target", "Warrior")
THEN
DB_UsedShieldAttack(1);
IF
NRD_OnPrepareHit(_Target,_Instigator, _Damage, _HitHandle)
AND
DB_UsedShieldAttack(1)
AND
IntegerDivide(_Damage, 2 ,_DamageToSelf)
THEN
NOT DB_UsedShieldAttack(1);
ApplyDamage(_Instigator, _DamageToSelf, "Physical", _Instigator);
在这段代码中的第一个规则中,如果有角色释放了技能盾击,技能 ID 为 Target_ShieldAttack
,那么就在DB_UsedShieldAttack
中插入一个 fact。
第二个规则:在技能命中前并且 DB_UsedShieldAttack
中有值为 1 的 fact,那么就把伤害的一半作为物理伤害返回给施法者。
第二个例子会更加复杂,牵扯到更多流程。在这段代码中,实现了盾战 Mod 中分享抗性这个技能的功能。这个技能会给两个人施加 SHARING_RESISTANCE
状态,并且分享两个人的最高元素抗性。
代码比较长,首先来看第一部分:
// When apply status, play 5 beam effects, give resistance boost
IF
NRD_OnStatusAttempt(_Target,"SHARING_RESISTANCE",_StatusHandle,_Instigator)
AND
_Target != _Instigator
AND
PlayLoopBeamEffect(_Target, _Instigator, "RS3_FX_GP_Status_GuardianAngel_Beam_01", "Dummy_BodyFX", "Dummy_BodyFX",(INTEGER64)_FxHandle1)
AND
PlayLoopBeamEffect(_Target, _Instigator, "RS3_FX_Char_Creatures_Shriker_Lightning_Beam_01", "Dummy_BodyFX", "Dummy_BodyFX",(INTEGER64)_FxHandle2)
AND
PlayLoopBeamEffect(_Target, _Instigator, "RS3_FX_GP_Beams_NecroFireBeam_Loop_01", "Dummy_BodyFX", "Dummy_BodyFX",(INTEGER64)_FxHandle3)
AND
PlayLoopBeamEffect(_Target, _Instigator, "RS3_FX_Skills_Water_ChainHeal_Beam_01", "Dummy_BodyFX", "Dummy_BodyFX",(INTEGER64)_FxHandle4)
AND
PlayLoopBeamEffect(_Target, _Instigator, "RS3_FX_GP_Beams_Telekinesis_01", "Dummy_BodyFX", "Dummy_BodyFX",(INTEGER64)_FxHandle5)
AND
CharacterGetAttribute((CHARACTERGUID)_Target,"FireResistance",_fr1)
AND
CharacterGetAttribute((CHARACTERGUID)_Target,"EarthResistance",_er1)
AND
CharacterGetAttribute((CHARACTERGUID)_Target,"WaterResistance",_wr1)
AND
CharacterGetAttribute((CHARACTERGUID)_Target,"AirResistance",_ar1)
AND
CharacterGetAttribute((CHARACTERGUID)_Target,"PoisonResistance",_pr1)
AND
CharacterGetAttribute((CHARACTERGUID)_Instigator,"FireResistance",_fr2)
AND
CharacterGetAttribute((CHARACTERGUID)_Instigator,"EarthResistance",_er2)
AND
CharacterGetAttribute((CHARACTERGUID)_Instigator,"WaterResistance",_wr2)
AND
CharacterGetAttribute((CHARACTERGUID)_Instigator,"AirResistance",_ar2)
AND
CharacterGetAttribute((CHARACTERGUID)_Instigator,"PoisonResistance",_pr2)
THEN
//NRD_DebugLog("shared resitance:");
PROC_GiveResistanceBoost(_Target,_fr1,_Instigator,_fr2,"FireResistance");
PROC_GiveResistanceBoost(_Target,_er1,_Instigator,_er2,"EarthResistance");
PROC_GiveResistanceBoost(_Target,_wr1,_Instigator,_wr2,"WaterResistance");
PROC_GiveResistanceBoost(_Target,_ar1,_Instigator,_ar2,"AirResistance");
PROC_GiveResistanceBoost(_Target,_pr1,_Instigator,_pr2,"PoisonResistance");
PROC_GiveResistanceBoost(_Instigator,_fr2,_Target,_fr1,"FireResistance");
PROC_GiveResistanceBoost(_Instigator,_er2,_Target,_er1,"EarthResistance");
PROC_GiveResistanceBoost(_Instigator,_wr2,_Target,_wr1,"WaterResistance");
PROC_GiveResistanceBoost(_Instigator,_ar2,_Target,_ar1,"AirResistance");
PROC_GiveResistanceBoost(_Instigator,_pr2,_Target,_pr1,"PoisonResistance");
DB_FxHandle(_FxHandle1,_FxHandle2,_FxHandle3,_FxHandle4,_FxHandle5);
DB_ShareResitCharacters(_Target,_Instigator);
//NRD_DebugLog("Beam Added");
PROC
PROC_GiveResistanceBoost((CHARACTERGUID)_Target,(INTEGER)_TargetResist,(CHARACTERGUID)_Compare,(INTEGER)_CompareResist,(STRING)_ResistType)
AND
_CompareResist > _TargetResist
AND
IntegerSubtract(_CompareResist,_TargetResist,_Boost)
THEN
NRD_CharacterSetPermanentBoostInt(_Target, _ResistType, _Boost);
CharacterAddAttribute(_Target, "Dummy", 0); // Force boost sync
//NRD_DebugLog(_ResistType);
虽然代码有点长,但是分为了几个部分,每一部分的功能还是很明确的,这么长的原因主要是有 5 个抗性需要处理。
首先触发条件是在角色被上 SHARING_RESISTANCE
这个增益状态之前,额外条件是状态来源不能是是自己,这个条件是防止重复执行下边的语句。因为自己给自己上状态也会触发这个条件。
然后的语句是给自己和目标添加链接双方的 5 个光束特效,通过函数 PlayLoopBeamEffect
来实现。
然后是 10 个查询基本属性的语句。用来获取双方的 5 个元素抗性。
在这里定义的流程PROC_GiveResistanceBoost
,是用来给目标提供相应抗性的,但是如果当前目标已经是两者中的高抗性,那就不需要执行这段动作。
随后在 DB_FxHandle
和DB_ShareResitCharacters
中存储了 5 个光束特效的 Handle 和两个角色 GUID。目的是在状态消失可以停止播放特效,并且把双方的抗性还原。
// When SHARING_RESISTANCE is removed, stop playing beam effects
// and set a mark to remove resistance boost
IF
CharacterStatusRemoved(_Target,"SHARING_RESISTANCE",_Instigator)
AND
DB_FxHandle(_FxHandle1,_FxHandle2,_FxHandle3,_FxHandle4,_FxHandle5)
THEN
StopLoopEffect(_FxHandle1);
StopLoopEffect(_FxHandle2);
StopLoopEffect(_FxHandle3);
StopLoopEffect(_FxHandle4);
StopLoopEffect(_FxHandle5);
NOT DB_FxHandle(_FxHandle1,_FxHandle2,_FxHandle3,_FxHandle4,_FxHandle5);
DB_RemoveCasterResistanceBoost(1);
// remove resistance boost
IF
DB_RemoveCasterResistanceBoost(1)
AND
DB_ShareResitCharacters(_Target,_Instigator)
THEN
//NRD_DebugLog("Removed:");
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Target,"FireResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Target,"EarthResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Target,"WaterResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Target,"AirResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Target,"PoisonResistance", 0);
CharacterAddAttribute(_Target, "Dummy", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Instigator,"FireResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Instigator,"EarthResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Instigator,"WaterResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Instigator,"AirResistance", 0);
NRD_CharacterSetPermanentBoostInt((CHARACTERGUID)_Instigator,"PoisonResistance", 0);
CharacterAddAttribute(_Instigator, "Dummy", 0);
NOT DB_RemoveCasterResistanceBoost(1);
NOT DB_ShareResitCharacters(_Target,_Instigator);
这段代码用于状态消失后停止播放特效,并把抗性增益清零。