diff --git a/mkxp-z/Kawariki-patches/patches.rb b/mkxp-z/Kawariki-patches/patches.rb index 6533077..06ef028 100644 --- a/mkxp-z/Kawariki-patches/patches.rb +++ b/mkxp-z/Kawariki-patches/patches.rb @@ -18,6 +18,23 @@ module Preload .include?('Win32API.new(self.steam_dll_name') # .remove!, .replace!("achievements.rb"), + Patch.new("Advanced Text System fix") + .include?('Advanced Text System') + .include?('modern algebra (rmrk.net)') + # .remove!, + .replace!("Advanced-Text-System.rb"), + Patch.new("AudioUtilities plugin fix") + .include?('waveOutOpen=Win32API.new("winmm.dll","waveOutOpen","plplll","l")') + .include?('def pbPlaySoundData(samples,volume,async=false,sampleFreq=11025)') + # .remove!, + # .replace!("dummyAudioUtilities.rb") + .sub!(/when (\d+):/, 'when \1'), + Patch.new("dummyPSystem_Utilities plugin fix") + .include?('pbNextComb') + # .include?('def pbIsJsonString') + # .remove!, + .replace!("dummyPSystem_Utilities.rb"), + # .sub!(/when (\d+):/, 'when \1'), Patch.new("XIV's Simple Reputation System (SRS) for RGSS3 fix") #XIV's Simple Reputation System (SRS) for RGSS3 Romance Level Version: 1.1 .include?("Simple Reputation System (SRS)") .sub!("$scene1 = Scene_Menu.new(0)", "$scene1 = Scene_Menu.new"), @@ -172,6 +189,11 @@ module Preload .imported?(nil) .include?('text.push(self.to_s.scan(/#<(\S+):/)[0][0].to_s)') .remove!, + Patch.new("tktk_bitmap dll test debug") + .imported?(nil) + .include?("DLL_NAME = 'tktk_bitmap'") + # .remove!, + .replace!("bitmap_tktk.rb"), Patch.new("Flirt quest") .imported?(nil) .include?('class Spriteset_BattleUnit') diff --git a/mkxp-z/Kawariki-patches/ports/Advanced-Text-System.rb b/mkxp-z/Kawariki-patches/ports/Advanced-Text-System.rb new file mode 100644 index 0000000..d42e4e4 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/Advanced-Text-System.rb @@ -0,0 +1,3708 @@ +#============================================================================== +# Advanced Text System +# Version: 3.0c +# Author: modern algebra (rmrk.net) +# Date: September 7, 2010 +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Thanks: +# *Zeriab* for his tutorial on RegExp, but most of all for being my mentor +# and friend. Without him, this script, and all my other scripts, will +# never have been made. +# For ideas on improving this script through bug reports or suggestions: +# Arrow-1, Irock, Stonewall, PhantomH, Seasons in the Abyss, Aindra, Arion, +# Okogawa, Megatronx, Charbel, Adrien., dricc, Raukue, EricDahRed, redyugi, +# HeartofShadow, Woratana, tsy0302, Kayo, deadnub. +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Description: +# +# This scripts adds a whole bunch of features to message boxes, making them +# much prettier and with a lot more functionality than the default. What and +# how to use each feature will be exhaustively detailed in the instructions, +# but for now I will just give a list of a few of the staple features: +# +# ~Appended Text~ +# A staple feature of ATS, this will combine the messages of subsequent text +# events (that share properties). It is useful particularly when combined +# with text scrolling. +# +# ~Text Scrolling~ +# This feature allows longer text messages to scroll upwards, so that for +# longer messages, the contents of the window will scroll upwards when it +# reaches the bottom, allowing the player to read what has come before and +# ignore the annoying new page requirements. This feature has been beefed up +# in ATS 3.0, with the scroll now being much smoother and the speed user- +# definable, in addition to the new feature of Scroll Review, which, once the +# message is finished, allows the player to scroll upwards to see any text he +# or she might have missed. Moreover, you can also scroll by page instead. +# +# ~Paragraph Format~ +# Always a staple feature in the ATS, turning this feature on allows you to +# avoid the hassle that comes with making the text fit within the window by +# analyzing the exact length with reference to the grey arrows. Now you can +# type freely and know that nothing will get cut off. This feature takes on +# additional importance in ATS 3.0 with the addition of a special message +# codes like \S[] and \S![], more on which will be discussed later. As +# always, you can still manually define line breaks with the \LB code. As in +# previous versions, you can also justify text, meaning that the letters will +# be spaced out so as to exactly cover the width of the message box. Unlike +# previous versions, this paragraph formatter is integrated with the ATS in +# order to better accomodate text features and therefore does not require a +# separate Paragraph Formatter script to work. +# +# ~Advanced Choices~ +# Another staple feature of the ATS, you have been able to append choices +# from subsequent choice branches and show them all in a separate window. +# This feature has been vastly expanded on in ATS 3.0. In earlier versions, +# many of the features respecting advanced choices, such as appending them +# and tying choices to switches, were available only if using the choice +# window. In ATS 3.0, these features have been included in the traditional +# in-message window. Don't want to use the choice window but do want to +# exclude some choices unless a particular switch is ON? You can do that now. +# In addition to all of the old features, there are quite a few new ones that +# will make your choices more dynamic. One is the ability to make an the +# cursor pass over an option. This means it is a perfect way to include +# headings or blank spaces in your choice box. Another new feature are the +# disable codes which allow you to make a choice unselectable, so when the +# player tries to select it the buzzer sound is played and nothing happens. +# Further, there is an external message property that can be set which you +# can use to automatically add certain properties to modify the choice text +# if it is disabled, such as a command to reduce the opacity of it. In +# addition, you can automatically modify all choice texts at once - want to +# add a larger indent or make all choices blue - you can do that. Finally, +# another unique feature of ATS choices is the \+{} code. Placed in a comment +# directly below a when branch of a choice branch, this allows you to add +# whatever text you want to that choice, effectively ignoring the limitations +# on choice message size. This means you can make your choices sentences +# instead of simple yes or nos. In addition to this, another new feature of +# ATS 3.0 choices is that choice messages can be longer than one line and +# that is recognized both in the choicebox and the new and improved default +# choice scheme. The last new feature added to choices in ATS 3.0 is the +# ability to set a help window for it. This means it will set a window whose +# contents will change depending on which choice the player is hovering over. +# +# ~Advanced Faces~ +# From the very start, animated faces have been a part of the ATS. This +# feature allows you to have the faces animate for every few letters drawn - +# perfect to make it look like the face is talking. However, this is not all +# the special features you gain with faces. As always, you can also use much +# larger or smaller faces, mirror them, set them to the right side of the +# text box, or move them anywhere you like, change the opacity, or surround +# it with a window. New in ATS 3.0, you can now define the exact size of the +# border of the window if using it, or you can use another sprite for it +# (just like the dim background of the Message Window). Also new in ATS 3.0, +# you can scroll the face in when it first appears either horizontally or +# vertically, or fade it in. You can also choose to make the face appear over +# or under the message window, and change the blend type. +# +# ~Letter Control~ +# This has always been a feature of the ATS and it allows you to control the +# speed that text is drawn, as well as putting in pauses at will. It also +# allows you to specify a sound, and you can even have it vary the pitch with +# every letter so that it mimics the sound of a voice. New in ATS 3.0, you +# can control the speed text is drawn not only through the \> and \< codes, +# but more directly via the \S[] command, where you can either set it to add +# or subtract a number from the current speed, or you can set it directly. +# See the section on this code in Special Message Codes for details. +# +# ~Advanced Message Window~ +# There are also a number of special features related to the appearance and +# position of the message window. Most of these should be familiar to users +# of ATS 2.0. You can manually control the size and exact position of the +# text box, or you can set it to automatic. Further, you can use the +# :fit_window_to_text property to set the width of the window to be only as +# long as it needs to be to accomodate the longest line. As always, you can +# set what windowskin to use, the font, how much space for each line, and the +# dim background. New features include the :do_not_obscure property, which, +# when using default positioning, will ensure that the characters specified +# in the :obscure_characters array. This feature will be ignored if manually +# setting position however. The only thing it does is, if you have the +# position set to bottom, but that would cover the player and you include the +# player in your obscure_characters array, then it would move the message box +# to the top instead. As always, you can use \OC[], \UC[], \LC[], and \RC[] +# to position the window in reference to a character. Something new is the +# \E[] command, which will set it above the character, unless there is not +# enough room, in which case it sets it below the character. Another new +# feature is speech tags, which, when using \OC[], \UC[], or \E[] codes will +# place a tag from the character speaking to the message window if this +# feature is enabled. +# +# ~Word Boxes~ +# New to ATS 3.0, this is a very simple feature that works akin to the Gold +# or Name window. Basically, it allows you to set a one line message to +# appear instantly. It can be used, for instance, if you want to show the +# value of a variable in the corner while showing this message or choice. +# Naturally, name boxes have also been retained from ATS 2.0 +# +# ~Graphic Novel Support~ +# New to ATS 3.0, this feature, when activated, allows the player to +# completely hide and prevent from updating every message related window for +# as long as the player holds a button you choose or, if you wish, it can be +# a toggle rather than a press. It is useful if you have a game that relies +# heavily on graphics and you want to give the player the opportunity to just +# look at the background, like in many graphic novel games. +# +# ~Move While Showing~ +# Also new to ATS 3.0, this is a feature that, when activated, allows the +# player to move around while a message is showing. However, when a choice +# selection is active, this feature will be disabled. +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Instructions: +# +# Place this script in its own slot in the Script Editor (F11) above Main +# and below Materials. This script is not compatible with most other message +# systems, so you may need to remove any other message systems. If you are +# upgrading to this from ATS 2.0, you will need to also install the +# conversion patch, located in this script's thread at RMRK. +# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~Special Message Codes~ +# These codes work in all ATS windows. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# You will recognize many of these from previous versions of the ATS. A lot +# are new though. +# \lb - Line break. Go to next line. +# \v[x] - Draw the value of the variable with ID x. +# \n[x] - Draw the name of the actor with ID x. +# \c[x] - Set the colour of the text being drawn to the xth colour of the +# Windowskin palette. 0 is the normal color and 16 is the system color. +# \c[#RRGGBB] - Set the colour of the text being drawn to any colour, using +# hexadecimal. You can set each colour (red, green, blue) to anything +# from 0-255 (00-FF). You must use hexadecimal values. +# \p[x] OR \pid[x] - Draw the ID of the actor in the xth position in the party. +# \ni[x] - Draw the name of the item with ID x. +# \nw[x] - Draw the name of the weapon with ID x. +# \na[x] - Draw the name of the armor with ID x. +# \ns[x] - Draw the name of the skill with ID x. +# \nt[x] - Draw the name of the state with ID x. +# \nc[x] - Draw the name of the class with ID x. +# \ne[x] - Draw the name of the event with ID x on the current map. +# \nm[x] - Draw the name of the enemy with ID x. +# \nl[x] - Draw the name of the element with ID x. +# \nv[x] - Draw the name of the variable with ID x. +# \nsw[x] - Draw the name of the switch with ID x. +# \np[x] - Draw the name of the actor in the nth position in the party. +# \map - Draw the name of the map the player is currently on. +# \map[x] - Draw the name of the map with ID x. +# \di[x] - Draw the description of the item with ID x. +# \dw[x] - Draw the description of the weapon with ID x. +# \da[x] - Draw the description of the armor with ID x. +# \ds[x] - Draw the description of the skill with ID x. +# \pi[x] - Draw the price of the item with ID x. +# \pw[x] - Draw the price of the weapon with ID x. +# \pa[x] - Draw the price of the armor with ID x. +# \i#[x] - Draw the number of the item with ID x that the party posesses. +# \w#[x] - Draw the number of the weapon with ID x that the party posesses. +# \a#[x] - Draw the number of the armor with ID x that the party posesses. +# \ac[x] - Draw the class of the actor with ID x. +# \i[x] - Draw the icon with index x. +# \ii[x] - Draw the icon of the item with ID x. +# \wi[x] - Draw the icon of the weapon with ID x. +# \ai[x] - Draw the icon of the armor with ID x. +# \si[x] - Draw the icon of the skill with ID x. +# \ti[x] - Draw the icon of the state with ID x. +# \fn[fontname] - Change the font to fontname +# \fs[x] - Change the fontsize to x. +# \fa[x] - Change the alpha value (opacity) of the font to x. +# \b - Turn bold on. Text drawn after this code will be bolded. +# /b - Turn bold off. +# \i - Turn italic on. Text drawn after this code will be italicized. +# /i - Turn italic off. +# \s - Turn shadow on. Text drawn after this code will have a shadow. +# /s - Turn shadow off. +# \u - Turn underline on. Text drawn after this code will be underlined. +# /u - Turn underline off. +# \hl[x] - Turn highlight on. Text drawn after this code will be highlighted +# with colour x from the windowskin palette +# /hl OR \hl[-1] - Turn highlight off. +# \l - align the text to the left +# \r - align the text to the right +# \c - align the text to the centre. +# \t - Tab. Draws the next character at the nearest pixel that is a +# multiple of 32. Doesn't work well with the :justified_text property. +# \x[n] - Sets the x position for drawing directly to n. +# \f[key] - Draw the value corresponding to that key in the FILTERS array. If +# the key has quotation marks around it, you need to put "key" +# \s[x,text] - Will only draw text if the switch with ID x is ON. +# \s![x,text] - Will only draw text if the switch with ID x is OFF. +# \vocab[method] - Will draw whatever Vocab.method returns, if it is a valid +# method call. Suitable values for method are: level, level_a, hp, +# hp_a, mp, mp_a, atk, def, spi, agi, weapon, armor1, armor2, armor3, +# armor4, weapon1, weapon2, attack, skill, guard, item, equip, status, +# save, game_end, fight, escape, new_game, shutdown, to_title, gold, +# continue, cancel +# \actor_method[x] - This will draw whatever actor.method returns for whoever +# actor x. Some suitable values for method are: atk, def, spi, agi, +# level, exp, name, hp, maxhp, mp, maxmp, & any other methods that +# return values from Game_Actor. +# \i_method[x] - This will draw whatever item.method returns for the item with +# ID x. Some suitable values for method are: name, description, +# base_damage, variance, atk_f, spi_f, price, hp_recovery_rate, +# hp_recovery, mp_recovery_rate, mp_recovery, parameter_points, note, +# n, & any other methods that return values from RPG::Item. Also, note +# will return all of the contents of the note field, whereas n will +# only return notefield text located between a \msg[text]msg/ code. +# \w_method[x] - This will draw whatever weapon.method returns for the weapon +# with ID x. Some suitable values for method are: name, description, +# price, hit, atk, def, spi, agi, note, n, & any other methods that +# return values from RPG::Weapon. Also, note will return all +# of the contents of the note field, whereas n will only return +# notefield text located between a \msg[text]msg/ code. +# \a_method[x] - This will draw whatever armor.method returns for the armor +# with ID x. Some suitable values for method are: name, description, +# price, eva, atk, def, spi, agi, note, n, & any other methods that +# return values from RPG::Armor. Also, note will return all +# of the contents of the note field, whereas n will only return +# notefield text located between a \msg[text]msg/ code. +# \s_method[x] - This will draw whatever skill.method returns for the skill +# with ID x. Some suitable values for method are: name, description, +# base_damage, variance, atk_f, spi_f, hit, mp_cost, note, n, & any +# other methods that return values from RPG::Weapon. Also, note will +# return all of the contents of the note field, whereas n will only +# return notefield text located between a \msg[text]msg/ code. +# \t_method[x] - This will draw whatever state.method returns for the state +# with ID x. Some suitable values for method are: name, atk_rate, +# def_rate, spi_rate, agi_rate, message1, message2, message3, +# message4, note, n, & any other methods that return values from +# RPG::State. Also, note will return all of the contents of the note +# field, whereas n will only return notefield text located between a +# \msg[text]msg/ code. +# \enemy_method[x] - This will draw whatever enemy.method returns for whoever +# the enemy with ID x is. Some suitable values for method are: name, +# atk, def, spi, agi, hit, eva, exp, gold, note, n, & any other +# methods that return values from RPG::Enemy. Also, note will return +# all of the contents of the note field, whereas n will only return +# text located between a \msg[text]msg/ code. +# \#{code}# - This will evaluate code. So, if you know scipting, you can place +# any code there and it will draw whatever is returned by it. For +# instance: \#{$game_system.save_count}# would draw the number of +# times the player has saved the current game. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~Message Box specific codes~ +# These codes will only work in the Message Window. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# \g - Shows a window with the party's gold. Closes if already open +# \nb[name] - Shows a name box with name displayed in the box +# /nb - Closes the namebox +# \wb[word] - shows the word in its own window, similar to the gold window. +# /wb - Closes a wordbox +# \. - Wait 15 frames (1/4 second) before drawing the next letter. +# \| - Wait 60 frames (1 second) before drawing the next letter. +# \w[x] - Wait x frames before drawing the next letter. +# \! - Pause. Make the message wait for player input before continuing. +# \^ - Skip the next pause without waiting for player input. +# \> - Speed up the text drawing by reducing wait time between letters by +# one frame. +# \< - Slow down the text drawing by increasing wait time between letters by +# one frame. +# \S[x] - Change the speed the text draws by adding x to the current time +# between drawing letters. Thus, \s[2] is the equivalent of \<\< and +# \s[-3] is the equivalent of \>\>\>. You can also directly set the +# speed by putting an equal sign, so \s[=0] would set the time to one +# frame between drawing letters, and \s[=-1] would set it to instant. +# \@ - Turn on Show line fast - the line up to /@ will be shown instantly +# /@ - Turn off Show line fast. +# \@@ - Turn on Show message fast. This will show the entire message +# instantly, at least until the next scroll or it hits a /@@ +# /@@ - Turn off Show message fast. +# \% - Disable Text Skip through user input +# \se[sound effect name] - Plays a sound effect +# \me[music effect name] - Plays a musical effect +# \ani[target_id,animation_id] - Shows animation_id on target_id. 0 => player, +# other numbers indicate the ID of the event +# \bln[target_id,balloon_id] - Same as ani, but shows a balloon +# \af[x] - Show the face of the actor with ID x. +# \pb - Page Break. Clear the contents and start drawing from the first line +# \oc[x] - positions the message box over a character. 0 => player; when >1, it +# will show over the event with that ID on the map. +# \uc[x] - same as \oc, but places box under character +# \lc[x] - same as \oc, but places box to left of character +# \rc[x] - same as \oc, but places box to right of character +# \e[x] - same as \oc, but if the box is too tall to comfortably fit, is moved +# below the character instead. +# \mxy[x, y] - Set the position of the message window to x, y. +# \fxy[x, y] - Set the position of the face window to x, y. +# \nxy[x, y] - Set the position of the name window to x, y. +# \#!{code}# - This will evaluate code at the time the window reaches this code +# when drawing. It does not put the result of the code into the message +# but is instead intended to be evaluated. For instance: \#!{new_page}# +# would perform the same function as \pb. You need to know syntax! +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~Choice Branch Specific Codes~ +# These codes only work in choice branches. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# \skip - Placing this in a choice text means that the player will not be able +# to hover over or select this option, but will skip to the next one. +# Perfect for making subheadings for your choices, or blank spaces. +# \soff[x] - A choice with this in it will only appear in the branch if the +# switch with ID x is OFF. +# \son[x] - Same as soff[x], but it will only appear if switch x is ON. +# \d[x] - A choice with this in it will be disabled (unselectable) if the +# switch with ID x is OFF. It will still show up, and the player can +# hover over it, but he or she will be prevented from selecting it. +# \d![x] - Same as \d[x], except it will be disabled if switch x is ON. +# \wb[text] - This code will create a help window. When the player hovers over +# that choice, it will show text in the help window. This allows you to +# explain the choice or make any content in the help window dependent +# on which choice the player is on. +# \+{text} - This is actually a code you can put in a comment that is directly +# below the when branch of a choice, and it will add text to the +# choice. This effectively ignores the normal limitations on the size +# of a message in a choice, allowing you to make longer texts for +# choices. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~Message Properties~ +# The following are properties of the message window that are set prior to +# calling the message and govern many properties. You set the default values +# to the constants of the same name in Game_ATS, starting at line 766. Those +# will be the values that each game starts out with. You can then change them +# on a message to message basis through either of the codes: +# ats_next (:property, new_value) +# or: +# $game_message.property = new_value +# However, it will reset to the default after the next message is processed. +# If you want to change it on a permanent basis, you have to use either: +# ats_all (:property, new_value) +# or: +# $game_ats.property = new_value +# $game_message.property = new_value +# +# All of the properties and what types of values are expected for each are +# listed below. Note that if you use the ats_next or ats_all commands, you +# have to retain the : in front of the name. If you use the $game_ats or +# $game_message route then you don't use the : +# +# You can reset $game_ats to the constants with: $game_ats.reset +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :max_lines - This is the maximum number of lines to show at a time before +# either going to a new page or scrolling. Default: 4 +# :message_speed - This is the number of frames to wait in between drawing +# letters. -1 is instant. 0 is 1 frame. Default: 0 +# :skip_disabled - Whether the player can speed text up by pressing ENTER. +# :append_text - Whether to include all text from subsequent text commands in +# the next message. true => append; false => do not append. Default: true +# :append_choice - Whether to include all choices from subsequent choice +# choice branches in the next choice selection. The cancel branch is +# inherited from the last branch to not have cancel disabled. +# true => append; false => do not append. Default: true +# :scrolling - Whether to scroll once :max_lines is exceeded or to pause and +# start a new page. true => scroll; false => new page. Default: true +# :scroll_speed - If :scrolling is true, this is how many pixels it moves per +# frame when scrolling text. Default: 2 +# :scroll_show_arrows - If :scrolling is true, this determines whether or not +# to show the arrows when the contents grow. true => show; false => do +# not show. Default: true +# :scroll_autopause - If :scrolling is true, this determines whether or not to +# insert a pause before it scrolls to the next line. Default: false +# :scroll_review - If :scrolling is true, this option is whether or not the +# player has the option to review the message that has just finished by +# pressing up and down. true => yes; false => no. Default: true +# :scroll_by_page - Whether to scroll by whole page or by line. Default: false +# :paragraph_format - When this is true, the manual line breaks are ignored and +# it will draw as many characters as will fit on each line. You can still +# force a line break with the \lb code though. Default: false +# :justified_text - If :paragraph_format is true, then this option will make +# the spacing of a line exactly such that it takes up the entire width of +# the message window. Default: false +# :letter_sound - When this is true, a sound will be played upon drawing +# letters. Default: false +# :letter_se - If :letter_sound is true, this is the SE that is played. It is +# set in the format ["filename", volume, pitch]. Default: ["Open1", 40] +# :letters_per_se - If :letter_sound is true, this is the frequency that the +# SE is played. The SE will play every time this number of letters is +# drawn. Default: 3 +# :random_pitch - If :letter_sound is true, this is the range the pitch of the +# SE will vary within. It can be used to mimic the pitch variations of +# a human voice. It is a range in the format x..y. If x is equal to y, +# then the pitch won't vary. If set to an integer, it will take that as +# the variance from the regular pitch of the SE. Default: 100..100 +# :speech_tag_index - This refers to which speech tag to use when showing a +# message window above or below an event or player. -1 => no tag, while +# anything greater refers to the graphic of the corresponding index in +# :speech_tag_graphics. Default: -1 +# :speech_tag_graphics - This is an array containing the names of all speech +# tag graphics in the System folder of Graphics. Which one is used when +# showing a message depends on the value of :speech_tag_index. +# Default: ["Speech Tag 1", "Speech Tag 2", "Thought Tag 1"] +# :start_sound - If true, a sound will be played when a message starts. +# Default: false +# :start_se - If :start_sound is true, this is the SE that is played. It is +# set in the format ["filename", volume, pitch]. Default: ["Chime2"] +# :finish_sound - If true, a sound will be played when a message finishes. +# Default: false +# :finish_se - If :finish_sound is true, this is the SE that is played. It is +# set in the format ["filename", volume, pitch]. Default: ["Chime1"] +# :pause_sound - If true, a sound will be played when a message pauses. +# Default: false +# :pause_se - If :pause_sound is true, this is the SE that is played. It is +# set in the format ["filename", volume, pitch]. Default: ["Decision2"] +# :terminate_sound - If true, a sound will be played when a message closes. +# Default: false +# :terminate_se - If :terminate_sound is true, this is the SE that is played. It is +# set in the format ["filename", volume, pitch]. Default: ["Cancel"] +# :move_when_visible - When this is true, the player will still be able to move +# when the message window is displaying text. It is recommended that you +# turn scroll review off when using this feature though, as it looks +# dumb. Also, whenever a choice starts, it takes precedence and the +# player will not be able to move even if this is true. Default: false +# :graphic_novel - When this is true, it means the player can press the +# :hide_button and everything relating to the message window becomes +# invisible and stops updating until the player releases the button. It +# is intended for use where a player might want to see the whole screen +# and not have things hidden by the ATS windows. Default: false +# :hide_button - If :graphic_novel is true, this is the button that hides the +# ATS windows when pressed. Default: Input::F5 +# :gn_press_or_toggle - If :graphic_novel is true, this determines whether the +# player has to hold down the :hide_button or whether it acts as a toggle. +# true => press; false => toggle. Default: true +# :filters - This is a hash holding user-defined replacements for special +# codes. This is the hash that is accessed when using the \f[x] message +# code and is useful, for instance, if you have a character choose their +# sex at the start of the game. Then you can set the filters to the +# appropriate pronoun and use that and not have to have separate text +# boxes just to differentiate between when people say he or her. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :message_x - This is the x coordinate of the message window. If it is set to +# -1, then it will be centred. If not, it will be directly set to that +# coordinate of the screen. Default: -1 +# :message_y - This is the y coordinate of the message window. If it is set to +# -1, then it will abide by the normal way of setting position through +# the top, middle, bottom option in the text window. If it is not -1, +# then it will be directly set to that coordinate. Default: -1 +# :wlh - the line height of lines in the message window. Default: 24 +# :battle_wlh - the line height of lines in the message window during battle. +# This should always be 24 unless you have a good reason. Default: 24 +# :do_not_obscure - When using default positioning, this will choose a +# different position setting if the current one is over any of the +# characters included in :obscure_characters +# :obscure_characters - If :do_not_obscure is true and the message is being set +# using default positioning, then the message box will be choose override +# the chosen position if the chosen position obscures one or more of the +# characters in this array and another position would obscure less. The +# player is identified by 0 and any number greater than that refers to +# the event with that ID. When setting up the default, it is recommended +# that you only include the player and set others only on a map by map +# basis. Default: [0] +# :obscure_buffer - this is how much space from the bottom of the character you +# want to avoid obscuring. It should be set to the height of the tallest +# character you want to avoid obscuring. Default: 32 +# :fit_window_to_text - If this is true, then the window will be made to be +# just wide enough to contain the longest line and just tall enough to +# contain either all the lines up to :max_lines. Default: false +# :message_width - This is how many pixels wide the message window is. +# Default: 544 +# :message_height - This is how many pixels tall the message window is. +# Default: 128 +# :message_opacity - This is the opacity of the window. Default: 255 +# :message_backopacity - This is the opacity of the background of the +# windowskin. Default: 200 +# :message_windowskin - This is the windowskin that this window uses. It must +# be a graphic located in the System folder. Default: "Window" +# :message_fontcolour - This is the default colour of the font that is used +# everytime a new page is made. It can either refer to the windowskin +# palette (0-31) or be [red, green, blue] array. Default: 0 +# :message_fontname - This is the default font of the window. It can either +# refer to a single font or to a prioritized array of fonts, where the +# first one is used unless that font is not installed, and so on. +# Default: ["Verdana", "Arial", "Courier New"] +# :message_fontsize - This is the default size of the font. Default: 20 +# :message_fontalpha - This is the default opacity of the text. Default: 255 +# :message_dim - The graphic to use when selecting the "Dim" option. +# Default: "MessageBack" +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :face_x - The x coordinate of the face box. If set to -1, the x-coordinate +# will be automatically positioned. If not, it will be directly set to +# this value. Default: -1 +# :face_y - The y coordinate of the face box. If set to -1, the y-coordinate +# will be automatically positioned. If not, it will be directly set to +# this value. Default: -1 +# :face_z - If this is >0, it will show up above the message window. If less +# than 0, it will show up below the message window. Default: 10 +# :face_side - When :face_x is -1, this determines which side of the message +# box the face shows up on. true => Left; false => Right. Default: true +# :face_offset_x - When :choice_x is -1, this is added to the x position. +# Default: 0 +# :face_offset_y - When :choice_y is -1, this is added to the y position. +# Default: 0 +# :face_width - This determines the width of the face. When 0, it will show the +# whole width of the face, even if using single face graphics. If you want +# to show only a portion of the face, then set it directly. Default: 0 +# :face_height - This determines the height of the face. When 0, it will show +# the whole height of the face, even if using single face graphics. If +# you want to show only part of the face, then set it directly. Default: 0 +# :face_mirror - When true, the face will be drawn mirrored. Default: false +# :face_opacity - This is the opacity of the face. Default: 255 +# :face_blend_type - This is the blend type of the face sprites. 0 => normal; +# 1 => addition; 2 => subtraction. Default: 0 +# :face_fadein - When this is true, the face will fadein when starting a +# message instead of just automatically appearing. Default: false +# :face_fade_speed - If :face_fadein is true, this is how much the opacity will +# change every frame. Default: 10 +# :face_scroll_x - Option to scroll the face in horizontally. Default: false +# :face_scroll_y - Option to scroll the face in vertically. Default: false +# :face_scroll_speed - If either :face_scroll_x or :face_scroll_y are true, +# this is the number of pixels per frame it scrolls in at. +# :animate_faces - When this is true, specially labelled face files will +# animate. Face files that have ![x] in the name will take the first x +# faces in the file and animate between them. Alternatively, it can take +# the face of the same index from separate face files that are sequenced +# by suffixes _1, _2, etc... Default: true +# :letters_per_face - when :animate_faces is true, this is how many letters to +# draw between rotating the faces. +# :face_window - If true, the face will be framed by a window. Default: false +# :face_window_opacity - If :face_window is true, this is the opacity of that +# window. Default: 255 +# :face_windowskin - If :face_window is true, this is the windowskin it uses. +# Default: "Window" +# :face_border_size - If :face_window is true, this is the size of the border. +# Default: 6 +# :face_dim - If using :face_window and dim, the sprite to stretch as a +# background for the face. Default: "MessageBack" +# :face_use_dim - The setting for using dim. If 0, it will never use dim. If 1, +# it will use the dim sprite if :face_window is true. If 2, it will use +# dim only when the message window is also using dim. Default: 0 +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :choice_text - This is a bracket for any choice text. It is useful if, for +# instance, you want particular codes to apply to every choice in a +# branch. You must include %s in the string, and that is where the actual +# choice text will show up, bracketed by whatever other codes you put in +# Default: " %s" (meaning every choice will be indented by four spaces) +# :disabled_choice_text - Same as above, except it only applies to choices that +# are disabled and is applied after :choice_text is. It is useful if you +# want to differentiate between disabled choices and non-disabled ones. +# Unlike :choice_text, %s doesn't have to be included and in that case all +# disabled choices would simply be replaced by this text. +# Default: "\\FA[128]%s\\FA[255]" (meaning disabled choices will be drawn +# at 128 opacity). +# :choicebox_text - The same as :choice_text, but this is what is applied when +# using a choicebox instead of :choice_text. Default: "%s" (no change) +# :choice_window - Whether or not choices will show up in a separate window. +# Default: false +# :choice_x - The x coordinate of the choice window. When this is -1, the x +# position will be set automatically in reference to the position of the +# message window (end of the side opposite the face). When anything else, +# the x position is directly set to that coordinate. Default: -1 +# :choice_y - The y coordinate of the choice window. Whwn this is -1, the y +# position is set automatically directly above the message window or +# below, if it goes off screen when placed above. Default: -1 +# :choice_offset_x - When :choice_x is -1, this is added to the auto x position. +# Default: -16 +# :choice_offset_y - When :choice_y is -1, this is added to the auto y position. +# Default: 16 +# :choice_width - This is the width of the choice window. When -1, it will set +# it to as wide as necessary to accomodate the longest line, up to a +# maximum of the screen width. Default: 192 +# :choice_height - This is the height of the choice window. When -1, it is set +# to the number of rows necessary to draw all choices, up to :max_lines. +# :column_max - When using :choice_window, the number of columns. Default: 1 +# :row_max - When :choice_height is -1, the max number of rows. Default: 4 +# :choice_spacing - When :column_max > 1, this is how much empty space is left +# between options on the same row. Default: 20 +# :choice_opacity - This is the opacity of the choice window. Default: 255 +# :choice_backopacity - This is the opacity of the background of the choice +# window. Default: 200 +# :choice_windowskin - This is the name of the System file to use as the +# windowskin for the choice window. Default: "Window" +# :choice_fontcolour - The default colour of text in the choice window. It can +# be from the windowskin palette or a [red, green, blue] array. Default: 0 +# :choice_fontname - The font for the choice window. If an array, it will take +# the first font in the array that the player has installed. +# Default: ["Verdana", "Arial", "Courier New"] +# :choice_fontsize - Default size of the font in the choice window. Default: 20 +# :choice_wlh - The vertical space of each row of the choice window. Default: 24 +# :choice_dim - If using :choice_window and dim, the sprite to stretch as a +# background for the choice branch. Default: "MessageBack" +# :choice_use_dim - The setting for using dim. If 0, it will never use dim. If +# 1, it will use the dim sprite if :choice_window is true. If 2, it will +# use dim only when the message window is also using dim. Default: 2 +# :choice_on_line - Whether to show the choice box adjacent to the message +# window or not. Doesn't apply if :choice_width is not directly set or if +# :fit_window_to_text is on and larger than :choice_width. Default: false +# :choice_opposite_face - When true, the side the choice is shown on is the +# opposite side the face is on. And vice versa. Default: true +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :choicehelp_x - The x coordinate of the choicehelp window. When this is -1, +# the x position is centred. Default: -1 +# :choicehelp_y - The y coordinate of the choicehelp window. Whwn this is -1, +# the y position is at the top of the screen unless it overlaps with the +# message window, in which case it goes to the bottom. Default: -1 +# :choicehelp_width - This is the width of the choicehelp window. When -1, it +# will set it to the width of the longest line, up to a maximum of the +# screen width. Default: -1 +# :choicehelp_height - This is the height of the choicehelp window. When -1, it +# is set to accomodate the greatest number of lines that any choice option +# requires: Default: -1 +# :choicehelp_center - Whether or not to centre the text vertically when the +# number of lines is smaller than the height of the window. Default: true +# :choicehelp_opacity - This is the opacity of the choicehelp window. +# Default: 255 +# :choicehelp_backopacity - This is the opacity of the background of the +# choicehelp window. Default: 200 +# :choicehelp_windowskin - This is the name of the System file to use as the +# windowskin for the choicehelp window. Default: "Window" +# :choicehelp_fontcolour - This is the default colour of text in the choicehelp +# window. It can either be from the windowskin palette or a +# [red, green, blue, alpha] array. Default: 0 +# :choicehelp_fontname - This is either a string or array containing the name +# of the font used in the choicehelp window. If an array, it will take +# the first font in the array that the player has installed. +# Default: ["Verdana", "Arial", "Courier New"] +# :choicehelp_fontsize - This is the default size of the font in the choicehelp +# window. Default: 20 +# :choicehelp_wlh - This is the vertical space required for each line of the +# choicehelp window. Default: 24 +# :choicehelp_dim - If using :choicehelp_window and dim, the sprite to stretch +# as a background for the choicehelp branch. Default: "MessageBack" +# :choicehelp_use_dim - The setting for using dim. If 0, it will never use dim. +# If 1, it will use the dim sprite if :choicehelp_window is true. If 2, +# it will use dim only when the message window is also using dim. +# Default: 2 +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :name_x - The x coordinate of the name window. When this is -1, the x +# coordinate is automatically placed on the same side the face would be +# if automatically positioned. Default: -1 +# :name_y - The y coordinate of the name window. When this is -1, the y +# coordinate is just above the message window, unless it goes off the +# screen, in which case it is placed just below. Default: -1 +# :name_offset_x - When :name_x is -1, this is added to the x position. +# Default: 16 +# :name_offset_y - When :name_y is -1, this is added to the y position. +# Default: 16 +# :name_opacity - This is the opacity of the name window. Default: 255 +# :name_backopacity - This is the opacity of the background of the name window. +# Default: 200 +# :name_windowskin - This is the name of the System file to use as the +# windowskin for the name window. Default: "Window" +# :name_border_size - The size of the windowskin border around the name. +# Default: 8 +# :name_wlh - This is the vertical space required for each line of the name +# window. Default: 24 +# :name_fontcolour - This is the default colour of text in the name window. It +# can either be from the windowskin palette or a [red, green, blue] +# array. Default: 0 +# :name_fontname - This is either a string or array containing the name of the +# font used in the name window. If an array, it will take the first font +# in the array that the player has installed. +# Default: ["Verdana", "Arial", "Courier New"] +# :name_fontsize - This is the default size of the font in the name window. +# Default: 20 +# :name_dim - If using dim, the sprite to stretch as a background for the name +# box. Default: "MessageBack" +# :name_use_dim - The setting for using dim. If 0, it will never use dim. +# If 1, it will use the dim sprite. If 2, it will use dim only when the +# message window is also using dim. Default: 2 +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :word_x - The x coordinate of the word window. When this is -1, the word +# window is centred, or to the left of the gold window if the gold window +# is visible and :word_y is -1. If not -1, it is directly set to that +# coordinate. Default: -1 +# :word_y - The y coordinate of the word window. When this is -1, the y +# coordinate is at the same position as the gold window, unless the gold +# window is visible and the word window is too wide to fit to the left of +# it, in which case it is directly above or below the gold window, +# depending on where it fits. If not -1, it is directly set to that +# coordinate. Default: -1 +# :word_width - The width of the word window. When -1, it is set to accomodate +# the longest line. Default: 160 +# :word_height - The height of the word window. When -1, it will be set to +# accomodate the number of lines. Default: -1 +# :word_opacity - This is the opacity of the word window. Default: 255 +# :word_backopacity - This is the opacity of the background of the word window. +# Default: 200 +# :word_windowskin - This is the word of the System file to use as the +# windowskin for the word window. Default: "Window" +# :word_wlh - This is the vertical space required for each line of the word +# window. Default: 24 +# :word_fontcolour - This is the default colour of text in the word window. It +# can either be from the windowskin palette or a [red, green, blue] +# array. Default: 0 +# :word_fontname - This is either a string or array containing the name of the +# font used in the word window. If an array, it will take the first font +# in the array that the player has installed. +# Default: ["Verdana", "Arial", "Courier New"] +# :word_fontsize - This is the default size of the font in the word window. +# Default: 20 +# :word_dim - If using dim, the sprite to stretch as a background for the word +# box. Default: "MessageBack" +# :word_use_dim - The setting for using dim. If 0, it will never use dim. +# If 1, it will use the dim sprite. If 2, it will use dim only when the +# message window is also using dim. Default: 2 +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~ats_next only codes~ +# The following codes can only be set on a message by message basis with +# ats_next or $game_message. ats_all and $game_ats does not work for these. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# :do_not_refresh - When this is set to true, the contents of the message +# window will not refresh until it is turned off. This means that text +# messages will be appended to all the previous messages even if there +# are other events in between, such as a Show Picture or Control +# Variables switch and so on... Note that you will not be able to change +# the face either without forcibly setting a new page. To turn it off, +# you need to use a ats_next (:do_not_refresh, false) right before the +# last message that you want appended. I do not recommend doing anything +# too fancy with this; the idea is to permit you to do small things, like +# Showing a picture, turning a switch on or off or doing some stuff with +# conditional branches in between drawing letters. It's not designed or +# tested for much of anything else. +# :character - This is the same idea as the \oc, \uc, etc... character +# positioning codes. This allows you to place it prior to opening the +# window however. Set this value to the ID of the character you want to +# position it in reference to (0 => Player, >1 => Event with that ID. +# :char_ref - This is how you want to place it. 0 => Over; 1 => Left; +# 2 => Under; 3 => Right; 4 => Over if fits, otherwise under. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# You can set default values for all properties starting at line 766. +#============================================================================== + +#============================================================================== +# ** Game_ATS +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This class holds all of the default data for the ATS +#============================================================================== + +class Game_ATS + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # EDITABLE REGION + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + MAX_LINES = 4 # See line 376. + MESSAGE_SPEED = 0 # See line 378. + SKIP_DISABLED = false # See line 380. + APPEND_TEXT = false # See line 381. + APPEND_CHOICE = true # See line 383. + SCROLLING = true # See line 387. + SCROLL_SPEED = 2 # See line 389. + SCROLL_SHOW_ARROWS = true # See line 391. + SCROLL_AUTOPAUSE = false # See line 394. + SCROLL_REVIEW = true # See line 396. + SCROLL_BY_PAGE = false # See line 399. + PARAGRAPH_FORMAT = true # See line 400. + JUSTIFIED_TEXT = false # See line 403. + LETTER_SOUND = false # See line 406. + LETTER_SE = "Open1", 40 # See line 408. + LETTERS_PER_SE = 3 # See line 410. + RANDOM_PITCH = 100..100 # See line 413. + SPEECH_TAG_INDEX = -1 # See line 418. + # SPEECH_TAG_GRAPHICS # See line 422. + SPEECH_TAG_GRAPHICS = ["Speech Tag 1", "Speech Tag 2", "Thought Tag 1"] + START_SOUND = false # See line 426. + START_SE = "Chime2" # See line 428. + FINISH_SOUND = false # See line 430. + FINISH_SE = "Chime1" # See line 432. + PAUSE_SOUND = false # See line 434. + PAUSE_SE = "Decision2" # See line 436. + TERMINATE_SOUND = false # See line 438. + TERMINATE_SE = "Cancel" # See line 440. + MOVE_WHEN_VISIBLE = false # See line 442. + GRAPHIC_NOVEL = false # See line 447. + HIDE_BUTTON = Input::F5 # See line 452. + GN_PRESS_OR_TOGGLE = true # See line 454. + FILTERS = { # See line 457. + 'ATS' => '\c[1]Advanced Text System\c[0], Version 3.0', + 0 => 'Numbered filters work too', + } + FILTERS.default = "" # <- Do not touch + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + MESSAGE_X = -1 # See line 464. + MESSAGE_Y = -1 # See line 467. + WLH = 24 # See line 471. + BATTLE_WLH = 24 # See line 472. + DO_NOT_OBSCURE = false # See line 474. + OBSCURE_CHARACTERS = [0] # See line 477. + OBSCURE_BUFFER = 32 # See line 485. + FIT_WINDOW_TO_TEXT = false # See line 488. + MESSAGE_WIDTH = 544 # See line 491. + MESSAGE_HEIGHT = 128 # See line 493. + MESSAGE_OPACITY = 255 # See line 495. + MESSAGE_BACKOPACITY = 200 # See line 496. + MESSAGE_WINDOWSKIN = "Window" # See line 498. + MESSAGE_FONTCOLOUR = 0 # See line 500. + MESSAGE_FONTNAME = ["Verdana", "Arial", "Courier New"] # See line 503. + MESSAGE_FONTSIZE = 20 # See line 507. + MESSAGE_FONTALPHA = 255 # See line 508. + MESSAGE_DIM = "MessageBack" # See line 509. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + FACE_X = -1 # See line 512. + FACE_Y = -1 # See line 515. + FACE_Z = 10 # See line 518. + FACE_SIDE = true # See line 520. + FACE_OFFSET_X = 0 # See line 522. + FACE_OFFSET_Y = 0 # See line 524. + FACE_WIDTH = 0 # See line 526. + FACE_HEIGHT = 0 # See line 529. + FACE_MIRROR = false # See line 532. + FACE_OPACITY = 255 # See line 533. + FACE_BLEND_TYPE = 0 # See line 534. + FACE_FADEIN = false # See line 536. + FACE_FADE_SPEED = 10 # See line 538. + FACE_SCROLL_X = false # See line 540. + FACE_SCROLL_Y = false # See line 541. + FACE_SCROLL_SPEED = 12 # See line 542. + ANIMATE_FACES = true # See line 544. + LETTERS_PER_FACE = 6 # See line 545. + FACE_WINDOW = false # See line 551. + FACE_WINDOW_OPACITY = 255 # See line 552. + FACE_WINDOWSKIN = "Window" # See line 554. + FACE_BORDER_SIZE = 6 # See line 556. + FACE_DIM = "MessageBack" # See line 558. + FACE_USE_DIM = 0 # See line 560. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CHOICE_TEXT = " %s" # See line 564. + DISABLED_CHOICE_TEXT = "\\FA[128]%s\\FA[255]" # See line 569. + CHOICEBOX_TEXT = "%s" # See line 576. + CHOICE_WINDOW = false # See line 578. + CHOICE_X = -1 # See line 580. + CHOICE_Y = -1 # See line 584. + CHOICE_OFFSET_X = -16 # See line 587. + CHOICE_OFFSET_Y = 16 # See line 589. + CHOICE_WIDTH = 192 # See line 591. + CHOICE_HEIGHT = -1 # See line 594. + COLUMN_MAX = 1 # See line 596. + ROW_MAX = 4 # See line 597. + CHOICE_SPACING = 20 # See line 598. + CHOICE_OPACITY = 255 # See line 600. + CHOICE_BACKOPACITY = 200 # See line 601. + CHOICE_WINDOWSKIN = "Window" # See line 603. + CHOICE_FONTCOLOUR = 0 # See line 605. + CHOICE_FONTNAME = ["Verdana", "Arial", "Courier New"] # See line 607. + CHOICE_FONTSIZE = 20 # See line 610. + CHOICE_WLH = -1 # See line 611. + CHOICE_DIM = "MessageBack" # See line 612. + CHOICE_USE_DIM = 2 # See line 614. + CHOICE_ON_LINE = false # See line 617. + CHOICE_OPPOSITE_FACE = true # See line 620. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CHOICEHELP_X = -1 # See line 623. + CHOICEHELP_Y = -1 # See line 625. + CHOICEHELP_WIDTH = 544 # See line 628. + CHOICEHELP_HEIGHT = -1 # See line 631. + CHOICEHELP_CENTER = true # See line 634. + CHOICEHELP_OPACITY = 255 # See line 636. + CHOICEHELP_BACKOPACITY = 200 # See line 638. + CHOICEHELP_WLH = -1 # See line 640. + CHOICEHELP_WINDOWSKIN = "Window" # See line 642. + CHOICEHELP_FONTCOLOUR = 0 # See line 645. + CHOICEHELP_FONTNAME = ["Verdana", "Arial", "Courier New"] # See line 649. + CHOICEHELP_FONTSIZE = 20 # See line 651. + CHOICEHELP_DIM = "MessageBack"# See line 653. + CHOICEHELP_USE_DIM = 2 # See line 655. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + NAME_X = -1 # See line 660. + NAME_Y = -1 # See line 663. + NAME_OFFSET_X = 16 # See line 666. + NAME_OFFSET_Y = 16 # See line 668. + NAME_OPACITY = 255 # See line 670. + NAME_BACKOPACITY = 200 # See line 671. + NAME_WINDOWSKIN = "Window" # See line 673. + NAME_BORDER_SIZE = 8 # See line 675. + NAME_WLH = -1 # See line 677. + NAME_FONTCOLOUR = 0 # See line 679. + NAME_FONTNAME = ["Verdana", "Arial", "Courier New"] # See line 682. + NAME_FONTSIZE = 20 # See line 686. + NAME_DIM = "MessageBack" # See line 688. + NAME_USE_DIM = 2 # See line 690. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + WORD_X = -1 # See line 694. + WORD_Y = -1 # See line 698. + WORD_WIDTH = 160 # See line 704. + WORD_HEIGHT = -1 # See line 706. + WORD_OPACITY = 255 # See line 708. + WORD_BACKOPACITY = 200 # See line 709. + WORD_WINDOWSKIN = "Window" # See line 711. + WORD_WLH = -1 # See line 713. + WORD_FONTCOLOUR = 0 # See line 715. + WORD_FONTNAME = ["Verdana", "Arial", "Courier New"] # See line 718. + WORD_FONTSIZE = 20 # See line 722. + WORD_DIM = "MessageBack" # See line 724. + WORD_USE_DIM = 2 # See line 726. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # END EDITABLE REGION + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ATS_2 = false + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Public Instance Variables + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # For each constant, create accessor + for name in self.class.constants + # Run the script + attr_accessor name.downcase.to_sym + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize + reset + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Reset + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def reset + constants = Game_ATS.constants + constants.each do |name| + method_name = "#{name.downcase}=" # Build the setter method name + value = Game_ATS.const_get(name) # Get the constant value from Game_ATS + self.send(method_name.to_sym, value) # Send the method call dynamically + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Sound Effect + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_sound_effect(settings) + return settings if settings.is_a?(RPG::SE) + + settings = [settings] if settings.is_a?(String) + + settings[1] = 80 if !settings[1] # Ensure the second element is set + + return RPG::SE.new(*settings) # Call RPG::SE.new with the unpacked arguments + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Overwrite SE methods * Thanks Zeriab + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for method in ["letter", "terminate", "pause", "start", "finish"] + # Run the script + eval("def #{method}_se= (*args); @#{method}_se = set_sound_effect (*args); end") + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Random Pitch + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def random_pitch= (val) + @random_pitch = val.is_a? (Integer) ? self.letter_se.pitch..val : val + end +end + +#============================================================================== +# ** Game_Message +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# new attr_accessor - all Game_ATS accessors; char_ref; character; +# appending_text; choices; disabled_choices; help_choices; skip_choices; +# override_run +# aliased methods - initialize; clear; busy +# new methods - convert_special_characters; perform_substitution; +# perform_conversion; play_se; random_pitch= +#============================================================================== + +class Game_Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Public Instance Variables + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # For each ATS constant, create accessor + for name in (Game_ATS.constants) do attr_accessor name.downcase.to_sym end + # Non Game_ATS variables + attr_accessor :char_ref + attr_accessor :character + attr_accessor :appending_text + attr_accessor :choices + attr_accessor :disabled_choices + attr_accessor :skip_choices + attr_accessor :help_choices + attr_accessor :override_run + attr_accessor :highlight + attr_accessor :underline + attr_accessor :do_not_refresh + attr_accessor :do_not_start + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malgb_advtx_inliz_2fk9 initialize unless $@ + + def initialize(*args) + @do_not_refresh = false + # malg_ats3_clr_msg_9lo1(*args) # Call your method without a space before `(*args)` + malgb_advtx_inliz_2fk9(*args) # Call the original `initialize` method (aliased) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Clear + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malg_ats3_clr_msg_9lo1 clear unless $@ + def clear (*args) + @do_not_start = @do_not_refresh + @texts = [] + @choices = [] + @disabled_choices = [] + @skip_choices = [] + @help_choices = [] + @choice_start = 99 + @choice_max = 0 + @choice_cancel_type = 0 + @choice_proc = nil + @ignored_codes = [] + return if @do_not_start + malg_ats3_clr_msg_9lo1 (*args) # Run Original Method + @alignment = 0 + @appending_text = false + @character = -1 + @char_ref = 0 + @override_run = false + @highlight = -1 + @underline = false + # Set ATS variables + (Game_ATS.constants).each { |name| + self.send ("#{name.downcase}=".to_sym, $game_ats.send (name.downcase.to_sym)) + } + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Busy? + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malbr_busy_ats3_8ik2 busy unless $@ + def busy (*args) + return false if @appending_text + return malbr_busy_ats3_8ik2 (*args) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Convert Special Characters + # text : the text to convert + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def convert_special_characters (text) + return if text == nil + @ignored_codes.clear + # Remove Ignore code strings + text.gsub! (/\\\$(.*?)\/\$/i) { + @ignored_codes.push ($1.to_s) + "\x0a<#{@ignored_codes.size - 1}>" + } + # Get substitutions + text = perform_substitution (text) + text = perform_conversion (text) + text.gsub! (/\\\\/) { "\\" } + text.gsub! (/\x0a<(\d+)>/) { @ignored_codes[$1.to_i] } # Recover Protected strings + return text + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Convert Substitution Codes + # text : the text to convert + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def perform_substitution (text) + # Switch conditional text + text.gsub! (/\\V\[(\d+)\]/i) { $game_variables[$1.to_i] } # Variable + text.gsub! (/\\PID\[(\d+)\]/i) { $game_party.members[($1.to_i % $game_party.members.size)].id } # Party Member X + text.gsub! (/\\P\[(\d+)\]/i) { $game_party.members[($1.to_i % $game_party.members.size)].id } # Party Member X + text.gsub! (/\\V\[(\d+)\]/i) { $game_variables[$1.to_i] } # Variable + # FILTERS + text.gsub!(/\\F\[["'](.+?)["']\]/i) { Game_ATS::FILTERS[$1.to_s] } + text.gsub!(/\\F\[(.+?)\]/i) { Game_ATS::FILTERS[$1.to_i] } + # Party ID to Actor ID + text.gsub!(/\\N\[([0-9]+)\]/i) { $game_actors[$1.to_i].name rescue ""} # Actor Name + # New Codes + text.gsub! (/\\AC\[(\d+)\]/i) { $game_actors[$1.to_i].class.name rescue "" } # Actor Class + text.gsub! (/\\VOCAB\[(\w+)\]/i) { Vocab.send ($1.downcase) rescue "" } + # Actor, Item, Weapon, Armor, Skill Stats + data_arrays = [$game_actors, $data_items, $data_weapons, $data_armors, + $data_skills, $data_states, $data_enemies] + regexp_array = ["ACTOR_", "I_", "W_", "A_", "S_", "T_", "ENEMY_"] + for i in 0...regexp_array.size + regexp = regexp_array[i] + data = data_arrays[i] + if i != 0 + text.gsub! (/\\#{regexp}N\[(\d+)\]/i) { + id = $1.to_i + data[id].note[/\\MSG\[(.+?)\]MSG\//i].nil? ? data[id].note : $1.to_s.gsub (/\n/) { "" } # Remove \n + } + end + # Item Stats + text.gsub! (/\\#{regexp}([^\[]+?)\[(\d+)\]/i) { |match| data[$2.to_i].send ($1.downcase).to_s rescue match } + end + text.gsub! (/\\MAP\[(\d+)\]/i) { (load_data ("Data/MapInfos.rvdata"))[$1.to_i].name rescue "" } + text.gsub! (/\\MAP/i) { (load_data ("Data/MapInfos.rvdata"))[$game_map.map_id].name rescue "" } + text.gsub! (/\\NL\[(\d+)\]/i){ $data_system.elements[$1.to_i] rescue "" } + text.gsub! (/\\NC\[(\d+)\]/i) { $data_classes[$1.to_i].name rescue "" } # Class Name + text.gsub! (/\\NE\[(\d+)\]/i) { $game_map.events[$1.to_i].name rescue "" } # Event Name + text.gsub! (/\\NM\[(\d+)\]/i) { $data_enemies[$1.to_i].name rescue "" } # Monster Name + text.gsub! (/\\NI\[(\d+)\]/i) { $data_items[$1.to_i].name rescue "" } # Item Name + text.gsub! (/\\NW\[(\d+)\]/i) { $data_weapons[$1.to_i].name rescue "" } # Weapon Name + text.gsub! (/\\NA\[(\d+)\]/i) { $data_armors[$1.to_i].name rescue "" } # Armor Name + text.gsub! (/\\NS\[(\d+)\]/i) { $data_skills[$1.to_i].name rescue "" } # Skill Name + text.gsub! (/\\NT\[(\d+)\]/i) { $data_states[$1.to_i].name rescue "" } # State Name + text.gsub! (/\\NP\[(\d+)\]/i) { $game_party.members[($1.to_i % $game_party.members.size)].name } # Party Name + text.gsub! (/\\NV\[(\d+)\]/i) { $data_system.variables[$1.to_i] rescue "" } # Variable Name + text.gsub! (/\\NSW\[(\d+)\]/i){ $data_system.switches[$1.to_i] rescue "" } # Switch Name + text.gsub! (/\\PI\[(\d+)\]/i) { $data_items[$1.to_i].price.to_s rescue "" } # Item Price + text.gsub! (/\\PW\[(\d+)\]/i) { $data_weapons[$1.to_i].price.to_s rescue "" } # Weapon Price + text.gsub! (/\\PA\[(\d+)\]/i) { $data_armors[$1.to_i].price.to_s rescue "" } # Armor Price + text.gsub! (/\\DI\[(\d+)\]/i) { $data_items[$1.to_i].description rescue "" } # Item Description + text.gsub! (/\\DW\[(\d+)\]/i) { $data_weapons[$1.to_i].description rescue "" } # Weapon Description + text.gsub! (/\\DA\[(\d+)\]/i) { $data_armors[$1.to_i].description rescue "" } # Armor Description + text.gsub! (/\\DS\[(\d+)\]/i) { $data_skills[$1.to_i].description rescue "" } # Skill Description + text.gsub! (/\\I#\[(\d+)\]/i) { $game_party.item_number ($data_items[$1.to_i]) } # Item Number + text.gsub! (/\\W#\[(\d+)\]/i) { $game_party.item_number ($data_weapons[$1.to_i]) } # Weapon Number + text.gsub! (/\\A#\[(\d+)\]/i) { $game_party.item_number ($data_armors[$1.to_i]) } # Armor Number + text.gsub! (/\\S\!<(\d+),(.+?)>/i) { $game_switches[$1.to_i] ? "" : $2.to_s } + text.gsub! (/\\S<(\d+),(.+?)>/i) { $game_switches[$1.to_i] ? $2.to_s : "" } + text.gsub! (/\\#\{(.+?)\}#/im) { (eval ($1.to_s)).to_s rescue "" } + return text.sub! (/\\RESUB/i, "") != nil ? perform_substitution (text) : text + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Perform Conversion + # text : the text to convert + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def perform_conversion (text) + text.gsub! (/\\C\[(\d+)\]/i) { "\x01<#{$1}>" } # Colour + text.gsub! (/\\C\[#([\dABCDEF]{6,6})\]/i) { "\x01<##{$1}>"} # Colour Hex + text.gsub! (/\\G/i) { "\x02" } # Gold Window + text.gsub! (/\\\./) { "\x03<15>" } # Wait 15 frames + text.gsub! (/\\\|/) { "\x03<60>" } # Wait 60 frames + text.gsub! (/\\W\[(\d+)\]/i) { "\x03<#{$1}>" } # Wait x frames + text.gsub! (/\\!/) { "\x04" } # Wait for Input + text.gsub! (/\\@@/) { "\x05<0>" } # Show Fast ON + text.gsub! (/\/@@/) { "\x05<1>" } # Show Fast OFF + text.gsub! (/\\@/) { "\x06<0>" } # Show Line Fast ON + text.gsub! (/\/@/) { "\x06<1>" } # Show Line Fast OFF + text.gsub! (/\\\^/) { "\x07" } # Skip Pause + text.gsub! (/\\>/) { "\x08<-1>" } # Message speed- 1 + text.gsub! (/\\" } # Message speed + 1 + text.gsub! (/\\[Ss]\[(=?-?\d+)\]/i) { "\x08<#{$1}>" } # Message speed + x + text.gsub! (/\\%/) { "\xb0<0>" } # Enable Skip + text.gsub! (/\/%/) { "\xb0<1>" } # Disable Skip + # Animation and Balloons + text.gsub! (/\\ANI\[(\d+),\s*(\d+)\]/i) { "\x0b<#{$1},#{$2},0>" } # Animation + text.gsub! (/\\BLN\[(\d+),\s*(\d+)\]/i) { "\x0b<#{$1},#{$2},1>" } # Balloon + # Position to Character + text.gsub! (/\\OC\[(\d+)\]/i) { "\x0c<#{$1},0>" } # Over Character + text.gsub! (/\\LC\[(\d+)\]/i) { "\x0c<#{$1},1>" } # Left of Character + text.gsub! (/\\UC\[(\d+)\]/i) { "\x0c<#{$1},2>" } # Under Character + text.gsub! (/\\RC\[(\d+)\]/i) { "\x0c<#{$1},3>" } # Right of Character + text.gsub! (/\\E\[(\d+)\]/i) { "\x0c<#{$1},4>" } # Under if over doesn't work + text.gsub! (/\\SE\[(.+?)\]/i) { "\x1b<#{$1},SE>" } # Play Sound Effect + text.gsub! (/\\ME\[(.+?)\]/i) { "\x1b<#{$1},ME>" } # Play Musical Effect + # Show Icons + text.gsub! (/\\IC?O?N?\[(\d+)\]/i) { "\x0d<#{$1}>" } # Show Icon X + text.gsub! (/\\IIC?O?N?\[(\d+)\]/i) { "\x0d<#{$data_items[$1.to_i].icon_index}>" rescue "" } # Item Icon + text.gsub! (/\\WIC?O?N?\[(\d+)\]/i) { "\x0d<#{$data_weapons[$1.to_i].icon_index}>" rescue "" } # Weapon Icon + text.gsub! (/\\AIC?O?N?\[(\d+)\]/i) { "\x0d<#{$data_armors[$1.to_i].icon_index}>" rescue "" } # Armor Icon + text.gsub! (/\\SIC?O?N?\[(\d+)\]/i) { "\x0d<#{$data_skills[$1.to_i].icon_index}>" rescue "" } + text.gsub! (/\\TIC?O?N?\[(\d+)\]/i) { "\x0d<#{$data_states[$1.to_i].icon_index}>" rescue "" } + text.gsub! (/\\[Bb]/) { "\x0e<0>" } # Bold ON + text.gsub! (/\/[Bb]/) { "\x0e<1>" } # Bold OFF + text.gsub! (/\\[Ii]/) { "\x0f<0>" } # Italics ON + text.gsub! (/\/[Ii]/) { "\x0f<1>" } # Italics OFF + text.gsub! (/\\[Ss]/) { "\x10<0>" } # Shadow ON + text.gsub! (/\/[Ss]/) { "\x10<1>" } # Shadow OFF + text.gsub! (/\\[Uu]/) { "\x11<0>" } # Underline ON + text.gsub! (/\/[Uu]/) { "\x11<1>" } # Underline OFF + text.gsub! (/\\HL\[(-?\d+)\]/i) { "\x12<#{$1}>" } # Higlight X + text.gsub! (/\/HL/i) { "\x12<-1>" } # Highlight OFF + text.gsub! (/\\FN\[(.+?)\]/i) { "\x13<#{$1}>" } # Font Name + text.gsub! (/\\FS\[(\d+)\]/i) { "\x14<#{$1}>" } # Font Size + text.gsub! (/\\FA\[(\d+)\]/i) { "\x15<#{$1}>" } # Font Alpha + text.gsub! (/\\LB/i) { "\x16" } # Force Line Break + text.gsub! (/\\AF\[(\d+)\]/i) { "\x17<#{$1.to_s}>" } # Use Actor Face + text.gsub! (/\\LEFT/i) { "\x18<0>" } # Align Left + text.gsub! (/\\L/i) { "\x18<0>" } # Align Left + text.gsub! (/\\CENTRE/i) { "\x18<1>" } # Align Centre + text.gsub! (/\\CE?NTE?R/i) { "\x18<1>" } # Align Centre + text.gsub! (/\\C/i) { "\x18<1>" } # Align Centre + text.gsub! (/\\RI?GHT/i) { "\x18<2>" } # Align Right + text.gsub! (/\\R/i) { "\x18<2>" } # Align Right + text.gsub! (/\\PB/i) { "\x1d" } # Force Page Break + text.gsub! (/(\x1d\s*)\x00/i) { $1.to_s } # If force page, remove x00 code + text.gsub! (/\\T/i) { "\x09" } # Tab + text.gsub! (/\\X\[(\d+)\]/i) { "\xb1<#{$1}>" } # Content X + # Position Direct Setting + text.gsub! (/\\[PM]XY\[(-?\d+),\s*(-?\d+)\]/im) { "\x1f<0,#{$1.to_s},#{$2.to_s}>" } + # Face Direct Setting + text.gsub! (/\\FXY\[(-?\d+),\s*(-?\d+)\]/im) { "\x1f<1,#{$1.to_s},#{$2.to_s}>" } + # Name Direct Setting + text.gsub! (/\\NXY\[(-?\d+),\s*(-?\d+)\]/im) { "\x1f<2,#{$1.to_s},#{$2.to_s}>" } + text.gsub! (/\\#\!\{(.+?)\}#/im) { "\x7f{#{$1}#}" } + text.gsub! (/\\NB\[(.*?)\]/im) { "\x19{#{$1}}" } # Name Window + text.gsub! (/\/NB/i) { "\x19{}"} + text.gsub! (/\\NAME\[(.*?)\]/im) { "\x19{#{$1}}" } # Name Window + text.gsub! (/\\WB\[(.*?)\]/im) { "\x1a{#{$1}}" } # Word Box + text.gsub! (/\/WB/i) { "\x1a{}" } + return text + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Play SE + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def play_se (se) + # Avoid FileErrors + begin + se.play + rescue + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Choice Texts + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def choice_text= (string) + if string.is_a? (String) + @choice_text = convert_special_characters (string) + @choice_text += "%s" if @choice_text[/%s/] == nil + else + @choice_text = "%s" + end + end + def choicebox_text= (string) + if string.is_a? (String) + @choicebox_text = convert_special_characters (string) + @choicebox_text += "%s" if @choicebox_text[/%s/] == nil + else + @choicebox_text = "%s" + end + end + def disabled_choice_text= (string) + @disabled_choice_text = string.is_a? (String) ? convert_special_characters (string) : "%s" + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Random Pitch + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def random_pitch= (val) + @random_pitch = val.is_a? (Integer) ? self.letter_se.pitch..val : val + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Overwrite SE methods * Thanks Zeriab + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for method in ["letter", "terminate", "pause", "start", "finish"] + # Run the script + eval("def #{method}_se= (*args); @#{method}_se = $game_ats.set_sound_effect (*args); end") + end +end + +#============================================================================== +# ** Game_Event +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# new method - name +#============================================================================== + +class Game_Event + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Name + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def name + return @event.name + end +end + +#============================================================================== +# ** Game_Player +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# aliased methods - movable?; move_by_input +#============================================================================== + +class Game_Player + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Move By Input + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias mabr_adtxs3_inpmv_6yw2 move_by_input unless $@ + def move_by_input (*args) + $game_message.override_run = $game_message.move_when_visible + mabr_adtxs3_inpmv_6yw2 (*args) # Run Original Method + $game_message.override_run = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Check if Player can Move + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malg_advtxt3_movble_5sa2 movable? unless $@ + def movable? (*args) + true_visible = $game_message.visible + $game_message.visible = false if $game_message.move_when_visible + check = malg_advtxt3_movble_5sa2 (*args) # Run Original Method + $game_message.visible = true_visible + return check + end +end + +#============================================================================== +# ** Game_Interpreter +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# overwritten methods - command_403; setup_choices; setup_num_input +# aliased methods - command_101; command_102; running? +# new methods - interpret_choices; choice_plus; choice_switch; choice_help; +# ats_next; ats_all +#============================================================================== + +class Game_Interpreter + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Show Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modalg_ats_apnd_txt_show_94b command_101 unless $@ + def command_101 (*args) + value = modalg_ats_apnd_txt_show_94b (*args) + if $game_message.append_text && @list[@index].code == 101 && @list[@index].parameters == @params + $game_message.appending_text = true + value = command_101 (*args) + $game_message.appending_text = false + end + return value + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Show Choices + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malgr_ats3_shwchc_4ac2 command_102 unless $@ + def command_102 (*args) + if @list[@index].indent > 1000 + @list[@index - 1].indent -= 1000 + @list[@index].indent -= 1000 + return command_skip + else + return malgr_ats3_shwchc_4ac2 (*args) # Run Original Method + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * When [Cancel] + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def command_403 + if @branch[@indent] == 999 # If canceling choice + @branch.delete(@indent) # Erase branching data + return true # Continue + else # If doesn't match condition + return command_skip # Command skip + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Setup Choices + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def setup_choices (params) + params = interpret_choices (Marshal.load (Marshal.dump (params))) + $game_message.choice_start = $game_message.texts.size + $game_message.choice_max = params[0].size + for s in params[0] + $game_message.choices.push(s) + end + $game_message.texts.push ("\x1c") if $game_message.texts.empty? + $game_message.choice_cancel_type = params[1] + $game_message.choice_proc = Proc.new { |n| @branch[@indent] = n } + @index += 1 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Number Input Setup + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def setup_num_input(params) + if $game_message.texts.size < $game_message.max_lines || $game_message.scrolling + $game_message.num_input_variable_id = params[0] + $game_message.num_input_digits_max = params[1] + @index += 1 + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Interpret Choices + # params : the parameters of the initial 102 command. + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def interpret_choices (params) + # Collect all next choices as well. + ind = @index + 1 + choice_id = 0 + params[1] = 1000 if params[1] == 5 + choice_text = $game_message.choice_window ? $game_message.choicebox_text : $game_message.choice_text + loop do + # Advance to next choice code + while (@list[ind].indent % 1000) > @indent + ind += 1 # Advance index + end + case @list[ind].code + when 402 + choice_s = @list[ind].parameters[1].dup + ind += 1 + # Choice+ processing. + choice_s, ind = choice_plus (choice_s, ind) + # SOFF and SON processing + choice_s, delete, disable = choice_switch (choice_s) + choice_s, help = choice_help (choice_s) + if delete || choice_s.empty? + @list[ind - 1].parameters[0] = -1 + params[0].delete_at (choice_id) + else + $game_message.help_choices[choice_id] = help + choice_s = sprintf (choice_text, choice_s) + if disable + choice_s = sprintf ($game_message.disabled_choice_text, choice_s) + $game_message.disabled_choices.push (choice_id) + end + # Choice Skip? + choice_s.gsub! (/\x1e/i) { $game_message.skip_choices.push (choice_id); "" } + @list[ind - 1].parameters[0] = choice_id + params[0][choice_id] = choice_s + choice_id += 1 + end + when 403 + ind += 1 + when 404 + if !$game_message.append_choice || @list[ind + 1].code != 102 + @index = ind if $game_message.skip_choices.size == params.size + $game_message.help_choices.clear if $game_message.help_choices.uniq.size == 1 && $game_message.help_choices[0].empty? + break + end + p2 = @list[ind + 1].parameters.dup + @list[ind].indent += 1000 if @list[ind].indent < 1000 + @list[ind + 1].indent += 1000 if @list[ind + 1].indent < 1000 + params[1] = p2[1] == 5 ? 1000 : p2[1] == 0 ? params[1] : params[0].size + p2[1] + params[0].push (*p2[0]) + ind += 2 + else + break + end + end + return params + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Running? + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modral_atsys3_runn_5tg3 running? unless $@ + def running? (*args) + return false if $game_message.override_run + return modral_atsys3_runn_5tg3 (*args) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Choice + Processing + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def choice_plus (choice_s, ind) + # Choice + Processing + if @list[ind].code == 108 # Comment + string = @list[ind].parameters[0].dup + ind2 = ind + 1 + while @list[ind2].code == 408 + string += @list[ind2].parameters[0] + ind2 += 1 + end + string.gsub (/\\\+{(.+?)}/im) { choice_s += $1.to_s; "" } + choice_s.gsub! (/\n/) { "" } # Remove \n + end + return choice_s, ind + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Choice Switch + # s : the choice string + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def choice_switch (s) + del = false + dis = false + s.gsub! (/\\SOFF\[(\d+)\]/i) { del = true if $game_switches[$1.to_i]; "" } + s.gsub! (/\\SON\[(\d+)\]/i) { del = true if !$game_switches[$1.to_i]; "" } + if !del + s.gsub! (/\\D\[(\d+)\]/i) { dis = true if !$game_switches[$1.to_i]; "" } + s.gsub! (/\\D!\[(\d+)\]/i) { dis = true if $game_switches[$1.to_i]; "" } + s.gsub! (/\\SKIP/i) { "\x1e" } + s.gsub! (/\\PB/i) { "" } # Delete any page breaks. + $game_message.convert_special_characters (s) + s.gsub! (/\x16/) { "\x00" } # Force Line Break + end + return s, del, dis + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Choice Help + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def choice_help (choice_s) + help_text = "" + choice_s.gsub! (/\x1a{(.+?)}/im) { help_text += $1.to_s; "" } + return choice_s, help_text + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Change ATS parameter for next message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def ats_next (parameter, *args) + $game_message.send ("#{parameter}=".to_sym, *args) unless args.empty? + return $game_message.send (parameter) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Change ATS + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def ats_all (parameter, *args) + $game_ats.send ("#{parameter}=".to_sym, *args) unless args.empty? + $game_message.send ("#{parameter}=".to_sym, *args) unless args.empty? + return $game_message.send (parameter) + end +end + +#============================================================================== +# ** P_Formatter_ATS +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# A special formatter class designed to accomodate ATS message codes +#============================================================================== + +class P_Formatter_ATS + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Public Instance Variables + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + attr_accessor :bitmap + attr_reader :force_break + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (font = nil) + @bitmap = Bitmap.new (1, 1) + @bitmap.font = font if font + @curly_args_codes = ["\x19", "\x1a", "\x7f"] + @args_codes = ["\x01", "\x03", "\x05", "\x06", "\x08", "\x0b", + "\x0c", "\x0d", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13", "\x14", + "\x15", "\x17", "\x18", "\x1b", "\x1f", "\xb0", "\xb1"] + @no_args_codes = ["\x00", "\x02", "\x04", "\x07", "\x09", "\x16", "\x1c", + "\x1d", "\x1e"] + @force_break = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose Bitmap + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def dispose + @bitmap.dispose unless @bitmap.nil? || @bitmap.disposed? + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Format By Line + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def format_by_line (string, width) + @string = string + @max_width = width + @justify = $game_message.justified_text + return 0, 0, @justify if string.empty? + @line_width = 0 + @word_length = 0 + @w_draw_count = 0 + @l_draw_count = 0 + @curly_argument = 0 + @code_argument = 0 + # Set up + @line_space = 0 + @last_word = 0 + @break_loop = false + @last_space_counted = false + @force_break = false + i = 0 + while !@break_loop + if i >= @string.size + if @l_draw_count != 0 + @l_draw_count += 1 + @line_width += @bitmap.text_size (" ").width + end + @l_draw_count += @w_draw_count + @line_width += @word_length + next_line (i) + else + format_character (i) + i += 1 + end + end + return @line_space, @l_draw_count, @justify + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Format Character + # i : index of character to format, or the character + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def format_character (i) + character = @string[i, 1] + if @curly_argument > 0 + @curly_argument -= 1 if character == "}" + elsif @code_argument > 0 + @code_argument -= 1 if character == ">" + elsif @curly_args_codes.include? (character) + extract_curly_args_code (character, i) + elsif @args_codes.include? (character) + extract_args_code (character, i) + elsif @no_args_codes.include? (character) + extract_no_args_code (character, i) + elsif character == " " + next_word (i) + return if @break_loop + @line_width += @bitmap.text_size (" ").width + @l_draw_count += 1 + @last_space_counted = true + else # Regular Character + @word_length += @bitmap.text_size(character).width + @w_draw_count += 1 + if i == @string.size - 1 && @line_width + @word_length > @max_width + next_line (@last_word) + end + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Next Word + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def next_word (i) + if @line_width + @word_length > @max_width + if @line_width == 0 + @last_word = i + @line_width = @word_length + @l_draw_count = @w_draw_count + @word_length = 0 + @w_draw_count = 0 + end + next_line (@last_word) + end + @last_word = i + @line_width += @word_length + @l_draw_count += @w_draw_count + @word_length = 0 + @w_draw_count = 0 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Proceed to Next Line + # last_word : the index of the beginning of the previous word + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def next_line (last_word) + @justify = false if last_word == @string.size + @string[last_word, 1] = "\x00" + if @last_space_counted + @line_width -= @bitmap.text_size (" ").width + @l_draw_count -= 1 + end + # Calculates the blank space left to cover in the line + line_blank = @max_width - @line_width + @line_space = ( line_blank.to_f / [(@l_draw_count.to_f - 1.0), 1.0].max ) + @break_loop = true + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Extract Curly Argument Code + # code : the code to extract + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def extract_curly_args_code (code, index) + @curly_argument += 1 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Extract Argument Code + # code : the code to extract + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def extract_args_code (code, index) + case code + when "\x0d" # Icon + @w_draw_count += 1 + @word_length += 24 + when "\x0e" # Bold + @bitmap.font.bold = (@string[index + 2, 1].to_i == 0) + when "\x0f" # Italic + @bitmap.font.italic = (@string[index + 2, 1].to_i == 0) + when "\x10" # Shadow + @bitmap.font.shadow = (@string[index + 2, 1].to_i == 0) + when "\x13" # Font Name + bmp = @bitmap + bmp.font.name = bmp.font.name.to_ary if !bmp.font.name.is_a? (Array) + @string[index, @string.size - index][/<(.*?)>/] + bmp.font.name = ([$1.to_s] + bmp.font.name).uniq + when "\x14" # Font Size + @string[index, @string.size - index][/<(\d*)>/] + @bitmap.font.size = $1.to_i + when "\xb1" # Set Contents_X + @string[index, @string.size - index][/<(\d*)>/] + @word_length = $1.to_i + end + @code_argument += 1 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Extract No Argument Code + # code : the code to extract + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def extract_no_args_code (code, index) + if ["\x00", "\x1d"].include? (code) # New line or New Page + if @line_width + @word_length > @max_width + next_line (@last_word) + else + @line_width += @word_length + @l_draw_count += @w_draw_count + line_blank = @max_width - @line_width + @line_space = ( line_blank.to_f / [(@l_draw_count.to_f - 1.0), 1.0].max ) + @break_loop = true + @justify = false + @force_break = true + end + elsif code == "\x09" # Tab + next_word (index) + @line_width = $game_message.justified_text ? @line_width + 32 :((@line_width / 32) + 1)*32 + end + end +end + +#============================================================================== +# ** Sprite MessageFace +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This sprite displays face graphics +#============================================================================== + +class Sprite_MessageFace < Sprite_Base + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (face_file, face_index, viewport = nil) + super (viewport) + wdth, hght = $game_message.face_width, $game_message.face_height + face = Cache.face (face_file) + if face_file[/^\$/] != nil # SINGLE + wdth = face.width if wdth <= 0 + wdth = [face.width, wdth].min + hght = face.height if hght <= 0 + hght = [face.height, hght].min + rect = Rect.new((face.width-wdth) / 2, (face.height-hght) / 2, wdth, hght) + else + # Resize if face smaller than allotted size + wdth = face.width / 4 if wdth <= 0 + wdth = [face.width / 4, wdth].min + hght = face.height / 2 if hght <= 0 + hght = [face.height / 2, hght].min + rect = Rect.new(0, 0, wdth, hght) + rect.x = (face_index % 4) * wdth + ((face.width / 4) - wdth) / 2 + rect.y = (face_index / 4) * hght + ((face.height / 2) - hght) / 2 + end + self.bitmap = Bitmap.new (rect.width, rect.height) + self.bitmap.blt(0, 0, face, rect) + self.mirror = $game_message.face_mirror + self.blend_type = $game_message.face_blend_type + self.visible = false + self.opacity = $game_message.face_fadein ? 0 : $game_message.face_opacity + $game_message.face_width, $game_message.face_height = rect.width, rect.height + end +end + +#============================================================================== +# ** Sprite SpeechTag +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This sprite displays the tag when speaking +#============================================================================== + +class Sprite_SpeechTag < Sprite + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Public Instance Variable + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + attr_reader :speech_tag_index + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (viewport = nil, *args) + @border_sprite = Sprite.new (viewport) + super (viewport, *args) + @border_sprite.z = self.z + 1 + @bitmaps = [] + reset_graphic + self.visible = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def dispose (*args) + if self.bitmap + self.bitmap.dispose + @border_sprite.bitmap.dispose + end + @border_sprite.dispose + super (*args) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Reset Graphic + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def reset_graphic (index = $game_message.speech_tag_index) + return if @speech_tag_index == index + @speech_tag_index = index + return if index < 0 + begin + bitmap = Cache.system ($game_message.speech_tag_graphics[index]) + rescue + return + end + @bitmaps.each { |bmp| bmp.dispose unless bmp.disposed? } + @bitmaps.clear + dummy_bmp = Bitmap.new (bitmap.width / 2, bitmap.height / 2) + src_rect = Rect.new (0, 0, bitmap.width / 2, bitmap.height / 2) + for i in 0...4 + new_bmp = dummy_bmp.dup + src_rect.x, src_rect.y = (i % 2)*new_bmp.width, (i / 2)*new_bmp.height + new_bmp.blt (0, 0, bitmap, src_rect) + @bitmaps.push (new_bmp) + end + dummy_bmp.dispose + self.bitmap = @bitmaps[0] + @border_sprite.bitmap = @bitmaps[1] + self.back_opacity = $game_message.message_backopacity + self.opacity = $game_message.message_opacity + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Direction + # dir : 0 => Down; 2 => Up + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_direction (dir) + self.bitmap = @bitmaps[dir] + @border_sprite.bitmap = @bitmaps[dir + 1] + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Back Opacity + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias back_opacity= opacity= unless self.method_defined? (:back_opacity=) unless $@ + def opacity= (value) + self.back_opacity = (self.opacity*(value.to_f / 255.0)).to_i + @border_sprite.opacity = value + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Visible + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def visible= (boolean) + super (boolean) + @border_sprite.visible = boolean + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * X=, Y=, Z=, Angle + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for attr in ["x", "y", "z", "angle"] + ATS_ATTR = <<_END_ + def #{attr}= (*args) + old_val = self.#{attr} + super (*args) + diff = self.#{attr} - old_val + @border_sprite.#{attr} += diff + end +_END_ + eval (ATS_ATTR) + end +end + +#============================================================================== +# *** Window_MessageBase +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This module holds methods shared by all ATS windows +#============================================================================== + +module Window_MessageBase + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Message Draw Icon + # allows for different opacities + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def message_draw_icon (icon_index, x, y, o = $game_message.message_fontalpha) + bitmap = Cache.system("Iconset") + rect = Rect.new(icon_index % 16 * 24, icon_index / 16 * 24, 24, 24) + self.contents.blt(x, y, bitmap, rect, o) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Text Color + # n : either a windowskin palette or the array + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def text_color (n) + if n.is_a? (Array) + return Color.new (*n) + else + return super (n) + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Alignment + # align : The alignment ( 1 => Centre, 2 => Right ) + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_alignment (align = 1) + # Get rest of line + formatter = P_Formatter_ATS.new (self.contents.font.dup) + ls, lc, j = formatter.format_by_line (@text.dup, @contents_width - @contents_x) + formatter.dispose + es = ls * (lc - 1) + @contents_x += (es / 2)*align + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Draw Message Character + # c : the character to be drawn + #`````````````````````````````````````````````````````````````````````````` + # This method utilizes the codes shared between the MessageBox, WordBox, + # and ChoiceBox + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def draw_message_character (c, bmp = self.contents) + @text = "" if @text.nil? + case c + when "\x01" # \C[n] (text character color change) + @text.sub! (/<(#?)(.+?)>/i, "") + if $1 == "" + bmp.font.color = text_color($2.to_i) + if $game_message.message_fontalpha != 255 + bmp.font.color.alpha = $game_message.message_fontalpha + end + else + a = $2.to_s.scan (/../) + bmp.font.color = text_color ([a[0].to_i (16), a[1].to_i (16), a[2].to_i (16), $game_message.message_fontalpha]) + end + when "\x09" # Tab + x = @line_x + (((@contents_x - @line_x).to_i / 32) + 1)*32 + highlight_underline (x - @contents_x) + @contents_x = x + when "\xb1" # Set Contents X + @text.sub! (/<(\d*)>/, "") + @contents_x = $1.to_i + return false if self.is_a? (Window_Message) + when "\x0d" # Icon + @text.sub!(/<(\d*)>/, "") + highlight_underline (24) + message_draw_icon ($1.to_i, @contents_x, @contents_y) + @contents_x += 24 + @contents_x += @ls if self.is_a? (Window_Message) + when "\x0e" # Bold + @text.sub!(/<([01])>/, "") + bmp.font.bold = (Game_ATS::ATS_2 && $1.to_i == 0) ? !bmp.font.bold : $1.to_i == 0 + when "\x0f" # Italics + @text.sub!(/<([01])>/, "") + bmp.font.italic = (Game_ATS::ATS_2 && $1.to_i == 0) ? !bmp.font.italic : $1.to_i == 0 + when "\x10" # Shadow + @text.sub!(/<([01])>/, "") + bmp.font.shadow = (Game_ATS::ATS_2 && $1.to_i == 0) ? !bmp.font.shadow : $1.to_i == 0 + when "\x11" # Underline + @text.sub!(/<([01])>/, "") + @underline = (Game_ATS::ATS_2 && $1.to_i == 0) ? !@underline : $1.to_i == 0 + when "\x12" # Highlight + @text.sub! (/<(-?\d+)>/, "") + @highlight = $1.to_i + when "\x13" # Font Name + bmp.font.name = bmp.font.name.to_ary if !bmp.font.name.is_a? (Array) + @text.sub!(/<(.*?)>/, "") + bmp.font.name = ([$1.to_s] + bmp.font.name).uniq + when "\x14" # Font Size + @text.sub!(/<(\d*)>/, "") + bmp.font.size = $1.to_i + when "\x15" # Font Alpha + @text.sub! (/<(\d*)>/, "") + $game_message.message_fontalpha = $1.to_i + bmp.font.color.alpha = $game_message.message_fontalpha + when "\x18" # Alignment + @text.sub! (/<([012])>/, "") + return true if @align == $1.to_i + @align = $1.to_i + set_alignment (@align) + else + return false + end + return true + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Draw Regular Character + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def draw_regular_character (c, bmp = self.contents) + c_width = bmp.text_size(c).width + c_width += @ls if !@ls.nil? + highlight_underline (c_width) + bmp.draw_text(@contents_x, @contents_y, c_width + 4, @wlh, c) + @contents_x += c_width + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Highlight and Underline section + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def highlight_underline (c_width) + # Highlight + if @highlight >=0 + hl_rect = Rect.new (@contents_x, @contents_y + 1, c_width, @wlh - 2) + colour = text_color (@highlight) + colour.alpha = 128 if colour.alpha > 128 + contents.fill_rect (hl_rect, colour) + end + # Underline + if @underline + y = @contents_y + contents.font.size + (@wlh - contents.font.size) / 2 + contents.fill_rect (@contents_x, y, c_width, 2, contents.font.color) + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create Background Sprite + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def create_back_sprite (dim, use_dim) + @back_sprite.viewport = self.viewport + bmp = Cache.system(dim) + @back_sprite.bitmap = Bitmap.new (self.width, self.height) + @back_sprite.bitmap.stretch_blt (@back_sprite.bitmap.rect, bmp, bmp.rect) + if use_dim.is_a? (Integer) + @back_sprite.visible = use_dim == 0 ? false : use_dim == 1 ? true : $game_message.background == 1 + else + @back_sprite.visible = use_dim + end + @back_sprite.x = self.x + @back_sprite.y = self.y + @back_sprite.z = self.z - 10 + self.opacity = 0 if @back_sprite.visible + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * X, Y, Z + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for attr in ["x", "y", "z"] + ATS_NAMATTR = <<__END__ + def #{attr}= (*args) + old_val = self.#{attr} + super (*args) + diff = self.#{attr} - old_val + @back_sprite.#{attr} += diff + end +__END__ + eval (ATS_NAMATTR) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Get Top Row + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def top_row + return super if !@wlh + return self.oy / @wlh + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Top Row + # row : row shown on top + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def top_row=(row) + if !@wlh + super (row) + else + row = 0 if row < 0 + self.oy = row * @wlh + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Get Number of Rows Displayable on 1 Page + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def page_row_max + return super if !@wlh + return (self.height - 32) / @wlh + end +end + +#============================================================================== +# ** Window_WordBox +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This window will show a single line of text +#============================================================================== + +class Window_WordBox < Window_Base + include Window_MessageBase + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (viewport, word, x = 0, y = 0, width = -1, height = 56, name = false) + @back_sprite = Sprite.new (viewport) + @text = $game_message.convert_special_characters (word) + @wlh = name ? $game_message.name_wlh : $game_message.word_wlh + @wlh = $game_message.wlh if @wlh < 0 + height = 32 + (((word.scan (/\x00/).size) + 1)*@wlh) if height == -1 + @underline = false + @highlight = -1 + # Get Text Size + dummy_formatter = P_Formatter_ATS.new + lines = (@text + "\x00").scan (/.+?\x00/) + tw = 32 + for line in lines + ls, lc, j = dummy_formatter.format_by_line (line, 5000) + tw1 = 5000 - (ls * (lc - 1)) + tw = tw1 if tw1 > tw + end + dummy_formatter.dispose + if width < 0 + width = [(name ? tw + ($game_message.name_border_size*2) + 4 : tw + 36), 33].max + end + super (x, y, width, height) + self.viewport = viewport + self.openness = 0 + self.z = 300 + set_stats + @contents_x, @line_x = 0, 0 + @contents_y = 0 + @contents_width = contents.width + @align = 0 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Stats + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_stats + self.windowskin = Cache.system ($game_message.word_windowskin) + self.opacity = $game_message.word_opacity + self.back_opacity = $game_message.word_backopacity + create_back_sprite ($game_message.word_dim, $game_message.word_use_dim) + self.contents.font.name = $game_message.word_fontname + self.contents.font.size = $game_message.word_fontsize + self.contents.font.color = text_color ($game_message.word_fontcolour) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def dispose (*args) + @back_sprite.bitmap.dispose + @back_sprite.dispose + super (*args) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Draw Word + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def draw_word (text = @text, bmp = self.contents) + @text = text.dup + loop do + c = @text.slice!(/./m) # Get next text character + # Stop when text finished + break if c.nil? + if !draw_message_character (c, bmp) + if c == "\x00" + @contents_y += @wlh + @contents_x, @line_x = 0, 0 + set_alignment (@align) + else + draw_regular_character (c, bmp) + end + end + end + end +end + +#============================================================================== +# ** Window_NameBox +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This window shows the name +#============================================================================== + +class Window_NameBox < Window_WordBox + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (name, viewport = nil) + @word_sprite = Sprite_Base.new (viewport) + wlh = $game_message.name_wlh > 0 ? $game_message.name_wlh : $game_message.wlh + rows = (name.scan (/\x00/).size) + 1 + hght = [($game_message.name_border_size*2) + rows*wlh, 33].max + super (viewport, name, 0, 0, -1, hght, true) + self.viewport = viewport + @word_sprite.x, @word_sprite.y = $game_message.name_border_size, $game_message.name_border_size + @word_sprite.bitmap = Bitmap.new (self.width - (2*$game_message.name_border_size), self.height - (2*$game_message.name_border_size)) + @word_sprite.bitmap.font.name = $game_message.name_fontname + @word_sprite.bitmap.font.size = $game_message.name_fontsize + @word_sprite.bitmap.font.color = text_color ($game_message.name_fontcolour) + @contents_x, @line_x = 2, 2 + draw_word (name, @word_sprite.bitmap) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Stats + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_stats + self.opacity = $game_message.name_opacity + self.back_opacity = $game_message.name_backopacity + create_back_sprite ($game_message.name_dim, $game_message.name_use_dim) + self.windowskin = Cache.system ($game_message.name_windowskin) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def dispose (*args) + super (*args) + @word_sprite.bitmap.dispose + @word_sprite.dispose + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * X, Y, Z + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for attr in ["x", "y", "z"] + ATS_NAMATTR = <<__END__ + def #{attr}= (*args) + old_val = self.#{attr} + super (*args) + diff = self.#{attr} - old_val + @word_sprite.#{attr} += diff + end +__END__ + eval (ATS_NAMATTR) + end +end + +#============================================================================== +# ** Window_FaceBox +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This window processes faces +#============================================================================== + +class Window_FaceBox < Window_Base + include Window_MessageBase + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Public Instance Variables + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + attr_reader :busy + attr_reader :face_sprites + attr_accessor :scroll_x + attr_accessor :scroll_y + attr_accessor :scroll_x_speed + attr_accessor :scroll_y_speed + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (viewport) + @back_sprite = Sprite.new (viewport) + @face_sprites = [] + @animate_face_count = 0 + @fade_count = 0 + @active_face = 0 + @busy = false + @scroll_x = 0 + @scroll_y = 0 + @scroll_x_speed = 8 + @scroll_y_speed = 8 + super (0, 0, 128, 128) + self.viewport = viewport + self.visible = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Remake Window + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def remake_window (width = $game_message.face_width, height = $game_message.face_height) + self.width = [width + 2*$game_message.face_border_size, 33].max + self.height = [height + 2*$game_message.face_border_size, 33].max + create_contents + self.windowskin = Cache.system ($game_message.face_windowskin) + if $game_message.face_fadein + self.back_opacity = 0 + @fade_count = $game_message.face_opacity + else + self.back_opacity = $game_message.face_opacity + end + create_back_sprite ($game_message.face_dim, $game_message.face_use_dim) + self.visible = $game_message.face_window + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def dispose (*args) + super (*args) + clear + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Clear + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def clear + @face_sprites.each { |face| face.dispose } # Dispose all faces + @face_sprites.clear + return if self.disposed? + self.visible = false + @busy = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def update + if !@face_sprites.empty? && @face_sprites[@active_face].visible + # Update Fade In + if @fade_count > 0 + speed = [$game_message.face_fade_speed, @fade_count].min + @fade_count -= speed + self.back_opacity += speed + @face_sprites.each { |sprite| sprite.opacity += speed } + end + # Update Horizontal Scroll + if @scroll_x != 0 + speed = @scroll_x > 0 ? [@scroll_x_speed, @scroll_x].min : [-1*@scroll_x_speed, @scroll_x].max + self.x += speed + @scroll_x -= speed + end + # Update Verticaltal Scroll + if @scroll_y != 0 + speed = @scroll_y > 0 ? [@scroll_y_speed, @scroll_y].min : [-1*@scroll_y_speed, @scroll_y].max + self.y += speed + @scroll_y -= speed + end + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Animate Face + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def animate_face + if @face_sprites.size > 1 + @face_sprites[@active_face].visible = false + @animate_face_count = (@animate_face_count + 1) % $game_message.letters_per_face + @active_face = (@active_face + 1) % @face_sprites.size if @animate_face_count == 0 + @face_sprites[@active_face].visible = true + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Pause Animation + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def pause + return if @face_sprites.size < 2 + @face_sprites[@active_face].visible = false + @active_face = 0 + @face_sprites[0].visible = true + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Face + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_face (face_file, index = 0) + clear + name = face_file.dup + loop do + # Make face and store in @face_sprites unless invalied + face = Sprite_MessageFace.new (name, index, self.viewport) rescue break + @face_sprites.push (face) + break unless $game_message.animate_faces + # If face_file has a number appended, add all of it's animations + if name[/\_(\d+)$/i] != nil + name.sub! (/(\d+)$/) { ($1.to_i + 1).to_s } + elsif name[/^\!\[(\d+)\]./] != nil # If name has exclamation code + # Take all animations from the same face file + index += 1 + break if index == $1.to_i + else + break + end + end + @active_face = 0 + @face_sprites[@active_face].visible = true if @face_sprites[@active_face] + @busy = true + remake_window + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Goal X; Goal Y : accomodate for scrolling + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def goal_x + return self.x + @scroll_x + end + def goal_y + return self.y + @scroll_y + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * X=; Y=; Z= + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def x= (value) + super (value) + @face_sprites.each { |sprite| sprite.x = self.x + $game_message.face_border_size} + end + def y= (value) + super (value) + @face_sprites.each { |sprite| sprite.y = self.y + $game_message.face_border_size} + end + def z= (value) + super (value) + @face_sprites.each { |sprite| sprite.z = self.z } + end +end + +#============================================================================== +# ** Window_ChoiceBox +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This makes a separate window for handling choices, if desired. +#============================================================================== + +class Window_ChoiceBox < Window_Selectable + include Window_MessageBase + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (viewport = nil) + font = Font.new ($game_message.choice_fontname, $game_message.choice_fontsize) + @wlh = $game_message.choice_wlh > 0 ? $game_message.choice_wlh : $game_message.wlh + width, height, line_num = prepare_choices (font.dup) + @back_sprite = Sprite.new (viewport) + super (0, 0, width, height) + if line_num*@wlh > (height - 32) + self.contents.dispose + self.contents = Bitmap.new (width - 32, line_num*@wlh) + end + self.viewport = viewport + self.windowskin = Cache.system ($game_message.choice_windowskin) + self.opacity = $game_message.choice_opacity + self.back_opacity = $game_message.choice_backopacity + self.openness = 0 + self.contents.font = font + self.contents.font.color = text_color ($game_message.choice_fontcolour) + create_back_sprite ($game_message.choice_dim, $game_message.choice_use_dim) + @spacing = $game_message.choice_spacing + @column_max = $game_message.column_max + @item_max = $game_message.choices.size + @underline = false + @highlight = -1 + @last_index = -1 + refresh + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Prepare Choices + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def prepare_choices (font) + max_width = ($game_message.choice_width > 32 ? $game_message.choice_width : Graphics.width) - 32 + max_width = (max_width - ($game_message.choice_spacing*($game_message.column_max - 1))) / $game_message.column_max + longest_line = 1 + line_num = 0 + formatter = P_Formatter_ATS.new (font) + @group_sizes = [] + @choices = [] + group_size = 1 + $game_message.choices.each_index { |i| + lines = [] + line_sizes = [] + if i % $game_message.column_max == 0 + if i != 0 + line_num += group_size + @group_sizes.push (group_size) + end + group_size = 1 + end + string = $game_message.choices[i].dup + while !string.empty? + ls, lc, j = formatter.format_by_line (string, max_width) + length = max_width - (ls*(lc - 1)) + string.sub! (/(.*?)\x00/) { "" } + lines.push ($1.to_s) + line_sizes.push (length) + longest_line = [length, longest_line].max + end + group_size = [group_size, lines.size].max + @choices.push ([lines, line_sizes]) + } + line_num += group_size + @group_sizes.push (group_size) + if $game_message.choice_width > 32 + width = $game_message.choice_width + @line_size = max_width + else + width = 36 + ((longest_line + $game_message.choice_spacing)*$game_message.column_max) - $game_message.choice_spacing + @line_size = longest_line + end + height = $game_message.choice_height > 32 ? $game_message.choice_height : 32 + (@wlh*[line_num, $game_message.row_max].min) + return width, height, line_num + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Refresh + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def refresh + @choices.each_index { |i| + @align = 0 + rect = item_rect (i) + start_x = rect.x + @contents_y = rect.y + for j in 0...@choices[i][0].size + @text = @choices[i][0][j].dup + length = @choices[i][1][j] + @contents_x, @line_x = start_x, start_x + @contents_width = start_x + @line_size + set_alignment (@align) + while !@text.empty? + c = @text.slice! (/./m) + draw_regular_character (c) if !draw_message_character (c) + end + @contents_y += @wlh + end + } + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Item Rect + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def item_rect(index) + rect = super (index) + rect.y = 0 + for j in 0...(index / $game_message.column_max) + rect.y += @group_sizes[j] + end + rect.y *= @wlh + rect.height = @group_sizes[index / $game_message.column_max]*@wlh + return rect + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update Cursor + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def update_cursor + if @index < 0 # If the cursor position is less than 0 + self.cursor_rect.empty # Empty cursor + else # If the cursor position is 0 or more + @help_window.set_text (@index) if @help_window + return if @index == @last_index + if $game_message.skip_choices.include? (@index) + mod = @index - @last_index + @index = (@index + mod) % $game_message.choice_max + return + end + rect = item_rect(@index) # Get rectangle of selected item + row = rect.y / @wlh # Get current row + # Scroll up if before the currently displayed + self.top_row = row if row < top_row + # Scroll down if after the currently displayed + row += (rect.height / @wlh) - 1 + self.bottom_row = row if row > bottom_row + rect.y -= self.oy # Match rectangle to scroll position + self.cursor_rect = rect # Refresh cursor rectangle + @last_index = @index + end + end +end + +#============================================================================== +# ** Window ChoiceHelp +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# This window shows help text when hovering over choices +#============================================================================== + +class Window_ChoiceHelp < Window_Base + include Window_MessageBase + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def initialize (choice_helps) + @back_sprite = Sprite.new + @choice_helps = [] + @underline = false + @highlight = -1 + font = Font.new ($game_message.choicehelp_fontname, $game_message.choicehelp_fontsize) + p_formatter = P_Formatter_ATS.new (font) + longest_line = 1 + x, y = $game_message.choicehelp_x, $game_message.choicehelp_y + width, height = $game_message.choicehelp_width, $game_message.choicehelp_height + @wlh = $game_message.choicehelp_wlh < 0 ? $game_message.wlh : $game_message.choicehelp_wlh + wdth = width > 0 ? width - 32 : 5000 + tallest = 1 + choice_helps.each { |string| + s_d = $game_message.convert_special_characters (string.dup) + s_d.gsub! (/\x16/) { "\x00" } + lines = [] + line_lengths = [] + while !s_d.empty? + ls, lc, j = p_formatter.format_by_line (s_d, wdth) + line_lengths.push (wdth - (ls * (lc - 1))) + lines.push (s_d.slice! (/.*?\x00/)) + end + tallest = [tallest, lines.size].max + @choice_helps.push ([lines, line_lengths]) + } + p_formatter.dispose + width = line_lengths.max + 32 if width < 0 + x = x >= 0 ? x : [(Graphics.width - width) / 2, 0].max + y = $game_message.choicehelp_y < 0 ? 0 : $game_message.choicehelp_y + width = [33, [width, Graphics.width - x].min].max + height = height < 0 ? 32 + (tallest*@wlh) : [height, 33].max + super (x, y, width, height) + @contents_width = contents.width + self.windowskin = Cache.system ($game_message.choicehelp_windowskin) + self.opacity = $game_message.choicehelp_opacity + self.back_opacity = $game_message.choicehelp_backopacity + self.openness = 0 + create_back_sprite ($game_message.choicehelp_dim, $game_message.choicehelp_use_dim) + self.contents.font = Font.new ($game_message.choicehelp_fontname, $game_message.choicehelp_fontsize) + self.contents.font.color = text_color ($game_message.choicehelp_fontcolour) + @index = -1 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Text + # index : the index of the choice being highlighted + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_text (index) + if index != @index + return if self.disposed? + contents.clear + @index = index + lines = @choice_helps[index][0] + lengths = @choice_helps[index][1] + @contents_y = $game_message.choicehelp_center ? (contents.height - (lines.size*@wlh)) / 2 : 0 + @align = 0 + lines.each_index { |i| + @text = lines[i].dup + @contents_x, @line_x = 0, 0 + set_alignment (@align) + while !@text.empty? # Stop when text finished + c = @text.slice!(/./m) # Get next text character + draw_regular_character (c) if !draw_message_character (c) + end + @contents_y += @wlh + } + end + end +end + +#============================================================================== +# ** Window_Gold +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# Make @closing and @opening publicly accessible +#============================================================================== + +class Window_Gold + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Public Instance Variables + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + attr_reader :closing + attr_reader :opening +end + +#============================================================================== +# ** Window_Message +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# overwritten methods - create_back_sprite; update_message; update_cursor; +# convert_special_characters; reset_window; draw_face; input_pause; z=; +# close; visible= +# aliased methods - initialize; dispose; update; update_show_fast; new_page; +# start_message; new_line; finish_message; start_choice; terminate_message +# start_number_input; input_choice +# new methods - create_namebox; create_wordbox; create_choicebox; +# create_choicehelp; remake_window; fit_window_to_text; set_position; +# position_to_character; do_not_obscure_characters; set_face_position; +# set_speechtag_position; set_name_position; set_choice_position; +# dispose_ats_windows; update_letter_se; format_line; start_scroll_message; +# contents_width; draw_message_character; set_alignment +#============================================================================== + +class Window_Message + include Window_MessageBase + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modalg_ats3_win_msg_init_9id1 initialize unless $@ + def initialize (viewport = nil, *args) + @back_sprite = Sprite.new + modalg_ats3_win_msg_init_9id1 (*args) + remake_window + self.viewport = viewport ? viewport : Viewport.new (0, 0, Graphics.width, Graphics.height) + self.viewport.z = self.z + @face_window = Window_FaceBox.new (self.viewport) + @speechtag_sprite = Sprite_SpeechTag.new (self.viewport) + @longest_line = 0 + @scrolling = 0 + @review_scroll = 0 + @letter_se_count = 0 + @ls = 0 + @underline = false + @highlight = -1 + @last_index = -1 + @align = 0 + @max_oy = 0 + @p_formatter = P_Formatter_ATS.new (self.contents.font.dup) + @wlh = $game_message.wlh + @anti_update = true if self.is_a? (Window_BattleMessage) # Yanfly Melody compatibility measure + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modrna_atxts3_dsps_9th5 dispose unless $@ + def dispose (*args) + modrna_atxts3_dsps_9th5 (*args) # Run Original Method + dispose_ats_windows + @face_window.dispose + @speechtag_sprite.dispose + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias mdrnalg_advtxt3_upd_3xa2 update unless $@ + def update (*args) + # Graphic Novel Support + if $game_message.graphic_novel && !self.is_a? (Window_BattleMessage) + if $game_message.gn_press_or_toggle # Press + self.viewport.visible = !Input.press? ($game_message.hide_button) + else # Toggle + self.viewport.visible = !self.viewport.visible if Input.trigger? ($game_message.hide_button) + end + return if !self.viewport.visible + end + [@choice_window, @choicehelp_window, @word_window, @name_window].each { |window| + window.update if window && !window.disposed? } + mdrnalg_advtxt3_upd_3xa2 (*args) + # If Window opening, but no text and just a choice window. + if @opening && $game_message.choice_window && @text.empty? && !$game_message.choices.empty? + close + $game_message.visible = @closing + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create Background Sprite + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def create_back_sprite + if @back_sprite + @back_sprite.bitmap.dispose if @back_sprite.bitmap && !@back_sprite.bitmap.disposed? + @back_sprite.dispose if !@back_sprite.disposed? + end + @back_sprite = Sprite.new (self.viewport) + bmp = Cache.system($game_message.message_dim) + @back_sprite.bitmap = Bitmap.new (self.width, self.height + 32) + @back_sprite.bitmap.stretch_blt (@back_sprite.bitmap.rect, bmp, bmp.rect) + @back_sprite.visible = (@background == 1) + @back_sprite.x = self.x + @back_sprite.y = self.y + @back_sprite.z = 190 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create Namebox + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def create_namebox (name) + @name_window.dispose unless @name_window.nil? || @name_window.disposed? + return if name.empty? + @name_window = Window_NameBox.new (name, self.viewport) + @name_window.open + set_name_position + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create Wordbox + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def create_wordbox (word) + @word_window.dispose unless @word_window.nil? || @word_window.disposed? + return if word.empty? + x = $game_message.word_x + y = $game_message.word_y == -1 ? @gold_window.y : $game_message.word_y + @word_window = Window_WordBox.new (self.viewport, word, x, y, $game_message.word_width, $game_message.word_height) + if $game_message.word_x == -1 + if $game_message.word_y == -1 && (@gold_window.opening || (@gold_window.openness == 255 && !@gold_window.closing)) + @word_window.x = @gold_window.x - @word_window.width + if @word_window.x < 0 + val = @gold_window.y == 0 ? @gold_window.height : -1*@word_window.height + @word_window.y += val + @word_window.x = (Graphics.width - @word_window.width) / 2 + end + else + @word_window.x = (Graphics.width - @word_window.width) / 2 + end + end + @word_window.draw_word + @word_window.open + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create Choicebox + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def create_choicebox + @choice_window = Window_ChoiceBox.new (self.viewport) + @choice_window.z = self.z + 10 + @choice_window.open + set_choice_position + $game_message.choices.clear + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create ChoiceHelp Window + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def create_choicehelp + @choicehelp_window = Window_ChoiceHelp.new ($game_message.help_choices) + @choicehelp_window.open + @choicehelp_window.viewport = self.viewport + if $game_message.choicehelp_y < 0 + unless Graphics.height - @choicehelp_window.height < self.y + self.height + @choicehelp_window.y = Graphics.height - @choicehelp_window.height + end + end + @choicehelp_window.z = self.z + 5 + @choice_window.help_window = @choicehelp_window if @choice_window + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Remake Window + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def remake_window (width = $game_message.message_width, height = $game_message.message_height) + return if width <= 32 || height <= 32 + self.width, self.height = width, height + self.contents.dispose + self.contents = Bitmap.new (width - 32, height - 32) + create_back_sprite + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Fit Window to Text + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def fit_window_to_text + all_lines = $game_message.texts.dup + all_lines += $game_message.choices.dup if !$game_message.choice_window + return if all_lines.size < 1 + @longest_line = 0 + con_hght = [$game_message.max_lines, all_lines.size].min + $game_message.message_height = 32 + (con_hght*@wlh) + $game_message.max_lines = con_hght + # Get Width of longest line + dummy_formatter = P_Formatter_ATS.new + all_lines.each { |line| + c_line = $game_message.convert_special_characters (line.dup) + ls, lc, j = dummy_formatter.format_by_line (c_line, 5000) + @longest_line = [@longest_line, (5000 - ls * (lc - 1))].max + if @longest_line > (Graphics.width - 32) + @longest_line = Graphics.width - 32 + break + end + } + dummy_formatter.dispose + $game_message.message_width = 32 + @longest_line + $game_message.choice_on_line = false if $game_message.message_width + $game_message.choice_width > Graphics.width + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Position + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_position (x = $game_message.message_x, y = $game_message.message_y) + if $game_message.character >= 0 + position_to_character ($game_message.character, $game_message.char_ref) + self.x = $game_message.message_x + self.y = $game_message.message_y + else + # Set the y position, either directly or by default + if y == -1 + do_not_obscure_characters if $game_message.do_not_obscure + self.y = ((Graphics.height - $game_message.message_height) / 2)*@position + else + self.y = y + end + if self.y < @gold_window.height + @gold_window.y = Graphics.height - @gold_window.height + else + @gold_window.y = 0 + end + # Set the x position + if x == -1 # Centre by default + self.x = (Graphics.width - $game_message.message_width) / 2 + else + self.x = x + end + end + set_face_position + set_name_position + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Position To Character + # character_id : the character around which the message window is to go + # type : Over, Below, Left or Right of character + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def position_to_character (character_id, type) + whatever_fits = (type == 4) + type = 0 if whatever_fits # Above if fits, else Under + character = character_id == 0 ? $game_player : $game_map.events[character_id] + # Do not change position if character does not exist + return if character == nil + # Do not change position if the character is not on screen. + return unless character.screen_x.between? (0, Graphics.width) && character.screen_y.between? (0, Graphics.height) + # Get the size of the character + bmp = Cache.character (character.character_name).clone + if character.character_name[/^.?\$/] == nil + c_wdth, c_hght = bmp.width / 12, bmp.height / 8 + else + c_wdth, c_hght = bmp.width / 3, bmp.height / 4 + end + bmp.dispose + # Centre X or Y depending on position + if type % 2 == 0 + # Centre X + x = [character.screen_x - (self.width / 2) , 0].max + x = (x + self.width <= Graphics.width) ? x : Graphics.width - self.width + else + # Centre Y + y = [character.screen_y - ((self.height + c_hght) / 2), 0].max + y = (y + self.height <= Graphics.height) ? y : Graphics.height - self.height + end + case type + when 0 # Over + if whatever_fits && character.screen_y - c_hght - self.height < 0 + position_to_character (character_id, 2) + return + end + y = [character.screen_y - c_hght - self.height, 0].max + when 1 # Left + x = [character.screen_x - (c_wdth / 2) - self.width, 0].max + when 2 # Below + y = [character.screen_y, Graphics.height - self.height].min + when 3 # Right + x = [character.screen_x + (c_wdth / 2), Graphics.width - self.width].min + end + $game_message.message_x = x + $game_message.message_y = y + if character_id >= 0 && $game_message.speech_tag_index >= 0 + set_speech_sprite_position (character.screen_x, character.screen_y, c_wdth, c_hght, type) + position_to_character (character_id, 2) if whatever_fits && !@speechtag_sprite.visible + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Do Not Obstruct Characters + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def do_not_obscure_characters + obscured = [0, 0, 0] + positions = [2, 0, 1] + positions.delete (@position) + positions.unshift (@position) + positions.each { |pos| + y = ((Graphics.height - $game_message.message_height) / 2)*pos + range = y..(y + self.height) + $game_message.obscure_characters.each { |id| + char = id == 0 ? $game_player : $game_map.events[id] + next if char.nil? + range2 = (char.screen_y - $game_message.obscure_buffer)..char.screen_y + obscured[pos] += 1 if range === range2.first || range2 === range.first + } + if obscured[pos] == 0 + @position = pos + return + end + } + @position = obscured.index (obscured.min) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Speech Sprite Position + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_speech_sprite_position (c_x, c_y, c_w, c_h, char_ref) + @speechtag_sprite.opacity = $game_message.message_opacity + @speechtag_sprite.set_direction (char_ref) + @speechtag_sprite.x = c_x - (@speechtag_sprite.width / 2) + if char_ref == 0 + return if $game_message.message_y < @speechtag_sprite.height - 16 + @speechtag_sprite.y = c_y - c_h - @speechtag_sprite.height + $game_message.message_y -= (@speechtag_sprite.height - 16) + @speechtag_sprite.visible = true + elsif char_ref == 2 + return if $game_message.message_y + (@speechtag_sprite.height - 16) > Graphics.height + @speechtag_sprite.y = c_y + $game_message.message_y += @speechtag_sprite.height - 16 + @speechtag_sprite.visible = true + else + @speechtag_sprite.visible = false + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Face Position + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_face_position (x = $game_message.face_x, y = $game_message.face_y) + return if @face_window.nil? || @face_window.disposed? + # Determine Goal X + if x == -1 + if $game_message.face_side + x = (self.x + 16 - $game_message.face_border_size) + $game_message.face_offset_x + else + x = (self.x + self.width - @face_window.width - 16 + $game_message.face_border_size) - $game_message.face_offset_x + end + end + if y == -1 + if @face_window.height < self.height + y = self.y + ((self.height - @face_window.height) / 2) # Centre + else + y = (self.y + self.height) - @face_window.height + end + y = self.y if y < 0 # Align to top of window if out of bounds + y += $game_message.face_offset_y + end + # Set up Horizontal Scroll + if $game_message.face_scroll_x + left = x + @face_window.width + right = Graphics.width - x + if left < right + @face_window.x = -1*@face_window.width + @face_window.scroll_x = left + @face_window.scroll_x_speed = left / $game_message.face_scroll_speed + else + @face_window.x = Graphics.width + @face_window.scroll_x = -1*right + @face_window.scroll_x_speed = right / $game_message.face_scroll_speed + end + else + @face_window.x = x + end + # Set up Vertical Scroll + if $game_message.face_scroll_y + up = y + @face_window.height + down = Graphics.height - y + if up < down + @face_window.y = -1*@face_window.height + @face_window.scroll_y = up + @face_window.scroll_y_speed = up / $game_message.face_scroll_speed + else + @face_window.y = Graphics.height + @face_window.scroll_y = -1*down + @face_window.scroll_y_speed = down / $game_message.face_scroll_speed + end + else + @face_window.y = y + end + # If there is overlap and meant to fit to text_size + if $game_message.fit_window_to_text && @longest_line > 0 + con_x, con_w = contents_width (-1) + $game_message.message_width = @longest_line + contents.width + 32 - con_w + remake_window + if self.x + self.width > Graphics.width + diff = self.x - (Graphics.width - self.width) + self.x -= diff + @face_window.x -= diff + end + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Name Position + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_name_position (x = $game_message.name_x, y = $game_message.name_y) + return if @name_window.nil? || @name_window.disposed? + # Set NameBox coordinates + if y == -1 + y = self.y - @name_window.height + $game_message.name_offset_y + y = self.y + self.height - $game_message.name_offset_y if y < 0 + end + if x == -1 + if $game_message.face_side + x = self.x + $game_message.name_offset_x + else + x = self.x + self.width - @name_window.width - $game_message.name_offset_x + end + # If face is showing + if @face_window.busy + # If overlap with face + if ((@face_window.goal_y + $game_message.face_border_size + 1).between? (y, y + @name_window.height) || + y.between? (@face_window.goal_y + $game_message.face_border_size + 1, @face_window.goal_y + @face_window.height)) && + (x.between? (@face_window.goal_x + $game_message.face_border_size, @face_window.goal_x + @face_window.width) || + @face_window.goal_x.between? (x, x + @name_window.width)) + if $game_message.face_side + x = @face_window.goal_x + @face_window.width + else + x = @face_window.goal_x - @name_window.width + end + end + end + end + @name_window.x = x + @name_window.y = y + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Choice Position + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_choice_position (x = $game_message.choice_x, y = $game_message.choice_y) + return if @choice_window.nil? || @choice_window.disposed? + if y == -1 + if self.openness == 0 + y = (Graphics.height - @choice_window.height) / 2 + else + y = self.y - @choice_window.height + $game_message.choice_offset_y + y = self.y + self.height - $game_message.choice_offset_y if y < 0 + end + end + if x == -1 + choice_side = $game_message.choice_opposite_face ? !$game_message.face_side : $game_message.face_side + if self.openness == 0 + x = (Graphics.width - @choice_window.width) / 2 + elsif choice_side + x = self.x - $game_message.choice_offset_x + else + x = self.x + self.width - @choice_window.width + $game_message.choice_offset_x + end + end + @choice_window.x = x + @choice_window.y = y + @choice_window.y = self.y if $game_message.choice_on_line + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Dispose Windows + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def dispose_ats_windows + ats_windows = [@name_window, @word_window, @choice_window, @choicehelp_window] + ats_windows.each { |window| window.dispose unless window.nil? || window.disposed? } + @name_window = nil + @word_window = nil + @choice_window = nil + @choicehelp_window = nil + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update Show Fast + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias mrnagb_atsys3_updshfst_6yv3 update_show_fast unless $@ + def update_show_fast (*args) + real_count = @wait_count + @wait_count = 2 if $game_message.skip_disabled + mrnagb_atsys3_updshfst_6yv3 (*args) # Run Original Method + @wait_count = real_count + @face_window.update + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def update_message + loop do + if @scrolling > 0 + speed = [$game_message.scroll_speed, @scrolling].min + @scrolling -= speed + self.oy += speed + if @scrolling <= 0 && self.contents.height < self.oy + self.height - 32 + bmp = self.contents + # Max # of lines before cutting top out approx. 200 + if self.contents.width * (self.oy + self.height - 32) < 2500000 + self.contents = Bitmap.new (self.contents.width, self.oy + self.height - 32) + self.contents.font = bmp.font.dup + src_rect = bmp.rect + else + src_rect = Rect.new (0, @wlh, bmp.rect.width, bmp.rect.height - @wlh) + end + self.contents.blt (0, 0, bmp, src_rect) + bmp.dispose if src_rect.y != 0 + end + break + end + c = @text.slice!(/./m) # Get next text character + # If no text left + if c.nil? + if @choice_sizes + @choice_sizes[-1].push (@line_count - @choice_sizes[-1][0]) + end + if $game_message.choices.empty? + $game_message.choice_start = @choice_sizes[0][0] if @choice_sizes + finish_message # Finish update + break + else + if @choice_sizes.nil? + $game_message.scroll_by_page = false + if $game_message.choice_window + create_choicebox + finish_message + return + end + if !$game_message.scrolling + if $game_message.max_lines < @line_count + $game_message.choices.size + self.pause = true + $game_message.choice_start = 0 + @text = "\x1d" + end + $game_message.scrolling = true + return + end + @choice_sizes = [] + end + @text = "#{$game_message.choices.shift}\x00" + # New line stuff + format_line + @choice_sizes.push ([@line_count]) + c = @text.slice!(/./m) # Get next text character + end + end + draw_message_character (c) + break unless @show_fast || @line_show_fast || $game_message.message_speed < 0 + break if self.pause + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update Letter SE + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def update_letter_se + return if !$game_message.letter_sound || @show_fast || @line_show_fast || + $game_message.message_speed < 0 + # Randomize Pitch + rp = $game_message.random_pitch + $game_message.letter_se.pitch = rp.first + rand(rp.last - rp.first) unless rp.first == rp.last + # Update Sound + $game_message.play_se ($game_message.letter_se) if @letter_se_count == 0 + @letter_se_count = (@letter_se_count + 1) % $game_message.letters_per_se + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Update cursor + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def update_cursor + return if @choice_window + if @index >= 0 + @choicehelp_window.set_text (@index) if @choicehelp_window + return if @index == @last_index + if $game_message.skip_choices.include? (@index) + mod = @last_index < @index ? 1 : -1 + @index = (@index + mod) % $game_message.choice_max + return + end + row = @choice_sizes[@index][0] + hght = @choice_sizes[@index][1]*@wlh + y = (row) * @wlh + x, con_width = contents_width (self.contents.height > self.height - 32 ? -1 : y) + # Scroll up if before the currently displayed + self.top_row = row if row < top_row + row += @choice_sizes[@index][1] - 1 + # Scroll down if after the currently displayed + self.bottom_row = row if row > bottom_row + self.cursor_rect.set(x, y - self.oy, con_width, hght) + @last_index = @index + else + self.cursor_rect.empty + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Start Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malg_atxs3_msgstrt_8ik3 start_message unless $@ + def start_message (*args) + @starting = true + @wlh = $game_message.wlh + @align = 0 + @max_oy = 0 + @line_x = 0 + @underline = $game_message.underline + @highlight = $game_message.highlight + malg_atxs3_msgstrt_8ik3 (*args) + @text.gsub! (/([^\s])\s*\x00/) { "#{$1} " } if $game_message.paragraph_format + # Remove x00 from middle of wb and nb boxes + @text.gsub! (/[\x19\x1a]{.*?}/) { |match| match.gsub (/\x00/) { "" } } + @text.gsub! (/\x16/) { "\x00" } + format_line + self.windowskin = Cache.system ($game_message.message_windowskin).dup + self.windowskin.clear_rect (80, 16, 32, 32) unless $game_message.scroll_show_arrows + self.contents.font.name = $game_message.message_fontname + self.contents.font.size = $game_message.message_fontsize + @p_formatter.bitmap.font = self.contents.font.dup + $game_message.play_se ($game_message.start_se) if $game_message.start_sound + @starting = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * New Page + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malba_advts3_nwpge_7uj2 new_page unless $@ + def new_page (*args) + if !$game_message.do_not_start || !@starting + if @text.sub! (/\A\x17<(\d+)>/, "") != nil + actor = $game_actors[$1.to_i] + if !actor.nil? + $game_message.face_name = actor.face_name + $game_message.face_index = actor.face_index + end + end + @face_window.clear if !@face_window.nil? && !@face_window.disposed? + malba_advts3_nwpge_7uj2 (*args) # Run Original Method + self.contents.font.color = text_color ($game_message.message_fontcolour) + self.contents.font.color.alpha = $game_message.message_fontalpha if $game_message.message_fontalpha != 255 + self.oy = 0 + elsif (@line_count - (self.oy / @wlh)) >= $game_message.max_lines + # If scrolling + start_scroll_message if $game_message.scrolling + end + @contents_x, @contents_width = contents_width + @line_x = @contents_x + unless @starting + @text.sub! ("\x00", " ") if @text && $game_message.paragraph_format && !(@p_formatter && @p_formatter.force_break) + format_line + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * New Line + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modalg_ats3_nl_scroll_0ol2 new_line unless $@ + def new_line (*args) + modalg_ats3_nl_scroll_0ol2 (*args) + @contents_y = @line_count*@wlh + @contents_x, @contents_width = contents_width (@contents_y) + @line_x = @contents_x + format_line + # If Reached end of page + if (@line_count - (self.oy / @wlh)) >= $game_message.max_lines + # Do not pause if message is over + return if @text == "" && ($game_message.choices.empty? || $game_message.choice_window) + # If scrolling + if $game_message.scrolling + start_scroll_message + else + self.pause = true + end + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Format Line + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def format_line + if $game_message.paragraph_format + @ls, dc, justify = @p_formatter.format_by_line (@text, @contents_width) + else + @ls = 0 + end + set_alignment (@align) if @align != 0 + @ls = 0 if @ls > 0 && (!justify || @align != 0) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Convert Special Characters + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def convert_special_characters + $game_message.convert_special_characters (@text) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Window Background and Position + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def reset_window + @background = $game_message.background + @position = $game_message.position + $game_message.choice_on_line = false if $game_message.choice_width < 32 + fit_window_to_text if $game_message.fit_window_to_text + if $game_message.choice_window && $game_message.choice_on_line + $game_message.message_width = [$game_message.message_width, Graphics.width - $game_message.choice_width].min + total_width = $game_message.message_width + $game_message.choice_width + if $game_message.message_x == -1 + $game_message.message_x = (Graphics.width - total_width) / 2 + elsif $game_message.message_x + total_width > Graphics.width + $game_message.message_x = Graphics.width - total_width + end + choice_side = $game_message.choice_opposite_face ? !$game_message.face_side : $game_message.face_side + if choice_side + $game_message.choice_x = $game_message.message_x + $game_message.message_x += $game_message.choice_width + else + $game_message.choice_x = $game_message.message_x + $game_message.message_width + end + $game_message.choice_height = $game_message.message_height + end + remake_window unless $game_message.do_not_start + # Set opacity + self.opacity = @background == 0 ? $game_message.message_opacity : 0 + self.back_opacity = $game_message.message_backopacity + # Remake speech bubble if different + @speechtag_sprite.reset_graphic + @speechtag_sprite.z = self.z + 1 + if @text.include? ("\x1c") + @text = "" + self.openness = 0 unless $game_message.do_not_refresh + end + set_position + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Finish Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malbra_ats3_fnmsg_7uj2 finish_message unless $@ + def finish_message (*args) + @face_window.pause + $game_message.play_se ($game_message.finish_se) if $game_message.finish_sound + malbra_ats3_fnmsg_7uj2 (*args) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Start Choices + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modrn_ats3_strtchc_5sf2 start_choice unless $@ + def start_choice (*args) + modrn_ats3_strtchc_5sf2 (*args) + $game_message.move_when_visible = false + if @choice_window + self.index = -1 + @choice_window.index = 0 + end + create_choicehelp if !$game_message.help_choices.empty? + @max_oy = [(@line_count - $game_message.max_lines)*@wlh, 0].max + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Start Number Input + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malgr_advts3_strtnuminp_8qw2 start_number_input unless $@ + def start_number_input (*args) + malgr_advts3_strtnuminp_8qw2 (*args) # Run Original Method + if $game_message.scrolling + self.oy += @wlh if @line_count >= $game_message.max_lines + @number_input_window.y -= self.oy + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Start Scroll Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def start_scroll_message + scroll_plus = @wlh + scroll_plus *= $game_message.max_lines if $game_message.scroll_by_page + @scrolling += scroll_plus + @show_fast = false + if $game_message.scroll_autopause && !@text.nil? && !@text.empty? + self.pause = true + $game_message.play_se ($game_message.pause_se) if $game_message.pause_sound + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * End Message + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modal_atsv3_trmmsg_8yx4 terminate_message unless $@ + def terminate_message (*args) + if !$game_message.do_not_refresh + if !@face_window.nil? && !@face_window.disposed? + if self.is_a? (Window_BattleMessage) + @face_window.visible = false + @face_window.clear + create_contents if contents.height > height - 32 + self.oy = 0 + end + end + dispose_ats_windows + else + @choice_window.dispose if @choice_window && !@choice_window.disposed? + @choicehelp_window.dispose if @choicehelp_window && !@choicehelp_window.disposed? + @choice_window = nil + @choicehelp_window = nil + end + $game_message.play_se ($game_message.terminate_se) if $game_message.terminate_sound + modal_atsv3_trmmsg_8yx4 (*args) # Run Original Method + @last_index = -1 + @choice_sizes = nil + @speechtag_sprite.visible = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Contents Width + # y : the line to check it on + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def contents_width (y = 0) + # If can scroll, make sure no overlap over whole range. Get the ranges + if y < 0 || ($game_message.scrolling && $game_message.texts.size > $game_message.max_lines) + m_range = (self.y + 16)..(self.y + 16 + ($game_message.max_lines*@wlh)) + else + screen_y = self.y + 16 + y - self.oy + m_range = screen_y..screen_y + @wlh + end + return 0, contents.width if @face_window.z < self.z + f_range = @face_window.goal_y..(@face_window.goal_y + @face_window.height) + # If there is overlap with the face window + if @face_window.busy && (m_range === f_range.first || f_range === m_range.first) + left = @face_window.goal_x - (self.x + 20) + right = self.x + self.width - 20 - (@face_window.goal_x + @face_window.width) + if left >= right + return 0, left + else + return @face_window.goal_x + @face_window.width - (self.x + 12), right + end + end + return 0, contents.width # Start X, total width + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Draw Face + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def draw_face (face_file, face_index, *args) + @face_window.set_face (face_file, face_index) + set_face_position + @face_window.z = self.z + $game_message.face_z + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Draw Message Character + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def draw_message_character (c) + return if super (c) + case c + when "\x00" # New line + new_line + when "\x02" # \G (gold display) + @gold_window.refresh + @gold_window.openness > 0 ? @gold_window.close : @gold_window.open + when "\x03" # \W[x] (Wait x frames) + @text.sub!(/<(\d+)>/, "") + @wait_count = $1.to_i + @face_window.pause + @show_fast, @line_show_fast = false, false + when "\x04" # \! (Wait for input) + self.pause = true + $game_message.play_se ($game_message.pause_se) if $game_message.pause_sound + @show_fast, @line_show_fast = false, false + when "\x05" # \/@ (Fast display ON) + @text.sub!(/<([01])>/, "") + @line_show_fast = (Game_ATS::ATS_2 && $1.to_i == 0) ? !@line_show_fast: $1.to_i == 0 + when "\x06" # \/@@ (Fast display OFF) + @text.sub!(/<([01])>/, "") + @show_fast = (Game_ATS::ATS_2 && $1.to_i == 0) ? !@show_fast : $1.to_i == 0 + when "\x07" # \^ (No wait for input) + @pause_skip = true + when "\x08" # Speed Change + @text.sub!(/<(=?-?\d+)>/, "") + s = $1.to_s + s.sub! (/(=)/, "") + if $1.nil? + $game_message.message_speed = [$game_message.message_speed + s.to_i, -1].max + else + $game_message.message_speed = [s.to_i, -1].max + end + when "\x0b" # Show Animation + # Extract Target and Graphic ID + @text.sub! (/<(\d*),(\d*),([01])>/, "") + return if self.is_a? (Window_BattleMessage) + char = $1.to_i < 1 ? $game_player : $game_map.events[$1.to_i] # Target + if char != nil + $3.to_i == 0 ? char.animation_id = $2.to_i : char.balloon_id = $2.to_i + end + when "\x0c" # Position to Character + @text.sub! (/<(\d+),([0-4])>/, "") + return if self.is_a? (Window_BattleMessage) + $game_message.character = $1.to_i + $game_message.char_ref = $2.to_i + set_position + new_page + when "\x17" # Use Actor Face + @text.sub! (/<(\d+)>/, "") + $game_message.face_name = $game_actors[$1.to_i].face_name + $game_message.face_index = $game_actors[$1.to_i].face_index + draw_face ($game_message.face_name, $game_message.face_index) + set_name_position + when "\x19" # Name Box + @text.sub! (/{(.*?)}/, "") + create_namebox ($1.to_s) + when "\x1a" # Word Box + @text.sub! (/{(.*?)}/, "") + create_wordbox ($1.to_s) + when "\x1b" # Play SE + @text.sub! (/<(.+?),([MS]E)>/, "") + $game_message.play_se ((RPG.const_get ($2.to_s)).new ($1.to_s)) + when "\x1d" # New Page + create_contents + new_page + when "\x1f" # Position setting + @text.sub! (/<([012]),(-?\d+),(-?\d+)>/, "" ) + return if self.is_a? (Window_BattleMessage) + case $1.to_i + when 0 then set_position ($2.to_i, $3.to_i) + when 1 then set_face_position ($2.to_i, $3.to_i) + when 2 then set_name_position ($2.to_i, $3.to_i) + end + new_page unless $1.to_i == 2 + when "\x7f" # Evaluate Code + @text.sub! (/\{(.+?)#\}/, "") + eval ($1.to_s) + when "\xb0" # Enable/Disable Skipping + @text.sub!(/<([01])>/, "") + $game_message.skip_disabled = (Game_ATS::ATS_2 && $1.to_i == 0) ? !$game_message.skip_disabled : $1.to_i == 0 + @show_fast, @line_show_fast = false, false + when "\xb1" # Set Contents X + start_x, throwaway = contents_width (@contents_y) + @contents_x += start_x + else # Normal text character + draw_regular_character (c) + @wait_count += $game_message.message_speed if !@show_fast && + !@line_show_fast && $game_message.message_speed > 0 + update_letter_se + @face_window.animate_face + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Set Alignment + # align : The alignment ( 1 => Centre, 2 => Right ) + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def set_alignment (align = 1) + # Get rest of line + formatter = P_Formatter_ATS.new (self.contents.font.dup) + txt = @text[/.*?\x00/] + txt = "" if txt.nil? + ls, lc, j = formatter.format_by_line (txt, @line_x + @contents_width - @contents_x) + formatter.dispose + es = ls * (lc - 1) + @contents_x += (es / 2)*align + @ls = 0 if align > 0 + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Input Pause + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def input_pause (*args) + # Allow to review message before inputting + if $game_message.scroll_review + if @review_scroll != 0 + scroll_plus = @review_scroll > 0 ? [@review_scroll, $game_message.scroll_speed].min : + [@review_scroll, -1*$game_message.scroll_speed].max + self.oy += scroll_plus + self.oy = [[self.oy, 0].max, @max_oy].min + @review_scroll -= scroll_plus + end + if Input.press? (Input::UP) + @review_scroll -= $game_message.scroll_speed + elsif Input.press? (Input::DOWN) + @review_scroll += $game_message.scroll_speed + end + end + # Original Method + if Input.trigger?(Input::B) or Input.trigger?(Input::C) + self.oy = @max_oy + self.pause = false + if @text != nil and not @text.empty? + new_page if !$game_message.scrolling && @line_count >= $game_message.max_lines + else + terminate_message + end + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Choice Input + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modab_ats3_inptchc_7yh2 input_choice unless $@ + def input_choice (*args) + if Input.trigger?(Input::C) + indx = @choice_window ? @choice_window.index : self.index + if $game_message.disabled_choices.include? (indx) + Sound.play_buzzer + else + Sound.play_decision + $game_message.choice_proc.call(indx) + terminate_message + end + return + end + modab_ats3_inptchc_7yh2 (*args) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Pause= + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias malabra_ats3_setpause_8uj2 pause= unless (self.method_defined? (:malabra_ats3_setpause_8uj2) || $@) + def pause= (boolean) + malabra_ats3_setpause_8uj2 (boolean) + if boolean + @max_oy = self.oy + @face_window.pause + end + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Z= + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def z= (*args) + diff = -1*self.z + super (*args) + diff += self.z + ats_gfx = [@face_window, @name_window, @word_window, @speechtag_sprite, + @back_sprite, @choice_window, @choicehelp_window] + ats_gfx.each { |gfx| gfx.z += diff unless gfx.nil? } + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Visible = + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def visible= (boolean) + super (boolean) + @face_window.clear if @face_window && !@face_window.disposed? && !boolean + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Close + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def close (*args) + super (*args) + if !@face_window.nil? && !@face_window.disposed? + @face_window.clear + @face_window.close + end + @name_window.close if @name_window && !@name_window.disposed? + end +end + +#============================================================================== +# ** Window_BattleMessage +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# aliased method - initialize +# overwritten method - draw_line +#============================================================================== + +class Window_BattleMessage + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Object Initialization + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modrna_ynfl_melatscomp_5yh2 initialize unless $@ + def initialize (*args) # Yanfly Melody compatibility measure + modrna_ynfl_melatscomp_5yh2 (*args) + @anti_update = false + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Draw Line + # index : the line to draw on + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + def draw_line(index) + return if !@text.nil? + rect = Rect.new(0, 0, 0, 0) + rect.x += 4 + rect.y += index * @wlh + rect.width = contents.width - 8 + rect.height = @wlh + self.contents.clear_rect(rect) + self.contents.font.color = normal_color + @wlh = $game_message.battle_wlh + @text = @lines[index].dup + convert_special_characters + @contents_x = rect.x + @contents_y = rect.y + loop do + c = @text.slice!(/./m) # Get next text character + # Stop when text finished + break if c.nil? + draw_message_character (c) + end + @text = nil + end +end + +#============================================================================== +# ** Scene_Title +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# aliased method - create_game_objects +#============================================================================== + +class Scene_Title + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Create Game Objects + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modalg_ats3_gm_obj_create create_game_objects unless $@ + def create_game_objects (*args) + # Create the object which holds default values for message + $game_ats = $ats_default = Game_ATS.new + # Run original method + modalg_ats3_gm_obj_create (*args) + end +end + +#============================================================================== +# ** Scene_File +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Summary of Changes: +# aliased methods - write_save_data; read_save_data +#============================================================================== + +class Scene_File + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Write Save Data + # file : the file being written to + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modalg_ats3_write_save write_save_data unless $@ + def write_save_data (file, *args) + # Run Original Method + modalg_ats3_write_save (file, *args) + Marshal.dump($game_ats, file) + end + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # * Read Save Data + # file : the file being read from + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + alias modalg_ats3_read_save_data read_save_data unless $@ + def read_save_data (file, *args) + # Run Original Method + modalg_ats3_read_save_data (file, *args) + begin + $game_ats = $ats_default = Marshal.load (file) + rescue # Initialize if old save + $game_ats = $ats_default = Game_ATS.new + $game_message.clear + end + end +end diff --git a/mkxp-z/Kawariki-patches/ports/bitmap_tktk.rb b/mkxp-z/Kawariki-patches/ports/bitmap_tktk.rb new file mode 100644 index 0000000..168997f --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/bitmap_tktk.rb @@ -0,0 +1,237 @@ +# coding: utf-8 +=begin += Bitmapクラスの拡張 (DLL版) +RPGツクールXP/VX共用 +Bitmapクラスに機能を追加します。 +- Marshaldump可能に +- PNGファイルとして保存 +- 色調変更 +- モザイク効果 +- 色の反転 +- ぼかし効果 +- マスクを用いた切り抜き +- ブレンディング + +■ 注意 + このスクリプトの他に"tktk_bitmap.dll"(ver0.1.2.6以上)が必要になります。 + +Author:: 半生 +Date:: 2010/12/13 +Version:: 0.1.2.6 +URL:: http://www.tktkgame.com/ + +############################################ + 2010/12/13 ver 0.1.2.6 + dllの名称を"hn_rg_bitmap.dll"から"tktk_bitmap.dll"に変更 + LARGE_BITMAP機能でメモリを確保できなかった場合の処理を追加 + 2010/10/12 ver 0.1.2.5(デンジャラスベータ版) + 大きいサイズのBitmapオブジェクトを機能を試験的に実装(危険) + 2010/03/24 ver 0.1.2.2 + ブレンディング機能関連の軽量化。 + 画像連結系メソッドの分離。 + 2010/03/24 ver 0.1.2.1 + ブレンディング機能関連のバグフィックス + 2010/03/22 ver 0.1.2.0 + 加算合成等のブレンディング機能の追加 +2010/02/07 ver 0.1.1.0 + マーシャル化の処理の一部をDLLに移動 +2010/01/17 ver 0.1.0.0 + dllの名称を"hn_rx_bitmap.dll"から"hn_rg_bitmap.dll"に変更 + モザイク効果・色反転・ぼかし効果の追加 +############################################ +=end + +module TKTK_Bitmap + LARGE_BITMAP = true # 大容量のBitmap作成機能を使うかどうか + DLL_NAME = 'tktk_bitmap' + + ERROR_ALLOCATE_FAILED = -110002 + + @@png_save = Win32API.new(DLL_NAME, 'PngSaveA', 'p n i i', 'i') + @@blur = Win32API.new(DLL_NAME, 'Blur', 'n i', 'i') + @@change_tone = Win32API.new(DLL_NAME, 'ChangeTone', 'n i i i i', 'i') + @@clip_mask = Win32API.new(DLL_NAME, 'ClipMask', 'n n i i i', 'i') + @@invert = Win32API.new(DLL_NAME, 'InvertColor', 'n', 'i') + @@mosaic = Win32API.new(DLL_NAME, 'Mosaic', 'n i i i i i i', 'i') + @@address = Win32API.new(DLL_NAME, 'GetAddress', 'n', 'n') + @@get_pixel_data = Win32API.new(DLL_NAME, 'GetPixelData', 'n p i', 'i') + @@set_pixel_data = Win32API.new(DLL_NAME, 'SetPixelData', 'n p i', 'i') + @@blend_blt = Win32API.new(DLL_NAME, 'BlendBlt', 'n i i n i i i i i i', 'i') + #@@get_hwnd = Win32API.new(DLL_NAME, 'GetGameHWND', 'v', 'l') + @@change_size = Win32API.new(DLL_NAME, 'ChangeSize', 'n i i', 'i') + module_function + + # PNG形式で保存 + def png_save(bitmap,file_name,compression_level,filter) + return @@png_save.call(file_name, bitmap.object_id, compression_level, filter) + end + + # ぼかし効果 + def blur(bitmap, r = 1) + return @@blur.call(bitmap.object_id, r) + end + + # カラーバランス変更? + def change_tone(bitmap, red = 0, green = 0, blue = 0, simplify = 1) + return @@change_tone.call(bitmap.object_id, red, green, blue, simplify) + end + + # マスクによる画像の切り抜き(アルファとの乗算) + def clip_mask(g_bitmap, m_bitmap, x=0, y=0, outer=0) + return @@clip_mask.call(g_bitmap.object_id, m_bitmap.object_id, x, y, outer) + end + + # 色の反転 + def invert(bitmap) + return @@invert.call(bitmap.object_id) + end + + # モザイク効果 + def mosaic(bitmap, msw=5, msh=5) + return self.mosaic_rect(bitmap, bitmap.rect, msw, msh) + end + + # モザイク効果(範囲指定) + def mosaic_rect(bitmap, rect, msw=5, msh=5) + return @@mosaic.call(bitmap.object_id, + rect.x, rect.y, rect.width, rect.height, msw, msh) + end + + # ビットマップデータのアドレスを取得 + def address(bitmap) + return @@address.call(bitmap.object_id) + end + + # ビットマップのバイナリデータを取得 + def get_pixel_data(bitmap) + buffer = "bgra" * bitmap.width * bitmap.height + @@get_pixel_data.call(bitmap.object_id, buffer, buffer.size) + return buffer + end + + # ビットマップのバイナリデータを置き換え + def set_pixel_data(bitmap, data) + return @@set_pixel_data.call(bitmap.object_id, data, data.size) + end + + def blend_blt(dest_bmp, x, y, src_bmp, rect, blend_type=0, opacity=255) + @@blend_blt.call(dest_bmp.object_id, x, y, src_bmp.object_id, + rect.x, rect.y, rect.width, rect.height, + blend_type, opacity) + end + + # ビットマップのサイズを変更(危険) + def change_size(bitmap, new_width, new_height) + return -1 if (new_width <=0 or new_height <= 0) + result = @@change_size.call(bitmap.object_id, new_width, new_height) + if result == ERROR_ALLOCATE_FAILED + raise("tktk_bitmap:ERROR ALLOCATE FAILED") + end + return result + end + +end + +class Font + def marshal_dump;end + def marshal_load(obj);end +end +class Bitmap + # PNG圧縮用フィルタ + PNG_NO_FILTERS = 0x00 + PNG_FILTER_NONE = 0x08 + PNG_FILTER_SUB = 0x10 + PNG_FILTER_UP = 0x20 + PNG_FILTER_AVG = 0x40 + PNG_FILTER_PAETH = 0x80 + PNG_ALL_FILTERS = (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | + PNG_FILTER_AVG | PNG_FILTER_PAETH) + + # Marshal_dump + def _dump(limit) + return "" if self.disposed? + data = TKTK_Bitmap.get_pixel_data(self) + [width, height, Zlib::Deflate.deflate(data)].pack("LLa*") # ついでに圧縮 + end + + # Marshal_load + def self._load(str) + if str == "" + b = Bitmap.new(1,1) + b.dispose + return b + end + w, h, zdata = str.unpack("LLa*"); b = new(w, h) + TKTK_Bitmap.set_pixel_data(b, Zlib::Inflate.inflate(zdata)) + return b + end + + def address + TKTK_Bitmap.address(self) + end + + # ぼかし効果 + def blur2(r=1) + TKTK_Bitmap.blur(self, r) + end + + # 色調変更 + def change_tone(red, green, blue, simplify = 1) + TKTK_Bitmap.change_tone(self, red, green, blue, simplify) + end + + # クリッピング + def clip_mask(bitmap, x=0, y=0, outer=0) + TKTK_Bitmap.clip_mask(self, bitmap, x, y, outer) + end + + # 色の反転 + def invert + TKTK_Bitmap.invert(self) + end + + # モザイク効果 + def mosaic(msw=5, msh=5) + TKTK_Bitmap.mosaic(self, msw, msh) + end + + # モザイク効果(領域指定) + def mosaic_rect(rect=self.rect, msw=5, msh=5) + TKTK_Bitmap.mosaic_rect(self, rect, msw, msh) + end + + # ブレンディング + def blend_blt(x, y, src_bmp, rect, blend_type=0, opacity=255) + return if opacity <= 0 + TKTK_Bitmap.blend_blt(self, x, y, src_bmp, rect, blend_type, opacity) + end + + #png形式で保存 + def png_save(outp, level = 9, filter = PNG_NO_FILTERS) + if (TKTK_Bitmap.png_save(self, outp, level, filter) != 0) + raise("Bitmap\#png_save failed") + end + end + + # 大きいサイズのBitmap.newを可能に(危険) + # width * height が大体1073741823位まで + if TKTK_Bitmap::LARGE_BITMAP + class << self + unless method_defined?(:_hn_large_bm__new) + alias :_hn_large_bm__new :new + end + def new(*args) + if args.size == 2 && args[0] * args[1] >= 4194304 + new_width = args[0] + new_height = args[1] + # とりあえず小さなサイズで作成 + bitmap = _hn_large_bm__new(16, 16) + TKTK_Bitmap.change_size(bitmap, new_width, new_height) + return bitmap + else + _hn_large_bm__new(*args) + end + end + end # Bitmap.new + end +end # CLASS Bitmap diff --git a/mkxp-z/Kawariki-patches/ports/bitmap_tktkk.rb b/mkxp-z/Kawariki-patches/ports/bitmap_tktkk.rb new file mode 100644 index 0000000..c1717d6 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/bitmap_tktkk.rb @@ -0,0 +1,236 @@ +# coding: utf-8 +=begin += Bitmapクラスの拡張 (DLL版) +RPGツクールXP/VX共用 +Bitmapクラスに機能を追加します。 +- Marshaldump可能に +- PNGファイルとして保存 +- 色調変更 +- モザイク効果 +- 色の反転 +- ぼかし効果 +- マスクを用いた切り抜き +- ブレンディング + +■ 注意 + このスクリプトの他に"tktk_bitmap.dll"(ver0.1.2.6以上)が必要になります。 + +Author:: 半生 +Date:: 2010/12/13 +Version:: 0.1.2.6 +URL:: http://www.tktkgame.com/ + +############################################ + 2010/12/13 ver 0.1.2.6 + dllの名称を"hn_rg_bitmap.dll"から"tktk_bitmap.dll"に変更 + LARGE_BITMAP機能でメモリを確保できなかった場合の処理を追加 + 2010/10/12 ver 0.1.2.5(デンジャラスベータ版) + 大きいサイズのBitmapオブジェクトを機能を試験的に実装(危険) + 2010/03/24 ver 0.1.2.2 + ブレンディング機能関連の軽量化。 + 画像連結系メソッドの分離。 + 2010/03/24 ver 0.1.2.1 + ブレンディング機能関連のバグフィックス + 2010/03/22 ver 0.1.2.0 + 加算合成等のブレンディング機能の追加 +2010/02/07 ver 0.1.1.0 + マーシャル化の処理の一部をDLLに移動 +2010/01/17 ver 0.1.0.0 + dllの名称を"hn_rx_bitmap.dll"から"hn_rg_bitmap.dll"に変更 + モザイク効果・色反転・ぼかし効果の追加 +############################################ +=end + +module TKTK_Bitmap + LARGE_BITMAP = true # 大容量のBitmap作成機能を使うかどうか + DLL_NAME = 'tktk_bitmap' + + ERROR_ALLOCATE_FAILED = -110002 + + @@png_save = Win32API.new(DLL_NAME, 'PngSaveA', 'p n i i', 'i') + @@blur = Win32API.new(DLL_NAME, 'Blur', 'n i', 'i') + @@change_tone = Win32API.new(DLL_NAME, 'ChangeTone', 'n i i i i', 'i') + @@clip_mask = Win32API.new(DLL_NAME, 'ClipMask', 'n n i i i', 'i') + @@invert = Win32API.new(DLL_NAME, 'InvertColor', 'n', 'i') + @@mosaic = Win32API.new(DLL_NAME, 'Mosaic', 'n i i i i i i', 'i') + @@address = Win32API.new(DLL_NAME, 'GetAddress', 'n', 'n') + @@get_pixel_data = Win32API.new(DLL_NAME, 'GetPixelData', 'n p i', 'i') + @@set_pixel_data = Win32API.new(DLL_NAME, 'SetPixelData', 'n p i', 'i') + @@blend_blt = Win32API.new(DLL_NAME, 'BlendBlt', 'n i i n i i i i i i', 'i') + #@@get_hwnd = Win32API.new(DLL_NAME, 'GetGameHWND', 'v', 'l') + @@change_size = Win32API.new(DLL_NAME, 'ChangeSize', 'n i i', 'i') + module_function + + # PNG形式で保存 + def png_save(bitmap,file_name,compression_level,filter) + return file_name, bitmap.object_id, compression_level, filter + end + + # ぼかし効果 + def blur(bitmap, r = 1) + return bitmap.object_id, r + end + + # カラーバランス変更? + def change_tone(bitmap, red = 0, green = 0, blue = 0, simplify = 1) + return bitmap.object_id, red, green, blue, simplify + end + + # マスクによる画像の切り抜き(アルファとの乗算) + def clip_mask(g_bitmap, m_bitmap, x=0, y=0, outer=0) + return g_bitmap.object_id, m_bitmap.object_id, x, y, outer + end + + # 色の反転 + def invert(bitmap) + return bitmap.object_id + end + + # モザイク効果 + def mosaic(bitmap, msw=5, msh=5) + return bitmap, bitmap.rect, msw, msh + end + + # モザイク効果(範囲指定) + def mosaic_rect(bitmap, rect, msw=5, msh=5) + return bitmap.object_id, rect.x, rect.y, rect.width, rect.height, msw, msh + end + + # ビットマップデータのアドレスを取得 + def address(bitmap) + return bitmap.object_id + end + + # ビットマップのバイナリデータを取得 + def get_pixel_data(bitmap) + buffer = "bgra" * bitmap.width * bitmap.height + @@get_pixel_data.call(bitmap.object_id, buffer, buffer.size) + return buffer + end + + # ビットマップのバイナリデータを置き換え + def set_pixel_data(bitmap, data) + return bitmap.object_id, data, data.size + end + + def blend_blt(dest_bmp, x, y, src_bmp, rect, blend_type=0, opacity=255) + dest_bmp.object_id, x, y, src_bmp.object_id, + rect.x, rect.y, rect.width, rect.height, + blend_type, opacity + end + + # ビットマップのサイズを変更(危険) + def change_size(bitmap, new_width, new_height) + return -1 if (new_width <=0 or new_height <= 0) + result = @@change_size.call(bitmap.object_id, new_width, new_height) + if result == ERROR_ALLOCATE_FAILED + raise("tktk_bitmap:ERROR ALLOCATE FAILED") + end + return result + end + +end + +class Font + def marshal_dump;end + def marshal_load(obj);end +end +class Bitmap + # PNG圧縮用フィルタ + PNG_NO_FILTERS = 0x00 + PNG_FILTER_NONE = 0x08 + PNG_FILTER_SUB = 0x10 + PNG_FILTER_UP = 0x20 + PNG_FILTER_AVG = 0x40 + PNG_FILTER_PAETH = 0x80 + PNG_ALL_FILTERS = (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | + PNG_FILTER_AVG | PNG_FILTER_PAETH) + + # Marshal_dump + def _dump(limit) + return "" if self.disposed? + data = TKTK_Bitmap.get_pixel_data(self) + [width, height, Zlib::Deflate.deflate(data)].pack("LLa*") # ついでに圧縮 + end + + # Marshal_load + def self._load(str) + if str == "" + b = Bitmap.new(1,1) + b.dispose + return b + end + w, h, zdata = str.unpack("LLa*"); b = new(w, h) + TKTK_Bitmap.set_pixel_data(b, Zlib::Inflate.inflate(zdata)) + return b + end + + def address + TKTK_Bitmap.address(self) + end + + # ぼかし効果 + def blur2(r=1) + TKTK_Bitmap.blur(self, r) + end + + # 色調変更 + def change_tone(red, green, blue, simplify = 1) + TKTK_Bitmap.change_tone(self, red, green, blue, simplify) + end + + # クリッピング + def clip_mask(bitmap, x=0, y=0, outer=0) + TKTK_Bitmap.clip_mask(self, bitmap, x, y, outer) + end + + # 色の反転 + def invert + TKTK_Bitmap.invert(self) + end + + # モザイク効果 + def mosaic(msw=5, msh=5) + TKTK_Bitmap.mosaic(self, msw, msh) + end + + # モザイク効果(領域指定) + def mosaic_rect(rect=self.rect, msw=5, msh=5) + TKTK_Bitmap.mosaic_rect(self, rect, msw, msh) + end + + # ブレンディング + def blend_blt(x, y, src_bmp, rect, blend_type=0, opacity=255) + return if opacity <= 0 + TKTK_Bitmap.blend_blt(self, x, y, src_bmp, rect, blend_type, opacity) + end + + #png形式で保存 + def png_save(outp, level = 9, filter = PNG_NO_FILTERS) + if (TKTK_Bitmap.png_save(self, outp, level, filter) != 0) + raise("Bitmap\#png_save failed") + end + end + + # 大きいサイズのBitmap.newを可能に(危険) + # width * height が大体1073741823位まで + if TKTK_Bitmap::LARGE_BITMAP + class << self + unless method_defined?(:_hn_large_bm__new) + alias :_hn_large_bm__new :new + end + def new(*args) + if args.size == 2 && args[0] * args[1] >= 4194304 + new_width = args[0] + new_height = args[1] + # とりあえず小さなサイズで作成 + bitmap = _hn_large_bm__new(16, 16) + TKTK_Bitmap.change_size(bitmap, new_width, new_height) + return bitmap + else + _hn_large_bm__new(*args) + end + end + end # Bitmap.new + end +end # CLASS Bitmap diff --git a/mkxp-z/Kawariki-patches/ports/dummyAudioUtilities.rb b/mkxp-z/Kawariki-patches/ports/dummyAudioUtilities.rb new file mode 100644 index 0000000..cda972c --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/dummyAudioUtilities.rb @@ -0,0 +1,1351 @@ +=begin +This script contains various utility functions and classes for dealing +with audio. This is a stand-alone script. + +Audio.square(durationInMs,freq,volume,timbre,async) - Generates a square wave. +Audio.beep(durationInMs,freq,volume,timbre,async) - Alias for Audio.square +Audio.sine(durationInMs,freq,volume,timbre,async) - Generates a sine wave. +Audio.triangle(durationInMs,freq,volume,timbre,async) - Generates a triangle wave. +Audio.saw(durationInMs,freq,volume,async) - Generates a saw wave. +Audio.noise(durationInMs,volume,async) - Generates white noise. +Audio.playTone(toneFile,async) - Plays a tone in the Apple iPod alarm tone format. +Parameters: + durationInMs - duration of the sound in milliseconds. + The module Audio::NoteLength contains useful durations for tones. + If 0 or nil, the frequency is determined using the maximum duration + of the given sound envelopes. + freq - the frequency of the sound in Hz. The higher the frequency, + the higher the pitch. If 0, no sound will be generated. + The module Audio::Note contains useful frequencies for tones. + freq can also be a SoundEnvelope or an array of two element arrays, + as follows: + freq[0] - time in ms to apply the specified frequency + freq[1] - frequency to apply. In between, values will be interpolated + volume - volume of the sound, from 0 through 100 + volume can also be a SoundEnvelope. + async - specifies whether the function will return immediately + without waiting for the sound to finish (stands for asynchronous) + timbre - specifies the timbre of the tone; from 0.0 through 1.0 + timbre can also be a SoundEnvelope or an array of two element arrays, + as follows: + volume[0] - time in ms to apply the specified timbre + volume[1] - timbre to apply. In between, values will be interpolated + +WaveData - A class for holding audio data in memory. This class +is easy to serialize into the save file. + intensity() - Calculates the intensity, or loudness of the data + Returns a value from 0 through 127. + time() - Length of the data in seconds. + play() - Plays the wave data + +getPlayTime(filename) - Gets the length of an audio file in seconds. + Supports WAV, MP3, and OGG files. +getWaveData(filename) - Creates wave data from the given WAV file path. + Returns a WaveData object or an integer: 1=not found; 2=invalid format; + 3=format not supported; 4=no sound in the data (the last error is helpful + for diagnosing whether anything was recorded, since a recording device + can record even if no microphone is attached.) + +beginRecord() - Starts recording. Returns 0 if successful. +getRecorderSample() - Gets a single sample from the microphone. + The beginRecord function must have been called beforehand. +stopRecord() - Stops recording without saving the recording to a file. +endRecord(file) - Stops recording and saves the recording to a file. +=end + +if !defined?(safeExists?) + def safeExists?(f) + ret=false + File.open(f,"rb") { ret=true } rescue nil + return ret + end +end + +def pbSaveSoundData(samples, freq, filename) + samples="" if !samples + data=[ + 0x46464952,samples.length+0x2C, + 0x45564157,0x20746d66,0x10, + 0x01,0x01, # PCM,mono + freq,freq, + 1,8, # 8-bit + 0x61746164,samples.length + ].pack("VVVVVvvVVvvVV") + f=File.open(filename,"wb") + if f + f.write(data) + f.write(samples) + f.close + end +end + +# plays 8 bit mono sound data (default: 11025 Hz) +def pbPlaySoundData(samples,volume,async=false,sampleFreq=11025) + return if !samples || samples.length==0 || sampleFreq==0 + waveOutOpen=Win32API.new("winmm.dll","waveOutOpen","plplll","l") + waveOutPrepareHeader=Win32API.new("winmm.dll","waveOutPrepareHeader","lpl","l") + waveOutWrite=Win32API.new("winmm.dll","waveOutWrite","lpl","l") + waveOutSetVolume=Win32API.new("winmm.dll","waveOutSetVolume","ll","l") + waveOutClose=Win32API.new("winmm.dll","waveOutClose","l","l") + waveOutGetNumDevs=Win32API.new("winmm.dll","waveOutGetNumDevs","","l") + getStringAddress=proc {|obj| + return 0 if !obj + buffer=" "*4 + rtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i') + stringPointer=(obj.__id__*2)+12 + rtlMoveMemory_pi.call(buffer,stringPointer,4) + return buffer.unpack("L")[0] + } + saveToTemp=proc {|samples,freq| + chars="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + for i in 1...1000 + name=""; 8.times { name+=chars[rand(chars.length),1] } + name=ENV["TEMP"]+"\\"+name+"_tmp.wav" + if !safeExists?(name) + pbSaveSoundData(samples,freq,name) + return name + end + end + return nil + } + playThenDelete=proc {|path,volume,length,async| + return if !path || !safeExists?(path) + thread=Thread.new { + Thread.stop + _path=Thread.current[:path] + _length=Thread.current[:length] + sleep(_length); + File.delete(_path) rescue nil + } + thread[:path]=path + thread[:length]=length + Audio.se_play(path,volume) + thread.run + sleep(length); + } + waveHdr=[getStringAddress.call(samples),samples.length, + 0,0,0,0,0,0].pack("V*") + # 8 bit mono sound data + waveFormat=[0x01,0x01,sampleFreq,sampleFreq,1,8,0].pack("vvVVvvv") + duration=samples.length + waveOutHandle=" "*4 + code=waveOutOpen.call(waveOutHandle,-1,waveFormat,0,0,0) + if code!=0 + timeLength=samples.length.to_f/sampleFreq + path=saveToTemp.call(samples,sampleFreq) + playThenDelete.call(path,volume,timeLength,async) + return + end + waveOutHandle=waveOutHandle.unpack("L")[0] + volume=(volume*65535/100) + volume=(volume<<16)|volume + waveOutSetVolume.call(waveOutHandle,volume) + code=waveOutPrepareHeader.call(waveOutHandle,waveHdr,waveHdr.length) + if code!=0 + waveOutClose.call(waveOutHandle) + return + end + thread=Thread.new { + Thread.stop + waveOut=Thread.current[:waveOut] + waveHdr=Thread.current[:waveHeader] + waveData=Thread.current[:waveData] + waveOutUnprepareHeader=Win32API.new("winmm.dll","waveOutUnprepareHeader","lpl","l") + waveOutClose=Win32API.new("winmm.dll","waveOutClose","l","l") + loop do + sleep(1) + hdr=waveHdr.unpack("V*") + flags=hdr[4] + if (flags&1)==1 + # All done + waveOutUnprepareHeader.call(waveOut,waveHdr,waveHdr.length) + waveOutClose.call(waveOut) + break + end + end + } + thread[:waveOut]=waveOutHandle + thread[:waveHeader]=waveHdr + thread[:waveData]=@samples + if waveOutWrite.call(waveOutHandle,waveHdr,waveHdr.length)!=0 + waveOutClose.call(waveOutHandle) + return + end + thread.run + sleep(@duration/1000.0) if !async + return +end + + + +class NoteEnvelope + attr_accessor :fall + attr_accessor :max + attr_reader :envelope + + def initialize(fall=1200,maxPoint=30) + @fall=fall # time until fall to zero + @maxPoint=maxPoint # maximum point + @envelope=SoundEnvelope.new + end + + def falling(duration) + return self if duration<=0 + @envelope.changeDiscrete(0,@maxPoint) + if duration>=@fall + @envelope.change(fall,0) + @envelope.change(duration-fall,0) + else + @envelope.change(duration,@maxPoint*duration/@fall) + end + @envelope.changeDiscrete(0,0) + return self + end + + def sweeping(duration,sweepDuration) + return self if duration<=0 + return steady(duration) if sweepDuration<=0 + @envelope.changeDiscrete(0,@maxPoint) + falling=true + while duration>0 + dur=duration>sweepDuration ? sweepDuration : duration + if falling + self.falling(dur) + else + sd=sweepDuration + if sd>@fall + d=[(sweepDuration-@fall),dur].min + @envelope.change(d,0) + dur-=d + sd-=d + end + if d==sd + @envelope.change(dur,@maxPoint) + else + @envelope.change(dur,@maxPoint*(@fall-(sd-dur))/@fall) + end + end + falling=!falling + duration-=sweepDuration + end + @envelope.changeDiscrete(0,0) + return self + end + + def rest(duration) + if duration>0 + @envelope.changeDiscrete(0,0) + @envelope.changeDiscrete(duration,0) + end + return self + end + + def steady(duration) + if duration>0 + @envelope.changeDiscrete(0,@maxPoint) + @envelope.changeDiscrete(duration,@maxPoint) + @envelope.changeDiscrete(0,0) + end + return self + end +end + + + +# A class for holding audio data in memory. This class +# is easy to serialize into the save file. +class WaveData + def initialize(samplesPerSec,samples) + @freq=samplesPerSec + @samples=samples.is_a?(String) ? samples.clone : samples.pack("C*") + end + + def setSamples(samples) + @samples=samples.is_a?(String) ? samples.clone : samples + end + + def self._load(string) + data=Marshal.load(string) + ret=self.new(data[0],[]) + ret.setSamples(Zlib::Inflate.inflate(data[1])) + return ret + end + + def _dump(depth=100) + return Marshal.dump([@freq,Zlib::Deflate.deflate(@samples)]) + end + + def intensity + distance=@samples.length/2000 + i=distance/2 + count=0 + volume=0 + while i<@samples.length + vol=(@samples[i]-128).abs + vol=127 if vol>127 + if vol>=16 + volume+=vol + count+=1 + end + i+=distance + end + return 0 if count==0 + return volume/count # from 0 through 127 + end + + def time + return @freq==0 ? 0.0 : (@samples.length)*1.0/@freq + end + + def play + # Play sound data asynchronously + pbPlaySoundData(@samples,100,true,@freq) + end + + def save(filename) + pbSaveSoundData(@samples,@freq,filename) + end +end + + + +# A class for specifying volume, frequency, and timbre envelopes. +class SoundEnvelope; include Enumerable + def initialize(env=nil) + @e=[]; + set(env) if env + end + + def self.fromToneFile(file) + envelope=self.new + File.open(file,"rb"){|f| + f.gets if !f.eof? + while !f.eof? + ln=f.gets + if ln[ /^(\d+)\s+(\d+)/ ] + envelope.addValueChange($1.to_i,$2.to_i) + end + end + } + return envelope + end + + def length; @e.length; end + def [](x); @e[x]; end + def each; @e.each {|x| yield x }; end + def clear; @e.clear; end + + def changeAbsolute(pos,volume) + return self if pos<0 + velength=@e.length + if velength>0 + @e.push([pos,volume]) + stableSort() + else + @e.push([pos,volume]) + end + return self + end + + def self.initial(value) + return self.new.change(0,value) + end + + def self.smoothVolume(duration,volume) + env=self.new + return env if duration<8 + env.change(0,0) + env.change(5,volume) + env.changeAbsolute(duration-10,volume) + env.changeAbsolute(duration-5,0) + env.changeAbsolute(duration,0) + return env + end + + # Creates a volume envelope using the given attack, decay, and + # release times and the given sustain level. + # duration - duration of the sound + # attack - attack time (in ms), or time between the start of the sound + # and the time where the sound reaches its maximum volume. + # If this value is less than 0, the sound will decay from silence to + # the sustain volume instead (see below). + # decay - decay time (in ms), or time after the attack phase until + # the time where the sound reaches its sustain volume + # sustain - sustain volume, or normal volume of sound (0-100) + # release - release time (in ms), or amount of time to fade out the + # sound when it reaches its end. The sound's duration includes its + # release time. + def self.attackDecayRelease(duration,attack,decay,sustain,release) + env=self.new + if attack<=0 + env.change(0,attack==0 ? 100 : 0) + else + env.change(attack,100) + end + env.change(decay,sustain) + env.changeAbsolute(duration-release,sustain) + if release>20 + env.changeAbsolute(duration-release-4,0) + end + env.changeAbsolute(duration,0) + return env + end + + def self.blink(value,onDuration,offDuration,totalDuration) + return self.new.addValueChanges( + value,onDuration,0,offDuration).repeat(totalDuration) + end + + def change(delta,volume) + return self if delta<0 + velength=@e.length + if velength>0 + @e.push([@e[velength-1][0]+delta,volume]) + else + @e.push([delta,volume]) + end + return self + end + + def duration + return @e.length==0 ? 0 : @e[@e.length-1][0] + end + + def value + return @e.length==0 ? 0 : @e[@e.length-1][1] + end + + def addValueChange(value,duration) + changeDiscrete(0,value) + changeDiscrete(duration,value) + return self + end + + def sweep(value1,value2,duration,sweepDuration=1000/16) + val=true + while duration>0 + dur=durationdesiredDuration + deltaNew=(newDuration-self.duration) + deltaDesired=(desiredDuration-self.duration) + newValue=deltaNew==0 ? self.value : self.value+(item[1]-self.value)*deltaDesired/deltaNew + @e.push([desiredDuration,newValue]) + break + else + @e.push([newDuration,item[1]]) + end + i+=1 + if i>=oldLength + i=0; currentDuration+=oldDuration + end + end + return self + end + + # Changes the volume, frequency, etc. abruptly without blending. + def changeDiscrete(delta,volume) + return self if delta<0 + velength=@e.length + if velength>0 + oldValue=@e[velength-1][1] + oldDelta=@e[velength-1][0] + newDelta=oldDelta+delta + newValue=oldValue + if newDelta!=oldDelta || newValue!=oldValue + @e.push([newDelta,newValue]) + oldDelta=newDelta + oldValue=newValue + end + newValue=volume + if newDelta!=oldDelta || newValue!=oldValue + @e.push([newDelta,newValue]) + end + else + @e.push([delta,volume]) + end + return self + end + + def set(value) + if value.is_a?(SoundEnvelope) || value.is_a?(Array) + @e.clear + for v in value; @e.push(v); end + end + return self + end + + private + + def stableSort + pm=1;while pm<@e.length + pl=pm; while pl>0 && @e[pl-1][0]>@e[pl][0] + tmp=@e[pl]; @e[pl]=@e[pl-1]; @e[pl-1]=tmp + pl-=1; end + pm+=1;end + end +end + + + +# internal class +class SoundEnvelopeIterator# :nodoc: + def initialize(env) + @env=env + @envIndex=0 + end + + def getValue(frame) + value=0 + if @envIndex==@env.length + value=@env[@envIndex-1][1] + elsif @envIndex==0 + value=@env[@envIndex][1] + else + lastPos=@env[@envIndex-1][0] + thisPos=@env[@envIndex][0] + if thisPos!=lastPos + lastVolume=@env[@envIndex-1][1] + thisVolume=@env[@envIndex][1] + value=(thisVolume-lastVolume)*(frame-lastPos)/(thisPos-lastPos)+lastVolume + else + value=@env[@envIndex][1] + end + end + while @envIndex+1<=@env.length && @env[@envIndex][0]==frame.to_i + @envIndex+=1 + end + return value + end +end + + + +# internal class +class WaveForm# :nodoc: + SAMPLEFREQ=11025 + + def initialize(proc,freq,duration,timbre=0.5) + @duration=duration # in ms + @volumeEnvelope=SoundEnvelope.new + @freqEnvelope=SoundEnvelope.new + @timbreEnvelope=SoundEnvelope.new + @proc=proc + @freq=freq + @timbre=timbre + if @freq.is_a?(Array) || @freq.is_a?(SoundEnvelope) + @freqEnvelope.set(@freq) + @freq=@freq.length>0 ? @freq[0][1] : 800 + end + if @timbre.is_a?(Array) || @timbre.is_a?(SoundEnvelope) + @timbreEnvelope.set(@timbre) + @timbre=@timbre.length>0 ? @timbre[0][1] : 0.5 + end + end + + def setFrequencyEnvelope(env) + if env.is_a?(Numeric) + @freqEnvelope.clear + @freqEnvelope.addValueChange(env,@duration) + else + @freqEnvelope.set(env) + end + end + + def setVolumeEnvelope(env) + if env.is_a?(Numeric) + @volumeEnvelope.clear + @volumeEnvelope.addValueChange(env,@duration) + else + @volumeEnvelope.set(env) + end + end + + def lcm(x,y) + return y if x==0; return x if y==0 + return x if x==y + if x>y; incr=x + while x%y!=0; x+=incr; end + return x + else; incr=y + while y%x!=0; y+=incr; end + return y + end + end + + def start + @i=0 + @volumeIterator=SoundEnvelopeIterator.new(@volumeEnvelope) + @freqIterator=SoundEnvelopeIterator.new(@freqEnvelope) + @timbreIterator=SoundEnvelopeIterator.new(@timbreEnvelope) + @sampleCount=@duration*SAMPLEFREQ/1000 + @samples=" "*@sampleCount + @exactSamplesPerPass=@freq==0 ? 1.0 : [SAMPLEFREQ.to_f/@freq,1].max + @step=1.0/@exactSamplesPerPass + @exactCounter=0 + end + + def self.frac(x) + return x-x.floor + end + + def nextSample + vol=100 + fframe=@i*1000.0/SAMPLEFREQ + if @volumeEnvelope.length>0 # Update volume + vol=@volumeIterator.getValue(fframe) + end + if @proc + updateBuffer=false + if @freqEnvelope.length>0 # Update frequency + freq=@freqIterator.getValue(fframe) + if freq!=@freq # update sample buffer + @freq=freq + @exactSamplesPerPass=@freq==0 ? 1.0 : [SAMPLEFREQ.to_f/@freq,1].max + @step=1.0/@exactSamplesPerPass + end + end + if @timbreEnvelope.length>0 # Update timbre + @timbre=@timbreIterator.getValue(fframe) + end + if @freq==0 || vol==0 + @samples[@i]=0x80 + else + sample=@proc.call(@exactCounter,@timbre) + @samples[@i]=0x80+(vol*sample).round + end + else # noise + v=vol.abs.to_i*2 + @samples[@i]=0x80+(rand(v).to_i-(v/2)) + end + @i+=1 + @exactCounter+=@step + while @exactCounter>1.0; @exactCounter-=1.0; end + end + + def mixSamples(other) + for i in 0...other.sampleCount + newSamp=((@samples[i]-0x80)+(other.samples[i]-0x80))/2+0x80 + @samples[i]=newSamp + end + end + + def play(volume=100, async=false) + pbPlaySoundData(@samples,volume,async,SAMPLEFREQ) + end + + def generateSound + start + while @i<@sampleCount + nextSample + end + end + + def toWaveData + return WaveData.new(SAMPLEFREQ,@samples) + end + + def save(filename) + pbSaveSoundData(@samples,SAMPLEFREQ,filename) + end + + attr_accessor :samples,:sampleCount +end + + + +module Audio + module Note + REST = 0 + GbelowC = 196 + A = 220 + Asharp = 233 + B = 247 + C = 262 + Csharp = 277 + D = 294 + Dsharp = 311 + E = 330 + F = 349 + Fsharp = 370 + G = 392 + Gsharp = 415 + end + + module NoteLength + WHOLE = 1600 + HALF = WHOLE/2 + QUARTER = HALF/2 + EIGHTH = QUARTER/2 + SIXTEENTH = EIGHTH/2 + end + + def self.noise(durationInMs=200, volume=100, async=false) + return if durationInMs<=0 + waveForm=WaveForm.new(nil,0,durationInMs) + if volume.is_a?(Array) || volume.is_a?(SoundEnvelope) + waveForm.setVolumeEnvelope(volume) + volume=100 + end + waveForm.generateSound + waveForm.play(volume,async) + end + + # internal method + def self.envelopeDuration(env) + return 0 if !env + if env.is_a?(SoundEnvelope) || env.is_a?(Array) + return SoundEnvelope.new(env).duration + end + return 0 + end + + def self.oscillateDouble(durationInMs, freq1, freq2, volume1, volume2, + timbre1, timbre2, proc1, proc2, async=false) + return if durationInMs<0 + freq1Zero=(freq1.is_a?(Numeric) && freq1<=0) || + (freq1.is_a?(Array) && freq1.length==0) + freq2Zero=(freq2.is_a?(Numeric) && freq2<=0) || + (freq2.is_a?(Array) && freq2.length==0) + if freq1Zero && freq2Zero + Thread.sleep(durationInMs/1000.0) if !async + return + end + if durationInMs==0 || durationInMs==nil + durationInMs=[ + envelopeDuration(freq1), + envelopeDuration(freq2), + envelopeDuration(volume1), + envelopeDuration(volume2), + envelopeDuration(timbre1), + envelopeDuration(timbre2) + ].max + return if durationInMs<=0 + end + waveForm1=WaveForm.new(proc1,freq1,durationInMs,timbre1) + waveForm2=WaveForm.new(proc2,freq2,durationInMs,timbre2) + waveForm1.setVolumeEnvelope(volume1) + waveForm2.setVolumeEnvelope(volume2) + waveForm1.generateSound + waveForm2.generateSound + waveForm1.mixSamples(waveForm2) + waveForm1.play(100,async) + end + + def self.oscillate(durationInMs=200, freq=800, volume=100, timbre=0.5, async=false, &block) + return if durationInMs<0 + if (freq.is_a?(Numeric) && freq<=0) || (freq.is_a?(Array) && freq.length==0) + Thread.sleep(durationInMs/1000.0) if !async + return + end + if durationInMs==0 || durationInMs==nil + durationInMs=[ + envelopeDuration(freq), + envelopeDuration(volume), + envelopeDuration(timbre)].max + return if durationInMs<=0 + end + waveForm=WaveForm.new(block,freq,durationInMs,timbre) + if volume.is_a?(Array) || volume.is_a?(SoundEnvelope) + waveForm.setVolumeEnvelope(volume) + volume=100 + end + waveForm.generateSound + waveForm.play(volume,async) + end + + def self.frac(x) + return x-x.floor + end + + TWOPI=Math::PI*2 + @@sineProc2=proc {|z,timbre| + x=z1 + for i in 0...tone.length + dtmf(tone[i,1],durationInMs,volume,false) + end + end + t1=0 + t1=1209 if "14ghi7pqrs*".include?(tone) + t1=1336 if "2abc5jkl8tuv0".include?(tone) + t1=1477 if "3def6mno9wxyz#".include?(tone) + t1=1633 if "ABCD".include?(tone) + t2=0 + t2=697 if "12abc3defA".include?(tone) + t2=770 if "4ghi5jkl6mnoB".include?(tone) + t2=852 if "7pqrs8tuv9wxyzC".include?(tone) + t2=941 if "*0#D".include?(tone) + return if t1==0 || t2==0 + f1=Math::PI*2.0*t1/WaveForm::SAMPLEFREQ + f2=Math::PI*2.0*t2/WaveForm::SAMPLEFREQ + doubleSine(durationInMs,t1,t2,volume,volume,0.5,0.5,async) + end + + def self.beep(durationInMs=200, freq=800, volume=100, timbre=0.5, async=false) + square(durationInMs,freq,volume,timbre,async) + end + + @@triangleProc2=proc {|z,timbre| + z>4 + next if t==0 || t==15 + freqs=[44100,22050,11025,48000] + bitrates=[32,40,48,56,64,80,96,112,128,160,192,224,256,320] + bitrate=bitrates[t] + t=(rstr[1]>>2)&3 + freq=freqs[t] + t=(rstr[1]>>1)&1 + filesize=FileTest.size(filename) + frameLength=((144000*bitrate)/freq)+t + numFrames=filesize/(frameLength+4) + time=(numFrames*1152.0/freq) + break + end + end + } + return time +end + +# Creates wave data from the given WAV file path +def getWaveData(filename) + time=-1 + fgetdw=proc{|file| + (file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0)) + } + fgetw=proc{|file| + (file.eof? ? 0 : (file.read(2).unpack("v")[0] || 0)) + } + if !safeExists?(filename) + return 1 # Not found + end + File.open(filename,"rb"){|file| + file.pos=0 + fdw=fgetdw.call(file) + if fdw==0x46464952 # "RIFF" + filesize=fgetdw.call(file) + wave=fgetdw.call(file) + if wave!=0x45564157 # "WAVE" + return 2 + end + fmt=fgetdw.call(file) + if fmt!=0x20746d66 # "fmt " + return 2 + end + fmtsize=fgetdw.call(file) + format=fgetw.call(file) + if format!=1 + return 3 # unsupported + end + channels=fgetw.call(file) # channels (1 or 2) + if channels!=1 + return 3 # unsupported + end + rate=fgetdw.call(file) # samples per second + bytessec=fgetdw.call(file) # avg bytes per second + if bytessec==0 + return 2 + end + bytessample=fgetw.call(file) # bytes per sample + bitssample=fgetw.call(file) # bits per sample (8, 16, etc.) + if bitssample!=8 && bitssample!=16 + return 3 # unsupported + end + data=fgetdw.call(file) + if data!=0x61746164 # "data" + return 2 + end + datasize=fgetdw.call(file) + data=file.read(datasize) + samples=nil + if bitssample==8 + samples=data.unpack("C*") + start=0 + for i in 0...samples.length + s=samples[i] + if s<0x70 || s>=0x90 + start=i + break + end + end + finish=start + i=samples.length-1 + while i>=start + s=samples[i] + if s<0x70 || s>=0x90 + finish=i+1 + break + end + i-=1 + end + if finish==start + return 4 # Nothing was recorded + end + start=0 + finish=samples.length + wave=WaveData.new(rate,samples[start,finish-start]) + return wave + elsif bitssample==16 + samples=data.unpack("v*") + start=0 + for i in 0...samples.length + s=samples[i] + if s>0x1000 && s<0xF000 + start=i + break + end + end + finish=start + i=samples.length-1 + while i>=start + s=samples[i] + if s<0x1000 && s<0xF000 + finish=i+1 + break + end + i-=1 + end + if finish==start + return 4 # Nothing was recorded + end + start=0 + # Convert to 8-bit samples + for i in start...finish + samples[i]=((samples[i]-0x8000)>>8)&0xFF + end + finish=samples.length + return WaveData.new(rate,samples[start,finish-start]) + end + end + } + return 2 +end + +############################### + +begin + MciSendString = Win32API.new('winmm','mciSendString','%w(p,p,l,l)','l') + MciErrorString = Win32API.new('winmm','mciGetErrorString','%w(l,p,l)','l') +rescue + MciSendString=nil + MciErrorString=nil +end + +# Starts recording. Returns 0 if successful. +def beginRecord + return 256+72 if !MciSendString + MciSendString.call("open new type waveaudio alias RECORDER buffer 4",0,0,0) + MciSendString.call("set RECORDER channels 1",0,0,0) + retval=MciSendString.call("record RECORDER",0,0,0) + if retval!=0 + MciSendString.call("close RECORDER",0,0,0) + end + return retval +end + + +# Gets a single sample from the microphone. +# The beginRecord or beginRecordUI function must have been called beforehand. +def getRecorderSample + return 0x8000 if !MciSendString + buffer="\0"*256 + ret=0 + MciSendString.call("stop RECORDER",0,0,0) + MciSendString.call("status RECORDER bitspersample",buffer,256,0) + bitspersample=buffer.to_i + MciSendString.call("status RECORDER level",buffer,256,0) + MciSendString.call("record RECORDER",0,0,0) + if bitspersample==8 + ret=buffer.to_i<<8 # max 128 + else + ret=buffer.to_i # max 0x8000 + end + return ret +end + +def stopRecord() + return if !MciSendString + MciSendString.call("stop RECORDER",0,0,0) + MciSendString.call("close RECORDER",0,0,0) +end + +def endRecord(file) + return if !MciSendString + MciSendString.call("stop RECORDER",0,0,0) + if file && file!="" + MciSendString.call("save RECORDER #{file}",0,0,0) + end + MciSendString.call("close RECORDER",0,0,0) +end + +#Audio.sine(140,SoundEnvelope.initial(6400).change(140,11400),50) diff --git a/mkxp-z/Kawariki-patches/ports/dummyPSystem_Utilities.rb b/mkxp-z/Kawariki-patches/ports/dummyPSystem_Utilities.rb new file mode 100644 index 0000000..c1f1bcf --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/dummyPSystem_Utilities.rb @@ -0,0 +1,2510 @@ +################################################################################ +# General purpose utilities +################################################################################ +def _pbNextComb(comb,length) + i=comb.length-1 + begin + valid=true + for j in i...comb.length + if j==i + comb[j]+=1 + else + comb[j]=comb[i]+(j-i) + end + if comb[j]>=length + valid=false + break + end + end + return true if valid + i-=1 + end while i>=0 + return false +end + +# Iterates through the array and yields each combination of _num_ elements in +# the array. +def pbEachCombination(array,num) + return if array.length> 16) + + ((((@seed & 0xffff0000) >> 16) * (@s1 & 0x0000ffff)) & 0x0000ffff) + + (((@seed & 0x0000ffff) * ((@s1 & 0xffff0000) >> 16)) & 0x0000ffff)) & + 0x0000ffff) << 16)) + @s2 + r=(@seed>>16) + r=(r+0xFFFFFFFF)+1 if r<0 + return r + end + + def getNext + r=(getNext16()<<16)|(getNext16()) + r=(r+0xFFFFFFFF)+1 if r<0 + return r + end +end + + + +################################################################################ +# JavaScript-related utilities +################################################################################ +# Returns true if the given string represents a valid object in JavaScript +# Object Notation, and false otherwise. + + + + +def pbIsJsonString(str) + + return false if !str || str.strip.empty? + + # Regular expressions for matching parts of a JSON string + d = /(?:^|:|,)(?: ?\[)+/ + charEscapes = /\\[\"\\\/nrtubf]/ + + # Match string literals that may contain escaped characters + # This will match strings with valid JSON escapes, such as \", \\, \n, \r, etc. + stringLiterals = /"(?:[^"\\\n\r\x00-\x1f\x7f\x80-\x9f\\]|\\.)*"/ + + # White space matching + whiteSpace = /[\s]+/ + + # Pre-process the string to replace escapes, literals, and whitespace + str = str.gsub(charEscapes, "@").gsub(stringLiterals, "true").gsub(whiteSpace, " ") + + # Prevent cases like "truetrue", "true true", "true[true]", or other malformed patterns + otherLiterals = /(true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)(?! ?[0-9a-z\-\[\{\"])/ + + str = str.gsub(otherLiterals, "]").gsub(d, "") + + # Check if the string matches the format of a valid JSON-like structure + return str =~ /^[\],:{}\s]*$/ ? true : false + +end + + + +# Returns a Ruby object that corresponds to the given string, which is encoded in +# JavaScript Object Notation (JSON). Returns nil if the string is not valid JSON. +def pbParseJson(str) + if !pbIsJsonString(str) + return nil + end + stringRE=/(\"(\\[\"\'\\rntbf]|\\u[0-9A-Fa-f]{4,4}|[^\\\"])*\")/ #" + strings=[] + str=str.gsub(stringRE){ + sl=strings.length + ss=$1 + if ss.include?("\\u") + ss.gsub!(/\\u([0-9A-Fa-f]{4,4})/){ + codepoint=$1.to_i(16) + if codepoint<=0x7F + next sprintf("\\x%02X",codepoint) + elsif codepoint<=0x7FF + next sprintf("%s%s", + (0xC0|((codepoint>>6)&0x1F)).chr, + (0x80|(codepoint &0x3F)).chr) + else + next sprintf("%s%s%s", + (0xE0|((codepoint>>12)&0x0F)).chr, + (0x80|((codepoint>>6)&0x3F)).chr, + (0x80|(codepoint &0x3F)).chr) + end + } + end + strings.push(eval(ss)) + next sprintf("strings[%d]",sl) + } + str=str.gsub(/\:/,"=>") + str=str.gsub(/null/,"nil") + return eval("("+str+")") +end + + + +################################################################################ +# XML-related utilities +################################################################################ +# Represents XML content. +class MiniXmlContent + attr_reader :value + + def initialize(value) + @value=value + end +end + + + +# Represents an XML element. +class MiniXmlElement + attr_accessor :name,:attributes,:children + + def initialize(name) + @name=name + @attributes={} + @children=[] + end + +# Gets the value of the attribute with the given name, or nil if it doesn't +# exist. + def a(name) + self.attributes[name] + end + +# Gets the entire text of this element. + def value + ret="" + for c in @children + ret+=c.value + end + return ret + end + +# Gets the first child of this element with the given name, or nil if it +# doesn't exist. + def e(name) + for c in @children + return c if c.is_a?(MiniXmlElement) && c.name==name + end + return nil + end + + def eachElementNamed(name) + for c in @children + yield c if c.is_a?(MiniXmlElement) && c.name==name + end + end +end + + + +# A small class for reading simple XML documents. Such documents must +# meet the following restrictions: +# They may contain comments and processing instructions, but they are +# ignored. +# They can't contain any entity references other than 'gt', 'lt', +# 'amp', 'apos', or 'quot'. +# They can't contain a DOCTYPE declaration or DTDs. +class MiniXmlReader + def initialize(data) + @root=nil + @elements=[] + @done=false + @data=data + @content="" + end + + def createUtf8(codepoint) #:nodoc: + raise ArgumentError.new("Illegal character") if codepoint<9 || + codepoint==11||codepoint==12||(codepoint>=14 && codepoint<32) || + codepoint==0xFFFE||codepoint==0xFFFF||(codepoint>=0xD800 && codepoint<0xE000) + if codepoint<=0x7F + return codepoint.chr + elsif codepoint<=0x7FF + str=(0xC0|((codepoint>>6)&0x1F)).chr + str+=(0x80|(codepoint &0x3F)).chr + return str + elsif codepoint<=0xFFFF + str=(0xE0|((codepoint>>12)&0x0F)).chr + str+=(0x80|((codepoint>>6)&0x3F)).chr + str+=(0x80|(codepoint &0x3F)).chr + return str + elsif codepoint<=0x10FFFF + str=(0xF0|((codepoint>>18)&0x07)).chr + str+=(0x80|((codepoint>>12)&0x3F)).chr + str+=(0x80|((codepoint>>6)&0x3F)).chr + str+=(0x80|(codepoint &0x3F)).chr + return str + else + raise ArgumentError.new("Illegal character") + end + return str + end + + def unescape(attr) #:nodoc: + attr=attr.gsub(/\r(\n|$|(?=[^\n]))/,"\n") + raise ArgumentError.new("Attribute value contains '<'") if attr.include?("<") + attr=attr.gsub(/&(lt|gt|apos|quot|amp|\#([0-9]+)|\#x([0-9a-fA-F]+));|([\n\r\t])/){ + next " " if $4=="\n"||$4=="\r"||$4=="\t" + next "<" if $1=="lt" + next ">" if $1=="gt" + next "'" if $1=="apos" + next "\"" if $1=="quot" + next "&" if $1=="amp" + next createUtf8($2.to_i) if $2 + next createUtf8($3.to_i(16)) if $3 + } + return attr + end + + def readAttributes(attribs) #:nodoc: + ret={} + while attribs.length>0 + if attribs[/(\s+([\w\-]+)\s*\=\s*\"([^\"]*)\")/] + attribs=attribs[$1.length,attribs.length] + name=$2; value=$3 + if ret[name]!=nil + raise ArgumentError.new("Attribute already exists") + end + ret[name]=unescape(value) + elsif attribs[/(\s+([\w\-]+)\s*\=\s*\'([^\']*)\')/] + attribs=attribs[$1.length,attribs.length] + name=$2; value=$3 + if ret[name]!=nil + raise ArgumentError.new("Attribute already exists") + end + ret[name]=unescape(value) + else + raise ArgumentError.new("Can't parse attributes") + end + end + return ret + end + +# Reads the entire contents of an XML document. Returns the root element of +# the document or raises an ArgumentError if an error occurs. + def read + if @data[/\A((\xef\xbb\xbf)?<\?xml\s+version\s*=\s*(\"1\.[0-9]\"|\'1\.[0-9]\')(\s+encoding\s*=\s*(\"[^\"]*\"|\'[^\']*\'))?(\s+standalone\s*=\s*(\"(yes|no)\"|\'(yes|no)\'))?\s*\?>)/] + # Ignore XML declaration + @data=@data[$1.length,@data.length] + end + while readOneElement(); end + return @root + end + + def readOneElement #:nodoc: + if @data[/\A\s*\z/] + @data="" + if !@root + raise ArgumentError.new("Not an XML document.") + elsif !@done + raise ArgumentError.new("Unexpected end of document.") + end + return false + end + if @data[/\A(\s*<([\w\-]+)((?:\s+[\w\-]+\s*\=\s*(?:\"[^\"]*\"|\'[^\']*\'))*)\s*(\/>|>))/] + @data=@data[$1.length,@data.length] + elementName=$2 + attributes=$3 + endtag=$4 + if @done + raise ArgumentError.new("Element tag at end of document") + end + if @content.length>0 && @elements.length>0 + @elements[@elements.length-1].children.push(MiniXmlContent.new(@content)) + @content="" + end + element=MiniXmlElement.new(elementName) + element.attributes=readAttributes(attributes) + if !@root + @root=element + else + @elements[@elements.length-1].children.push(element) + end + if endtag==">" + @elements.push(element) + else + if @elements.length==0 + @done=true + end + end + elsif @data[/\A()/] + # ignore comments + if $2.include?("--") + raise ArgumentError.new("Incorrect comment") + end + @data=@data[$1.length,@data.length] + elsif @data[/\A(<\?([\w\-]+)\s+[\s\S]*?\?>)/] + # ignore processing instructions + @data=@data[$1.length,@data.length] + if $2.downcase=="xml" + raise ArgumentError.new("'xml' processing instruction not allowed") + end + elsif @data[/\A(<\?([\w\-]+)\?>)/] + # ignore processing instructions + @data=@data[$1.length,@data.length] + if $2.downcase=="xml" + raise ArgumentError.new("'xml' processing instruction not allowed") + end + elsif @data[/\A(\s*<\/([\w\-]+)>)/] + @data=@data[$1.length,@data.length] + elementName=$2 + if @done + raise ArgumentError.new("End tag at end of document") + end + if @elements.length==0 + raise ArgumentError.new("Unexpected end tag") + elsif @elements[@elements.length-1].name!=elementName + raise ArgumentError.new("Incorrect end tag") + else + if @content.length>0 + @elements[@elements.length-1].children.push(MiniXmlContent.new(@content)) + @content="" + end + @elements.pop() + if @elements.length==0 + @done=true + end + end + else + if @elements.length>0 + # Parse content + if @data[/\A([^<&]+)/] + content=$1 + @data=@data[content.length,@data.length] + if content.include?("]]>") + raise ArgumentError.new("Incorrect content") + end + content.gsub!(/\r(\n|\z|(?=[^\n]))/,"\n") + @content+=content + elsif @data[/\A(<\!\[CDATA\[([\s\S]*?)\]\]>)/] + content=$2 + @data=@data[$1.length,@data.length] + content.gsub!(/\r(\n|\z|(?=[^\n]))/,"\n") + @content+=content + elsif @data[/\A(&(lt|gt|apos|quot|amp|\#([0-9]+)|\#x([0-9a-fA-F]+));)/] + @data=@data[$1.length,@data.length] + content="" + if $2=="lt"; content="<" + elsif $2=="gt"; content=">" + elsif $2=="apos"; content="'" + elsif $2=="quot"; content="\"" + elsif $2=="amp"; content="&" + elsif $3; content=createUtf8($2.to_i) + elsif $4; content=createUtf8($3.to_i(16)) + end + @content+=content + elsif !@data[/\A=8 + meta=pbGetMetadata(0,MetadataPlayerA+id) + return false if !meta + $Trainer.trainertype=meta[0] if $Trainer + $game_player.character_name=meta[1] + $game_player.character_hue=0 + $PokemonGlobal.playerID=id + $Trainer.metaID=id if $Trainer +end + +def pbGetPlayerGraphic + id=$PokemonGlobal.playerID + return "" if id<0 || id>=8 + meta=pbGetMetadata(0,MetadataPlayerA+id) + return "" if !meta + return pbPlayerSpriteFile(meta[0]) +end + +def pbGetPlayerTrainerType + id=$PokemonGlobal.playerID + return 0 if id<0 || id>=8 + meta=pbGetMetadata(0,MetadataPlayerA+id) + return 0 if !meta + return meta[0] +end + +def pbGetTrainerTypeGender(trainertype) + ret=2 # 2 = gender unknown + pbRgssOpen("Data/trainertypes.dat","rb"){|f| + trainertypes=Marshal.load(f) + if !trainertypes[trainertype] + ret=2 + else + ret=trainertypes[trainertype][7] + ret=2 if !ret + end + } + return ret +end + +def pbTrainerName(name=nil,outfit=0) + if $PokemonGlobal.playerID<0 + pbChangePlayer(0) + end + trainertype=pbGetPlayerTrainerType + trname=name + $Trainer=PokeBattle_Trainer.new(trname,trainertype) + $Trainer.outfit=outfit + if trname==nil + trname=pbEnterPlayerName(_INTL("Your name?"),0,7) + if trname=="" +# gender=pbGetTrainerTypeGender(trainertype) +# trname=pbSuggestTrainerName(gender) + #REDMAGE - replaced the above two lines with the one below + trname=[_INTL("Simon"),_INTL("Sofia")][$PokemonGlobal.playerID] + end + end + $Trainer.name=trname + $PokemonBag=PokemonBag.new + $PokemonTemp.begunNewGame=true +end + +def pbSuggestTrainerName(gender) + userName=pbGetUserName() + userName=userName.gsub(/\s+.*$/,"") + if userName.length>0 && userName.length<7 + userName[0,1]=userName[0,1].upcase + return userName + end + userName=userName.gsub(/\d+$/,"") + if userName.length>0 && userName.length<7 + userName[0,1]=userName[0,1].upcase + return userName + end + owner=MiniRegistry.get(MiniRegistry::HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + "RegisteredOwner","") + owner=owner.gsub(/\s+.*$/,"") + if owner.length>0 && owner.length<7 + owner[0,1]=owner[0,1].upcase + return owner + end + return getRandomNameEx(gender,nil,1,7) +end + +def pbGetUserName() + buffersize=100 + getUserName=Win32API.new('advapi32.dll','GetUserName','pp','i') + 10.times do + size=[buffersize].pack("V") + buffer="\0"*buffersize + if getUserName.call(buffer,size)!=0 + return buffer.gsub(/\0/,"") + end + buffersize+=200 + end + return "" +end + +def getRandomNameEx(type,variable,upper,maxLength=100) + return "" if maxLength<=0 + name="" + 50.times { + name="" + formats=[] + case type + when 0 # Names for males + formats=%w( F5 BvE FE FE5 FEvE ) + when 1 # Names for females + formats=%w( vE6 vEvE6 BvE6 B4 v3 vEv3 Bv3 ) + when 2 # Neutral gender names + formats=%w( WE WEU WEvE BvE BvEU BvEvE ) + else + return "" + end + format=formats[rand(formats.length)] + format.scan(/./) {|c| + case c + when "c" # consonant + set=%w( b c d f g h j k l m n p r s t v w x z ) + name+=set[rand(set.length)] + when "v" # vowel + set=%w( a a a e e e i i i o o o u u u ) + name+=set[rand(set.length)] + when "W" # beginning vowel + set=%w( a a a e e e i i i o o o u u u au au ay ay + ea ea ee ee oo oo ou ou ) + name+=set[rand(set.length)] + when "U" # ending vowel + set=%w( a a a a a e e e i i i o o o o o u u ay ay ie ie ee ue oo ) + name+=set[rand(set.length)] + when "B" # beginning consonant + set1=%w( b c d f g h j k l l m n n p r r s s t t v w y z ) + set2=%w( + bl br ch cl cr dr fr fl gl gr kh kl kr ph pl pr sc sk sl + sm sn sp st sw th tr tw vl zh ) + name+=rand(3)>0 ? set1[rand(set1.length)] : set2[rand(set2.length)] + when "E" # ending consonant + set1=%w( b c d f g h j k k l l m n n p r r s s t t v z ) + set2=%w( bb bs ch cs ds fs ft gs gg ld ls + nd ng nk rn kt ks + ms ns ph pt ps sk sh sp ss st rd + rn rp rm rt rk ns th zh) + name+=rand(3)>0 ? set1[rand(set1.length)] : set2[rand(set2.length)] + when "f" # consonant and vowel + set=%w( iz us or ) + name+=set[rand(set.length)] + when "F" # consonant and vowel + set=%w( bo ba be bu re ro si mi zho se nya gru gruu glee gra glo ra do zo ri + di ze go ga pree pro po pa ka ki ku de da ma mo le la li ) + name+=set[rand(set.length)] + when "2" + set=%w( c f g k l p r s t ) + name+=set[rand(set.length)] + when "3" + set=%w( nka nda la li ndra sta cha chie ) + name+=set[rand(set.length)] + when "4" + set=%w( una ona ina ita ila ala ana ia iana ) + name+=set[rand(set.length)] + when "5" + set=%w( e e o o ius io u u ito io ius us ) + name+=set[rand(set.length)] + when "6" + set=%w( a a a elle ine ika ina ita ila ala ana ) + name+=set[rand(set.length)] + end + } + break if name.length<=maxLength + } + name=name[0,maxLength] + case upper + when 0 + name=name.upcase + when 1 + name[0,1]=name[0,1].upcase + end + if $game_variables && variable + $game_variables[variable]=name + $game_map.need_refresh = true if $game_map + end + return name +end + +def getRandomName(maxLength=100) + return getRandomNameEx(2,nil,nil,maxLength) +end + + + +################################################################################ +# Event timing utilities +################################################################################ +def pbTimeEvent(variableNumber,secs=86400) + if variableNumber && variableNumber>=0 + if $game_variables + secs=0 if secs<0 + timenow=pbGetTimeNow + $game_variables[variableNumber]=[timenow.to_f,secs] + $game_map.refresh if $game_map + end + end +end + +def pbTimeEventDays(variableNumber,days=0) + if variableNumber && variableNumber>=0 + if $game_variables + days=0 if days<0 + timenow=pbGetTimeNow + time=timenow.to_f + expiry=(time%86400.0)+(days*86400.0) + $game_variables[variableNumber]=[time,expiry-time] + $game_map.refresh if $game_map + end + end +end + +def pbTimeEventValid(variableNumber) + retval=false + if variableNumber && variableNumber>=0 && $game_variables + value=$game_variables[variableNumber] + if value.is_a?(Array) + timenow=pbGetTimeNow + retval=(timenow.to_f - value[0] > value[1]) # value[1] is age in seconds + retval=false if value[1]<=0 # zero age + end + if !retval + $game_variables[variableNumber]=0 + $game_map.refresh if $game_map + end + end + return retval +end + + + +################################################################################ +# Constants utilities +################################################################################ +def isConst?(val,mod,constant) + begin + isdef=mod.const_defined?(constant.to_sym) + return false if !isdef + rescue + return false + end + return (val==mod.const_get(constant.to_sym)) +end + +def hasConst?(mod,constant) + return false if !mod || !constant || constant=="" + return mod.const_defined?(constant.to_sym) rescue false +end + +def getConst(mod,constant) + return nil if !mod || !constant || constant=="" + return mod.const_get(constant.to_sym) rescue nil +end + +def getID(mod,constant) + return nil if !mod || !constant || constant=="" + if constant.is_a?(Symbol) || constant.is_a?(String) + if (mod.const_defined?(constant.to_sym) rescue false) + return mod.const_get(constant.to_sym) rescue 0 + else + return 0 + end + else + return constant + end +end + + + +################################################################################ +# Implements methods that act on arrays of items. Each element in an item +# array is itself an array of [itemID, itemCount]. +# Used by the Bag, PC item storage, and Triple Triad. +################################################################################ +module ItemStorageHelper + # Returns the quantity of the given item in the items array, maximum size per slot, and item ID + def self.pbQuantity(items,maxsize,item) + ret=0 + for i in 0...maxsize + itemslot=items[i] + if itemslot && itemslot[0]==item + ret+=itemslot[1] + end + end + return ret + end + + # Deletes an item from items array, maximum size per slot, item, and number of items to delete + def self.pbDeleteItem(items,maxsize,item,qty) + raise "Invalid value for qty: #{qty}" if qty<0 + return true if qty==0 + ret=false + for i in 0...maxsize + itemslot=items[i] + if itemslot && itemslot[0]==item + amount=[qty,itemslot[1]].min + itemslot[1]-=amount + qty-=amount + items[i]=nil if itemslot[1]==0 + if qty==0 + ret=true + break + end + end + end + items.compact! + return ret + end + + def self.pbCanStore?(items,maxsize,maxPerSlot,item,qty) + raise "Invalid value for qty: #{qty}" if qty<0 + return true if qty==0 + for i in 0...maxsize + itemslot=items[i] + if !itemslot + qty-=[qty,maxPerSlot].min + return true if qty==0 + elsif itemslot[0]==item && itemslot[1]=0 + $game_variables[id]=value if $game_variables + $game_map.need_refresh = true if $game_map + end +end + +# Runs a common event and waits until the common event is finished. +# Requires the script "PokemonMessages" +def pbCommonEvent(id) + return false if id<0 + ce=$data_common_events[id] + return false if !ce + celist=ce.list + interp=Interpreter.new + interp.setup(celist,0) + begin + Graphics.update + Input.update + interp.update + pbUpdateSceneMap + end while interp.running? + return true +end + +def pbExclaim(event,id=EXCLAMATION_ANIMATION_ID,tinting=false) + if event.is_a?(Array) + sprite=nil + done=[] + for i in event + if !done.include?(i.id) + sprite=$scene.spriteset.addUserAnimation(id,i.x,i.y,tinting) + done.push(i.id) + end + end + else + sprite=$scene.spriteset.addUserAnimation(id,event.x,event.y-1,tinting) #Nave exclamaition mark fix (added that -1) + end + while !sprite.disposed? + Graphics.update + Input.update + pbUpdateSceneMap + end +end + +def pbNoticePlayer(event) + if !pbFacingEachOther(event,$game_player) || !Input.press?(Input::C) + pbExclaim(event) + end + pbTurnTowardEvent($game_player,event) + Kernel.pbMoveTowardPlayer(event) +end + + + +################################################################################ +# Loads Pokémon/item/trainer graphics +################################################################################ +def pbPokemonBitmapFile(species, shiny, back=false) # Unused + if shiny + # Load shiny bitmap + ret=sprintf("Graphics/Battlers/%ss%s",getConstantName(PBSpecies,species),back ? "b" : "") rescue nil + if !pbResolveBitmap(ret) + ret=sprintf("Graphics/Battlers/%03ds%s",species,back ? "b" : "") + end + return ret + else + # Load normal bitmap + ret=sprintf("Graphics/Battlers/%s%s",getConstantName(PBSpecies,species),back ? "b" : "") rescue nil + if !pbResolveBitmap(ret) + ret=sprintf("Graphics/Battlers/%03d%s",species,back ? "b" : "") + end + return ret + end +end + +def pbLoadPokemonBitmap(pokemon, back=false) + return pbLoadPokemonBitmapSpecies(pokemon,pokemon.species,back) +end + +# Note: Returns an AnimatedBitmap, not a Bitmap +def pbLoadPokemonBitmapSpecies(pokemon, species, back=false) + ret=nil + if pokemon.isEgg? + bitmapFileName=sprintf("Graphics/Battlers/%segg",getConstantName(PBSpecies,species)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Battlers/%03degg",species) + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Battlers/egg") + end + end + bitmapFileName=pbResolveBitmap(bitmapFileName) + else + bitmapFileName=pbCheckPokemonBitmapFiles([species,back, + (pokemon.isFemale?), + pokemon.isShiny?, + (pokemon.form rescue 0), + (pokemon.isShadow? rescue false)]) + # Alter bitmap if supported + alterBitmap=(MultipleForms.getFunction(species,"alterBitmap") rescue nil) + end + if bitmapFileName && alterBitmap + animatedBitmap=AnimatedBitmap.new(bitmapFileName) + copiedBitmap=animatedBitmap.copy + animatedBitmap.dispose + copiedBitmap.each {|bitmap| + alterBitmap.call(pokemon,bitmap) + } + ret=copiedBitmap + elsif bitmapFileName + ret=AnimatedBitmap.new(bitmapFileName) + end + return ret +end + +# Note: Returns an AnimatedBitmap, not a Bitmap +def pbLoadSpeciesBitmap(species,female=false,form=0,shiny=false,shadow=false,back=false,egg=false) + ret=nil + if egg + bitmapFileName=sprintf("Graphics/Battlers/%segg",getConstantName(PBSpecies,species)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Battlers/%03degg",species) + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Battlers/egg") + end + end + bitmapFileName=pbResolveBitmap(bitmapFileName) + else + bitmapFileName=pbCheckPokemonBitmapFiles([species,back,female,shiny,form,shadow]) + end + if bitmapFileName + ret=AnimatedBitmap.new(bitmapFileName) + end + return ret +end + +def pbCheckPokemonBitmapFiles(params) + species=params[0] + back=params[1] + factors=[] + factors.push([5,params[5],false]) if params[5] && params[5]!=false # shadow + factors.push([2,params[2],false]) if params[2] && params[2]!=false # gender + factors.push([3,params[3],false]) if params[3] && params[3]!=false # shiny + factors.push([4,params[4].to_s,""]) if params[4] && params[4].to_s!="" && + params[4].to_s!="0" # form + tshadow=false + tgender=false + tshiny=false + tform="" + for i in 0...2**factors.length + for j in 0...factors.length + case factors[j][0] + when 2 # gender + tgender=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + when 3 # shiny + tshiny=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + when 4 # form + tform=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + when 5 # shadow + tshadow=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + end + end + bitmapFileName=sprintf("Graphics/Battlers/%s%s%s%s%s%s", + getConstantName(PBSpecies,species), + tgender ? "f" : "", + tshiny ? "s" : "", + back ? "b" : "", + (tform!="" ? "_"+tform : ""), + tshadow ? "_shadow" : "") rescue nil + ret=pbResolveBitmap(bitmapFileName) + return ret if ret + bitmapFileName=sprintf("Graphics/Battlers/%03d%s%s%s%s%s", + species, + tgender ? "f" : "", + tshiny ? "s" : "", + back ? "b" : "", + (tform!="" ? "_"+tform : ""), + tshadow ? "_shadow" : "") + ret=pbResolveBitmap(bitmapFileName) + return ret if ret + end + return nil +end + +def pbLoadPokemonIcon(pokemon) + return AnimatedBitmap.new(pbPokemonIconFile(pokemon)).deanimate +end + +def pbPokemonIconFile(pokemon) + bitmapFileName=nil + bitmapFileName=pbCheckPokemonIconFiles([pokemon.species, + (pokemon.isFemale?), + pokemon.isShiny?, + (pokemon.form rescue 0), + (pokemon.isShadow? rescue false)], + pokemon.isEgg?) + return bitmapFileName +end + +def pbCheckPokemonIconFiles(params,egg=false) + species=params[0] + if egg + bitmapFileName=sprintf("Graphics/Icons/icon%segg",getConstantName(PBSpecies,species)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Icons/icon%03degg",species) + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Icons/iconEgg") + end + end + return pbResolveBitmap(bitmapFileName) + else + factors=[] + factors.push([4,params[4],false]) if params[4] && params[4]!=false # shadow + factors.push([1,params[1],false]) if params[1] && params[1]!=false # gender + factors.push([2,params[2],false]) if params[2] && params[2]!=false # shiny + factors.push([3,params[3].to_s,""]) if params[3] && params[3].to_s!="" && + params[3].to_s!="0" # form + tshadow=false + tgender=false + tshiny=false + tform="" + for i in 0...2**factors.length + for j in 0...factors.length + case factors[j][0] + when 1 # gender + tgender=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + when 2 # shiny + tshiny=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + when 3 # form + tform=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + when 4 # shadow + tshadow=((i/(2**j))%2==0) ? factors[j][1] : factors[j][2] + end + end + bitmapFileName=sprintf("Graphics/Icons/icon%s%s%s%s%s", + getConstantName(PBSpecies,species), + tgender ? "f" : "", + tshiny ? "s" : "", + (tform!="" ? "_"+tform : ""), + tshadow ? "_shadow" : "") rescue nil + ret=pbResolveBitmap(bitmapFileName) + return ret if ret + bitmapFileName=sprintf("Graphics/Icons/icon%03d%s%s%s%s", + species, + tgender ? "f" : "", + tshiny ? "s" : "", + (tform!="" ? "_"+tform : ""), + tshadow ? "_shadow" : "") + ret=pbResolveBitmap(bitmapFileName) + return ret if ret + end + end + return nil +end + +def pbPokemonFootprintFile(pokemon) # Used by the Pokédex + return nil if !pokemon + if pokemon.is_a?(Numeric) + bitmapFileName=sprintf("Graphics/Icons/Footprints/footprint%s",getConstantName(PBSpecies,pokemon)) rescue nil + bitmapFileName=sprintf("Graphics/Icons/Footprints/footprint%03d",pokemon) if !pbResolveBitmap(bitmapFileName) + else + bitmapFileName=sprintf("Graphics/Icons/Footprints/footprint%s_%d",getConstantName(PBSpecies,pokemon.species),(pokemon.form rescue 0)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Icons/Footprints/footprint%03d_%d",pokemon.species,(pokemon.form rescue 0)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Icons/Footprints/footprint%s",getConstantName(PBSpecies,pokemon.species)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Icons/Footprints/footprint%03d",pokemon.species) + end + end + end + end + return pbResolveBitmap(bitmapFileName) +end + +def pbItemIconFile(item) + return nil if !item + bitmapFileName=nil + if item==0 + bitmapFileName=sprintf("Graphics/Icons/itemBack") + else + bitmapFileName=sprintf("Graphics/Icons/item%s",getConstantName(PBItems,item)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Icons/item%03d",item) + end + end + return bitmapFileName +end + +def pbMailBackFile(item) + return nil if !item + bitmapFileName=sprintf("Graphics/Pictures/mail%s",getConstantName(PBItems,item)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Pictures/mail%03d",item) + end + return bitmapFileName +end + +def pbTrainerCharFile(type) + return nil if !type + bitmapFileName=sprintf("Graphics/Characters/trchar%s",getConstantName(PBTrainers,type)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Characters/trchar%03d",type) + end + return bitmapFileName +end + +def pbTrainerCharNameFile(type) + return nil if !type + bitmapFileName=sprintf("trchar%s",getConstantName(PBTrainers,type)) rescue nil + if !pbResolveBitmap(sprintf("Graphics/Characters/"+bitmapFileName)) + bitmapFileName=sprintf("trchar%03d",type) + end + return bitmapFileName +end + +def pbTrainerHeadFile(type) + return nil if !type + bitmapFileName=sprintf("Graphics/Pictures/mapPlayer%s",getConstantName(PBTrainers,type)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Pictures/mapPlayer%03d",type) + end + return bitmapFileName +end + +def pbPlayerHeadFile(type) + return nil if !type + outfit=$Trainer ? $Trainer.outfit : 0 + bitmapFileName=sprintf("Graphics/Pictures/mapPlayer%s_%d", + getConstantName(PBTrainers,type),outfit) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Pictures/mapPlayer%03d_%d",type,outfit) + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=pbTrainerHeadFile(type) + end + end + return bitmapFileName +end + +def pbTrainerSpriteFile(type) + return nil if !type + bitmapFileName=sprintf("Graphics/Characters/trainer%s",getConstantName(PBTrainers,type)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Characters/trainer%03d",type) + end + return bitmapFileName +end + +def pbTrainerSpriteBackFile(type) + return nil if !type + bitmapFileName=sprintf("Graphics/Characters/trback%s",getConstantName(PBTrainers,type)) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Characters/trback%03d",type) + end + return bitmapFileName +end + +def pbPlayerSpriteFile(type) + return nil if !type + outfit=$Trainer ? $Trainer.outfit : 0 + bitmapFileName=sprintf("Graphics/Characters/trainer%s_%d", + getConstantName(PBTrainers,type),outfit) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Characters/trainer%03d_%d",type,outfit) + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=pbTrainerSpriteFile(type) + end + end + return bitmapFileName +end + +def pbPlayerSpriteBackFile(type) + return nil if !type + outfit=$Trainer ? $Trainer.outfit : 0 + bitmapFileName=sprintf("Graphics/Characters/trback%s_%d", + getConstantName(PBTrainers,type),outfit) rescue nil + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=sprintf("Graphics/Characters/trback%03d_%d",type,outfit) + if !pbResolveBitmap(bitmapFileName) + bitmapFileName=pbTrainerSpriteBackFile(type) + end + end + return bitmapFileName +end + + + +################################################################################ +# Loads music and sound effects +################################################################################ +def pbResolveAudioSE(file) + return nil if !file + if RTP.exists?("Audio/SE/"+file,["",".wav",".mp3",".ogg"]) + return RTP.getPath("Audio/SE/"+file,["",".wav",".mp3",".ogg"]) + end + return nil +end + +def pbCryFrameLength(pokemon,pitch=nil) + return 0 if !pokemon + pitch=100 if !pitch + pitch=pitch.to_f/100 + return 0 if pitch<=0 + playtime=0.0 + if pokemon.is_a?(Numeric) + pkmnwav=pbResolveAudioSE(pbCryFile(pokemon)) + playtime=getPlayTime(pkmnwav) if pkmnwav + elsif !pokemon.isEgg? + if pokemon.respond_to?("chatter") && pokemon.chatter + playtime=pokemon.chatter.time + pitch=1.0 + else + pkmnwav=pbResolveAudioSE(pbCryFile(pokemon)) + playtime=getPlayTime(pkmnwav) if pkmnwav + end + end + playtime/=pitch # sound is lengthened the lower the pitch + # 4 is added to provide a buffer between sounds + return (playtime*Graphics.frame_rate).ceil+4 +end + +def pbPlayCry(pokemon,volume=90,pitch=nil) + return if !pokemon + if pokemon.is_a?(Numeric) + pkmnwav=pbCryFile(pokemon) + if pkmnwav + pbSEPlay(RPG::AudioFile.new(pkmnwav,volume,pitch ? pitch : 100)) rescue nil + end + elsif !pokemon.isEgg? + if pokemon.respond_to?("chatter") && pokemon.chatter + pokemon.chatter.play + else + pkmnwav=pbCryFile(pokemon) + if pkmnwav + pbSEPlay(RPG::AudioFile.new(pkmnwav,volume, + pitch ? pitch : (pokemon.hp*25/pokemon.totalhp)+75)) rescue nil + end + end + end +end + +def pbCryFile(pokemon) + return nil if !pokemon + if pokemon.is_a?(Numeric) + filename=sprintf("Cries/%sCry",getConstantName(PBSpecies,pokemon)) rescue nil + filename=sprintf("Cries/%03dCry",pokemon) if !pbResolveAudioSE(filename) + return filename if pbResolveAudioSE(filename) + elsif !pokemon.isEgg? + filename=sprintf("Cries/%sCry_%d",getConstantName(PBSpecies,pokemon.species),(pokemon.form rescue 0)) rescue nil + filename=sprintf("Cries/%03dCry_%d",pokemon.species,(pokemon.form rescue 0)) if !pbResolveAudioSE(filename) + if !pbResolveAudioSE(filename) + filename=sprintf("Cries/%sCry",getConstantName(PBSpecies,pokemon.species)) rescue nil + end + filename=sprintf("Cries/%03dCry",pokemon.species) if !pbResolveAudioSE(filename) + return filename if pbResolveAudioSE(filename) + end + return nil +end + +def pbGetWildBattleBGM(species) + if $PokemonGlobal.nextBattleBGM + return $PokemonGlobal.nextBattleBGM.clone + end + ret=nil + if !ret && $game_map + # Check map-specific metadata + music=pbGetMetadata($game_map.map_id,MetadataMapWildBattleBGM) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + if !ret + # Check global metadata + music=pbGetMetadata(0,MetadataWildBattleBGM) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + ret=pbStringToAudioFile("BT-Wild.mp3") if !ret + return ret +end + +def pbGetWildVictoryME + if $PokemonGlobal.nextBattleME + return $PokemonGlobal.nextBattleME.clone + end + ret=nil + if !ret && $game_map + # Check map-specific metadata + music=pbGetMetadata($game_map.map_id,MetadataMapWildVictoryME) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + if !ret + # Check global metadata + music=pbGetMetadata(0,MetadataWildVictoryME) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + ret=pbStringToAudioFile("001-Victory01") if !ret + ret.name="../../Audio/ME/"+ret.name + return ret +end + +def pbPlayTrainerIntroME(trainertype) + pbRgssOpen("Data/trainertypes.dat","rb"){|f| + trainertypes=Marshal.load(f) + if trainertypes[trainertype] + bgm=trainertypes[trainertype][6] + if bgm && bgm!="" + bgm=pbStringToAudioFile(bgm) + pbMEPlay(bgm) + return + end + end + } +end + +def pbGetTrainerBattleBGM(trainer) # can be a PokeBattle_Trainer or an array of PokeBattle_Trainer + if $PokemonGlobal.nextBattleBGM + return $PokemonGlobal.nextBattleBGM.clone + end + music=nil + pbRgssOpen("Data/trainertypes.dat","rb"){|f| + trainertypes=Marshal.load(f) + if !trainer.is_a?(Array) + trainerarray=[trainer] + else + trainerarray=trainer + end + for i in 0...trainerarray.length + trainertype=trainerarray[i].trainertype + if trainertypes[trainertype] + music=trainertypes[trainertype][4] + end + end + } + ret=nil + if music && music!="" + ret=pbStringToAudioFile(music) + end + if !ret && $game_map + # Check map-specific metadata + music=pbGetMetadata($game_map.map_id,MetadataMapTrainerBattleBGM) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + if !ret + # Check global metadata + music=pbGetMetadata(0,MetadataTrainerBattleBGM) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + ret=pbStringToAudioFile("005-Boss01") if !ret + return ret +end + +def pbGetTrainerBattleBGMFromType(trainertype) + if $PokemonGlobal.nextBattleBGM + return $PokemonGlobal.nextBattleBGM.clone + end + music=nil + pbRgssOpen("Data/trainertypes.dat","rb"){|f| + trainertypes=Marshal.load(f) + if trainertypes[trainertype] + music=trainertypes[trainertype][4] + end + } + ret=nil + if music && music!="" + ret=pbStringToAudioFile(music) + end + if !ret && $game_map + # Check map-specific metadata + music=pbGetMetadata($game_map.map_id,MetadataMapTrainerBattleBGM) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + if !ret + # Check global metadata + music=pbGetMetadata(0,MetadataTrainerBattleBGM) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + ret=pbStringToAudioFile("005-Boss01") if !ret + return ret +end + +def pbGetTrainerVictoryME(trainer) # can be a PokeBattle_Trainer or an array of PokeBattle_Trainer + if $PokemonGlobal.nextBattleME + return $PokemonGlobal.nextBattleME.clone + end + music=nil + pbRgssOpen("Data/trainertypes.dat","rb"){|f| + trainertypes=Marshal.load(f) + if !trainer.is_a?(Array) + trainerarray=[trainer] + else + trainerarray=trainer + end + for i in 0...trainerarray.length + trainertype=trainerarray[i].trainertype + if trainertypes[trainertype] + music=trainertypes[trainertype][5] + end + end + } + ret=nil + if music && music!="" + ret=pbStringToAudioFile(music) + end + if !ret && $game_map + # Check map-specific metadata + music=pbGetMetadata($game_map.map_id,MetadataMapTrainerVictoryME) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + if !ret + # Check global metadata + music=pbGetMetadata(0,MetadataTrainerVictoryME) + if music && music!="" + ret=pbStringToAudioFile(music) + end + end + ret=pbStringToAudioFile("001-Victory01") if !ret + ret.name="../../Audio/ME/"+ret.name + return ret +end + + + +################################################################################ +# Creating and storing Pokémon +################################################################################ +# For demonstration purposes only, not to be used in a real game. +def pbCreatePokemon + party=[] + species=[:PIKACHU,:PIDGEOTTO,:KADABRA,:GYARADOS,:DIGLETT,:CHANSEY] + for id in species + party.push(getConst(PBSpecies,id)) if hasConst?(PBSpecies,id) + end + # Species IDs of the Pokémon to be created + for i in 0...party.length + species=party[i] + # Generate Pokémon with species and level 20 + $Trainer.party[i]=PokeBattle_Pokemon.new(species,20,$Trainer) + $Trainer.seen[species]=true # Set this species to seen and owned + $Trainer.owned[species]=true + pbSeenForm($Trainer.party[i]) + end + $Trainer.party[1].pbLearnMove(:FLY) + $Trainer.party[2].pbLearnMove(:FLASH) + $Trainer.party[2].pbLearnMove(:TELEPORT) + $Trainer.party[3].pbLearnMove(:SURF) + $Trainer.party[3].pbLearnMove(:DIVE) + $Trainer.party[3].pbLearnMove(:WATERFALL) + $Trainer.party[4].pbLearnMove(:DIG) + $Trainer.party[4].pbLearnMove(:CUT) + $Trainer.party[4].pbLearnMove(:HEADBUTT) + $Trainer.party[4].pbLearnMove(:ROCKSMASH) + $Trainer.party[5].pbLearnMove(:SOFTBOILED) + $Trainer.party[5].pbLearnMove(:STRENGTH) + $Trainer.party[5].pbLearnMove(:SWEETSCENT) + for i in 0...party.length + $Trainer.party[i].pbRecordFirstMoves + end +end + +def pbBoxesFull? + return !$Trainer || ($Trainer.party.length==6 && $PokemonStorage.full?) +end + +def pbNickname(pokemon) + speciesname=PBSpecies.getName(pokemon.species) + if Kernel.pbConfirmMessage(_INTL("Would you like to give a nickname to {1}?",speciesname)) + helptext=_INTL("{1}'s nickname?",speciesname) + newname=pbEnterPokemonName(helptext,0,10,"",pokemon) + pokemon.name=newname if newname!="" + end +end + +def pbStorePokemon(pokemon) + if pbBoxesFull? + Kernel.pbMessage(_INTL("There's no more room for Pokémon!\1")) + Kernel.pbMessage(_INTL("The Pokémon Boxes are full and can't accept any more!")) + return + end + pokemon.pbRecordFirstMoves + if $Trainer.party.length<6 + $Trainer.party[$Trainer.party.length]=pokemon + else + oldcurbox=$PokemonStorage.currentBox + storedbox=$PokemonStorage.pbStoreCaught(pokemon) + curboxname=$PokemonStorage[oldcurbox].name + boxname=$PokemonStorage[storedbox].name + creator=nil + creator=Kernel.pbGetStorageCreator if $PokemonGlobal.seenStorageCreator + if storedbox!=oldcurbox + if creator + Kernel.pbMessage(_INTL("Box \"{1}\" on {2}'s PC was full.\1",curboxname,creator)) + else + Kernel.pbMessage(_INTL("Box \"{1}\" on someone's PC was full.\1",curboxname)) + end + Kernel.pbMessage(_INTL("{1} was transferred to box \"{2}.\"",pokemon.name,boxname)) + else + if creator + Kernel.pbMessage(_INTL("{1} was transferred to {2}'s PC.\1",pokemon.name,creator)) + else + Kernel.pbMessage(_INTL("{1} was transferred to someone's PC.\1",pokemon.name)) + end + Kernel.pbMessage(_INTL("It was stored in box \"{1}.\"",boxname)) + end + end +end + +def pbNicknameAndStore(pokemon) + if pbBoxesFull? + Kernel.pbMessage(_INTL("There's no more room for Pokémon!\1")) + Kernel.pbMessage(_INTL("The Pokémon Boxes are full and can't accept any more!")) + return + end + $Trainer.seen[pokemon.species]=true + $Trainer.owned[pokemon.species]=true + pbNickname(pokemon) + pbStorePokemon(pokemon) +end + +def pbAddPokemon(pokemon,level=nil,seeform=true) + return if !pokemon || !$Trainer + if pbBoxesFull? + Kernel.pbMessage(_INTL("There's no more room for Pokémon!\1")) + Kernel.pbMessage(_INTL("The Pokémon Boxes are full and can't accept any more!")) + return false + end + if pokemon.is_a?(String) || pokemon.is_a?(Symbol) + pokemon=getID(PBSpecies,pokemon) + end + if pokemon.is_a?(Integer) && level.is_a?(Integer) + pokemon=PokeBattle_Pokemon.new(pokemon,level,$Trainer) + end + speciesname=PBSpecies.getName(pokemon.species) + if $Trainer.party.length==0 + pbMEPlay("fffirstpokemon") + Kernel.pbMessage(_INTL("{1} obtained {2}!\1",$Trainer.name,speciesname)) + else + pbMEPlay("PokemonGet") + Kernel.pbMessage(_INTL("{1} obtained {2}!\1",$Trainer.name,speciesname)) + end + pbNicknameAndStore(pokemon) + pbSeenForm(pokemon) if seeform + return true +end + +def pbAddPokemonSilent(pokemon,level=nil,seeform=true) + return false if !pokemon || pbBoxesFull? || !$Trainer + if pokemon.is_a?(String) || pokemon.is_a?(Symbol) + pokemon=getID(PBSpecies,pokemon) + end + if pokemon.is_a?(Integer) && level.is_a?(Integer) + pokemon=PokeBattle_Pokemon.new(pokemon,level,$Trainer) + end + $Trainer.seen[pokemon.species]=true + $Trainer.owned[pokemon.species]=true + pbSeenForm(pokemon) if seeform + pokemon.pbRecordFirstMoves + if $Trainer.party.length<6 + $Trainer.party[$Trainer.party.length]=pokemon + else + $PokemonStorage.pbStoreCaught(pokemon) + end + return true +end + +def pbAddToParty(pokemon,level=nil,seeform=true) + return false if !pokemon || !$Trainer || $Trainer.party.length>=6 + if pokemon.is_a?(String) || pokemon.is_a?(Symbol) + pokemon=getID(PBSpecies,pokemon) + end + if pokemon.is_a?(Integer) && level.is_a?(Integer) + pokemon=PokeBattle_Pokemon.new(pokemon,level,$Trainer) + end + speciesname=PBSpecies.getName(pokemon.species) + Kernel.pbMessage(_INTL("{1} obtained {2}!\\se[PokemonGet]\1",$Trainer.name,speciesname)) + pbNicknameAndStore(pokemon) + pbSeenForm(pokemon) if seeform + return true +end + +def pbAddToPartySilent(pokemon,level=nil,seeform=true) + return false if !pokemon || !$Trainer || $Trainer.party.length>=6 + if pokemon.is_a?(String) || pokemon.is_a?(Symbol) + pokemon=getID(PBSpecies,pokemon) + end + if pokemon.is_a?(Integer) && level.is_a?(Integer) + pokemon=PokeBattle_Pokemon.new(pokemon,level,$Trainer) + end + $Trainer.seen[pokemon.species]=true + $Trainer.owned[pokemon.species]=true + pbSeenForm(pokemon) if seeform + pokemon.pbRecordFirstMoves + $Trainer.party[$Trainer.party.length]=pokemon + return true +end + +def pbAddForeignPokemon(pokemon,level=nil,ownerName=nil,nickname=nil,ownerGender=0,seeform=true) + return false if !pokemon || !$Trainer || $Trainer.party.length>=6 + if pokemon.is_a?(String) || pokemon.is_a?(Symbol) + pokemon=getID(PBSpecies,pokemon) + end + if pokemon.is_a?(Integer) && level.is_a?(Integer) + pokemon=PokeBattle_Pokemon.new(pokemon,level,$Trainer) + end + # Set original trainer to a foreign one (if ID isn't already foreign) + if pokemon.trainerID==$Trainer.id + pokemon.trainerID=$Trainer.getForeignID + pokemon.ot=ownerName if ownerName && ownerName!="" + pokemon.otgender=ownerGender + end + # Set nickname + pokemon.name=nickname[0,10] if nickname && nickname!="" + # Recalculate stats + pokemon.calcStats + if ownerName + Kernel.pbMessage(_INTL("{1} received a Pokémon from {2}.\\se[PokemonGet]\1",$Trainer.name,ownerName)) + else + Kernel.pbMessage(_INTL("{1} received a Pokémon.\\se[PokemonGet]\1",$Trainer.name)) + end + pbStorePokemon(pokemon) + $Trainer.seen[pokemon.species]=true + $Trainer.owned[pokemon.species]=true + pbSeenForm(pokemon) if seeform + return true +end + +def pbGenerateEgg(pokemon,text="") + return false if !pokemon || !$Trainer || $Trainer.party.length>=6 + if pokemon.is_a?(String) || pokemon.is_a?(Symbol) + pokemon=getID(PBSpecies,pokemon) + end + if pokemon.is_a?(Integer) + pokemon=PokeBattle_Pokemon.new(pokemon,EGGINITIALLEVEL,$Trainer) + end + # Get egg steps + dexdata=pbOpenDexData + pbDexDataOffset(dexdata,pokemon.species,21) + eggsteps=dexdata.fgetw + dexdata.close + # Set egg's details + pokemon.name=_INTL("Egg") + pokemon.eggsteps=eggsteps + pokemon.obtainText=text + pokemon.calcStats + # Add egg to party + $Trainer.party[$Trainer.party.length]=pokemon + return true +end + +def pbRemovePokemonAt(index) + return false if index<0 || !$Trainer || index>=$Trainer.party.length + haveAble=false + for i in 0...$Trainer.party.length + next if i==index + haveAble=true if $Trainer.party[i].hp>0 && !$Trainer.party[i].isEgg? + end + return false if !haveAble + $Trainer.party.delete_at(index) + return true +end + +def pbSeenForm(poke,gender=0,form=0) + $Trainer.formseen=[] if !$Trainer.formseen + $Trainer.formlastseen=[] if !$Trainer.formlastseen + if poke.is_a?(String) || poke.is_a?(Symbol) + poke=getID(PBSpecies,poke) + end + if poke.is_a?(PokeBattle_Pokemon) + gender=poke.gender + form=(poke.form rescue 0) + species=poke.species + else + species=poke + end + return if !species || species<=0 + gender=0 if gender>1 + formnames=pbGetMessage(MessageTypes::FormNames,species) + form=0 if !formnames || formnames=="" #Emily 7-23 + $Trainer.formseen[species]=[[],[]] if !$Trainer.formseen[species] + $Trainer.formseen[species][gender][form]=true + $Trainer.formlastseen[species]=[] if !$Trainer.formlastseen[species] + $Trainer.formlastseen[species]=[gender,form] if $Trainer.formlastseen[species]==[] +end + + + +################################################################################ +# Analysing Pokémon +################################################################################ +# Heals all Pokémon in the party. +def pbHealAll + return if !$Trainer + for i in $Trainer.party + i.heal + end +end + +# Returns the first unfainted, non-egg Pokémon in the player's party. +def pbFirstAblePokemon(variableNumber) + for i in 0...$Trainer.party.length + p=$Trainer.party[i] + if p && !p.isEgg? && p.hp>0 + pbSet(variableNumber,i) + return $Trainer.party[i] + end + end + pbSet(variableNumber,-1) + return nil +end + +# Checks whether the player would still have an unfainted Pokémon if the +# Pokémon given by _pokemonIndex_ were removed from the party. +def pbCheckAble(pokemonIndex) + for i in 0...$Trainer.party.length + p=$Trainer.party[i] + next if i==pokemonIndex + return true if p && !p.isEgg? && p.hp>0 + end + return false +end + +# Returns true if there are no usable Pokémon in the player's party. +def pbAllFainted + for i in $Trainer.party + return false if !i.isEgg? && i.hp>0 + end + return true +end + +def pbBalancedLevel(party) + return 1 if party.length==0 + # Calculate the mean of all levels + sum=0 + party.each{|p| sum+=p.level } + return 1 if sum==0 + average=sum.to_f/party.length.to_f + # Calculate the standard deviation + varianceTimesN=0 + for i in 0...party.length + deviation=party[i].level-average + varianceTimesN+=deviation*deviation + end + # Note: This is the "population" standard deviation calculation, since no + # sample is being taken + stdev=Math.sqrt(varianceTimesN/party.length) + mean=0 + weights=[] + # Skew weights according to standard deviation + for i in 0...party.length + weight=party[i].level.to_f/sum.to_f + if weight<0.5 + weight-=(stdev/PBExperience::MAXLEVEL.to_f) + weight=0.001 if weight<=0.001 + else + weight+=(stdev/PBExperience::MAXLEVEL.to_f) + weight=0.999 if weight>=0.999 + end + weights.push(weight) + end + weightSum=0 + weights.each{|weight| weightSum+=weight } + # Calculate the weighted mean, assigning each weight to each level's + # contribution to the sum + for i in 0...party.length + mean+=party[i].level*weights[i] + end + mean/=weightSum + # Round to nearest number + mean=mean.round + # Adjust level to minimum + mean=1 if mean<1 + # Add 2 to the mean to challenge the player + mean+=2 + # Adjust level to maximum + mean=PBExperience::MAXLEVEL if mean>PBExperience::MAXLEVEL + return mean +end + +# Returns the Pokémon's size in millimeters. +def pbSize(pokemon) + dexdata=pbOpenDexData + pbDexDataOffset(dexdata,pokemon.species,33) + baseheight=dexdata.fgetw # Gets the base height in tenths of a meter + dexdata.close + hpiv=pokemon.iv[0]&15 + ativ=pokemon.iv[1]&15 + dfiv=pokemon.iv[2]&15 + spiv=pokemon.iv[3]&15 + saiv=pokemon.iv[4]&15 + sdiv=pokemon.iv[5]&15 + m=pokemon.personalID&0xFF + n=(pokemon.personalID>>8)&0xFF + s=(((ativ^dfiv)*hpiv)^m)*256+(((saiv^sdiv)*spiv)^n) + xyz=[] + if s<10 + xyz=[290,1,0] + elsif s<110 + xyz=[300,1,10] + elsif s<310 + xyz=[400,2,110] + elsif s<710 + xyz=[500,4,310] + elsif s<2710 + xyz=[600,20,710] + elsif s<7710 + xyz=[700,50,2710] + elsif s<17710 + xyz=[800,100,7710] + elsif s<32710 + xyz=[900,150,17710] + elsif s<47710 + xyz=[1000,150,32710] + elsif s<57710 + xyz=[1100,100,47710] + elsif s<62710 + xyz=[1200,50,57710] + elsif s<64710 + xyz=[1300,20,62710] + elsif s<65210 + xyz=[1400,5,64710] + elsif s<65410 + xyz=[1500,2,65210] + else + xyz=[1700,1,65510] + end + return (((s-xyz[2])/xyz[1]+xyz[0]).floor*baseheight/10).floor +end + +# Returns true if the given species can be legitimately obtained as an egg. +def pbHasEgg?(species) + if species.is_a?(String) || species.is_a?(Symbol) + species=getID(PBSpecies,species) + end + evospecies=pbGetEvolvedFormData(species) + compatspecies=(evospecies && evospecies[0]) ? evospecies[0][2] : species + dexdata=pbOpenDexData + pbDexDataOffset(dexdata,compatspecies,31) + compat1=dexdata.fgetb # Get egg group 1 of this species + compat2=dexdata.fgetb # Get egg group 2 of this species + dexdata.close + return false if isConst?(compat1,PBEggGroups,:Ditto) || + isConst?(compat1,PBEggGroups,:Undiscovered) || + isConst?(compat2,PBEggGroups,:Ditto) || + isConst?(compat2,PBEggGroups,:Undiscovered) + baby=pbGetBabySpecies(species) + return true if species==baby # Is a basic species + baby=pbGetBabySpecies(species,0,0) + return true if species==baby # Is an egg species without incense + return false +end + + + +################################################################################ +# Look through Pokémon in storage, choose a Pokémon in the party +################################################################################ +# Yields every Pokémon/egg in storage in turn. +def pbEachPokemon + for i in -1...$PokemonStorage.maxBoxes + for j in 0...$PokemonStorage.maxPokemon(i) + poke=$PokemonStorage[i][j] + yield(poke,i) if poke + end + end +end + +# Yields every Pokémon in storage in turn. +def pbEachNonEggPokemon + pbEachPokemon{|pokemon,box| + yield(pokemon,box) if !pokemon.isEgg? + } +end + +# Choose a Pokémon/egg from the party. +# Stores result in variable _variableNumber_ and the chosen Pokémon's name in +# variable _nameVarNumber_; result is -1 if no Pokémon was chosen +def pbChoosePokemon(variableNumber,nameVarNumber,ableProc=nil, allowIneligible=false) + chosen=0 + pbFadeOutIn(99999){ + scene=PokemonScreen_Scene.new + screen=PokemonScreen.new(scene,$Trainer.party) + if ableProc + chosen=screen.pbChooseAblePokemon(ableProc,allowIneligible) + else + screen.pbStartScene(_INTL("Choose a Pokémon."),false) + chosen=screen.pbChoosePokemon + screen.pbEndScene + end + } + pbSet(variableNumber,chosen) + if chosen>=0 + pbSet(nameVarNumber,$Trainer.party[chosen].name) + else + pbSet(nameVarNumber,"") + end +end + +def pbChooseNonEggPokemon(variableNumber,nameVarNumber) + pbChoosePokemon(variableNumber,nameVarNumber,proc {|poke| + !poke.isEgg? + }) +end + +def pbChooseAblePokemon(variableNumber,nameVarNumber) + pbChoosePokemon(variableNumber,nameVarNumber,proc {|poke| + !poke.isEgg? && poke.hp>0 + }) +end + +def pbChoosePokemonForTrade(variableNumber,nameVarNumber,wanted) + pbChoosePokemon(variableNumber,nameVarNumber,proc {|poke| + if wanted.is_a?(String) || wanted.is_a?(Symbol) + wanted=getID(PBSpecies,wanted) + end + return !poke.isEgg? && !(poke.isShadow? rescue false) && poke.species==wanted + }) +end + + + +################################################################################ +# Checks through the party for something +################################################################################ +def pbHasSpecies?(species) + if species.is_a?(String) || species.is_a?(Symbol) + species=getID(PBSpecies,species) + end + for pokemon in $Trainer.party + next if pokemon.isEgg? + return true if pokemon.species==species + end + return false +end + +def pbHasFatefulSpecies?(species) + if species.is_a?(String) || species.is_a?(Symbol) + species=getID(PBSpecies,species) + end + for pokemon in $Trainer.party + next if pokemon.isEgg? + return true if pokemon.species==species && pokemon.obtainMode==4 + end + return false +end + +def pbHasType?(type) + if type.is_a?(String) || type.is_a?(Symbol) + type=getID(PBTypes,type) + end + for pokemon in $Trainer.party + next if pokemon.isEgg? + return true if pokemon.hasType?(type) + end + return false +end + +# Checks whether any Pokémon in the party knows the given move, and returns +# the index of that Pokémon, or nil if no Pokémon has that move. +def pbCheckMove(move) + move=getID(PBMoves,move) + return nil if !move || move<=0 + for i in $Trainer.party + next if i.isEgg? + for j in i.moves + return i if j.id==move + end + end + return nil +end + + + +################################################################################ +# Regional and National Pokédexes +################################################################################ +# Gets the Regional Pokédex number of the national species for the specified +# Regional Dex. The parameter "region" is zero-based. For example, if two +# regions are defined, they would each be specified as 0 and 1. +def pbGetRegionalNumber(region, nationalSpecies) + if nationalSpecies<=0 || nationalSpecies>PBSpecies.maxValue + # Return 0 if national species is outside range + return 0 + end + pbRgssOpen("Data/regionals.dat","rb"){|f| + numRegions=f.fgetw + numDexDatas=f.fgetw + if region>=0 && region=0 && region=0 && region=$PokemonGlobal.pokedexUnlocked.length-1 + if $Trainer.pokedexSeen(region)>0 + $PokemonGlobal.pokedexViable[0]=region + end + else + numDexes=$PokemonGlobal.pokedexUnlocked.length + case numDexes + when 1 # National Dex only + if $PokemonGlobal.pokedexUnlocked[0] + if $Trainer.pokedexSeen>0 + $PokemonGlobal.pokedexViable.push(0) + end + end + else # Regional dexes + National Dex + for i in 0...numDexes + regionToCheck=(i==numDexes-1) ? -1 : i + if $PokemonGlobal.pokedexUnlocked[i] + if $Trainer.pokedexSeen(regionToCheck)>0 + $PokemonGlobal.pokedexViable.push(i) + end + end + end + end + end +end + +# Unlocks a Dex list. The National Dex is -1 here (or nil argument). +def pbUnlockDex(dex=-1) + index=dex + index=$PokemonGlobal.pokedexUnlocked.length-1 if index<0 + index=$PokemonGlobal.pokedexUnlocked.length-1 if index>$PokemonGlobal.pokedexUnlocked.length-1 + $PokemonGlobal.pokedexUnlocked[index]=true +end + +# Locks a Dex list. The National Dex is -1 here (or nil argument). +def pbLockDex(dex=-1) + index=dex + index=$PokemonGlobal.pokedexUnlocked.length-1 if index<0 + index=$PokemonGlobal.pokedexUnlocked.length-1 if index>$PokemonGlobal.pokedexUnlocked.length-1 + $PokemonGlobal.pokedexUnlocked[index]=false +end + + + +################################################################################ +# Other utilities +################################################################################ +def pbTextEntry(helptext,minlength,maxlength,variableNumber) + $game_variables[variableNumber]=pbEnterText(helptext,minlength,maxlength) + $game_map.need_refresh = true if $game_map +end + +def pbMoveTutorAnnotations(move,movelist=nil) + ret=[] + for i in 0...6 + ret[i]=nil + next if i>=$Trainer.party.length + found=false + for j in 0...4 + if !$Trainer.party[i].isEgg? && $Trainer.party[i].moves[j].id==move + ret[i]=_INTL("LEARNED") + found=true + end + end + next if found + species=$Trainer.party[i].species + if !$Trainer.party[i].isEgg? && movelist && movelist.any?{|j| j==species } + # Checked data from movelist + ret[i]=_INTL("ABLE") + elsif !$Trainer.party[i].isEgg? && $Trainer.party[i].isCompatibleWithMove?(move) + # Checked data from PBS/tm.txt + ret[i]=_INTL("ABLE") + else + ret[i]=_INTL("NOT ABLE") + end + end + return ret +end + +def pbMoveTutorChoose(move,movelist=nil,bymachine=false) + ret=false + if move.is_a?(String) || move.is_a?(Symbol) + move=getID(PBMoves,move) + end + if movelist!=nil && movelist.is_a?(Array) + for i in 0...movelist.length + if movelist[i].is_a?(String) || movelist[i].is_a?(Symbol) + movelist[i]=getID(PBSpecies,movelist[i]) + end + end + end + pbFadeOutIn(99999){ + scene=PokemonScreen_Scene.new + movename=PBMoves.getName(move) + screen=PokemonScreen.new(scene,$Trainer.party) + annot=pbMoveTutorAnnotations(move,movelist) + screen.pbStartScene(_INTL("Teach which Pokémon?"),false,annot) + loop do + chosen=screen.pbChoosePokemon + if chosen>=0 + pokemon=$Trainer.party[chosen] + if pokemon.isEgg? + Kernel.pbMessage(_INTL("{1} can't be taught to an Egg.",movename)) + elsif (pokemon.isShadow? rescue false) + Kernel.pbMessage(_INTL("Shadow Pokémon can't be taught any moves.")) + elsif movelist && !movelist.any?{|j| j==pokemon.species } + Kernel.pbMessage(_INTL("{1} and {2} are not compatible.",pokemon.name,movename)) + Kernel.pbMessage(_INTL("{1} can't be learned.",movename)) + elsif !pokemon.isCompatibleWithMove?(move) + Kernel.pbMessage(_INTL("{1} and {2} are not compatible.",pokemon.name,movename)) + Kernel.pbMessage(_INTL("{1} can't be learned.",movename)) + else + if pbLearnMove(pokemon,move,false,bymachine) + ret=true + break + end + end + else + break + end + end + screen.pbEndScene + } + return ret # Returns whether the move was learned by a Pokemon +end + +def pbChooseMove(pokemon,variableNumber,nameVarNumber) + return if !pokemon + ret=-1 + pbFadeOutIn(99999){ + scene=PokemonSummaryScene.new + screen=PokemonSummary.new(scene) + ret=screen.pbStartForgetScreen([pokemon],0,0) + } + $game_variables[variableNumber]=ret + if ret>=0 + $game_variables[nameVarNumber]=PBMoves.getName(pokemon.moves[ret].id) + else + $game_variables[nameVarNumber]="" + end + $game_map.need_refresh = true if $game_map +end + +# Opens the Pokémon screen +def pbPokemonScreen + return if !$Trainer + sscene=PokemonScreen_Scene.new + sscreen=PokemonScreen.new(sscene,$Trainer.party) + pbFadeOutIn(99999) { sscreen.pbPokemonScreen } +end + +def pbSaveScreen + ret=false + scene=PokemonSaveScene.new + screen=PokemonSave.new(scene) + ret=screen.pbSaveScreen + return ret +end + +def pbConvertItemToItem(variable,array) + item=pbGet(variable) + pbSet(variable,0) + for i in 0...(array.length/2) + if isConst?(item,PBItems,array[2*i]) + pbSet(variable,getID(PBItems,array[2*i+1])) + return + end + end +end + +def pbConvertItemToPokemon(variable,array) + item=pbGet(variable) + pbSet(variable,0) + for i in 0...(array.length/2) + if isConst?(item,PBItems,array[2*i]) + pbSet(variable,getID(PBSpecies,array[2*i+1])) + return + end + end +end + + + + +class PokemonGlobalMetadata + attr_accessor :trainerRecording +end + + + +def pbRecordTrainer + wave=pbRecord(nil,10) + if wave + $PokemonGlobal.trainerRecording=wave + return true + end + return false +end diff --git a/mkxp-z/Kawariki-patches/preload.rb b/mkxp-z/Kawariki-patches/preload.rb index ceed237..d608f97 100644 --- a/mkxp-z/Kawariki-patches/preload.rb +++ b/mkxp-z/Kawariki-patches/preload.rb @@ -216,11 +216,74 @@ module Preload ImportedKeyExpr = /^\s*(?:\$imported|\(\s*\$imported(?:\s*\|\|=\s*\{\s*\})?\s*\))\[(:\w+|'[^']+'|"[^"]+")\]\s*=\s*(.+)\s*$/ def _extract_imported - match = ImportedKeyExpr.match(source) - @imported_entry = !match.nil? - return unless @imported_entry - @imported_key = match[1][0] == ':' ? match[1][1..].to_sym : match[1][1...-1] - @imported_value = match[2] + if source.nil? + puts "Warning: 'source' is nil at the beginning of _extract_imported" + return + end + + # Ensure 'source' is a string, and log the class type if it's not + unless source.is_a?(String) + puts "Warning: 'source' is not a string, it's a #{source.class}!" + return + end + + # Log the encoding type for debugging purposes + # puts "Source encoding before: #{source.encoding.name}" + + # Force early return if source is unexpectedly nil just before encoding + if source.nil? + puts "Warning: 'source' unexpectedly nil before encoding" + return + end + + # Backup the original source value before encoding, to see if it changes unexpectedly + original_source = source.dup + + # Force encode to ASCII-8BIT (binary ASCII), replacing non-ASCII characters with '?' + if source.encoding.name != "ASCII-8BIT" + begin + # puts "Attempting to encode source..." + # Try encoding, but ensure that source remains unchanged if encoding fails + encoded_source = source.encode("ASCII-8BIT", invalid: :replace, undef: :replace, replace: "?") + + # If encoding results in an empty string or nil, restore original source + if encoded_source.nil? || encoded_source.empty? + # puts "Warning: 'source' became nil or empty after encoding! Reverting to original source." + source = original_source + return + else + # Otherwise, use the encoded result + source = encoded_source + end + + # puts "Encoding successful, source encoding is now: #{source.encoding.name}" + rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e + puts "Encoding failed: #{e.message}" + return + rescue StandardError => e + puts "Unexpected error during encoding: #{e.message}" + return + end + end + + # Check source after encoding to ensure it isn't nil + if source.nil? + # puts "Warning: 'source' is nil after encoding!" + return + end + + # After encoding (or skipping), match the regex pattern + match = ImportedKeyExpr.match(source) + + # Check if a match was found + @imported_entry = !match.nil? + + return unless @imported_entry + + # Extract the imported key and value from the match result + @imported_key = match[1][0] == ':' ? match[1][1..].to_sym : match[1][1...-1] + @imported_value = match[2] + end def imported_entry? @@ -436,7 +499,9 @@ module Preload print "Patched #{script.loc}: #{script.log.join(', ')}" if script.log.size > 0 # Warn if Win32API references in source if script.source.include? "Win32API.new" then + print "Warning: Script #{script.loc} uses Win32API." + script.source.gsub!(/class\s+Win32API/, 'module Win32API') require "Win32API.rb" end # Restore encoding @@ -557,6 +622,8 @@ game_ini_path = find_game_ini_in_directory(_config["gameFolder"].to_s) def checkini(file_path) # Check if the file exists + file_path = file_path.nil? || file_path.empty? ? Dir.pwd : file_path + if File.exist?(file_path) # Read the content of the file input_string = File.read(file_path, encoding: 'ASCII-8BIT') @@ -570,7 +637,7 @@ def checkini(file_path) elsif input_string =~ /rxdata/ return 1 else - return 0 # Return nil if none of the patterns match + return 3 # Return nil if none of the patterns match end else puts "File does not exist!"