module Input
DOWN=2
LEFT=4
RIGHT=6
UP=8
A=11
B=12
C=13
X=14
Y=15
Z=16
L=17
R=18
SHIFT=21
CTRL=22
ALT=23
F5=25
F6=26
F7=27
F8=28
F9=29
# GetAsyncKeyState or GetKeyState will work here
@GetKeyState=Win32API.new("user32", "GetAsyncKeyState", "i", "i")
# Returns whether a key is being pressed
def self.getstate(key)
return (@GetKeyState.call(key)&0x8000)>0
end
def self.updateKeyState(i)
if !@stateUpdated[i]
newstate=self.getstate(i)
@triggerstate[i]=(newstate&&@keystate[i]==0)
@releasestate[i]=(!newstate&&@keystate[i]>0)
@keystate[i]=newstate ? @keystate[i]+1 : 0
@stateUpdated[i]=true
end
end
def self.update
if @keystate
for i in 0...256
# just noting that the state should be updated
# instead of thunking to Win32 256 times
@stateUpdated[i]=false
if @keystate[i] > 0
# If there is a repeat count, update anyway
# (will normally apply only to a very few keys)
updateKeyState(i)
end
end
else
@stateUpdated=[]
@keystate=[]
@triggerstate=[]
@releasestate=[]
for i in 0...256
@stateUpdated[i]=true
@keystate[i]=self.getstate(i) ? 1 : 0
@triggerstate[i]=false
@releasestate[i]=false
end
end
end
def self.buttonToKey(button)
case button
when Input::DOWN
return [0x28] # Down
when Input::LEFT
return [0x25] # Left
when Input::RIGHT
return [0x27] # Right
when Input::UP
return [0x26] # Up
when Input::A
return [0x5A,0x10] # Z, Shift
when Input::B
return [0x58,0x1B] # X, ESC
when Input::C
return [0x43,0x0d,0x20] # C, ENTER, Space
when Input::X
return [0x41] # A
when Input::Y
return [0x53] # S
when Input::Z
return [0x44] # D
when Input::L
return [0x51,0x21] # Q, Page Up
when Input::R
return [0x57,0x22] # W, Page Up
when Input::SHIFT
return [0x10] # Shift
when Input::CTRL
return [0x11] # Ctrl
when Input::ALT
return [0x12] # Alt
when Input::F5
return [0x74] # F5
when Input::F6
return [0x75] # F6
when Input::F7
return [0x76] # F7
when Input::F8
return [0x77] # F8
when Input::F9
return [0x78] # F9
else
return []
end
end
def self.dir4
button=0
repeatcount=0
if self.press?(Input::DOWN) && self.press?(Input::UP)
return 0
end
if self.press?(Input::LEFT) && self.press?(Input::RIGHT)
return 0
end
for b in [Input::DOWN,Input::LEFT,Input::RIGHT,Input::UP]
rc=self.count(b)
if rc>0
if repeatcount==0 || rc<repeatcount
button=b
repeatcount=rc
end
end
end
return button
end
def self.dir8
buttons=[]
for b in [Input::DOWN,Input::LEFT,Input::RIGHT,Input::UP]
rc=self.count(b)
if rc>0
buttons.push([b,rc])
end
end
if buttons.length==0
return 0
elsif buttons.length==1
return buttons[0][0]
elsif buttons.length==2
# since buttons sorted by button, no need to sort here
if (buttons[0][0]==Input::DOWN && buttons[1][0]==Input::UP)
return 0
end
if (buttons[0][0]==Input::LEFT && buttons[1][0]==Input::RIGHT)
return 0
end
end
buttons.sort!{|a,b| a[1]<=>b[1]}
updown=0
leftright=0
for b in buttons
if updown==0 && (b[0]==Input::UP || b[0]==Input::DOWN)
updown=b[0]
end
if leftright==0 && (b[0]==Input::LEFT || b[0]==Input::RIGHT)
leftright=b[0]
end
end
if updown==Input::DOWN
return 1 if leftright==Input::LEFT
return 3 if leftright==Input::RIGHT
return 2
elsif updown==Input::UP
return 7 if leftright==Input::LEFT
return 9 if leftright==Input::RIGHT
return 8
else
return 4 if leftright==Input::LEFT
return 6 if leftright==Input::RIGHT
return 0
end
end
def self.count(button)
for btn in self.buttonToKey(button)
c=self.repeatcount(btn)
return c if c>0
end
return 0
end
def self.trigger?(button)
return self.buttonToKey(button).any? {|item| self.triggerex?(item) }
end
def self.repeat?(button)
return self.buttonToKey(button).any? {|item| self.repeatex?(item) }
end
def self.press?(button)
return self.count(button)>0
end
def self.repeatex?(key)
return false if !@keystate
updateKeyState(key)
return @keystate[key]==1 || (@keystate[key]>20 && (@keystate[key]&1)==0)
end
def self.releaseex?(key)
return false if !@releasestate
updateKeyState(key)
return @releasestate[key]
end
def self.triggerex?(key)
return false if !@triggerstate
updateKeyState(key)
return @triggerstate[key]
end
def self.repeatcount(key)
return 0 if !@keystate
updateKeyState(key)
return @keystate[key]
end
def self.pressex?(key)
return self.repeatcount(key)>0
end
end
def getLineBrokenText(bitmap,value,width,dims)
x=0
y=0
textheight=0
ret=[]
if dims
dims[0]=0
dims[1]=0
end
line=0
position=0
column=0
return ret if !bitmap || bitmap.disposed? || width<=0
textmsg=value.clone
lines=0
color=Font.default_color
while ((c = textmsg.slice!(/\n|(\S*([ \r\t\f]?))/)) != nil)
break if c==""
length=c.scan(/./m).length
ccheck=c
if ccheck=="\n"
ret.push(["",x,y,0,textheight,line,position,column,0])
x=0
y+=(textheight==0) ? bitmap.text_size("X").height : textheight
line+=1
textheight=0
column=0
position+=length
next
end
textcols=[]
words=[ccheck]
for i in 0...words.length
word=words[i]
if word && word!=""
textSize=bitmap.text_size(word)
textwidth=textSize.width
if x>0 && x+textwidth>=width-2
# Zero-length word break
ret.push(["",x,y,0,textheight,line,position,column,0])
x=0
column=0
y+=(textheight==0) ? bitmap.text_size("X").height : textheight
line+=1
textheight=0
end
textheight=[textheight,textSize.height].max
ret.push([word,x,y,textwidth,textheight,line,position,column,length])
x+=textwidth
dims[0]=x if dims && dims[0]<x
end
if textcols[i]
color=textcols[i]
end
end
position+=length
column+=length
end
dims[1]=y+textheight if dims
return ret
end
class CharacterEntryHelper
attr_reader :text
attr_reader :maxlength
attr_reader :passwordChar
attr_accessor :cursor
def text=(value)
@text=value
ensure
end
def textChars
chars=text.scan(/./m)
if @passwordChar!=""
chars.length.times {|i|
chars[i]=@passwordChar
}
end
return chars
end
def initialize(text)
@maxlength=-1
@text=text
@passwordChar=""
@cursor=text.scan(/./m).length
ensure
end
def passwordChar=(value)
@passwordChar=value ? value : ""
end
def maxlength=(value)
@maxlength=value
ensure
end
def length
return self.text.scan(/./m).length
end
def insert(ch)
chars=self.text.scan(/./m)
return false if @maxlength>=0 && chars.length>=@maxlength
chars.insert(@cursor,ch)
@text=""
for ch in chars
@text+=ch if ch
end
@cursor+=1
return true
end
def delete
chars=self.text.scan(/./m)
return false if chars.length<=0 || @cursor<=0
chars.delete_at(@cursor-1)
@text=""
for ch in chars
@text+=ch if ch
end
@cursor-=1
return true
end
private
def ensure
return if @maxlength<0
chars=self.text.scan(/./m)
if chars.length>@maxlength && @maxlength>=0
chars=chars[0,@maxlength]
end
@text=""
for ch in chars
@text+=ch if ch
end
end
end
class Window_MultilineTextEntry < Window_Base
def initialize(text,x,y,width,height)
super(x,y,width,height)
@baseColor=self.normal_color
@shadowColor=Color.new(0,0,0,0)
@helper=CharacterEntryHelper.new(text)
@firstline=0
@cursorLine=0
@cursorColumn=0
@frame=0
refresh
end
attr_reader :baseColor
attr_reader :shadowColor
def baseColor=(value)
@baseColor=value
refresh
end
def shadowColor=(value)
@shadowColor=value
refresh
end
def text
@helper.text
end
def maxlength
@helper.maxlength
end
def text=(value)
@helper.text=value
@textchars=nil
self.refresh
end
def maxlength=(value)
@helper.maxlength=value
@textchars=nil
self.refresh
end
def insert(ch)
@helper.cursor=getPosFromLineAndColumn(
@cursorLine,@cursorColumn)
if @helper.insert(ch)
@frame=0
@textchars=nil
moveCursor(0,1)
self.refresh
return true
end
return false
end
def delete
@helper.cursor=getPosFromLineAndColumn(
@cursorLine,@cursorColumn)
if @helper.delete
@frame=0
moveCursor(0,-1) # use old textchars
@textchars=nil
self.refresh
return true
end
return false
end
def getTextChars
if !@textchars
@textchars=getLineBrokenText(self.contents,
@helper.text,self.contents.width,nil)
end
return @textchars
end
def getTotalLines
textchars=getTextChars
if textchars.length==0
return 1
else
tchar=textchars[textchars.length-1]
return tchar[5]+1
end
end
def getLineY(line)
textchars=getTextChars
if textchars.length==0
return 0
else
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
maximumY=0
for i in 0...textchars.length
thisline=textchars[i][5]
y=textchars[i][2]
return y if thisline==line
maximumY=y if maximumY<y
end
return maximumY
end
end
def getColumnsInLine(line)
textchars=getTextChars
if textchars.length==0
return 0
else
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
endpos=0
for i in 0...textchars.length
thisline=textchars[i][5]
thispos=textchars[i][6]
thislength=textchars[i][8]
if thisline==line
endpos+=thislength
end
end
return endpos
end
end
def getPosFromLineAndColumn(line,column)
textchars=getTextChars
if textchars.length==0
return 0
else
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
endpos=0
for i in 0...textchars.length
thisline=textchars[i][5]
thispos=textchars[i][6]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
if thisline==line
endpos=thispos+thislength
# echoln [endpos,thispos+(column-thiscolumn),textchars[i]]
if column>=thiscolumn && column<=thiscolumn+thislength && thislength>0
return thispos+(column-thiscolumn)
end
end
end
if endpos==0
# echoln [totallines,line,column]
# echoln textchars
end
# echoln "endpos=#{endpos}"
return endpos
end
end
def getLastVisibleLine
textchars=getTextChars()
textheight=[1,self.contents.text_size("X").height].max
lastVisible=@firstline+((self.height-32)/textheight)-1
return lastVisible
end
def updateCursorPos(doRefresh)
# Calculate new cursor position
@helper.cursor=getPosFromLineAndColumn(
@cursorLine,@cursorColumn)
if doRefresh
@frame=0
self.refresh
end
if @cursorLine<@firstline
@firstline=@cursorLine
end
lastVisible=getLastVisibleLine()
if @cursorLine>lastVisible
@firstline+=(@cursorLine-lastVisible)
end
end
def moveCursor(lineOffset, columnOffset)
# Move column offset first, then lines (since column offset
# can affect line offset)
# echoln ["beforemoving",@cursorLine,@cursorColumn]
totalColumns=getColumnsInLine(@cursorLine) # check current line
totalLines=getTotalLines()
oldCursorLine=@cursorLine
oldCursorColumn=@cursorColumn
@cursorColumn+=columnOffset
if @cursorColumn<0 && @cursorLine>0
# Will happen if cursor is moved left from the beginning
# of a line
@cursorLine-=1
@cursorColumn=getColumnsInLine(@cursorLine)
elsif @cursorColumn>totalColumns && @cursorLine<totalLines-1
# Will happen if cursor is moved right from the end
# of a line
@cursorLine+=1
@cursorColumn=0
updateColumns=true
end
# Ensure column bounds
totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
# Move line offset
@cursorLine+=lineOffset
@cursorLine=0 if @cursorLine<0
@cursorLine=totalLines-1 if @cursorLine>=totalLines
# Ensure column bounds again
totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
updateCursorPos(
oldCursorLine!=@cursorLine ||
oldCursorColumn!=@cursorColumn
)
# echoln ["aftermoving",@cursorLine,@cursorColumn]
end
def update
@frame+=1
@frame%=20
self.refresh if ((@frame%10)==0)
# Moving cursor
if Input.repeat?(Input::LEFT)
moveCursor(0,-1)
return
elsif Input.repeat?(Input::UP)
moveCursor(-1,0)
return
elsif Input.repeat?(Input::DOWN)
moveCursor(1,0)
return
elsif Input.repeat?(Input::RIGHT)
moveCursor(0,1)
return
elsif Input.pressex?(0x11) && Input.triggerex?(0x24) # Ctrl + Home
# Move cursor to beginning
@cursorLine=0
@cursorColumn=0
updateCursorPos(true)
return
elsif Input.pressex?(0x11) && Input.triggerex?(0x23) # Ctrl + End
# Move cursor to end
@cursorLine=getTotalLines()-1
@cursorColumn=getColumnsInLine(@cursorLine)
updateCursorPos(true)
return
elsif Input.repeatex?(13)
self.insert("\n")
return
elsif Input.repeatex?(8) || Input.repeatex?(0x2E)
# Backspace
self.delete
return
end
# Letter keys
for i in 65..90
if Input.repeatex?(i)
shift=(Input.press?(Input::SHIFT)) ? 0x41 : 0x61
insert((shift+(i-65)).chr)
return
end
end
# Number keys
shifted=")!@\#$%^&*("
unshifted="0123456789"
for i in 48..57
if Input.repeatex?(i)
insert((Input.press?(Input::SHIFT)) ? shifted[i-48].chr : unshifted[i-48].chr)
return
end
end
keys=[
[32," "," "],
[106,"*","*"],
[107,"+","+"],
[109,"-","-"],
[111,"/","/"],
[186,";",":"],
[187,"=","+"],
[189,"-","_"],
[191,"/","?"],
[219,"[","{"],
[190,".",">"],
[188,",","<"],
[220,"\\","|"],
[190,".",""],
[188,",",""],
[221,"]","}"],
[222,"'","\""]
]
for i in keys
if Input.repeatex?(i[0])
insert((Input.press?(Input::SHIFT)) ? i[2] : i[1])
return
end
end
end
def refresh
if !self.contents || self.contents.disposed? ||
self.contents.width!=self.width-32 ||
self.contents.height!=self.height-32
# Re-create bitmap if necessary
self.contents.dispose if self.contents
self.contents=Bitmap.new([1,self.width-32].max,[1,self.height-32].max)
@textchars=nil
end
bitmap=self.contents
bitmap.clear
x=0
y=0
getTextChars
x+=4
width=self.width-32
height=self.height-32
cursorcolor=Color.new(0,0,0)
textchars=getTextChars()
scanlength=@helper.length
startY=getLineY(@firstline)
lastheight=32
for i in 0...textchars.length
thisline=textchars[i][5]
thispos=textchars[i][6]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
textY=textchars[i][2]-startY
# Don't draw lines before the first or zero-length segments
next if thisline<@firstline || thislength==0
# Don't draw lines beyond the window's height
break if textY >= height
c=textchars[i][0]
# Don't draw spaces
next if c==" "
textwidth=textchars[i][3]+4 # add 4 to prevent draw_text from stretching text
textheight=textchars[i][4]
lastheight=textheight
# Draw text
# bitmap.font.color=@shadowColor
# pbDrawShadow(bitmap,textchars[i][1],textY, textwidth, textheight, c)
bitmap.font.color=@baseColor
bitmap.draw_text(textchars[i][1],textY, textwidth, textheight, c)
end
# Draw cursor
if ((@frame/10)&1) == 0
textheight=bitmap.text_size("X").height
cursorY=(textheight*@cursorLine)-startY
cursorX=0
for i in 0...textchars.length
thisline=textchars[i][5]
thispos=textchars[i][6]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
if thisline==@cursorLine &&
@cursorColumn>=thiscolumn && @cursorColumn<=thiscolumn+thislength
cursorY=textchars[i][2]-startY
cursorX=textchars[i][1]
textheight=textchars[i][4]
posToCursor=@cursorColumn-thiscolumn
if posToCursor>=0
partialString=textchars[i][0].scan(/./m)[0,posToCursor].join("")
cursorX+=bitmap.text_size(partialString).width
end
break
end
end
cursorY+=4
cursorHeight=[4,textheight-4,bitmap.text_size("X").height-4].max
bitmap.fill_rect(cursorX,cursorY,2,cursorHeight,cursorcolor)
end
end
end
class Game_System
attr_accessor :journalText
attr_accessor :journalMaxSize
def journalText
@journalText="" if !@journalText
return @journalText
end
def journalMaxSize
@journalMaxSize=200 if !@journalMaxSize
return @journalMaxSize
end
end
class Scene_Journal
def main
sprite=Sprite.new
##########
## Set name of background image here
##########
sprite.bitmap=RPG::Cache.picture("ScrollBackground.jpg")
window=Window_MultilineTextEntry.new("",32,32,640-64,480-64)
window.maxlength=$game_system.journalMaxSize
window.z=1
window.visible=true
window.text=$game_system.journalText
##########
## Set text color here
##########
window.baseColor=Color.new(40,20,0)
window.opacity=0
Graphics.transition(15)
loop do
Graphics.update
Input.update
window.update
if Input.triggerex?(0x1B)
$scene=Scene_Map.new
$game_system.journalText=window.text
end
if $scene != self
break
end
end
Graphics.freeze
sprite.dispose
sprite.bitmap.dispose
window.dispose
end
end