在这一章中,我们将学习如何为《骑马与砍杀2》创建一个名为“决斗任务”的模块。通过决斗解决与竞争对手的纷争,争取爱人的芳心。
创建C#类库项目
- 启动 Visual Studio。
- 选择“创建新项目”。
- 搜索并选择“类库(.NET Framework)”,确保它与游戏兼容(建议.NET Framework 4.7.2)。
- 命名项目为 CompetitionQuest 并点击“创建”。
项目配置
- 参考之前章节,对项目进行配置,确保可以作为游戏模块运行。
- 引入必要的游戏DLL,如 TaleWorlds.CampaignSystem.dll、TaleWorlds.Core.dll 等。
创建模块目录和配置文件
- 在游戏的 Modules 目录下创建名为 CompetitionQuest 的文件夹。
- 在此文件夹中创建 SubModule.xml 配置文件,填写模块的基本信息。
SubModule.xml 示例配置
<?xml version="1.0" encoding="utf-8"?>
<Module>
<Name value="Competition Quest"/>
<Id value="CompetitionQuest"/>
<Version value="v1.0.0"/>
<SingleplayerModule value="true"/>
<MultiplayerModule value="false"/>
<DependedModules>
<DependedModule Id="Native"/>
<DependedModule Id="SandBoxCore"/>
<DependedModule Id="Sandbox"/>
<DependedModule Id="CustomBattle"/>
<DependedModule Id="StoryMode"/>
</DependedModules>
<SubModules>
<SubModule>
<Name value="CompetitionQuest"/>
<DLLName value="CompetitionQuest.dll"/>
<SubModuleClassType value="CompetitionQuest.MySubModule"/>
</SubModule>
</SubModules>
</Module>C# 代码实现
创建 MySubModule.cs 文件
这个文件定义了模块的主要逻辑和初始化。
using TaleWorlds.CampaignSystem;
using TaleWorlds.Core;
using TaleWorlds.MountAndBlade;
namespace CompetitionQuest
{
public class MySubModule : MBSubModuleBase
{
protected override void OnGameStart(Game game, IGameStarter gameStarterObject)
{
if (starterObject is CampaignGameStarter campaignGameStarter)
{
campaignGameStarter.AddBehavior(new QuestBehavior());
}
}
}
}创建任务类 CompetitionQuest
这个类继承自 StoryModeQuestBase,用于定义与处理整个任务的流程,包括对话、决斗以及任务的结果。
using System;
using TaleWorlds.CampaignSystem;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.GameMenus;
using TaleWorlds.Localization;
using TaleWorlds.SaveSystem;
namespace CompetitionQuest.Quests;
public class CompetitionQuest : StoryModeQuestBase
{
[SaveableField(1)]
private JournalLog _startLog;
[SaveableField(2)]
private Hero _competitor;
[SaveableField(3)]
private Hero _lover;
public CompetitionQuest(string questId, Hero lover, Hero competitor, CampaignTime duration)
: base(questId, lover, duration)
{
_competitor = competitor;
_lover = lover;
var startLog = GetStartLog(lover, competitor);
_startLog = AddDiscreteLog(startLog, TalkLog, 0, 1);
AddDialogs();
}
private TextObject GetStartLog(Hero lover, Hero competitor)
{
TextObject startLog = new TextObject("{PLAYER}与{RIVAL}因争夺{LOVED_ONE}的爱情而决定决斗。");
startLog.SetTextVariable("PLAYER", Hero.MainHero.CharacterObject);
startLog.SetTextVariable("LOVED_ONE", lover.CharacterObject);
startLog.SetTextVariable("RIVAL", competitor.CharacterObject);
return startLog;
}
private TextObject TalkLog => new TextObject("与{HERO_NAME}为了{LOVED_ONE}的爱情进行决斗。")
.SetTextVariable("HERO_NAME", _competitor.Name)
.SetTextVariable("LOVED_ONE", _lover.Name);
protected override void RegisterEvents()
{
// 在此注册任务相关的事件
}
protected override void HourlyTick()
{
// 处理任务每小时的更新
}
private TextObject TimeOutLog => new TextObject("与{HERO_NAME}的决斗因时间耗尽而未能发生。")
.SetTextVariable("HERO_NAME", _competitor.Name);
protected override void OnTimedOut()
{
CompleteQuestWithTimeOut(TimeOutLog);
_competitor.ForceNpcMarry(_lover, false);
}
public override TextObject Title => new TextObject("爱情决斗");
protected override void InitializeQuestOnGameLoad()
{
SetDialogs();
}
protected override void SetDialogs()
{
AddDialogs();
}
private void AddDialogs()
{
Campaign.Current.ConversationManager.AddDialogFlow(CreateDuelQuestDialog(), this);
}
private DialogFlow CreateDuelQuestDialog()
{
return DialogFlow.CreateDialogFlow("hero_main_options", 150)
.BeginPlayerOptions()
.PlayerOption(new TextObject("我为{LOVED_ONE}的心挑战你!").SetTextVariable("LOVED_ONE", _lover.Name))
.Condition(() => Hero.OneToOneConversationHero == _competitor)
.NpcResponse(new TextObject("很好,让我们开始决斗!"))
.Consequence(StartDuelWithCompetitor)
.CloseDialog();
}
private void StartDuelWithCompetitor()
{
// 在此实现启动决斗的逻辑
}
private void CompleteDuel()
{
// 在此处理决斗完成后的逻辑
}
}
- 任务初始化:在构造函数中设置了任务的基本信息,如任务ID、参与者、持续时间,并初始化对话。
- 获取起始日志:GetStartLog 方法定义了任务开始时的日志内容,使用中文描述任务的背景。
- 注册事件和定时操作:通过 RegisterEvents 和 HourlyTick 方法可以添加任务的事件监听和时间更新逻辑。
- 决斗对话流程:CreateDuelQuestDialog 方法中定义了玩家与竞争对手之间的对话流程,包括挑战和接受决斗的交互。
- 决斗逻辑:StartDuelWithCompetitor 方法需要实现具体的决斗逻辑,如何开始和结束决斗,以及决斗的结果处理。
QuestBehavior 类
此类将处理恋人和玩家之间的对话,以及启动决斗任务的逻辑。
using TaleWorlds.CampaignSystem;
using TaleWorlds.Localization;
namespace CompetitionQuest.Behaviors;
public class QuestBehavior : CampaignBehaviorBase
{
public override void RegisterEvents()
{
CampaignEvents.OnSessionLaunchedEvent.AddNonSerializedListener(this, OnSessionLaunched);
}
private void OnSessionLaunched(CampaignGameStarter starter)
{
AddQuestDialogs(starter);
}
private void AddQuestDialogs(CampaignGameStarter starter)
{
// 添加对话选项,恋人让玩家决斗
starter.AddPlayerLine("talk_to_lover", "hero_main_options", "lover_discuss_competition",
new TextObject("我们该如何处理那个混蛋的问题?").ToString(), null, null);
starter.AddDialogLine("lover_suggest_duel", "lover_discuss_competition", "player_accept_duel",
new TextObject("我觉得你需要和他决斗,证明你的爱。”").ToString(),
null, null);
starter.AddPlayerLine("player_agree_duel", "player_accept_duel", "close_window",
new TextObject("你说得对,我会证明我的爱!").ToString(), null, AgreeToDuel);
}
private void AgreeToDuel()
{
Hero player = Hero.MainHero;
Hero lover = Hero.OneToOneConversationHero;
Hero rival = lover.GetHighestRankedEnemy(); // 假设情敌是恋人认为的最大威胁,需要实现这个方法
if (lover != null && rival != null)
{
CompetitionQuest competitionQuest = new CompetitionQuest("CompetitionDuelQuest", lover, rival, CampaignTime.Days(30));
competitionQuest.StartQuest();
}
}
public override void SyncData(IDataStore dataStore)
{
}
}