Mod 开发 -- 星露谷中的自定义条件与脚本
Mod 开发 – 星露谷中的自定义条件与脚本
前言
最近星露谷更新了 1.6, 大量 mod 需要更新, 在贴吧闲逛时遇到了个友友在求蘑菇洞与水果洞共存的 Mod, 觉得甚是滑稽, 因此有了实现的想法.
准备
首先探索实现的可能性, 首先入口是蘑菇洞与水果洞的选择入口是农场事件 Content\Data\Events
, 内容如下:1
2
3{
"65/m 25000/t 600 1200/H": "continue/64 15/farmer 64 16 2 Demetrius 64 18 0/pause 1500/speak Demetrius \"嗨 @!我有好消息要告诉你。前几天,我对本地环境的研究取得了突破。$h#$b#技术细节我就不跟你说了,直接说结果……你知道往西一段距离的那个洞穴吧?我有办法把它变成有用的东西了……对你我都有用。#$b#我想利用那个洞吸引一些本地的物种。这样我就可以在一个更可控的环境下观察它们了。然后不管它们生出来啥都归你了。#$b#我也可以用这个洞吸引食菇蝠或果蝠。这些蝙蝠有时候会给你留下水果。\"/cave/speak Demetrius \"就这么说好了!我现在就给你收拾去!不会花很长时间的!$h#$b#谢谢你给我这次机会。\"/end dialogue Demetrius \"我很期待那个山洞会发生什么事,你也是吧?#$e#希望你对我的工作满意。\""
}
解析为 :
Preconditions:
- 事件ID 65
- 赚了 2,5000
- 六点到十二点
- 主机玩家
Commands:
- 播放当前音乐
- 相机对准 Tile 64,15
- 玩家站位 Tile 64,16 向下看
- Demetrius 站位 Tile 64,16 向上看
- 停顿 1,500ms
- Demetrius 说话:”嗨…略
- 触发 洞穴 选项
- Demetrius 说话:”就…略
- 关闭 选项 dialogue
- Demetrius 说话:”我很…略
实际的代码应该在 洞穴选项
源码分析
前置检查 与 事件脚本
检查/脚本 应该属于属于具体抽象, 为: StardewValley.Delegates.EventPreconditionDelegate
/ StardewValley.Delegates.EventCommandDelegate
, 默认实现分别为 StardewValley.Preconditions
/ StardewValley.Event.DefaultCommands
, 注册在代码 StardewValley.Event.SetupEventCommandsIfNeeded
函数中.
洞穴事件
定位到 StardewValley.Event.DefaultCommands.Cave
事件函数, 可以看到这里打开了一个选择窗口并指定窗口 ID 为 “cave”, Dialogue 没有回调函数, 因此处理应该是由 action 处理(比如鼠标点击事件), 由 Game1.currentLocation.createQuestionDialogue
可以看到, 我们应该是在 GameLocation
中处理, handle 入口递进为 StardewValley.Game1.pressActionButton
> StardewValley.Event.answerDialogue
, 从 “cave” 定位到如下代码:1
2
3
4
5
6
7
8
9
10
11 if(!(questionKey =="cave"))
break;
if(answerChoice ==0)
{
Game1.MasterPlayer.caveChoice.Value =2;
Game1.RequireLocation<FarmCave>("FarmCave").
setUpMushroomHouse();
break;
}
Game1.MasterPlayer.caveChoice.Value =1;
可以看到 根据选项不同, 主机玩家的 洞穴选择会持久化为 水果洞1, 蘑菇洞2 并设置蘑菇屋 (BC)128 与烘干机 (BC)Dehydrator, 在每日更新 StardewValley.Locations.FarmCave.DayUpdate
时, 水果洞1 会随机在地上放置几个水果 并设置 farmCaveReady
值为 true. 对于蘑菇洞, 适用建筑规则, 在 Content\Data\Machines
, key (BC)128
实现
为了更融入游戏场景, 因此不选择 hack 的方式, 改为让小黑过来玩中国名梗我全都要, 因此我们应该让他在前置检查在: 选择事件之后, 大于 2,5000 的金钱是很好的选择但是无法处理前后事件顺序, 因此需要编写一个自定义的前置检查 caveChoice
的值初始化检查是一个很好的选择, 接下来是脚本的执行可以读取, 由于每日更新刷新水果硬编码需要 caveChoice
为 值1, 所以我们能做的是, 根据之间的选择将 caveChoice
更新为 值1 并可能需要设置蘑菇屋.
自定义检查:1
2
3
4
5
6
7
8
9
10
11
12/// <summary> 检查农场洞穴事件是否已经触发.</summary>
/// <param name="location">The location which is checking the event.</param>
/// <param name="eventId">The unique ID for the event being checked.</param>
/// <param name="args">The space-delimited event precondition string, including the precondition name.</param>
public static bool FarmCaveFirstComplete(GameLocation location,
string eventId, string[] args)
{
int caveChoiceValue = Game1.MasterPlayer.caveChoice.Value;
Trace(
$"[FarmCaveFirstComplete] event : {eventId} , caveChoiceValue: {caveChoiceValue}");
return caveChoiceValue is 1 or 2;
}
自定义事件脚本:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/// <summary> 洞穴升级.
/// 见 <see cref="FarmCavePreconditions.FarmCaveFirstComplete"/></summary>
public static void FarmCaveUpgrade(Event , string[] args, EventContext context)
{
var wrapper = Game1.MasterPlayer.caveChoice;
var lastCave = wrapper?.Value;
switch (lastCave)
{
// 水果 + 蘑菇
case 1:
Game1.RequireLocation<FarmCave>("FarmCave").setUpMushroomHouse();
break;
// 蘑菇 + 水果
case 2:
wrapper!.Set(1);
break;
// 错误状态
default:
context.LogErrorAndSkip("必须在洞穴事件后");
break;
}
Debug("FarmCaveUpgrade success!");
++ .CurrentCommand;
}
注意 [email protected] , 脚本执行的块递进(符号 / 区分)由该值确定
使用
最终效果见 Nexus