Commit f8d64192 authored by 神楽坂玲奈's avatar 神楽坂玲奈

linux fix

parent 5b522cc2
source http://rubygems.org
source 'http://rubygems.org'
ruby '1.9.3'
gem 'rubysdl', :platform => :ruby
......
......@@ -11,6 +11,56 @@ module Association
Win32::Registry::HKEY_CLASSES_ROOT.create('.ydk') { |reg| reg[nil] = 'mycard' }
Win32::Registry::HKEY_CLASSES_ROOT.create('.yrp') { |reg| reg[nil] = 'mycard' }
Win32::Registry::HKEY_CLASSES_ROOT.create('.deck') { |reg| reg[nil] = 'mycard' }
else
require 'fileutils'
FileUtils.mkdir_p("#{ENV['HOME']}/.local/share/applications") unless File.directory?("#{ENV['HOME']}/.local/share/applications")
open("#{ENV['HOME']}/.local/share/applications/mycard.desktop", 'w') { |f| f.write <<EOF
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=Mycard
Name[zh_CN]=Mycard - 萌卡
Comment=a card game platform
Comment[zh_CN]=卡片游戏对战客户端
Exec=/usr/bin/env ruby -KU lib/main.rb %u
Terminal=false
Icon=#{Dir.pwd}/graphics/system/icon.png
Type=Application
Categories=Game
Path=#{Dir.pwd}
URL=http://my-card.in/
MimeType=x-scheme-handler/mycard;application/x-ygopro-deck;application/x-ygopro-replay'
EOF
}
FileUtils.mkdir_p("#{ENV['HOME']}/.local/share/mime/packages") unless File.directory?("#{ENV['HOME']}/.local/share/mime/packages")
open("#{ENV['HOME']}/.local/share/mime/packages/application-x-ygopro-deck.xml", 'w') { |f| f.write <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-ygopro-deck">
<comment>ygopro deck</comment>
<icon name="application-x-ygopro-deck"/>
<glob-deleteall/>
<glob pattern="*.ydk"/>
</mime-type>
</mime-info>
EOF
}
open("#{ENV['HOME']}/.local/share/mime/packages/application-x-ygopro-replay.xml", 'w') { |f| f.write <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-ygopro-replay">
<comment>ygopro replay</comment>
<icon name="application-x-ygopro-replay"/>
<glob-deleteall/>
<glob pattern="*.yrp"/>
</mime-type>
</mime-info>
EOF
}
system("install -D #{Dir.pwd}/graphics/system/icon.png ~/.icons/application-x-ygopro-deck.png")
system("install -D #{Dir.pwd}/graphics/system/icon.png ~/.icons/application-x-ygopro-replay.png")
system("xdg-mime default mycard.desktop application/x-ygopro-deck application/x-ygopro-replay x-scheme-handler/mycard")
system("update-mime-database #{ENV['HOME']}/.local/share/mime")
system("update-desktop-database #{ENV['HOME']}/.local/share/applications")
end
end
......@@ -23,19 +73,22 @@ module Association
end
def need?
return false unless Windows
return false if $config['no_assoc']
path, command, icon = paths
require 'win32/registry'
begin
Win32::Registry::HKEY_CLASSES_ROOT.open('mycard') { |reg| return true unless reg['URL Protocol'] == path }
Win32::Registry::HKEY_CLASSES_ROOT.open('mycard\shell\open\command') { |reg| return true unless reg[nil] == command }
Win32::Registry::HKEY_CLASSES_ROOT.open('mycard\DefaultIcon') { |reg| return true unless reg[nil] == icon }
Win32::Registry::HKEY_CLASSES_ROOT.open('.ydk') { |reg| return true unless reg[nil] == 'mycard' }
Win32::Registry::HKEY_CLASSES_ROOT.open('.yrp') { |reg| return true unless reg[nil] == 'mycard' }
Win32::Registry::HKEY_CLASSES_ROOT.open('.deck') { |reg| return true unless reg[nil] == 'mycard' }
rescue
return true
if Windows
path, command, icon = paths
require 'win32/registry'
begin
Win32::Registry::HKEY_CLASSES_ROOT.open('mycard') { |reg| return true unless reg['URL Protocol'] == path }
Win32::Registry::HKEY_CLASSES_ROOT.open('mycard\shell\open\command') { |reg| return true unless reg[nil] == command }
Win32::Registry::HKEY_CLASSES_ROOT.open('mycard\DefaultIcon') { |reg| return true unless reg[nil] == icon }
Win32::Registry::HKEY_CLASSES_ROOT.open('.ydk') { |reg| return true unless reg[nil] == 'mycard' }
Win32::Registry::HKEY_CLASSES_ROOT.open('.yrp') { |reg| return true unless reg[nil] == 'mycard' }
Win32::Registry::HKEY_CLASSES_ROOT.open('.deck') { |reg| return true unless reg[nil] == 'mycard' }
rescue
return true
end
else
true #how to detect?
end
end
......@@ -51,11 +104,17 @@ module Association
end
end
end
def start
if need?
request do
register rescue Dialog.uac("ruby/bin/rubyw.exe", "-KU lib/main.rb register_association")
if Windows
request do
register rescue Dialog.uac("ruby/bin/rubyw.exe", "-KU lib/main.rb register_association")
end
else
register
end
end
end
end
\ No newline at end of file
module Dialog
#选择文件对话框
require 'win32api'
GetOpenFileName = Win32API.new("comdlg32.dll", "GetOpenFileNameW", "p", "i")
GetSaveFileName = Win32API.new("comdlg32.dll", "GetSaveFileNameW", "p", "i")
OFN_EXPLORER = 0x00080000
OFN_PATHMUSTEXIST = 0x00000800
OFN_FILEMUSTEXIST = 0x00001000
OFN_ALLOWMULTISELECT = 0x00000200
OFN_FLAGS = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
OFN_ALLOWMULTISELECT
#打开网页
require 'win32ole'
Shell = WIN32OLE.new('Shell.Application')
module_function
if Windows
#选择文件对话框
require 'win32api'
GetOpenFileName = Win32API.new("comdlg32.dll", "GetOpenFileNameW", "p", "i")
GetSaveFileName = Win32API.new("comdlg32.dll", "GetSaveFileNameW", "p", "i")
OFN_EXPLORER = 0x00080000
OFN_PATHMUSTEXIST = 0x00000800
OFN_FILEMUSTEXIST = 0x00001000
OFN_ALLOWMULTISELECT = 0x00000200
OFN_FLAGS = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
OFN_ALLOWMULTISELECT
#打开网页
require 'win32ole'
Shell = WIN32OLE.new('Shell.Application')
end
def get_open_file(title="选择文件", filter = {"所有文件 (*.*)" => "*.*"}, save=nil)
szFile = (0.chr * 20481).encode("UTF-16LE")
szFileTitle = 0.chr * 2049
szTitle = (title+"\0").encode("UTF-16LE")
szFilter = (filter.flatten.join("\0")+"\0\0").encode("UTF-16LE")
szInitialDir = "\0"
if Windows
szFile = (0.chr * 20481).encode("UTF-16LE")
szFileTitle = 0.chr * 2049
szTitle = (title+"\0").encode("UTF-16LE")
szFilter = (filter.flatten.join("\0")+"\0\0").encode("UTF-16LE")
szInitialDir = "\0"
ofn =
[
76, # lStructSize L
0, # hwndOwner L
0, # hInstance L
szFilter, # lpstrFilter L
0, # lpstrCustomFilter L
0, # nMaxCustFilter L
1, # nFilterIndex L
szFile, # lpstrFile L
szFile.size - 1, # nMaxFile L
szFileTitle, # lpstrFileTitle L
szFileTitle.size - 1, # nMaxFileTitle L
szInitialDir, # lpstrInitialDir L
szTitle, # lpstrTitle L
OFN_FLAGS, # Flags L
0, # nFileOffset S
0, # nFileExtension S
0, # lpstrDefExt L
0, # lCustData L
0, # lpfnHook L
0 # lpTemplateName L
].pack("LLLPLLLPLPLPPLS2L4")
Dir.chdir {
if save
GetSaveFileName.call(ofn)
else
GetOpenFileName.call(ofn)
ofn =
[
76, # lStructSize L
0, # hwndOwner L
0, # hInstance L
szFilter, # lpstrFilter L
0, # lpstrCustomFilter L
0, # nMaxCustFilter L
1, # nFilterIndex L
szFile, # lpstrFile L
szFile.size - 1, # nMaxFile L
szFileTitle, # lpstrFileTitle L
szFileTitle.size - 1, # nMaxFileTitle L
szInitialDir, # lpstrInitialDir L
szTitle, # lpstrTitle L
OFN_FLAGS, # Flags L
0, # nFileOffset S
0, # nFileExtension S
0, # lpstrDefExt L
0, # lCustData L
0, # lpfnHook L
0 # lpTemplateName L
].pack("LLLPLLLPLPLPPLS2L4")
Dir.chdir {
if save
GetSaveFileName.call(ofn)
else
GetOpenFileName.call(ofn)
end
}
szFile.delete!("\0".encode("UTF-16LE"))
result = szFile.encode("UTF-8")
if !result.empty? and save.is_a? Array
ext = save[ofn.unpack("LLLPLLLPLPLPPLS2L4")[6] - 1]
if result[-ext.size, ext.size].downcase != ext.downcase
result << ext
end
end
}
szFile.delete!("\0".encode("UTF-16LE"))
result = szFile.encode("UTF-8")
if !result.empty? and save.is_a? Array
ext = save[ofn.unpack("LLLPLLLPLPLPPLS2L4")[6] - 1]
if result[-ext.size, ext.size].downcase != ext.downcase
result << ext
end
result
else
[]
end
result
end
def web(url)
Shell.ShellExecute url
if Windows
Shell.ShellExecute url
else
system('xdg-open ' + url)
end
end
def uac(command, *args)
Shell.ShellExecute File.expand_path(command), args.join(' '), Dir.pwd, "runas"
if Windows
Shell.ShellExecute File.expand_path(command), args.join(' '), Dir.pwd, "runas"
end
end
end
\ No newline at end of file
end
class Action
CardFilter = /(<?\[?Token[ \\\d]*\]?>?|<?(?:\[.*?\])?\[(?:.*?)\][ \d?]*>?|一张怪兽卡|一张魔\/陷卡|一张卡|\?\?)/.to_s
PosFilter = /((?:手卡|手牌|场上|魔陷区|怪兽区|墓地|墓地\|,,,,,\|\*\:\d+张\:\*|额外牌堆|除外区|卡组|卡组顶端|\(\d+\)){1,2})/.to_s
PositionFilter = /(表攻|表守|里守|攻击表示|防守表示|里侧表示|背面守备表示)/.to_s
PhaseFilter = /(抽卡`阶段|准备`阶段|主`阶段1|战斗`阶段|主`阶段2|结束`阶段)/.to_s
CountersFilter = /(?:\()?(\d+)?(?:\))?/.to_s
FieldFilter = /(?:LP:(\d+)\n手卡(?:数)?:(\d+)\n卡组:(\d+)\n墓地:(\d+)\n除外:(\d+)\n前场:\n <(?:#{CountersFilter}#{PositionFilter}\|#{CardFilter})?>\n <(?:#{CountersFilter}#{PositionFilter}\|#{CardFilter})?>\n <(?:#{CountersFilter}#{PositionFilter}\|#{CardFilter})?>\n <(?:#{CountersFilter}#{PositionFilter}\|#{CardFilter})?>\n <(?:#{CountersFilter}#{PositionFilter}\|#{CardFilter})?>\n后场:<(?:#{CountersFilter}#{CardFilter})?><(?:#{CountersFilter}#{CardFilter})?><(?:#{CountersFilter}#{CardFilter})?><(?:#{CountersFilter}#{CardFilter})?><(?:#{CountersFilter}#{CardFilter})?>\n场地\|<(?:无|#{CountersFilter}#{CardFilter})>\n(?:◎|●)→\\)/.to_s
def self.parse_pos(pos)
if index = pos.index("(")
index += 1
result = pos[index, pos.index(")")-index].to_i
result += 10 if pos["手卡"]
result
else
case pos
when "手卡", "手牌"
:hand
when "场上", "魔陷区", "怪兽区"
:field
when "墓地"
:graveyard
when "额外牌堆"
:extra
when "除外区"
:removed
when "卡组顶端"
:decktop
when "卡组"
:deck
end
end
end
def self.parse_card(card)
if card['Token']
@token ||= Card.new('name'=>:Token, 'id'=>-1, 'token'=>true, 'number'=>:"00000000", 'attribute' => :, 'level' => 1, 'card_type' => :通常怪兽, 'stats' => "", 'archettypes' => "", 'mediums' => "", 'lore' => "这张卡作为衍生物使用。")
elsif index = card.rindex('[')
name = card[index+1, card.rindex(']')-index-1].to_sym
result = Card.find(name)
if result.diy? and index = card[0, index].index('[')
card_type = card[index+1, card.index(']')-2].to_sym
card_type = :超量怪兽 if [:XYZ怪兽, :XYZ].include? card_type
result.card_type = card_type
end
result
else
Card.find(nil)
end
end
def self.parse_fieldcard(card)
case card
when "<>"
[nil, nil]
when "<??>"
[:set, Card.find(nil)]
else
[card["表守"] ? :defense : card["里守"] ? :set : :attack, parse_card(card)]
end
end
def self.parse_field(matched)
#LP, 手卡, 卡组, 墓地, 除外, 6指示物, 6表示形式, 6卡, 7指示物, 7表示形式, 7卡, 8指示物, 8表示形式, 8卡, 9指示物, 9表示形式, 9卡, 10指示物, 10表示形式, 10卡, 1指示物, 1卡, 2指示物, 2卡, 3指示物, 3卡, 4指示物, 4卡, 5指示物, 5卡, 0指示物, 0卡
{
:lp => matched[0].to_i,
:hand => matched[1].to_i,
:deck => matched[2].to_i,
:graveyard => matched[3].to_i,
:removed => matched[4].to_i,
6 => matched[7] && {:counters => parse_counters(matched[5]), :position => parse_position(matched[6]), :card => parse_card(matched[7])},
7 => matched[10] && {:counters => parse_counters(matched[8]), :position => parse_position(matched[9]), :card => parse_card(matched[10])},
8 => matched[13] && {:counters => parse_counters(matched[11]), :position => parse_position(matched[12]), :card => parse_card(matched[13])},
9 => matched[16] && {:counters => parse_counters(matched[14]), :position => parse_position(matched[15]), :card => parse_card(matched[16])},
10=> matched[19] && {:counters => parse_counters(matched[17]), :position => parse_position(matched[18]), :card => parse_card(matched[19])},
1 => matched[21] && {:counters => parse_counters(matched[20]), :position => matched[21] == "??" ? :set : :attack, :card => parse_card(matched[21])},
2 => matched[23] && {:counters => parse_counters(matched[22]), :position => matched[23] == "??" ? :set : :attack, :card => parse_card(matched[23])},
3 => matched[25] && {:counters => parse_counters(matched[24]), :position => matched[25] == "??" ? :set : :attack, :card => parse_card(matched[25])},
4 => matched[27] && {:counters => parse_counters(matched[26]), :position => matched[27] == "??" ? :set : :attack, :card => parse_card(matched[27])},
5 => matched[29] && {:counters => parse_counters(matched[28]), :position => matched[29] == "??" ? :set : :attack, :card => parse_card(matched[29])},
0 => matched[31] && {:counters => parse_counters(matched[30]), :position => matched[31] == "??" ? :set : :attack, :card => parse_card(matched[31])}
}
end
def self.parse_position(position)
case position
when "攻击表示", "表攻"
:attack
when "防守表示", "表守"
:defense
when "里侧表示", "背面守备表示", "里守"
:set
end
end
def self.parse_phase(phase)
case phase
when "抽卡`阶段"
:DP
when "准备`阶段"
:SP
when "主`阶段1"
:M1
when "战斗`阶段"
:BP
when "主`阶段2"
:M2
when "结束`阶段"
:EP
end
end
def self.escape_pos(pos)
case pos
when 0..5
"魔陷区(#{pos})"
when 6..10
"怪兽区(#{pos})"
when :hand
"手卡"
when :field
"场上"
when :graveyard
"墓地"
when :extra
"除外区"
when :deck
"卡组顶端"
end
end
def self.escape_pos2(pos)
case pos
when :hand
"手卡"
when :deck
"卡组"
when :graveyard
"墓地"
when :extra
"额外牌堆"
when :removed
"除外区"
when 0..10
"场上(#{pos})"
end
end
def self.escape_position(position)
case position
when :attack
"攻击表示"
when :defense
"防守表示"
when :set
"里侧表示"
end
end
def self.escape_position_short(position)
case position
when :attack
"表攻"
when :defense
"表守"
when :set
"里守"
end
end
def self.escape_phase(phase)
case phase
when :DP
"抽卡`阶段"
when :SP
"准备`阶段"
when :M1
"主`阶段1"
when :BP
"战斗`阶段"
when :M2
"主`阶段2"
when :EP
"结束`阶段"
end
end
def self.parse_counters(counters)
counters.to_i
end
def self.parse(str)
#TODO:效率优化
from_player = nil
case str
when /^\[(\d+)\] (.*)$/m
id = $1.to_i
result = case $2
when /^┊(.*)┊$/m
Chat.new from_player, $1
when /^※\[(.*)\]\n(.*)\n注释.*$/m
card = Card.find($1.to_sym)
case $2
when /(.+怪兽),种族:(.+),属性:(.+),星级:(\d+),攻击:(\d+|?),防御:(\d+|?),效果:(.+)/
CardInfo.new(card, $1.to_sym, $5 == "?" ? nil : $5.to_i, $6 == "?" ? nil : $6.to_i, $3.to_sym, $2.to_sym, $4.to_sym, $7)
when /(魔法|陷阱)种类:(.+),效果:(.+)/
CardInfo.new(card, ($2+$1).to_sym, nil, nil, nil, nil, nil, $3)
else
Unknown.new str
end
when /^※(.*)$/
Chat.new from_player, $1
when /^(◎|●)→=\[0:0:0\]==回合结束==<(\d+)>=\[\d+\]\n#{FieldFilter}(.*)$/ #把这货弄外面的原因是因为这个指令里开头有一个●→,后面还有,下面判msg的正则会判错
field = $~.to_a
field.shift #去掉第一个完整匹配信息
from_player = field.shift == "◎"
turn = field.shift.to_i
msg = field.pop
TurnEnd.new(from_player, parse_field(field), turn, msg)
when /^(◎|●)→#{FieldFilter}$/
field = $~.to_a
field.shift
from_player = field.shift == "◎"
RefreshField.new(from_player, parse_field(field))
when /^(?:(.*)\n)?(◎|●)→(.*)$/m
from_player = $2 == "◎"
msg = $1
case $3
when /^\[\d+年\d+月\d+日禁卡表\] Duel!!/
Reset.new from_player
when /(.*)抽牌/
Draw.new from_player, $1
when "开启更换卡组"
Deck.new from_player
when "更换新卡组-检查卡组中..."
Reset.new from_player
when "换SIDE……"
Side.new from_player
when "卡组洗切", "切洗卡组"
Shuffle.new from_player
when "要连锁吗?"
ActivateAsk.new from_player
when "我要连锁!"
ActivateAnswer.new from_player, true
when "请继续吧~"
ActivateAnswer.new from_player, false
when "将顶牌放回卡组底部"
ReturnToDeckBottom.new(from_player, :decktop)
when /抽取\((\d+)\)张卡/
MultiDraw.new from_player, $1.to_i
when /己方卡组第(\d+)张加入手卡/
ReturnToHand.new(from_player, $1.to_i+71, nil)
when /\[\d+年\d+月\d+日禁卡表\](?:<(.+)> )?先攻/
FirstToGo.new from_player, $1
when /\[\d+年\d+月\d+日禁卡表\](?:<(.+)> )?后攻/
SecondToGo.new from_player, $1
when /(.*)掷骰子,结果为 (\d+)/
Dice.new from_player, $2.to_i, $1
when /(.*)抛硬币,结果为(.+)/
Coin.new from_player, $2=="正面", $1
when "查看卡组"
ViewDeck.new from_player
when /查看卡组上方(\d+)张卡/
ViewDeck.new from_player, $1.to_i
when /刚才抽到的卡是:#{CardFilter}/
Show.new(from_player, :handtop, parse_card($1))
when /从#{PosFilter}~发动#{CardFilter}#{PosFilter}/
Activate.new from_player, parse_pos($1), parse_pos($3), parse_card($2)
when /从#{PosFilter}~召唤#{CardFilter}#{PosFilter}/
Summon.new from_player, parse_pos($1), parse_pos($3), parse_card($2), msg
when /从#{PosFilter}~特殊召唤#{CardFilter}#{PosFilter}(?:呈#{PositionFilter})?/
SpecialSummon.new from_player, parse_pos($1), parse_pos($3), parse_card($2), msg, $4 ? parse_position($4) : :attack
when /从手卡~取#{CardFilter}盖到#{PosFilter}/
Set.new from_player, :hand, parse_pos($2), parse_card($1)
when /将#{CardFilter}从~#{PosFilter}~送往墓地/
SendToGraveyard.new(from_player, parse_pos($2), parse_card($1))
when /将~#{PosFilter}~的#{CardFilter}解~放/
Tribute.new(from_player, parse_pos($1), parse_card($2))
when /随机将一张卡从手卡\((\d+\))~放回卡组顶端/
ReturnToDeck.new(from_player, $1.to_i+10, nil)
when /随机舍弃~手卡~#{CardFilter}/
Discard.new(from_player, :handrandom, parse_card($1))
when /随机将手卡的#{CardFilter}从游戏中除外/
Remove.new from_player, :handrandom, parse_card($1)
when /随机显示一张手卡为:#{CardFilter}/
Show.new(from_player, :handrandom, parse_card($1))
when /第(\d+)张手牌为:#{CardFilter}/
Show.new(from_player, $1.to_i+10, parse_card($2))
when /\|--\+>手卡:(?:\[#{CardFilter}\])*/
MultiShow.new from_player, :hand, $&.scan(/#{CardFilter}/).collect{|matched|parse_card(matched.first)}
when /^(?:(\d+)#{CardFilter}\n?)+$/
from_pos = 71
cards = $&.lines.collect do |line|
line =~ /(\d+)#{CardFilter}/
from_pos ||= $1.to_i + 71
parse_card($2)
end
MultiShow.new from_player, from_pos, cards
when /将#{PosFilter}#{CardFilter}从游戏中除外/
Remove.new from_player, parse_pos($1), parse_card($2)
when /#{CardFilter}#{PosFilter}~放回卡组顶端/
ReturnToDeck.new from_player, parse_pos($2), parse_card($1)
when /#{CardFilter}#{PosFilter}~放回卡组底端/
ReturnToDeckBottom.new from_player, parse_pos($2), parse_card($1)
when /#{CardFilter}#{PosFilter}返回额外牌堆/
ReturnToExtra.new from_player, parse_pos($2), parse_card($1)
when /从#{PosFilter}#{CardFilter}加入手卡/
ReturnToHand.new from_player, parse_pos($1), parse_card($2)
when /(?:己方)?#{PosFilter}.*?#{CardFilter}(?:选择(.*)为对象>)?效果发(?:\~)?动/
EffectActivate.new(from_player, parse_pos($1), parse_card($2))
when /#{PosFilter}#{CardFilter}(?:变|改)为#{PositionFilter}/
ChangePosition.new(from_player, parse_pos($1), parse_card($2), parse_position($3))
when /#{PosFilter}#{CardFilter}转移控制权/
Control.new(from_player, parse_pos($1), parse_card($2))
when /#{PosFilter}#{CardFilter}打开/
Flip.new(from_player, parse_pos($1), parse_card($2))
when /#{PosFilter}#{CardFilter}反转/
FlipSummon.new(from_player, parse_pos($1), parse_card($2))
when /#{PosFilter}#{CardFilter}向(左|右)移动/
from_pos = parse_pos($1)
to_pos = $3 == '左' ? from_pos - 1 : from_pos + 1
Move.new(from_player, from_pos, to_pos, parse_card($2))
when /己方(场上所有怪兽卡\||场上所有魔\/陷卡\||所有手卡\||墓地\|,,,,,\|\*:\d+张:\*)(?:~#{CardFilter})*~全部(放回卡组顶端|送往墓地|除外|加入手卡)/
from_pos = case $1
when "场上所有怪兽卡|"
:monsters
when "场上所有魔\/陷卡|"
:spellsandtraps
when "所有手卡|"
:hand
else
if $1["墓地"]
:graveyard
end
end
to_pos = case $3
when "放回卡组顶端"
:deck
when "送往墓地"
:graveyard
when "除外"
:removed
when "加入手卡"
:hand
end
cards = $&.scan(/#{CardFilter}/).collect{|matched|parse_card matched.first}
MultiMove.new(from_player, from_pos, to_pos, cards)
when /己方#{PosFilter}#{CardFilter}送入对手墓地/
SendToOpponentGraveyard.new(from_player, parse_pos($1), parse_card($2))
when /#{PosFilter}#{CardFilter}选择(我方)?-#{PosFilter}- (?:#{PositionFilter}\|)?#{CardFilter}为效果对象/
Target.new(from_player, parse_pos($1), parse_card($2), $3 ? true : false, parse_pos($4), parse_card($6))
when /#{PhaseFilter}/
ChangePhase.new(from_player, parse_phase($1))
when /LP(损失|回复|变成)<(-?\d+)>/
LP.new(from_player, case $1 when "损失"; :lose; when "回复"; :increase; when "变成"; :become end, $2.to_i)
when /#{PosFilter}#{CardFilter}攻击-#{PosFilter}- #{CardFilter}/
Attack.new(from_player, parse_pos($1), parse_pos($3), parse_card($2))
when /#{CardFilter}(?:直接)?攻击/
Attack.new(from_player, nil, nil, parse_card($1))
when /(?:清空)?#{PosFilter}#{CardFilter}的指示物(?:加一|减一)?,现为#{CountersFilter}/
Counter.new(from_player, parse_pos($1), parse_card($2), :become, parse_counters($3))
when /己方#{PosFilter}#{CardFilter}修改备注为:(.*)/
Note.new(from_player, parse_pos($1), parse_card($2), $3)
when /~特殊召唤#{CardFilter}#{PosFilter}#{PositionFilter}/
Token.new(from_player, parse_pos($2), parse_card($1), parse_position($3))
when /~特殊召唤(\d+)个#{CardFilter}#{PositionFilter}/
MultiToken.new(from_player, $1.to_i, parse_card($2), parse_position($3))
when /添加一张手牌#{CardFilter}/
Add.new(from_player, parse_card($1))
when /将#{PosFilter}#{CardFilter}撕掉!/
Destroy.new(from_player, parse_pos($1), parse_card($2))
else
Unknown.new str
end
else
Unknown.new str
end
result.id = id
result
when /系统消息:.+?\(\d+\)已经退出房间/
Reset.new from_player
when /^(#{CardFilter}\n?)*$/
MultiShow.new from_player, nil, $&.lines.collect{|card|parse_card(card)}
else
Unknown.new str
end
end
def escape
inspect
end
class FirstToGo
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→[11年3月1日禁卡表]先攻"
end
end
class Draw
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→抽牌"
end
end
class MultiDraw
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→抽取(#{@count})张卡"
end
end
class Dice
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→掷骰子,结果为 #{@result}"
end
end
class Reset
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→[11年3月1日禁卡表] Duel!!"
end
end
class ChangePhase
def escape
"[#{@id}] #{from_player ? '◎' : '●'}#{Action.escape_phase(@phase)}"
end
end
class TurnEnd
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→=[0:0:0]==回合结束==<#{@turn}>=[0]\n"+ @field.escape + "#{from_player ? '◎' : '●'}→\\"
end
end
class Shuffle
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→卡组洗切"
end
end
class Set
def escape
case @from_pos
when :hand
"[#{@id}] #{from_player ? '◎' : '●'}→从手卡~取一张#{@card.monster? ? "怪兽卡" : "魔/陷卡"}盖到场上(#{@to_pos})"
end
end
end
class Summon
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→从手卡~召唤#{@card.escape}(#{@to_pos})"
end
end
class SpecialSummon
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→从#{Action.escape_pos2(@from_pos)}~特殊召唤#{@card.escape}(#{@to_pos})呈#{case @position; when :attack; "攻击"; when :defense; "守备";when :set; "背面守备"; end}表示"
end
end
class Activate
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→从手卡~发动#{@card.escape}(#{@to_pos})"
end
end
class SendToGraveyard
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→将#{@card.escape}从~#{Action.escape_pos2(@from_pos)}~送往墓地"
end
end
class Tribute
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→将~#{Action.escape_pos2(@from_pos)}~的#{@card.escape}解~放"
end
end
class Remove
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→将#{Action.escape_pos2(@from_pos)}#{@card.escape}从游戏中除外"
end
end
class ReturnToHand
def escape
pos = case @from_pos
when :deck
"卡组顶端"
when :graveyard
"墓地"
when :removed
"除外区"
when 0..10
"场上(#{@from_pos})"
end
"[#{@id}] #{from_player ? '◎' : '●'}→从#{pos}#{@card.escape}加入手卡"
end
end
class ReturnToDeck
def escape
pos = case @from_pos
when :hand
"手卡"
when :graveyard
"墓地"
when :removed
"除外区"
when 0..10
"场上(#{@from_pos})"
end
"[#{@id}] #{from_player ? '◎' : '●'}#{@card.nil? or @from_pos == :hand ? "一张卡" : @card.escape}#{pos}~放回卡组顶端" #TODO:set=【一张卡】
end
end
class ReturnToDeckBottom
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→将顶牌放回卡组底部"
end
end
class ReturnToExtra
def escape
pos = case @from_pos
when :graveyard
"墓地"
when :removed
"除外区"
when 0..10
"场上(#{pos})"
end
"[#{@id}] #{from_player ? '◎' : '●'}#{@card.escape}#{pos}返回额外牌堆"
end
end
class Flip
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→(#{@from_pos})#{@card.escape}打开"
end
end
class FlipSummon
def escape
"[#{@id}] #{from_player ? '◎' : '●'}→(#{@from_pos})#{@card.escape}反转"
end
end
class ChangePosition
def escape
if @position == :set
if (6..10).include? @from_pos #攻击表示的怪兽,由于iduel没有变成里侧守备指令,所以采用重新放置的方式
"[#{@id}] #{from_player ? '◎' : '●'}→从怪兽区(#{@from_pos})~取一张怪兽卡盖到场上(#{@to_pos})"
else
"[#{@id}] #{from_player ? '◎' : '●'}→(#{@from_pos})#{@card.escape}变为里侧表示"
end
else
"[#{@id}] #{from_player ? '◎' : '●'}→(#{@from_pos})#{@card.escape}改为#{position == :attack ? '攻击' : '防守'}表示"
end
end
end
class Show
def escape
case from_pos
when 0..10
#场上
when Integer
"第#{@from_pos-10}张手牌为:#{@card.escape}"
end
end
end
class MultiShow
def escape
@cards.collect{|card|card.escape}.join("\n")
end
end
class EffectActivate
def escape
pos = case @from_pos
when :hand
"己方手牌"
when :graveyard
"己方墓地"
when :deck
"己方卡组"
when :extra
"己方额外牌堆"
when :removed
"己方除外区"
when 0..10
"(#{@from_pos})"
end
"[#{@id}] #{from_player ? '◎' : '●'}#{pos}#{@card.escape}效果发#{"~" unless (0..10).include? @from_pos}动"
end
end
class Chat
def escape
"[#{@id}] ┊#{@msg}┊"
end
end
end
class Game_Field
def escape
"LP:#{@lp}\n手卡:#{@hand.size}\n卡组:#{@deck.size}\n墓地:#{@graveyard.size}\n除外:#{@removed.size}\n前场:\n" +
@field[6..10].collect{|card|" <#{"#{Action.escape_position_short(card)}|#{card.position == :set ? '??' : "[#{card.card_type}][#{card.name}] #{card.atk}#{' '+card.def.to_s}"}" if card}>\n"}.join +
"后场:" +
@field[1..5].collect{|card|"<#{card.position == :set ? '??' : card.escape if card}>"}.join +
"\n场地|<#{@field[0] ? @field[0].escape : '无'}>\n"
end
def self.parse(str)
end
end
class Card
def escape
if [:通常魔法, :永续魔法, :装备魔法, :场地魔法, :通常陷阱, :永续陷阱, :反击陷阱].include? @card_type
if @position == :set
"一张魔/陷卡"
else
"<[#{@card_type}][#{@name}] >"
end
else
if @position == :set
"一张怪兽卡"
else
"<[#{@card_type}][#{@name}] #{@atk} #{@def}>"
end
end
end
end
class Game_Event
def self.parse(info)
info =~ /^\$([A-Z])\|(.*)$/m
case $1
when "A"
Error
when "B"
Login
when "C"
AllUsers
when "F"
Join
when "Q"
NewRoom
when "G"
Watch
when "H", "M"
Leave
when "J"
Action
when "K"
WatchAction
when 'N'
PrivateChat
when "O"
Chat
when "P"
AllRooms
when "R"
QROOMOK #卡表
else
Unknown
end.parse($2)
end
class Login
def self.parse(info)
info = info.split(",")
#>> $B|201629,zh99997,5da9e5fa,Level-1 (总经验:183),,20101118
info[3] =~ /Level-(\d)+ \(总经验:(\d+)\)/
result = self.new User.new(info[0].to_i, info[1], $1.to_i, $2.to_i)
$game.get_friends
$game.session = info[2]
$game.key = ($game.user.id - 0x186a0) ^ 0x22133
result
end
end
class AllUsers
def self.parse(info)
self.new info.split(',').collect{|user|User.parse(user)}
end
end
class AllRooms
def self.parse(info)
info = info.split("|")
rooms = []
templist = rooms
empty = false
info.each do |room|
if room == '~~'
empty = true
templist = []
else
room = room.split(",")
room = if empty
Room.new(room[0].to_i, room[1], User.parse(room[2]), nil, room[3]=="1", Room::Color[room[4].to_i], nil, room[6])
else
Room.new(room[0].to_i, room[3], User.parse(room[1]), User.parse(room[2]), false, Room::Color[room[5].to_i], room[3])
end
room.name = room.name[1,room.name.size-1] #iduel服务器发来的消息中,房名前有一空格
templist << room
end
end
rooms = templist + rooms
self.new rooms
end
end
class NewUser
def self.parse(info)
p info
#super
#@args = @args.collect do |user|
# User.new(user)
#end
end
end
class MissingUser
def self.parse(info)
p info
#super
#@args = @args.collect do |user|
# User.new(user)
#end
end
end
class Join
def self.parse(info)
self.new Room.new(info.to_i)
end
end
class Leave
def self.parse(info)
self.new
end
end
class NewRoom
def self.parse(info)
id, x, player1, player2 = info.split(",", 4)
room = Room.new(id.to_i)
room.player1 = User.parse(player1)
room.player2 = User.parse(player2)
room.name = room.name[1,room.name.size-1] #iduel服务器发来的消息中,房名前有一空格
self.new room
end
end
#"Q"
#"273,1,zh99998(201448),zh99997(201629)"
class Watch
def self.parse(info)
id, name = info.split(",", 2)
name = name[1,name.size-1] #iduel服务器发来的消息中,房名前有一空格
self.new Room.new(id.to_i, name)
end
end
class Action
def self.parse(info)
info =~ /(.*)▊▊▊.*?$/m
info = $1
info["◎"] = "●" if info["◎"]
self.new ::Action.parse(info), info
end
end
class Leave
end
class Chat
def self.parse(info)
user, content = info.split(",", 2)
user = user == "System" ? User.new(100000, "iDuel管理中心") : User.parse(user)
content.gsub!('@@@@', ',')
self.new(ChatMessage.new(user, content, :lobby))
end
end
class Error
def self.parse(info)
title, message,fatal = case info.to_i
when 0x00
["网络错误", "网络连接中断",true]
when 0x65
["出错啦~", "服务器程序出现未知错误,请记录好出现错误的事件,并联系管理员。",true]
when 0x66
["错误", "通信验证错误",true]
when 0x67
["错误", "通信钥匙错误",true]
when 0xc9
["登录失败", "错误的帐号名或密码",true]
when 0xca
["登录失败", "你的账号还未激活",true]
when 0xcb
["登录失败", "你的账号被系统封锁",true]
when 0x12d
["错误", "房间已满",false]
when 0x12e
["错误", "房间密码错误",false]
when 0x12f
["错误", "你没有权限给房间上密码",false]
when 0x130
["错误", "你已经加入房间,请不要重新加入",false]
when 0x131
["加入房间", "你未达到房间要求的等级限制。",false]
when 0x132
["观战错误", "所请求的房间无效,或未开始决斗",false]
when 0x133
["观战错误", "你已经在该房间观战",false]
when 0x134
["发送信息错误", "你还未加入房间",false]
when 0x135
["错误", "请求的房间无效",false]
end
self.new title, message, fatal
end
end
class Unknown
def self.parse(*args)
$log.info '--------Unknown iDuel Event-------'
p $1, $2, args
end
end
#以下iDuel专有
class WatchAction < Action
attr_reader :user
def initialize(action, str, user)
@user = user
super(action, str)
if $game.room.nil? #通常是由于断线重连引起的
Game_Event.push Watch.new(Room.new(0, @user, User.new(0,"")))
#$game.refresh
end
@action.from_player = @user == $game.room.player1
end
def self.parse(info)
info =~ /(.+)\((\d+)\), (.*)/m
self.new ::Action.parse($3), $3, User.new($2.to_i, $1)
end
end
class QROOMOK < Game_Event
def self.parse(info)
end
end
class PrivateChat < Chat
def self.parse(info)
user, content = info.split(",", 2)
user = User.parse(user)
content.gsub!('@@@@', ',')
self.new(ChatMessage.new(user, content, user))
end
end
end
\ No newline at end of file
#encoding: UTF-8
load 'lib/iduel/window_login.rb'
require 'open-uri'
class Iduel < Game
Version = "20110131"
Server = "iduel.ocgsoft.cn"
Register_Url = 'http://www.duelcn.com/member.php?mod=join_id'
Port = 38522
RS = "¢"
attr_accessor :session
attr_accessor :key
def initialize
super
require 'digest/md5'
load 'lib/iduel/action.rb'
load 'lib/iduel/event.rb'
load 'lib/iduel/user.rb'
load 'lib/iduel/replay.rb'
end
def rename
##8|241019,测试改昵称,5b58559aaf8869282fe3cb9585ffa909¢
#$N|iDuel系统,您的改名请求已经提交,重新登录后即可看到效果。¢
end
def login(username, password)
connect
md5 = Digest::MD5.hexdigest(password)
send(0, username, md5, checknum("LOGINMSG", username, md5), Version)
end
def refresh
send(1, @key, checknum("UPINFOMSG", @session))
end
def host(name, password="", lv=0, color = 0)
end
def host(room_name, room_config)
password = ""
color = 0
lv = 0
send(6, @key, room_name, password, checknum("JOINROOMMSG", @session + room_name + password + "0"), 0, color, lv, 0, 0, 0) #TODO:v.ak, v.al
end
def join(room, password="")
send(6, @key, room.id, password, checknum("JOINROOMMSG", @session + room.id.to_s + password + "1"),1)
end
def watch(room, password="")
send(5, @key, room.id, password, checknum("WATCHROOMMSG", "#{@session}#{room.id}#{password}"))
end
def leave
return unless @room
if @room.include? @user
send(10, @key, room.id, checknum("QROOM", @session + room.id.to_s))
else
send(9, @key, checknum("QUITWATCHROOM", @session))
end
end
def action(action)
send(2, "#{checknum("RMSG", @session)}@#{@key}", "#{action.escape}▊▊▊mycard") #消息校验字串,为了防止由于mycard开源造成外挂泛滥扰乱正常iduel秩序,这里不模仿iduel计算校验字串,直接发送mycard供iduel识别
end
def exit
@recv.exit if @recv
if @conn
leave
send(11, @key, checknum("ULO", "#{@session}"))
@conn.close
@conn = nil
end
super
end
def recv(info)
if info.nil?
@conn.close
@conn = nil
$log.error 'socket已中断'
Game_Event.push Game_Event::Error.parse(0)
else
info.chomp!(RS)
info.delete!("\r")
$log.info ">> #{info}"
Game_Event.push Game_Event.parse info
end
end
#def qroom(room)
# send(10, @key, room.id, checknum("QROOM", @session + room.id.to_s))
#end
def chat(chatmessage)
msg = chatmessage.message.gsub(",", "@@@@")
case chatmessage.channel
when :lobby
send(4, @key, msg, checknum("CHATP", @session))
when User #私聊
send(3, @key, "#{chatmessage.channel.name}(#{chatmessage.channel.id})", msg, checknum("CHATX", @session + "X" + "#{chatmessage.channel.name}(#{chatmessage.channel.id})"))
when Room #房间消息:向双方分别私聊
channel = chatmessage.channel
chatmessage.channel = channel.player1
chat chatmessage
if channel.player2
chatmessage.channel = channel.player2
chat chatmessage
end
chatmessage.channel = channel
end
#4|241019,test,2368c6b89b3e2eedb92e1b624a2a157c
end
def get_friends
$config['iDuel']['friends'] ||= []
$config['iDuel']['friends'].each {|id|User.new(id).friend = true}
Thread.new do
begin
open("http://www.duelcn.com/home.php?mod=space&uid=#{@user.id-100000}&do=friend&view=me&from=space") do |file|
$config['iDuel']['friends'].each {|id|User.new(id).friend = false}
$config['iDuel']['friends'].clear
file.set_encoding "GBK", "UTF-8"
file.read.scan(/<a href="home.php\?mod=space&amp;uid=(\d+)" title=".*" target="_blank">.*<\/a>/) do |uid, others|
id = uid.to_i + 100000
User.new(id).friend = true
$config['iDuel']['friends'] << id
end
Config.save
end
rescue Exception => exception
$log.error('读取好友信息') {[exception.inspect, *exception.backtrace].collect{|str|str.encode("UTF-8")}.join("\n")}
end
end
end
def show_chat_self
true
end
private
def connect
require 'socket'
begin
@conn = TCPSocket.new(Server, Port) #TODO: 阻塞优化,注意login。下面注释掉的两句实现connect无阻塞,但是login依然会阻塞所以只优化这里没有意义
#@conn = Socket.new(:INET, :STREAM)
@conn.set_encoding "GBK", "UTF-8", :invalid => :replace, :undef => :replace
Thread.abort_on_exception=true
@recv = Thread.new do
begin
#@conn.connect Socket.pack_sockaddr_in(Port, Server)
recv @conn.gets(RS) while @conn
rescue => exception
$log.error('iduel-connect-1') {[exception.inspect, *exception.backtrace].collect{|str|str.encode("UTF-8")}.join("\n")}
Game_Event.push Game_Event::Error.new(exception.class.to_s, exception.message)
ensure
self.exit
end
end
rescue => exception
$log.error('iduel-connect-2') {[exception.inspect, *exception.backtrace].collect{|str|str.encode("UTF-8")}.join("\n")}
Game_Event.push Game_Event::Error.new("网络错误", "连接服务器失败")
end
end
def checknum(head, *args)
Digest::MD5.hexdigest("[#{head}]_#{args.join('_')}_SCNERO".gsub("\n", "\r\n").encode("GBK"))
end
def send(head, *args)
return unless @conn
info = "##{head.to_s(16).upcase}|#{args.join(',')}" + RS
$log.info "<< #{info}"
info.gsub!("\n", "\r\n")
(@conn.write info) rescue Game_Event.push Game_Event::Error.new($!.class.to_s, $!.message)
end
def self.get_announcements
#公告
$config['iDuel']['announcements'] ||= [Announcement.new("正在读取公告...", nil, nil)]
Thread.new do
begin
open('http://www.duelcn.com/topic-Announce.html') do |file|
announcements = []
file.read.scan(/<li><a href="(.*?)" title="(.*?)" target="_blank">/).each do |url, title|
announcements << Announcement.new(title.encode("UTF-8"), "http://www.duelcn.com/#{url}", nil)
end
$config['iDuel']['announcements'].replace announcements
Config.save
end
rescue Exception => exception
$log.error('公告') {[exception.inspect, *exception.backtrace].collect{|str|str.encode("UTF-8")}.join("\n")}
end
end
end
get_announcements
end
name: iDuel
\ No newline at end of file
class Replay
User_Filter = /(.+?)(?:\((\d+)\))?(?:\(\d+:\d+:\d+\))?(?:: |:) */
Delimiter = /^#{User_Filter}\n ?/
Player_Filter = /#{Delimiter}\[\d+\] ◎→/
Opponent_Filter =/#{Delimiter}\[\d+\] ●→/
HTML_Player_Filter = /<font color=blue><strong>#{User_Filter}<\/strong>/
HTML_Opponent_Filter = /<font color=red><strong>#{User_Filter}<\/strong>/
attr_accessor :room, :player1, :player2, :actions
def add(action)
# user = action.from_player ? $game.player1 : $game.player2
# @file.write("#{user.name}(#{user.id}):\r\n#{action.escape}\r\n")
end
def self.load(filename)
#TODO:效率优化
file = open(filename)
file.set_encoding "GBK", "UTF-8", :invalid => :replace, :undef => :replace
result = self.new(file)
contents = file.read.strip
if contents['<td width="75%" valign="top">']
result.player1 = User.new($2.to_i, $1) if contents =~ HTML_Player_Filter
result.player2 = User.new($2.to_i, $1) if contents =~ HTML_Opponent_Filter
from_players = contents.scan(Regexp.union(HTML_Player_Filter, HTML_Opponent_Filter)).collect{|matched|matched[0] ? true : false} #匹配player成功matched前两位有值,opponent成功后两位有值,["尸体", "100015", nil, nil], il, nil], [nil, nil, "游戏的徒弟", "288436"]
#去除HTML标签
contents.gsub!(/<.*?>/,'')
#处理HTML转义字符
require 'cgi'
contents = CGI.unescape_html(contents)
else
result.player1 = User.new($2 ? $2.to_i : $1.to_sym, $1) if contents =~ Player_Filter
result.player2 = User.new($2 ? $2.to_i : $1.to_sym, $1) if contents =~ Opponent_Filter
from_players = contents.scan(Delimiter).collect do |matched|
id = (matched[1] || matched[0]).to_sym
name = matched[0]
if result.player1 and result.player1.id == id
true
elsif result.player2 and result.player2.id == id
false
elsif result.player1.nil?
result.player1 = User.new(id , name)
true
elsif result.player2.nil?
result.player2 = User.new(id, name)
false
else
#无法匹配玩家,一般是观战消息..
false
end
end
end
result.player1 ||= User.new($1.to_sym, "我")
result.player2 ||= User.new($1.to_sym, "对手")
lines = contents.split(Delimiter)
lines.shift #split后,在第一个操作之前会多出一个空白元素
if from_players.empty?
Game_Event.push Game_Event::Error.new("播放战报", "战报无法识别")
return []
end
lines = lines.each_slice(lines.size/from_players.size).collect{|a|a.last.strip}
from_players = from_players.to_enum
result.actions = lines.collect do |action_str|
action = Action.parse action_str
action.from_player = from_players.next
Game_Event::Action.new(action, action_str)
end
$game.room = result.room = Room.new(0, "Replay", result.player1, result.player2)
result
end
def self.html_decode(text)
text.gsub(Regexp.new(HTML_Replacement.keys.collect{|key|Regexp.escape key}.join('|')), HTML_Replacement)
end
def get
@actions.shift
end
def eof?
@actions.empty?
end
end
class User
attr_accessor :level, :exp
def self.parse(info)
if info =~ /(.+)\((\d+)\)/
new $2.to_i, $1
else
nil
end
end
def initialize(id, name = "", level = nil, exp = nil)
@id = id
@name = name
@level = level
@exp = exp
end
def set(id, name = :keep, level = :keep, exp = :keep)
@id = id unless id == :keep
@name = name unless name == :keep
@level = level unless level == :keep
@exp = exp unless exp == :keep
end
def avatar(size = :small)
cache = "graphics/avatars/#{@id}_#{size}.png"
result = Surface.load(cache) rescue Surface.load("graphics/avatars/loading_#{size}.png")
scene = $scene
if block_given?
yield result
Thread.new do
open("http://www.duelcn.com/uc_server/avatar.php?uid=#{id-100000}&size=#{size}", 'rb') {|io|open(cache, 'wb') {|c|c.write io.read}} rescue cache = "graphics/avatars/error_#{size}.png"
(yield Surface.load(cache) if scene == $scene) rescue nil
end
else
result
end
end
def status
room = room()
result = case
when room.nil?
:lobby
when room.player2
:dueling
else
:waiting
end
result
end
def room
$game.rooms.find{|room|room.include? self}
end
def space
Dialog.web "http://www.duelcn.com/home.php?mod=space&uid=#{@id-100000}"
end
def color
@friend ? [255,0,0] : [0,0,0]
end
end
class Window_Login
def clicked
return if @last_clicked and Time.now - @last_clicked < 3 #防止重复点击
case @index
when :login
Widget_Msgbox.new("iDuel", "正在登录")
$scene.draw #强制重绘一次,下面会阻塞
$game = Iduel.new
$config[$config['game']]['username'] = @username_inputbox.value
$config[$config['game']]['password'] = @remember_password.checked? ? @password_inputbox.value : nil
Config.save
$game.login(@username_inputbox.value, @password_inputbox.value)
@last_clicked = Time.now
when :register
Dialog.web Iduel::Register_Url
@last_clicked = Time.now
when :replay
file = Dialog.get_open_file("播放录像", "所有支持的录像 (*.txt;*.htm)" => "*.txt;*.htm", "iDuel的html的录像 (*.htm)" => "*.htm", "文本录像 (*.txt)" => "*.txt")
if !file.empty?
$game = Iduel.new
$game.user = User.new(0)
Widget_Msgbox.new("回放录像", "录像读取中...")
$scene.draw
$log.info('iduel window_login'){'loading reply file'}
$scene = Scene_Replay.new Replay.load file
end
@last_clicked = Time.now
end
end
end
\ No newline at end of file
#输入法by夏娜
require 'dl'
require 'dl/import'
module RM_IME
extend DL::Importer
Dir.chdir('ruby/bin'){dlload 'RMIME.dll'}
extern 'void _init(long, int, int)'
extern 'void _update(int, int)'
extern 'void _dispose()'
extern 'void _get_text(char*)'
extern 'void _back(char*)'
module RM_INPUT
if Windows
require 'dl'
require 'dl/import'
extend DL::Importer
dlload 'user32.dll'
extern 'int GetAsyncKeyState(int)'
extern 'long FindWindow(char*, char*)'
ENTER = 0x0D
ESC = 0x1B
TAB = 0x09
UP = 0x26
DOWN = 0x28
LEFT = 0x25
RIGHT = 0x27
Key_Hash = {}
Key_Repeat = {}
module_function
#----------------------------------------------------------------------
# ● 按下判断
#----------------------------------------------------------------------
def press?(rkey)
return GetAsyncKeyState(rkey) != 0
end
#----------------------------------------------------------------------
# ● 重复按下判断
#----------------------------------------------------------------------
def repeat?(rkey)
result = GetAsyncKeyState(rkey)
if result != 0
if Key_Repeat[rkey].nil?
Dir.chdir('ruby/bin') { dlload 'RMIME.dll' }
extern 'void _init(long, int, int)'
extern 'void _update(int, int)'
extern 'void _dispose()'
extern 'void _get_text(char*)'
extern 'void _back(char*)'
module RM_INPUT
extend DL::Importer
dlload 'user32.dll'
extern 'int GetAsyncKeyState(int)'
extern 'long FindWindow(char*, char*)'
ENTER = 0x0D
ESC = 0x1B
TAB = 0x09
UP = 0x26
DOWN = 0x28
LEFT = 0x25
RIGHT = 0x27
Key_Hash = {}
Key_Repeat = {}
module_function
#----------------------------------------------------------------------
# ● 按下判断
#----------------------------------------------------------------------
def press?(rkey)
return GetAsyncKeyState(rkey) != 0
end
#----------------------------------------------------------------------
# ● 重复按下判断
#----------------------------------------------------------------------
def repeat?(rkey)
result = GetAsyncKeyState(rkey)
if result != 0
if Key_Repeat[rkey].nil?
Key_Repeat[rkey] = 0
return true
end
Key_Repeat[rkey] += 1
else
Key_Repeat[rkey] = nil
Key_Hash[rkey] = 0
end
if !Key_Repeat[rkey].nil? and Key_Repeat[rkey] > 4
Key_Repeat[rkey] = 0
return true
else
return false
end
Key_Repeat[rkey] += 1
else
Key_Repeat[rkey] = nil
Key_Hash[rkey] = 0
end
if !Key_Repeat[rkey].nil? and Key_Repeat[rkey] > 4
Key_Repeat[rkey] = 0
return true
else
return false
end
end
#----------------------------------------------------------------------
# ● 击键判断
#----------------------------------------------------------------------
def trigger?(rkey)
result = GetAsyncKeyState(rkey)
if Key_Hash[rkey] == 1 and result != 0
return false
end
if result != 0
Key_Hash[rkey] = 1
return true
else
Key_Hash[rkey] = 0
return false
#----------------------------------------------------------------------
# ● 击键判断
#----------------------------------------------------------------------
def trigger?(rkey)
result = GetAsyncKeyState(rkey)
if Key_Hash[rkey] == 1 and result != 0
return false
end
if result != 0
Key_Hash[rkey] = 1
return true
else
Key_Hash[rkey] = 0
return false
end
end
end
HWND = RM_INPUT.FindWindow('SDL_app', WM.caption[0])
end
HWND = RM_INPUT.FindWindow('SDL_app', WM.caption[0])
module_function
def init
return unless Windows
return if @active
$log.info('输入法'){'开启'}
$log.info('输入法') { '开启' }
_init(HWND, 0, 0)
@x = 0
@y = 0
@active = true
end
def set(x,y)
def set(x, y)
return unless Windows
@x = x
@y = y
end
def text
return "" unless Windows
buf = 0.chr * 1024
_get_text(buf)
buf.force_encoding("UTF-8")
buf.delete!("\0")
end
def update
_update(@x,@y)
buf = [0,0].pack("LL")
return unless Windows
_update(@x, @y)
buf = [0, 0].pack("LL")
_back(buf)
buf = buf.unpack("LL")
@backspace = buf[0] == 1
@delete = buf[1] == 1
end
def dispose
return unless Windows
return if !@active
$log.info('输入法'){'关闭'}
$log.info('输入法') { '关闭' }
_dispose
@active = false
end
def active?
@active
end
def backspace?
@backspace
end
def delete?
@delete
end
def left?
return unless Windows
RM_INPUT.repeat?(RM_INPUT::LEFT)
end
def right?
return unless Windows
RM_INPUT.repeat?(RM_INPUT::RIGHT)
end
def tab?
return unless Windows
RM_INPUT.trigger?(RM_INPUT::TAB)
end
def enter?
return unless Windows
RM_INPUT.trigger?(RM_INPUT::ENTER)
end
def esc?
return unless Windows
RM_INPUT.trigger?(RM_INPUT::ESC)
end
end
class Widget_InputBox < Window
attr_accessor :type
attr_reader :value
......@@ -139,8 +167,9 @@ class Widget_InputBox < Window
@@active = nil
@@cursor = nil
@@focus = true
def initialize(x,y,width,height,z=300, &proc)
super(x,y,width,height,z)
def initialize(x, y, width, height, z=300, &proc)
super(x, y, width, height, z)
@font = TTF.open("fonts/wqy-microhei.ttc", 20)
@proc = proc
@value = ""
......@@ -149,6 +178,7 @@ class Widget_InputBox < Window
@count = 0
@char_pos = [2]
end
def value=(value)
return if @value == value
@value = value
......@@ -161,6 +191,7 @@ class Widget_InputBox < Window
end
refresh
end
def index=(index)
if index > @value.size
index = @value.size
......@@ -174,17 +205,19 @@ class Widget_InputBox < Window
@@cursor.x = @x + @char_pos[@index]
RM_IME.set(@@cursor.x, @@cursor.y)
end
def refresh
clear
@font.draw_blended_utf8(@contents, @type == :password ? '*' * @value.size : @value, 2, 0, 0x00, 0x00, 0x00) unless @value.empty?
end
def clicked
RM_IME.init
@@active = self
@@focus = true
unless @@cursor and !@@cursor.destroyed?
@@cursor = Window.new(0,0,2,@height-4,301)
@@cursor.contents.fill_rect(0,0,@@cursor.width,@@cursor.height,0xFF000000)
@@cursor = Window.new(0, 0, 2, @height-4, 301)
@@cursor.contents.fill_rect(0, 0, @@cursor.width, @@cursor.height, 0xFF000000)
end
@@cursor.y = @y + 2
mouse_x = Mouse.state[0] - @x
......@@ -197,15 +230,17 @@ class Widget_InputBox < Window
return self.index = index - 1
end
end
self.index = @value.size
end
end
def clear(x=0, y=0, width=@width, height=@height)
@contents.fill_rect(x,y,width,height,0x110000FF)
@contents.fill_rect(x+2,y+2,width-4,height-4,0xFFFFFFFF)
@contents.fill_rect(x, y, width, height, 0x110000FF)
@contents.fill_rect(x+2, y+2, width-4, height-4, 0xFFFFFFFF)
end
def update
return unless self == @@active and @@focus
if @count >= 40
......@@ -214,7 +249,6 @@ class Widget_InputBox < Window
else
@count += 1
end
RM_IME.update
new_value = self.value.dup
new_index = self.index
......@@ -240,7 +274,7 @@ class Widget_InputBox < Window
self.index = new_index
if @proc
if RM_IME.esc?
self.value = '' if @proc.call :ESC
self.value = '' if @proc.call :ESC
end
if RM_IME.tab?
self.value = '' if @proc.call :TAB
......@@ -250,12 +284,14 @@ class Widget_InputBox < Window
end
end
end
def destroy
if @@active == self
Widget_InputBox.focus = false
end
super
end
def self.focus=(focus)
@@focus = focus
if !@@focus
......
......@@ -11,7 +11,7 @@ class Widget_ScrollBar < Window
@scroll ||= 0
@scroll_max ||= 0
@scrolling = nil
Surface.transform_draw(@back,@contents,0,1,@contents.h.to_f/@back.h,0,0,0,0,0)
Surface.transform_draw(@back,@contents,0,1,@contents.h.to_f/@back.h,0,0,0,0,0) rescue nil
refresh
end
def index=(index)
......
require_relative 'window_host'
class Window_LobbyButtons < Window_List
def initialize(x, y)
@items = [I18n.t('lobby.faq'), I18n.t('lobby.filter'), I18n.t('lobby.editdeck'), I18n.t('lobby.newroom'), "自动匹配"]
@items = [I18n.t('lobby.faq'), I18n.t('lobby.filter'), I18n.t('lobby.editdeck'), I18n.t('lobby.newroom'), I18n.t('lobby.match')]
@button = Surface.load("graphics/lobby/button.png")
super(x, y, @items.size*@button.w/3+@items.size*4, 30)
@font = TTF.open("fonts/wqy-microhei.ttc", 15)
......
......@@ -39,10 +39,10 @@ class Ygocore < Game
jid = Jabber::JID::new @username, 'my-card.in', 'mycard'
end
@@im = Jabber::Client.new(jid)
@@im_room = Jabber::MUC::MUCClient.new(@@im)
Jabber.logger = $log
Jabber.debug = true
@@im = Jabber::Client.new(jid)
@@im_room = Jabber::MUC::MUCClient.new(@@im)
Jabber.logger = $log
Jabber.debug = true
@@im.on_exception do |exception, c, where|
$log.error('聊天出错') { [exception, c, where] }
......@@ -87,7 +87,7 @@ class Ygocore < Game
http = EM::HttpRequest.new("http://my-card.in/servers.json").get
http.callback {
begin
self.servers.replace JSON.parse(http.response).collect {|data| Server.new(data['id'], data['name'], data['ip'], data['port'], data['auth'])}
self.servers.replace JSON.parse(http.response).collect { |data| Server.new(data['id'], data['name'], data['ip'], data['port'], data['auth']) }
self.filter[:servers] = self.servers.clone
rescue
Game_Event.push Game_Event::Error.new('ygocore', '读取服务器列表失败.1', true)
......@@ -96,7 +96,7 @@ class Ygocore < Game
#EventMachine::connect "mycard-server.my-card.in", 9997, Client
ws = WebSocket::EventMachine::Client.connect(:host => "mycard-server.my-card.in", :port => 9998);
ws.onmessage do |msg, type|
$log.info('收到websocket消息'){msg.force_encoding("UTF-8")}
$log.info('收到websocket消息') { msg.force_encoding("UTF-8") }
Game_Event.push Game_Event::RoomsUpdate.new JSON.parse(msg).collect { |room| Game_Event.parse_room(room) }
end
ws.onclose do
......@@ -105,7 +105,7 @@ class Ygocore < Game
end
}
http.errback{
http.errback {
Game_Event.push Game_Event::Error.new('ygocore', '读取服务器列表失败', true)
}
}
......@@ -116,12 +116,16 @@ class Ygocore < Game
Thread.new {
begin
@@im.allow_tls = false
@@im.use_ssl = true
@@im.use_ssl = true
connected = false
if @@im.jid.domain == "my-card.in"
@@im.connect("ygopro-server.my-card.in", 5223) rescue Game_Event.push Game_Event::Error.new('登录', '连接服务器失败')
connected = true
begin
@@im.connect("ygopro-server.my-card.in", 5223)
connected = true
rescue
Game_Event.push Game_Event::Error.new('登录', '连接服务器失败')
end
else
srv = []
Resolv::DNS.open { |dns|
......@@ -131,10 +135,9 @@ class Ygocore < Game
if srv.empty?
Game_Event.push Game_Event::Error.new('登录', '解析服务器地址失败')
Thread.exit
end
# Sort SRV records: lowest priority first, highest weight first
srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
srv.sort! { |a, b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
srv.each { |record|
begin
......@@ -150,6 +153,7 @@ class Ygocore < Game
if connected
begin
@@im.fd.define_singleton_method(:external_encoding) { |*args| @@im.fd.io.external_encoding(*args) }
@@im.auth(@password)
rescue Jabber::ClientAuthenticationFailure
Game_Event.push Game_Event::Error.new('登录', '用户名或密码错误')
......@@ -168,9 +172,6 @@ class Ygocore < Game
end
end
Game_Event.push Game_Event::AllUsers.new @@im_room.roster.keys.collect { |nick| User.new(nick.to_sym, nick) } rescue p $!
else
$log.error('聊天连接出错.1') { exception }
Game_Event.push Game_Event::Error.new('登录', '连接服务器失败.1')
end
rescue StandardError => exception
$log.error('聊天连接出错') { exception }
......@@ -190,6 +191,7 @@ class Ygocore < Game
#send(:chat, channel: chatmessage.channel.id, message: chatmessage.message, time: chatmessage.time)
end
end
#def chat(chatmessage)
# case chatmessage.channel
# when :lobby
......@@ -239,8 +241,8 @@ class Ygocore < Game
@recv = nil
end
def ygocore_path
"ygocore/ygopro_vs.exe"
def self.ygocore_path
Windows ? 'ygocore/ygopro_vs.exe' : 'ygocore/gframe'
end
def self.register
......@@ -267,10 +269,9 @@ class Ygocore < Game
if !image_downloading and !Update.images.empty?
return Widget_Msgbox.new("加入房间", "卡图正在下载中,可能显示不出部分卡图", :ok => "确定") { run_ygocore(option, true) }
end
path = 'ygocore/ygopro_vs.exe'
Widget_Msgbox.new("ygocore", "正在启动ygocore") rescue nil
#写入配置文件并运行ygocore
Dir.chdir(File.dirname(path)) do
Dir.chdir(File.dirname(ygocore_path)) do
case option
when Room
room = option
......@@ -305,14 +306,18 @@ class Ygocore < Game
system_conf[field] = contents
end
rescue
system_conf['antialias'] = 2
system_conf['textfont'] = 'c:/windows/fonts/simsun.ttc 14'
system_conf['numfont'] = 'c:/windows/fonts/arialbd.ttf'
end
if $game.user
system_conf['nickname'] = $game.user.name
system_conf['nickname'] += '$' + $game.password if $game.password and !$game.password.empty? and room.server.auth
end
font, size = system_conf['textfont'].split(' ')
if !File.file?(font) or size.to_i.to_s != size
system_conf['textfont'] = '../fonts/wqy_microhei.ttc 14'
end
if !File.file?(system_conf['numfont'])
system_conf['textfont'] = Windows ? '../fonts/wqy_microhei.ttc 14' : '/usr/share/fonts/gnu-free/FreeSansBold.ttf'
end
system_conf['lastip'] = room.server.ip
system_conf['lastport'] = room.server.port.to_s
system_conf['roompass'] = room_name if room_name and !room_name.empty?
......@@ -340,7 +345,7 @@ class Ygocore < Game
open('system.conf', 'w') { |file| file.write system_conf.collect { |key, value| "#{key} = #{value}" }.join("\n") }
args = '-d'
end
IO.popen("ygopro_vs.exe #{args}")
IO.popen("./#{File.basename(ygocore_path)} #{args}")
WM.iconify rescue nil
end
Widget_Msgbox.destroy rescue nil
......@@ -368,7 +373,7 @@ class Ygocore < Game
Config.save
end
rescue Exception => exception
$log.error('公告读取失败') { [exception.inspect, *exception.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
$log.error('公告读取失败') { [exception.inspect, *exception.backtrace].collect { |str| str.encode("UTF-8") }.join("\n") }
end
end
end
......
......@@ -12,7 +12,9 @@ en:
replay: "Replay"
lobby:
faq: "FAQ"
filter: "Filter"
editdeck: "Edit Deck"
newroom: "NewRoom"
match: "QuickMatch"
lobby: "Lobby"
en:
zh:
lang: "中文"
chat:
room: "lobby.zh"
......@@ -16,6 +16,7 @@ en:
newroom: "建立房间"
lobby: "大厅"
filter: "房间筛选"
match: "自动匹配"
waiting_only: "仅等待中"
normal_only: "仅标准房"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment