返回目录
Chapter 14 第十四章:创建自己的商城
开发者文档 更新于 2024-03-21 19:32:28

第十四章:创建自己的商城

在本章中,我们将学习如何为“骑马与砍杀2”创建一个自定义商城模块。我们将通过C#编程创建一个名为CustomerShop的模块,该模块允许玩家在游戏中访问一个自定义的商城,其中包含特定的物品列表。

创建项目

  1. 创建一个C#类库项目
  1. 项目配置
  1. 模块目录结构

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#代码实现

  1. 创建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());
        }
    }
}
  1. 创建ShopBehavior.cs文件
// 引入所需的命名空间
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);
        }
    }
}
  1. 创建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,我们可以确保引用的物品或部队与游戏数据保持一致。