命令的冷却时间


某些类型的游戏希望限制一个命令的执行频率。例如一个角色施放“火焰风暴”,你肯定不希望他无休止地滥用这个命令。再比如一个高级战斗系统,“超级挥砍”能提供一个造成大量伤害的机会,代价是必须稍等一会儿才能再次使用。这类等待效果就叫做冷却时间。

Evennia允许通过多种方式实现冷却时间(另一个方式是使用异步时间,参见这里)。

非持续性冷却时间

这个小技巧将限制某个特定命令的执行频率。既然命令是类的实例并且保存在内存中,一个命令实例自然记得住你向它存储的东西,因此仅需要存储当前的运行时刻!下一次该命令执行时,它只需检查是否储存过运行时刻,然后与当前时刻比较,以判断是否延迟了期望的时长。
import time 
from evennia import default_cmds

class CmdSpellFirestorm(default_cmds.MuxCommand):
    """
    Spell - Firestorm

    Usage: 
      cast firestorm 

    This will unleash a storm of flame. You can only release one 
    firestorm every five minutes (assuming you have the mana). 
    """
    key = "cast firestorm"
    locks = "cmd:isFireMage()"

    def func(self):
        "Implement the spell"

        # check cooldown (5 minute cooldown)
        if hasattr(self, "lastcast") and \
                      time.time() - self.lastcast < 5 * 60:
            mess = "You cannot cast this spell again yet."
            self.caller.msg(mess)
            return 

        #[the spell effect is implemented]

        # if the spell was successfully cast, store the casting time
        self.lastcast = time.time()
仅仅检查 lastcast 标记,然后如果相关事情都处理完了,就更新它。简单但非常有效,因为一切都储存在内存里。这个简单方案的不利之处是它的非持续性。当你执行 @reload 时,缓存被清理,一切正在运行的冷却时间不再继续保留。另外该方案仅限对使用方案的那个命令有效,其他命令不能(至少并不容易)检查到这个值。

持续性冷却时间

本质与上面那个简单方案是一样的,不同的是我们使用数据库来存储信息,意味着冷却时间将在服务器重载/重启后幸免于难。因为命令本身无法直接连接数据库,你需要借助其他对象来进行存储。

    #[...]
    # check cooldown (5 minute cooldown)

    lastcast = self.caller.db.firestorm_lastcast 

    if lastcast and time.time() - lastcast < 5 * 60:
        mess = "You need to wait before casting this spell again."
        self.caller.msg(mess)
        return      

    #[the spell effect is implemented]

    # if the spell was successfully cast, store the casting time
    self.caller.db.firestorm_lastcast = time.time()
因为我们把它当作属性来存储,我们需要以 firestorm_lastcast 这样的命名方式来区别变量,以确保找得到正确的那个(毕竟我们很可能还有其他技能也需要冷却时间)。但是这种冷却时间方案仍然比对每个命令都设置运行时刻好得多——举例来说,你可以让所有火焰系咒语检查同样的冷却时间以确保释放“火焰风暴”可以阻塞所有火焰系咒语一段时间。或者,在一个拿着剑并使出“超级挥砍”的例子中,命令将阻塞所有其他类型的攻击一段时间直到该战士恢复过来。