# Stackable States by AzureFx
# Revision 20220519.1
# Licensed under the MIT License
def parse_note(note)
result=[]
note.each_line{|line|
m=/\[\s*([^\s=]+)\s*([^\]]+)?\s*\]/i.match(line)
if m!=nil
elem=m[1]
attr={}
attr_s=m[2]
if attr_s!=nil
attr_s.to_enum(:scan, /([^=\s]+)\s*=\s*([^=\s]+)/)
.map{Regexp.last_match}
.each{|m|attr[m[1]]=m[2]}
end
result.push([elem,attr])
end
}
result
end
class RPG::State
def stackable?
if @stackable==nil
parse_note_stackable
end
return @stackable
end
def max_stack
if @max_stack==nil
parse_note_stackable
end
return @max_stack
end
def parse_note_stackable
stackable=false
max_stack=0
parse_note(note).each{|entry|
elem, attr=entry
if elem=='stackable'
stackable=true
if attr['max']!=nil
max_stack=attr['max'].to_i
end
end
}
@stackable=stackable
@max_stack=max_stack
end
end
class RPG::Skill
def consume_stack
if @consume_stack_parsed==nil
parse_consume_stack
@consume_stack_parsed=true
end
@consume_stack
end
def parse_consume_stack
parse_note(note).each{|entry|
elem, attr=entry
if elem=='consume_state'
if attr['state']!=nil
state_id=attr['state'].to_i
@consume_stack={state: state_id}
if attr['count']!=nil
@consume_stack[:count]=attr['count'].to_i
end
end
end
}
end
end
class Game_BattlerBase
alias clear_states_b1f2b6 clear_states
def clear_states
clear_states_b1f2b6
@state_stacks={}
end
alias erase_state_b1f2b6 erase_state
def erase_state(state_id)
erase_state_b1f2b6(state_id)
@state_stacks.delete(state_id)
end
def state_stack_count(state_id)
@state_stacks[state_id]||0
end
def decrease_state_stack(state_id, count)
new_count=state_stack_count(state_id)-count
if new_count<=0
erase_state(state_id)
else
@state_stacks[state_id]=new_count
end
end
alias skill_cost_payable_b1f2b6? skill_cost_payable?
def skill_cost_payable?(skill)
cs=skill.consume_stack
if cs!=nil
count=state_stack_count(cs[:state])
cost=cs[:count]||-1
return false unless count>0&&count>=cost
end
skill_cost_payable_b1f2b6?(skill)
end
alias pay_skill_cost_b1f2b6 pay_skill_cost
def pay_skill_cost(skill)
pay_skill_cost_b1f2b6(skill)
cs=skill.consume_stack
if cs!=nil
cost=cs[:count]||-1
if cost<0
cost=state_stack_count(cs[:state])
end
@@last_state_cost=cost
decrease_state_stack(cs[:state], cost)
end
end
def last_state_cost
@@last_state_cost
end
end
class Game_Battler
alias reset_state_counts_b1f2b6 reset_state_counts
def reset_state_counts(state_id)
reset_state_counts_b1f2b6(state_id)
state = $data_states[state_id]
if state.stackable?
if @state_stacks[state_id]==nil
@state_stacks[state_id]=0
end
if state.max_stack<=0||@state_stacks[state_id]<state.max_stack
@state_stacks[state_id]+=1
end
end
end
alias on_action_end_b1f2b6 on_action_end
def on_action_end
on_action_end_b1f2b6
@@last_state_cost=nil
end
end
STACK_COUNT_FONT_SIZE=14
STACK_COUNT_FONT=Font.new
STACK_COUNT_FONT.size=STACK_COUNT_FONT_SIZE
STACK_COUNT_FONT.color=Color.new(0,255,0)
STACK_COUNT_FONT.bold=true
STACK_COUNT_FONT.outline=false
STACK_COUNT_FONT.shadow=true
class Window_Base
alias draw_actor_icons_b1f2b6 draw_actor_icons
def draw_actor_icons(actor, x, y, width = 96)
draw_actor_icons_b1f2b6(actor, x, y, width)
last_font=contents.font
last_size=last_font.size
last_bold=last_font.bold
contents.font=STACK_COUNT_FONT
actor.states.select{|s|s.icon_index!=0}.each_with_index {|s, i|
if s.stackable?
count=actor.state_stack_count(s.id)
contents.draw_text(x, y+1, 22, STACK_COUNT_FONT_SIZE, count, 2)
end
}
contents.font=last_font
contents.font.size=last_size
contents.font.bold=last_bold
end
end