大概在SEP Core里实现了自己的思路,并且避免了一些前述讨论中发现的问题
装备附带(也包括以后扩展附带)的状态不再走add和remove逻辑,而是从装备中直接统计
#-------------------------------------------------------------------------- # ● 获取额外状态(非附身但有效) #-------------------------------------------------------------------------- def extra_states set = armors.map {|a| a.auto_state_id} - [0] i = 0 while i < set.size set |= $data_states[set[i]].plus_state_set i += 1 end set.reject {|i| $data_states[i].zero_hp} end
#--------------------------------------------------------------------------
# ● 获取额外状态(非附身但有效)
#--------------------------------------------------------------------------
def extra_states
set = armors.map {|a| a.auto_state_id} - [0]
i = 0
while i < set.size
set |= $data_states[set[i]].plus_state_set
i += 1
end
set.reject {|i| $data_states[i].zero_hp}
end
自动状态就是你设置的状态以及该状态所有+的状态,可以连锁+,反正都会并进来
注意XP在这里有一个编辑器约束,即(作为HP0的状态)是不可能被认定为防具自动状态的,在各种狂野模式备注,各种连锁附加的情况下,这里的最后一步还原了这个约束,保证游戏逻辑的正确。
如果要增加技能附带、职业附带状态,就可以更改这里的定义。
(在目前正在开发的基于SEP core的战斗类插件中,所有VA/MV的traits全部是定义在角色/敌人(固有)和状态(修正)上面的,这也是为了在设定的时候更符合习惯,但是技能和职业这种身外之物要附带traits,就只能通过类似自动状态的逻辑来附加,没法像VA那样直接设置)
获取身上的有效状态使用reader方法,该方法会覆盖原本的attr_reader默认方法
#-------------------------------------------------------------------------- # ● 获取状态 #-------------------------------------------------------------------------- def states return @states_cache if @states_cache extras = extra_states denies = extras.inject([]) {|a, i| a |= $data_states[i].minus_state_set} @states_cache = (@states - denies | extras).sort_by do |i| s = $data_states[i] [-s.rating, -s.restriction, i] end end
#--------------------------------------------------------------------------
# ● 获取状态
#--------------------------------------------------------------------------
def states
return @states_cache if @states_cache
extras = extra_states
denies = extras.inject([]) {|a, i| a |= $data_states[i].minus_state_set}
@states_cache = (@states - denies | extras).sort_by do |i|
s = $data_states[i]
[-s.rating, -s.restriction, i]
end
end
由于状态调用委实过于频繁,采用了缓存的技术思路,防止反复取值时无意义地遍历装备、重新对状态排序等。当然代价就是缓存需要手工清除。
换装备时若触发自动状态更新,只是简单的清掉状态缓存,可以看到脱钩了人物本身状态后,update_auto_state现在变得非常简单(默认系统的update_auto_state调用在实际换装以前,这样改的话没有问题),换装后任何试图读取states的方法(比如取能力值)都会重建缓存,fix_hp里面会取到maxhp,即会重建缓存,如此bug1和2被自动修复。
#-------------------------------------------------------------------------- # ● 更新自动状态 # old_equip : 卸下防具 # new_equip : 装备防具 #-------------------------------------------------------------------------- def update_auto_state(old_equip, new_equip) @states_cache = nil unless old_equip == new_equip end #-------------------------------------------------------------------------- # ● 变更装备 # equip_type : 装备类型 # id : 武器 or 防具 ID (0 为解除装备) #-------------------------------------------------------------------------- def_after :equip do |equip_type, id| fix_hp fix_sp end
#--------------------------------------------------------------------------
# ● 更新自动状态
# old_equip : 卸下防具
# new_equip : 装备防具
#--------------------------------------------------------------------------
def update_auto_state(old_equip, new_equip)
@states_cache = nil unless old_equip == new_equip
end
#--------------------------------------------------------------------------
# ● 变更装备
# equip_type : 装备类型
# id : 武器 or 防具 ID (0 为解除装备)
#--------------------------------------------------------------------------
def_after :equip do |equip_type, id|
fix_hp
fix_sp
end
状态的两个检定用方法:这里将本来取@states的地方取成了states,并且所有取能力值的地方也同样都这么改了,共11处(状态在XP里默认能加成11项能力),就不列了,因为现在@states仅仅只是身上通过事件、战斗等方式附加上的实体状态,并不等同于身上的有效状态。特别要说明的是state_full?这个方法,这是覆写同一状态时用到的检定,由于自动状态里可能包括连锁-的状态,原来的逻辑中当换装时,连锁-的状态就会被清除掉。现在换装不走add_state,连锁-的状态就不会被清除,但按照游戏原本的正常逻辑,它却不是身上的有效状态。即,附加的实体状态在state?里检定不出来,因此state_full?检定中除了原先的state?调用外还加上了@states.include?调用(这个就是原始的state?逻辑),为什么不直接只写@states.include?是因为自动状态也是true,至于为什么保留回合==-1的逻辑下面谈。
#-------------------------------------------------------------------------- # ● 检查状态 # state_id : 状态 ID #-------------------------------------------------------------------------- def state?(state_id) states.include?(state_id) end #-------------------------------------------------------------------------- # ● 判断状态是否已满 # state_id : 状态 ID #-------------------------------------------------------------------------- def state_full?(state_id) return false unless state?(state_id) or @states.include?(state_id) return true if extra_states.include?(state_id) return true if @states_turn[state_id] == -1 @states_turn[state_id] == $data_states[state_id].hold_turn end
#--------------------------------------------------------------------------
# ● 检查状态
# state_id : 状态 ID
#--------------------------------------------------------------------------
def state?(state_id)
states.include?(state_id)
end
#--------------------------------------------------------------------------
# ● 判断状态是否已满
# state_id : 状态 ID
#--------------------------------------------------------------------------
def state_full?(state_id)
return false unless state?(state_id) or @states.include?(state_id)
return true if extra_states.include?(state_id)
return true if @states_turn[state_id] == -1
@states_turn[state_id] == $data_states[state_id].hold_turn
end
重头戏来了:状态的附加过程,基本上是原先的逻辑,简化了一些代码,例如不再进行movable?的判定而是直接写restriction == 4(movable?会判定身上所有状态,没必要,清行动肯定是因为附加的当前状态将restriction提到了4啊),这里的接口force没有改,force时回合设为-1的逻辑也没有改,但实际上系统内部update_auto_state改了之后,已经不可能再用force=true来调这个方法了。但用户可能会用事件脚本来调这个方法,接口必须留下。而且这是一个对于游戏非常有用的feature:剧情来源的自动状态(比如难度选简单后atk, pdef, mdef都*110%,困难则都*90%)。因为剧情来源的自动状态没法绑定到角色身上的东西,只能让它真实附加到角色身上。而为了防止它被意外解除掉(完全回复或事件解除),做成自动状态是最简单的办法。连锁附加和连锁解除都将force给递归传了进去,确保逻辑不出问题。这种状态虽然不能被彻底解除,但通过装备自动状态对其打-,可以在装了某装备的时候暂时屏蔽掉,这也是应有之意,如用特定装备抵消剧情带来的队伍debuff(原系统连锁解除时不传递force进去,便无法做到屏蔽,而且它也没法屏蔽啊,一屏蔽就真解除了),参考前面取states时候的逻辑。
#-------------------------------------------------------------------------- # ● 附加状态 # state_id : 状态 ID # force : 强制附加标志 #-------------------------------------------------------------------------- def add_state(state_id, force = false) # 无效状态的情况下,过程结束 return unless state = $data_states[state_id] # 不是强制附加,且状态被当前状态抵抗的情况下,过程结束 return if not force and states.any? {|i| $data_states[i].minus_state_set.include?(state_id) and not $data_states[state_id].minus_state_set.include?(i)} # 并未附加本状态(任何来源)的情况下 unless state?(state_id) or @states.include?(state_id) # 清除状态缓存 @states_cache = nil # 附加状态 @states.push(state_id) # HP 0的检查更改 @hp = 0 if state.zero_hp # 限制不行动的检查更改 @current_action.clear if state.restriction == 4 # 状态变化 (+) (-) 处理 state.plus_state_set.each {|i| add_state(i, force)} state.minus_state_set.each {|i| remove_state(i, force)} # 修正当前 HP 及 SP fix_hp fix_sp end # 按强制附加与否设置解除回合数 if @states.include?(state_id) @states_turn[state_id] = force ? -1 : state.hold_turn end end
#--------------------------------------------------------------------------
# ● 附加状态
# state_id : 状态 ID
# force : 强制附加标志
#--------------------------------------------------------------------------
def add_state(state_id, force = false)
# 无效状态的情况下,过程结束
return unless state = $data_states[state_id]
# 不是强制附加,且状态被当前状态抵抗的情况下,过程结束
return if not force and states.any? {|i|
$data_states[i].minus_state_set.include?(state_id) and
not $data_states[state_id].minus_state_set.include?(i)}
# 并未附加本状态(任何来源)的情况下
unless state?(state_id) or @states.include?(state_id)
# 清除状态缓存
@states_cache = nil
# 附加状态
@states.push(state_id)
# HP 0的检查更改
@hp = 0 if state.zero_hp
# 限制不行动的检查更改
@current_action.clear if state.restriction == 4
# 状态变化 (+) (-) 处理
state.plus_state_set.each {|i| add_state(i, force)}
state.minus_state_set.each {|i| remove_state(i, force)}
# 修正当前 HP 及 SP
fix_hp
fix_sp
end
# 按强制附加与否设置解除回合数
if @states.include?(state_id)
@states_turn[state_id] = force ? -1 : state.hold_turn
end
end
这里有一个我并不打算修复的"bug":若状态A连锁+状态B和C,状态B和C彼此连锁-,则最后附加的状态(不论是不是强制附加)除了A以外,要看B和C的ID号哪个在前,在前的会被解除掉。默认脚本里就是这样,现在改了之后还是这样。我觉得这个是自己数据库设置的问题,状态B和C既然彼此连锁-,那本来逻辑上就不应该同时共存,都被A连锁+上你是想要闹哪样?
比如加速50%和减速50%,逻辑上互相抵销,但因为XP的状态能力乘算原理,如果真的都同时附加上,结果会是速度变成原来的(150%*50%)=75%,相当于半个减速。
不过,如果装备的自动状态A,满足上述条件的话,则确实目前会把A、B、C都处理成自动状态,也问问大家,虽然这个"bug"我是不打算修,但如果希望逻辑弄成一致,那倒确实是可以的。
状态的移除过程:基本直接copy代码,注意装备来的自动状态不在@states里面,是没法移除的,因装备自动状态被屏蔽掉的本体状态却在@states里面,是可以移除的,所以能不能移除只需要检查实体状态,有并能移除就移除。HP0的检查移到状态实际被删除之后,简化很多判断。最重要的是添加了连锁附加的状态连锁移除的过程。
#-------------------------------------------------------------------------- # ● 解除状态 # state_id : 状态 ID # force : 强制解除标志 (处理自动状态时使用) #-------------------------------------------------------------------------- def remove_state(state_id, force = false) # 无效状态的情况下,过程结束 return unless state = $data_states[state_id] # 战斗者本体未附加本状态的情况下,过程结束 return unless @states.include?(state_id) # 被强制附加的状态、并不是强制解除的情况下,过程结束 return if @states_turn[state_id] == -1 and not force # 清除状态缓存 @states_cache = nil # 删除本体状态 @states.delete(state_id) @states_turn.delete(state_id) # HP 0的检查更改 if @hp == 0 and state.zero_hp and states.all? {|i| not $data_states[i].zero_hp} @hp = 1 end # 强制解除的情况下,同样解除连锁附加的状态 state.plus_state_set.each {|i| remove_state(i, true)} if force # 修正当前 HP 及 SP fix_hp fix_sp end
#--------------------------------------------------------------------------
# ● 解除状态
# state_id : 状态 ID
# force : 强制解除标志 (处理自动状态时使用)
#--------------------------------------------------------------------------
def remove_state(state_id, force = false)
# 无效状态的情况下,过程结束
return unless state = $data_states[state_id]
# 战斗者本体未附加本状态的情况下,过程结束
return unless @states.include?(state_id)
# 被强制附加的状态、并不是强制解除的情况下,过程结束
return if @states_turn[state_id] == -1 and not force
# 清除状态缓存
@states_cache = nil
# 删除本体状态
@states.delete(state_id)
@states_turn.delete(state_id)
# HP 0的检查更改
if @hp == 0 and state.zero_hp and states.all? {|i|
not $data_states[i].zero_hp}
@hp = 1
end
# 强制解除的情况下,同样解除连锁附加的状态
state.plus_state_set.each {|i| remove_state(i, true)} if force
# 修正当前 HP 及 SP
fix_hp
fix_sp
end
其他零星修改:除了取能力值都要改成states以外,以下一些方法也得改。记住@states是身上的实体状态,(self.)states是身上的有效状态,两者既不等同,也不是子集关系就行。完全回复的那个bug犯的真的非常之蠢,只要将hp/sp的复设挪到状态被清除之后马上就可以了。
#-------------------------------------------------------------------------- # ● 获取状态的动画 ID #-------------------------------------------------------------------------- def state_animation_id states.empty? ? 0 : $data_states[states[0]].animation_id end #-------------------------------------------------------------------------- # ● 获取限制 #-------------------------------------------------------------------------- def restriction states.empty? ? 0 : states.max_by {|i| $data_states[i].restriction} end #-------------------------------------------------------------------------- # ● 判断状态 [无法获得 EXP]、[无法回避攻击]、[连续伤害] #-------------------------------------------------------------------------- def cant_get_exp?; states.any? {|i| $data_states[i].cant_get_exp}; end def cant_evade?; states.any? {|i| $data_states[i].cant_evade}; end def slip_damage?; states.any? {|i| $data_states[i].slip_damage}; end #-------------------------------------------------------------------------- # ● 状态变化 (+) 的适用 # plus_state_set : 状态变化 (+) #-------------------------------------------------------------------------- def states_plus(plus_state_set) # 清除有效标志 effective = false # 循环 (附加状态) for i in plus_state_set # 防御本状态的情况下,跳过 next if state_guard?(i) # 非满状态的情况下,设置有效标志 effective |= !state_full?(i) # 状态为 [不能抵抗],或状态非满且概率判定通过的情况下 if $data_states[i].nonresistance or (not state_full?(i) and rand(100) < state_table[state_ranks[i]]) # 设置状态变化标志 @state_changed = true # 附加状态 add_state(i) end end # 过程结束 return effective end #-------------------------------------------------------------------------- # ● 状态变化 (-) 的使用 # minus_state_set : 状态变化 (-) #-------------------------------------------------------------------------- def states_minus(minus_state_set) # 清除有效标志 effective = false # 循环 (解除状态) for i in minus_state_set # 状态被实际附加的情况下,设置有效标志 effective |= @states.include?(i) # 设置状态变化标志 @state_changed = true # 解除状态 remove_state(i) end # 过程结束 return effective end #-------------------------------------------------------------------------- # ● 全回复 #-------------------------------------------------------------------------- def recover_all @states.reject! {|i| @states_turn[i] >= 0} @states_turn.reject! {|k, v| v >= 0} @states_cache = nil @hp = maxhp @sp = maxsp end
#--------------------------------------------------------------------------
# ● 获取状态的动画 ID
#--------------------------------------------------------------------------
def state_animation_id
states.empty? ? 0 : $data_states[states[0]].animation_id
end
#--------------------------------------------------------------------------
# ● 获取限制
#--------------------------------------------------------------------------
def restriction
states.empty? ? 0 : states.max_by {|i| $data_states[i].restriction}
end
#--------------------------------------------------------------------------
# ● 判断状态 [无法获得 EXP]、[无法回避攻击]、[连续伤害]
#--------------------------------------------------------------------------
def cant_get_exp?; states.any? {|i| $data_states[i].cant_get_exp}; end
def cant_evade?; states.any? {|i| $data_states[i].cant_evade}; end
def slip_damage?; states.any? {|i| $data_states[i].slip_damage}; end
#--------------------------------------------------------------------------
# ● 状态变化 (+) 的适用
# plus_state_set : 状态变化 (+)
#--------------------------------------------------------------------------
def states_plus(plus_state_set)
# 清除有效标志
effective = false
# 循环 (附加状态)
for i in plus_state_set
# 防御本状态的情况下,跳过
next if state_guard?(i)
# 非满状态的情况下,设置有效标志
effective |= !state_full?(i)
# 状态为 [不能抵抗],或状态非满且概率判定通过的情况下
if $data_states[i].nonresistance or (not state_full?(i) and
rand(100) < state_table[state_ranks[i]])
# 设置状态变化标志
@state_changed = true
# 附加状态
add_state(i)
end
end
# 过程结束
return effective
end
#--------------------------------------------------------------------------
# ● 状态变化 (-) 的使用
# minus_state_set : 状态变化 (-)
#--------------------------------------------------------------------------
def states_minus(minus_state_set)
# 清除有效标志
effective = false
# 循环 (解除状态)
for i in minus_state_set
# 状态被实际附加的情况下,设置有效标志
effective |= @states.include?(i)
# 设置状态变化标志
@state_changed = true
# 解除状态
remove_state(i)
end
# 过程结束
return effective
end
#--------------------------------------------------------------------------
# ● 全回复
#--------------------------------------------------------------------------
def recover_all
@states.reject! {|i| @states_turn[i] >= 0}
@states_turn.reject! {|k, v| v >= 0}
@states_cache = nil
@hp = maxhp
@sp = maxsp
end
以上,对Game_Battler 2的重写基本就是这样。 |