8. 非玩家角色 (NPCs)

Non-Player-Characters,或 NPC,是指所有_不_由玩家控制的活动代理。NPC 可以是商人、任务给予者、怪物和首领。它们也可以是“调味”的角色——镇上居民在做家务,农民在耕作——目的是让世界感觉“更有生命”。

在本课中,我们将根据 Knave 规则集创建 EvAdventure NPC 的基础类。根据 Knave 规则,NPC 的某些简化属性与我们之前设计的 PC 角色 相比更为简单。

8.1. NPC 基类

创建新模块 evadventure/npcs.py

# in evadventure/npcs.py 

from evennia import DefaultCharacter, AttributeProperty
from .characters import LivingMixin
from .enums import Ability
from .objects import get_bare_hands

class EvAdventureNPC(LivingMixin, DefaultCharacter): 
    """NPC 的基类""" 

    is_pc = False
    hit_dice = AttributeProperty(default=1, autocreate=False)
    armor = AttributeProperty(default=1, autocreate=False)  # +10 来计算防御
    hp_multiplier = AttributeProperty(default=4, autocreate=False)  # Knave 默认值为 4
    hp = AttributeProperty(default=None, autocreate=False)  # 内部跟踪,使用 .hp 属性
    morale = AttributeProperty(default=9, autocreate=False)
    allegiance = AttributeProperty(default=Ability.ALLEGIANCE_HOSTILE, autocreate=False)

    weapon = AttributeProperty(default=get_bare_hands, autocreate=False)  # 代替装备系统
    coins = AttributeProperty(default=1, autocreate=False)  # 硬币掉落
 
    is_idle = AttributeProperty(default=False, autocreate=False)
    
    @property
    def strength(self):
        return self.hit_dice
        
    @property
    def dexterity(self):
        return self.hit_dice
 
    @property
    def constitution(self):
        return self.hit_dice
 
    @property
    def intelligence(self):
        return self.hit_dice
 
    @property
    def wisdom(self):
        return self.hit_dice
 
    @property
    def charisma(self):
        return self.hit_dice
 
    @property
    def hp_max(self):
        return self.hit_dice * self.hp_multiplier
    
    def at_object_creation(self):
        """
        初始时具有最大生命值。
        """
        self.hp = self.hp_max
        self.tags.add("npcs", category="group")


class EvAdventureMob(EvAdventureNPC): 
    """
    Mob(ile) NPC 用于作为敌人。
    """
  • 第 9 行:通过使用 多重继承,我们使用了在角色课程中创建的 LivingMixin。这包含了许多有用的方法,例如显示“受伤程度”、治愈方法、被攻击时调用的 hooks 等。我们可以在即将到来的 NPC 子类中重用所有这些方法。

  • 第 12 行is_pc 是一种快速方便的方式来检查这个角色是否是玩家角色。我们将在接下来的基础战斗课程中使用它。

  • 第 13 行:NPC 被简化为所有属性都仅基于 Hit dice 数字(见 第 25-51 行)。我们将 armorweapon 作为类的直接 Attributes 存储,而不是实施完整的装备系统。

  • 第 17、18 行moraleallegianceKnave 属性,用于确定 NPC 在战斗情况下逃跑的可能性以及他们是敌对还是友好的。

  • 第 19 行is_idle 属性是一个有用的属性。它应该在所有 NPC 中可用,并将用于完全禁用 AI。

  • 第 59 行:我们确保对 NPC 进行标记。我们可能希望稍后将不同的 NPC 分组,例如让所有带有相同标签的 NPC 在其中一个 NPC 被攻击时做出反应。

我们创建了一个空的子类 EvAdventureMob。Mob(短语为“mobile”)是 MUD 游戏中用于那些能够自己移动的 NPC 的常见术语。我们将在未来的课程中使用该类来表示游戏中的敌人。关于添加 AI 的课程

8.2. 测试

创建新模块 evadventure/tests/test_npcs.py

现在尚无太多可测试的内容,但我们将在同一模块中测试 NPC 的其他方面,因此现在让我们创建它。

# in evadventure/tests/test_npcs.py

from evennia import create_object                                           
from evennia.utils.test_resources import EvenniaTest                        
                                                                            
from .. import npcs                                                         
                                                                            
class TestNPCBase(EvenniaTest):                                             
    """测试 NPC 基类""" 
    
    def test_npc_base(self):
        npc = create_object(
            npcs.EvAdventureNPC,
            key="TestNPC",
            attributes=[("hit_dice", 4)],  # 设置 hit_dice 为 4
        )
        
        self.assertEqual(npc.hp_multiplier, 4)
        self.assertEqual(npc.hp, 16)
        self.assertEqual(npc.strength, 4)
        self.assertEqual(npc.charisma, 4)

这里没有什么特别的注意事项。请注意,create_object 辅助函数将 attributes 作为关键字参数。 这是一个元组列表,我们用来设置 Attribute 的不同值而不是默认值。我们随后检查几个属性,以确保它们返回我们所期望的值。

8.3. 结论

Knave 中,NPC 是玩家角色的简化版本。在其他游戏和规则系统中,它们可能几乎相同。

有了 NPC 类,我们已经有能力创建一个“测试靶子”。由于它尚未具有 AI,它不会反击,但在我们即将到来的课程中,它将足够用于测试战斗。