Code:
# * KSkillRunes XP
# Scripter : Kyonides Arkanthes
# 2021-04-03 - Beta 9
# This script is a Rune Based Skill Learning System vaguely similar to a
# Materia System.
# * Instructions * #
# Create Items representing the Rune Skill Gem Stones.
# Create a Temp directory and place the template TXT file here and set its
# values accordingly. Copy and Paste it into new TXT files as many times as
# needed.
# Set the ITEM_IDS_RANGE constant's value below.
# If a Hero has acquired a Rune Gem Stone, no existing skills will be
# overwritten. Every Rune Gem Stone has an activation cost (in gold pieces).
# Every Rune Skill needs Rune Points so spend them wisely.
# Game_Battler#skill_effect has been overwritten.
# * Script Calls * #
# * Open Rune Scene - Positions: 0 through 3
# $scene = KSkillRuneScene.new
# $scene = KSkillRuneScene.new(ActorIndex)
# * First Select an Actor - Positions: 0 through 3
# actor = $game_party.actors[Position]
# * Set or Increase or Decrease the Maximum Number of the Actor's Skill Runes
# actor.skill_runes_max = Number
# actor.skill_runes_max += Number
# actor.skill_runes_max -= Number
# * Manually Increase an Actor's RP (Rune Points)
# actor.rp += Number
# * Use Rune Points RP to Activate a given Skill the Rune can teach the Hero
# actor.learn_rune_skill(RuneItemID, TargetSkillID)
module KSRunes
TEMP_DIR = "Temp"
MISSED_TARGET = "Miss"
TITLE = 'Skill Runes'
STARTING_RUNE_MAX = 1
ITEM_IDS_RANGE = 40..51
BACKDROP = ''
ACTOR_BLOCKED_RUNES = {}
# * End of Setup Section * #
class Skill
attr_accessor :id, :level, :learn_skill, :skill_points
def can_learn?(pts) @skill_points == pts end
end
class Rune
def initialize
@id = 0
@name = ""
@level = 0
@level_max = 1
@blocked_rune = 0
@price = 0
@skills = {}
end
attr_accessor :id, :name, :level, :level_max, :blocked_rune, :price, :skills
end
extend self
attr_reader :runes
def scan_int(line) line.scan(/\d+/).map{|d| d.to_i } end
def parse_skill(numbers)
skill = Skill.new
skill.id = numbers.shift
skill.level = numbers.shift
skill.learn_skill = numbers.shift
skill.skill_points = numbers.shift
skill
end
def parse_file(name)
rune = Rune.new
lines = File.readlines(name)
rune.id = scan_int(lines.shift)[0]
rune.name = lines.shift.sub(/magic type: /i, "")
rune.level = scan_int(lines.shift)[0]
rune.level_max = scan_int(lines.shift)[0]
rune.blocked_rune = scan_int(lines.shift)[0] || 0
rune.price = scan_int(lines.shift)[0]
n = 0
lines.each do |line|
numbers = scan_int(line)
skill = parse_skill(numbers)
rune.skills[skill.id] = skill
end
@runes[rune.id] = rune
end
def retrieve_runes
return @runes = load_data('Data/KSkillRunes.rxdata') unless $DEBUG
@runes = {}
Dir[TEMP_DIR + '/*.txt'].sort.each{|name| parse_file(name) }
File.open('Data/KSkillRunes.rxdata', 'wb'){|f| Marshal.dump(@runes, f) }
end
retrieve_runes
end
class Game_Battler
def alive_not_dead?(skill)
(skill.scope == 3 or skill.scope == 4) and @hp == 0
end
def dead_not_alive?(skill)
(skill.scope == 5 or skill.scope == 6) and @hp >= 1
end
def calculate_skill_power
power = @skill.power
if @user.is_actor? and @user.rune_skill?(@skill.id)
rune_skill = @user.rune_skills[@skill.id]
power += @skill.variance * rune_skill.level
end
power = power + @user.atk * @skill.atk_f / 100
if power > 0
power -= self.pdef * @skill.pdef_f / 200
power -= self.mdef * @skill.mdef_f / 200
power = [power, 0].max
end
power
end
def checking_hit(hit, hit_result)
return hit unless hit_result
power = calculate_skill_power
rate = 20
rate += (@user.str * @skill.str_f / 100)
rate += (@user.dex * @skill.dex_f / 100)
rate += (@user.agi * @skill.agi_f / 100)
rate += (@user.int * @skill.int_f / 100)
@damage = power * rate / 20
@damage *= elements_correct(@skill.element_set)
@damage /= 100
@damage /= 2 if @damage > 0 and self.guarding?
if @skill.variance > 0 and @damage.abs > 0
amp = [@damage.abs * @skill.variance / 100, 1].max
@damage += rand(amp+1) + rand(amp+1) - amp
end
eva = 8 * self.agi / @user.dex + self.eva
hit = @damage < 0 ? 100 : 100 - eva * @skill.eva_f / 100
self.cant_evade? ? 100 : hit
end
def checking_effectivity(effective)
if @skill.power != 0 and @skill.atk_f > 0
remove_states_shock
effective = true
end
last_hp = @hp
self.hp -= @damage
effective |= @hp != last_hp
@state_changed = false
effective |= states_plus(@skill.plus_state_set)
effective |= states_minus(@skill.minus_state_set)
@last_damage = @damage
if @skill.power == 0
@damage = @state_changed ? "" : KSRunes::MISSED_TARGET
@last_damage = 0
end
effective
end
def skill_effect(user, skill)
@critical = false
user.last_skill = $game_temp.in_battle ? skill : nil
return false if alive_not_dead?(skill) or dead_not_alive?(skill)
@user = user
@skill = skill
effective = false
effective |= @skill.common_event_id > 0
hit = @skill.hit
hit *= @user.hit / 100 if @skill.atk_f > 0
hit_result = (rand(100) < hit)
effective |= hit < 100
hit = checking_hit(hit, hit_result)
effective |= hit < 100
hit_result = (rand(100) < hit)
if hit_result
effective = checking_effectivity(effective)
else
@damage = "Miss"
@last_damage = 0
end
unless $game_temp.in_battle
@damage = nil
@last_damage = 0
end
@skill = @user = nil
return effective
end
attr_accessor :last_skill, :last_damage
end
class Game_Actor
alias :kyon_skillrunes_gm_actor_init :initialize
def initialize(actor_id)
kyon_skillrunes_gm_actor_init(actor_id)
@skill_runes_max = KSRunes::STARTING_RUNE_MAX
@rune_skills = {}
@rp = 0
end
def item_effect(item)
item_id = item.id
return super(item) unless KSRunes::ITEM_IDS_RANGE.include?(item_id)
return false unless learn_from_rune?(item_id)
return false if rune_skill?(item_id)
return false if skill_runes_max?
rune = KSRunes.runes[item_id]
return false if $game_party.gold < rune.price
$game_party.gain_gold(-rune.price)
skills = rune.skills
learn_rune_skills(rune.blocked_rune, skills.values)
skills.each{|k,v| @rune_skills[k] ||= v.dup }
end
def learn_rune_skill(rid, sid)
rskills = @rune_skills[rid]
return "No Rune" unless rskills
skill = rskills[sid]
return "Wrong Skill ID" unless skill
return "Not Enough RP" if @rp == 0 or skill.skill_points > @rp
skill.level += 1 if skill.level < KSRunes.runes[rid].level_max
@rp -= skill.skill_points
end
def learn_from_rune?(rune_id)
blocked_runes = KSRunes::ACTOR_BLOCKED_RUNES[@actor_id]
return true unless blocked_runes
!blocked_runes.include?(rune_id)
end
def learn_rune_skill?(level, skill_id, skill)
return false if @level < level or skill_learn?(skill_id)
skill.can_learn?(@rp)
end
def learn_rune_skills(rune_id, blocked_rune, skills)
br = blocked_rune
skills = skills.map{|s| s.id if learn_rune_skill?(s.level, br, s) }
skills = skills.compact
@skills = (@skills + skills).uniq.sort
end
def rune_skill?(skill_id) @rune_skills.has_key?(skill_id) end
def skill_runes_max?() @rune_skills.size == @skill_runes_max end
def is_actor?() true end
def runes() @rune_skills.keys.sort end
attr_accessor :skill_runes_max, :rp
attr_reader :rune_skills
end
class Game_Enemy
def is_actor?() false end
end
class KSRuneSpriteset
include KSRunes
def initialize(pos)
@index = pos
@rune_index = 0
@actors = $game_party.actors
actor = @actors[@index]
@backdrop = Sprite.new
@backdrop.bitmap = RPG::Cache.title(BACKDROP)
@heading = Sprite.new
@heading.x = 160
@heading.y = 8
@heading.bitmap = b = Bitmap.new(320, 32)
b.draw_text(0, 4, 320, 24, TITLE, 1)
@name_sprite = Sprite.new
@name_sprite.x = 8
@name_sprite.y = 36
@name_sprite.bitmap = b = Bitmap.new(240, 32)
b.draw_text(0, 4, 240, 24, actor.name, 1)
@icon_names = actor.runes.map{|n| $data_items[n].icon_name }
@icons = []
@icon_labels = []
@icon_names.size.times{|n| draw_rune_icons(n) }
@sprites = [@backdrop, @heading, @name_sprite]
end
def draw_rune_icons(pos)
@icons << s = Sprite.new
s.x = 8
s.y = 64 + pos * 28
s.bitmap = RPG::Cache.icon(@icon_names[pos])
@icon_labels << l = Sprite.new
l.x = s.x + 28
l.y = s.y
l.bitmap = b = Bitmap.new(200, 24)
b.draw_text(0, 0, 200, 24, @icon_names[pos])
end
def dispose
@icon_labels.each{|s| s.dispose }
@sprites.each{|s| s.dispose }
@icons.each{|s| s.dispose }
@sprites.clear
@icons.clear
@icon_names.clear
@actors = nil
end
attr_reader :rune_index
end
class KSkillRuneScene
def initialize(pos=0) @index = pos end
def main
@run = true
@spriteset = KSRuneSpriteset.new(@index)
Graphics.transition
while @run
Graphics.update
Input.update
update
end
Graphics.freeze
@spriteset.dispose
end
def update
if Input.trigger?(Input::B)
$game_system.se_play($data_system.cancel_se)
$scene = Scene_Map.new
return @run = nil
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
end
end
end