• 欢迎!在发帖前请阅读每个板块置顶的版块须知。论坛 QQ 群:931748551
    鼓励大家多多发帖,这里有问必答。论坛左下角可切换明亮 / 黑暗模式哦。

服务端插件 【ARK技术帝】新版本EC的skill配置JS脚本示例

大家好,我是ARK服务器的主策划-技术帝,这是我们的群422100977。
由于新的EC系统,作者少司命大佬已经丢弃原skill中所有事件循环的可行性,采用全新的js脚本方式来撰写技能,接下来由我来带领大家进行js脚本的编写教程,大家可以通过复制代码然后随意修改。我会在每一段添加注释,方便大家学习~~~
JS学习网站https://www.runoob.com/js/js-syntax.html
这是群内Caigito大佬发的大家可以学习学习
 
最后编辑:
这是EC的skill起源,从玩家位置上向鼠标位置发射一颗泰拉剑气


// 导入脚本所需的命名空间,这是良好的编程习惯
var Xna = importNamespace("Microsoft.Xna.Framework");

/**
* @description 通用武技:泰拉剑气。向鼠标指针方向发射一道泰拉光束。
* @param {object} skill 技能的JSON配置对象
* @param {object} ply 释放技能的玩家对象
* @param {object} pos 技能释放的位置 (通常是玩家为中心)
* @param {object} vel 技能释放的方向 (从玩家指向鼠标)
* @param {number} identify 如果由弹幕触发,则为该弹幕的ID,否则为-1
*/
function main(skill, ply, pos, vel, identify) {
// === 参数定义 ===
const projectileId = 132; // 弹幕ID:132 为泰拉光束
const damage = 70; // 伤害值
const knockback = 5; // 击退值 (0-10, 5为适中)
const speed = 12; // 弹幕飞行速度

// === 核心逻辑 ===

// 计算弹幕的发射速度向量。
// 'vel' 参数已包含了正确的方向(从玩家指向鼠标),我们只需设定其长度(即速度)。
// ToLenOf 是插件提供的一个扩展函数,可以方便地设置向量长度。
var velocity = vel.ToLenOf(speed);

// 从玩家的中心位置发射弹幕。
var spawnPosition = ply.TPlayer.Center;

// 调用插件内置的 SpawnProjtile 函数来生成弹幕。
// 函数原型: SpawnProjtile(玩家, 位置, 速度, 弹幕ID, 伤害, 击退, 弹幕所有者索引)
var projectileIndex = SpawnProjtile(ply, spawnPosition, velocity, projectileId, damage, knockback, ply.Index);

// 调用插件内置的 SendProjectilePacket 函数将生成的弹幕同步给所有客户端,确保其他玩家能看到。
SendProjectilePacket(projectileIndex);

// log("泰拉剑气已发射!"); // 可选的调试信息,会输出在TShock控制台
}
 
/**
* @description 通用武技:四方剑气。
* 当技能触发时,会以玩家为中心,向上下左右四个基本方向同时发射一道飞龙弹幕。
*
* @skill_trigger [主动, CD]
* 这个脚本预设在 tshock/Economics/Skill.json 文件中与一个技能绑定,
* 并且该技能的 "触发模式" 被设置为 ["主动", "CD"]。
*
* @author
* @version 1.0
*
* ---------------------------------------------------------------------------------------------
* Main 函数参数说明:
* @param {object} skill - (技能对象) 触发此脚本的技能在 Skill.json 中的完整配置信息。
* @param {object} ply - (玩家对象) 触发此技能的玩家实例 (TSPlayer)。
* @param {object} pos - (向量对象) 技能释放的起始位置 (Vector2)。
* @param {object} vel - (向量对象) 技能释放的方向向量 (Vector2),此脚本中未使用,因为方向是固定的。
* @param {number} identify - (数字) 如果此技能是由另一个弹幕触发的,这里是那个弹幕的ID,否则为 -1。
* ---------------------------------------------------------------------------------------------
*/
function main(skill, ply, pos, vel, identify) {

// 导入脚本所需的XNA框架命名空间,以便我们使用向量(Vector2)对象。
var Xna = importNamespace("Microsoft.Xna.Framework");

// --- 1. 定义技能的核心参数 ---
const projectileId = 684; // 弹幕ID: 684 是“畅快猛斩”(来源于武器“飞龙”)
const damage = 120; // 伤害值
const knockback = 6.0; // 击退力度
const speed = 10; // 弹幕飞行速度

// --- 2. 定义发射方向的数组 ---
// 我们需要向四个方向发射,所以预先定义好这四个方向的单位向量。
// 在泰拉瑞亚的坐标系中:Y轴向下为正,向上为负;X轴向右为正,向左为负。
const directions = [
new Xna.Vector2(0, -1), // 向上
new Xna.Vector2(0, 1), // 向下
new Xna.Vector2(-1, 0), // 向左
new Xna.Vector2(1, 0) // 向右
];

// --- 3. 循环发射弹幕 ---

// 将弹幕的生成位置固定在玩家的身体中心。
var spawnPosition = ply.TPlayer.Center;

// 使用 forEach 循环遍历我们定义好的四个方向向量。
// 对于数组中的每一个 direction,都会执行一次循环内的代码。
directions.forEach(direction => {
// 计算出最终的速度向量:将方向向量的长度设置为我们预定义的 speed。
var velocity = direction.ToLenOf(speed);

// 在每个方向上都调用 SpawnProjtile 函数生成一个弹幕。
var projIndex = SpawnProjtile(ply, spawnPosition, velocity, projectileId, damage, knockback, ply.Index);

// 将新生成的弹幕同步给所有客户端。
SendProjectilePacket(projIndex);
});

// (可选) 在TShock控制台输出日志,用于调试。
// log(ply.Name + " 使用了四方剑气!");
}
 

附件

  • 通用武技“四方剑气”png.png
    通用武技“四方剑气”png.png
    497 KB · 查看: 2
/**
* @description 通用武技:波涌剑气。
* 技能触发时,向玩家鼠标指针方向呈扇形发射三道波涌之刃弹幕。
*
* @skill_trigger [主动, CD]
* 这个脚本预设在 tshock/Economics/Skill.json 文件中与一个技能绑定,
* 并且该技能的 "触发模式" 被设置为 ["主动", "CD"]。
*
* @author
* @version 2.0
*
* ---------------------------------------------------------------------------------------------
* Main 函数参数说明:
* @param {object} skill - (技能对象) 触发此脚本的技能在 Skill.json 中的完整配置信息。
* @param {object} ply - (玩家对象) 触发此技能的玩家实例 (TSPlayer)。
* @param {object} pos - (向量对象) 技能释放的起始位置 (Vector2)。
* @param {object} vel - (向量对象) 技能释放的方向向量 (Vector2),从玩家指向鼠标光标。
* @param {number} identify - (数字) 如果此技能是由另一个弹幕触发的,这里是那个弹幕的ID,否则为 -1。
* ---------------------------------------------------------------------------------------------
*/
function main(skill, ply, pos, vel, identify) {

// 导入XNA框架的命名空间,以便使用Vector2等对象。
var Xna = importNamespace("Microsoft.Xna.Framework");

// --- 1. 定义技能的核心参数 ---
const projectileId = 451; // 弹幕ID: 451 是“波涌之刃”
const damage = 200; // 伤害值
const knockback = 5.0; // 击退力度
const speed = 14; // 弹幕飞行速度
const spreadAngle = 15; // 扇形扩散的角度(单边),15度可以形成一个不错的扇形区域

// --- 2. 计算扇形发射所需的角度 ---

// 我们首先需要一个纯粹的“方向”,所以把 'vel' 向量的长度标准化为1。
// .SafeNormalize(Xna.Vector2.UnitX) 是一个安全的操作,即使'vel'是零向量,也会返回一个默认方向(向右),防止脚本出错。
var baseDirection = vel.SafeNormalize(Xna.Vector2.UnitX);

// 将我们习惯的“角度制”转换为数学计算所需的“弧度制”。
const angleInRadians = spreadAngle * (Math.PI / 180);

// 定义一个旋转角度数组,分别代表向左偏转、正前方、向右偏转。
const rotationAngles = [-angleInRadians, 0, angleInRadians];

// --- 3. 循环发射弹幕 (修正部分) ---

// 定义弹幕的统一生成位置。
var spawnPosition = ply.TPlayer.Center;

// 遍历角度数组,为每个角度生成一道弹幕。
rotationAngles.forEach(angle => {
// 使用标准的二维向量旋转公式来计算新的方向向量。
// x' = x*cos(θ) - y*sin(θ)
// y' = x*sin(θ) + y*cos(θ)
// 这确保了我们能精确控制每一个弹幕的发射方向。
var rotatedX = baseDirection.X * Math.cos(angle) - baseDirection.Y * Math.sin(angle);
var rotatedY = baseDirection.X * Math.sin(angle) + baseDirection.Y * Math.cos(angle);

// 创建一个新的向量来存储计算出的旋转后方向。
var rotatedDirection = new Xna.Vector2(rotatedX, rotatedY);

// 将新方向向量的长度设置为我们预想的速度,得到最终的速度向量。
var finalVelocity = rotatedDirection.ToLenOf(speed);

// 调用插件函数生成弹幕。
var projIndex = SpawnProjtile(ply, spawnPosition, finalVelocity, projectileId, damage, knockback, ply.Index);

// 同步弹幕。
SendProjectilePacket(projIndex);
});
}
 

附件

  • 通用武技:波涌剑气.png
    通用武技:波涌剑气.png
    221.2 KB · 查看: 3
/**
* @description 通用武技:无影之镰。
* 技能触发时,以玩家为中心,向周围10个方向发射死神镰刀弹幕,共分3波次进行发射,每次之间有短暂延迟。
*
* @skill_trigger [主动, CD]
* 这个脚本预设在 tshock/Economics/Skill.json 文件中与一个技能绑定,
* 并且该技能的 "触发模式" 被设置为 ["主动", "CD"]。
*
* @author
* @version 1.0
*
* ---------------------------------------------------------------------------------------------
* Main 函数参数说明:
* @param {object} skill - (技能对象) 触发此脚本的技能在 Skill.json 中的完整配置信息。
* @param {object} ply - (玩家对象) 触发此技能的玩家实例 (TSPlayer)。
* @param {object} pos - (向量对象) 技能释放的起始位置 (Vector2)。
* @param {object} vel - (向量对象) 技能释放的方向向量 (Vector2),此脚本中未使用,因为方向是固定的。
* @param {number} identify - (数字) 如果此技能是由另一个弹幕触发的,这里是那个弹幕的ID,否则为 -1。
* ---------------------------------------------------------------------------------------------
*/
function main(skill, ply, pos, vel, identify) {

// 导入XNA框架的命名空间,以便使用向量(Vector2)对象。
var Xna = importNamespace("Microsoft.Xna.Framework");

// --- 1. 定义技能的核心参数 ---
const projectileId = 274; // 弹幕ID: 274 是“死神镰刀”
const damage = 200; // 伤害值
const knockback = 7.0; // 击退力度,死神镰刀有较强击退
const speed = 9; // 弹幕飞行速度
const numberOfProjectiles = 10; // 每波发射的弹幕数量
const numberOfWaves = 3; // 总共发射的波次
const delayBetweenWaves = 12; // 每一波之间的延迟时间(单位:帧,12帧约等于0.2秒)

// --- 2. 使用循环和延迟函数(Schedule)来创建多波次攻击 ---

// 外层循环负责创建3个波次。
for (let wave = 0; wave < numberOfWaves; wave++) {

// 计算当前波次需要延迟执行的时间。
// 第一波(wave=0): 延迟 0 * 12 = 0 帧 (立即执行)
// 第二波(wave=1): 延迟 1 * 12 = 12 帧
// 第三波(wave=2): 延迟 2 * 12 = 24 帧
const currentDelay = wave * delayBetweenWaves;

// 调用插件内置的 Schedule 函数。
// 它会安排一个任务,在指定的延迟(currentDelay)帧数后,执行第一个参数提供的函数。
Schedule(() => {
// ==========================================================
// 以下是单波弹幕的发射逻辑,它将在延迟之后被执行
// ==========================================================

// 计算每个弹幕之间的角度间隔。一个完整的圆是 2 * PI 弧度。
const angleIncrement = (2 * Math.PI) / numberOfProjectiles;
const spawnPosition = ply.TPlayer.Center;

// 内层循环负责在同一波次内,向10个不同方向发射弹幕。
for (let i = 0; i < numberOfProjectiles; i++) {

// 计算当前这枚弹幕的发射角度。
const currentAngle = i * angleIncrement;

// 根据角度计算出X和Y方向上的速度分量,构成方向向量。
const direction = new Xna.Vector2(Math.cos(currentAngle), Math.sin(currentAngle));

// 设置最终的速度向量(方向 * 速度)。
const velocity = direction.ToLenOf(speed);

// 生成弹幕。
const projIndex = SpawnProjtile(ply, spawnPosition, velocity, projectileId, damage, knockback, ply.Index);

// 同步弹幕。
SendProjectilePacket(projIndex);
}
}, currentDelay);
}
}
 

附件

  • 通用武技:无影之镰.png
    通用武技:无影之镰.png
    329 KB · 查看: 4
/**
* ============================================================================================
* @description 通用枪术:炸弹支援 (教学版)
* * [技能效果简介]
* 当玩家主动使用此技能时,脚本会自动执行以下操作:
* 1. 在玩家周围一个很大的范围(50格)内进行“雷达扫描”,寻找敌人。
* 2. 锁定最多5个离玩家最近的敌人作为打击目标。
* 3. 模拟一次空中支援,在每个被锁定敌人的头顶正上方,降下一枚导弹。
* 4. 为了视觉效果,每枚导弹的投放都有轻微的延迟和水平位置的随机浮动。
* * [教学要点]
* - 如何使用插件函数进行范围索敌 (FindRangeNPCs)。
* - 如何处理和筛选从C#返回的集合对象 (.Take, .ToList)。
* - 如何使用 for 循环遍历目标并发动攻击。
* - 如何使用 Schedule 函数制造延迟,实现“波次”效果。
* - 如何理解和计算泰拉瑞亚世界中的坐标。
* - 如何解决常见的JS与C#交互时的类型问题(整数 vs 浮点数)。
*
* @skill_trigger [主动, CD]
* * @author
* @version 1.0
* ============================================================================================
*/
function main(skill, ply, pos, vel, identify) {

// ------------------------------------------------------------------------------------------
// 准备工作:导入脚本中需要用到的C#命名空间
// ------------------------------------------------------------------------------------------
// 这允许我们在JS中使用C#的强大功能,比如“向量(Vector2)”和“泰拉瑞亚主类(Main)”。
var Xna = importNamespace("Microsoft.Xna.Framework");
var Terraria = importNamespace("Terraria");

// ------------------------------------------------------------------------------------------
// 第1步:定义技能的所有核心参数
// 将所有可调整的数值定义为常量,可以让代码更易读、易修改。
// ------------------------------------------------------------------------------------------

// [弹幕相关]
const projectileId = 134; // 弹幕ID: 134是“导弹1。它垂直下落,爆炸效果好,很适合做空袭。
const damage = 200.0; // 伤害值。写成200.0(浮点数)是为了避免和C#底层函数发生类型冲突。
const knockback = 8.0; // 击退值。同样使用浮点数。
const bombSpeed = 10; // 炸弹下落的速度。

// [逻辑相关]
const numberOfBombs = 5; // 最多攻击的目标数量。
const delayBetweenBombs = 6; // 每个炸弹之间的投放延迟(单位:帧, 60帧≈1秒)。
const searchRadius = 800; // 索敌半径(单位:像素)。1格=16像素,所以800像素就是50格。

// [坐标与视觉效果相关]
const spawnHeight = 320; // 炸弹在目标头顶上方多高处生成。320像素=20格。
const randomOffsetX = 64; // 为了让轰炸不那么死板,给炸弹一个水平方向的随机偏移,最大64像素(4格)。


// ------------------------------------------------------------------------------------------
// 第2步:寻找并筛选打击目标
// ------------------------------------------------------------------------------------------

// 调用插件提供的扩展函数 FindRangeNPCs,它就像一个雷达,扫描玩家周围指定半径内的所有敌对NPC。
// 这个函数会返回一个包含所有找到的NPC对象的C#集合。
var nearbyNpcs = ply.TPlayer.position.FindRangeNPCs(searchRadius);

// 这是一个非常重要的“防御性”代码:如果雷达没有扫描到任何敌人,就直接结束技能,避免后续代码出错。
if (!nearbyNpcs || nearbyNpcs.Count == 0) {
ply.SendInfoMessage("周围没有发现可攻击的目标。"); // 可以给玩家一个提示
return; // 使用 return 提前结束函数
}

// 我们最多只想攻击5个目标。使用 .Take(5) 可以从找到的所有敌人中,只取出前5个。
// 注意:.Take()返回的仍然是一个C#的集合类型。
var targetedNpcs = nearbyNpcs.Take(numberOfBombs);

// [重点] 将C#集合转换为JS可以安全遍历的列表。
// 因为C#集合没有JS数组的 .forEach() 方法,我们用 .ToList() 将其转换为列表,
// 然后就可以用标准的 for 循环来处理了。这是解决上次报错的关键。
var npcList = targetedNpcs.ToList();

// ------------------------------------------------------------------------------------------
// 第3步:遍历所有目标,并为每个目标安排一次延迟轰炸
// ------------------------------------------------------------------------------------------

// 使用一个标准的 for 循环,它的通用性最强,可以安全地遍历我们从C#得到的 npcList。
for (let i = 0; i < npcList.Count; i++) {
// 从列表中按顺序取出当前要攻击的敌人对象。
var npc = npcList;

// 利用循环的索引 i 来计算出阶梯式的延迟,实现“依次落下”的效果。
const currentDelay = i * delayBetweenBombs;

// 调用 Schedule 函数,它像一个定时器,安排一个任务在 currentDelay 帧之后执行。
Schedule(() => {
// --- 这里的代码块,会在延迟时间到达后,为单个NPC执行 ---

// a. 计算随机偏移,让每次轰炸的位置都略有不同。
const offsetX = (Math.random() - 0.5) * 2 * randomOffsetX;

// b. 计算炸弹的精确生成坐标。
const spawnX = npc.Center.X + offsetX;
// 泰拉瑞亚坐标系Y轴向下为正,所以要从目标Y坐标减去高度,才能在头顶生成。
const spawnY = npc.Center.Y - spawnHeight;
const spawnPosition = new Xna.Vector2(spawnX, spawnY);

// c. 设置炸弹的速度向量(垂直向下)。
const velocity = new Xna.Vector2(0, 1).ToLenOf(bombSpeed);

// d. 调用核心函数生成炸弹。
const projIndex = SpawnProjtile(ply, spawnPosition, velocity, projectileId, damage, knockback, ply.Index);

// e. 如果弹幕成功创建(返回的索引不是-1),就将它同步给所有玩家。
if (projIndex > -1) {
SendProjectilePacket(projIndex);
}

}, currentDelay);
}
}
 

附件

  • 通用枪术:导弹支援.png
    通用枪术:导弹支援.png
    549.2 KB · 查看: 3
最后编辑:
其实还有些更牛逼的技能,比如说我们游戏中最垃圾的宠物,让其寻找弹幕然后让宠物进行攻击,这个功能正在测试,一堆bug,比如说每次使用都会重新召唤一个新的宠物导致两个宠物都在你身边,一直召唤一直出之类的。。。。
 

附件

  • 宠物.png
    宠物.png
    279.4 KB · 查看: 5
  • 宠物1.png
    宠物1.png
    502.4 KB · 查看: 4