在本章中,我们将学习如何为“骑马与砍杀2”创建一个自定义商城模块。我们将通过C#编程创建一个名为CustomerShop的模块,该模块允许玩家在游戏中访问一个自定义的商城,其中包含特定的物品列表。
创建项目
- 创建一个C#类库项目:
- 打开Visual Studio。
- 选择“创建新项目”。
- 在项目类型中搜索并选择“类库(.NET Framework)”。
- 项目名称设为CustomerShop。
- 选择合适的.NET Framework版本(建议与游戏兼容的版本,例如.NET Framework 4.7.2)。
- 点击“创建”。
- 项目配置:
- 参考第三章教程,对CustomerShop项目进行配置,以便它能够作为游戏的一个模块运行。
- 确保引用了游戏的DLL文件,如TaleWorlds.CampaignSystem.dll、TaleWorlds.Core.dll和TaleWorlds.MountAndBlade.dll。
- 模块目录结构:
- 在游戏的Modules目录下,创建一个新的文件夹,命名为CustomerShop。
- 在CustomerShop目录中,创建一个名为SubModule.xml的新文件,并填写基本的模块信息。
SubModule.xml配置
<?xml version="1.0" encoding="utf-8"?>
<Module>
<Name value="Customer Shop"/>
<Id value="CustomerShop"/>
<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="CustomerShop"/>
<DLLName value="CustomerShop.dll"/>
<SubModuleClassType value="CustomerShop.MySubModule"/>
</SubModule>
</SubModules>
</Module>C#代码实现
- 创建MySubModule.cs文件:
- 这个文件将定义模块的主要逻辑,包括初始化游戏启动器时添加自定义行为。
// 引用CustomerShop.Behaviors命名空间来访问ShopBehavior类
// 引用TaleWorlds.CampaignSystem和TaleWorlds.Core命名空间以访问游戏的核心功能和API
using CustomerShop.Behaviors;
using TaleWorlds.CampaignSystem;
using TaleWorlds.Core;
// 定义CustomerShop命名空间
namespace CustomerShop
{
// MySubModule类继承自MBSubModuleBase,这是创建模块时需要继承的基类
public class MySubModule : MBSubModuleBase
{
// 重写InitializeGameStarter方法,该方法在游戏启动时被调用
// 它允许我们向游戏添加自定义的行为或逻辑
protected override void InitializeGameStarter(Game game, IGameStarter starterObject)
{
// 检查传入的starterObject是否是CampaignGameStarter类型
// CampaignGameStarter用于单人战役游戏模式,包含了管理战役相关功能的方法
if (!(starterObject is CampaignGameStarter campaignGameStarter))
return; // 如果不是,直接返回,不执行任何操作
// 调用AddBehaviors方法,向游戏添加自定义行为
AddBehaviors(campaignGameStarter);
}
// 定义AddBehaviors私有方法,负责向CampaignGameStarter添加自定义行为
private void AddBehaviors(CampaignGameStarter campaignGameStarter)
{
// 使用CampaignGameStarter的AddBehavior方法添加ShopBehavior实例
// ShopBehavior是我们定义的一个CampaignBehaviorBase派生类,用于实现自定义商城的逻辑
campaignGameStarter.AddBehavior(new ShopBehavior());
}
}
}
- 创建ShopBehavior.cs文件:
- 该文件定义了一个CampaignBehaviorBase派生类,负责向游戏中添加自定义的商城菜单和逻辑。
// 引入所需的命名空间
using System.Collections.Generic;
using CustomerShop.Helpers; // 引入自定义的帮助类命名空间
using TaleWorlds.CampaignSystem; // 引入用于访问战役系统相关功能的命名空间
using TaleWorlds.CampaignSystem.GameMenus; // 引入用于操作游戏菜单的命名空间
using TaleWorlds.Core; // 引入核心功能命名空间
using TaleWorlds.Localization; // 引入本地化命名空间,用于支持多语言
// 定义CustomerShop.Behaviors命名空间
namespace CustomerShop.Behaviors
{
// ShopBehavior类继承自CampaignBehaviorBase,用于定义一个新的战役行为
public class ShopBehavior : CampaignBehaviorBase
{
// 使用XmlHelper获取物品ID列表,该列表将用于自定义商城中展示的物品
private static readonly List<string> items = XmLHelper.ItemIdList;
// 注册事件的方法,此方法在行为被添加到战役系统时调用
public override void RegisterEvents()
{
// 在新游戏创建时和游戏加载时添加非序列化监听器,以触发OnSessionLoad方法
CampaignEvents.OnNewGameCreatedEvent.AddNonSerializedListener(this, OnSessionLoad);
CampaignEvents.OnGameLoadedEvent.AddNonSerializedListener(this, OnSessionLoad);
}
// 用于同步数据,但在此示例中不需要进行数据同步
public override void SyncData(IDataStore dataStore)
{
}
// 在游戏会话加载时调用的方法,用于添加游戏菜单
private void OnSessionLoad(CampaignGameStarter campaignGameStart) => AddGameMenus(campaignGameStart);
// 向游戏中添加自定义商城的菜单选项
private void AddGameMenus(CampaignGameStarter campaignGameStarter)
{
// 在城镇菜单中添加一个新的菜单选项,用于进入自定义商城
campaignGameStarter.AddGameMenuOption(
"town", // 目标菜单
"customerShop", // 菜单选项的唯一标识
"自定义商城", // 菜单选项显示的文本
game_menu_trade_on_condition, // 显示条件
game_menu_town_town_market_on_consequence); // 选中后的行动
}
// 定义菜单选项的显示条件
private static bool game_menu_trade_on_condition(MenuCallbackArgs args)
{
bool disableOption;
TextObject disabledText;
// 检查玩家是否可以进行交易
bool canPlayerDo = Campaign.Current.Models.SettlementAccessModel.CanMainHeroDoSettlementAction(
Settlement.CurrentSettlement, SettlementAccessModel.SettlementAction.Trade, out disableOption,
out disabledText);
args.optionLeaveType = GameMenuOption.LeaveType.Trade;
// 根据条件设置菜单选项的属性
return MenuHelper.SetOptionProperties(args, canPlayerDo, disableOption, disabledText);
}
// 定义选择菜单选项后的行动
private static void game_menu_town_town_market_on_consequence(MenuCallbackArgs args)
{
ItemRoster itemRoster = new ItemRoster();
// 根据items列表筛选出需要在商城中展示的物品
var itemList = Items.All.FindAll(item => items.Contains(item.StringId));
// 将筛选出的物品添加到物品名册中
foreach (ItemObject itemObj in itemList)
{
itemRoster.Add(new ItemRosterElement(itemObj, 1));
}
// 创建一个新的SettlementComponent实例,用于代表商城
SettlementComponent settlementComponent = new Town();
// 重置商城的金币(此行为可能需要根据实际情况调整)
settlementComponent.ChangeGold(-settlementComponent.Gold);
// 打开交易界面,允许玩家与商城进行交易
InventoryManager.OpenScreenAsTrade(itemRoster, settlementComponent);
}
}
}
- 创建XmLHelper.cs文件:
从XML文件中提取物品和部队的ID列表。这个帮助类主要用于读取游戏中定义的物品(如武器、装备)和部队的ID,以便在自定义模块中使用。
// 引入所需的命名空间
using System.Xml; // 用于处理XML文件
using TaleWorlds.Library; // 包含游戏内显示信息的功能
using TaleWorlds.ModuleManager; // 提供获取模块文件路径的方法
// 定义CustomerShop.Helpers命名空间
namespace CustomerShop.Helpers
{
// 定义一个静态的帮助类XmLHelper
public static class XmLHelper
{
// 定义一个私有静态字段,用于存储物品ID列表
private static List<string> _itemIdList;
// 定义一个私有静态字段,用于存储部队ID列表(此示例中未使用,但为了扩展性保留)
private static List<string> _troopIdList;
// 公开的静态属性,用于获取物品ID列表
public static List<string> ItemIdList
{
get
{
// 如果_itemIdList未初始化或为空,则调用ExtractItemIds方法进行加载
if (_itemIdList == null || _itemIdList.Count <= 0)
{
// ModuleHelper.GetXmlPath方法用于获取指定模块的XML文件路径
// 此处需要替换"mod编号"和"物品xml路径,如items.xml"为实际使用的模块ID和XML文件名,这个用于读取自己做的装备xml文件
_itemIdList = ExtractItemIds(ModuleHelper.GetXmlPath("mod编号", "物品xml路径,如items.xml"));
}
// 返回加载好的物品ID列表
return _itemIdList;
}
}
// 私有静态方法,用于从指定的XML文件路径中提取物品ID
private static List<string> ExtractItemIds(string filePath)
{
// 创建一个空的列表用于存储提取的ID
List<string> itemIds = new List<string>();
try
{
// 实例化XmlDocument,用于加载和解析XML文件
XmlDocument doc = new XmlDocument();
doc.Load(filePath); // 加载XML文件
// 调用GetIdsFromElements方法提取"Item"和"CraftedItem"元素的ID,并添加到列表中
itemIds.AddRange(GetIdsFromElements(doc, "Item"));
itemIds.AddRange(GetIdsFromElements(doc, "CraftedItem"));
}
catch (Exception ex) // 捕获并处理可能发生的异常
{
// 如果读取XML文件时发生错误,显示错误信息
InformationManager.DisplayMessage(new InformationMessage("Error reading XML file: " + ex.Message));
}
// 返回提取的ID列表
return itemIds;
}
// 私有静态方法,用于从XML文档中提取指定元素名称的ID
private static IEnumerable<string> GetIdsFromElements(XmlDocument doc, string elementName)
{
// 创建一个空列表用于存储ID
List<string> idsFromElements = new List<string>();
// 遍历文档中所有指定名称的元素
foreach (XmlNode selectNode in doc.DocumentElement.SelectNodes("//" + elementName))
{
// 获取元素的"id"属性
XmlAttribute attribute = selectNode.Attributes["id"];
if (attribute != null) // 如果该属性存在
idsFromElements.Add(attribute.Value); // 添加到ID列表中
}
// 返回提取的ID列表
return idsFromElements;
}
}
}这个帮助类XmLHelper提供了一种从游戏模块的XML文件中提取物品和部队ID的方法,便于在自定义模块中根据这些ID进行操作。例如,在自定义商城中只显示特定的物品,通过提取ID,我们可以确保引用的物品或部队与游戏数据保持一致。