前言

v0.7更新

  1. 为空涡龙增加了三个技能:烈焰风暴(Z键)、光束彗星(X键)、龙彗星(C键)。烈焰风暴会吸附周围的敌对NPC、龙彗星会沿鼠标方向发射、光束彗星的初始方向沿鼠标方向发射,随后自动追踪周围敌怪。
  2. 云海鹿和草莽猪的冲刺改为Z键。

v0.6更新

  1. 增加了空涡龙坐骑,合成配方:10个夜明锭 + 20个丝绸 + 5个蓝玉,需要帕鲁装置制作台
  2. 增加了帕鲁装置制作台,合成材料:30个木头 + 10个丝绸 + 10个蓝玉
  3. 修改了草莽猪和云海鹿的合成配方
  • 草莽猪:10个木头 + 10个石头 + 5个蓝玉 + 10个皮革 + 5个椎骨,单击右键进行冲刺,冲刺会摧毁沿途路上的普通树木(一些特殊树木如樱花树等除外)
  • 云海鹿:20个神圣锭 + 5个飞翔之魂 + 5个光明之魂 + 5个蓝玉,可以进行二段跳,单击右键进行冲刺

如何自定义坐骑

  • 原版Terraria里的坐骑,首先都要有一个召唤物。

  • 其次就是有一个buff图标

  • 最后就是坐骑本体

  • 所以首先我们要先来制作这个召唤物。那么第一步我们先来创建一个Mod项目吧。

制作召唤物

  • 召唤物的本质实际上就是一个Item,所以我们创建一个类,继承ModItem即可。
  • 在这个类中,我们来定义召唤物的一些属性,以及合成材料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using PolWorldMounts.Content.Mounts;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace PolWorldMounts.Content.Items.Mounts
{
public class FenglopeMountItem : ModItem
{
public override void SetDefaults() {
Item.width = 20;
Item.height = 30;
Item.useTime = 20;
Item.useAnimation = 20;
Item.useStyle = ItemUseStyleID.Swing;
Item.value = Item.sellPrice(gold: 3);
Item.rare = ItemRarityID.Green;
Item.UseSound = SoundID.Item79;
Item.noMelee = true;
Item.mountType = ModContent.MountType<FenglopeMount>(); // 这个是调用我们自定义的坐骑
}

// 这里简单用一个泥土合成,实际上可以改成10个木材、10个石头、5个蓝宝石来进行合成,这样的合成配方和帕鲁中类似
public override void AddRecipes() {
Recipe recipe = CreateRecipe();
recipe.AddIngredient(ItemID.DirtBlock, 1);
recipe.Register();
}
}
}
  • 物品制作完毕后,我们还需要制作贴图,这里我就以帕鲁球为例制作贴图了。画风最好是符合泰拉瑞亚的像素风,这样看起来比较舒服。
  • 那我们直接启动幻兽帕鲁,进游戏截一张帕鲁球的图
  • 拿到图肯定不能直接用,我们首先要扣掉背景,随后转为像素风,调整尺寸为32*32。扣背景我这里直接用的百度AI图片助手,扣好了之后下载到本地,我们来进一步处理像素风
  • 像素风的处理,我这里直接用的Python的PIL库来进行处理
1
2
3
4
5
6
7
8
9
10
11
12
13
from PIL import Image


def pixelate(image_path, block_size):
img = Image.open(image_path)
w, h = img.size
img_small = img.resize((w // block_size, h // block_size), resample=Image.NEAREST)
result = img_small.resize(img.size, Image.NEAREST)
return result


pixelated_image = pixelate('帕鲁球.png', 7)
pixelated_image.save('pixelated.png')
  • 处理后的效果还是不错的,那么云海鹿召唤物的功能,我们就开发完毕了

制作坐骑

  • 坐骑的创建,我们同样需要继承ModMount,随后就是定义坐骑的基本属性,云海鹿的特性有二段跳,所以我们这里不阻止使用额外跳跃,只要你装备了云朵瓶或者气球束之类的会产生额外跳跃的饰品,当你在骑行云海鹿的时候,都可以正常触发。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    public class FenglopeMount : ModMount
    {
    public override void SetStaticDefaults()
    {
    // 定义坐骑的基本属性
    MountData.jumpHeight = 20; // 坐骑能跳多高
    MountData.acceleration = 0.19f; // 加速率,即坐骑加速的速度
    MountData.jumpSpeed = 20f; // 当按下跳跃键时,坐骑和玩家向上跳跃的速度
    MountData.blockExtraJumps = false; // 是否阻止使用额外跳跃(如瓶子中的云)
    MountData.constantJump = true; // 是否允许按住跳跃键进行连续跳跃
    MountData.heightBoost = 20; // 坐骑与地面之间的高度
    MountData.fallDamage = 0f; // 从高处跌落时受到的伤害倍率
    MountData.runSpeed = 11f; // 坐骑的奔跑速度
    MountData.dashSpeed = 8f; // 冲刺速度
    MountData.flightTimeMax = 0; // 最大飞行时间,0表示不能飞行

    // 设置疲劳最大值为0,意味着骑乘时不会产生疲劳
    MountData.fatigueMax = 0;

    // 关联坐骑的Buff,即骑乘时玩家获得的状态效果
    MountData.buff = ModContent.BuffType<Buffs.FenglopeMountBuff>();

    // 动画帧数和玩家偏移量设置
    MountData.totalFrames = 5; // 总共的动画帧数,取决于你自己的贴图
    MountData.playerYOffsets = Enumerable.Repeat(20, MountData.totalFrames).ToArray(); // 玩家相对于坐骑的Y轴偏移量数组,用于微调玩家在坐骑贴图的位置
    MountData.xOffset = 20; // 玩家相对于坐骑的X轴偏移量,用于微调玩家在坐骑贴图上的位置
    MountData.yOffset = -12; // 玩家相对于坐骑的Y轴偏移量,用于微调玩家在坐骑贴图上的位置
    MountData.playerHeadOffset = 22; // 玩家头部相对于坐骑的偏移量,用于微调玩家在坐骑贴图上的位置
    MountData.bodyFrame = 3; // 玩家身体在坐骑上的帧号

    // 不同状态下的动画设置
    // 站立
    MountData.standingFrameCount = 0;
    MountData.standingFrameDelay = 12;
    MountData.standingFrameStart = 0;
    // 奔跑
    MountData.runningFrameCount = 4;
    MountData.runningFrameDelay = 120;
    MountData.runningFrameStart = 1;
    // 飞行
    MountData.flyingFrameCount = 0;
    MountData.flyingFrameDelay = 0;
    MountData.flyingFrameStart = 2;
    // 空中
    MountData.inAirFrameCount = 1;
    MountData.inAirFrameDelay = 12;
    MountData.inAirFrameStart = 2;
    // 闲置
    MountData.idleFrameCount = 4;
    MountData.idleFrameDelay = 12;
    MountData.idleFrameStart = 0;
    MountData.idleFrameLoop = true;
    // 游泳
    MountData.swimFrameCount = MountData.inAirFrameCount;
    MountData.swimFrameDelay = MountData.inAirFrameDelay;
    MountData.swimFrameStart = MountData.inAirFrameStart;
    }
    }
  • 直接叫上一个好兄弟当工具人,去游戏里简单录一段云海鹿的跑步动画,然后自己截图扣下来
  • 整个Mod开发耗时最久的就是抠图,哈哈哈哈,一张一张扣完之后,下载保存到本地,然后我简单写了个合成脚本,把多张图垂直排列合并,并缩放,代码目录下按你的顺序存放1.png~5.png即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from PIL import Image


def pixelate(image_path, block_size):
img = Image.open(image_path)
w, h = img.size
img_small = img.resize((w // block_size, h // block_size), resample=Image.NEAREST)
result = img_small.resize(img.size, Image.NEAREST)
return result


def scale_image(image, scale_factor):
# 获取当前图片的尺寸
width, height = image.size
# 计算新的尺寸
new_width = width // scale_factor
new_height = height // scale_factor
# 返回缩放后的图片
return image.resize((new_width, new_height), Image.ANTIALIAS)


# 保存处理后的图片对象和尺寸
processed_images = []
total_width = 0
total_height = 0
scale_factor = 4 # 缩放比例

# 处理图片并计算总高度和宽度
for i in range(5):
pixelated_image = pixelate(f'{i + 1}.png', 10)
scaled_image = scale_image(pixelated_image, scale_factor)
processed_images.append(scaled_image)
total_width = max(total_width, scaled_image.width)
total_height += scaled_image.height

# 创建一张新图片,背景颜色为透明
new_image = Image.new('RGBA', (total_width, total_height), color=(0, 0, 0, 0))

# 将每张处理后的图片粘贴到新图片上
y_offset = 0
for img in processed_images:
new_image.paste(img, (0, y_offset), img)
y_offset += img.height

# 保存新图片
new_image.save('combined_pixelated.png', 'PNG')
  • 最终的效果

制作坐骑BUFF

  • 玩家使用坐骑,其实是对自身施加一个buff,所以我们还要编写一个云海鹿的坐骑BUFF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using PolWorldMounts.Content.Mounts; // 引入模组中 Mounts 目录下的命名空间
using Terraria; // 引入游戏主引擎的命名空间
using Terraria.ModLoader; // 引入模组加载器的命名空间

namespace PolWorldMounts.Content.Buffs { // 定义模组 Buffs 目录下的命名空间
public class FenglopeMountBuff : ModBuff { // 定义一个继承自 ModBuff 的公共类 FenglopeMountBuff
public override void SetStaticDefaults() { // 重写父类的方法 SetStaticDefaults
Main.buffNoTimeDisplay[Type] = true; // 设置此Buff不会显示持续时间
Main.buffNoSave[Type] = true; // 设置此Buff不会被保存,即在死亡或退出世界后消失
}

public override void Update(Player player, ref int buffIndex) { // 重写父类的方法 Update,当Buff应用到玩家身上时被调用
player.mount.SetMount(ModContent.MountType<FenglopeMount>(), player); // 设置玩家的坐骑为 FenglopeMount
player.buffTime[buffIndex] = 10; // 设置Buff的持续时间为10帧,实际作用是持续刷新,因为会不断被重新应用
}
}
}
  • 简单制作一个Buff图标,同样是使用百度抠图,随后将图片下载到本地,随后像素化,并且指定图片尺寸为32x32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from PIL import Image


def pixelate(image_path, output_size):
img = Image.open(image_path)
img_small = img.resize(output_size, resample=Image.NEAREST)
block_size_w = img.width // output_size[0]
block_size_h = img.height // output_size[1]
img_pixelated = img_small.resize((img.width // block_size_w, img.height // block_size_h), resample=Image.NEAREST)
result = img_pixelated.resize(output_size, Image.NEAREST)
return result


pixelated_image = pixelate('云海鹿.png', (32, 32))
pixelated_image.save('pixelated_image_云海鹿.png')

泰拉瑞亚 启动!

  • 重新生成并加载Mod,我们的云海鹿就已经可以使用了,只需要一块泥土进行制作,不需要任何合成台

增强云海鹿

不再需要云瓶进行二段跳

  • 带个云瓶太麻烦了,能不能自己想想办法来实现二段跳呢?当然是有的,我们可以先创建一个二段跳的标志位。标识玩家是否已经进行了二段跳。
  • 随后我们可以通过判断玩家的Y轴速度是否为0来判断是否处于地面。
    • 如果是的话,那么重置一下二段跳的标志位。
    • 如果不是的话,就可以进行二段跳了。
  • 那么接下来看代码逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private bool hasDoubleJumped = false; // 标志位:是否已经二段跳

// 二段跳逻辑
if (player.controlJump) // 玩家按下了跳跃键
{
if (player.velocity.Y == 0)
{
hasDoubleJumped = false; // 重置二段跳标志位
}
else if (!hasDoubleJumped && player.releaseJump) // 确保没有执行二段跳且玩家松开了跳跃键
{
player.velocity.Y = -18; // 设置跳跃速度
hasDoubleJumped = true; // 设置二段跳标志位,防止玩家无限跳跃

for (int i = 0; i < 200; i++) // 这里只是加了一个粒子特效,模仿云瓶的二段跳效果,只是为了好看,没有其他作用
{
Vector2 dustPosition = player.position;
Dust dust = Dust.NewDustDirect(dustPostion, player.width, player.height, DustID.Snow, 0f, 0f, 100, Color.White, 1.5f);
dust.velocity *= 1.2f;
dust.color = Color.White;
dust.noGravity = true;
}
}
}
  • 这样的话,就不再需要云瓶来实现二段跳啦。

增加冲刺技能

  • 云海鹿的招牌技能:阴云之岚,这里当然也是要尽量还原一下的啦,不过贴图和特效就还原不了了,除非能让我白嫖一个画师,哈哈哈哈。
  • 技能实现方式其实很简单,本质上就是一个BUFF技能。施放技能后,玩家会被附加一个DEBUFF,持续一段时间,在此期间无法再次释放技能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 检查冲刺冷却时间,如果冷却中,减少冷却时间
if (dashCooldown > 0)
{
dashCooldown--;
}

// 检查冲刺时间是否还剩余
if (dashTimeLeft > 0)
{
// 仍在冲刺中,减少剩余冲刺时间
dashTimeLeft--;
// 设置玩家的速度,根据当前方向和冲刺速度
player.velocity.X = player.direction * DashSpeed;

// 生成玩家的冲刺命中框
Rectangle hitbox = new Rectangle((int)(player.position.X + player.velocity.X),
(int)(player.position.Y + player.velocity.Y),
player.width,
player.height);
// 遍历所有NPC,检测是否与冲刺命中框相交
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC target = Main.npc[i];
// 检查NPC是否活跃且不是友好的
if (target.active && !target.friendly && target.Hitbox.Intersects(hitbox))
{
// 创建击中信息,设置伤害、击退和命中方向
NPC.HitInfo hitInfo = new NPC.HitInfo
{
Damage = DashDamage,
Knockback = DashKnockBack,
HitDirection = player.direction
};
// 对目标NPC造成伤害
target.StrikeNPC(hitInfo);
}
}

// 生成粒子特效
for (int i = 0; i < 10; i++) // 每帧生成10个粒子
{
// 生成粒子位置,基于玩家的中心位置并添加随机偏移
Vector2 dustPosition = player.Center + new Vector2(Main.rand.Next(-20, 5), Main.rand.Next(-20, 5));
// 创建新粒子,设置速度和生命周期
Dust.NewDust(dustPosition, 0, 0, DustID.Snow, player.velocity.X * 0.5f, player.velocity.Y * 0.5f, 100, default, 1.5f);
}
}
// 如果冷却时间为0,并且玩家没有被“疲劳”BUFF影响
else if (dashCooldown == 0 && !player.HasBuff(ModContent.BuffType<Buffs.FenglopeExhaustedBuff>()))
{
// 获取冲刺按键的配置
mountDashKey = ModContent.GetInstance<PolworldModConfig>().MountDashKey;
// 检查玩家是否按下了冲刺按键
if (Main.keyState.IsKeyDown(mountDashKey))
{
// 开始冲刺,设置剩余冲刺时间和冷却时间
dashTimeLeft = DashDuration;
dashCooldown = DashCooldown;
// 为玩家添加疲劳BUFF,持续600帧(10秒)
player.AddBuff(ModContent.BuffType<Buffs.FenglopeExhaustedBuff>(), 600);
}
}

草莽猪

  • 草莽猪只不过移速降低了一点,原版能挖矿,但是泰拉里没有石头矿能撞,所以就改成了撞断沿途的树木。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
using Terraria.GameInput;
using System.Linq;
using Terraria.Audio;
using Microsoft.Xna.Framework.Input;
using PolWorldMounts.Content;

namespace PolWorldMounts.Content.Mounts
{
public class RushoarMount : ModMount
{
private Keys mountDashKey;
private const int DashCooldown = 60; // 冲刺冷却时间,单位:帧
private const int DashDuration = 90; // 冲刺持续时间,单位:帧
private const float DashSpeed = 12f; // 冲刺速度
private const int DashDamage = 50; // 冲刺造成的伤害
private const float DashKnockBack = 10f; // 冲刺造成的击退力

private int dashTimeLeft = 0;
private int dashCooldown = 0;

public override void SetStaticDefaults()
{
MountData.jumpHeight = 5; // How high the mount can jump.
MountData.acceleration = 0.19f; // The rate at which the mount speeds up.
MountData.jumpSpeed = 5f; // The rate at which the player and mount ascend towards (negative y velocity) the jump height when the jump button is pressed.
MountData.blockExtraJumps = true; // 阻止饰品增加跳跃次数
MountData.constantJump = true; // Allows you to hold the jump button down.
MountData.heightBoost = -20; // Height between the mount and the ground
MountData.fallDamage = 0f; // Fall damage multiplier.
MountData.runSpeed = 8f; // The speed of the mount
MountData.dashSpeed = 8f; // The speed the mount moves when in the state of dashing.
MountData.flightTimeMax = 0; // The amount of time in frames a mount can be in the state of flying.

MountData.fatigueMax = 0;
MountData.buff = ModContent.BuffType<Buffs.RushoarMountBuff>(); // The ID number of the buff assigned to the mount.

// Effects
// MountData.spawnDust = ModContent.DustType<Dusts.Sparkle>(); // The ID of the dust spawned when mounted or dismounted.

// Frame data and player offsets
MountData.totalFrames = 5; // Amount of animation frames for the mount
MountData.playerYOffsets = Enumerable.Repeat(20, MountData.totalFrames).ToArray(); // Fills an array with values for less repeating code
MountData.xOffset = 20;
MountData.yOffset = -12;
MountData.playerHeadOffset = 22;
MountData.bodyFrame = 3;
// Standing
MountData.standingFrameCount = 0;
MountData.standingFrameDelay = 12;
MountData.standingFrameStart = 0;
// Running
MountData.runningFrameCount = 4;
MountData.runningFrameDelay = 50;
MountData.runningFrameStart = 0;
// Flying
MountData.flyingFrameCount = 0;
MountData.flyingFrameDelay = 0;
MountData.flyingFrameStart = 2;
// In-air
MountData.inAirFrameCount = 1;
MountData.inAirFrameDelay = 12;
MountData.inAirFrameStart = 1;
// Idle
MountData.idleFrameCount = 0;
MountData.idleFrameDelay = 12;
MountData.idleFrameStart = 0;
MountData.idleFrameLoop = true;
// Swim
MountData.swimFrameCount = MountData.inAirFrameCount;
MountData.swimFrameDelay = MountData.inAirFrameDelay;
MountData.swimFrameStart = MountData.inAirFrameStart;

mountDashKey = ModContent.GetInstance<PolworldModConfig>().MountDashKey;

if (!Main.dedServ)
{
MountData.textureWidth = MountData.backTexture.Width() + 20;
MountData.textureHeight = MountData.backTexture.Height();
}
}

public override void UpdateEffects(Player player)
{
if (dashCooldown > 0)
{
dashCooldown--;
}

if (dashTimeLeft > 0)
{
dashTimeLeft--;
player.velocity.X = player.direction * DashSpeed;

Rectangle hitbox = new Rectangle((int)(player.position.X + player.velocity.X), (int)(player.position.Y + player.velocity.Y), player.width, player.height);
BreakTreesAlongPath(player, hitbox);

for (int i = 0; i < Main.maxNPCs; i++)
{
NPC target = Main.npc[i];
if (target.active && !target.friendly && target.Hitbox.Intersects(hitbox))
{
NPC.HitInfo hitInfo = new NPC.HitInfo
{
Damage = DashDamage,
Knockback = DashKnockBack,
HitDirection = player.direction
};
target.StrikeNPC(hitInfo);
}
}

// 生成粒子特效
for (int i = 0; i < 10; i++) // 每帧生成10个粒子
{
Vector2 dustPosition = player.Center + new Vector2(Main.rand.Next(-20, 5), Main.rand.Next(-20, 5));
Dust.NewDust(dustPosition, 0, 0, DustID.Dirt, player.velocity.X * 0.5f, player.velocity.Y * 0.5f, 100, default, 1.5f);
}
}
else if (dashCooldown == 0 && !player.HasBuff(ModContent.BuffType<Buffs.RushoarExhaustedBuff>()))
{
mountDashKey = ModContent.GetInstance<PolworldModConfig>().MountDashKey;
if (Main.keyState.IsKeyDown(mountDashKey))
{
dashTimeLeft = DashDuration;
dashCooldown = DashCooldown;
player.AddBuff(ModContent.BuffType<Buffs.RushoarExhaustedBuff>(), 600);
}
}
}

private void BreakTreesAlongPath(Player player, Rectangle hitbox)
{
int tileStartX = hitbox.Left / 16;
int tileEndX = hitbox.Right / 16;
int tileStartY = hitbox.Top / 16;
int tileEndY = hitbox.Bottom / 16;

for (int x = tileStartX; x <= tileEndX; x++)
{
for (int y = tileStartY; y <= tileEndY; y++)
{
Tile tile = Framing.GetTileSafely(x, y);
if ((tile.TileType == TileID.Trees
//tile.TileType == TileID.PalmTree ||
//tile.TileType == TileID.VanityTreeSakura
)
&& tile.HasTile)
{
// 如果是树顶
if (tile.TileFrameY == 0)
{
WorldGen.KillTile(x, y, false, false, true);
SoundEngine.PlaySound(SoundID.Item14);
}
// 如果是树身
else if (tile.TileFrameY % 22 == 0)
{
WorldGen.KillTile(x, y, false, false, true);
SoundEngine.PlaySound(SoundID.Item14);
}
}
}
}
}
}
}

空涡龙

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace PolWorldMounts.Content.Mounts
{
public class JetragonMount : ModMount
{
private const float FlightAcceleration = 0.8f; // 飞行加速度
private const float MaxVerticalSpeed = 25f; // 最大垂直速度
private const float HorizontalAcceleration = 0.1f; // 水平加速度
private const float DecelerationFactor = 0.95f; // 减速因子
private bool isShooting = false;
private bool isShootingMeteor = false;
private bool isShootingFlareStorm = false;

public override void SetStaticDefaults()
{
MountData.jumpHeight = 15; // How high the mount can jump.
MountData.acceleration = HorizontalAcceleration; // The rate at which the mount speeds up.
MountData.jumpSpeed = 15f; // The rate at which the player and mount ascend towards (negative y velocity) the jump height when the jump button is pressed.
MountData.blockExtraJumps = true; // 阻止饰品增加跳跃次数
MountData.constantJump = true; // Allows you to hold the jump button down.
MountData.heightBoost = 20; // Height between the mount and the ground
MountData.fallDamage = 0f; // Fall damage multiplier.
MountData.runSpeed = 25f; // The speed of the mount
MountData.dashSpeed = 15f; // The speed the mount moves when in the state of dashing.
MountData.flightTimeMax = 999999; // 实现持续飞行

MountData.fatigueMax = 0;
MountData.buff = ModContent.BuffType<Buffs.JetragonMountBuff>(); // The ID number of the buff assigned to the mount.

// Effects
// MountData.spawnDust = ModContent.DustType<Dusts.Sparkle>(); // The ID of the dust spawned when mounted or dismounted.

// Frame data and player offsets
MountData.totalFrames = 3; // Amount of animation frames for the mount
MountData.playerYOffsets = Enumerable.Repeat(20, MountData.totalFrames).ToArray(); // Fills an array with values for less repeating code
MountData.xOffset = -10;
MountData.yOffset = -12;
MountData.playerHeadOffset = 30; // 确保玩家头部不被遮挡
MountData.bodyFrame = 4; // 调整这个参数,以确保与玩家的动画帧兼容

// Standing
MountData.standingFrameCount = 3;
MountData.standingFrameDelay = 8;
MountData.standingFrameStart = 0;
// Running
MountData.runningFrameCount = 3;
MountData.runningFrameDelay = 8;
MountData.runningFrameStart = 0;
// Flying
MountData.flyingFrameCount = 3;
MountData.flyingFrameDelay = 8;
MountData.flyingFrameStart = 0;
// In-air
MountData.inAirFrameCount = 3;
MountData.inAirFrameDelay = 8;
MountData.inAirFrameStart = 0;
// Idle
MountData.idleFrameCount = 0;
MountData.idleFrameDelay = 8;
MountData.idleFrameStart = 0;
MountData.idleFrameLoop = true;
// Swim
MountData.swimFrameCount = MountData.inAirFrameCount;
MountData.swimFrameDelay = MountData.inAirFrameDelay;
MountData.swimFrameStart = MountData.inAirFrameStart;

if (!Main.dedServ)
{
MountData.textureWidth = MountData.backTexture.Width() + 20;
MountData.textureHeight = MountData.backTexture.Height();
}
}

public override void UpdateEffects(Player player)
{
// 不受重力影响
player.gravity = 0f;

// 控制上升和下降
if (player.controlJump)
{
player.velocity.Y = MathHelper.Clamp(
player.velocity.Y - FlightAcceleration,
-MaxVerticalSpeed,
MaxVerticalSpeed
); // 上升
}
else if (player.controlDown)
{
player.velocity.Y = MathHelper.Clamp(
player.velocity.Y + FlightAcceleration,
-MaxVerticalSpeed,
MaxVerticalSpeed
); // 下降
}
else
{
player.velocity.Y *= DecelerationFactor; // 缓慢减速至静止
}

// 控制左右移动
if (player.controlLeft)
{
player.velocity.X = MathHelper.Clamp(
player.velocity.X - MountData.acceleration,
-MountData.runSpeed,
MountData.runSpeed
);
}
else if (player.controlRight)
{
player.velocity.X = MathHelper.Clamp(
player.velocity.X + MountData.acceleration,
-MountData.runSpeed,
MountData.runSpeed
);
}
else
{
player.velocity.X *= DecelerationFactor; // 缓慢减速至静止
}

// 常驻粉色粒子特效
Vector2 pinkDustOffset = new Vector2(-40 * player.direction, -30); // 设定偏移量,这里乘以玩家方向是为了当玩家镜像翻转时,贴图也会镜像翻转
for (int i = 0; i < 3; i++) // 每帧生成3个粒子
{
Vector2 pinkDustPosition =
player.Center
+ pinkDustOffset
+ new Vector2(Main.rand.Next(-10, 10), Main.rand.Next(-10, 10));
int pinkDustIndex = Dust.NewDust(
pinkDustPosition,
0,
0,
DustID.PinkTorch,
0f,
0f,
100,
default,
1.5f
);
Main.dust[pinkDustIndex].noGravity = true; // 粉色粒子不受重力影响
}

// 添加技能检测和冷却逻辑
if (!player.buffType.Contains(ModContent.BuffType<Buffs.JetragonBeamCometBuff>()))
{
// 检查是否按下X键并且当前没有在发射弹幕
if (Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.X) && !isShooting)
{
// 启动异步发射弹幕
isShooting = true;
FireProjectiles(player);
}
}

if (!player.buffType.Contains(ModContent.BuffType<Buffs.JetragonMeteorBuff>()))
{
if (Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C) && !isShootingMeteor)
{
isShootingMeteor = true;
ShootMeteors(player);
}
}

if (!player.buffType.Contains(ModContent.BuffType<Buffs.JetragonFlareStormBuff>()))
{
if (Main.keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Z) && !isShootingFlareStorm)
{
isShootingFlareStorm = true;
FireFlareStorm(player);
}
}
}

public override void Load()
{
base.Load();
}

private async void FireProjectiles(Player player)
{
Vector2 mousePosition = Main.MouseWorld; // 获取鼠标位置
Vector2 playerPosition = player.Center; // 获取玩家位置

for (int i = 0; i < 6; i++)
{
Vector2 offset = new Vector2(player.direction * -20, -20); // 在X轴上偏移20个单位,可以根据需要调整偏移量

// 应用偏移量到player.Center
Vector2 spawnPosition = playerPosition + offset;

// 计算方向向量,从玩家到鼠标
Vector2 directionToMouse = mousePosition - spawnPosition;

// 仅保留水平分量,忽略垂直方向
directionToMouse.Normalize(); // 归一化方向向量

// 设置速度,调整速度大小以适应你的需求
Vector2 projectileVelocity = directionToMouse * 10f; // 10f 为弹幕速度的大小

try
{
// 创建弹幕
Projectile.NewProjectile(player.GetSource_FromThis(), spawnPosition, projectileVelocity, ModContent.ProjectileType<Projectiles.BeamCometProjectile>(), 200, 2, player.whoAmI);
}
catch (Exception ex)
{
// 处理异常,记录错误或采取其他措施
Main.NewText($"Error creating projectile: {ex.Message}");
continue;
}

// 等待0.2秒(200毫秒)
await Task.Delay(200);
}

// 添加冷却Buff
player.AddBuff(ModContent.BuffType<Buffs.JetragonBeamCometBuff>(), 300); // 10秒

// 完成发射后重置状态
isShooting = false;
}

private async void ShootMeteors(Player player)
{
Vector2 mousePosition = Main.MouseWorld;
for (int k = 0; k < 4; k++)
{
for (int i = 0; i < 4; i++)
{
// 随机偏移以模拟不同的落点
Vector2 offset = new Vector2(Main.rand.Next(-100, 100), Main.rand.Next(-100, 100));
Vector2 targetPosition = mousePosition + offset;

// 从玩家位置到目标位置的方向
Vector2 direction = targetPosition - player.Center;
direction.Normalize();
float speed = 10f; // 陨石速度

// 随机化初始速度方向(沿弧形路径)
float angle = Main.rand.NextFloat(-MathHelper.Pi / 4, MathHelper.Pi / 4); // 随机弧形角度
Vector2 initialVelocity = new Vector2(
direction.X * (float)Math.Cos(angle) - direction.Y * (float)Math.Sin(angle),
direction.X * (float)Math.Sin(angle) + direction.Y * (float)Math.Cos(angle)
) * speed;

// 应用偏移量到player.Center
Vector2 offsetProj = new Vector2(
(player.direction * -30) + Main.rand.NextFloat(-10f, 10f), // 随机化X偏移量
-60 + Main.rand.NextFloat(-10f, 10f) // 随机化Y偏移量
);

Vector2 spawnPosition = player.Center + offsetProj;
try
{
// 创建弹幕
int projIndex = Projectile.NewProjectile(player.GetSource_FromThis(), spawnPosition, initialVelocity, ModContent.ProjectileType<Projectiles.DragonMeteorProjectile>(), 200, 2, player.whoAmI);
// 获取创建的弹幕
Projectile projectile = Main.projectile[projIndex];

// 设置弹幕的初始状态,确保它会在开始时沿弧形路径运动
projectile.ai[0] = 1; // 使用 ai[0] 标记弹幕已开始弧形运动
}
catch (NullReferenceException ex)
{
continue;
}
// 等待0.1秒(100毫秒)
await Task.Delay(50);
}
isShootingMeteor = false;
// 等待0.3秒(300毫秒)
await Task.Delay(500);
}
player.AddBuff(ModContent.BuffType<Buffs.JetragonMeteorBuff>(), 1200);
}

private async void FireFlareStorm(Player player)
{
Vector2 mousePosition = Main.MouseWorld; // 获取鼠标位置
Vector2 playerPosition = player.Center; // 获取玩家位置

for (int i = 0; i < 5; i++) // 生成5个FlareStorm弹幕
{
Vector2 spawnPosition = playerPosition + new Vector2(0, 10); // 在玩家下方生成弹幕

// 计算方向向量,从玩家到鼠标
Vector2 directionToMouse = mousePosition - spawnPosition;
directionToMouse.Y = 0;
directionToMouse.Normalize(); // 归一化方向向量

// 设置速度,调整速度大小以适应你的需求
Vector2 velocity = directionToMouse * 10f;

try
{
// 创建FlareStorm弹幕
Projectile.NewProjectile(player.GetSource_FromThis(), spawnPosition, velocity, ModContent.ProjectileType<Projectiles.FlareStormProjectile>(), 300, 2, player.whoAmI);
}
catch (NullReferenceException ex)
{
// 处理异常,记录错误或采取其他措施
continue;
}

// 等待0.1秒(100毫秒)
await Task.Delay(100);
}

// 添加冷却时间

player.AddBuff(ModContent.BuffType<Buffs.JetragonFlareStormBuff>(), 1800); // 10秒

// 完成发射后重置状态
isShootingFlareStorm = false;
}

}
}
  • 光束彗星一开始会沿着鼠标方向发射,随后会自动追踪800像素范围内的敌怪。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace PolWorldMounts.Content.Projectiles
{
public class BeamCometProjectile : ModProjectile
{

public override void SetDefaults()
{
Projectile.width = 10; // 弹幕宽度
Projectile.height = 10; // 弹幕高度
Projectile.friendly = true; // 是否对玩家友好
Projectile.hostile = false; // 是否对敌人友好
Projectile.tileCollide = true; // 是否与瓷砖碰撞
Projectile.penetrate = 5; // 穿透数量
Projectile.timeLeft = 600; // 弹幕的存活时间(帧)
Projectile.light = 0.5f; // 弹幕的光亮度
Projectile.ignoreWater = true; // 是否忽略水
Projectile.extraUpdates = 1; // 每帧更新次数
}

public override void AI()
{
Player player = Main.player[Projectile.owner];
Vector2 mousePosition = Main.MouseWorld;
NPC target = FindClosestNPC(800f); // 设定追踪范围为800像素

if (target != null)
{
// 如果找到目标,计算弹幕朝向目标的方向
Vector2 directionToTarget = target.Center - Projectile.Center;
directionToTarget.Normalize();

// 平滑过渡到目标方向
float smoothFactor = 0.05f; // 调整平滑的因子,0到1之间,数值越小转向越平滑
Projectile.velocity = Vector2.Lerp(Projectile.velocity, directionToTarget * 4f, smoothFactor);
}

// 粒子效果
for (int i = 0; i < 1; i++) // 每帧生成1个粒子
{
int dustIndex = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.Firework_Pink, Projectile.velocity.X * 0.2f, Projectile.velocity.Y * 0.2f, 100, new Color(205, 71, 208), 0.7f);
Dust dust = Main.dust[dustIndex];
dust.noGravity = true;
dust.velocity *= 0.3f;
dust.scale *= 0.95f;
}

// 添加光效
Lighting.AddLight(Projectile.Center, 0.84f, 0.48f, 0.73f);
}


// 辅助方法,查找最近的敌人
private NPC FindClosestNPC(float maxDetectDistance)
{
NPC closestNPC = null;
float sqrMaxDetectDistance = maxDetectDistance * maxDetectDistance;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (npc.CanBeChasedBy(this))
{
float sqrDistanceToNPC = Vector2.DistanceSquared(npc.Center, Projectile.Center);
if (sqrDistanceToNPC < sqrMaxDetectDistance)
{
sqrMaxDetectDistance = sqrDistanceToNPC;
closestNPC = npc;
}
}
}
return closestNPC;
}

public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
{
target.AddBuff(BuffID.OnFire, 300);
}

public override bool PreDraw(ref Color lightColor)
{
// 获取当前帧的源矩形
Rectangle sourceRectangle = new Rectangle(0, Projectile.frame * Projectile.height, Projectile.width, Projectile.height);

// 获取弹幕的贴图
Texture2D texture = Terraria.GameContent.TextureAssets.Projectile[Projectile.type].Value;

// 拖影效果参数
float alpha = 0.5f; // 拖影的透明度
float scale = Projectile.scale; // 拓影的缩放

// 获取弹幕的速度方向
Vector2 velocity = Projectile.velocity;
velocity.Normalize(); // 归一化速度向量,用于确定拖影的方向

// 计算拖影偏移量
float offsetDistance = 10f; // 拖影偏移量的距离(可以根据需要调整)

// 绘制拖影
for (int i = 0; i < 20; i++) // 绘制5个拖影(可以调整数量)
{
// 计算每个拖影的位置
Vector2 offset = velocity * offsetDistance * i; // 沿着速度方向的偏移量
Color color = Color.Lerp(lightColor, Color.Transparent, alpha / 5f * i); // 逐渐变透明的颜色

Main.spriteBatch.Draw(
texture,
Projectile.Center - Main.screenPosition - offset,
sourceRectangle,
color,
Projectile.rotation,
new Vector2(Projectile.width / 2, Projectile.height / 2),
scale,
SpriteEffects.None,
0f
);
}

// 绘制正常的弹幕
Main.spriteBatch.Draw(
texture,
Projectile.Center - Main.screenPosition,
sourceRectangle,
lightColor,
Projectile.rotation,
new Vector2(Projectile.width / 2, Projectile.height / 2),
scale,
SpriteEffects.None,
0f
);

return false; // 返回false,表示我们自己绘制了弹幕
}


}
}
  • 烈焰风暴个人感觉还是很不错的,抠图就扣了老半天了,会赋予敌人燃烧效果,并且会有吸附效果,清兵神器,自己调试的时候,把CD改成0.1,直接秒天秒地了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using System;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using PolWorldMounts.Content.Projectiles;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace PolWorldMounts.Content.Projectiles
{
public class FlareStormProjectile : ModProjectile
{
public override void SetDefaults()
{
Projectile.width = 168; // 弹幕宽度
Projectile.height = 164; // 弹幕高度
Projectile.friendly = true; // 是否对玩家友好
Projectile.hostile = false; // 是否对敌人友好
Projectile.tileCollide = true; // 是否与瓷砖碰撞
Projectile.penetrate = 9999; // 穿透数量
Projectile.timeLeft = 300; // 弹幕的存活时间(帧)
Projectile.light = 0.8f; // 弹幕的光亮度
Projectile.ignoreWater = true; // 是否忽略水
Projectile.extraUpdates = 1; // 每帧更新次数
Projectile.aiStyle = -1; // 自定义AI
Main.projFrames[Projectile.type] = 4; // 设置弹幕的帧数
}

public override void AI()
{
// 创建火焰粒子特效
if (Main.rand.NextBool(3))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Firework_Yellow);
dust.noGravity = true; // 粒子不受重力影响
dust.velocity *= 1.2f; // 增加粒子速度
dust.scale *= 1.5f; // 增加粒子规模
}

// 设置弹幕沿地面前进
if (Projectile.velocity.Y == 0f) // 检查弹幕是否在地面上
{
if (Projectile.velocity.X > 0f)
{
Projectile.velocity.X = Math.Max(Projectile.velocity.X - 0.1f, 0); // 左移减速
}
else if (Projectile.velocity.X < 0f)
{
Projectile.velocity.X = Math.Min(Projectile.velocity.X + 0.1f, 0); // 右移减速
}

if (Math.Abs(Projectile.velocity.X) < 0.2f)
{
Projectile.velocity.X = 0f; // 停止移动
}
}
else
{
Projectile.velocity.Y += 0.2f; // 重力作用
}

// 添加光效
Lighting.AddLight(Projectile.Center, 0.8f, 0.4f, 0.1f); // 添加橙色光效

// 更新动画帧
Projectile.frameCounter++;
if (Projectile.frameCounter >= 5) // 每5帧切换一次图片
{
Projectile.frame++;
Projectile.frame %= 4; // 循环帧动画
Projectile.frameCounter = 0;
}

// 吸附敌怪
foreach (NPC npc in Main.npc)
{
if (npc.active && !npc.friendly && !npc.dontTakeDamage && Vector2.Distance(Projectile.Center, npc.Center) < 300f)
{
Vector2 direction = Projectile.Center - npc.Center;
direction.Normalize();
npc.velocity += direction * 0.1f; // 吸附效果
}
}
}

public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
{
target.AddBuff(BuffID.OnFire, 300); // 造成燃烧效果,持续5秒
}

public override bool OnTileCollide(Vector2 oldVelocity)
{
Projectile.velocity = Vector2.Zero; // 停止移动,但不消失
return false; // 不摧毁弹幕
}

public override void Kill(int timeLeft)
{
// 可以在弹幕摧毁时添加特效
}
}
}
  • 龙彗星倒是没有什么特别的,感觉这个不是很好还原的其实,弹幕生成的不会很丝滑,运动轨迹,emmm 也很奇怪,可能是会有更平滑的函数实现吧。
  • 每次发射的时候都会沿鼠标方向。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace PolWorldMounts.Content.Projectiles
{
public class DragonMeteorProjectile : ModProjectile
{
private bool hasAccelerated = false; // 是否已经开始加速
private int hoverTime = 60; // 悬停时间(帧)

public override void SetDefaults()
{
Projectile.width = 32; // 弹幕宽度
Projectile.height = 32; // 弹幕高度
Projectile.friendly = true; // 是否对玩家友好
Projectile.hostile = false; // 是否对敌人友好
Projectile.tileCollide = true; // 是否与瓷砖碰撞
Projectile.penetrate = -1; // 穿透数量,设置为-1表示不会消失
Projectile.timeLeft = 300; // 弹幕的存活时间(帧)
Projectile.light = 0.8f; // 弹幕的光亮度
Projectile.ignoreWater = true; // 是否忽略水
Projectile.extraUpdates = 1; // 每帧更新次数
Main.projFrames[Projectile.type] = 4; // 设置弹幕的帧数
}

public override void AI()
{
if (hoverTime > 0)
{
hoverTime--;
Projectile.velocity = Vector2.Zero; // 悬停时速度为零

// 不产生粒子特效
if (hoverTime % 10 == 0) // 每10帧产生一次粒子特效(可以调整频率)
{
int dustIndex = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.Firework_Pink, 0f, 0f, 100, default(Color), 1.0f);
Main.dust[dustIndex].velocity *= 0.5f;
Main.dust[dustIndex].scale *= 1.2f;
Main.dust[dustIndex].noGravity = true;
}
}
else if (!hasAccelerated)
{
Vector2 targetPosition = Main.MouseWorld;
Vector2 direction = targetPosition - Projectile.Center;
direction.Normalize();
Projectile.velocity = direction * 2f; // 设置初始速度
hasAccelerated = true;
}
else
{
Projectile.velocity *= 1.05f; // 缓慢加速
}

// 添加粒子效果或其他视觉效果
if (hoverTime <= 0) // 只有在加速阶段才产生粒子特效
{
int dustIndex = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.Firework_Red, Projectile.velocity.X * 0.5f, Projectile.velocity.Y * 0.5f, 100, default(Color), 1.0f);
Main.dust[dustIndex].velocity *= 0.5f;
Main.dust[dustIndex].scale *= 1.2f;
Main.dust[dustIndex].noGravity = true;
}
}
public override void Kill(int timeLeft)
{
// 创建爆炸效果的范围伤害
float explosionRadius = 150f; // 爆炸范围
int damage = 300; // 爆炸伤害

// 查找爆炸范围内的敌人并造成伤害
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (npc.CanBeChasedBy(this) && Vector2.Distance(npc.Center, Projectile.Center) < explosionRadius)
{
// 计算伤害并应用
npc.SimpleStrikeNPC(damage, 0, false, 0, null, false, 0, false); // hitDirection 设置为 -1 表示无特定方向
}
}

// 播放音效
for (int i = 0; i < 20; i++)
{
int dustIndex = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.Smoke, 0f, 0f, 100, default(Color), 2f);
Main.dust[dustIndex].velocity *= 1.4f;
}
for (int i = 0; i < 10; i++)
{
int dustIndex = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.Firework_Blue, 0f, 0f, 100, default(Color), 3f);
Main.dust[dustIndex].noGravity = true;
Main.dust[dustIndex].velocity *= 5f;
dustIndex = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.Firework_Red, 0f, 0f, 100, default(Color), 2f);
Main.dust[dustIndex].velocity *= 3f;
}
}


public override bool PreDraw(ref Color lightColor)
{
// 获取当前帧的源矩形
Rectangle sourceRectangle = new Rectangle(0, Projectile.frame * Projectile.height, Projectile.width, Projectile.height);

// 绘制弹幕
Main.spriteBatch.Draw(
Terraria.GameContent.TextureAssets.Projectile[Projectile.type].Value,
Projectile.Center - Main.screenPosition,
sourceRectangle,
lightColor,
Projectile.rotation,
new Vector2(Projectile.width / 2, Projectile.height / 2),
Projectile.scale,
SpriteEffects.None,
0f
);

return false; // 返回false,表示我们自己绘制了弹幕
}
}
}

帕鲁工作台

  • 工作台实际上就是一个物块而已
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;

namespace PolWorldMounts.Content.Items.WorkBench
{
public class PolworldBasicWorkBenchItem : ModItem
{

public override void SetDefaults()
{
Item.width = 28;
Item.height = 14;
Item.maxStack = 99;
Item.useTurn = true;
Item.autoReuse = true;
Item.useAnimation = 15;
Item.useTime = 10;
Item.useStyle = ItemUseStyleID.Swing;
Item.consumable = true;
Item.createTile = ModContent.TileType<Tiles.PolworldBasicWorkBench>(); // 设置对应的Tile
Item.rare = ItemRarityID.Blue;
}

public override void AddRecipes()
{
Recipe recipe = CreateRecipe();
recipe.AddIngredient(ItemID.Wood, 30);
recipe.AddIngredient(ItemID.Sapphire, 10);
recipe.AddIngredient(ItemID.Silk, 10);
recipe.AddTile(TileID.WorkBenches);
recipe.Register();
}
}
}
  • 随后在制作物品的时候,指明需要的物块即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using PolWorldMounts.Content.Mounts;
using PolWorldMounts.Content.Tiles;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace PolWorldMounts.Content.Items.Mounts
{
public class FenglopeMountItem : ModItem
{
public override void SetDefaults() {
Item.width = 20;
Item.height = 30;
Item.useTime = 20;
Item.useAnimation = 20;
Item.useStyle = ItemUseStyleID.Swing;
Item.value = Item.sellPrice(gold: 3);
Item.rare = ItemRarityID.Yellow;
Item.UseSound = SoundID.Item79;
Item.noMelee = true;
Item.mountType = ModContent.MountType<FenglopeMount>();
}

public override void AddRecipes() {
Recipe recipe = CreateRecipe();
recipe.AddIngredient(ItemID.HallowedBar, 20);
recipe.AddIngredient(ItemID.SoulofFlight, 5);
recipe.AddIngredient(ItemID.SoulofLight, 10);
recipe.AddIngredient(ItemID.Sapphire, 5);
recipe.AddTile(Mod, "PolworldBasicWorkBench"); // 这里指明需要帕鲁基础工作台就好啦,这样的话必须使用工作台才能制作哦
recipe.Register();
}
}
}