-
Notifications
You must be signed in to change notification settings - Fork 7
Battle Framework
本文档于 2022-12-16
由 sunist-c
创建,版本0.0.1
要介绍GISB的战斗框架,我们需要先知道GISB的战斗框架的作用。
GISB的战斗框架负责从网络/cli等输入模块获取并解析玩家的战斗操作,并执行相关指令;GISB负责执行战斗阶段的所有动作,包括:
- 骰子的投掷与重新投掷
- 费用的计算与管理
- 伤害(普通攻击/元素战技/元素爆发)的计算与执行
- 装备(圣遗物/武器/天赋)的附加与切换
- 事件(切换角色/攻击结束/使用食物等)的触发与回调
- 元素反应的计算(类型计算/伤害计算/效果计算)与执行
GISB的战斗框架管理着所有的战斗相关实体,包括:
- 玩家信息
- 基本信息(UID/昵称);
- 持有物品(骰子/持有卡牌/牌堆);
- 协同效果(协同技能/协同Buff);
- 在场信息(角色/支援/召唤物);
- 前台Buff(防御Buff/事件效果)
- 角色信息
- 基本信息(ID/元素类型/武器类型);
- 战斗信息(HP/最大HP/MP/最大MP/饱腹/装备/状态/元素附着);
- 技能信息(技能效果/技能花费/技能伤害);
- 角色Buff(攻击Buff/装备Buff)
大部份的战斗过程都是在攻击-防御操作中进行的,但是由于攻击-防御过程的特殊性,我们没办法将这个操作在一个角色内进行完成,所以攻击-防御过程均托管在GISB的战斗框架中。战斗过程中,核心的数据是AttackDamageContext
和DefenceDamageContext
,它们都包括下面的核心结构:
type Damage struct {
element ElementType // 伤害的元素类型
amount uint // 伤害的数值
}
type DamageContext struct {
damages map[*Character]Damage // 伤害表,主键为承受伤害的角色,值为具体伤害
}
在攻击防御过程中,GISB战斗框架的战斗流程如下:
- GISB战斗框架解析玩家操作,并确定玩家释放的技能
- GISB战斗框架根据技能生成基础伤害,类型为
AttackDamageContext
,包括伤害元素类型,伤害值与伤害目标 - GISB战斗框架将调用攻击发起玩家的前台角色的攻击BUFF事件链,生成相应的执行函数,对
AttackDamageContext
进行操作 - GISB战斗框架调用指定的规则实现,根据
AttackDamageContext
的元素类型与目标角色自身的元素附着,计算元素反应类型并对伤害做出相应修正 - GISB战斗框架将最终的
AttackDamageContext
传递给目标,并生成DefenceDamageContext
- GISB战斗框架调用目标玩家身上的防御BUFF事件链,生成相应的执行函数,对
DefenceDamageContext
进行操作 - GISB战斗框架对最终的
DefenceDamageContext
进行执行,扣除相应目标的HP并更新他们的状态 - GISB战斗框架对触发的元素反应效果(对方角色的元素反应效果与自身角色的元素反应效果)进行操作
- 操作执行完毕,阻塞并等待下个玩家指令
在GISB中,我们广泛地使用了中间件的思想与类似结构,让我们通过func (ctx *Context)
这类HandlerFunc对我们相关过程的Context进行操作并能够传递到下层。
由于事件链的存在和Context的引入,我们可以很方便的对战斗过程进行把握,例如我们需要增加一个攻击BUFF,其作用为角色造成的伤害+1,如果为物理伤害则额外+1,我们就可以这么实现(此处代码仅为理解用,非实际结构或接口):
func Handler(ctx *AttackDamageContext) {
ctx.damage.Add(1) // 伤害+1
ctx.Continue() // 先让事件链后面的事件执行,等待执行完毕后判断是否为物理伤害
if ctx.damage.ElementType == ElementNone {
ctx.damage.Add(1) // 如果是物理伤害,则伤害+1
}
}
同时,go的闭包特性也能在框架中使用,例如我们需要增加一个攻击BUFF,其作用为角色造成的伤害+1,若本回合没有进行过攻击,则伤害额外+1,那么我们可以这么实现:
type BuffEvent struct {
activated bool
}
func (b *BuffEvent) Handler(ctx *AttackDamageContext) {
if !b.activated {
ctx.damage.Add(2) // 如果没有进行过攻击,则伤害+2
b.activated = true // 将攻击信息传入事件
} else {
ctx.damage.Add(1) // 如果进行过攻击,则伤害+1
}
}
当然,这个BUFF需要在每回合开始时重置activated
,这个我们将在事件系统中做出额外说明。
防御BUFF也同理,此处我们就不过多赘述了。
这一类操作仅涉及对当前角色的操作,只是具体细节有所不同罢了,他们的本质都是修改当前角色/玩家的相关数据,例如增加HP/MP,增加攻击BUFF等,此类操作GISB战斗框架均直接执行,只是具体的接口不一致而已,此处不做过多展开。
但此处重点介绍BUFF类操作,由于GISB战斗系统将BUFF分类托管于不同的队列中,以便于战斗系统进行统一操作,生成相应的函数链,所以在实现大多数卡时,我们都需要将BUFF正确地挂到相应的队列中去,下面是主要的事件队列的说明,详情请转到./model
目录下查看相应源代码:
- AttackModifiers 攻击修正队列,此队列托管于角色之下,因为每个角色的装备和天赋都不相同
- DefenceModifiers 防御修正队列,此队列托管于玩家之下,因为只有前台角色能够享受护盾/减伤效果
- CostModifiers 元素骰子费用修正队列,此队列托管于角色之下,因为圣遗物/料理效果只对一个角色有效
- CooperativeAttackModifiers 协同攻击修正队列,此队列托管于玩家之下
- SwitchModifiers 切换角色事件队列,此队列托管于玩家之下
- CallbackChain 回调函数队列,此队列托管于角色之下,将在事件相关文档重点介绍
例如,我们想实现一个料理,其作用为当前角色攻击所消耗的无色骰子-1,我们可以这么实现:
type FoodEvent struct {}
func (f FoodEvent) Handler(ctx *CharacterContext) {
ctx.AddCostModifier("${FoodEventName}", func (context *CostContext) {
context.SubCosts(ElementSet{ElementCurrency: 1})
})
}
在对战中,我们还有一类技能比较特殊,它们会在角色攻击结束后追加一次操作,这就是协同技能。在GISB中,我们的协同技能依赖于事件系统进行触发,在触发后,GISB将如同普通的攻击-防御过程一样调用CooperativeSkill,以相同的流程发起攻击。
由于涉及回调函数与事件钩子,此处不针对事件系统给出示例。