|
| 1 | +--- |
| 2 | +sidebar_position: 3 |
| 3 | +--- |
| 4 | + |
| 5 | +# 创造第一个武器 |
| 6 | + |
| 7 | +本教程将指导你创建一个简单的 Mod ,实现修改游戏内某个基础功能的功能。 |
| 8 | + |
| 9 | +:::info |
| 10 | +本教程的 Mod 代码储存在 [Github](https://github.com/dead-cells-core-modding/docs-zh/blob/main/modproject/FirstWeapon) 上。 |
| 11 | + |
| 12 | +本章节大部分代码来自 **Frostbite**。感谢 Frostbite 对本教程的帮助。 |
| 13 | +::: |
| 14 | + |
| 15 | +:::warning |
| 16 | +本教程假设你拥有以下技能: |
| 17 | + |
| 18 | +- C# 编程基础 |
| 19 | +- 死亡细胞基础 Mod 制作 ([教程](https://www.bilibili.com/opus/681293864647000128)) |
| 20 | + |
| 21 | +::: |
| 22 | + |
| 23 | +## 创建 Mod 项目 |
| 24 | + |
| 25 | +按照[教程](./first-mod.md)创造名为 **FirstWeapon** 的 Mod 项目。 |
| 26 | + |
| 27 | +## 制作 res.pak |
| 28 | + |
| 29 | +按照[教程](https://www.bilibili.com/opus/681993184170999904)制作一个**diff pak**。**CDB**中应包括: |
| 30 | + |
| 31 | +- 一个名为 **OtherDashSword** 新武器以及它对应的 **item** |
| 32 | + |
| 33 | +:::tip |
| 34 | +**OtherDashSword** 可以换为其他名字。 |
| 35 | +::: |
| 36 | + |
| 37 | +一个现成的 [res.pak](https://github.com/dead-cells-core-modding/docs-zh/blob/main/modproject/FirstWeapon/res.pak)。 |
| 38 | + |
| 39 | +### 复制 res.pak |
| 40 | + |
| 41 | +- 将上一步获得的**res.pak**复制到项目根目录下。 |
| 42 | +- 修改**FirstWeapon.csproj**,添加以下内容 |
| 43 | + |
| 44 | +```xml |
| 45 | +<ItemGroup> |
| 46 | + <!--将res.pak文件复制到bin\Debug\net9.0目录--> |
| 47 | + <None Update="res.pak"> |
| 48 | + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
| 49 | + </None> |
| 50 | + |
| 51 | + <!--将res.pak文件复制到最终的输出目录--> |
| 52 | + <OutputFiles Include="res.pak" /> |
| 53 | +</ItemGroup> |
| 54 | +``` |
| 55 | + |
| 56 | +## 编写代码 |
| 57 | + |
| 58 | +### 加载 res.pak |
| 59 | + |
| 60 | +- 修改`FirstWeaponMod.FirstWeapon`类,使其实现`IOnGameEndInit`接口。 |
| 61 | + |
| 62 | +```csharp |
| 63 | +//加载res.pak并刷新CDB |
| 64 | +void IOnGameEndInit.OnGameEndInit() |
| 65 | +{ |
| 66 | + var res = Info.ModRoot!.GetFilePath("res.pak"); //获取 Mod 根目录 下的 res.pak 文件的绝对路径 |
| 67 | + FsPak.Instance.FileSystem.loadPak(res.AsHaxeString()); //加载 res.pak |
| 68 | + var json = CDBManager.Class.instance.getAlteredCDB(); //获取合并后的 CDB Json |
| 69 | + Data.Class.loadJson( //加载合并后的 CDB Json |
| 70 | + json, |
| 71 | + default); |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +:::info |
| 76 | +成功加载 **res.pak** 后,你可以在日志中看到类似的内容: |
| 77 | + |
| 78 | +```text |
| 79 | +[14:30:13 INF][FsPak] Loading pak from C:\SteamLibrary\steamapps\common\Dead Cells\coremod\mods\FirstWeapon\res.pak |
| 80 | +``` |
| 81 | + |
| 82 | +::: |
| 83 | + |
| 84 | +:::info |
| 85 | +`IOnGameExit`接口和`IOnGameEndInit`接口都是**事件系统**的一部分。它们会在对应的**事件**触发时被调用。 |
| 86 | + |
| 87 | +- `IOnGameExit` 将会在游戏退出前调用。 |
| 88 | +- `OnGameEndInit` 将会在游戏初始化后被调用。 |
| 89 | +- `IOnHeroUpdate` 将会在游戏内每帧调用一次。 |
| 90 | + |
| 91 | +::: |
| 92 | + |
| 93 | +### 创造武器类 |
| 94 | + |
| 95 | +创造一个 `FirstWeaponMod.OtherDashSwordWeapon` 类,使其继承于`DashSword`类并实现`IHxbitSerializable<object>`接口。 |
| 96 | + |
| 97 | +```csharp |
| 98 | +//武器类 |
| 99 | +internal class OtherDashSwordWeapon : |
| 100 | + DashSword, //基类 |
| 101 | + IHxbitSerializable<object> |
| 102 | +{ |
| 103 | + |
| 104 | + //默认构造方法 |
| 105 | + public OtherDashSwordWeapon(Hero o, InventItem i) : base(o, i) |
| 106 | + { |
| 107 | + } |
| 108 | + |
| 109 | + //留空 |
| 110 | + object IHxbitSerializable<object>.GetData() |
| 111 | + { |
| 112 | + return new(); //TODO |
| 113 | + } |
| 114 | + |
| 115 | + //留空 |
| 116 | + void IHxbitSerializable<object>.SetData(object data) |
| 117 | + { |
| 118 | + //TODO |
| 119 | + } |
| 120 | + |
| 121 | + // 测试效果——每帧增加10细胞 |
| 122 | + public override void fixedUpdate() |
| 123 | + { |
| 124 | + base.fixedUpdate(); |
| 125 | + bool noStats = false; |
| 126 | + this.owner.addCells(10, new HaxeProxy.Runtime.Ref<bool>(ref noStats)); |
| 127 | + } |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +:::info |
| 132 | +`IHxbitSerializable<>`接口用于保存游戏对象的数据。 |
| 133 | + |
| 134 | +为了简单起见,武器类并没有直接继承于 `Weapon`类(所有武器的基类),而是继承于`DashSword`类。 |
| 135 | +::: |
| 136 | + |
| 137 | +### 注册新武器 |
| 138 | + |
| 139 | +仅仅将新武器的信息添加到 **CDB** 中是仍然是不够的。 |
| 140 | + |
| 141 | +为了让游戏可以识别新武器,还需要修改`FirstWeaponMod.FirstWeapon`类,添加以下内容: |
| 142 | + |
| 143 | +```csharp |
| 144 | + public override void Initialize() |
| 145 | + { |
| 146 | + Logger.Information("你好,世界"); |
| 147 | + |
| 148 | + Hook__Weapon.create += Hook__Weapon_create; //挂钩 $Weapon.create |
| 149 | + } |
| 150 | + |
| 151 | + private Weapon Hook__Weapon_create(Hook__Weapon.orig_create orig, dc.en.Hero o, InventItem i) |
| 152 | + { |
| 153 | + var id = i._itemData.id.ToString(); //获取武器id |
| 154 | + if(id == "OtherDashSword") |
| 155 | + { |
| 156 | + return new OtherDashSwordWeapon(o, i); //返回自定义的武器 |
| 157 | + } |
| 158 | + else |
| 159 | + { |
| 160 | + return orig(o, i); //调用原始方法 |
| 161 | + } |
| 162 | + } |
| 163 | +``` |
| 164 | + |
| 165 | +### 获取新武器 |
| 166 | + |
| 167 | +很明显,现在无法拿到新武器,因为目前它没有任何获取途径。 |
| 168 | + |
| 169 | +可以通过以下的代码创造新武器对应的物品: |
| 170 | + |
| 171 | +```csharp |
| 172 | +//生成物品 |
| 173 | +private void SpawnWeapon(Hero hero) |
| 174 | +{ |
| 175 | + InventItem testItem = new InventItem(new InventItemKind.Weapon("OtherDashSword".AsHaxeString())); |
| 176 | + bool test_boolean = false; |
| 177 | + ItemDrop itemDrop = new ItemDrop(hero._level, hero.cx, hero.cy, testItem, true, new HaxeProxy.Runtime.Ref<bool>(ref test_boolean)); |
| 178 | + // 生成掉落物后必须调用init方法,否则游戏会崩溃 |
| 179 | + itemDrop.init(); |
| 180 | + itemDrop.onDropAsLoot(); |
| 181 | + itemDrop.dx = hero.dx; // 不知道为什么要有这一步,但是原版代码这么写的 |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +为了简单起见,使用以下代码使得在**游戏中**可以通过按下**反斜杠**来获取**新武器** |
| 186 | + |
| 187 | +```csharp |
| 188 | +//实现IOnHeroUpdate接口 |
| 189 | +void IOnHeroUpdate.OnHeroUpdate(double dt) |
| 190 | +{ |
| 191 | + if(Key.Class.isPressed(0xDC /**反斜杠键码**/)) |
| 192 | + { |
| 193 | + SpawnWeapon(Game.Instance.HeroInstance!); |
| 194 | + } |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +## 效果演示 |
| 199 | + |
| 200 | +[视频](https://github.com/dead-cells-core-modding/docs-zh/blob/main/docs/dev/videos/Dead%20Cells%20with%20Core%20Modding%202025-07-21%2015-30-36.mp4) |
0 commit comments