【TMOD】TMLMOD制作:从入门到入土

首先。
如果你有条件的话,可以去官网下载ExampleMod,也比我这个教程强。
引用小东的话:你们来提问我接受,但你们不喜欢做觉得太难或者看我不爽 就不需要来投诉我了,还是私底下抱怨吧
被说是白嫖,所以p了个图。


楼主 清水之火  发布于 2017-06-25 15:23:00 +0800 CST  
1.序言
NOTE:本教程里的任何资料来源分别来自于
Tml制作wiki:https://github.com/blushiemagic/tModLoader/wiki/Developing-with-Visual-Studio
ID表查询:http://terraria.gamepedia.com/Data_IDs
(PS:
Q:镜子mod大神重出江湖?
A:仰慕我就来打求生(泥滚啊


好的,据说,现在的mod普及率已经达到60%了啊。
以前拼死拼活想让mod火起来呢,我还想玩很多很多的mod。现在mod多了,没那个心情了。
因为以前那个搬运贴老旧丑,而且tcf贴写的是搬运帖估计会有人不知道那其实有教程。
而且新版更新这么多了,但是教程还没出来。
那么我做个苦力吧!虽然这么长时间过去,我还是没有一点长进。
做到哪儿写到哪儿,更新时间不定。暑假快来了,会更新就是了。

注意,此教程为基础向教程,不涉及深入的拓展请注意。基本都是参考ExampleMod来做的。如果你懒得看那个mod研究可以看看我的教程。Tml跟源码mod的缺点就是局限性大,写起来还有点麻烦。但是,它很容易就能让你做mod啊。

楼主 清水之火  发布于 2017-06-25 15:24:00 +0800 CST  
2.Mod.cs

做mod,你先把mod的源放到tml能加载的地方,这就是C:\Users\Administrator\Documents\My games\Terraria\ModLoader\Mod Sources
然后创建一个包含你的mod的文件,比如我这个DeadLine。


好的,做mod,我们先从物品开始,因为物品是最基础的,我们先做一把武器,好吧。
打开DeadLine文件夹,我们需要这几样东西:build.txt,DeadLine.cs,description.txt
和一个包含物品的文件夹,


里面Items文件夹只是用来标志里面放的东西都是与item有关的而不是projectile之类的,所以请不要局限于格式,因为这不是强制要求的。
Build里面要这么写:
author = ColdWhite
version = 2.0.3.2
displayName = Dead Line Mod
homepage =http://steamcommunity.com/id/bailengbai/
hideCode = false
hideResources = false
includeSource = true
buildIgnore = *.csproj, *.user, obj\*, bin\*, .vs\*
includePDB = true
里面要注意的是,hideCode = true 的话就无法Extract这个mod了。而hideResources = true的话,Extract出来的,不包含mod图片。Author,version,displayName改一下就可以,后面的建议不要动。
然后description.txt里是介绍,也就是游戏里的此Mod的More Info里面的内容。

关键就是DeadLine.cs,我这样打个比方,这个东西是启动mod的按钮,有了它才能加载此mod。
using Terraria.Graphics.Shaders;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using ReLogic.Graphics;
using Terraria.UI;
using Terraria.DataStructures;
using Terraria.GameContent.UI;

namespace DeadLine //DeadLine是你的mod文件夹的名字
{
public class DeadLine : Mod //这里的DeadLine是你的源代码名字
{
public DeadLine()
{
}
}
}
这是最基本的格式,里面可以什么都不写。
如果不牵扯到与原版有关的内容,只是拓展新物品之类的,完全用不到它。但是如果你想的话。

楼主 清水之火  发布于 2017-06-25 15:25:00 +0800 CST  
下面是一些我在Example 里找出来的一些例子,可以的话可以查wiki。
Properties = new ModProperties()
{
//是否自动加载。
Autoload = true,
AutoloadGores = true,
AutoloadSounds = true,
AutoloadBackgrounds = true
};
//例子都是Example里面找的,我就不做什么名字修改了。
}
public override void AddRecipeGroups()
{
//按照说明,拿木头举例子,我们不能把每一个木头的id都一一列出来,我们就要把相似的(是木头的)都整合到一个Group里,然后就有了AddRecipeGroups
RecipeGroup group = new RecipeGroup(() => Lang.misc[37] + " " + Lang.GetItemNameValue(ItemType("ExampleItem")), new int[] //wiki上说,lang.misc[37]只是为了方便读取任何语言的文字“任何”……
{
ItemType("ExampleItem"),
ItemType("EquipMaterial"),
ItemType("BossItem")
});
RecipeGroup.RegisterGroup("ExampleMod:ExampleItem", group);
//这一切写完后,得到的结果就是,如果在物品的合成里用到AddRecipeGroups,那么ItemType的3个物品都可以用来合成所用到AddRecipeGroups的物品。这听起来有点复杂,等到做item的时候,就会有更多的认识。
}
public override void AddRecipes()
{
//这个添加的是,用mod的物品合成原版里的配方。
ModRecipe recipe = new ModRecipe(this);//调用方法,这一行不用复制。

recipe.AddIngredient(null, "ExampleItem"); //需要一个ExampleItem,
recipe.SetResult(ItemID.Wood, 999); //生成999个ItemID.Wood,也就是999个木头,也可以这样写,recipe.SetResult(1, 999);,1代指物品id。
recipe.AddRecipe();
}
public override void UpdateMusic(ref int music)
{
if (Main.myPlayer != -1 && !Main.gameMenu)
{
加载音乐,例如
if (Main.LocalPlayer.active && Main.LocalPlayer.FindBuffIndex(this.BuffType("CarMount")) != -1)
{
music = this.GetSoundSlot(SoundType.Music, "Sounds/Music/DriveMusic");
}就是说,人物活着,有CarMount这个buff,就播放音乐。
}
以上大概是最基本的内容,拓展后面再说。

楼主 清水之火  发布于 2017-06-25 15:25:00 +0800 CST  
3.Item:
然后,这个时候,你的mod就可以加载了,虽然没啥用,里面没内容,好的,我们在Items里面,添
加一个新物品吧


就叫它test算了。
物品名字要和图片名字对应,大概是这样的。
物品就比较简单一点,两个using,三个方法,就组成了。
using Terraria.ID;
using Terraria.ModLoader;
namespace DeadLine.Items //在DeadLine.Items里面
{
public class test : ModItem //test是此物品的名字
{
public override void SetStaticDefaults()
{
Tooltip.SetDefault("test<3."); //介绍设置。至于两个介绍什么的,我现在还不知道怎么写。
}
public override void SetDefaults() //item属性。
{
/*拓展的有
item.damage = 20;
item.melee = true; //对应的有magic,ranged,noMelee
item.width = 40;
item.height = 40;
item.useTime = 10;
item.useAnimation = 10;
item.axe = 30;//斧子,游戏里是30 x 5 %
item.hammer = 100;//斧子100%
item.pick = 220; //稿子220%
item.useStyle = 1; //1是挥动,5是枪那样的,3是橫持,24懒得测试了。
item.knockBack = 6; //击退。
item.UseSound = SoundID.Item1;
item.autoReuse = true; //能否连击
item.shoot = 255 //发射projectile
item.shootSpeed = 6f; //发射速度
item.consumable = true;//消耗品
item.scale = 1f; //大小
item.noUseGraphic = true; //如果是长矛,需要这一行
item.buffType = 1;//当玩家使用此物品,会出现buff
item.buffTime = 3600; //buff时间,60秒?。
item.CloneDefaults(ItemID.AmethystHook); //如果这是一个钩爪,你可以用这行代码,复制AmethystHook的Defaults,然后你配上自己的pro就可以了。
这应该就比较全了*/
item.width = 20;
item.height = 20;
item.maxStack = 999;
item.value = 100;
item.rare = 9;
}
}
}
然后我们设置了item的属性,我们要它可以制作:
public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
//制作方式1
recipe.AddIngredient(ItemID.DirtBlock); //需要用泥土块制作。
recipe.SetResult(this, 999);
recipe.AddRecipe();
/*
//制作方式2
recipe = new ModRecipe(mod);
recipe.AddRecipeGroup("ExampleMod:ExampleItem"); //包含到组里。用组里那几个物品都可以制作这个东西
recipe.SetResult(this, 999);
recipe.AddRecipe();
*/ //需要用泥土块制作。
recipe = new ModRecipe(mod);
//这里没有recipe.AddIngredient(ItemID.DirtBlock);,也就是说,可以空手合成
recipe.SetResult(this, 999); //制作999个test
recipe.AddRecipe();
}

楼主 清水之火  发布于 2017-06-25 15:26:00 +0800 CST  


图。

这样的话,基本上大部分item都可以做出来。


比如这个魔法书,图片随便找的,也是examplemod里的

楼主 清水之火  发布于 2017-06-25 15:27:00 +0800 CST  
如果我们要此武器出现特效之类的,那么可以用以下方法:
public override bool AltFunctionUse(Player player)
{return true;} //这个是可以右键使用此武器。
使用方法是:public override bool CanUseItem(Player player)
{
if (player.altFunctionUse == 2)
{//这是右键的时候会怎么样,比如射出一个rpg?}
else
{}
return base.CanUseItem(player);
}

public override void OnHitNPC(Player player, NPC target, int damage, float knockBack, bool crit)
{
target.AddBuff(BuffID.OnFire, 60);
}//击中npc会使其着火


public override void MeleeEffects(Player player, Rectangle hitbox)
{
if (Main.rand.Next(3) == 0)
{int dust = Dust.NewDust(new Vector2(hitbox.X, hitbox.Y), hitbox.Width, hitbox.Height, DustID.Fire, player.velocity.X * 0.2f + (float)(player.direction * 3), player.velocity.Y * 0.2f, 100, default(Color), 2.5f);
Main.dust[dust].noGravity = true;
}
}//攻击出现火焰粒子

还有一个有趣的方法,就是重写Shoot,具体自己看,用途未知。
public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
// Fix the speedX and Y to point them horizontally.
speedX = new Vector2(speedX, speedY).Length() * (speedX > 0 ? 1 : -1);
speedY = 0;
//增加随机旋转?
Vector2 speed = new Vector2(speedX, speedY);
speed = speed.RotatedByRandom(MathHelper.ToRadians(30));
// 基于此武器的伤害
damage = (int)(damage * .1f);
speedX = speed.X;
speedY = speed.Y;
return true;
}


楼主 清水之火  发布于 2017-06-25 15:27:00 +0800 CST  
一下子发完,这是这星期的更新,打求生打求生打求生打求生。


楼主 清水之火  发布于 2017-06-25 15:28:00 +0800 CST  
真好啊,水贴都没人的……

楼主 清水之火  发布于 2017-06-25 16:13:00 +0800 CST  
@GOO世界
申。容量其实比想象的大。

楼主 清水之火  发布于 2017-06-25 16:34:00 +0800 CST  
多国语言的介绍的添加方式:
public override void SetStaticDefaults()
{
Tooltip.SetDefault("This is a modded block.");
ItemID.Sets.ExtractinatorMode[item.type] = item.type;
DisplayName.AddTranslation(GameCulture.German, "Beispielblock");

Tooltip.AddTranslation(GameCulture.German, "Dies ist ein modded Block");
DisplayName.AddTranslation(GameCulture.Italian, "Blocco di esempio");
Tooltip.AddTranslation(GameCulture.Italian, "Questo è un blocco moddato");
DisplayName.AddTranslation(GameCulture.French, "Bloc d'exemple");
Tooltip.AddTranslation(GameCulture.French, "C'est un bloc modgé");
DisplayName.AddTranslation(GameCulture.Spanish, "Bloque de ejemplo");
Tooltip.AddTranslation(GameCulture.Spanish, "Este es un bloque modded");
DisplayName.AddTranslation(GameCulture.Russian, "Блок примера");
Tooltip.AddTranslation(GameCulture.Russian, "Это модифицированный блок");
DisplayName.AddTranslation(GameCulture.Chinese, "例子块");
Tooltip.AddTranslation(GameCulture.Chinese, "这是一个修改块");
DisplayName.AddTranslation(GameCulture.Portuguese, "Bloco de exemplo");
Tooltip.AddTranslation(GameCulture.Portuguese, "Este é um bloco modded");
DisplayName.AddTranslation(GameCulture.Polish, "Przykładowy blok");
Tooltip.AddTranslation(GameCulture.Polish, "Jest to modded blok");
}


如果是中文,那就
DisplayName.AddTranslation(GameCulture.Chinese, "例子块");
Tooltip.AddTranslation(GameCulture.Chinese, "这是一个修改块");

楼主 清水之火  发布于 2017-06-26 14:26:00 +0800 CST  
等等,说一下,挺可笑的,据说trmod,国内的,普及度60+%了,mod大神出来了好几个。
嗯。
教程呢?
以前1.1.2,1.2,那些可爱的大手子们,还知道出点教程呢。
现在为什么没有呢?
emmmm。
鬼知道。
更新的话,我得补课到7.15.
会更新的。

楼主 清水之火  发布于 2017-07-02 16:13:00 +0800 CST  
下次可能会更新这个,先丢一个方法
PlaceTile(int i, int j, int type, bool mute = false, bool forced = false, int plr = -1, int style = 0)
嗯,上学了。

楼主 清水之火  发布于 2017-07-02 17:19:00 +0800 CST  
做mod测试的时候,很难受的一点,就是在测试武器的时候,还没开炮呢,就被boss给干掉了。怀着这份怨念,我们怎么做?那当然是……烧烤蠕虫(划掉)——做一套自己的专属套装啦!
假定我们现在有一套现成的贴图。
这个贴图分别有装备的图片x3,行走图x5,行走图多余的俩是手臂和女性装备效果。
如果没有FemaleBody……?(。这些行走图的格式也应该一一对应。
A_Arms
B_Body
B_FemaleBody
H_Head
L_Legs
然后我们开始……给我们可爱的装备写它的特效!
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace DeadLine.Items.Armor
{
[AutoloadEquip(EquipType.Body)]
public class ExampleBreastplate : ModItem
{
public override void SetStaticDefaults()
{
base.SetStaticDefaults();
DisplayName.SetDefault("炮玉战衣");
Tooltip.SetDefault("炮玉的力量由你掌握。"+ "——/nWMDSCJHDPY");
}

public override void SetDefaults()
{
item.width = 18;
item.height = 18;
item.value = 10000;
item.rare = 2;
item.defense = 60; //这里其实应该写到item里=。=
}

public override void UpdateEquip(Player player)
{
player.buffImmune[BuffID.OnFire] = true;
player.statManaMax2 += 20;
player.maxMinions++;
}

public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
recipe.SetResult(this);
recipe.AddRecipe();
}
}
}
其实可以看到,与其他的item相比,装备多了两个东西。
一个是[AutoloadEquip(EquipType.Body)]属性。(同样的有[AutoloadEquip(EquipType.Legs)]和[AutoloadEquip(EquipType.Head)]
还有一个是UpdateEquip方法。这个是重点,套装的效果,饰品的效果,基本都是写在这里面的。
我们能写什么?(其实以前的教程里面有饰品的写法,不过是tapi版的)
加血,加魔,buff效果,套装效果?
statLifeMax,statManaMax,meleeSpeed,lightOrb,lifeRegen?
因为以前的教程很详细,我就不想说那么多……所以列出来一些变量,感兴趣的可以自己去试验一下。lifeSteal,turtleThorns,thorns,noFallDmg,longInvince,lavaImmune ,fireWalk ,honey。
……不知道讲什么了。那就先到这里,脑子有点疼。
做不出来想做的,也不知道怎么做。那就下次更新吧。

楼主 清水之火  发布于 2017-07-19 15:47:00 +0800 CST  
@啦尔比灵,我不知道你哪里不明白。
HoldStyle方法里面其实作者大概是懒,没有做360度全方位旋转,就做了上和下两个方位,然后被左和右分割,说白了就是左上右上左下右下。if ((position.Y >= player.Center.Y) == (player.gravDir == 1))是判定,在玩家中心为半径的近处,是否会拥有这一特效。很难理解?看图~!
我们把else后面那段过滤掉。
HoldItemFrame方法里面写的是绘制=w=,自己看一下就好
HoldItem就是放在手上,会出现什么效果的。
然后套入一个Dust生成~
GetLightPosition的方法就是获得玩家的位置。



楼主 清水之火  发布于 2017-07-21 21:06:00 +0800 CST  
做套装忘了一件事情,你做完了套装,你没想到过,tr里面还有套装效果的吗?!!!!
为什么没人提醒我!!??
好的,是我忘了。
那么套装效果怎么添加?
在你这个套装的任意的一个衣服啊,头盔,鞋子什么的
只用在一个里面添加这么一些代码。
比如我在头盔.cs里面添加。
public override bool IsArmorSet(Item head, Item body, Item legs)
{
return body.type == mod.ItemType("裤子") && legs.type == mod.ItemType("上衣");
}
判定3个穿在一起。
然后动用……UpdateArmorSet方法!
public override void UpdateArmorSet(Player player)
{//然后就可以在里面添加套装效果啦!
player.KillMe(1,0,false,"?");}

楼主 清水之火  发布于 2017-07-28 14:24:00 +0800 CST  

坐骑啊,你要一个坐骑, 你得要一个坐骑的召唤物对吧?
public override void SetDefaults()
{
item.width = 20;
item.height = 30;
item.useTime = 10;
item.useAnimation = 10;
item.useStyle = 1;
item.value = 12450;
item.rare = 8;
item.UseSound = SoundID.Item79;
item.noMelee = true;
item.mountType = mod.MountType("//Your Mount");
}
写出来,在你的坐骑召唤物的item里面,有这么一行
item.mountType = mod.MountType("//Your Mount");
PS:这里提一下,usetime和useanimation过小会导致物品无法使用desu。
然后另开一个cs,Creat你的mount!(留学生风范惹)
比如著名的epmod里面,是这么写的。
namespace ExampleMod.Mounts
{
public class Car : ModMountData //继承自ModMountData.
{
public override void SetDefaults()
{
//代码
//代码
//代码
}
_(:3」∠)_那么……代码里面怎么写嘛!
坐骑是怎么操作的?
……使用之后,有一个buff,然后你就可以使用的,这个样子?
恩,你还要加一个buff!这也是基础!那么我们先,在此之前把buff创建出来。
开一个新文件夹分类。或者丢一起都可以。



然后,我们的buff里面这么些
using Terraria;
using Terraria.ModLoader;

namespace DeadLine{
public class YourBuff : ModBuff
{
public override void SetDefaults()
{
DisplayName.SetDefault("//buffname");
Description.SetDefault("//buff介绍");
Main.buffNoTimeDisplay[Type] = true; //buff没有时间限制
Main.buffNoSave[Type] = true; //buff无法保存
}
public override void Update(Player player, ref int buffIndex)
{
player.mount.SetMount(mod.MountType<Mounts.YourMount>(), player);
player.buffTime[buffIndex] = 10;
}
}
}
Buff写什么?我们之后再讨论也不迟嘛~
Update方法,更新player的……emm……算是effect吧。
给player加上坐骑。

楼主 清水之火  发布于 2017-08-06 02:31:00 +0800 CST  
好了,接下来,buff有显示了。那么我们在我们的mount.cs里面
开始敲mountData里面的变量
mountData.spawnDust = mod.DustType("Smoke"); //这个坐骑召唤的时候会生成烟
mountData.buff = mod.BuffType("//YourMount"); //buff显示~
比如某car 坐骑
mountData.heightBoost = 20; //坐骑的实际高度,如果是2000,你几乎召唤不了。
mountData.fallDamage = 0.5f; //下落伤害
mountData.runSpeed = 11f; //跑步速度
mountData.dashSpeed = 8f; //冲刺速度
mountData.flightTimeMax = 0; //飞行时间
mountData.fatigueMax = 0; //疲劳时间 跟飞行没差多少吧,类似于火箭鞋
mountData.jumpHeight = 5; //跳跃高度
mountData.acceleration = 0.19f; //加速度
mountData.jumpSpeed = 4f; //跳跃速度(在这儿停顿!
mountData.blockExtraJumps = false; //这个是连跳吧2333,就是按住跳可以自动跳?

mountData.totalFrames = 4; //总共的帧数,这个carmount是4帧~
mountData.constantJump = true; //这个是令我迷茫的操作,有知道的请补充。
int[] array = new int[mountData.totalFrames];
for (int l = 0; l < array.Length; l++)
{
array[l] = 20;
}
//这个……是人物离坐骑的距离……怎么说?比如我们把array[l] = 20; 改为200……
mountData.playerYOffsets = array; //人物y轴的偏移量。因为坐骑的姿势不同嘛
mountData.xOffset = 13; //坐骑X轴偏移度
mountData.bodyFrame = 3; //人物的……动作帧数,比如改成5(乌鸦坐飞机!)

mountData.yOffset = -12; //坐骑Y轴偏移度
mountData.playerHeadOffset = 22; //人物头部偏移度 哈?这只是小地图里的人物头部显示啦。没有你想象中的神经病的效果!

//以下是坐骑移动时不同的动画显示。
mountData.standingFrameCount = 4;
mountData.standingFrameDelay = 12;
mountData.standingFrameStart = 0;
mountData.runningFrameCount = 4;
mountData.runningFrameDelay = 12;
mountData.runningFrameStart = 0;
mountData.flyingFrameCount = 0;
mountData.flyingFrameDelay = 0;
mountData.flyingFrameStart = 0;
mountData.inAirFrameCount = 1;
mountData.inAirFrameDelay = 12;
mountData.inAirFrameStart = 0;
mountData.idleFrameCount = 4;
mountData.idleFrameDelay = 12;
mountData.idleFrameStart = 0;
mountData.idleFrameLoop = true;
mountData.swimFrameCount = mountData.inAirFrameCount;
mountData.swimFrameDelay = mountData.inAirFrameDelay;
mountData.swimFrameStart = mountData.inAirFrameStart;
if (Main.netMode != 2)
{
mountData.textureWidth = mountData.backTexture.Width + 20;
mountData.textureHeight = mountData.backTexture.Height;
}
//绘制人物的贴图。哈?为什么这是绘制人物的贴图?我不信!我把Width + 20改为500
Hhhh神经病啊。

楼主 清水之火  发布于 2017-08-06 02:33:00 +0800 CST  

这大概就是很全的内容了,那么如果我们要我们的坐骑帮你自动挖矿砍树清怪通关怎么办?
你需要这个! UpdateEffects方法!顾雀小卖部授权批发产品!(?)
比如还是著名的epmod的例子
if (Math.Abs(player.velocity.X) > 4f)
{
Rectangle rect = player.getRect();
Dust.NewDust(new Vector2(rect.X, rect.Y), rect.Width, rect.Height, mod.DustType("Smoke"));
}
}
移动的时候车子冒烟,怕不是五菱宏光.
别把,这没意思啊。那么,你可以写一个,屏幕内出现怪物自动发射 变形药剂的 坐骑 也是 不可以的。
反正我不写坐骑desu,所以大家加油研究一下!
御剑飞行什么的,这可是男人的浪♂漫啊!
@狗仔服腐竹,玄铁紫水陨光魔剑,请。

楼主 清水之火  发布于 2017-08-06 02:34:00 +0800 CST  
顺便发发牢骚。
最近鼓捣mod陷入了 陷入了 好吧 我什么都不会 的境界。
怎么玩啊 百度不到啊 咋写啊
再加上天天求生之路和csgo锻炼,有空还画会儿画,以至于更新愈加龟化。
请督促我填坑

楼主 清水之火  发布于 2017-08-06 02:36:00 +0800 CST  

楼主:清水之火

字数:21256

发表时间:2017-06-25 23:23:00 +0800 CST

更新时间:2019-08-15 21:55:11 +0800 CST

评论数:468条评论

帖子来源:百度贴吧  访问原帖

 

热门帖子

随机列表

大家在看