跳至正文

基础物品 — 武器和工具

其实这就是在之前的基础物品上多加亿些属性和亿些重写函数而已~

亿点新属性

之前,我们设置过了物品的宽高、价值和稀有度,但这个物品并不能使用。

要让物品可以使用,我们需要在 SetDefaults() 中写这些东西:

// 使用速度和使用动画持续时间!
// 这个数值越低越快,因为TR游戏速度每秒是60帧,这里的20就是
// 20.0 / 60.0 = 0.333 秒挥动一次!也就是一秒三次
Item.useTime = 20;
Item.useAnimation = 20;
// 一般来说我们要把这两个值设成一样,但也有例外的时候
// 比如下边的工具,让useTime = 5,而useAnimation还是20,这这么做就会让这个物品点击一次使用20 / 5 = 4次!但是只有一次使用动画()

// 使用类型,这个值决定了武器使用时到底是按什么样的动画播放
// 0 或 None 代表......字面意思,就是啥都没有!你写了之后甚至无法使用!
// 1 或 Swing 代表挥动,也就是剑类武器!
// 2 或 EatFood 代表像食物一样,拥有物品,手持,放在盘子上三帧的贴图,具体可见exmod里头的🥧(派)https://github.com/Cyrillya/Example-Mod-Zh-Project/blob/master/Content/Items/Consumables/ExampleFoodItem.cs
// 3 或 Thrust 代表像1.3的同志短剑一样刺x 出去(也就是朝左或右刺出)(如果你想要写全方位刺出的剑,那你还是得看exmod)
// 4 或 HoldUp 唔,这个一般不是用在武器上的,想象一下生命水晶使用的时候的动作
// 5 或 Shoot 手持,枪、弓、法杖类武器的动作,用途最广
// 6 或 DrinkLong 代表直接猛喝,感兴趣可以自己看看,挺好玩的(
// 7 或 DrinkOld 代表1.3的喝药水动作
// 8 或 GolfPlay 代表高尔夫球杆的动作
// 9 或 DrinkLiquid 代表1.4的喝药水动作,相比1.3的来说,这个动作的手臂更加流畅,持握位置会在瓶口
// 10 或 HiddenAnimation 代表使用时无动画
// 11 或 MowTheLawn 代表割草机的动作,神奇,这玩意还有单独的动作
// 12 或 Guitar 代表常春藤、雨之歌等物品的动作,对这玩意也是单独的动作(爱抚剑ing
// 13 或 Rapier 代表标尺、星光等武器的动作
// 14 或 RaiseLamp 代表夜光的动作,好吧这也单独写一个动作的吗?话说这玩意翻译过来叫吊灯......夜光大吊灯(bushi
// Item.useStyle = 1; 请不要写魔法值
Item.useStyle = ItemUseStyleID.Swing; //需要注意一点,泰拉中的挥动型物品的贴图基本都是朝着右上角倾斜的,如果你的贴图不是如此,那可能会导致奇怪的视觉问题。

// 决定了这个武器鼠标按住不放能不能自动使用, true代表可以, false代表不行
// (鼠标别按废了,这条属性你要是不写那就是默认的false
Item.autoReuse = true;

// 决定了你在使用物品的时候可否转身,不写就是false
Item.useTurn = true;

// 这是使用时的音效,不写就是没有
// 这是一个挥动音效
Item.useSound = SoundID.Item1;
使用类型,来自原版wiki

wiki上的SoundID我是加载不出来,,给你们另一个链接SoundID(部分)

写了这三属性物品就能使用了!但是没有任何效果,诶就是玩儿~

武器

现在咱们要让这个物品可以打怪辣!

我是一个一个会打怪的剑啊啊啊啊啊啊

还是在 SetDefaults() 中:

// 使用时的体积倍率,不写就是默认1倍,即不变大也不变小
Item.scale = 1f;

// 伤害!想都不要想,后面这个值随便改吧,但是不要超过2147483647
// 不然…… 你试试就知道了
Item.damage = 50;

// 决定了这个武器的伤害类型
// Default 代表无属性(也就是不吃任何加成)
// Generic 代表全属性(也就是全部加成都吃)所谓1.3的allDamage就是它了
// MagicSummonHybrid 代表什么我不知道,但是可以同时吃到魔法和召唤加成
// MeleeNoSpeed 代表近战,但是不吃攻速加成
// Melee 代表近战
// Ranged 代表远程
// Magic 代表马猴烧酒,不,魔法
// Summon 代表召唤
// SummonMeleeSpeed 代表额......看看鞭子吧,可以吃到近战和召唤加成
// Throwing 代表投掷(没错虽然1.4没了投掷武器,全给远程了,但是这个伤害类型还在!)
Item.DamageType = DamageClass.Melee; // 这里用的是一把剑所以是Melee,近战伤害

// 击退力,写0~20f之间的数值,越大击退力越强
Item.knockBack = 6f;

// 暴击率,角色自带4%
Item.crit = 20; // 20%暴击率,游戏内显示会是20 + 4 = 24%暴击率

// 这是控制贴图是否造成伤害,默认是造成伤害
Item.noMelee = false; // 这是一把剑所以贴图是造成伤害的
// 但如果你是枪类的远程武器,你不希望拿着枪敲人的话就要把它设置为true

// 这是控制使用时是否显示贴图,默认是有的
Item.noUseGraphic = false;
// 吸血飞刀这个就是true,它使用时不显示贴图

// 假如这是一个法杖类型,不写默认false,这里就用到物品Type了
Item.staff[Type] = false; // enn,这是把剑
// 一般来说,法杖类武器会使用Shoot的那个使用方式,但它的贴图不像枪一样是水平朝向而是向右上倾斜
// 让它变成true就会导致使用时贴图再转45度,变成法杖尖端朝着射击方向

// 这是使用消耗的魔力值,不写就不消耗
Item.mana = 0;

你也可以使用 ModContent.GetInstance<你的伤害类>() 来使用你自定义的伤害类型,这里是一篇如何制作自定义伤害类的示例

自定义伤害类

有了物品的使用时间,使用动画,使用方式,现在又加上了伤害类型和伤害,那么这个物品就可以创人了!现在,它是一个挥动起来的效果,并且,按住可以连续使用。

一个挥动效果

我们还可以给这个使用效果加一些特效。

给武器加特效(近战限定ver)

让我们找到这个叫 MeleeEffect 的重写函数:

这个函数会在武器使用时被触发。可以看到它给了两个参数,一个是player,这是正拿着这个物品的玩家实例,另一个是hitbox,这是这个物品使用的时候的碰撞箱。那么让我们给这把武器加一些使用时的粒子吧。

在碰撞箱区域(挥动区域)生成粒子

来看看效果:

哇哦非常夸张

看起来有点多而且乱七八糟,不过我们可以改改粒子的属性和生成间隔。

Main.GameUpdateCount 是游戏更新计数器,从你进入世界开始每帧+1,即使你用旅途模式暂停了游戏时间 Main.time 也不会受影响,是一个天然计时器。有关计时器的概念之后会提到,是运用很广泛的玩意儿~

现在它就变成了每三帧才生成一次(之前是每帧都生成),并且没有初速度(之前会有一个随机的初速度),还不受到重力影响。再来看看:

似乎又太少了

你们可以自己试试调整参数,来达到想要的效果。

不过以上只是近战,如果你希望它射出一些东西?

让武器发射弹幕

那就再加些属性!一样是在 SetDefaults() 中:

Item.shoot = ProjectileID.TerraBeam; // 物品使用时发射的弹幕类型,这里是泰拉刃的剑气
Item.shootSpeed = 6f; // 物品发射弹幕的速度,单位:像素/帧,一秒 = 60帧

现在,这个物品会在使用的时候朝着人物-鼠标的方向以每帧6个像素的速度射出泰拉刃的剑气。使用 ModContent.ProjectileType<你的弹幕类>() 来获取你的弹幕id,或是用ProjectileID.xxx来使用原版弹幕。

Item.shoot = ModContent.ProjectileType<你的弹幕类>(); // 此时一般会报错提示找不到这个类,使用Alt+Enter修复它!

这里要介绍一个新的重写函数——

Shoot

重写函数Shoot

先来看看提供的参数。player,是正使用这个物品的玩家实例;source,这是生成源信息,下边会说;position,弹幕的发射位置,这里提供的position是玩家的中心 player.Centervelocity,发射速度,此处是从玩家中心朝着使用时鼠标位置的大小为 Item.shootSpeed 的向量;type,发射的 弹幕/射弹 的id;damage,即是之前的物品伤害 Item.damage ;最后一个就是之前写过的击退力了。

这玩意的作用可大了。可以看到它的返回类型是 bool。当你 return true 的时候,使用这个物品时,它就会使用你之前设定过的 Item.shootItem.shootSpeed 发射一个弹幕。但是当你 return false 时,这个自带的发射便会被阻止。

这样你就可以发射很多个弹幕:

public override bool Shoot(Player player, EntitySource_ItemUse_WithAmmo source, Vector2 position, Vector2 velocity, int type, int damage, float knockback)
{
    // 保持发射原有的弹幕,并且朝着反方向额外发射一个霜冻箭矢!
    Projectile p = Projectile.NewProjectileDirect(source, position, -velocity, ProjectileID.FrostburnArrow, damage, knockback, player.whoAmI);
    return true;
}
这把剑已经变得奇奇怪怪了

看起来很cool,你们可以利用这个函数来实现很多奇奇怪怪的效果。

顺便介绍一下这个发射弹幕的方法:

发射弹幕

OMG,它的介绍太多了

第一个参数 spawnSource,就是之前提过的生成源了,它可以用来做很多花式操作,在之后的弹幕篇咱们再细嗦。

有关生成源的使用,请点击查看。接下来的 position,velocity 分别是射弹的生成位置初速度,type 就是射弹的类型啦,damage 伤害,knockBack 击退。

然后这个owner很重要,它代表弹幕的主人,一定要进行一个正确的写。上文中,我们使用了 player.WhoAmI 来获取弹幕主人在 Main.player[] (玩家数组)中的索引并填入。这个参数没有正确传入的话,这个弹幕可能没法打怪(即使打到了也不会执行伤害判定)或者刚发射出来就被系统干掉。不过由于144版本的默认参数它会自动获取当前端玩家,所以不是特别情况下可以不填。但我还是推荐你们手动填上。

再后边的 ai012 是弹幕的 ai数组,也放在弹幕篇再讲。

消耗弹药的武器

假如你想让你的武器在使用时需要消耗弹药,那么:

// 让这个武器在使用时需要消耗被标记为箭矢的物品
// 同时这个物品在快捷栏中时会自动显示背包内有多少弹药
Item.useAmmo = AmmoID.Arrow;

注意,设置了这个属性,你写过的 Item.shoot 就会被弹药代表的弹幕给覆盖掉。
但是请不要只写 Item.useAmmo 不写 Item.shoot,那会射不出来的。

要是你还想让这个武器跟巨兽鲨那样有一定概率不消耗弹药,那就用这个叫 CanConsumeAmmo 的重写函数:

public override bool CanConsumeAmmo(Item ammo, Player player)
{
    return !Main.rand.NextBool(33, 100);
}

当这个函数的返回值是 true 的时候,弹药就会消耗。

Main.rand 是TR自带的一个随机类。它有很多的随机数生成方法,返回类型也不同。这里使用的 Main.rand.NextBool(33, 100) 的效果是他有33/100的概率返回 true,那么效果就是有33%的概率不消耗弹药了。(注意代码中有一个 !,表示对 bool 取反)

这个方法提供了将被使用的弹药的实例,和正在使用这个武器的玩家的实例,我想你可以用这些来做点奇奇怪怪的判定。

你还可以用这个重写函数来让弹药被消耗掉的时候发生一些事情:

public override void OnConsumeAmmo(Item ammo, Player player)
{
    // 键入你的想法
}

下面的函数可以让你的武器可以使用更多种的弹药:

public override bool? CanChooseAmmo(Item ammo, Player player)
{
    if (ammo.type == ItemID.SandBlock)
    { 
        return true; // 让沙子也可以被这把武器作为弹药
        // 注意!此时射出的是这个ammo的shoot,如果这个ammo没有shoot就会射出此武器的shoot
        // 比如这里,沙子是没有shoot的,你射出的就是泰拉剑气而不是之前AmmoID.Arrow的箭矢了
    }
    return null; // 默认返回null使用原本指定的弹药
    // return false你就射不出来了
}

下面这个是上边的被动版本:

public override bool? CanBeChosenAsAmmo(Item weapon, Player player)
{
    // 这次是给了武器实例
    // 使用if (weapon.type == xxx) return true;来让这个物品能被特定武器作为弹药使用
    return base.CanBeChosenAsAmmo(weapon, player);
}

什么,你还想写什么效果?

武器案例参考

这是来自tml的ExampleMod的示例武器链接。

工具

再来说说工具,工具也就是在一把剑的基础上再加几个属性,让它们可以破坏物块,或是砍树锤墙。

还是那个熟悉的函数 SetDefaults

public override void SetDefaults()
{
    //宽高啊,伤害啊,伤害类型什么的就自己写吧

    // 加快使用
    Item.useAnimation = 20;
    Item.useTime = 5;

    // 新属性
    Item.pick = 100; // 100%的镐力!
    Item.axe = 20; // 这个比较特殊,20 * 5 = 100%的斧力!
    //Item.hammer = 100; // 100%的锤力!
}

注意这个是用时间和使用动画时间,因为TR的机制是使用动画结束才算一次完整的使用,也就是说 Item.useAnimation 决定了你是不是在使用这个物品,而 Item.useTime 决定了多久触发一次效果,比如这里就是20/5=4次,你使用一次就能对着物块造成四次伤害

唔,这里还有一个属性: Item.reuseDelay ,不写的话就是默认为0,它决定了你完成一次触发后还要多久才能再次触发。

有关更多工具,比如鱼竿,捕虫网之类的请点此查看

警告:镐力和锤力冲突,请不要同时写(除非你把他们拆开放在左右键)(或者你想一边破坏物块一边把它们敲成半砖顺便还拆了个背景墙)。同时工具不可以拥有弹幕,即Item.shoot需要保持默认值0。

更多有关使用的重写函数介绍

修改发射

噢,看看这个玩意儿:

蝶狱弓

它把木箭变成烈焰蝙蝠了,这是怎么做的呢?

// 这个重写函数在你使用了武器,这个武器要射出弹幕了!诶但是还没射出来,现在你可以修改它的发射参数
public override void ModifyShootStats(Player player, ref Vector2 position, ref Vector2 velocity, ref int type, ref int damage, ref float knockback)
{
    // 可以看到这里面的参数有些带上了“ref”,这表明你在这里修改过的值会被传回去,影响到之后发射弹幕的效果。
    // 当当前射弹类型是木箭的时候,把它改成别的
    if(type == ProjectileID.WoodenArrowFriendly)
    {
        type = ProjectileID.Hellwing; // 这就是地狱之翼弓的弹幕
    }
    // 当然你也可以直接让射出的弹幕转成另一种弹幕,而不是只有木箭
    //type = ProjectileID.Hellwing; // 都会转化成地狱之翼弓弹幕
    //type = Main.rand.Next(Main.maxProjectileTypes); // 也可以让它射出随机的弹幕!小心别把自己创死
}

命中效果

注意,这里的效果只作用于物品本身,并不会作用到由该物品射出的射弹

player是造成本次攻击的玩家实例,target是被命中的NPC实例。

// 这个效果发生在你已经命中了目标,并且已经结算了伤害的那一刻
public override void OnHitNPC(Player player, NPC target, NPC.HitInfo hit, int damageDone)
{
    // 给怪物加上灵液buff,持续10秒,后面的参数别管
    target.AddBuff(BuffID.Ichor, 600);
    // 至于如何制作自己的Buff,之后会讲

    // 可以在命中的时候发射一个弹幕,或者给自己回血等等
    // 如果造成的伤害大于10或者本次伤害是暴击
    if (damageDone > 10 || hit.Crit)
    {
        // player.statLife += 10; // 回复10点生命,但是这个不会跳字
        // player.HealEffect(10); // 玩家头上跳出绿色的10,就像使用生命水晶或是生命果那样
        player.Heal(10); // 这种写法会同时回血+跳字,如无特殊需求建议使用这个
    }
}

握持偏移

有些武器使用时是这样的,看起来没啥问题,其实手握的地方是枪托。而这显然不符合我们注重细节的要求,那么如何修改武器的持握位置呢?

你把握不住
public override Vector2? HoldoutOffset()
{
    return base.HoldoutOffset();
}

这个函数允许你设置useStyleItemUseStyleID.Shoot不是法杖的物品的使用贴图偏移量,如果函数返回null就是使用原版的偏移量,上图的武器我用了这个偏移量:

public override Vector2? HoldoutOffset()
{
    // 横坐标往左移动10像素,纵坐标向上移动5像素
    return new Vector2(-10, -5);
}

于是效果是不是好多了?

你把握住了

除此之外,这两个函数也能修改物品的握持位置:

public override Vector2? HoldoutOrigin()
{
    return base.HoldoutOrigin();
}

public override void HoldStyle(Player player)
{
    base.HoldStyle(player);
}

值得一提的是 HoldoutOrigin 只对 useStyleItemUseStyleID.Shoot是法杖的物品有用,能修改物品贴图旋转中心的偏移量。HoldStyle 是物品在使用中(握在手上)会发生的事情,可以用来修改物品贴图的位置和旋转。

把握了某种。。东西

这个函数也十分有意思,能修改玩家在用这个武器(被选中)的时候玩家的动作。返回true如果你修改了玩家的动画。

public override bool HoldItemFrame(Player player)
{
    return base.HoldItemFrame(player);
}

我们可以这么写:

public override bool HoldItemFrame(Player player)
{
    // 选中武器的时候设置为第3帧
    player.bodyFrame.Y = player.bodyFrame.Height * 2;
    return true;
}

这样只要切到这个武器玩家就会举起手来(笑)

调整魔力消耗

这个重写函数就比较神秘,呃啊。

public override void ModifyManaCost(Player player, ref float reduce, ref float mult)
{
    // 嗯,这也是ref,但我不明白它为什么要给两个调整参数,总之最后的效果是mana * reduce * mult = 本次实际花费的mana
    // 想要让魔力消耗减少20%,就
    reduce -= 0.2f;
    // 增加30%,就
    mult *= 1.3f;
    // 加算和乘算的区别在哪呢?
}

可否使用

public override bool CanUseItem(Player player)
{
    // return true; 可以使用
    // return false; 不能使用
    // 假如是夜晚就不能用
    if(!Main.dayTime)
    {
        return false;
    }
    // 玩家在海洋就不能用
    if (player.ZoneBeach)
    {
        return false;
    }
    return true;
}

这个函数的运用很广泛。

练习

基础

  1. 如何实现玩家每次使用武器时,武器大小会随机变化?答案
    Item.scale = Main.rand.NextFloat(1.00f, 114.00f);// 哈哈这个随机大小的范围可能有一点大
    // 具体放哪自己看着办吧~
    
  2. 如何实现使用武器后,会朝以发射角度为基准,发射五个平均分的弹幕?答案
    // 让我们使用for和三角函数来达成这个效果吧!(tr使用的是弧度制,且一圈是-1~1Pi)
    for (float r = -MathHelper.Pi; r < MathHelper.Pi; r += MathHelper.TwoPi / 5f)// 循环五次,让r分别等于相当于角度的0、72、144、216、288五个度数
    {
    	float r2 = r + velocity.ToRotation(); // 加上发射向量所代表的角度
    	Vector2 v = new Vector2((float)Math.Cos(r2), (float)Math.Sin(r2)) * 10f;// 使用三角函数将其转换成向量
    	Projectile.NewProjectile(source, position, v, type, damage, knockback, player.whoAmI);// 发射!
    }
    // 写在shoot重写函数里即可
    

进阶

  1. 如何实现玩家在使用物品时,物品会每帧变换各种属性?(如大小、镐力/斧力/锤力、伤害等)

劝退

1.如何实现一个可以当作武器(可同时造成近战/远程/魔法/召唤伤害,并附带50%敌怪最大生命值无视护甲的伤害)/工具(斧头/镐子/锤子/钓鱼竿)/弹药/摸彩袋/药水/饰品/装甲/时装/物块。该物品的稀有度是一个名为“无意识”的稀有度(稀有度颜色会从浅绿到深绿不断变化)。将光标放到该物品上时,该物品描述会不断变化,随机复制游戏内任意一个有多行描述的物品当作本物品的描述。伤害根据游戏内总物品数量决定,并乘以(白色稀有度物品数量-蓝色稀有度物品数量)+红色稀有度物品数量 * 黄色稀有度物品数量。使用时间则是游戏内总npc数量。发射的弹幕会随机挑选玩家身上三个能发射弹幕物品的弹幕,发射方式则是随机挑选原版一个会发射弹幕敌怪的发射方式(包括boss),放置的物块只会是游戏内正常游玩无法放置的物块,右键打开后会开出来游戏内全部摸彩袋,喝下后会随机给予2~7个正面buff和4~12个负面buff,持续时间无限且通过此种方式给予的buff永远无法削除(除非删除玩家存档),当作弹药时会每分钟变换为一种随机的游戏内弹药,饰品效果是原版全部饰品效果之和,盔甲效果是让玩家当场变成幽灵并让天上下这个物品的雨,时装会随机挑选一个原版时装当作自己的时装(重载mod时会切换一个新的时装)

标签:

发表回复