开发者日志 #168 - 可汗法典

0 点赞
十字军之王3
转载

大家好!本次扩展包新增了不少系统,今天我们来聊聊其中一些系统的模组支持性和底层技术。 朝贡国 我们新增的一个较具影响力的特色功能是朝贡国。为了统一术语,宗主国与朝贡国的关系,就如同领主与附庸的关系。 此前,附庸的政府类型中会定义一系列独立契约,共同构成其完整的“附庸契约”。这其中包括“封建政府税收”这类添加在“封建政府”中的内容,它决定了封建政府附庸需向领主缴纳的税额。 不过,随着朝贡国的加入,这一情况略有改变。首先,几乎所有与“附庸契约”相关的内容都已更名为“附庸国契约”。为了能在政府系统之外重复使用这些契约列表和功能,我们新增了一个名为“附庸国契约组”的数据库,其中包含常见的各类独立契约列表,还可包含额外参数,例如契约在继承时的传承方式以及在地图上的显示方式。这些内容在附庸国契约组的说明文件中有详细记录。

【一种朝贡国类型的主体契约组示例】 朝贡国的基本运作方式与附庸国相同,拥有相同的契约系统。但与附庸国在成为他人附庸时根据你的政府类型自动应用契约不同,朝贡国需要通过脚本手动启用契约。

【开始新的朝贡契约很简单!】 此效果仅在朝贡国和宗主国均为独立统治者,且由此产生的朝贡关系不会导致朝贡国之间形成循环依赖的情况下生效。这可以通过“can_be_tributary_of”触发器轻松检查。也可以使用“scope:some_tributary_character = { end_tributary = yes }”来解除朝贡关系。 朝贡契约组还标有“is_tributary = yes”,以便我们将其与常规附庸关系区分开来,因为拥有朝贡契约的角色仍被视为“独立统治者”。 要获取角色的宗主,可以使用新的“character.suzerain”链接,或者“character.”“overlord”(宗主)会根据角色身份获取相应对象:若角色是封臣,则获取领主;若角色是附庸国,则获取宗主国。还存在“top_suzerain”(最高宗主)和“top_overlord”(最高领主)。其中,“top_overlord”会先找到你的最高领主,然后继续寻找该最高领主的最高宗主(如果存在)。 AI每年有机会根据“cease_paying_tribute_interaction”(停止纳贡互动)中的“ai_will_do”(AI意愿值)脱离其宗主国,此处显示的正是该数值。

服从性 任何拥有使用【服从性】政府规则的政府类型的角色,或其领主/宗主/雇主拥有该规则的角色,都将适用服从性机制。达成服从状态所需的阈值以及服从性数值均通过脚本计算。

简单来说,如果计算得出的服从值大于阈值,该角色就会处于服从状态。服从目标在代码中按以下优先级设定: 实际领主 宗主 雇主 法理领主 无 在许多地方会通过“is_obedient”和“is_obedient_to”触发器来检查服从状态。 邦联 邦联是由一群较小的独立统治者组成的团体,他们联合起来,在抵御更大威胁时作为盟友行动。通过“create_confederation”效果即可简单创建邦联。

联盟若成员少于两人,将在每日结算时自动失效,因此创建联盟后必须通过【add_confederation_member】添加其他成员。

联邦成员的地图颜色会根据定义“CONFEDERATION_REALM_COLOR_FACTOR”向联邦创建者的颜色倾斜。 情境系统 我们实施了一个新的通用系统来支持基于动态区域的游戏玩法,我们称之为情境系统。其目的是为各种类型的系统提供基本构建模块,从类似“冲突”的社会政治系统,到像“大草原”这样的环境系统,或是像“自然灾害”这样较小的临时效果。(“天命”已提及!) 从这个角度来说,你可以将情境系统视为“冲突”系统的进化版,我们借鉴了该系统的经验,使其更具动态性和灵活性。可在我们的C++代码库中扩展(例如:迁移),同时在我们的图形用户界面系统中也具备灵活性,并且具有更高的可模组化程度。需要注意的是,我们尚未将现有的“斗争”转换为“情境”,因为“斗争”包含特定功能和定制界面,而这些在新的“情境”系统中无法以完全相同的方式实现。

正如您从上述文档文件中所见,我们在《草原可汗》和《天下一家》中均使用了此系统。它是基础游戏的一部分,因此所有模组制作者无论是否拥有DLC都可使用。 那么这个系统是如何构建的呢?我会尝试用简洁的方式进行说明。相关术语可能有些枯燥,但这是因为它是一个核心系统——每个具体应用都可以在界面中对内容进行重命名! 子区域——一种局势类型包含一个或多个地理子区域。这些子区域的数量是固定的,但在游戏运行时,每个子区域的地理范围都可以扩大或缩小。例如:大草原的三个子区域——西部、中部和东部。阶段 - 每种情境类型都有一系列阶段,这些阶段可通过各种机制相互转换。我们借鉴了斗争系统的部分功能并对其进行了扩展。 活跃阶段可通过以下方式转换到另一个阶段: 1. 达到最大持续时间 - 如果设定了阶段的最大持续时间,当达到该时间时,会根据规则从可能的后续阶段中选择一个新阶段。规则包括根据后续阶段积累的点数进行加权选择,或完全随机选择。例如:大草原的季节就是以这种方式更替的。 2. 点数接管 - 某个后续阶段积累了足够的点数,达到了其“接管”目标。与斗争系统类似,你可以使用催化剂为特定的后续阶段增加点数。它会停止当前阶段,并将自身设为活跃阶段。 示例:《伟大草原》的某些特殊季节就是这样运作的。 持续时间接管——某个后续阶段达到特定持续时间后,会停止当前阶段,并将自身设为活跃阶段。这可用于提供一个更可预测的点数接管阶段版本。 通过脚本更改——我们的脚本系统功能强大,可以直接切换阶段,跳过自动系统。它还可以切换到并非活跃阶段官方“后续阶段”的阶段。 每个子区域都有自己的活跃阶段和阶段进程,但可能的阶段是在情境层面定义的。不过,没有什么能阻止我们(或模组制作者)在一个情境中实现多阶段“循环”! 当然,情境阶段定义还包含将应用于区域的修正和参数(领地修正),或应用于参与情境的角色的修正和参数(角色修正),这些修正和参数会按其参与组进行筛选,这自然就引出了…… 参与组——哪些角色会受到情境的影响?我们可以在情境中定义一组参与组。与子区域类似,这些参与组本身在情境类型中预先定义,但在游戏运行时是动态的——角色可以加入或离开组。每个子区域都有其预定义的参与群体,在每个子区域内,一个角色只能属于一个参与群体(或不属于任何群体)。 那么,是什么决定了你会属于哪个群体呢?这由潜在候选人和群体设置决定。默认情况下,候选人是相关子区域地理范围内的所有统治者。但你也可以通过脚本手动添加和移除候选人(例如,你可以将教皇添加到大草原群体,即使他们根本不在附近)。 对于每个子区域,我们会遍历所有候选人,他们会被添加到第一个对其有效的群体中。也有可能不符合任何群体的条件!这种情况下,他们就不会参与该事件。

动态子区域创建 - 当你定义了一种情境类型后,可以通过历史定义或脚本效果将其添加到世界中。此外,你还可以在启动情境时定义一组新的子区域!这使我们能够在多个位置实例化相同的情境类型。

结束机制 - 当某一阶段(任何子区域)结束且未开始新阶段时,情境会自动结束。此外,也可通过脚本中的“end_situation”效果来结束情境。 图形特性 - 情境系统还附带了一些额外的图形特性。简单的包括定义特定子区域或参与方群体的地图颜色。更值得注意的是,我们添加了地图省份效果——一种根据子区域当前情境阶段在地图上应用效果的方式。 这些效果可应用特定的效果类型和强度(0.0 - 1.0),我们在《草原可汗》中内置了一些效果:干旱、夏季、降雪——当然,模组也可以添加更多效果!

【咸海以北的常规地形】

【盛夏区域效果已达最大强度】

【雪域效果已达最大强度——该效果与我们现有的冬季着色器相关联,因此会平滑地淡入/淡出】

【干旱省份达到最高强度】 总体而言,情境系统是一个构建模块,它允许游戏玩法功能使用所需内容并调整所需设置,通常无需新的C++代码。 迁移 游牧迁移是情境系统的扩展——情境类型可设置为支持迁移。这将为玩家和AI在特定情境覆盖的地图区域启用额外机制。迁移过程以及AI关于迁移的决策是一项复杂任务,主要由代码控制。每当存在高整体复杂性和性能风险时,我们都会这样做。以下是可用于调整迁移方式和迁移内容的自定义点。 迁移始终在某些情境中发生。一项游戏规则允许你在世界的不同区域启用额外情境,且这些区域内的迁徙将独立进行。当你自行制作支持迁徙的情境模组时,请注意此类情境不应重叠。 玩家和AI均通过脚本中名为【migration_interaction】的特殊交互来启动迁徙。迁徙旅行或战争的发起由代码处理,但其余部分可进行调整。例如,迁徙后的实际土地转移由脚本决定。交互属性【ai_accept】控制接收方是否允许你和平迁徙至其领地,否则将引发战争。而【ai_will_do】则用于在AI决定迁徙目的地时,为最佳迁徙目标进行评分。与其他交互不同,此交互会通过代码向脚本提供大量额外数据,因此评分可以更加细致。额外的考量范围包括迁移目标、敌对迁移情况下的所有潜在敌人、目标土地的肥力、所有防御者及其盟友的总军事力量——所有这些都能让你对不同目标进行优先级排序,平衡风险与收益。 当迁移为敌对性质时,会使用特殊的宣战理由——【migration_cb】。 迁移范围受迁移距离限制(MAX_MIGRATION_SQUARED_DISTANCE),并且存在可扩大或缩小该范围的修正值(max_migration_distance_mult)。这能让你控制不同的AI行为,使某些统治者更具漫游性,而对另一些统治者施加更严格的限制。居所 这是我们第二个使用居所系统的扩展包,其中引入了一种新的居所类型——游牧营地。随着对该系统实践经验的积累,我们发现之前的一些技术决策容易出错且难以操作。因此,我们决定改变居所与可游玩无地统治者的关联方式。 现在,居所不再归个人所有,而是由特殊的无地头衔持有。冒险者始终拥有其营地头衔,无地贵族家族拥有贵族家族头衔,而游牧民族则拥有游牧营地头衔。 这一改动同时解决了多个问题:居所继承现在完全由特殊头衔的继承规则控制。不再需要通过代码来决定统治者应保留自己的居所还是从他人处继承的居所。头衔在一段时间内可能没有持有者,当某个时候出现新的持有者时,宅邸会重新回到游戏中——不会因为不幸的继承而意外摧毁一个丰富且已建设的宅邸。脚本或用户界面没有任何变化——统治者与其拥有的活跃宅邸之间仍然存在直接联系。当统治者拥有多个带有宅邸的特殊头衔时,只有其中一个宅邸会处于活跃状态——即与统治者政府类型相匹配的那个。不过在基础游戏中,这种情况通常不会发生。 生育力 生育力是在伯爵领层面计算的数值。处于移民状态的伯爵领,或其领主的政府类型包含“使用伯爵领肥力”政府规则的伯爵领,其伯爵领肥力将被计算并显示在用户界面中。

【游牧政体的政府规则】 若你想在模组中添加一个使用生育率但不涉及迁移的情境,只需在情境设置中添加“migration = yes”,但需禁用迁移交互(migration_interaction),并确保在必要时添加“uses_county_fertility”政府规则。 牧民有其特殊性,他们还能补充领地生育率,这由另一个名为“replensishes_county_fertility”的政府规则所规定。他们每个领地每月会贡献一个基础生育率增益,具体数值由定义“BASE_HERDER_GAIN_PER_COUNTY”决定。 每月生育率变化相较于我们通常的系统要复杂一些。我们对增长和衰退进行了调整,以更好地模拟游牧牧群对当地牧场的消耗情况,总体变化通常会稳定在一个较低的数值(不过这取决于你的领地规模和其他一些因素)。 领地肥力增长根据以下函数进行修改:

【县域生育率增长方程与图表】 县域生育率下降根据以下函数变化:

【郡生育率下降方程式与图表】 两条曲线的交点在理论上是游戏中特定郡的平衡点。郡的生育率总会趋向于平衡点。不过,存在一个【推动因素】,它决定了平衡点对增长与下降之间差异的敏感程度。

【区域生育率推动方程】 考虑到这些方程的复杂性,我们选择了一种更简单的方法来计算要在用户界面中显示的平衡点,即使用小角度近似公式sin(x)≈x。

【领地生育率平衡近似方程】 结合角色、地产和领地修正值,这些函数共同作用形成游戏中显示的每月领地生育率变化。 需要注意的是,这背后的计算主要包含在月度变化细分中的【当前领地生育率修正值】里。这个数值是这些计算的结果,它引导领地的生育率趋向其平衡点。

【生育率月度变化分解示例】 对于近期未学习数学课程的玩家,上述内容可能难以理解,因此为模组制作者总结核心要点如下:影响领地生育率增减方式及平衡值的核心途径,是通过调整以下关联上述函数的定义参数。

【影响领地肥力变化的定义】 牲畜群 牲畜群是一种由角色住所储存的新货币。

【蒙古包住所脚本显示牧群参数】 每个住所都有当前牧群值和最大牧群值,可通过以下触发器访问:

【住所现支持畜群数和最大畜群数触发条件】 住所的畜群上限(最大畜群数)由其等级以及任何相关修正值决定。新增了一个定义项,用于按等级列出基础畜群上限值。

【影响牧群容量、牧群转化以及牧群每月黄金收入的定义】 随之而来的是基础牧群转化率,该转化率决定了你在征召军队时能将多少牧群转化为部落骑手。此外,还有一个定义用于控制角色基于其牧群获得的黄金收入量。 每个月,你的牧群会根据你的领地提供的牧群增益以及封臣和附庸的贡献而增长。你从领地获得的牧群数量等于该伯爵领当前的伯爵领肥力乘以定义HERD_GAIN_FROM_COUNTY_MULTIPLIER。

【来自郡 fertility 乘数定义的牧群增益】 突袭意图 通过突袭意图,我们为突袭玩法增加了一些自定义选项。它可以改变你带回战利品时获得的物品,并在某些方面对突袭部队进行调整。

这里我们看到了掠夺意图,当你的掠夺军队带着战利品返回时,它会增加威望和恐惧值,但会给掠夺速度带来-60%的修正。 我们还使用“on_raid_action_completion”触发事件,根据掠夺军队的活跃掠夺意图,在每次成功掠夺据点后产生不同的效果。你可以使用“raid_intent = NAME_OF_INTENT”触发器来检查范围内军队的掠夺意图。 生成角色模板 现在可以为每种政府类型指定一个脚本化角色模板。当代码中为该政府类型自动生成新角色时,例如将头衔授予自动生成的角色时,将使用此模板。

【生成角色模板的脚本语法】

【牧人角色脚本模板】 在草原地区,当你开始迁徙并将土地留给牧人时,我们会经常使用这个模板。因此,每当你开始迁徙,负责管理你原领地的牧人都会根据上述模板生成。 此举旨在让脚本和模组制作者能更好地控制为已指定政府类型的头衔所创建的角色。 【骨骼更新】 此次更新带来了角色骨骼的优化。我们新增了袖子、裙子和斗篷的关节,以提高服装动画的逼真度。这些关节已添加到male_body.mesh和female_body中。网格还意味着,任何模组制作者创建的使用这些骨骼的物品都必须添加这些关节,否则物品看起来会非常怪异(有点像“炸开”),因为关节计数数组现在以不同的顺序排列关节,且关节数量更多。为了帮助模组制作者,我们决定发布我们的骨骼文件,包括我们自己的Maya文件以及更通用的FBX格式文件。 为了加快修复旧衣服、斗篷和腿部装备的速度,模组制作者可以使用这些文件,将文件中各种服装的蒙皮复制到他们自己的物品上。通常需要一些手动重新蒙皮,但希望这能比完全手动操作快得多。一般来说,腿部服装(裤子)会复制常规身体(绑定到腿部)的蒙皮权重,斗篷则有文件中特定的斗篷项目可供复制(同时绑定到斗篷关节以及上臂/颈部/上背部),而普通服装可以从“身体和裙子”项目复制(上半身绑定到身体关节,下半身绑定到裙子关节)。选择看起来适合需要重新蒙皮的项目即可。 我们之前制作的服装外观和表现应该与以前基本相同,而新的动画和服装将能够拥有更多独立于身体的动作。这将使我们能够基于怪异姿势或快速移动来模拟更自然的动作。战斗方面 对于喜爱战斗的模组制作者,我们添加了一些常被请求的触发动作,你可以用它们在战斗开始时或军队加入战斗时施加效果。

此外,我们还添加了一些新的脚本效果,以允许对战斗结果进行更多控制: set_winner = yes - 可应用于战斗方的效果,无需将士兵减至0即可强制决定战斗胜负。(在下一回合生效,更为简洁) force_win = yes - 可应用于战斗方的效果,无需将士兵减至0即可强制决定战斗胜负。(立即生效) set_disallowed_retreat = yes - 禁止战斗方撤退的效果(战败时会被全歼) set_allow_early_retreat = yes - 允许战斗刚开始时撤退的效果(防止被全歼) set_skip_pursuit = yes - 战斗胜利后跳过追击阶段的效果(因此非致命伤亡不会转化为致命伤亡) 我们还添加了新的【战败】地图军事单位动画状态,当单位在战斗后被击败时会触发此状态,可实现一些额外的精彩动画。 目前就这些内容!我们下周将回来介绍【草原可汗】中的部分美术和音乐内容,记得下周二查看。