Commit 471d7d87 authored by 神楽坂玲奈's avatar 神楽坂玲奈

2.0.0

parent 583e0311
source 'https://rubygems.org'
ruby '1.9.3'
gem 'rubysdl', :platform => :ruby
gem 'rubysdl-mswin32-1.9', :platforms => [:mswin, :mingw]
gem 'eventmachine'
gem 'em-http-request'
gem 'xmpp4r'
gem 'locale'
gem 'i18n'
gem 'ruby-ogginfo'
gem 'sqlite3'
gem 'zip'
gem 'websocket'
gem 'net-http-pipeline'
gem 'fssm'
group :development do
gem 'rake'
gem 'bundler'
end
const path = require('path');
module.exports = function (grunt) {
switch (process.platform) {
case 'darwin':
build_prefix = 'Electron.app/Contents/Resources';
grunt.loadNpmTasks('grunt-appdmg');
var release_task = 'appdmg';
break;
case 'win32':
build_prefix = 'resources';
grunt.loadNpmTasks('grunt-electron-installer');
release_task = 'create-windows-installer';
break;
}
grunt.initConfig({
clean: ["build"],
copy: {
electron: {
expand: true,
options: {
mode: true,
timestamp: true
},
cwd: 'node_modules/electron-prebuilt/dist',
src: '**',
dest: 'build'
},
app: {
expand: true,
options: {
timestamp: true
},
src: ['package.json', 'README.txt', 'LICENSE.txt', 'index.html', 'main.js', 'ygopro.js', 'ygopro/**', 'node_modules/ws/**'],
dest: path.join('build', build_prefix, 'app')
}
},
electron: {
osxBuild: {
options: {
name: 'Fixture',
dir: 'app',
out: 'dist',
version: '0.25.3',
platform: 'darwin',
arch: 'x64'
}
}
},
'create-windows-installer': {
x64: {
appDirectory: 'build',
outputDirectory: 'release',
authors: 'MyCard',
exe: 'electron.exe',
description: 'nyanya'
}/*,
ia32: {
appDirectory: 'build/32',
outputDirectory: 'release/32',
authors: 'MyCard',
exe: 'mycard.exe'
}*/
}
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('build', ['clean', 'copy:electron','copy:app']);
grunt.registerTask('release', ['build', release_task]);
grunt.registerTask('default', ['release']);
};
== mycard GNU AFFERO GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE Version 3, 19 November 2007
Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/> of this license document, but changing it is not allowed.
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble
Preamble The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
The GNU General Public License is a free, copyleft license for software and other kinds of works. cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. software for all its users.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
Developers that use our General Public Licenses protect your rights
Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
The precise terms and conditions for copying, distribution and modification follow. and/or modify the software.
TERMS AND CONDITIONS A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
0. Definitions. receive widespread use, become available for other developers to
“This License” refers to version 3 of the GNU General Public License. incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. letting the public access it on a server without ever releasing its
source code to the public.
To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
The GNU Affero General Public License is designed specifically to
A “covered work” means either the unmodified Program or a work based on the Program. ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. a publicly accessible server, gives the public access to the source
code of the modified version.
An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
An older license, called the Affero General Public License and
1. Source Code. published by Affero, was designed to accomplish similar goals. This is
The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. this license.
The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The precise terms and conditions for copying, distribution and
modification follow.
The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
TERMS AND CONDITIONS
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
0. Definitions.
The Corresponding Source for a work in source code form is that same work.
"This License" refers to version 3 of the GNU Affero General Public License.
2. Basic Permissions.
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
"The Program" refers to any copyrightable work licensed under this
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. A "covered work" means either the unmodified Program or a work based
on the Program.
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
To "propagate" a work means to do anything with it that, without
5. Conveying Modified Source Versions. permission, would make you directly or secondarily liable for
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
a) The work must carry prominent notices stating that you modified it, and giving a relevant date. distribution (with or without modification), making available to the
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. public, and in some countries other activities as well.
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. To "convey" a work means any kind of propagation that enables other
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. feature that (1) displays an appropriate copyright notice, and (2)
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. tells the user that there is no warranty for the work (except to the
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. extent that warranties are provided), that licensees may convey the
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. work under this License, and how to view a copy of this License. If
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. the interface presents a list of user commands or options, such as a
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. menu, a prominent item in the list meets this criterion.
A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. 1. Source Code.
“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). form of a work.
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
7. Additional Terms.
“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or "Major Component", in this context, means a major essential component
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or (kernel, window system, and so on) of the specific operating system
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or (if any) on which the executable work runs, or a compiler used to
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or produce the work, or an object code interpreter used to run it.
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. The "Corresponding Source" for a work in object code form means all
All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
8. Termination. includes interface definition files associated with source files for
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. such as by intimate data communication or control flow between those
subprograms and other parts of the work.
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
The Corresponding Source need not include anything that users
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. can regenerate automatically from other parts of the Corresponding
Source.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. The Corresponding Source for a work in source code form is that
same work.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. 2. Basic Permissions.
An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
11. Patents. covered work is covered by this License only if the output, given its
A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
You may make, run and propagate covered works that you do not
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. your copyrighted material outside their relationship with you.
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
12. No Surrender of Others' Freedom. makes it unnecessary.
If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
14. Revised Versions of this License. 11 of the WIPO copyright treaty adopted on 20 December 1996, or
The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. similar laws prohibiting or restricting circumvention of such
measures.
Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
When you convey a covered work, you waive any legal power to forbid
If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
15. Disclaimer of Warranty. users, your or third parties' legal rights to forbid circumvention of
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. technological measures.
16. Limitation of Liability. 4. Conveying Verbatim Copies.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
You may convey verbatim copies of the Program's source code as you
17. Interpretation of Sections 15 and 16. receive it, in any medium, provided that you conspicuously and
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
END OF TERMS AND CONDITIONS non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
How to Apply These Terms to Your New Programs recipients a copy of this License along with the Program.
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> <one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author> Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.
== ruby
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:
1. You may make and give away verbatim copies of the source form of the
software without restriction, provided that you duplicate all of the
original copyright notices and associated disclaimers.
2. You may modify your copy of the software in any way, provided that
you do at least ONE of the following:
a) place your modifications in the Public Domain or otherwise Also add information on how to contact you by electronic and paper mail.
make them Freely Available, such as by posting said
modifications to Usenet or an equivalent medium, or by allowing
the author to include your modifications in the software.
b) use the modified software only within your corporation or
organization.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
3. You may distribute the software in object code or binary form,
provided that you do at least ONE of the following:
a) distribute the binaries and library files of the software,
together with instructions (in the manual page or equivalent)
on where to get the original distribution.
b) accompany the distribution with the machine-readable source of
the software.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
4. You may modify and include the part of the software into any other
software (possibly commercial). But some files in the distribution
are not written by the author, so that they are not under these terms.
For the list of those files and their copying conditions, see the
file LEGAL.
5. The scripts and library files supplied as input to or produced as
output from the software do not automatically fall under the
copyright of the software, but belong to whomever generated them,
and may be sold commercially, and may be aggregated with this
software.
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR If your software can interact with users remotely through a computer
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED network, you should also make sure that it provides a way for users to
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR get its source. For example, if your program is a web application, its
PURPOSE. interface could display a "Source" link that leads users to an archive
\ No newline at end of file of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
\ No newline at end of file
== mycard nyaa
\ No newline at end of file
这是一个游戏王对战器,与ygocore协议兼容
快捷键:
F12 返回上一层
常见问题:
Q:登陆时显示“解析服务器失败”怎么办?
A:如果是以Mycard用户登陆的话,请在登陆时去掉用户名中「@」后的内容。 Gmail用户请将DNS修改成8.8.8.8 208.67.222.222。
Q:为什么打完一局后换side时会无反应?&为什么经常提示内存不能为read?&为什么我的游戏的帧率(左上角的数字)极低?
A:可尝试用记事本打开mycard\ygocore\中的system.conf文件,找到use_d3d,其后边的数值原来是0就改成1,原来是1就改成0。或下载“驱动精灵”更新显卡驱动。
更多常见问题请到 https://forum.my-card.in/faq
作者联系方式:
1. mycard的论坛 https://forum.my-card.in
2. E-mail/QQ/GT: zh99998@gmail.com
#encoding: UTF-8
require 'rubygems'
require 'rake'
require 'rake/clean'
require 'rubygems/package_task'
require 'rdoc/task'
#require 'rake/testtask'
Windows = RUBY_PLATFORM["mingw"] || RUBY_PLATFORM["mswin"]
#在windows上UTF-8脚本编码环境中 Dir.glob无法列出中文目录下的文件 所以自己写个递归
def list(path)
result = []
Dir.foreach(path) do |file|
next if file == "." or file == ".."
result << "#{path}/#{file}"
result.concat list(result.last) if File.directory? result.last
end rescue p $!
result
end
spec = Gem::Specification.new do |s|
s.name = 'mycard'
s.version = '1.2.2'
s.extra_rdoc_files = ['README.txt', 'LICENSE.txt']
s.summary = 'a card game platform'
s.description = s.summary
s.author = 'zh99998'
s.email = 'zh99998@gmail.com'
s.homepage = 'http://my-card.in/'
# s.executables = ['your_executable_here']
s.files = %w(LICENSE.txt README.txt replay)
%w{lib audio data locales graphics ygocore}.each{|dir|s.files.concat list(dir)}
if Windows
s.files += %w(mycard.exe) + list("ruby") + list("fonts")
else
s.files += %w(mycard.sh)
s.platform = Gem::Platform::CURRENT
end
s.require_path = "lib"
#s.bindir = "bin"
end
Gem::PackageTask.new(spec) do |p|
p.gem_spec = spec
if Windows
p.need_zip = true
p.zip_command = '7z a'
def p.zip_file
"#{package_name}-win32.7z"
end
else
p.need_tar_gz = true
end
end
Rake::RDocTask.new do |rdoc|
files =['README.txt', 'LICENSE.txt', 'lib/**/*.rb']
rdoc.rdoc_files.add(files)
rdoc.main = "README.txt" # page to start on
rdoc.title = "Mycard Docs"
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
rdoc.options << '--line-numbers'
end
CLOBBER.include %w(error-程序出错请到论坛反馈.txt log.log profile.log config.yml doc ygocore/pics) + list('replay') + list('ygocore/replay') + list('.').keep_if{|file|File.basename(file) == "Thumbs.db"} + list("graphics/avatars").reject{|file|File.basename(file) =~ /(?:error|loading)_(?:small|middle|large)\.png/} + list("ygocore/deck").keep_if{|file|File.basename(file) != 'sample.ydk'}
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
---
name: "沙沙"
field:
cardimage:
- 1
- 2
- 3
- 4
cardtext:
- 5
- 6
- 7
- 8
chat:
- 9
- 10
- 11
- 12
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html {
font-size: 16px;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1rem;
line-height: 1.5;
color: #373a3c;
background-color: #fff;
margin: 0;
}
webview {
position: absolute;
top: 54px;
bottom: 0;
width: 100%;
display: none;
}
nav {
color: #eceeef;
background-color: #373a3c;
padding: .5rem 1rem;
position: fixed;
top: 0;
right: 0;
left: 0;
-webkit-user-select: none;
-webkit-app-region: drag;
}
#brand {
float: left;
padding-top: .25rem;
padding-bottom: .25rem;
margin-right: 1rem;
font-size: 1.25rem;
color: #fff;
touch-action: manipulation;
}
ul {
padding-left: 0;
margin-bottom: 0;
margin-top: 0;
list-style: none;
}
li {
float: left;
}
li + li {
margin-left: 1rem;
}
li > a {
display: block;
padding-top: .425rem;
padding-bottom: .425rem;
text-decoration: none;
color: rgba(255, 255, 255, .75);
}
li > a:hover {
color: #fff;
outline: 0;
}
li.active > a {
color: #fff;
}
.navbar-right {
float: right
}
.container {
margin-left: 100px;
margin-right: 100px;
}
</style>
</head>
<body>
<nav>
<div class="container">
<span id="brand">萌卡</span>
<ul>
<li id="nav-store">
<a href="#store">商店</a>
</li>
<li id="nav-ygopro" class="active">
<a href="#ygopro">游戏</a>
</li>
<li id="nav-forum">
<a href="#forum">社区</a>
</li>
</ul>
<div class="navbar-right">zh99998</div>
</div>
</nav>
<!--<webview id="ygopro" src="http://local.mycard.moe:3000/"></webview>-->
<webview id="store" src="http://mycard.moe/"></webview>
<webview id="ygopro" src="http://mycard.moe/lobby/"></webview> <!-- fuck https https://plus.google.com/u/0/+%E7%A5%9E%E6%A5%BD%E5%9D%82%E7%8E%B2%E5%A5%88/posts/79g2y5JRB1Z -->
<webview id="forum" src="https://forum.touhou.cc"></webview>
<script>
window.onhashchange = function (event) {
var hash = event.newURL.split('#', 2)[1];
document.getElementsByClassName('active')[0].className = "";
document.getElementById('nav-' + hash).className = "active";
document.getElementById(event.oldURL.split('#', 2)[1]).style.display = 'none';
document.getElementById(hash).style.display = 'block';
};
var hash = location.href.split('#', 2)[1];
document.getElementById('nav-' + hash).className = "active";
document.getElementById(hash).style.display = 'block';
var webviews = document.getElementsByTagName('webview');
for (var i = 0; i < webviews.length; i++) {
webviews.item(i).addEventListener('new-window', function (event) {
require('electron').shell.openExternal(event.url);
});
}
//debug
var webview = document.getElementById(hash);
webview.addEventListener("dom-ready", function () {
webview.openDevTools();
});
</script>
</body>
</html>
\ No newline at end of file
#encoding: UTF-8
class Action
attr_accessor :from_player, :msg
attr_accessor :id
def initialize(from_player=true, msg=nil)
@id = @@id
@from_player = from_player
@msg = msg
@@id += 1 if @from_player
end
def player_field
@from_player ? $game.player_field : $game.opponent_field
end
def opponent_field
@from_player ? $game.opponent_field : $game.player_field
end
def run
#子类定义
end
class Reset < Action
def run
player_field.reset
super
end
end
class Deck < Action; end
class Side < Deck; end
class Go < Reset
def run
super
player_field.hand = player_field.deck.pop(5)
end
end
class FirstToGo < Go; end
class SecondToGo < Go; end
class Chat < Action; end
class Shuffle < Action
def run
player_field.deck.shuffle!
super
end
end
class Coin < Action
attr_reader :result
def initialize(from_player, result=rand(1)==0, msg=nil)
super(from_player, msg)
@result = result
end
end
class Dice < Action
attr_reader :result
def initialize(from_player, result=rand(6)+1, msg=nil)
super(from_player, msg)
@result = result
end
end
class ChangePhase < Action
attr_reader :phase
def initialize(from_player, phase)
super(from_player)
@phase = phase
end
def run
$game.phase = phase
super
end
end
class Move < Action
attr_reader :from_pos, :to_pos, :card, :position
def initialize(from_player, from_pos, to_pos=nil, card=nil, msg=nil, position=nil)
super(from_player, msg)
@from_pos = from_pos
@to_pos = to_pos
@card = card
@position = position
end
def parse_field(pos)
case pos
when 0..10, :field
player_field.field
when 11..70, :hand, :handtop, :handrandom
player_field.hand
when 71..130,:deck, :decktop, :deckbottom
player_field.deck
when :graveyard
player_field.graveyard
when :extra
player_field.extra
when :removed
player_field.removed
end
end
def run
$log.info('移动操作执行'){self.inspect}
from_field = parse_field(@from_pos)
from_pos = case @from_pos
when 0..10
@from_pos
when 11..70
@from_pos - 11
when 71..130
@from_pos - 71
when :handtop
player_field.hand.size - 1
when :decktop
player_field.deck.size - 1
when nil
nil
else
(@card.is_a?(Game_Card) ? from_field.index(@card) : from_field.index{|card|card.card == @card}) || from_field.index{|card|!card.known?}
end
to_field = parse_field(@to_pos)
card = if from_pos
case @card
when Game_Card
@card
when Card
if from_field[from_pos]
from_field[from_pos].card = @card
else
$log.warn('移动操作1'){'似乎凭空产生了卡片' + self.inspect}
from_field[from_pos] = Game_Card.new(@card)
end
from_field[from_pos]
else
from_field[from_pos] || Game_Card.new
end
else #没有来源
$log.warn('移动操作2'){'似乎凭空产生了卡片' + self.inspect}
Game_Card.new(@card)
end
if @position
if @position == :"face-up"
if card.position != :attack and (6..10).include?(@to_pos || @from_pos) #里侧表示的怪兽
card.position = :defense
else
card.position = :attack
end
else
card.position = @position
end
end
if @to_pos
if from_pos
if from_field == player_field.field
from_field[from_pos] = nil
else
from_field.delete_at from_pos
end
end
case @to_pos
when 0..10
to_field[@to_pos] = card
when :hand, :deck, :decktop, :extra, :graveyard, :removed
to_field.push card
when :deckbottom
to_field.unshift card
else
$log.error('移动操作3'){'错误的to_pos' + self.inspect}
end
end
if from_field == player_field.hand and !@card || !@card.known?
case @to_pos
when 0..5
player_field.hand.each{|card|card.card = Card::Unknown if card.known? and !card.monster?}
when 6..10
player_field.hand.each{|card|card.card = Card::Unknown if card.known? and card.monster?}
else
player_field.hand.each{|card|card.card = Card::Unknown if card.known?}
end
end
super
end
end
class Set < Move
def initialize(from_player, from_pos, to_pos, card)
super(from_player, from_pos, to_pos, card, nil, :set)
end
end
class Activate < Move
def initialize(from_player, from_pos, to_pos, card)
super(from_player, from_pos, to_pos, card, nil, :attack)
end
end
class Summon < Move
def initialize(from_player, from_pos, to_pos, card, msg=nil)
super(from_player, from_pos, to_pos, card, msg, :attack)
end
end
class SpecialSummon < Move
def initialize(from_player, from_pos, to_pos, card, msg=nil, position=:attack)
super(from_player, from_pos, to_pos, card, msg, position)
end
end
class SendToGraveyard < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, :graveyard, card, nil, :attack)
end
end
class Remove < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, :removed, card, nil, :attack)
end
end
class ReturnToHand < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, :hand, card, nil, :set)
end
end
class ReturnToDeck < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, :deck, card, nil, :set)
end
end
class ReturnToDeckBottom < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, :deckbottom, card, nil, :set)
end
end
class ReturnToExtra < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, :extra, card, nil, :set)
end
end
class Control < Move
def initialize(from_player, from_pos, card)
super(from_player, from_pos, nil, card)
end
def run
to_pos = opponent_field.field[6..10].index(nil)+6
unless to_pos
$log.warn('转移控制权'){'没有空余场位'}
return
end
card = if @card.is_a? Game_Card
@card
elsif player_field.field[from_pos]
card = player_field.field[from_pos]
card.card = @card
card
else
$log.warn('转移控制权'){'似乎凭空产生了卡片'+self.inspect}
Game_Card.new(@card)
end
player_field.field[from_pos] = nil
opponent_field.field[to_pos] = card
end
end
class SendToOpponentGraveyard < SendToGraveyard
def run
card = if @card.is_a? Game_Card
@card
elsif player_field.field[from_pos]
card = player_field.field[from_pos]
card.card = @card
card
else
Game_Card.new(@card)
end
player_field.field[from_pos] = nil
opponent_field.graveyard.unshift card
end
end
class Tribute < SendToGraveyard; end
class Discard < SendToGraveyard; end
class ChangePosition < Move
def initialize(from_player, from_pos, card, position)
super(from_player, from_pos, from_pos, card, nil, position)
end
end
class Flip < ChangePosition
def initialize(from_player, from_pos, card, position=:defense)
super(from_player, from_pos, card, position)
end
end
class FlipSummon < Flip
def initialize(from_player, from_pos, card)
super(from_player, from_pos, card, :attack)
end
end
class Draw < Move
def initialize(from_player=true, msg=nil)
@from_player = from_player
super(from_player, :decktop, :hand, nil, msg, :set)
end
end
class Counter < Action
end
class MultiDraw < Action
def initialize(from_player, count, msg=nil)
super(from_player, msg)
@count = count
end
def run
super
player_field.hand.concat player_field.deck.pop(@count)
end
end
class MultiMove < Action
def initialize(from_player, from_pos, to_pos, cards=nil)
super(from_player)
@from_pos = from_pos
@to_pos = to_pos
@cards = cards
end
def run
from_field = case @from_pos
when :hand
player_field.hand
when :graveyard
player_field.graveyard
when :spellsandtraps
player_field.field[0..5]
when :monsters
player_field.field[6..10]
end
@cards = if @cards
@cards.collect do |card|
index = from_field.index{|fieldcard|fieldcard and fieldcard.card == card} || from_field.index{|fieldcard|fieldcard and !fieldcard.known?}
if index
fieldcard = from_field[index]
from_field[index] = nil
fieldcard.card = card
fieldcard
else
$log.warn '似乎凭空产生了卡片'
Game_Card.new(@card)
end
end
else
from_field.compact
end
to_field, position = case @to_pos
when :hand
[player_field.hand, :set]
when :graveyard
[player_field.graveyard, :attack]
when :deck
[player_field.deck, :set]
when :removed
[player_field.removed, :attack]
end
#执行部分
case @from_pos
when :hand
player_field.hand.clear
when :graveyard
player_field.graveyard.clear
when :spellsandtraps
player_field.field[0..5] = Array.new(6, nil)
when :monsters
player_field.field[6..10] = Array.new(5, nil)
end
if to_field == player_field.hand or to_field == player_field.deck
@cards.each{|card|card.position = position; to_field.push card}
else
@cards.each{|card|card.position = position; to_field.unshift card}
end
super
end
end
=begin #似乎不需要细分
class MonstersSendToGraveyard < MultiMove
def initialize(from_player, cards)
super(from_player, :monsters, :graveyard, cards)
end
end
class MonstersRemove < MultiMove
def initialize(from_player, cards)
super(from_player, :monsters, :remove, cards)
end
end
class MonstersReturnToDeck < MultiMove
def initialize(from_player, cards)
super(from_player, :monsters, :deck, cards)
end
end
class MonstersReturnToHand < MultiMove
def initialize(from_player, cards)
super(from_player, :monsters, :hand, cards)
end
end
class SpellsAndTrapsSendToGraveyard < MultiMove
def initialize(from_player, cards)
super(from_player, :spellsandtraps, :graveyard, cards)
end
end
class SpellsAndTrapsRemove < MultiMove
def initialize(from_player, cards)
super(from_player, :spellsandtraps, :remove, cards)
end
end
class SpellsAndTrapsReturnToDeck < MultiMove
def initialize(from_player, cards)
super(from_player, :spellsandtraps, :deck, cards)
end
end
class SpellsAndTrapsReturnToHand < MultiMove
def initialize(from_player, cards)
super(from_player, :spellsandtraps, :hand, cards)
end
end
class HandSendToGraveyard < MultiMove
def initialize(from_player, cards)
super(from_player, :hand, :graveyard, cards)
end
end
class HandRemove < MultiMove
def initialize(from_player, cards)
super(from_player, :hand, :remove, cards)
end
end
class HandReturnToDeck < MultiMove
def initialize(from_player, cards)
super(from_player, :hand, :deck, cards)
end
end
=end
class RefreshField < Action
attr_reader :field
def initialize(from_player, field, msg=nil)
super(from_player, msg)
@field = field
end
def run
super
return if @field.is_a? Game_Field #本地信息,无需处理。
player_field.lp = @field[:lp]
if player_field.hand.size > @field[:hand]
player_field.hand.pop(player_field.hand.size-@field[:hand])
elsif player_field.hand.size < @field[:hand]
(@field[:hand]-player_field.hand.size).times{$log.warn('刷新场地-手卡'){'似乎凭空产生了卡片'};player_field.hand.push Game_Card.new(Card::Unknown)}
end
if player_field.deck.size > @field[:deck]
player_field.deck.pop(player_field.deck.size-@field[:deck])
elsif player_field.deck.size < @field[:deck]
(@field[:deck]-player_field.deck.size).times{$log.warn('刷新场地-卡组'){'似乎凭空产生了卡片'};player_field.deck.push Game_Card.new(Card::Unknown)}
end
if player_field.graveyard.size > @field[:graveyard]
player_field.graveyard.pop(player_field.graveyard.size-@field[:graveyard])
elsif player_field.graveyard.size < @field[:graveyard]
(@field[:graveyard]-player_field.graveyard.size).times{$log.warn('刷新场地-墓地'){'似乎凭空产生了卡片'};player_field.graveyard.push Game_Card.new(Card::Unknown)}
end
(0..10).each do |pos|
if @field[pos]
if player_field.field[pos]
player_field.field[pos].card = @field[pos][:card]
else
$log.warn("刷新场地-#{pos}"){'似乎凭空产生了卡片'}
player_field.field[pos] = Game_Card.new(@field[pos][:card])
end
player_field.field[pos].position = @field[pos][:position]
else
player_field.field[pos] = nil
end
end
end
end
class TurnEnd < RefreshField
attr_reader :turn
def initialize(from_player, field, turn, msg=nil)
super(from_player, field, msg)
@turn = turn
end
def run
$game.phase = :DP
$game.turn = @turn.next
$game.turn_player = !from_player
super
end
end
class Show < Move
attr_reader :from_pos, :card
def initialize(from_player, from_pos, card)
super(from_player, from_pos, nil, card)
@from_pos = from_pos
@card = card
end
end
class MultiShow < Action
def initialize(from_player, from_pos=nil, cards)
super(from_player, nil)
@from_pos = from_pos
@cards = cards
end
def run
return if @cards[0].is_a? Game_Card #本地消息,不处理
case @from_pos
when :hand
if player_field.hand.size > @cards.size
player_field.hand.pop(player_field.hand.size-@cards.size)
end
@cards.each_with_index do |card, index|
if player_field.hand[index]
player_field.hand[index].card = card
else
player_field.hand[index] = Game_Card.new card
end
end
when 71..130
cards = @cards.to_enum
player_field.deck[@from_pos-71, @cards.size].each do |game_card|
game_card.card = cards.next
end
end
end
end
class EffectActivate < Move
def initialize(from_player, from_pos, card)
if (0..10).include?(from_pos)
position = :"face-up"
else
position = nil
end
super(from_player, from_pos, to_pos, card, nil, position)
end
end
class ActivateAsk < Action
def initialize(from_player)
super(from_player)
end
end
class ActivateAnswer < Action
def initialize(from_player, activate)
super(from_player)
@activate = activate
end
end
class Target < Action
def initialize(from_player, from_pos, card, target_player ,target_pos, target_card)
super(from_player)
@from_pos = from_pos
@card = card
@target_pos = target_player
@target_pos = target_pos
@target_card = target_card
end
def run
card = if @card.is_a? Game_Card
@card
else
if player_field.field[@from_pos]
player_field.field[@from_pos].card = @card
player_field.field[@from_pos]
else
$log.warn('攻击宣言'){'似乎凭空产生了卡片' + self.inspect}
player_field[@from_pos] = Game_Card.new(@card)
end
end
$log.info('攻击宣言'){self.inspect}
end
end
class ViewDeck < Action; end
class LP < Action
attr_accessor :operator, :value
def initialize(from_player, operator, value)
super(from_player)
@operator = operator
@value = value
end
def run
case operator
when :lose
player_field.lp -= @value
when :increase
player_field.lp += @value
when :become
player_field.lp = @value
end
end
end
class Attack < Action
def initialize(from_player, from_pos, to_pos=nil, card)
super(from_player)
@from_pos = from_pos
@to_pos = to_pos
@card = card
end
def run
card = if @card.is_a? Game_Card
@card
elsif @from_pos
if player_field.field[@from_pos]
player_field.field[@from_pos].card = @card
else
$log.warn('攻击宣言'){'似乎凭空产生了卡片' + self.inspect}
player_field.field[@from_pos] = Game_Card.new(@card)
player_field.field[@from_pos].position = :attack
end
player_field.field[@from_pos]
else
$log.info('直接攻击'){'功能未实现'}
@card
end
end
end
class Counter < Action
def initialize(from_player, from_pos, card, operator, value)
super(from_player)
@from_pos = from_pos
@card = card
@operator = operator
@value = value
end
def run
card = if @card.is_a? Game_Card
@card
else
if player_field.field[@from_pos]
player_field.field[@from_pos].card = @card
else
$log.warn('指示物操作'){'似乎凭空产生了卡片' + self.inspect}
player_field.field[@from_pos] = Game_Card.new(@card)
player_field.field[@from_pos].position = :attack
end
player_field.field[@from_pos]
end
case @operator
when :become
card.counters = @value
else
$log.warn('指示物操作'){'become以外的未实现' + self.inspect}
end
end
end
class Note < Action
def initialize(from_player, from_pos, card, note)
super(from_player)
@from_pos = from_pos
@card = card
@note = note
end
def run
card = if @card.is_a? Game_Card
@card
else
if player_field.field[@from_pos]
player_field.field[@from_pos].card = @card
else
$log.warn('指示物操作'){'似乎凭空产生了卡片' + self.inspect}
player_field.field[@from_pos] = Game_Card.new(@card)
player_field.field[@from_pos].position = :attack
end
player_field.field[@from_pos]
end
card.note = @note
end
end
class Token < SpecialSummon
def initialize(from_player, to_pos, card, position=:defense)
super(from_player, nil, to_pos, card)
end
end
class MultiToken < SpecialSummon
def initialize(from_player, num, card, position=:attack)
super(from_player, nil, nil, card)
@num = num
end
def run
@num.times do
@to_pos = player_field.field[6..10].index(nil)+6
super
end
end
end
class Add < Action
def initialize(from_player, card)
super(from_player)
@card = card
end
def run
if @card.extra?
player_field.extra << Game_Card.new(@card)
else
player_field.hand << Game_Card.new(@card)
end
end
end
class Destroy < Action
def initialize(from_player, from_pos, card)
super(from_player)
@from_pos = from_pos
@card = card
end
def run
if @from_pos <= 10
player_field.field[@from_pos] = nil
else
player_field.hand.delete_at(@from_pos - 11)
end
end
end
class CardInfo < Action
def initialize(card, card_type, atk, _def, attribute, type, level, lore)
return unless card.diy?
card.card_type = card_type
#card.monster_type = monster_type
card.atk = atk
card.def = _def
card.attribute = attribute
card.type = type
card.level = level
card.lore = lore
end
end
class Unknown < Action
def initialize(str)
@str = str
$log.warn('unkonwn action') { str }
end
def run
$log.warn('unkonwn action run'){ @str }
end
end
def self.reset
@@id=1
end
reset
end
\ No newline at end of file
class Announcement
attr_accessor :title
attr_accessor :url
attr_accessor :time
def initialize(title, url, time=nil)
@title = title
@url = url
@time = time
end
end
module Association
module_function
def register
if Windows
require 'win32/registry'
path, command, icon = paths
Win32::Registry::HKEY_CLASSES_ROOT.create('mycard') { |reg| reg['URL Protocol'] = path.ljust path.bytesize }
Win32::Registry::HKEY_CLASSES_ROOT.create('mycard\shell\open\command') { |reg| reg[nil] = command.ljust command.bytesize }
Win32::Registry::HKEY_CLASSES_ROOT.create('mycard\DefaultIcon') { |reg| reg[nil] = icon.ljust icon.bytesize }
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
desktop, x_ygopro_deck, x_ygopro_replay = paths
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 desktop }
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 x_ygopro_deck }
open("#{ENV['HOME']}/.local/share/mime/packages/application-x-ygopro-replay.xml", 'w') { |f| f.write x_ygopro_replay }
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
def paths
if Windows
pwd = Dir.pwd.gsub('/', '\\')
path = '"' + pwd + '\ruby\bin\rubyw.exe" -C"' + pwd + '" -KU lib/main.rb'
command = path + ' "%1"'
icon = '"' + pwd + '\mycard.exe", 0'
[path, command, icon]
else
desktop = <<EOF
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=Mycard
Name[zh_CN]=Mycard - 萌卡
Comment=a card game platform
Comment[zh_CN]=卡片游戏对战客户端
Exec=ruby -KU lib/main.rb %u
Terminal=false
Icon=#{Dir.pwd}/graphics/system/icon.png
Type=Application
Categories=Game
Path=#{Dir.pwd}
URL=https://my-card.in/
MimeType=x-scheme-handler/mycard;application/x-ygopro-deck;application/x-ygopro-replay'
EOF
x_ygopro_deck = <<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
x_ygopro_replay = <<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
[desktop, x_ygopro_deck, x_ygopro_replay]
end
end
def need?
return false if $config['no_assoc']
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
true
end
else
begin
(([IO.read("#{ENV['HOME']}/.local/share/applications/mycard.desktop"),
IO.read("#{ENV['HOME']}/.local/share/mime/packages/application-x-ygopro-deck.xml"),
IO.read("#{ENV['HOME']}/.local/share/mime/packages/application-x-ygopro-replay.xml")] != paths) or !(
File.file?("#{ENV['HOME']}/.icons/application-x-ygopro-deck.png") and
File.file?("#{ENV['HOME']}/.icons/application-x-ygopro-replay.png")))
rescue
true
end
end
end
def request
require_relative 'widget_msgbox'
Widget_Msgbox.new("mycard", "即将进行文件关联, 弹出安全警告请点允许", ok: "确定", cancel: "取消") do |clicked|
if clicked == :ok
yield
else
Widget_Msgbox.new("mycard", "未进行关联,要重新关联请删除config.yml", ok: "确定")
$config['no_assoc'] = true
Config.save
end
end
end
def start
if need?
request do
if Windows
require 'rbconfig'
register rescue Dialog.uac(File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["RUBY_INSTALL_NAME"] + RbConfig::CONFIG["EXEEXT"]), "-KU lib/main.rb register_association")
else
register
end
end
end
end
end
\ No newline at end of file
module Cacheable
@@all = {}
def new(id, *args)
@@all[self] ||= {}
if id and result = @@all[self][id]
result.set(id, *args)
result
else
@@all[self][id] = super(id, *args)
end
end
def find(id)
@@all[self][id]
end
end
\ No newline at end of file
#encoding: UTF-8
#==Card Model
class Card
require 'sqlite3'
@db = SQLite3::Database.new( "data/data.sqlite" )
@all = {}
@diy = {}
@count = @db.get_first_value("select COUNT(*) from `yu-gi-oh`") rescue 0
@db.results_as_hash = true
PicPath = if Windows
require 'win32/registry'
ospicpath = Win32::Registry::HKEY_CURRENT_USER.open('Software\OCGSOFT\Cards'){|reg|reg['Path']} rescue ''
ospicpath.force_encoding "GBK"
ospicpath.encode "UTF-8"
else
'' #其他操作系统卡图存放位置标准尚未制定。
end
CardBack = Surface.load("graphics/field/card.jpg").display_format rescue nil
CardBack_Small = Surface.load("graphics/field/card_small.gif").display_format rescue nil
class << self
def find(id, order_by=nil)
case id
when Integer
@all[id] || old_new(@db.get_first_row("select * from `yu-gi-oh` where id = #{id}"))
when Symbol
row = @db.get_first_row("select * from `yu-gi-oh` where name = '#{id}'")
if row
@all[row['id'].to_i] || old_new(row)
else
@diy[id] ||= Card.new('id' => 0, 'number' => :"00000000", 'name' => id, 'attribute' => :, 'level' => 1, 'card_type' => :通常怪兽, 'stats' => "", 'archettypes' => "", 'mediums' => "", 'lore' => "")
end
when Hash
old_new(id)
when nil
Card::Unknown
else
sql = "select * from `yu-gi-oh` where " << id
sql << " order by #{order_by}" if order_by
$log.debug('查询卡片执行SQL'){sql}
@db.execute(sql).collect {|row|@all[row['id'].to_i] || old_new(row)}
end
end
def all
if @all.size != @count
sql = "select * from `yu-gi-oh` where id not in (#{@all.keys.join(', ')})"
@db.execute(sql).each{|row|old_new(row)}
end
@all
end
def cache
@all
end
alias old_new new
def new(id)
find(id)
end
def load_from_ycff3(db = RUBY_PLATFORM["win"] || RUBY_PLATFORM["ming"] ? (require 'win32/registry';Win32::Registry::HKEY_CURRENT_USER.open('Software\OCGSOFT\YFCC'){|reg|reg['Path']+"YGODATA/YGODAT.dat"} rescue '') : '')
require 'win32ole'
conn = WIN32OLE.new('ADODB.Connection')
conn.open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + db + ";Jet OLEDB:Database Password=paradisefox@sohu.com" )
records = WIN32OLE.new('ADODB.Recordset')
records.open("select EFFECT from YGOEFFECT", conn)
stats = records.GetRows.first
stats.unshift nil
records.close
records = WIN32OLE.new('ADODB.Recordset')
records.open("YGODATA", conn)
records.MoveNext #跳过首行那个空白卡
sql = ""
while !records.EOF
sql << "INSERT INTO `yu-gi-oh` VALUES(
#{records.Fields.Item("CardID").value-1},
'#{records.Fields.Item("CardPass").value}',
'#{records.Fields.Item("SCCardName").value}',
'#{records.Fields.Item("SCCardType").value == "XYZ怪兽" ? "超量怪兽" : records.Fields.Item("SCCardType").value}',
#{records.Fields.Item("SCDCardType").value == '    ' ? "NULL" : "'#{records.Fields.Item("SCDCardType").value}'"},
#{records.Fields.Item("CardATK").value || "NULL"},
#{records.Fields.Item("CardDef").value || "NULL"},
#{records.Fields.Item("SCCardAttribute").value == '    ' ? "NULL" : "'#{records.Fields.Item("SCCardAttribute").value}'"},
#{records.Fields.Item("SCCardRace").value == '    ' ? "NULL" : "'#{records.Fields.Item("SCCardRace").value}'"},
#{records.Fields.Item("CardStarNum").value || "NULL"},
'#{records.Fields.Item("SCCardDepict").value}',
#{case records.Fields.Item("ENCardBan").value; when "Normal"; 3; when "SubConfine"; 2; when "Confine"; 1; else; 0; end},
'#{records.Fields.Item("CardEfficeType").value}',
'#{records.Fields.Item("CardPhal").value.split(",").collect{|stat|stats[stat.to_i]}.join("\t")}',
'#{records.Fields.Item("CardCamp").value.gsub("、", "\t")}',
#{records.Fields.Item("CardISTKEN").value}
);"
records.MoveNext
end
@db.execute('begin transaction')
@db.execute('DROP INDEX if exists "main"."name";')
@db.execute('DROP TABLE if exists "main"."yu-gi-oh";')
@db.execute('CREATE TABLE "yu-gi-oh" (
"id" INTEGER NOT NULL,
"number" TEXT NOT NULL,
"name" TEXT NOT NULL,
"card_type" TEXT NOT NULL,
"monster_type" TEXT,
"atk" INTEGER,
"def" INTEGER,
"attribute" TEXT,
"type" TEXT,
"level" INTEGER,
"lore" TEXT NOT NULL,
"status" INTEGER NOT NULL,
"stats" TEXT NOT NULL,
"archettypes" TEXT NOT NULL,
"mediums" TEXT NOT NULL,
"tokens" INTEGER NOT NULL,
PRIMARY KEY ("id")
);')
@db.execute_batch(sql)
@db.execute('CREATE UNIQUE INDEX "main"."name" ON "yu-gi-oh" ("name");')
@db.execute('commit transaction')
@count = @db.get_first_value("select COUNT(*) from `yu-gi-oh`") #重建计数
@all.clear #清空缓存
end
end
attr_accessor :id
attr_accessor :number
attr_accessor :name
attr_accessor :card_type
attr_accessor :monster_type
attr_accessor :atk
attr_accessor :def
attr_accessor :attribute
attr_accessor :type
attr_accessor :level
attr_accessor :lore
attr_accessor :status
attr_accessor :stats
attr_accessor :archettypes
attr_accessor :mediums
attr_accessor :tokens
def initialize(hash)
@id = hash['id'].to_i
@number = hash['number'].to_sym
@name = hash['name'].to_sym
@card_type = hash['card_type'].to_sym
@monster_type = hash["monster_type"] && hash["monster_type"].to_sym
@atk = hash['atk'] && hash['atk'].to_i
@def = hash['def'] && hash['def'].to_i
@attribute = hash['attribute'] && hash['attribute'].to_sym
@type = hash['type'] && hash['type'].to_sym
@level = hash['level'] && hash['level'].to_i
@lore = hash['lore']
@status = hash['status'].to_i
@stats = hash['stats'].split("\t").collect{|stat|stat.to_i}
@archettypes = hash['archettypes'].split("\t").collect{|archettype|stat.to_sym}
@mediums = hash['mediums'].split("\t").collect{|medium|medium.to_sym}
@tokens = hash['tokens'].to_i
@token = hash['token']
Card.cache[@id] = self
end
def create_image
@image ||= Surface.load("graphics/field/card.jpg").display_format
end
def image
@image ||= Surface.load("#{PicPath}/#{@id}.jpg").display_format rescue create_image
end
def image_small
@image_small ||= image.transform_surface(0xFF000000,0,54.0/image.w, 81.0/image.h,Surface::TRANSFORM_SAFE).copy_rect(1, 1, 54, 81).display_format
end
def image_horizontal
if @image_horizontal.nil?
image_horizontal = image_small.transform_surface(0xFF000000,90,1,1,Surface::TRANSFORM_SAFE)
@image_horizontal = image_horizontal.copy_rect(1, 1, 81, 54).display_format #SDL的bug,会多出1像素的黑边
image_horizontal.destroy
end
@image_horizontal
end
def unknown?
@id == 1
end
def monster?
[:融合怪兽, :同调怪兽, :超量怪兽, :通常怪兽, :效果怪兽, :调整怪兽, :仪式怪兽].include? card_type
end
def trap?
[:通常陷阱, :反击陷阱, :永续陷阱].include? card_type
end
def spell?
[:通常魔法, :速攻魔法, :装备魔法, :场地魔法, :仪式魔法, :永续魔法].include? card_type
end
def extra?
[:融合怪兽, :同调怪兽, :超量怪兽].include? card_type
end
def token?
@token
end
def diy?
number == :"00000000"
end
def known?
self != Unknown
end
def inspect
"[#{card_type}][#{name}]"
end
Unknown = Card.new('id' => 0, 'number' => :"00000000", 'attribute' => :, 'level' => 1, 'name' => "", 'lore' => '', 'card_type' => :通常怪兽, 'stats' => "", 'archettypes' => "", 'mediums' => "")
Unknown.instance_eval{@image = CardBack; @image_small = CardBack_Small}
end
#Card.load_from_ycff3
\ No newline at end of file
class ChatMessage
attr_accessor :user, :message, :channel, :time
def initialize(user, message, channel=:lobby, time=Time.now)
@user = user
@message = message
@channel = channel
@time = time
end
def name_visible?
case channel
when Symbol
true
when Room
!channel.include?(user)
when User
false
end
end
def name_color
case user.affiliation
when :owner
[220,20,60]
when :admin
[148,43,226]
else
if user.id == :subject
[128,128,128]
else
user == $game.user ? [0, 128, 0] : [0, 0, 255]
end
end
end
def message_color
if user.id == :subject
[128,128,128]
elsif name_visible?
[0, 0, 0]
elsif user == $game.user or ($game.room and !$game.room.include?($user) and user == $game.room.player1)
[0, 128, 0]
else
[255, 0, 0]
end
end
def self.channel_name(channel)
case channel
when :lobby
"#大厅"
when Symbol
"##{channel}"
when Room
"[#{channel.name}]"
when User
"@#{channel.name}"
else
channel
end
end
def self.channel_color(channel)
case channel
when Symbol
[0x34, 0x92, 0xEA]
when Room
[0xF2, 0x83, 0xC4]
#[255,250,240]
when User
[0xFA, 0x27, 0x27]
else
[0, 0, 0]
end
end
end
require 'yaml'
require_relative 'resolution'
Config = Module.new
module Config
module_function
def load(file="config.yml")
config = YAML.load_file(file) rescue {}
config = {} unless config.is_a? Hash
config['bgm'] = true if config['bgm'].nil?
config['screen'] ||= {}
config['screen']['width'], config['screen']['height'] = Resolution.default unless Resolution.all.include? [config['screen']['width'], config['screen']['height']]
config['i18n'] ||= {}
config['i18n']['locale'] ||= "#{Locale.current.language}-#{Locale.current.region}"
I18n.locale = config['i18n']['locale']
config
end
def save(config=$config, file="config.yml")
File.open(file, "w") { |file| YAML.dump(config, file) }
end
end
\ No newline at end of file
#encoding: UTF-8
#==============================================================================
# ■ Scene_Title
#------------------------------------------------------------------------------
#  title
#==============================================================================
require_relative 'card'
class Deck
attr_accessor :main
attr_accessor :side
attr_accessor :extra
attr_accessor :temp
Key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
def initialize(main, side=[], extra=[], temp=[])
@main = main
@side = side
@extra = extra
@temp = temp
end
def self.load(name)
main = []
side = []
extra = []
temp = []
now = main
open(name) do |file|
file.set_encoding "GBK", "UTF-8", :invalid => :replace, :undef => :replace
while line = file.readline.chomp!
case line
when /^\[(.+?)\](?:\#.*\#)?$/
now << Card.find($1.to_sym)
when "####"
now = side
when "===="
now = extra
when "$$$$"
now = temp
end
break if file.eof?
end
end
self.new(main, side, extra, temp)
end
def self.ygopro_deck_to_url_param(file)
card_usages = []
side = false
last_id = nil
count = 0
IO.readlines(file).each do |line|
if line[0] == '#'
next
elsif line[0, 5] == '!side'
card_usages.push({card_id: last_id, side: side, count: count}) if last_id
side = true
last_id = nil
else
card_id = line.to_i
if card_id.zero?
next
else
if card_id == last_id
count += 1
else
card_usages.push({card_id: last_id, side: side, count: count}) if last_id
last_id = card_id
count = 1
end
end
end
end
card_usages.push({card_id: last_id, side: side, count: count}) if last_id
result = ""
card_usages.each do |card_usage|
c = (card_usage[:side] ? 1 : 0) << 29 | card_usage[:count] << 27 | card_usage[:card_id]
4.downto(0) do |i|
result << Key[(c >> i * 6) & 0x3F]
end
end
require 'uri'
"name=#{URI.escape(File.basename(file, ".ydk"), Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&cards=#{result}"
end
#def self.decode(str)
# card_usages = []
# (0...str.length).step(5) do |i|
# decoded = 0
# str[i, 5].each do |char|
# decoded = (decoded << 6) + Key.index(char)
# side = decoded >> 29
# count = decoded >> 27 & 0x3
# card_id = decoded & 0x07FFFFFF
# card_usages.push(card_id: card_id, side: side, count: count)
# end
# end
#end
end
module Deck_Sync
require_relative 'deck'
class <<self
def start
Update.status = '正在同步卡组'
require 'open-uri'
require 'uri'
require 'net/http'
require 'json'
require 'date'
Thread.new {
just_updated = []
$log.info('下载卡组') { "https://my-card.in/decks/?user=#{URI.escape $game.user.id.bare.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")}" }
open("https://my-card.in/decks/?user=#{URI.escape $game.user.id.bare.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")}") { |list|
Dir.mkdir File.dirname(Ygocore.ygocore_path) if !File.directory? File.dirname(Ygocore.ygocore_path)
Dir.mkdir File.join File.dirname(Ygocore.ygocore_path), 'deck' if !File.directory? File.join File.dirname(Ygocore.ygocore_path), 'deck'
JSON.parse(list.read).each { |deck|
file = File.join(File.dirname(Ygocore.ygocore_path), 'deck', "#{deck['name']}.ydk")
if (!File.file?(file) || DateTime.parse(deck['updated_at']).to_time > File.mtime(file))
open(file, 'w') { |f|
main = []
side = []
deck['cards'].each { |card_usage|
card_usage['count'].times {
(card_usage['side'] ? side : main).push card_usage['card_id']
}
}
f.puts "#mycard deck sync #{deck['user']}"
f.puts "#main"
f.puts main.join("\n")
f.puts "!side"
f.puts side.join("\n")
}
File.utime(Time.now, DateTime.parse(deck['updated_at']).to_time, file)
end
if DateTime.parse(deck['updated_at']).to_time >= File.mtime(file)
just_updated.push file
end
}
} rescue $log.error('卡组下载') { [$!.inspect, *$!.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
Thread.new { watch } unless @watching
@watching = true
Dir.glob("#{File.dirname(Ygocore.ygocore_path)}/deck/*.ydk").each { |deck|
next if just_updated.include? deck
update(deck)
}
Update.status = nil
}
end
def watch
require 'fssm'
FSSM.monitor("#{File.dirname(Ygocore.ygocore_path)}/deck", '*.ydk') do
update { |base, relative| Deck_Sync.update "#{base}/#{relative}" }
delete { |base, relative| Deck_Sync.delete "#{base}/#{relative}" }
create { |base, relative| Deck_Sync.update "#{base}/#{relative}" }
end
end
def update(deck)
Update.status = "正在同步卡组: #{File.basename(deck, ".ydk")}"
begin
path = "/decks/?#{Deck.ygopro_deck_to_url_param(deck)}&user=#{URI.escape $game.user.id.bare.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")}&updated_at=#{URI.escape DateTime.now.iso8601, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")}"
$log.info("卡组上传") { path }
req = Net::HTTP::Put.new path
response = Net::HTTP.start('my-card.in', 443, use_ssl: true) { |http| http.request(req) }
rescue
$log.error('卡组上传') { [$!.inspect, *$!.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
end
Update.status = nil
end
def delete(deck)
Update.status = "正在同步卡组: #{File.basename(deck, ".ydk")}"
begin
path = "/decks/?name=#{URI.escape File.basename(deck, ".ydk"), Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")}&user=#{URI.escape $game.user.id.bare.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")}"
$log.info("卡组删除") { path }
req = Net::HTTP::Delete.new path
response = Net::HTTP.start('my-card.in', 443, use_ssl: true) { |http| http.request(req) }
rescue
$log.error('卡组删除') { [$!.inspect, *$!.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
end
Update.status = nil
end
end
end
\ No newline at end of file
module Dialog
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)
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)
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
result
else
[]
end
end
def web(url)
if Windows
Shell.ShellExecute url
else
system('xdg-open ' + url)
end
end
def uac(command, *args)
if Windows
Shell.ShellExecute File.expand_path(command), args.join(' '), Dir.pwd, "runas"
end
end
end
class FPSTimer
FPS_COUNT = 10
attr_accessor :fps
attr_reader :real_fps, :total_skip
attr_reader :count_sleep
# +fps+ is the number of frames per second that you want to keep,
# +accurary+ is the accurary of sleep/SDL.delay in milisecond
def initialize(fps = 60, accurary = 10, skip_limit = 15)
@fps = fps
@accurary = accurary / 1000.0
@skip_limit = skip_limit
reset
end
# reset timer, you should call just before starting loop
def reset
@old = get_ticks
@skip = 0
@real_fps = @fps
@frame_count = 0
@fps_old = @old
@count_sleep = 0
@total_skip = 0
end
# execute given block and wait
def wait_frame
now = get_ticks
nxt = @old + (1.0/@fps)
if nxt > now || @skip > @skip_limit
yield
@skip = 0
wait(nxt)
@old = nxt
else
@skip += 1
@total_skip += 1
@old = get_ticks
end
calc_real_fps
end
private
def wait(nxt)
#print "-"# 加了这货tk输入框不卡,原因不明=.=
sleeptime = nxt-get_ticks
sleep(sleeptime) if sleeptime > 0
end
def get_ticks
SDL.get_ticks / 1000.0
end
def calc_real_fps
@frame_count += 1
if @frame_count >= FPS_COUNT
@frame_count = 0
now = get_ticks
@real_fps = FPS_COUNT / (now - @fps_old)
@fps_old = now
end
end
end
#游戏适配器的抽象类
require_relative 'game_event'
require_relative 'action'
require_relative 'user'
require_relative 'room'
require_relative 'server'
class Game
attr_reader :users, :rooms, :servers, :filter
attr_accessor :user, :room, :player_field, :opponent_field, :turn, :turn_player, :phase
def initialize
@users = []
@rooms = []
@servers = []
@filter = {servers: [], waiting_only: false, normal_only: false}
end
def login(username, password=nil)
end
def refresh
end
def host(room_name, room_config)
end
def join(room)
end
def watch(room)
end
def leave
end
def action(action)
end
def chat(chatmessage)
end
def exit
$scene = Scene_Login.new if $scene
end
def watching?
@room and @room.include? @user
end
def self.deck_edit
require_relative 'window_deck'
@deck_window = Window_Deck.new
end
def refresh_interval
5
end
def show_chat_self
false
end
end
class Game_Card
attr_accessor :card, :position, :counters, :note
attr_writer :atk, :def
@@count = 0
def initialize(card=nil)
@@count += 1
$log.debug "创建活动卡片<#{card ? card.name : '??'}>,共计#{@@count}张"
@card = card || Card.find(nil)
reset
end
def atk
@card.atk.to_i #把"?"转为0
end
def def
@card.def.to_i #把"?"转为0
end
def reset(reset_position = true)
@position = :set if reset_position
@atk = @card.atk
@def = @card.def
@counters = 0
end
def card=(card)
return if @card == card
@card = card
@atk = @card.atk
@def = @card.def
end
def image_small
if @position == :set and !$game.player_field.hand.include?(self)
Card.find(nil).image_small
else
@card.image_small
end
end
def image_horizontal
if @position == :set and !$game.player_field.hand.include?(self)
Card.find(nil).image_horizontal
else
@card.image_horizontal
end
end
def method_missing(method, *args)
if method.to_s[0,9]== "original_"
method = method.to_s[9, method.to_s.size-9]
end
@card.send(method, *args)
end
def inspect
"<#{object_id}#{known? ? @card.inspect : '??'}>"
end
end
\ No newline at end of file
#游戏事件的抽象类
class Game_Event
@queue = []
def self.push(event)
@queue << event
end
def self.poll
@queue.shift
end
def self.parse(info, *args)
#适配器定义
end
class Login < Game_Event
attr_reader :user
def initialize(user)
@user = user
$game.user = @user
end
end
class AllUsers < Game_Event
attr_reader :users
def initialize(users)
@users = []
users.each do |user|
if user.friend?
@users.unshift user
else
@users << user
end
end
$game.users.replace @users
end
end
class AllServers < Game_Event
attr_reader :servers
def initialize(servers)
$game.servers.replace servers
end
end
class NewUser < AllUsers
attr_reader :users
def initialize(user)
@user = user
case @user.affiliation
when :owner
if index = $game.users.find_index { |user| user.affiliation != :owner }
$game.users.insert(index, @user)
else
$game.users << @user
end
when :admin
if index = $game.users.find_index { |user| user.affiliation != :owner and user.affiliation != :admin }
$game.users.insert(index, @user)
else
$game.users << @user
end
else
$game.users << @user
end
end
end
class MissingUser < AllUsers
attr_reader :users
def initialize(user)
@user = user
$game.users.delete @user
end
end
class AllRooms < Game_Event
attr_reader :rooms
def initialize(rooms)
@rooms = rooms
$game.rooms.replace @rooms
$game.rooms.sort_by! { |room| [room.status == :start ? 1 : 0, room.private ? 1 : 0, room.id] }
end
end
class RoomsUpdate < AllRooms
attr_reader :rooms
def initialize(rooms)
@rooms = rooms
$game.rooms.replace $game.rooms | @rooms
$game.rooms.delete_if { |room| room._deleted }
$game.rooms.sort_by! { |room| [room.status == :start ? 1 : 0, room.private ? 1 : 0, room.id] }
end
end
class NewRoom < AllRooms
attr_reader :room
def initialize(room)
@room = room
unless $game.rooms.include? @room
if @room.full?
$game.rooms << @room
else
$game.rooms.unshift @room
end
end
end
end
class MissingRoom < AllRooms
attr_reader :room
def initialize(room)
@room = room
$game.rooms.delete @room
end
end
class Chat < Game_Event
attr_reader :chatmessage
def initialize(chatmessage)
@chatmessage = chatmessage
end
end
class Join < Game_Event
attr_reader :room
def initialize(room)
@room = room
$game.room = @room
end
end
class Host < Join
end
class Watch < Game_Event
attr_reader :room
def initialize(room)
@room = room
$game.room = @room
end
end
class Leave < Game_Event
def initialize
end
end
class PlayerJoin < Game_Event
attr_reader :user
def initialize(user)
@user = user
$game.room.player2 = @user
end
end
class PlayerLeave < Game_Event
def initialize
$game.room.player2 = nil
end
end
class Action < Game_Event
attr_reader :action, :str
def initialize(action, str=action.escape)
@action = action
@str = str
end
end
class Error < Game_Event
attr_reader :title, :message, :fatal
def initialize(title, message, fatal=true)
@title = title
@message = message
@fatal = fatal
$log.error(@fatal ? "致命错误" : "一般错误") { "#{@title}: #{@message} #{caller}" }
end
end
class Unknown < Error
def initialize(*args)
super("unknown event", args.inspect)
end
end
end
\ No newline at end of file
#==============================================================================
# ■ Field
#------------------------------------------------------------------------------
#  Field
#==============================================================================
#英汉对照表
# field 场地
# fieldcard 场地魔法卡
# spelltrap 魔法陷阱
# spell 魔法
# trap 陷阱
# graveyard 墓地
# deck 卡组
# extra 额外卡组
# removed 除外区
class Game_Field
attr_accessor :lp
attr_accessor :deck
attr_accessor :extra
attr_accessor :field
attr_accessor :hand
attr_accessor :graveyard
attr_accessor :removed
def initialize(deck = nil)
@deck_original = deck || Deck.new(Array.new(60,Card.find(nil)), [], Array.new(15, Card.find(nil)))
reset
end
def reset
@lp = 8000
@deck = @deck_original.main.collect{|card|Game_Card.new(card)}.shuffle
@extra = @deck_original.extra.collect{|card|Game_Card.new(card)}
@field = Array.new(11)
@hand = []
@graveyard = []
@removed = []
end
def empty_monster_field
[8,7,9,6,10].each do |pos|
return pos if @field[pos].nil?
end
return
end
def empty_spelltrap_field
[3,2,4,1,5].each do |pos|
return pos if @field[pos].nil?
end
return
end
def empty_field(card)
if card.monster?
empty_monster_field
elsif card.card_type == :场地魔法
@field[0].nil? ? 0 : nil
else
empty_spelltrap_field
end
end
#def shuffle_hand
# @hand.shuffle!
# @hand.each{|card|card.card = Card::Unknown if card.position == :set}
#end
#def shuffle_deck
# @deck.shuffle!
# @deck.each{|card|card.card = Card::Unknown if card.position == set}
#end
end
\ No newline at end of file
module Graphics
module_function
Ext = ['.png', '.jpg', '.gif']
def load(directory, filename, alpha=true)
extname = File.extname(filename)
path = "graphics/#{directory}/#{File.dirname(filename)}/#{File.basename(filename, extname)}"
result = if extname.empty?
Ext.each do |ext|
result = load_file(path, ext)
break result if result
end
else
load_file(path, extname)
end
raise 'file not exist' if result.nil?
if alpha
result.display_format_alpha
else
result.display_format
end
end
def load_file(path, ext)
path_with_resolution = "#{path}-#{$config['screen']['width']}x#{$config['screen']['height']}#{ext}"
if File.file? path_with_resolution
Surface.load path_with_resolution
elsif File.file? path += ext
Surface.load path
end
end
end
\ No newline at end of file
#!/usr/bin/env ruby
begin
Windows = RUBY_PLATFORM["mswin"] || RUBY_PLATFORM["ming"]
Font = ['fonts/wqy-microhei.ttc', '/usr/share/fonts/wqy-microhei/wqy-microhei.ttc', '/usr/share/fonts/truetype/wqy/wqy-microhei.ttc', '/Library/Fonts/Hiragino Sans GB W3.otf'].find{|file|File.file? file}
#System_Encoding = Windows ? "CP#{`chcp`.scan(/\d+$/)}" : `locale |grep LANG |awk -F '=' '{print $2}'`
Dir.glob('post_update_*.rb').sort.each { |file| load file }
Thread.abort_on_exception = true
require_relative 'resolution'
require_relative 'announcement'
require_relative 'config'
require_relative 'association'
#i18n
require 'i18n'
require 'locale'
I18n.load_path += Dir['locales/*.yml']
I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
#读取配置文件
$config = Config.load
Config.save
#读取命令行参数
log = "log.log"
log_level = "INFO"
profile = nil
ARGV.each do |arg|
arg = arg.dup.force_encoding("UTF-8")
arg.force_encoding("GBK") unless arg.valid_encoding?
case arg
when /--log=(.*)/
log.replace $1
when /--log-level=(.*)/
log_level.replace $1
when /--profile=(.*)/
profile = $1
when /^mycard:.*|\.ydk$|\.yrp$|\.deck$/
require_relative 'quickstart'
$scene = false
when /register_association/
Association.register
$scene = false
end
end
unless $scene == false
#加载文件
require 'openssl'
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE #unsafe
require 'digest/sha1'
require 'digest/md5'
require 'logger'
require 'sdl'
include SDL
require_relative 'dialog'
require_relative 'graphics'
require_relative 'window'
require_relative 'widget_msgbox'
#日志
if log == "STDOUT" #调试用
log = STDOUT
end
$log = Logger.new(log, 1, 1024000)
$log.level = Logger.const_get log_level
#性能分析
if profile
if profile == "STDOUT"
profile = STDOUT
else
profile = open(profile, 'w')
end
require 'profiler'
RubyVM::InstructionSequence.compile_option = {
:trace_instruction => true,
:specialized_instruction => false
}
Profiler__::start_profile
end
SDL::Event::APPMOUSEFOCUS = 1
SDL::Event::APPINPUTFOCUS = 2
SDL::Event::APPACTIVE = 4
SDL.putenv ("SDL_VIDEO_CENTERED=1");
SDL.init(INIT_VIDEO)
WM::set_caption("MyCard", "MyCard")
WM::icon = Surface.load("graphics/system/icon.gif")
$screen = Screen.open($config['screen']['width'], $config['screen']['height'], 0, HWSURFACE | ($config['screen']['fullscreen'] ? FULLSCREEN : 0))
TTF.init
#声音
begin
SDL.init(INIT_AUDIO)
Mixer.open(Mixer::DEFAULT_FREQUENCY, Mixer::DEFAULT_FORMAT, Mixer::DEFAULT_CHANNELS, 1536)
Mixer.set_volume_music(60)
rescue
nil
end
#标题场景
require_relative 'scene_title'
$scene = Scene_Title.new
#自动更新, 加载放到SDL前面会崩, 原因不明
require_relative 'update'
Update.start
WM::set_caption("MyCard v#{Update::Version}", "MyCard")
#文件关联
Association.start
#初始化完毕
$log.info("main") { "初始化成功" }
end
rescue Exception => exception
open('error-程序出错请到论坛反馈.txt', 'w') { |f| f.write [exception.inspect, *exception.backtrace].join("\n") }
$scene = false
end
#主循环
begin
$scene.main while $scene
rescue ScriptError, StandardError => exception
exception.backtrace.each { |backtrace| break if backtrace =~ /^(.*)\.rb:\d+:in `.*'"$/ } #由于脚本是从main.rb开始执行的,总会有个能匹配成功的文件
$log.fatal($1) { [exception.inspect, *exception.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
$game.exit if $game
require_relative 'scene_error'
$scene = Scene_Error.new
retry
ensure
if profile
Profiler__::print_profile(profile)
profile.close
end
$log.close rescue nil
end
#==============================================================================
# 鈻�Scene_Title
#------------------------------------------------------------------------------
# 銆�itle
#==============================================================================
class Picture < Image
@load_path = "graphics/picture"
end
require_relative 'game'
require_relative 'user'
require_relative 'room'
require_relative 'ygocore/game'
$game = Ygocore.new
open('debug.txt', 'wb'){|f|f.write ARGV.first}
file = ARGV.first.dup.force_encoding("UTF-8")
file.force_encoding("GBK") unless file.valid_encoding?
file.encode!("UTF-8")
if ARGV.first[0, 9] == 'mycard://'
file = URI.unescape file[9, file.size-9]
uri = "http://" + URI.escape(file)
#elsif ARGV.first[0, 2] == '//'
# file = URI.unescape URI.unescape ARGV.first[2, ARGV.first.size-2]
# uri = "http://" + URI.escape(file)
else
uri = file
end
case file
when /^(.*\.yrp)$/i
require 'open-uri'
#fix File.basename
$1 =~ /(.*)(?:\\|\/)(.*?\.yrp)/
src = open(uri, 'rb') { |src| src.read }
Dir.mkdir("replay") unless File.directory?("replay")
open('replay/' + $2, 'wb') { |dest| dest.write src }
Ygocore.replay('replay/' + $2, true)
when /^(.*\.ydk)$/i
require 'open-uri'
#fix File.basename
$1 =~ /(.*)(?:\\|\/)(.*?)\.ydk/
src = open(uri, 'rb') { |src| src.read }
Dir.mkdir('ygocore/deck') unless File.directory?("ygocore/deck")
open('ygocore/deck/' + $2 + '.ydk', 'wb') { |dest| dest.write src }
Ygocore.run_ygocore($2, true)
when /^(.*)(\.txt|\.deck)$/i
require_relative 'deck'
d = $1
deck = Deck.load($&)
Dir.mkdir('ygocore/deck') unless File.directory?("ygocore/deck")
d =~ /^(.*)(?:\\|\/)(.*?)$/
open('ygocore/deck/' + $2 + '.ydk', 'w') do |dest|
dest.puts("#main")
deck.main.each { |card| dest.puts card.number }
dest.puts("#extra")
deck.extra.each { |card| dest.puts card.number }
dest.puts("!side")
deck.side.each { |card| dest.puts card.number }
end
Ygocore.run_ygocore($2, true)
when /^(?:(.+?)(?:\:(.+?))?\@)?([\d\.]+)\:(\d+)(?:\/(.*))$/
require 'uri'
require_relative 'server'
$game.user = User.new($1.to_sym, $1) if $1
$game.password = $2 if $2
room = Room.new(nil, $5.to_s)
room.server = Server.new(nil, nil, $3, $4.to_i, !!$2)
Ygocore.run_ygocore room, true
end
\ No newline at end of file
class Replay
ReplayPath = 'replay'
LastReplay = 'lastreplay.txt'
def initialize(filename=LastReplay)
@file = open(File.expand_path(filename, ReplayPath), 'w') unless filename.is_a? IO
end
def add(action)
action = action.escape if action.is_a? Action
@file.write action + "\n"
end
def save(filename="#{$game.room.player1.name}(#{$game.room.player1.id})_#{$game.room.player2.name}(#{$game.room.player2.id})_#{Time.now.strftime("%m%d%H%M")}.txt")
close
File.rename(@file.path, File.expand_path(filename, ReplayPath))
end
def close
@file.close
end
end
module Resolution
module_function
def all
[
[1024, 768],
[1024, 640]
]
end
def system
if Windows
require 'win32api'
get_system_metrics = Win32API.new "User32.dll", "GetSystemMetrics", ["L"], "L"
[get_system_metrics.call(0), get_system_metrics.call(1)]
else
`xdpyinfo`.scan(/dimensions: (\d+)x(\d+) pixels/).flatten.collect { |n| n.to_i } rescue [1440, 900]
end
end
def default
system_resolution = self.system
all.each do |width, height|
return [width, height] if system_resolution[0] > width and system_resolution[1] > height
end
all.last
end
end
\ No newline at end of file
require_relative 'cacheable'
class Room
Color = [[0,0,0], [255,0,0], [0,128,0], [0,0,255], [255, 165, 0]]
extend Cacheable
attr_accessor :id, :name, :player1, :player2, :private, :color, :forbid, :_deleted
attr_accessor :password
def initialize(id, name="等待更新", player1=nil, player2=nil, private=false, color=[0,0,0], session = nil, forbid = nil)
@id = id
@name = name
@player1 = player1
@player2 = player2
@private = private
@color = color
@session = session
@forbid = forbid
end
def set(id=:keep, name=:keep, player1=:keep, player2=:keep, private=:keep, color=:keep, session = nil, forbid=:keep)
@id = id unless id == :keep
@name = name unless name == :keep
@player1 = player1 unless player1 == :keep
@player2 = player2 unless player2 == :keep
@private = private unless private == :keep
@color = color unless color == :keep
@session = session unless session == :keep
@forbid = forbid unless forbid == :keep
end
def include?(user)
@player1 == user or @player2 == user
end
def extra
{}
end
def status
player2 ? :start : :wait
end
alias full? player2
alias private? private
end
\ No newline at end of file
#encoding: UTF-8
#==============================================================================
# ■ Scene_Base
#------------------------------------------------------------------------------
#  游戏中全部画面的超级类。
#==============================================================================
require_relative 'fpstimer'
require_relative 'game'
require_relative 'window_bgm'
require 'ogginfo'
require_relative 'widget_inputbox'
class Scene
attr_reader :windows
attr_reader :background
@@fpstimer = FPSTimer.new
@@last_bgm = @@bgm = nil
#--------------------------------------------------------------------------
# ● 主处理
#--------------------------------------------------------------------------
def initialize
@background = nil
@windows = []
@active_window = nil
@font = TTF.open(Font, 16)
end
def main
start
while $scene == self
update
@@fpstimer.wait_frame{draw}
end
terminate
end
def draw
if @background
$screen.put(@background,0,0)
else
$screen.fill_rect(0, 0, $screen.w, $screen.h, 0x000000)
end
@windows.each do |window|
window.draw($screen)
end
if Update.status
@font.draw_blended_utf8($screen, Update.status, 0, 0, 0xFF, 0xFF, 0xFF)
else
@font.draw_blended_utf8($screen, "%.1f" % @@fpstimer.real_fps, 0, 0, 0xFF, 0xFF, 0xFF)
end
$screen.update_rect(0,0,0,0)
end
#--------------------------------------------------------------------------
# ● 开始处理
#--------------------------------------------------------------------------
def start
if $config['bgm'] and @@last_bgm != bgm and SDL.inited_system(INIT_AUDIO) != 0 and File.file? "audio/bgm/#{bgm}"
@@bgm.destroy if @@bgm
@@bgm = Mixer::Music.load "audio/bgm/#{bgm}"
Mixer.fade_in_music(@@bgm, -1, 800)
title = OggInfo.new("audio/bgm/#{bgm}").tag["title"]
@bgm_window = Window_BGM.new title if title and !title.empty?
@@last_bgm = bgm
end rescue nil
end
def bgm
"title.ogg"
end
def last_bgm
@@last_bgm
end
def last_bgm=(bgm)
@@last_bgm = bgm
end
def refresh_rect(x, y, width, height, background=@background, ox=0,oy=0)
Surface.blit(background,x+ox,y+oy,width,height,$screen,x,y)
yield
$screen.update_rect(x, y, width, height)
end
#--------------------------------------------------------------------------
# ● 执行渐变
#--------------------------------------------------------------------------
def perform_transition
Graphics.transition(10)
end
#--------------------------------------------------------------------------
# ● 开始後处理
#--------------------------------------------------------------------------
def post_start
end
#--------------------------------------------------------------------------
# ● 更新画面
#--------------------------------------------------------------------------
def update
@bgm_window.update if @bgm_window and !@bgm_window.destroyed?
while event = Event.poll
handle(event)
end
#要不要放到一个Scene_Game里来处理这个?
while event = Game_Event.poll
handle_game(event)
end
end
def handle(event)
case event
when Event::MouseMotion
update_active_window(event.x, event.y)
when Event::MouseButtonDown
case event.button
when Mouse::BUTTON_LEFT
update_active_window(event.x, event.y)
@active_window.clicked if @active_window
if !(@active_window.is_a? Widget_InputBox)
Widget_InputBox.focus = false
end
when 4
@active_window.scroll_up if @active_window
when 5
@active_window.scroll_down if @active_window
end
when Event::MouseButtonUp
case event.button
when Mouse::BUTTON_LEFT
update_active_window(event.x, event.y)
@active_window.mouseleftbuttonup if @active_window
end
when Event::KeyDown
case event.sym
when Key::RETURN
if event.mod & Key::MOD_ALT != 0
$config['screen']['fullscreen'] = !$config['screen']['fullscreen']
$screen.destroy
style = HWSURFACE
style |= FULLSCREEN if $config['screen']["fullscreen"]
$screen = Screen.open($config['screen']["width"], $config['screen']["height"], 0, style)
Config.save
end
when Key::F12
$scene = Scene_Title.new
else
#$log.info('unhandled event'){event.inspect}
end
when Event::Quit
$scene = nil
when Event::Active
if (event.state & Event::APPINPUTFOCUS) != 0
Widget_InputBox.focus = event.gain
end
else
#$log.info('unhandled event'){event.inspect}
end
end
def handle_game(event)
case event
when Game_Event::Error
if event.fatal
Widget_Msgbox.new(event.title, event.message, :ok => "确定"){$game.exit if $game;$scene = Scene_Login.new}
else
Widget_Msgbox.new(event.title, event.message, :ok => "确定")
end
else
$log.debug('未处理的游戏事件'){event.inspect}
end
end
#--------------------------------------------------------------------------
# ● 结束前处理
#--------------------------------------------------------------------------
def pre_terminate
end
#--------------------------------------------------------------------------
# ● 结束处理
#--------------------------------------------------------------------------
def terminate
self.windows.each{|window|window.destroy}
end
def update_active_window(x, y)
self.windows.reverse.each do |window|
if window.include?(x, y) && window.visible
if window != @active_window
@active_window.lostfocus(window) if @active_window and !@active_window.destroyed?
@active_window = window
end
@active_window.mousemoved(x, y)
return @active_window
end
end
if @active_window and !@active_window.destroyed?
@active_window.lostfocus
@active_window = nil
end
end
end
#encoding: UTF-8
#==============================================================================
# ■ Scene_Config
#------------------------------------------------------------------------------
#  config
#==============================================================================
class Scene_Config < Scene
require_relative 'window_config'
BGM = 'title.ogg'
def start
@background = Surface.load("graphics/config/background.png").display_format
@config_window = Window_Config.new(0,0)
super
end
def handle(event)
case event
when Event::MouseMotion
self.windows.reverse.each do |window|
if window.include? event.x, event.y
@active_window = window
@active_window.mousemoved(event.x, event.y)
break
end
end
when Event::MouseButtonDown
case event.button
when Mouse::BUTTON_LEFT
@active_window.mousemoved(event.x, event.y)
@active_window.clicked
when 4
@active_window.cursor_up
when 5
@active_window.cursor_down
end
else
super
end
end
end
\ No newline at end of file
#encoding: UTF-8
#==============================================================================
# ■ Scene_Title
#------------------------------------------------------------------------------
#  title
#==============================================================================
class Scene_Deck < Scene
def start
@deck_window = Window.new(600,0,400,800)
loaddeck
@deck_window.contents.blt(@deck.main.first.pic, 200, 0)
@deck.main.each_with_index do |card, index|
@deck_window.contents.draw_text(0, index*24, card.name)
end
super
end
def loaddeck(file='/media/本地磁盘/zh99998/yu-gi-oh/token.txt')
src = IO.read(file)
src.force_encoding "GBK"
src.encode! 'UTF-8'
cards = {:main => [], :side => [], :extra => [], :temp => []}
now = :main
src.each_line do |line|
if line =~ /\[(.+)\]##(.*)\r\n/
cards[now] << Card.find($1.to_sym)
elsif line['####']
now = :side
elsif line['====']
now = :extra
elsif line['$$$$']
now = :temp
end
end
@deck = Deck.new(cards[:main], cards[:side], cards[:extra], cards[:temp])
end
def update
end
end
#encoding: UTF-8
#==============================================================================
# Scene_Duel
#------------------------------------------------------------------------------
# 决斗盘的场景
#==============================================================================
class Scene_Duel < Scene
require_relative 'window_lp'
require_relative 'window_phases'
require_relative 'window_field'
require_relative 'window_fieldback'
require_relative 'card'
require_relative 'deck'
require_relative 'action'
require_relative 'replay'
require_relative 'game_card'
require_relative 'game_field'
require_relative 'window_chat'
attr_reader :cardinfo_window
attr_reader :player_field_window
attr_reader :opponent_field_window
attr_reader :fieldback_window
def initialize(room, deck=nil)
super()
@room = room
@deck = deck
end
def start
WM::set_caption("MyCard v#{Update::Version} - #{$config['game']} - #{$game.user.name}(#{$game.user.id}) - #{@room.name}(#{@room.id})", "MyCard")
@background = Surface.load("graphics/field/main.png").display_format
Surface.blit(@background, 0, 0, 0, 0, $screen, 0, 0)
init_game
init_replay
@phases_window = Window_Phases.new(122, 356)
@fieldback_window = Window_FieldBack.new(131,173)
@cardinfo_window = Window_CardInfo.new(715, 0)
@player_field_window = Window_Field.new(2, 397, $game.player_field, true)
@opponent_field_window = Window_Field.new(2, 56, $game.opponent_field, false)
@player_lp_window = Window_LP.new(0,0, @room.player1, true)
@opponent_lp_window = Window_LP.new(360,0, @room.player2, false)
@join_se = Mixer::Wave.load("audio/se/join.ogg") if SDL.inited_system(INIT_AUDIO) != 0
create_action_window
create_chat_window
super
end
def bgm
"duel.ogg"
end
def create_action_window
@player_field_window.action_window = Window_Action.new
end
def create_chat_window
@background.fill_rect(@cardinfo_window.x, @cardinfo_window.height, 1024-@cardinfo_window.x, 768-@cardinfo_window.height,0xFFFFFFFF)
@chat_window = Window_Chat.new(@cardinfo_window.x, @cardinfo_window.height, 1024-@cardinfo_window.x, 768-@cardinfo_window.height){|text|chat(text)}
@chat_window.channel = @room
end
def chat(text)
action Action::Chat.new(true, text)
end
def init_replay
@replay = Replay.new
end
def save_replay
#@replay.save if @replay #功能尚不可用
end
def init_game
$game.player_field = Game_Field.new @deck
$game.opponent_field = Game_Field.new
$game.turn_player = true #
$game.turn = 0
end
def change_phase(phase)
action Action::ChangePhase.new(true, phase)
if phase == :EP and
action Action::TurnEnd.new(true, $game.player_field, $game.turn_player ? $game.turn : $game.turn.next)
end
end
def reset
action Action::Reset.new(true)
end
def first_to_go
action Action::FirstToGo.new(true)
end
def handle(event)
case event
when Event::MouseButtonDown
case event.button
when Mouse::BUTTON_RIGHT
if @player_field_window.action_window
@player_field_window.action_window.next
end
else
super
end
when Event::KeyDown
case event.sym
when Key::F1
action Action::Shuffle.new
@player_field_window.refresh
when Key::F2
first_to_go
@player_field_window.refresh
when Key::F3
action Action::Dice.new(true)
when Key::F5
reset
@player_field_window.refresh
when Key::F10
$game.leave
else
super
end
else
super
end
end
def action(action)
$game.action action# if @from_player
Game_Event.push Game_Event::Action.new(action)
end
def handle_game(event)
case event
when Game_Event::Chat
@chat_window.add event.chatmessage
when Game_Event::Action
if event.action.instance_of?(Action::Reset) and event.action.from_player
save_replay
init_replay
end
@replay.add event.str
str = event.str
if str =~ /^\[\d+\] (.*)$/m
str = $1
end
if str =~ /^(?:●|◎)→(.*)$/m
str = $1
end
user = if $game.room.player2 == $game.user
event.action.from_player ? $game.room.player2 : $game.room.player1
else
event.action.from_player ? $game.room.player1 : $game.room.player2
end
@chat_window.add ChatMessage.new(user, str, $game.room)
event.action.run
refresh
when Game_Event::Leave
$scene = Scene_Lobby.new
when Game_Event::Join
$game.room = event.room
@player_lp_window.player = $game.room.player1
@opponent_lp_window.player = $game.room.player2
player = $game.room.player1 == $game.user ? $game.room.player2 : $game.room.player1
if player
notify_send("对手加入房间", "#{player.name}(#{player.id})")
Mixer.play_channel(-1,@join_se,0) if SDL.inited_system(INIT_AUDIO) != 0
else
notify_send("对手离开房间", "对手离开房间")
end
else
super
end
end
def update
@cardinfo_window.update
@chat_window.update
super
end
def refresh
@fieldback_window.card = $game.player_field.field[0] && $game.player_field.field[0].card_type == :"场地魔法" && $game.player_field.field[0].position == :attack ? $game.player_field.field[0] : $game.opponent_field.field[0] && $game.opponent_field.field[0].card_type == :"场地魔法" && $game.opponent_field.field[0].position == :attack ? $game.opponent_field.field[0] : nil
@player_field_window.refresh
@opponent_field_window.refresh
@phases_window.player = $game.turn_player
@phases_window.phase = $game.phase
@player_lp_window.lp = $game.player_field.lp
@opponent_lp_window.lp = $game.opponent_field.lp
end
def terminate
unless $scene.is_a? Scene_Lobby or $scene.is_a? Scene_Duel
$game.exit
end
save_replay
super
end
def notify_send(title, msg)
command = "notify-send -i graphics/system/icon.ico #{title} #{msg}"
command = "start ruby/bin/#{command}".encode "GBK" if RUBY_PLATFORM["win"] || RUBY_PLATFORM["ming"]
system(command)
$log.info command
end
end
\ No newline at end of file
require_relative 'widget_msgbox'
class Scene_Error < Scene
def initialize(title="程序出错", text="似乎出现了一个bug,请到论坛反馈", &proc)
@title = title
@text = text
@proc = proc || proc{$scene = Scene_Title.new}
super()
end
def start
Widget_Msgbox.new(@title, @text, :ok => "确定", &@proc)
end
end
#encoding: UTF-8
#==============================================================================
# Scene_Lobby
#------------------------------------------------------------------------------
# 大厅
#==============================================================================
class Scene_Lobby < Scene
require_relative 'window_userlist'
require_relative 'window_userinfo'
require_relative 'window_roomlist'
require_relative 'window_chat'
require_relative 'window_host'
require_relative 'window_filter'
require_relative 'window_lobbybuttons'
require_relative 'chatmessage'
require_relative 'scene_duel'
require_relative 'deck_sync'
attr_reader :chat_window
def start
WM::set_caption("MyCard v#{Update::Version} - #{$config['game']} - #{$game.user.name}(#{$game.user.id})", "MyCard")
$game.refresh
@background = Graphics.load('lobby', 'background', false)
Surface.blit(@background, 0, 0, 0, 0, $screen, 0, 0)
@userlist = Window_UserList.new(24, 204, $game.users)
@roomlist = Window_RoomList.new(320, 50, $game.rooms)
@userinfo = Window_UserInfo.new(24, 24, $game.user)
@host_window = Window_LobbyButtons.new(595, 18)
@active_window = @roomlist
@chat_window = Window_Chat.new(313, $config['screen']['height'] - 225, 698, 212)
@count = 0
Deck_Sync.start
super
end
def bgm
"lobby.ogg"
end
def handle(event)
case event
when Event::KeyDown
case event.sym
when Key::UP
@active_window.cursor_up
when Key::DOWN
@active_window.cursor_down
when Key::F2
#@joinroom_msgbox = Widget_Msgbox.new("创建房间", "正在等待对手")
#$game.host Room.new(0, $game.user.name)
when Key::F3
#@joinroom_msgbox = Widget_Msgbox.new("加入房间", "正在加入房间")
#$game.join 'localhost'
when Key::F5
$game.refresh
when Key::F12
$game.exit
$scene = Scene_Login.new
end
else
super
end
end
def handle_game(event)
case event
when Game_Event::AllUsers
@userlist.items = $game.users
@userinfo.users = $game.users.size
when Game_Event::AllRooms, Game_Event::AllServers
@roomlist.items = $game.rooms.find_all { |room|
$game.filter[:servers].include?(room.server) and
$game.filter[:waiting_only] ? (room.status == :wait) : true and
$game.filter[:normal_only] ? (!room.tag? && (room.ot == 0) && (room.lp = 8000)) : true
}
@userinfo.rooms = $game.rooms.size
when Game_Event::Join
join(event.room)
when Game_Event::Watch
require_relative 'scene_watch'
$scene = Scene_Watch.new(event.room)
when Game_Event::Chat
@chat_window.add event.chatmessage
else
super
end
end
def join(room)
$scene = Scene_Duel.new(room)
end
def update
@chat_window.update
@host_window.update
@roomlist.update
if @count >= $game.refresh_interval*60
$game.refresh
@count = 0
end
@count += 1
super
end
def terminate
unless $scene.is_a? Scene_Lobby or $scene.is_a? Scene_Duel
$game.exit
end
end
end
\ No newline at end of file
#encoding: UTF-8
#==============================================================================
# ■ Scene_Login
#------------------------------------------------------------------------------
#  login
#==============================================================================
require_relative 'window_gameselect'
require_relative 'window_announcements'
require_relative 'window_login'
require_relative 'scene_replay'
require_relative 'scene_lobby'
class Scene_Login < Scene
def start
WM::set_caption("MyCard v#{Update::Version}", "MyCard")
@background = Graphics.load('login', 'background', false)
#======================================================
# We'll pay fpr that soon or later.
#======================================================
if $config['screen']['height'] == 768
@gameselect_window = Window_GameSelect.new(117,269)
elsif $config['screen']['height'] == 640
@gameselect_window = Window_GameSelect.new(117,134)
else
raise "无法分辨的分辨率"
end
#======================================================
# ENDS HERE
#======================================================
super
end
def update
@gameselect_window.update
super
end
def handle_game(event)
case event
when Game_Event::Login
require_relative 'scene_lobby'
$scene = Scene_Lobby.new
else
super
end
end
#def terminate
# @gameselect_window.destroy
#end
end
\ No newline at end of file
require_relative 'scene_watch'
class Scene_Replay < Scene_Watch
def initialize(replay)
@replay = replay
@count = 0
super(@replay.room)
$log.info('scene_reply'){'inited'}
end
def init_replay
end
def save_replay
end
def update
if @count and @count >= 60
event = @replay.get
if event
Game_Event.push event
@count = 0
else
Widget_Msgbox.new("回放", "战报回放完毕", :ok => "确定") { $scene = Scene_Login.new }
@count = nil #播放完毕标记
end
end
@count += 1 if @count
super
end
end
class Scene_Single < Scene
require 'Scene_Replay'
require_relative 'iduel/iduel'
def start
$game = Iduel.new
$scene = Scene_Replay.new Replay.load("E:/game/yu-gi-oh/test_rep.txt")
end
end
#encoding: UTF-8
#==============================================================================
# Scene_Title
#------------------------------------------------------------------------------
# title
#==============================================================================
require_relative 'scene'
require_relative 'widget_inputbox'
require_relative 'window_title'
BGM = 'title.ogg'
class Scene_Title < Scene
def start
WM::set_caption("MyCard v#{Update::Version}", "MyCard")
title = Dir.glob("graphics/titles/title_*.*")
if $config['screen']['height'] == 640
title.reject!{|t|t['full']}
else
title.reject!{|t|t['resize']}
end
title = title[rand(title.size)]
@background = Surface.load(title).display_format
Surface.blit(@background,0,0,0,0,$screen,0,0)
buttons_img = "graphics/system/titlebuttons.png"
if matched = title.match(/title_(\d+)/)
index = matched[1]
if File.exist? "graphics/titles/titlebuttons_#{index}.png"
buttons_img = "graphics/titles/titlebuttons_#{index}.png"
end
end
@command_window = Window_Title.new(title["left"] ? 200 : title["right"] ? 600 : title["special"] ? 42 : 400, title["special"] ? 321 : $config['screen']['height']/2-100,buttons_img)
(@decision_se = Mixer::Wave.load("audio/se/decision.ogg") if SDL.inited_system(INIT_AUDIO) != 0) rescue nil
super
end
def clear(x,y,width,height)
Surface.blit(@background,x,y,width,height,$screen,x,y)
end
def determine
return unless @command_window.index
Mixer.play_channel(-1,@decision_se,0) if @decision_se
case @command_window.index
when 0
require_relative 'scene_login'
$scene = Scene_Login.new
when 1
#require_relative 'scene_single'
require_relative 'widget_msgbox'
Widget_Msgbox.new("mycard", "功能未实现", :ok => "确定")
#Scene_Single.new
when 2
require_relative 'widget_msgbox'
require_relative 'scene_login'
require_relative 'deck'
load 'lib/ygocore/game.rb' #TODO:不规范啊不规范
Ygocore.deck_edit
when 3
require_relative 'scene_config'
$scene = Scene_Config.new
when 4
$scene = nil
end
end
def terminate
@command_window.destroy
@background.destroy
super
end
end
#encoding: UTF-8
#==============================================================================
# ■ Scene_Watch
#------------------------------------------------------------------------------
#  观战
#==============================================================================
require_relative 'scene_duel'
class Scene_Watch < Scene_Duel
def create_action_window
end
def chat(text)
$game.chat text, $game.room
Game_Event.push Game_Event::Action.new(Action::Chat.new(true, text), "#{$game.user}:#{text}")
end
def action(action)
end
def start
super
#$game.chat "#{$game.user.name}(#{$game.user.id})进入了观战", @room
end
def terminate
#$game.chat "#{$game.user.name}(#{$game.user.id})离开了观战", @room
end
def handle_game(event)
case event
when Game_Event::Leave
Widget_Msgbox.new("离开房间", "观战结束", :ok => "确定") { $scene = Scene_Lobby.new }
else
super
end
end
end
require_relative 'cacheable'
class Server
attr_accessor :id, :name, :ip, :port, :auth
extend Cacheable
def initialize(id, name="", ip="", port=0, auth=false)
@id = id
@name = name
@ip = ip
@port = port
@auth = auth
end
def set(id, name=:keep, ip=:keep, port=:keep, auth=:keep)
@id = id
@name = name unless name == :keep
@ip = ip unless ip == :keep
@port = port unless port == :keep
@auth = auth unless auth == :keep
end
end
\ No newline at end of file
#==============================================================================
# ■ Scene_Title
#------------------------------------------------------------------------------
#  title
#==============================================================================
class Sprite_Card < Sprite
Card_Witdh = 54
Card_Height = 81
def initialize(card, x, y)
super( card.pic ){|sprite|
sprite.x = x
sprite.y = y
#sprite.contents[0].font.size = 8
#sprite.contents[0].font.color = Color::White
sprite.zoom_x = Card_Witdh.to_f / sprite.contents[0].width
sprite.zoom_y = Card_Height.to_f / sprite.contents[0].height
#sprite.contents[0].font.smooth = true
#@font_bold = sprite.contents[0].font.dup
#@font_bold.bold = true
}
yield self if block_given?
end
end
require 'open-uri'
require "fileutils"
require_relative 'card'
module Update
Version = '1.3.8'
URL = "https://my-card.in/mycard/update.json?version=#{Version}"
class <<self
attr_reader :thumbnails, :images
attr_accessor :status
def start
Dir.glob("mycard-update-*-*.zip") do |file|
file =~ /mycard-update-(.+?)-(.+?)\.zip/
if $1 <= Version and $2 > Version
$log.info('安装更新') { file }
WM::set_caption("MyCard - 正在更新 #{Version} -> #{$2}", "MyCard")
require 'zip/zip'
Zip::ZipFile::open(file) do |zip|
zip.each do |f|
if !File.directory?(f.name)
FileUtils.mkdir_p(File.dirname(f.name))
end
f.extract { true }
end
end rescue $log.error('安装更新出错') { file+$!.inspect+$!.backtrace.inspect }
Version.replace $2
File.delete file
@updated = true
end
end
if @updated
require 'rbconfig'
spawn(File.join(RbConfig::CONFIG["bindir"],RbConfig::CONFIG[Windows ? "RUBYW_INSTALL_NAME" : "RUBY_INSTALL_NAME"] + RbConfig::CONFIG["EXEEXT"]) + " -KU lib/main.rb")
$scene = nil
end
@images = []
@thumbnails = []
@status = '正在检查更新'
@updated = false
(Thread.new do
open(URL) do |file|
require 'json'
reply = file.read
$log.info('下载更新-服务器回传') { reply }
reply = JSON.parse(reply)
$log.info('下载更新-解析后') { reply.inspect }
reply.each do |fil|
name = File.basename fil
@status = "正在下载更新#{name}"
open(fil, 'rb') do |fi|
$log.info('下载完毕') { name }
@updated = true
open(name, 'wb') do |f|
f.write fi.read
end
end rescue $log.error('下载更新') { '下载更新失败' }
end
end rescue $log.error('检查更新') { '检查更新失败' }
if @updated
require_relative 'widget_msgbox'
Widget_Msgbox.new('mycard', '下载更新完毕,点击确定重新运行mycard并安装更新', :ok => "确定") {
require 'rbconfig'
spawn(File.join(RbConfig::CONFIG["bindir"],RbConfig::CONFIG[Windows ? "RUBYW_INSTALL_NAME" : "RUBY_INSTALL_NAME"] + RbConfig::CONFIG["EXEEXT"]) + " -KU lib/main.rb")
$scene = nil
}
end
if File.file? "ygocore/cards.cdb"
require 'sqlite3'
db = SQLite3::Database.new("ygocore/cards.cdb")
db.execute("select id from datas") do |row|
@thumbnails << row[0]
end
@images.replace @thumbnails
if !File.directory?('ygocore/pics/thumbnail')
FileUtils.mkdir_p('ygocore/pics/thumbnail')
end
existed_thumbnails = []
Dir.foreach("ygocore/pics/thumbnail") do |file|
if file =~ /(\d+)\.jpg/
existed_thumbnails << $1.to_i
end
end
@thumbnails -= existed_thumbnails
existed_images = []
Dir.foreach("ygocore/pics") do |file|
if file =~ /(\d+)\.jpg/
existed_images << $1.to_i
end
end
@images -= existed_images
existed_images = []
if (!@images.empty? or !@thumbnails.empty?) and File.file?("#{Card::PicPath}/1.jpg")
db_mycard = SQLite3::Database.new("data/data.sqlite")
db_mycard.execute("select id, number from `yu-gi-oh` where number in (#{(@images+@thumbnails).uniq.collect { |number| "'%08d'" % number }.join(',')})") do |row|
id = row[0]
number = row[1].to_i
src = "#{Card::PicPath}/#{id}.jpg"
dest = "ygocore/pics/#{number}.jpg"
dest_thumb = "ygocore/pics/thumbnail/#{number}.jpg"
if File.file?(src)
@status = "检测到存在iDuel卡图 正在导入 #{id}.jpg"
existed_images << number
if !File.exist?(dest)
FileUtils.copy_file(src, dest)
FileUtils.copy_file(src, dest_thumb)
end
end
end
end
@images -= existed_images
@thumbnails -= existed_images
@thumbnails = (@thumbnails & @images) + (@thumbnails - @images)
unless @thumbnails.empty? and @images.empty?
$log.info('待下载的完整卡图') { @images.inspect }
$log.info('待下载的缩略卡图') { @thumbnails.inspect }
open('https://my-card.in/cards/image.json') do |f|
image_index = JSON.parse(f.read)
$log.info('卡图路径'){image_index}
url = image_index['url']
uri = URI(url)
image_req = uri.path
image_req += '?' + uri.query if uri.query
image_req += '#' + uri.fragment if uri.fragment
url = image_index['thumbnail_url']
uri = URI(url)
thumbnail_req = uri.path
thumbnail_req += '?' + uri.query if uri.query
thumbnail_req += '#' + uri.fragment if uri.fragment
require 'net/http/pipeline'
@thumbnails_left = @thumbnails.size
@images_left = @images.size
@error_count = 0
threads = 5.times.collect do
thread = Thread.new do
Net::HTTP.start uri.host, uri.port do |http|
http.pipelining = true
begin
list = @thumbnails
ids = []
while !@thumbnails.empty?
ids.replace @thumbnails.pop(100)
reqs = ids.reverse.collect { |id| Net::HTTP::Get.new thumbnail_req.gsub(':id', id.to_s) }
http.pipeline reqs do |res|
@status = "正在下载卡图 (剩余: 缩略#{@thumbnails_left} / 完整#{@images_left} #{"错误: #{@error_count}" if @error_count > 0})"
@thumbnails_left -= 1
id = ids.pop
if res.code[0] == '2' #http 2xx
next if File.file? "ygocore/pics/thumbnail/#{id}.jpg"
content = res.body
open("ygocore/pics/thumbnail/#{id}.jpg", 'wb') {|local|local.write content}
else
$log.warn("下载缩略卡图#{id}出错"){res}
@error_count += 1
end
end
end
list = @images
ids = []
while !@images.empty?
ids.replace @images.pop(100)
reqs = ids.reverse.collect { |id| Net::HTTP::Get.new image_req.gsub(':id', id.to_s) }
http.pipeline reqs do |res|
@status = "正在下载卡图 (剩余: 缩略#{@thumbnails_left} / 完整#{@images_left} #{"错误: #{@error_count}" if @error_count > 0})"
@images_left -= 1
id = ids.pop
if res.code[0] == '2' #http 2xx
next if File.file? "ygocore/pics/#{id}.jpg"
content = res.body
open("ygocore/pics/#{id}.jpg", 'wb') {|local|local.write content}
else
$log.warn("下载完整卡图#{id}出错"){res}
@error_count += 1
end
end
end
rescue
$log.error('卡图下载') { [$!.inspect, *$!.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
list.concat ids
end
end rescue $log.error('卡图下载线程出错') { $!.inspect.force_encoding("UTF-8") }
end
thread.priority = -1
thread
end
threads.each { |thread| thread.join }
end
end
end rescue $log.error('卡图更新') { [$!.inspect, *$!.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") }
@status = nil
end).priority = -1
end
end
end
require_relative 'cacheable'
class User
attr_accessor :id, :name, :friend, :affiliation
alias friend? friend
extend Cacheable
def initialize(id, name="")
@id = id
@name = name
end
def set(id, name = :keep)
@id = id
@name = name unless name == :keep
end
def avatar(size = :small)
Surface.new(SWSURFACE, 1, 1, 32, 0,0,0,0)
end
def status
room = room()
case
when room.nil?
:lobby
when room.player2
:dueling
else
:waiting
end
end
def room
$game && $game.rooms.find{|room|room.player1 == self or room.player2 == self}
end
def viewinfo
end
def color
[0,0,0]
end
def space
end
end
\ No newline at end of file
# To change this template, choose Tools | Templates
# and open the template in the editor.
class Widget_Checkbox < Window
attr_reader :checked
alias checked? checked
def initialize(window,x,y,width=window.width-x,height=24,checked=false,text="",&proc)
super(x+2,y+2,width,height,500) #+2是素材尺寸问题
@window = window
@text = text
@checked = checked
@checkbox = Surface.load('graphics/system/checkbox.png').display_format
@font = TTF.open(Font, 20)
@proc = proc
refresh
end
def checked=(checked)
@checked = checked
refresh
end
def mousemoved(x,y)
if x-@x < 24
@index = 0
else
@index = nil
end
end
def clicked
if @index
@checked = !@checked
@proc.call(@checked) if @proc
end
refresh
end
def refresh
clear
Surface.blit(@checkbox, 0, @checked ? @checkbox.h/2 : 0, @checkbox.w/3, @checkbox.h/2, self.contents, 0, 0)
@font.draw_blended_utf8(self.contents, @text, 24, 0, 0xFF, 0xFF, 0xFF)
end
#def clear(x=0,y=0,width=@width,height=@height)
# Surface.blit(@window.contents, @x-@window.x+x, @y-@window.y+y, width,height, self.contents, x, y)
#end
end
#输入法by夏娜
module RM_IME
if Windows
require 'dl'
require 'dl/import'
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
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
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
end
end
end
HWND = RM_INPUT.FindWindow('SDL_app', WM.caption[0])
end
module_function
def init
return unless Windows
return if @active
$log.info('输入法') { '开启' }
_init(HWND, 0, 0)
@x = 0
@y = 0
@active = true
end
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
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('输入法') { '关闭' }
_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
attr_reader :index
@@active = nil
@@cursor = nil
@@focus = true
def initialize(x, y, width, height, z=300, &proc)
super(x, y, width, height, z)
@font = TTF.open(Font, 20)
@proc = proc
@value = ""
@type = :text
@index = 0
@count = 0
@char_pos = [2]
end
def value=(value)
return if @value == value
@value = value
@char_pos.replace [2]
@value.each_char do |char|
@char_pos << @char_pos.last + @font.text_size(@type == :password ? '*' : char)[0]
end
if @index > value.size
self.index = value.size
end
refresh
end
def index=(index)
if index > @value.size
index = @value.size
elsif index < 0
index = 0
end
return if @index == index
@index = index
@count = 0
@@cursor.visible = true
@@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)
end
@@cursor.y = @y + 2
mouse_x = Mouse.state[0] - @x
@index = nil #强制重置
if mouse_x < 0 or @value.empty?
self.index = 0
else
@char_pos.each_with_index do |x, index|
if x > mouse_x
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)
end
def update
return unless self == @@active and @@focus
if @count >= 40
@count = 0
@@cursor.visible = !@@cursor.visible
else
@count += 1
end
RM_IME.update
new_value = self.value.dup
new_index = self.index
text = RM_IME.text
if !text.empty?
new_value.insert(@index, text)
new_index += text.size
end
if RM_IME.backspace? and @index > 0
new_value.slice!(@index-1, 1)
new_index -= 1
end
if RM_IME.delete? and @index < @value.size
new_value.slice!(@index, 1)
end
if RM_IME.left?
new_index -= 1
end
if RM_IME.right?
new_index += 1
end
self.value = new_value
self.index = new_index
if @proc
if RM_IME.esc?
self.value = '' if @proc.call :ESC
end
if RM_IME.tab?
self.value = '' if @proc.call :TAB
end
if RM_IME.enter? and text.empty?
self.value = '' if @proc.call :ENTER
end
end
end
def destroy
if @@active == self
Widget_InputBox.focus = false
end
super
end
def self.focus=(focus)
@@focus = focus
if !@@focus
RM_IME.dispose
@@cursor.destroy if @@cursor
end
end
end
\ No newline at end of file
class Widget_Msgbox < Window
Title_Color = [0xFF, 0xFF, 0xFF]
Message_Color = [0x04, 0x47, 0x7c]
class <<self
alias old_new new
def new(title, message, buttons={}, &proc)
if instance = $scene.windows.find{|window|window.class == self and !window.destroyed?} rescue nil
instance.set(title, message, buttons, &proc)
instance
else
old_new(title, message, buttons, &proc)
end
end
end
def initialize(title, message, buttons={}, &proc)
#@background = Surface.load 'graphics/system/msgbox.png'
@contents = Surface.load('graphics/system/msgbox.png').display_format
@button = Surface.load('graphics/system/button.png')
@font = TTF.open(Font, 16)
super((1024-@contents.w)/2, 230, @contents.w, @contents.h,500)
set(title, message, buttons, &proc)
end
def set(title, message, buttons={}, &proc)
@title = title
@message = message
@buttons = buttons
@proc = proc
@index = nil
@items = {}
@space = (@width - @buttons.size * @button.w / 3) / (@buttons.size + 1)
button_y = 100
@buttons.each_with_index do |button, index|
@items[button[0]] = [(@space+@button.w/3)*index+@space, button_y, @button.w/3, @button.h]
end
refresh
end
def title=(title)
@title.replace title
refresh
end
def message=(message)
@message.replace message
refresh
end
def buttons=(buttons)
@buttons.replace buttons
refresh
end
def refresh
@contents = Surface.load 'graphics/system/msgbox.png'
@font.draw_blended_utf8(@contents, @title, (@width-@font.text_size(@title)[0])/2, 2, *Title_Color)
@font.draw_blended_utf8(@contents, @message, 2, 24+2, *Message_Color)
@items.each_key {|index|draw_item(index, @index == index ? 1 : 0)}
end
def draw_item(index, status=0)
Surface.blit(@button,@button.w/3*status,0,@button.w/3,@button.h,@contents,@items[index][0],@items[index][1])
text_size = @font.text_size(@buttons[index])
@font.draw_blended_utf8(@contents, @buttons[index], @items[index][0]+(@button.w/3-text_size[0])/2, @items[index][1]+(@button.h-text_size[1])/2, 0xFF, 0xFF, 0xFF)
end
def mousemoved(x,y)
self.index = @items.each do |index, item_rect|
if x.between?(@x+item_rect[0], @x+item_rect[0]+item_rect[2]) and y.between?(@y+item_rect[1], @y+item_rect[1]+item_rect[3])
break index
end
end
end
def index=(index)
return if index == @index
if @index
#clear(*item_rect(@index))
draw_item(@index, 0)
end
if index.nil? or !@items.include? index
@index = nil
else
@index = index
draw_item(@index, 1)
end
end
def clicked
return if @index.nil?
self.destroy
@proc.call(@index) if @proc
end
def self.destroy
instance = $scene.windows.find{|window|window.class == self and !window.destroyed?}
instance.destroy if instance
end
end
class Widget_ScrollBar < Window
attr_reader :scroll, :scroll_max
def initialize(parent_window,x,y,height)
super(x,y,20,height,400)
@parent_window = parent_window
@up_button = Surface.load('graphics/lobby/scroll_up.png')
@down_button = Surface.load('graphics/lobby/scroll_down.png')
@back = Surface.load('graphics/lobby/scroll_background.png')
@bar = Surface.load('graphics/lobby/scroll.png')
@contents.fill_rect(0,0,@width, @height, 0xFFFFFFFF)
@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) rescue nil
refresh
end
def index=(index)
return if index == @index
if @index
clear(*item_rect(@index))
draw_item(@index, 0)
end
if index.nil? #or !@items.include? index
@index = nil
else
@index = index
draw_item(@index, 1)
end
end
def item_rect(index)
case index
when :up
[0,0,20,20]
when :scroll
[0,20,20,@height-40]
when :down
[0,@height-20,20,20]
end
end
def draw_item(index, status=0)
case index
when :up
Surface.blit(@up_button,status*20,0,20,20,@contents,0,0)
when :scroll
return if @scroll_max.zero?
Surface.blit(@bar,status*20,0,20,24,@contents,0,20+(@height-40-24)*@scroll/(@scroll_max))
when :down
Surface.blit(@down_button,status*20,0,20,20,@contents,0,@height-20)
end
end
def refresh
clear
[:up, :scroll, :down].each do |index|
draw_item(index, @index==index ? 1 : 0)
end
end
def mousemoved(x,y)
if Mouse.state[2] and @scrolling and @scroll_max > 0
@parent_window.scroll = [[0, (y - @y - @scrolling) * @scroll_max / (@height-40-24)].max, @scroll_max].min
end
case y-@y
when 0...20 #上按钮
self.index = :up
when 20...(@height-20)
self.index = :scroll
else
self.index = :down
end
end
def clicked
case @index
when :up
scroll_up
when :down
scroll_down
when :scroll
return if @scroll_max.zero?
y = (@height-40-24)*@scroll/(@scroll_max)
case Mouse.state[1] - @y - 20
when 0...y
scroll_up
when y..(y+24)
@scrolling = Mouse.state[1] - @y - y
else
scroll_down
end
end
end
def mouseleftbuttonup
@scrolling = nil
end
def scroll_up
@parent_window.scroll_up
end
def scroll_down
@parent_window.scroll_down
end
def scroll=(scroll)
return unless scroll and scroll.between?(0,@scroll_max)
@scroll = scroll
refresh
end
def scroll_max=(scroll_max)
return unless scroll_max and scroll_max != @scroll_max and scroll_max >=0
@scroll_max = scroll_max
if @scroll > @scroll_max
@scroll = @scroll_max
end
refresh
end
end
class Window
WLH = 24
attr_accessor :x, :y, :width, :height, :z, :contents, :visible, :viewport, :background
alias visible? visible
def initialize(x, y, width, height, z=200)
@x = x
@y = y
@z = z
@width = width
@height = height
@visible = true
#@angle = 0
@viewport = [0, 0, @width, @height]
@destroyed = false
amask = 0xff000000
rmask = 0x00ff0000
gmask = 0x0000ff00
bmask = 0x000000ff
#@background ||= Surface.new(SWSURFACE, @width, @height, 32, rmask, gmask, bmask, amask)
@contents ||= Surface.new(SWSURFACE, @width, @height, 32, rmask, gmask, bmask, amask)
#按Z坐标插入
unless $scene.windows.each_with_index do |window, index|
if window.z > @z
$scene.windows.insert(index, self)
break true
end
end == true
$scene.windows << self
end
end
def center_margin(text, width, font=@font)
(width - font.text_size(text)[0]) /2
end
def draw_stroked_text(text, x, y, size=1, font=@font, color=@color, color_stroke=@color_stroke)
[[x-size, y-size], [x-size, y], [x-size, y+size],
[x, y-size], [x, y+size],
[x+size, y-size], [x+size, y], [x+size, y+size],
].each { |pos| font.draw_blended_utf8(@contents, text, pos[0], pos[1], *color) }
font.draw_blended_utf8(@contents, text, x, y, *color_stroke)
end
def include?(x, y)
x >= @x && x < @x + @width && y >= @y && y < @y + @height
end
def destroy
@destroyed = true
@contents.destroy if @contents
$scene.windows.delete self if $scene
end
def destroyed?
@destroyed
end
def draw(screen)
return unless self.contents && self.visible? && !self.destroyed?
Surface.blit(self.contents, *self.viewport, screen, self.x, self.y)
end
def clear(x=0, y=0, width=@width, height=@height)
if @background
Surface.blit(@background, x, y, width, height, @contents, x, y)
elsif $scene and $scene.background
Surface.blit($scene.background, @x+x, @y+y, width, height, @contents, x, y)
else
@contents.fill_rect(x, y, width, height, 0xFF000000)
end
end
def update
#子类定义
end
def refresh
#子类定义
end
def mousemoved(x, y)
#子类定义
end
def clicked
#子类定义
end
def mouseleftbuttonup
#子类定义
end
def lostfocus(active_window=nil)
#子类定义
end
def cursor_up(wrap=false)
#子类定义
end
def cursor_down(wrap=false)
#子类定义
end
def scroll_up
cursor_up
end
def scroll_down
cursor_down
end
end
\ No newline at end of file
#决斗场景中下达动作指令的窗口
class Window_Action < Window_List
Color = [0xFF,0xFF,0xFF]
Color_Disabled = [0x66,0x66,0x66]
Color_Selected = [0xFF,0xFF,0x00]
def initialize#,items,items_available=Array.new(items.size, true))
super(0,0,123,20*WLH,300)
#@skin = Surface.load 'graphics/field/action.png'
@up = Surface.load('graphics/field/action_up.png')
@down = Surface.load('graphics/field/action_down.png')
@middle = Surface.load('graphics/field/action.png')
@up.set_alpha(RLEACCEL,255)
@middle.set_alpha(RLEACCEL,255)
@down.set_alpha(RLEACCEL,255)
@font = TTF.open(Font, 16)
@visible = false
end
def items=(items)
if items
@items = items.keys
@items_available = items.values
@height = @viewport[3] = @items.size*WLH+15*2
@index = @items_available.find_index(true) || 0
refresh
@visible = true
else
@visible = false
end
end
def clear(x=0,y=0,width=@width,height=@height)
@contents.put(@up, 0, 0)
Surface.transform_draw(@middle,@contents,0,1,(@items.size*WLH+20).to_f/@middle.h,0,0,0,15,Surface::TRANSFORM_SAFE) #+那里,我不知道为什么需要这么做,但是如果不+ 内容和底边会有一点空白
@contents.put(@down, 0, @height-15)
end
def index=(index)
if index and index >= 0 and index < @items.size
super(index)
refresh
end
end
def draw_item(index, status=0)
case status
when 0
color = @items_available[index] ? Color : Color_Disabled
@font.draw_blended_utf8(@contents, @items[index] , (@width-16*6)/2, index*WLH+15, *color)
when 1
@font.draw_blended_utf8(@contents, @items[index] , (@width-16*6)/2, index*WLH+15, *Color_Selected)
end
end
def next
if index = @items_available[@index.next...@items.size].find_index(true)
self.index = index + @index.next
elsif index = @items_available[0..@index].find_index(true)
self.index = index
else
self.index = (@index + 1) % @items.size
end
end
def mousemoved(x,y)
self.index = (y - @y-15) / WLH
end
def clicked
$scene.player_field_window.clicked
end
def lostfocus(active_window=nil)
if active_window != $scene.player_field_window
$scene.player_field_window.index = nil
end
end
end
\ No newline at end of file
class Window_Announcements < Window
def initialize(x,y,width,height)
super(x,y,width,height)
@index = 0
@count = 0
@items = $config[$config['game']]['announcements']
@last_item = @item = @items.first
@font = TTF.open(Font, 18)
@color = [44,64,78]
@time_color = [0x66, 0x66, 0x66]
@time_font = TTF.open(Font, 14)
@transforming = nil
refresh
end
def refresh
clear
return unless @item
if @transforming and @last_item
@font.style = TTF::STYLE_NORMAL
@font.draw_blended_utf8(@contents, @last_item.title, 0, -@transforming, *@color)
@time_font.draw_blended_utf8(@contents, @last_item.time.strftime('%Y-%m-%d'), 500, -@transforming+4, *@time_color) if @last_item.time
@font.style = TTF::STYLE_UNDERLINE if @focus
@font.draw_blended_utf8(@contents, @item.title, 0, -@transforming+24, *@color)
@time_font.draw_blended_utf8(@contents, @item.time.strftime('%Y-%m-%d'), 500, -@transforming+24+4, *@time_color) if @item.time
else
@font.style = @focus ? TTF::STYLE_UNDERLINE : TTF::STYLE_NORMAL
@font.draw_blended_utf8(@contents, @item.title, 0, 0, *@color)
@time_font.draw_blended_utf8(@contents, @item.time.strftime('%Y-%m-%d'), 500, 4, *@time_color) if @item.time
end
end
def update
if @transforming
refresh
if @transforming >= 24
@transforming = nil
@last_item = @item
else
@transforming += 1
end
else
if @last_item != @item
@transforming = 0
end
end
if @item != $config[$config['game']]['announcements'][@index]
@items = $config[$config['game']]['announcements']
@index = 0
@count = 0
@item = @items[@index]
end
if @focus
@count = 0
else
@count += 1
end
if @count>= 180 and !@items.empty?
@index = (@index + 1) % @items.size
@count = 0
@item = @items[@index]
end
super
end
def clicked
return unless @item
Dialog.web @item.url if @item.url
end
def mousemoved(x,y)
if !@focus
@focus = true
refresh
else
@focus = true
end
end
def lostfocus(active_window=nil)
@focus = false
refresh
end
end
require_relative 'window'
class Window_BGM < Window
WLH=20
def initialize(bgm_name)
@bgm_name = bgm_name
@font = TTF.open(Font, 18)
width = @font.text_size("♪#{@bgm_name}")[0]
@count = 0
@contents = @font.render_blended_utf8("♪#{@bgm_name}" , 255,255,255)
super($config['screen']['width']-width, -WLH, width, WLH,999)
end
def update
if @count >= 180
if @y <= -WLH
self.destroy
else
self.y -= 1
end
elsif @y < 0
self.y += 1
else
@count += 1
end
end
end
\ No newline at end of file
class Window_CardInfo < Window
WLH = 20
def initialize(x,y)
super(x,y,1024-x,524,300)
@font = TTF.open(Font, 16)
tip = Card.new('name' => :mycard, 'number' => :"000000", 'lore' => "iDuel部分仅实现了观战,无法进行决斗\n\n提示:\n快捷键:\nF10 退出房间\nF12 返回主界面", 'card_type' => :"通常魔法", 'stats' => "", 'archettypes' => "", "mediums" => "", "tokens" => 0)
tip.instance_eval { @image = Card::CardBack; @image_small = Card::CardBack_Small }
self.card = Game_Card.new tip
end
def card=(card)
return if card.nil? or card == @card or !card.known?
@card = card
refresh
end
def update
if @lore_start
if @lore_start >= @card.lore.size
@lore_start = nil #停止描绘
return
end
char = @card.lore[@lore_start]
@lore_start += 1
if char == "\n"
@lore_pos[0] = 0
@lore_pos[1] += WLH
return
end
width = @font.text_size(char)[0]
if @lore_pos[0] + width > @width
@lore_pos[0] = 0
@lore_pos[1] += WLH
end
@font.draw_blended_utf8(@contents, char, @lore_pos[0], @lore_pos[1], 0xFF, 0xFF, 0xFF)
@lore_pos[0] += width
end
end
def refresh
@contents.fill_rect(0,0, @width, @height,0xCC005555)
@contents.put @card.image,0,0
@font.draw_blended_utf8(@contents, "[#{@card.name}]", 160, 0, 0xFF, 0xFF, 0x55)
@font.draw_blended_utf8(@contents, "#{@card.card_type}", 160, WLH, 0xFF, 0xFF, 0x55)
if @card.monster?
@font.draw_blended_utf8(@contents, "种族: #{@card.type}", 160, WLH*2, 0xFF, 0xFF, 0xFF)
@font.draw_blended_utf8(@contents, "星级: #{@card.level}", 160, WLH*3, 0xFF, 0xFF, 0xFF)
@font.draw_blended_utf8(@contents, "攻击力: #{@card.atk}", 160, WLH*4, 0xFF, 0xFF, 0xFF)
@font.draw_blended_utf8(@contents, "防御力: #{@card.def}", 160, WLH*5, 0xFF, 0xFF, 0xFF)
end
@lore_start = 0
@lore_pos = [0, 234]
# @font.draw_blended_utf8(@contents, @card.inspect, 0, 300, 0xFF, 0xFF, 0x66)
end
end
#==============================================================================
# ■ Scene_Title
#------------------------------------------------------------------------------
#  title
#==============================================================================
require_relative 'widget_scrollbar'
require_relative 'widget_inputbox'
require_relative 'chatmessage'
require_relative 'window_scrollable'
class Window_Chat < Window_Scrollable
WLH=16
def initialize(x, y, width, height)
super(x,y,width,height)
if @width > 600 #判断大厅还是房间,这个判据比较囧,待优化
@chat_background = Surface.load("graphics/system/chat.png").display_format
else
@chat_background = Surface.load("graphics/system/chat_room.png").display_format
end
@background = @contents.copy_rect(0,0,@contents.w,@contents.h) #new而已。。
@background.fill_rect(0,0,@background.w, @background.h, 0xFFb2cefe)
@background.put(@chat_background,0,31-4)
@tab = Surface.load "graphics/system/tab.png"
@chat_input = Widget_InputBox.new(@x+8, @y+@height-24-10, @width-14, 24) do |key|
case key
when :ENTER
if !@chat_input.value.empty?
chatmessage = ChatMessage.new($game.user, @chat_input.value, @channel)
$game.chat chatmessage
Game_Event.push Game_Event::Chat.new(chatmessage) if @channel != :lobby
true
end
end
end
@chat_input.refresh
@font = TTF.open(Font, 14)
@scrollbar = Widget_ScrollBar.new(self,@x+@width-20-8,@y+31+3,@height-68)
@page_size = (@height-68)/WLH
@@list ||= {}
@list_splited = {}
@@list.each_pair do |channel, chatmessages|
chatmessages.each do |chatmessage|
add_split(chatmessage)
end
end
@channels = []
self.channel = :lobby
end
def add(chatmessage)
@@list[chatmessage.channel] ||= []
unless @channels.include? chatmessage.channel
@channels << chatmessage.channel
refresh
end
@@list[chatmessage.channel] << chatmessage
scroll_bottom = @items.size - self.scroll <= @page_size
add_split(chatmessage)
if chatmessage.channel == @channel
@scroll = [@items.size - @page_size, 0].max if scroll_bottom
refresh
end
end
def add_split(chatmessage)
@list_splited[chatmessage.channel] ||= []
@list_splited[chatmessage.channel] << [chatmessage, ""]
width = name_width(chatmessage)
line = 0
chatmessage.message.each_char do |char|
if char == "\n"
line += 1
width = 0
@list_splited[chatmessage.channel] << [chatmessage.message_color, ""]
else
char_width = @font.text_size(char)[0]
if char_width + width > @width-14-20
line += 1
width = char_width
@list_splited[chatmessage.channel] << [chatmessage.message_color, char]
else
@list_splited[chatmessage.channel].last[1] << char
width += char_width
end
end
end
end
def mousemoved(x,y)
if y-@y < 31 and (x-@x) < @channels.size * 100
self.index = @channels[(x-@x) / 100]
else
self.index = nil
end
end
def clicked
case @index
when nil
when Integer
else
self.channel = @index
end
end
def channel=(channel)
return if @channel == channel
@channel = channel
@channels << channel unless @channels.include? channel
@list_splited[channel] ||= []
@items = @list_splited[channel]
@scroll = [@items.size - @page_size, 0].max
refresh
end
def draw_item(index, status=0)
case index
when nil
when Integer #描绘聊天消息
draw_item_chatmessage(index, status)
else #描绘频道标签
draw_item_channel(index, status)
end
end
def draw_item_channel(channel, status)
index = @channels.index(channel)
Surface.blit(@tab,0,@channel == channel ? 0 : 31,100,31,@contents,index*100+3,0)
channel_name = ChatMessage.channel_name channel
x = index*100+(100 - @font.text_size(channel_name)[0])/2
draw_stroked_text(channel_name,x,8,1,@font, [255,255,255], ChatMessage.channel_color(channel))
end
def draw_item_chatmessage(index, status)
x,y = item_rect_chatmessage(index)
chatmessage, message = @items[index]
if chatmessage.is_a? ChatMessage
@font.draw_blended_utf8(@contents, chatmessage.user.name+':', x, y, *chatmessage.name_color) if chatmessage.name_visible?
@font.draw_blended_utf8(@contents, message, x+name_width(chatmessage), y, *chatmessage.message_color) unless chatmessage.message.lines.first.chomp.empty?
else
@font.draw_blended_utf8(@contents, message, x, y, *chatmessage) unless message.empty?
end
end
def item_rect(index)
case index
when nil
when Integer #描绘聊天消息
item_rect_chatmessage(index)
else #描绘频道标签
item_rect_channel(index)
end
end
def item_rect_channel(channel)
[@channels.index(channel)*100+3, 0, 100, 31]
end
def item_rect_chatmessage(index)
[8, (index-@scroll)*WLH+31+3, @width, self.class::WLH]
end
def refresh
super
@channels.each {|channel|draw_item_channel(channel, @index==channel)}
end
def name_width(chatmessage)
chatmessage.name_visible? ? @font.text_size(chatmessage.user.name+':')[0] : 0
end
def index_legal?(index)
case index
when nil,Integer
super
else
@channels.include? index
end
end
def scroll_up
self.scroll -= 1
end
def scroll_down
self.scroll += 1
end
def update
@chat_input.update
end
end
\ No newline at end of file
class Window_Config < Window
def initialize(x,y)
super(x,y,$screen.w, $screen.h)
@checkbox = Surface.load('graphics/system/checkbox.png')
@button = Surface.load('graphics/system/button.png')
@background = Surface.load('graphics/config/background.png').display_format
@contents = Surface.load('graphics/config/background.png').display_format
@font = TTF.open(Font, 20)
@index = nil
@items = {
:fullscreen => [200,160,120,WLH],
:bgm => [200,160+WLH,120,WLH],
:avatar_cache => [200+220, 160+WLH*2,@button.w/3, @button.h],
:return => [200,160+WLH*3+10,100,WLH]
}
refresh
end
def draw_item(index, status=0)
case index
when :fullscreen
Surface.blit(@checkbox, 20*status, $config['screen']['fullscreen'] ? 20 : 0, 20, 20, @contents, @items[:fullscreen][0],@items[:fullscreen][1])
case status
when 0
@font.draw_blended_utf8(@contents, "全屏模式", @items[:fullscreen][0]+24, @items[:fullscreen][1], 0xFF,0xFF,0xFF)
when 1
@font.draw_shaded_utf8(@contents, "全屏模式", @items[:fullscreen][0]+24, @items[:fullscreen][1], 0xFF,0xFF,0xFF, 0x11, 0x11, 0x11)
when 2
@font.draw_shaded_utf8(@contents, "全屏模式", @items[:fullscreen][0]+24, @items[:fullscreen][1], 0x11,0x11,0x11, 0xFF, 0xFF, 0xFF)
end
when :bgm
Surface.blit(@checkbox, 20*status, $config['bgm'] ? 20 : 0, 20, 20, @contents, @items[:bgm][0], @items[:bgm][1])
case status
when 0
@font.draw_blended_utf8(@contents, "背景音乐", @items[:bgm][0]+24, @items[:bgm][1], 0xFF,0xFF,0xFF)
when 1
@font.draw_shaded_utf8(@contents, "背景音乐", @items[:bgm][0]+24, @items[:bgm][1], 0xFF,0xFF,0xFF, 0x11, 0x11, 0x11)
when 2
@font.draw_shaded_utf8(@contents, "背景音乐", @items[:bgm][0]+24, @items[:bgm][1], 0x11,0x11,0x11, 0xFF, 0xFF, 0xFF)
end
when :avatar_cache
size = 0
count = 0
Dir.glob("graphics/avatars/*").reject{|file|File.basename(file) =~ /(?:error|loading)_(?:small|middle|large)\.png/}.each do |file|
count += 1
size += File.size(file)
end
@font.draw_blended_utf8(@contents, "头像缓存: #{count}个文件, #{filesize_inspect(size)}", 200, @items[:avatar_cache][1], 0xFF,0xFF,0xFF)
Surface.blit(@button, @button.w/3*status, 0, @button.w/3, @button.h, @contents, @items[:avatar_cache][0],@items[:avatar_cache][1])
@font.draw_blended_utf8(@contents, "清空", @items[:avatar_cache][0]+10, @items[:avatar_cache][1]+5, 0xFF,0xFF,0xFF)
when :return
@font.draw_blended_utf8(@contents, "回到标题画面", @items[:return][0],@items[:return][1], 0xFF,0xFF,0xFF)
end
end
def item_rect(index)
@items[index]
end
def index=(index)
return if index == @index
if @index
clear(*item_rect(@index))
draw_item(@index, 0)
end
if index.nil? or !@items.include? index
@index = nil
else
@index = index
clear(*item_rect(@index))
draw_item(@index, 1)
end
end
def mousemoved(x,y)
self.index = @items.each do |index, item_rect|
if x.between?(@x+item_rect[0], @x+item_rect[0]+item_rect[2]) and y.between?(@y+item_rect[1], @y+item_rect[1]+item_rect[3])
break index
end
end
end
def refresh
clear
@items.each_key{|index|draw_item(index)}
end
def clicked
case @index
when :fullscreen
clear(*item_rect(@index))
$config['screen']['fullscreen'] = !$config['screen']['fullscreen']
$screen.destroy
style = HWSURFACE
style |= FULLSCREEN if $config['screen']["fullscreen"]
$screen = Screen.open($config['screen']["width"], $config['screen']["height"], 0, style)
draw_item(@index, 1)
when :bgm
clear(*item_rect(@index))
$config['bgm'] = !$config['bgm']
if $config['bgm']
$scene = Scene_Config.new
else
$scene.last_bgm = nil
Mixer.fade_out_music(800) if SDL.inited_system(INIT_AUDIO) != 0
end
draw_item(@index, 1)
when :avatar_cache
#clear(*item_rect(@index))
Dir.glob("graphics/avatars/*").reject{|file|File.basename(file) =~ /(?:error|loading)_(?:small|middle|large)\.png/}.each do |file|
File.delete file
end
refresh
#draw_item(:avatar_cache,1)
when :return
$scene = Scene_Title.new
end
Config.save
end
def filesize_inspect(size)
case size
when 0...1024
size.to_s + "B"
when 1024...1024*1024
(size/1024).to_s + "KB"
else
(size/1024/1024).to_s + "MB"
end
end
end
\ No newline at end of file
class Window_Deck < Window_Scrollable
attr_reader :index
def initialize
@items = Dir.glob("ygocore/deck/*.ydk")
@background = Surface.load(@items.size > 4 ? 'graphics/lobby/host.png' : 'graphics/system/msgbox.png').display_format
super((1024-@background.w)/2, 230, @background.w, @background.h, 300)
@items_button = Surface.load("graphics/system/deck_buttons.png")
@items_buttons = {edit: "编辑", delete: "删除", export: "导出", share: "分享", buy: "打印"}
button_y = @height - 36
@button = Surface.load("graphics/system/button.png")
@buttons = {import: "导入", edit: "编辑", close: "关闭"}
space = (@width - @buttons.size * @button.w / 3) / (@buttons.size + 1)
@buttons_pos = {}
@buttons.each_with_index do |button, index|
@buttons_pos[button[0]] = [(space+@button.w/3)*index+space, button_y, @button.w/3, @button.h]
end
@font = TTF.open(Font, 16)
@title_color = [0xFF, 0xFF, 0xFF]
@color = [0x04, 0x47, 0x7c]
@page_size = 10
refresh
end
def refresh
clear
@font.draw_blended_utf8(@contents, "卡组编辑", (@width-@font.text_size("卡组编辑")[0])/2, 2, *@title_color)
@items = Dir.glob("ygocore/deck/*.ydk")
@background = Surface.load(@items.size > 4 ? 'graphics/lobby/host.png' : 'graphics/system/msgbox.png').display_format
@height = @background.h
@items[@scroll...[(@scroll+@page_size), @items.size].min].each_with_index do |deck, index|
@font.draw_blended_utf8(@contents, File.basename(deck, ".ydk"), 16, 28+WLH*index, *@color)
end
(@scroll...[(@scroll+@page_size), @items.size].min).each do |index|
@items_buttons.each_key do |key|
draw_item([index, key], self.index==[index, key] ? 1 : 0)
end
end
@buttons.each_key do |index|
draw_item(index, self.index==index ? 1 : 0)
end
end
def draw_item(index, status=0)
x, y=item_rect(index)
if index.is_a? Array
Surface.blit(@items_button, status*@items_button.w/3, @items_buttons.keys.index(index[1])*@items_button.h/@items_buttons.size, @items_button.w/3, @items_button.h/@items_buttons.size, @contents, x, y)
else
Surface.blit(@button, @button.w/3*status, 0, @button.w/3, @button.h, @contents, x, y)
text_size = @font.text_size(@buttons[index])
@font.draw_blended_utf8(@contents, @buttons[index], x+(@button.w/3-text_size[0])/2, y+(@button.h-text_size[1])/2, 0xFF, 0xFF, 0xFF)
end
end
def mousemoved(x, y)
new_index = nil
line = (y-@y-28)/WLH + @scroll
if line.between?(@scroll, [@scroll+@page_size-1, @items.size-1].min)
i = (x - @x - (@width - @items_buttons.size * @items_button.w / 3)) / (@items_button.w/3)
if i >= 0
new_index = [line, @items_buttons.keys[i]]
else
new_index = [line, :edit]
end
else
@buttons_pos.each_key do |index|
if (x - @x).between?(@buttons_pos[index][0], @buttons_pos[index][0]+@buttons_pos[index][2]) and (y-@y).between?(@buttons_pos[index][1], @buttons_pos[index][1]+@buttons_pos[index][3])
new_index = index
break
end
end
end
self.index = new_index
end
def cursor_up(wrap=false)
end
def cursor_down(wrap=false)
end
def item_rect(index)
if index.is_a? Array
[
@width - (@items_button.w/3) * (@items_buttons.keys.reverse.index(index[1])+1),
28+WLH*(index[0]-@scroll),
@items_button.w/3,
@items_button.h/@items_buttons.size
]
else
@buttons_pos[index]
end
end
def index=(index)
return if index == @index
if @index and index_legal?(@index)
clear(*item_rect(@index))
draw_item(@index, 0)
end
@index = index
if @index and index_legal?(@index)
clear(*item_rect(@index))
draw_item(@index, 1)
end
end
def index_legal?(index)
if index.is_a? Array
(@scroll...[(@scroll+@page_size), @items.size].min).include? index[0]
else
true
end
end
def clicked
case self.index
when :import
import
refresh
when :edit
Ygocore.run_ygocore(:deck)
when :close
destroy
when Array
case index[1]
when :edit
Ygocore.run_ygocore(File.basename(@items[index[0]], ".ydk"))
refresh
when :share, :buy
card_usages = []
side = false
last_id = nil
count = 0
IO.readlines(@items[index[0]]).each do |line|
if line[0] == '#'
next
elsif line[0, 5] == '!side'
card_usages.push({card_id: last_id, side: side, count: count}) if last_id
side = true
last_id = nil
else
card_id = line.to_i
if card_id.zero?
next
else
if card_id == last_id
count += 1
else
card_usages.push({card_id: last_id, side: side, count: count}) if last_id
last_id = card_id
count = 1
end
end
end
end
card_usages.push({card_id: last_id, side: side, count: count}) if last_id
result = ""
key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
card_usages.each do |card_usage|
c = (card_usage[:side] ? 1 : 0) << 29 | card_usage[:count] << 27 | card_usage[:card_id]
4.downto(0) do |i|
result << key[(c >> i * 6) & 0x3F]
end
end
require 'uri'
Dialog.web "https://my-card.in/decks/new#{'.pdf' if index[1] == :buy}?name=#{URI.escape(File.basename(@items[index[0]], ".ydk"), Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&cards=#{result}#{'#share' if index[1] == :share}"
when :delete
require_relative 'widget_msgbox'
index = @index
Widget_Msgbox.new("删除卡组", "确定要删除卡组 #{File.basename(@items[index[0]], '.ydk')} 吗", buttons={ok: "确定", cancel: "取消"}) do |clicked|
if clicked == :ok
File.delete @items[index[0]]
refresh
end
end
when :export
export
end
end
end
def import
file = Dialog.get_open_file("导入卡组", "所有支持的卡组 (*.ydk;*.txt;*.deck)" => "*.ydk;*.txt;*.deck", "ygocore卡组 (*.ydk)" => "*.ydk", "OcgSoft卡组 (*.txt;*.deck)" => "*.txt;*.deck")
if !file.empty?
#fix for stdlib File.extname
file =~ /(\.deck|\.txt|\.ydk)$/i
extname = $1
Dir.mkdir "ygocore/deck" unless File.directory?("ygocore/deck")
open("ygocore/deck/#{File.basename(file, extname)+".ydk"}", 'w') do |dest|
if file =~ /(\.deck|\.txt)$/i
deck = Deck.load(file)
dest.puts("#main")
deck.main.each { |card| dest.puts card.number }
dest.puts("#extra")
deck.extra.each { |card| dest.puts card.number }
dest.puts("!side")
deck.side.each { |card| dest.puts card.number }
else
open(file) do |src|
dest.write src.read
end
end
end rescue ($log.error($!.inspect) { $!.backtrace.inspect }; Widget_Msgbox.new("导入卡组", "导入卡组失败", :ok => "确定"))
end
end
def export
require_relative 'dialog'
file = Dialog.get_open_file("导出卡组", {"ygocore卡组 (*.ydk)" => "*.ydk", "OcgSoft卡组 (*.txt)" => "*.txt"}, [".ydk", ".txt"])
if !file.empty?
@items[index[0]]
open(@items[index[0]]) do |src|
if file =~ /.txt$/i
main = []
extra = []
side = []
now_side = false
src.readlines.each do |line|
line.chomp!
if line[0, 1] == "#"
next
elsif line[0, 5] == "!side"
now_side = true
else
card = Card.find("number = '#{line.rjust(8, '0')}'")[0]
next if card.nil?
if now_side
side << card
elsif card.extra?
extra << card
else
main << card
end
end
end
open(file, 'w:GBK') do |dest|
main.each { |card| dest.puts "[#{card.name}]##" }
dest.puts "####"
side.each { |card| dest.puts "[#{card.name}]##" }
dest.puts "===="
extra.each { |card| dest.puts "[#{card.name}]##" }
end
else
open(file, 'w') do |dest|
dest.write src.read
end
end
end
end
end
end
\ No newline at end of file
#==============================================================================
# ■ Scene_Title
#------------------------------------------------------------------------------
#  title
#==============================================================================
class Window_Field < Window
require_relative 'card'
require_relative 'window_action'
require_relative 'window_cardinfo'
Field_Pos = [[59,0], #场地魔法
[143, 84], [237,84], [331,84],[425,84], [519, 84], #后场
[143, 0], [237, 0], [331, 0],[425, 0], [519, 0]] #前场
Extra_Pos = [59,84] #额外卡组
Graveyard_Pos = [601,0] #墓地
Removed_Pos = [660,0] #除外区
Deck_Pos = [601, 84]
Hand_Pos = [0, 201, 62, 8] #手卡: x, y, width, 间距
#Card_Size = [Card::CardBack.w, Card::CardBack.h]
Card_Size = [54, 81]
attr_accessor :action_window
WLH = 16
def initialize(x, y, field,player=true)
@border = Surface.load('graphics/field/border.png')
@border_horizontal = Surface.load('graphics/field/border_horizontal.png') #@border.transform_surface(0x66000000,90,1,1,Surface::TRANSFORM_SAFE|Surface::TRANSFORM_AA)#FUCK!
super(x,y,714,282)
@field = field
@player = player
@font = TTF.open(Font, 12)
@items = {}
@cards = {}
refresh
end
def refresh
@items.clear
@cards.clear
if !@field.deck.empty?
@items[:deck] = Deck_Pos + Card_Size
@cards[:deck] = @field.deck.last
end
if !@field.extra.empty?
@items[:extra] = Extra_Pos + Card_Size
@cards[:extra] = @field.extra.first
end
if !@field.removed.empty?
@items[:removed] = Removed_Pos + Card_Size
@cards[:removed] = @field.removed.last
end
if !@field.graveyard.empty?
@items[:graveyard] = Graveyard_Pos + Card_Size
@cards[:graveyard] = @field.graveyard.last
end
@field.field.each_with_index do |card, index|
if card
if (6..10).include?(index) and card.position != :attack
@items[index] = [Field_Pos[index][0] + (Card_Size[0] - Card_Size[1])/2, Field_Pos[index][1] + (Card_Size[1] - Card_Size[0])/2, Card_Size[1], Card_Size[0]]
else
@items[index] = [Field_Pos[index][0], Field_Pos[index][1]] + Card_Size
end
@cards[index] = card
end
end
hand_width = @field.hand.size * Hand_Pos[2] + (@field.hand.size-1) * Hand_Pos[3]
hand_x = (@width - hand_width) / 2
@field.hand.each_with_index do |card, index|
@items[index+11] = [hand_x+index*Hand_Pos[2], Hand_Pos[1]]+ Card_Size
@cards[index+11] = card
end
if !@player #对手的情况,把卡片位置翻转
@items.each_pair do |key, value|
if (6..10).include?(key) and @cards[key].position != :attack
value[0] = @width - value[0] - Card_Size[1]
value[1] = @height - value[1] - Card_Size[0]
else
value[0] = @width - value[0] - Card_Size[0]
value[1] = @height - value[1] - Card_Size[1]
end
@items[key] = value
end
end
clear
@items.each_key{|index|draw_item(index, @index == index ? 1 : 0)}
refresh_action_window
end
def draw_item(index, status=0)
x,y = item_rect(index)
if (6..10).include?(index) and @cards[index].position != :attack
@contents.put(@cards[index].image_horizontal, x, y)
@contents.put(@border_horizontal, x-1, y-1) if status == 1
x += (Card_Size[1]-Card_Size[0])/2
y -= (Card_Size[1]-Card_Size[0])/2
else
@contents.put(@cards[index].image_small, x, y)
@contents.put(@border, x-1, y-1) if status == 1
end
if (6..10).include?(index) and @cards[index].position != :set
spacing = @font.text_size('/')[0]
atkdef_x = x + (Card_Size[0] - spacing) / 2
atkdef_y = y + Card_Size[1] - WLH
draw_shaded_text('/' , atkdef_x, atkdef_y)
draw_shaded_text(@cards[index].atk.to_s , atkdef_x - @font.text_size(@cards[index].atk.to_s)[0], atkdef_y)
draw_shaded_text(@cards[index].def.to_s , atkdef_x + spacing, atkdef_y)
#@font.draw_blended_utf8(@contents, '/' , atkdef_x, atkdef_y, 0xFF, 0xFF, 0xFF)
#@font.draw_blended_utf8(@contents, @cards[index].atk.to_s , atkdef_x - @font.text_size(@cards[index].atk.to_s)[0], atkdef_y, 0xFF, 0xFF, 0xFF)
#@font.draw_blended_utf8(@contents, @cards[index].def.to_s , atkdef_x + spacing, atkdef_y, 0xFF, 0xFF, 0xFF)
end
if @cards[index].note && !@cards[index].note.empty?
note_x = x
note_y = y + Card_Size[1] - WLH*3
draw_shaded_text(@cards[index].note, note_x, note_y)
end
if @cards[index].counters != 0
height ||= @font.text_size('/')[1] #不太规范,凑合能用
counters_x = x
counters_y = y + Card_Size[1] - height*2
@@counter ||= Surface.load("graphics/field/counter.png")
@contents.put @@counter, counters_x, counters_y
draw_shaded_text(" × #{@cards[index].counters}", counters_x+16, counters_y)
end
end
def draw_shaded_text(text,x,y,size=1,font=@font)
@font.draw_blended_utf8(@contents, text, x+size, y+size, 0x00, 0x00, 0x00)
@font.draw_blended_utf8(@contents, text, x, y, 0xFF, 0xFF, 0xFF)
end
def item_rect(index)
@items[index]
end
def index=(index)
index = nil if !@items.has_key?(index) or (index == :deck and @field.deck.empty?) or (index == :removed and @field.removed.empty?) or (index == :extra and @field.extra.empty?) or (index == :graveyard and @field.graveyard.empty?)
return if index == @index
if @index and @items.has_key?(@index) || (@index == :deck and !@field.deck.empty?) || (@index == :removed and !@field.removed.empty?) || (@index == :extra and !@field.extra.empty?) || (@index == :graveyard and !@field.graveyard.empty?)
clear(@items[@index][0]-1,@items[@index][1]-1,@items[@index][2]+2, @items[@index][3]+2)
draw_item(@index, 0)
end
@index = index
if @index
draw_item(@index, 1)
refresh_cardinfo_window
end
refresh_action_window
end
def refresh_cardinfo_window
@card = case @index
when :deck
@field.deck.last
when :extra
@field.extra.first
when :graveyard
@field.graveyard.last
when :removed
@field.removed.last
when 0..10
@field.field[@index]
when Integer #手卡
@field.hand[@index-11]
end
$scene.cardinfo_window.card = @card unless @index == :deck
end
def refresh_action_window
return unless @action_window
return @action_window.visible = false unless @index and @items[@index]
@action_window.items = case @index
when :deck
{"抽卡" => true,
"查看卡组" => true,
"卡组洗切" => true,
"抽卡并确认" => false,
"顶牌回卡组底" => false,
"顶牌送入墓地" => true,
"顶牌除外" => true,
"顶牌背面除外" => false,
"确认顶牌" => false,
"双方确认顶牌" => false,
"对方确认顶牌" => false
}
when :extra
{"查看" => true,
"特殊召唤" => !@field.empty_field(@card).nil?,
"效果发动" => true,
"从游戏中除外" => true,
"送入墓地" => true
}
when :removed
{"查看" => true,
"特殊召唤" => @card.monster? && !@field.empty_field(@card).nil?,
"效果发动" => true,
"加入手卡" => true,
"返回卡组" => true,
"送入墓地" => true
}
when :graveyard
{"查看" => true,
"特殊召唤" => @card.monster? && !@field.empty_field(@card).nil?,
"效果发动" => true,
"加入手卡" => true,
"返回卡组" => true,
"从游戏中除外" => true
}
when 0..5
{"效果发动" => true,
"返回卡组" => true,
"送入墓地" => true,
"从游戏中除外" => true,
"加入手卡" => true,
"盖伏" => true
}
when 6..10
{"攻击表示" => @card.position==:defense,
"守备表示" => @card.position==:attack,
"里侧表示" => @card.position!=:set,
"反转召唤" => @card.position==:set,
"打开" => @card.position==:set,
"效果发动" => true,
"攻击宣言" => @card.position==:attack,
"转移控制权" => false,
"放回卡组顶端" => true,
"送入墓地" => true,
"解放" => true,
"加入手卡" => true,
#"送入对手墓地" => false
}
when Integer #手卡
{"召唤" => @card.monster? && !@field.empty_field(@card).nil?,
"特殊召唤" => false,
"发动" => @card.spell? && !@field.empty_field(@card).nil?,
"放置到场上" => true && !@field.empty_field(@card).nil?,
"放回卡组顶端" => true,
"送入墓地" => true,
"从游戏中除外" => true,
"效果发动" => true
}
end
@action_window.x = @x + @items[@index][0] - (@action_window.width - @items[@index][2])/2
@action_window.y = @y + @items[@index][1] - @action_window.height
end
def mousemoved(x,y)
self.index = @items.each do |index, item_rect|
if x.between?(@x+item_rect[0], @x+item_rect[0]+item_rect[2]) and y.between?(@y+item_rect[1], @y+item_rect[1]+item_rect[3])
break index
end
end
end
def cursor_up
@action_window.cursor_up if @action_window
end
def cursor_down
@action_window.cursor_down if @action_window
end
def cursor_left
#self.index = @index ? (@index - 1) % [@items.size, @item_max].min : 0
end
def cursor_right
#self.index = @index ? (@index + 1) % [@items.size, @item_max].min : 0
end
def lostfocus(active_window=nil)
if active_window != @action_window
self.index = nil
end
end
def clicked
return unless @action_window && @index
action = case @index
when :deck
case @action_window.index
when 0
Action::Draw.new(true)
when 1
Widget_Msgbox.new("查看卡组", "功能未实现", :ok => "确定")
when 2
Action::Shuffle.new(true)
when 3
Widget_Msgbox.new("抽卡并确认", "功能未实现", :ok => "确定")
when 4
Widget_Msgbox.new("顶牌回卡组底", "功能未实现", :ok => "确定")
when 5
Action::SendToGraveyard.new(true, :deck, @card)
when 6
Action::Remove.new(true, :deck, @card)
when 7
Widget_Msgbox.new("顶牌背面除外", "功能未实现", :ok => "确定")
when 8
Widget_Msgbox.new("确认顶牌", "功能未实现", :ok => "确定")
when 9
Widget_Msgbox.new("双方确认顶牌", "功能未实现", :ok => "确定")
when 10
Widget_Msgbox.new("对方确认顶牌", "功能未实现", :ok => "确定")
end
when :extra
case @action_window.index
when 0
Widget_Msgbox.new("查看", "功能未实现", :ok => "确定")
when 1
if pos = @field.empty_field(@card)
Action::SpecialSummon.new(true, :extra, pos, @card, nil, :attack)
else
Widget_Msgbox.new("特殊召唤", "场位已满", :ok => "确定")
end
when 2
Action::EffectActivate.new(true, :extra, @card)
when 3
Action::Remove.new(true, :extra, @card)
when 4
Action::SendToGraveyard.new(true, :extra, @card)
end
when :removed
case @action_window.index
when 0
Widget_Msgbox.new("查看", "功能未实现", :ok => "确定")
when 1 #特殊召唤
if pos = @field.empty_field(@card)
Action::SpecialSummon.new(true, :removed, pos, @card)
else
Widget_Msgbox.new("特殊召唤", "场位已满", :ok => "确定")
end
when 2 #效果发动
Action::EffectActivate.new(true, :removed, @card)
when 3 #加入手卡
Action::ReturnToHand.new(true, :removed, @card)
when 4
Action::ReturnToDeck.new(true, :removed, @card)
when 5
Action::SendToGraveyard.new(true, :removed, @card)
end
when :graveyard
case @action_window.index
when 0
Widget_Msgbox.new("查看", "功能未实现", :ok => "确定")
when 1 #特殊召唤
if pos = @field.empty_field(@card)
Action::SpecialSummon.new(true, :graveyard, pos, @card)
else
Widget_Msgbox.new("特殊召唤", "场位已满", :ok => "确定")
end
when 2 #效果发动
Action::EffectActivate.new(true, :graveyard, @card)
when 3 #加入手卡
Action::ReturnToHand.new(true, :graveyard, @card)
when 4
Action::ReturnToDeck.new(true, :graveyard, @card)
when 5
Action::Remove.new(true, :graveyard, @card)
end
when 0..5 #后场
case @action_window.index
when 0 #效果发动
Action::EffectActivate.new(true, @index, @card)
when 1 #返回卡组
Action::ReturnToDeck.new(true, @index, @card)
when 2 #送入墓地
Action::SendToGraveyard.new(true, @index, @card)
when 3 #从游戏中除外
Action::Remove.new(true, @index, @card)
when 4 #加入手卡
Action::ReturnToHand.new(true, @index, @card)
when 5 #盖伏
Action::ChangePosition.new(true, @index, @card, :set)
end
when 6..10 #前场
case @action_window.index
when 0
Action::ChangePosition.new(true, @index, @card, :attack)
when 1
Action::ChangePosition.new(true, @index, @card, :defense)
when 2
Action::ChangePosition.new(true, @index, @card, :set)
when 3
Action::FlipSummon.new(true, @index, @card)
when 4
Action::Flip.new(true, @index, @card)
when 5
Action::EffectActivate.new(true, @index, @card)
when 6
Widget_Msgbox.new("攻击宣言", "功能未实现", :ok => "确定")
when 7
Widget_Msgbox.new("转移控制权", "功能未实现", :ok => "确定")
when 8
Action::ReturnToDeck.new(true, @index, @card)
when 9
Action::SendToGraveyard.new(true, @index, @card)
when 10
Action::Tribute.new(true, @index, @card)
when 11
Action::ReturnToHand.new(true, @index, @card)
end
when Integer #手卡
case @action_window.index
when 0 #召唤
if pos = @field.empty_field(@card)
Action::Summon.new(true, :hand, pos, @card)
else
Widget_Msgbox.new("召唤", "场位已满", :ok => "确定")
end
when 1 #特殊召唤
if pos = @field.empty_field(@card)
Action::SpecialSummon.new(true, :hand, pos, @card, nil, :attack)
else
Widget_Msgbox.new("特殊召唤", "场位已满", :ok => "确定")
end
when 2 #发动
if pos = @field.empty_field(@card)
Action::Activate.new(true, :hand, pos, @card)
else
Widget_Msgbox.new("发动", "场位已满", :ok => "确定")
end
when 3 #放置
if pos = @field.empty_field(@card)
Action::Set.new(true, :hand, pos, @card)
else
Widget_Msgbox.new("放置", "场位已满", :ok => "确定")
end
when 4 #返回卡组
Action::ReturnToDeck.new(true, :hand, @card)
when 5 #送入墓地
Action::SendToGraveyard.new(true, :hand, @card)
when 6 #从游戏中除外
Action::Remove.new(true, :hand, @card)
when 7 #效果发动
Action::EffectActivate.new(true, :hand, @card)
end
end
$scene.action action if action.is_a? Action
@index = nil
refresh
mousemoved(Mouse.state[0], Mouse.state[1])
end
def clear(x=0,y=0,width=@width,height=@height)
super
if $scene.fieldback_window and $scene.fieldback_window.visible?
Surface.blit($scene.fieldback_window.contents, @x+x-$scene.fieldback_window.x, @y+y-$scene.fieldback_window.y, width, height, @contents, x, y)
end
end
end
\ No newline at end of file
# To change this template, choose Tools | Templates
# and open the template in the editor.
class Window_FieldBack < Window
def initialize(x,y)
super(x,y,457,389,100)
end
def card=(card)
return if @card == card
@card = card
if card and File.file? file = "graphics/fields/#{card.name}.gif"
@contents = Surface.load(file).display_format
self.visible=true
else
self.visible=false
end
end
end
\ No newline at end of file
class Window_Filter < Window
attr_reader :index
def initialize(x,y)
@background = Surface.load('graphics/lobby/filter.png').display_format
super(x,y, @background.w, @background.h)
@font = TTF.open(Font, 16)
@color = [0x04, 0x47, 0x7c]
@title_color = [0xFF, 0xFF, 0xFF]
@servers = $game.servers.each_with_index.collect do |server, index|
result = Widget_Checkbox.new(self, 4+@x,@y+WLH+WLH*index,@width-8,24,true,server.name){|checked|checked ? $game.filter[:servers].push(server) : $game.filter[:servers].delete(server) ; Game_Event.push(Game_Event::AllRooms.new($game.rooms))}
result.background = @background.copy_rect(4,WLH+WLH*index,@width-8,24)
result.checked = $game.filter[:servers].include? server
result.refresh
result
end
@waiting_only = Widget_Checkbox.new(self, 4+@x,@y+WLH*7-4,@width-8,24,true, I18n.t('lobby.waiting_only')){|checked|$game.filter[:waiting_only] = checked; Game_Event.push(Game_Event::AllRooms.new($game.rooms))}
@waiting_only.background = @background.copy_rect(4,WLH*7-4,@width-8,24)
@waiting_only.checked = false
@waiting_only.refresh
@normal_only = Widget_Checkbox.new(self, 4+@x,@y+WLH*7+WLH-4,120,24,true,I18n.t('lobby.normal_only')){|checked|$game.filter[:normal_only] = checked; Game_Event.push(Game_Event::AllRooms.new($game.rooms))}
@normal_only.background = @background.copy_rect(4,WLH*7+WLH-4,120,24)
@normal_only.checked = false
@normal_only.refresh
refresh
end
def refresh
clear
@font.draw_blended_utf8(@contents, "服务器", 4, 4, *@color)
@contents.fill_rect(4,WLH,@contents.w-8, 2, 0xAA0A7AC5)
@font.draw_blended_utf8(@contents, "房间属性", 4, WLH*6+4-4, *@color)
@contents.fill_rect(4,WLH*7-4,@contents.w-8, 2, 0xAA0A7AC5)
end
def destroy
@servers.each{|server|server.destroy}
@normal_only.destroy
@waiting_only.destroy
super
end
end
\ No newline at end of file
class Window_GameSelect < Window_List
WLH = 56
def initialize(x,y)
@font = TTF.open(Font, 24)
@color = [255,255,255]
@game_color = [47,156,192]
@game_stroke_color = [0xFF,0xFF,0xFF]
@items = []
Dir.glob('lib/**/game.yml') do |file|
game = YAML.load_file(file)
if game.is_a?(Hash) && game["name"]
game['file'] ||= 'game.rb'
game['file'] = File.expand_path(game['file'], File.dirname(file))
$config[game['name']] ||= {}
@items << game
else
$log.error "#{game.inspect}读取失败(#{file})"
end
end
super(x,y,160,@items.size*WLH)
clear
@button = Surface.load("graphics/login/game_background.png")
#@button.set_alpha(RLEACCEL,255)
self.items = @items
self.index = @items.find_index{|game|game["name"] == $config['game']} || 0
clicked
#======================================================
# We'll pay fpr that soon or later.
#======================================================
if $config['screen']['height'] == 768
@announcements_window = Window_Announcements.new(313,265,600,24)
elsif $config['screen']['height'] == 640
@announcements_window = Window_Announcements.new(313,130,600,24)
else
raise "无法分辨的分辨率"
end
#======================================================
# ENDS HERE
#======================================================
refresh
end
def draw_item(index, status=0)
Surface.blit(@button, @button.w/3*status, @game == index ? @button.h/2 : 0, @button.w/3, @button.h/2, @contents, 0, WLH*index)
draw_stroked_text(@items[index]["name"], 24, WLH*index+14, 2)
end
def item_rect(index)
return [0,0,0,0] unless index
[0, WLH*index, @button.w/3, @button.h/2]
end
def draw_stroked_text(text,x,y,size=1)
[[x-size,y-size], [x-size,y], [x-size,y+size],
[x,y-size], [x,y+size],
[x+size,y-size], [x+size,y], [x+size,y+size],
].each{|pos|@font.draw_blended_utf8(@contents, text, pos[0], pos[1], *@game_stroke_color)}
@font.draw_blended_utf8(@contents, text, x, y, *@game_color)
end
def mousemoved(x,y)
self.index = (y-@y) / WLH
end
def index=(index)
return if @index == index# or index.nil?
if @index
clear(*item_rect(@index))
draw_item(@index, 0)
end
@index = index
clear(*item_rect(@index))
draw_item(@index, 1) if @index
end
def clicked
return unless @index
load @items[@index]["file"].encode("GBK") #TODO: load的这种架构微蛋疼,一时想不到更好的方案
$config['game'] = @items[@index]['name']
@login_window.destroy if @login_window
#======================================================
# We'll pay fpr that soon or later.
#======================================================
if $config['screen']['height'] == 768
@login_window = Window_Login.new(316,316,$config[$config['game']]["username"],$config[$config['game']]["password"])
elsif $config['screen']['height'] == 640
@login_window = Window_Login.new(316,183,$config[$config['game']]["username"],$config[$config['game']]["password"])
else
raise "无法分辨的分辨率"
end
#======================================================
# ENDS HERE
#======================================================
@announcements_window.refresh if @announcements_window
@game = @index
refresh
end
def update
@login_window.update if @login_window
@announcements_window.update if @announcements_window
end
#def lostfocus
# self.index = nil
#end
#def destroy
# @login_window.destroy if @login_window
# super
#end
end
class Window_Host < Window
attr_reader :index
def initialize(x,y)
@button = Surface.load("graphics/system/button.png")
@items = {:ok => [20,240,@button.w/3,@button.h], :cancel => [110,240,@button.w/3, @button.h], :share => [200,240,@button.w/3, @button.h]}
@buttons = {:ok => "确定", :cancel => "取消", :share => "约战"}
@background = Surface.load('graphics/lobby/host.png').display_format
super((1024-@background.w)/2, 230, @background.w, @background.h)
@font = TTF.open(Font, 16)
@title_color = [0xFF, 0xFF, 0xFF]
@color = [0x04, 0x47, 0x7c]
@roomname_inputbox = Widget_InputBox.new(@x+96, @y+41, 165, WLH)
@password_inputbox = Widget_InputBox.new(@x+96, @y+41+WLH, 165, WLH)
@lp_inputbox = Widget_InputBox.new(@x+96, @y+41+WLH*5+4, 64, WLH)
@match = Widget_Checkbox.new(self, 33+@x,@y+41+WLH*2,120,24,true,"三回决斗") {|checked|@tag.checked = false if checked}
@match.background = @background.copy_rect(33,70,120,24)
@tag = Widget_Checkbox.new(self, 150+@x,@y+41+WLH*2,120,24,false,"TAG双打") {|checked|@match.checked = false if checked}
@tag.background = @background.copy_rect(150,70,120,24)
@ocg = Widget_Checkbox.new(self, 33+@x,@y+41+WLH*4+4,120,24,true,"OCG"){|checked|@tcg.checked = true if !checked}
@ocg.background = @background.copy_rect(120,70,120,24)
@tcg = Widget_Checkbox.new(self, 120+@x,@y+41+WLH*4+4,120,24,false,"TCG"){|checked|@ocg.checked = true if !checked}
@tcg.background = @background.copy_rect(120,70,120,24)
@roomname_inputbox.value = rand(1000).to_s
@lp_inputbox.value = 8000.to_s
@password_inputbox.refresh
@match.refresh
@tag.refresh
@ocg.refresh
@tcg.refresh
refresh
end
def refresh
clear
@font.draw_blended_utf8(@contents, "建立房间", (@width-@font.text_size("建立房间")[0])/2, 2, *@title_color)
@font.draw_blended_utf8(@contents, "房间名", 33,43, *@color)
@font.draw_blended_utf8(@contents, "房间密码", 33,43+WLH, *@color)
@contents.fill_rect(4,43+WLH*3,@contents.w-8, 2, 0xAA0A7AC5)
@font.draw_blended_utf8(@contents, "卡片允许", 20,43+WLH*3+4, *@color)
@font.draw_blended_utf8(@contents, "初始LP", 20,44+WLH*5+4, *@color)
@font.draw_blended_utf8(@contents, "约战时建议设置房间密码以防乱入", 20,44+WLH*6+4, *@color)
@items.each_key do |index|
draw_item(index, self.index==index ? 1 : 0)
end
end
def draw_item(index, status=0)
Surface.blit(@button,@button.w/3*status,0,@button.w/3,@button.h,@contents,@items[index][0],@items[index][1])
text_size = @font.text_size(@buttons[index])
@font.draw_blended_utf8(@contents, @buttons[index], @items[index][0]+(@button.w/3-text_size[0])/2, @items[index][1]+(@button.h-text_size[1])/2, 0xFF, 0xFF, 0xFF)
end
def mousemoved(x,y)
new_index = nil
@items.each_key do |index|
if (x - @x).between?(@items[index][0], @items[index][0]+@items[index][2]) and (y-@y).between?(@items[index][1], @items[index][1]+@items[index][3])
new_index = index
break
end
end
self.index = new_index
end
def item_rect(index)
@items[index]
end
def index=(index)
return if index == @index
if @index
clear(*item_rect(@index))
draw_item(@index, 0)
end
if index.nil? or !@items.include? index
@index = nil
else
@index = index
draw_item(@index, 1)
end
end
def clicked
case self.index
when :ok, :share
if @roomname_inputbox.value.empty?
Widget_Msgbox.new("建立房间", "请输入房间名", ok: "确定" )
elsif !name_check
Widget_Msgbox.new("建立房间", "房间名/房间密码超过长度上限", ok: "确定")
elsif @lp_inputbox.value.to_i >= 99999
Widget_Msgbox.new("建立房间", "初始LP超过上限", ok: "确定")
else
Widget_Msgbox.new("建立房间", "正在建立房间")
destroy
room = $game.host(@roomname_inputbox.value, password: @password_inputbox.value, match: @match.checked?, tag: @tag.checked?, ot: @tcg.checked? ? @ocg.checked? ? 2 : 1 : 0, lp: @lp_inputbox.value.to_i)
if room and self.index == :share
require 'uri'
room_name = if room.ot != 0 or room.lp != 8000
mode = case when room.match? then
1; when room.tag? then
2
else
0
end
room_name = "#{room.ot}#{mode}FFF#{room.lp},5,1,#{room.name}"
elsif room.tag?
"T#" + room.name
elsif room.pvp? and room.match?
"PM#" + room.name
elsif room.pvp?
"P#" + room.name
elsif room.match?
"M#" + room.name
else
room.name
end
if room.password and !room.password.empty?
room_name += "$" + room.password
end
Dialog.web("http://my-card.in/rooms/#{room.server.ip}:#{room.server.port}/#{URI.escape(room_name)}#{'?server_auth=true' if room.server.auth}#share")
end
end
when :cancel
destroy
end
end
def destroy
@roomname_inputbox.destroy
@password_inputbox.destroy
@lp_inputbox.destroy
@match.destroy
@tag.destroy
@ocg.destroy
@tcg.destroy
super
end
def update
@roomname_inputbox.update
@password_inputbox.update
@lp_inputbox.update
end
def name_check
name = @roomname_inputbox.value
# P#/PM#/M#/T# 的总房名长度允许为13
# 其他情况下如果全英文,那么上限19,否则上限20
# TCG代码自定义房占15个字符
# 一个汉字两个字符,密码算在内
if @tcg.checked #代码自定义房
max = 5
max -= 1 if name.ascii_only?
elsif @match.checked or @tag.checked
# 去掉那个#占用的
max = 12
max -= 1 if @match.checked
max -= 1 if @tag.checked
else
max = 20
max -= 1 if name.ascii_only?
end
max -= @lp_inputbox.value.size - 4
if !@password_inputbox.value.empty?
max -= 1
max -= @password_inputbox.value.encode("GBK").bytesize
end
max -= name.encode("GBK").bytesize
return max >= 0
end
end
\ No newline at end of file
class Window_Join < Window
attr_reader :index
def initialize(x,y,room)
@room = room
@button = Surface.load("graphics/system/button.png")
@items = {:ok => [46,110,@button.w/3,@button.h], :cancel => [156,110,@button.w/3, @button.h]}
@buttons = {:ok => "确定", :cancel => "取消"}
@background = Surface.load('graphics/system/msgbox.png').display_format
super((1024-@background.w)/2, 230, @background.w, @background.h)
@font = TTF.open(Font, 16)
@title_color = [0xFF, 0xFF, 0xFF]
@color = [0x04, 0x47, 0x7c]
@password_inputbox = Widget_InputBox.new(@x+96, @y+41, 165, WLH) do |key|
case key
when :ENTER
clicked
false
when :ESC
true
end
end
@password_inputbox.refresh
refresh
end
def refresh
clear
@font.draw_blended_utf8(@contents, "加入私密房间", (@width-@font.text_size("加入私密房间")[0])/2, 2, *@title_color)
@font.draw_blended_utf8(@contents, "房间密码", 33,43, *@color)
@items.each_key do |index|
draw_item(index, self.index==index ? 1 : 0)
end
@font.draw_blended_utf8(@contents, "如果确定后创建了新房间,是密码错误", 20,43+WLH, *@color)
end
def draw_item(index, status=0)
Surface.blit(@button,@button.w/3*status,0,@button.w/3,@button.h,@contents,@items[index][0],@items[index][1])
text_size = @font.text_size(@buttons[index])
@font.draw_blended_utf8(@contents, @buttons[index], @items[index][0]+(@button.w/3-text_size[0])/2, @items[index][1]+(@button.h-text_size[1])/2, 0xFF, 0xFF, 0xFF)
end
def mousemoved(x,y)
new_index = nil
@items.each_key do |index|
if (x - @x).between?(@items[index][0], @items[index][0]+@items[index][2]) and (y-@y).between?(@items[index][1], @items[index][1]+@items[index][3])
new_index = index
break
end
end
self.index = new_index
end
def item_rect(index)
@items[index]
end
def index=(index)
return if index == @index
if @index
clear(*item_rect(@index))
draw_item(@index, 0)
end
if index.nil? or !@items.include? index
@index = nil
else
@index = index
draw_item(@index, 1)
end
end
def clicked
case self.index
when :ok
return if @password_inputbox.value.empty?
@joinroom_msgbox = Widget_Msgbox.new("加入", "正在加入房间")
destroy
@room.password = @password_inputbox.value
$game.join(@room)
when :cancel
destroy
end
end
def destroy
@password_inputbox.destroy
super
end
def update
@password_inputbox.update
end
end
\ No newline at end of file
#==============================================================================
# ■ Window_Roomitems
#------------------------------------------------------------------------------
#  大厅内房间列表
#==============================================================================
require_relative 'window'
class Window_List < Window
attr_reader :items
attr_reader :index
def initialize(x, y, width, height, z=200)
@items ||= []
@index ||= nil
super(x,y,width, height,z)
end
def index=(index)
index = nil unless index_legal?(index)
return if index == @index
if @index
clear(*item_rect(@index))
draw_item(@index, 0) if index_legal?(@index)
end
if index.nil?
@index = nil
else
@index = index
draw_item(@index, 1)
end
end
def draw_item(index, status=0)
#子类定义
end
def item_rect(index)
[0, index*self.class::WLH, @width, self.class::WLH]
end
def items=(items)
@items = items
refresh
end
def refresh
clear
@items.each_index {|index|draw_item(index, index==@index ? 1 : 0)}
end
def cursor_up(wrap=false)
return unless wrap or @index.nil? or @index > 0
self.index = @index ? (@index - 1) % @items.size : 0
end
def cursor_down(wrap=false)
return unless wrap or @index.nil? or @index < @items.size-1
self.index = @index ? (@index + 1) % @items.size : 0
end
def cursor_left(wrap=false)
cursor_up(wrap)
end
def cursor_right(wrap=false)
cursor_down(wrap)
end
def mousemoved(x,y)
#子类定义
#return unless include?(x,y)
#self.index = (y - @y) / @single_height
end
def lostfocus(active_window=nil)
self.index = nil
end
def clicked
#子类定义
end
def index_legal?(index)
index.nil? or (index >= 0 and index < @items.size)
end
end
require_relative 'window_host'
class Window_LobbyButtons < Window_List
def initialize(x, y)
@items = [I18n.t('lobby.forum'), 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(Font, 15)
refresh
end
def draw_item(index, status=0)
x, y, width=item_rect(index)
Surface.blit(@button, status*@button.w/3, 0, @button.w/3, @button.h, @contents, x, y)
draw_stroked_text(@items[index], x+center_margin(@items[index], width, @font), y+3, 2, @font, [0xdf, 0xf1, 0xff], [0x27, 0x43, 0x59])
end
def item_rect(index)
[index*@button.w/3+(index)*4, 0, @button.w/3, @height]
end
def mousemoved(x, y)
if (x-@x) % (@button.w/3+4) >= @button.w/3
self.index = nil
else
self.index = (x-@x)/(@button.w/3+4)
end
end
def lostfocus(active_window = nil)
self.index = nil
end
def clicked
case @index
when 0 #常见问题
require_relative 'dialog'
Dialog.web "https://forum.my-card.in/"
when 1 #房间筛选
if @filter_window and !@filter_window.destroyed?
@filter_window.destroy
else
@filter_window = Window_Filter.new(678, 44)
end
when 2 #卡组编辑
require_relative 'deck'
$game.class.deck_edit
when 3 #建立房间
@host_window = Window_Host.new(300, 200)
when 4 #自动匹配
return if @waiting
@waiting = true
waiting_window = Widget_Msgbox.new("自动匹配", "正在等待对手")
require 'uri'
require 'net/http'
require 'net/https'
Thread.new {
begin
uri = URI.parse("https://my-card.in/match")
http = Net::HTTP.new(uri.host,uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
req = Net::HTTP::Post.new(uri.path)
req.basic_auth $game.username, $game.password
res = http.request(req)
@waiting = false
if res.body =~ /^mycard:\/\/([\d\.]+):(\d+)\/(.*?)(\?server_auth=true)?$/
room = Room.new(nil, $3.to_s)
room.server = Server.new(nil, nil, $1, $2.to_i, !!$4)
$game.join(room)
else
$log.error('自动匹配非法回复'){res.body}
Widget_Msgbox.new("自动匹配", "错误: #{res.body}", ok: "确定")
end
rescue Exception => exception
@waiting = false
$log.error('自动匹配出错'){exception.to_s.encode('UTF-8')}
Widget_Msgbox.new("自动匹配", "匹配失败: #{exception.to_s.encode('UTF-8')}", ok: "确定")
end
}
end
end
def update
@host_window.update if @host_window and !@host_window.destroyed?
end
end
require_relative 'widget_inputbox'
require_relative 'widget_msgbox'
require_relative 'widget_checkbox'
class Window_Login < Window
def initialize(x,y,username=nil, password=nil)
@username = username
@password = password
@button = Surface.load("graphics/login/button.png")
super(x,y,597,338)
@username_inputbox = Widget_InputBox.new(@x+192, @y+80, 165, WLH) do |key|
case key
when :TAB
@password_inputbox.clicked
false
when :ESC
true
end
end
@username && !@username.empty? ? @username_inputbox.value = @username : @username_inputbox.refresh
@password_inputbox = Widget_InputBox.new(@x+192, @y+125, 165, WLH) do |key|
case key
when :TAB
self.index=:login
false
when :ENTER
self.index=:login
self.clicked
false
when :ESC
true
end
end
@password_inputbox.type = :password
@password && !@password.empty? ? @password_inputbox.value = @password : @password_inputbox.refresh
@color = [255,255,255]
@color_stroke = [0,0,0]
@font = TTF.open(Font, 16)
@font_button = TTF.open(Font, 18)
#@font.draw_blended_utf8(@contents, text, 105,80, *@game_color)
@items = {
#:username => [192,80,165,WLH],
#:password => [192,125,165,WLH],
:login => [192,200,@button.w/3,@button.h],
:register => [285,200,@button.w/3,@button.h],
:replay => [378,200,@button.w/3,@button.h]
}
@items_text = {
:login => I18n.t("login.login"),
:register => I18n.t("login.register"),
:replay => I18n.t("login.replay"),
}
#self.index = nil
@remember_password = Widget_Checkbox.new(self, 357+@x,80+@y,self.width-357,24,password,I18n.t('login.remember'))
refresh
end
def refresh
clear
@items.each_pair{|index, rect|draw_item(index, rect)}
draw_stroked_text(I18n.t('login.name'), 105,80+2,1)
draw_stroked_text(I18n.t('login.password'), 105,125+2,1)
end
def draw_item(index, rect, status=0)
Surface.blit(@button,rect[2]*status,0,rect[2],rect[3],@contents,rect[0],rect[1])
draw_stroked_text(@items_text[index], rect[0] + center_margin(@items_text[index], rect[2], @font_button), rect[1]+9,1,@font_button)
end
def mousemoved(x,y)
self.index = @items.each_pair{|index, rect|break index if (x-@x >= rect[0] and x-@x < rect[0]+rect[2] and y-@y >= rect[1] and y-@y < rect[1]+rect[3])}
end
def lostfocus(active_window=nil)
self.index = nil
end
def item_rect(index)
@items[index]
end
def index=(index)
index = nil if !@items.has_key?(index)
return if @index == index
if @index
clear(*item_rect(@index))
draw_item(@index, item_rect(@index), 0)
end
@index = index
if @index
clear(*item_rect(@index))
draw_item(@index, item_rect(@index), 1)
end
end
def update
@username_inputbox.update
@password_inputbox.update
end
#def destroy
# @username_inputbox.destroy
# @password_inputbox.destroy
# super
#end
end
class Window_LP < Window
Avatar_Size = 48
def initialize(x,y,player,position=true) #true:左 false:右
super(x,y,355,48)
@position = position
@font = TTF.open(Font, 20)
@color = [255,255,255]
self.player = player
end
def player=(player)
return if @player == player
clear
@player = player
if @player
@player.avatar do |avatar|
clear(@position ? 0 : @width-Avatar_Size, 0, Avatar_Size, Avatar_Size)
@contents.put avatar, @position ? 0 : @width-Avatar_Size, 0
end
if @position
@font.draw_blended_utf8(@contents, @player.name, Avatar_Size, 24, *@color)
else
@font.draw_blended_utf8(@contents, @player.name, @width-Avatar_Size-96, 24, *@color)
end
end
self.lp = 8000
end
def lp=(lp)
return if lp == @lp
@lp = lp
width = [0, [(200*lp/8000), 200].min].max
if @position
clear(64,0,200, WLH)
@contents.fill_rect(48,0,width, WLH, 0xFFFF0000)
@font.draw_blended_utf8(@contents, @lp.to_s, 56, 0, *@color)
else
clear(@width-200-64,0,200 , WLH)
@contents.fill_rect(@width-width-48,0,width , WLH, 0xFFFF0000)
@font.draw_blended_utf8(@contents, @lp.to_s, 240, 0, *@color)
end
end
end
\ No newline at end of file
class Window_Phases < Window_List
WLH = 81 #其实是列宽
def initialize(x,y)
@phases_player = Surface.load('graphics/system/phases_player.png')
@phases_player.set_alpha(RLEACCEL,255)
@phases_opponent = Surface.load('graphics/system/phases_opponent.png')
@phases_opponent.set_alpha(RLEACCEL,255)
super(x,y,5*WLH+@phases_player.w/3, @phases_player.h/6)
@items = [:DP, :SP, :M1, :BP, :M2, :EP]
self.player = true
end
def player=(player)
return if player == (@phases == @phases_player)
@phases = player ? @phases_player : @phases_opponent
@phase = 0
refresh
end
def phase=(phase)
phase = @items.index(phase) unless (0..5).include? phase
return if phase == @phase
@index = @phase
@phase = phase
self.index = @phase
end
def draw_item(index, status=0)
status = 2 if index == @phase
Surface.blit(@phases, status*@phases.w/3, index*@height, @phases.w/3, @height, @contents, index*WLH, 0)
end
def item_rect(index)
[WLH*index, 0, @phases.w/3, @height]
end
def mousemoved(x,y)
self.index = include?(x,y) ? (x - @x) / WLH : nil
end
def clear(x=0,y=0,width=@width,height=@height)
@contents.fill_rect(x,y,width, height, 0x00000000)
end
def clicked
$scene.change_phase(@items[@index])
end
end
\ No newline at end of file
#encoding: UTF-8
#==============================================================================
# ■ Window_Roomitems
#------------------------------------------------------------------------------
#  大厅内房间列表
#==============================================================================
require_relative 'window_scrollable'
require_relative 'window_join'
class Window_RoomList < Window_Scrollable
attr_reader :items
WLH = 48
def initialize(x, y, items)
@button = Surface.load('graphics/lobby/room.png')
@button.set_alpha(RLEACCEL, 255)
#@background = Surface.load 'graphics/lobby/roomitems.png'
#@contents = Surface.load 'graphics/lobby/roomitems.png'
super(x,y,@button.w / 3, ($config['screen']['height'] - 288) / 48 * 48)
@item_max = 0
@font = TTF.open(Font, 16)
@color = [0x03, 0x11, 0x22]
@scrollbar = Widget_ScrollBar.new(self,@x+@width,@y,@height)
self.items = items
end
def draw_item(index, status=0)
y = item_rect(index)[1]
room = @items[index]
Surface.blit(@button, @width*status, room.full? ? WLH : 0, @width, WLH, @contents, 0, y)
@font.draw_blended_utf8(@contents, room.id.to_s, 24, y+8, *@color) unless room.id.to_s.empty?
@font.draw_blended_utf8(@contents, room.full? ? "【决斗中】" : room.private? ? "【私密房】" : "【等待中】", 8, y+24, *@color)
@font.draw_blended_utf8(@contents, room.name, 128, y+8, *room.color) unless room.name.nil? or room.name.empty? or room.name.size > 100
@font.draw_blended_utf8(@contents, room.player1.name, 128, y+24, *room.player1.color) if room.player1 and !room.player1.name.empty?
@font.draw_blended_utf8(@contents, room.player2.name, 320, y+24, *room.player2.color) if room.player2 and !room.player2.name.empty?
room.extra.each_with_index do |extra, index|
str, color = extra
@font.draw_blended_utf8(@contents, str, 300+index*96, y+8, *color)
end
end
def update
@join_window.update if @join_window and !@join_window.destroyed?
end
def mousemoved(x,y)
return unless self.include?(x,y)
self.index = (y - @y) / WLH + @scroll
end
def clicked
return unless @index and room = @items[@index]
if room.full?
@joinroom_msgbox = Widget_Msgbox.new("加入房间", "正在加入观战")
$game.watch room
else
if room.private
@join_window = Window_Join.new(0,0,room)
else
@joinroom_msgbox = Widget_Msgbox.new("加入房间", "正在加入房间")
$game.join room
end
end
end
end
\ No newline at end of file
require_relative 'window_list'
class Window_Scrollable < Window_List
attr_reader :scroll
attr_accessor :scrollbar
def initialize(x, y, width, height, z=200)
super(x, y, width, height, z)
@page_size ||= @height / self.class::WLH
@scroll ||= 0
end
def cursor_up(wrap=false)
return unless wrap or @index.nil? or @index > 0
self.index = @index ? (@index - @scroll - 1) % @items.size + @scroll : @scroll
end
def cursor_down(wrap=false)
return unless wrap or @index.nil? or @index < @items.size-1
self.index = @index ? (@index - @scroll + 1) % @items.size + @scroll : @scroll
end
def scroll_up
cursor_up(false)
self.scroll -= 1
end
def scroll_down
cursor_down(false)
self.scroll += 1
end
def scroll=(scroll)
return unless scroll != @scroll and scroll and scroll >= 0 and scroll <= @items.size - @page_size
#有背景的不能这么用....
#if scroll > @scroll
# Surface.blit(@contents, 0, self.class::WLH * (scroll - @scroll), @width, (@page_size - (scroll - @scroll)) * self.class::WLH, @contents, 0, 0)
# clear(0, @page_size - (scroll - @scroll) * self.class::WLH, @width, self.class::WLH * (scroll - @scroll))
#else
# Surface.blit(@contents, 0, 0, @width, (@page_size - (scroll - @scroll)) * self.class::WLH, @contents, 0, self.class::WLH * (scroll - @scroll))
# clear(0, 0, @width, self.class::WLH * (scroll - @scroll))
#end
@scroll = scroll
refresh
end
def refresh
clear
(@scroll...[(@scroll+@page_size), @items.size].min).each{|index|draw_item(index, @index == index ? 1 : 0)}
if @scrollbar
@scrollbar.scroll_max = [@items.size - @page_size, 0].max
@scrollbar.scroll = @scroll
end
end
def item_rect(index)
[0, (index-@scroll)*self.class::WLH, @width, self.class::WLH]
end
end
require_relative 'window_list'
class Window_Title < Window_List
Button_Count = 5
WLH = 50
attr_reader :x, :y, :width, :height, :single_height, :index
def initialize(x,y,img="graphics/system/titlebuttons.png")
@button = Surface.load(img)
@button.set_alpha(RLEACCEL,255)
@single_height = @button.h / Button_Count
super(x,y,@button.w / 3,WLH * Button_Count - (WLH - @button.h / Button_Count))
@cursor_se = (Mixer::Wave.load 'audio/se/cursor.ogg' if SDL.inited_system(INIT_AUDIO) != 0) rescue nil
self.items = [:决斗开始, :单人模式, :卡组编成, :选项设置, :退出游戏]
end
def index=(index)
if index and @index != index
Mixer.play_channel(-1,@cursor_se,0) if @cursor_se
end
super
end
def mousemoved(x,y)
self.index = (y - @y) / WLH
end
def draw_item(index, status=0)
Surface.blit(@button, @width*status, @single_height*index, @width, @single_height, @contents, 0, WLH*index)
end
def clicked
$scene.determine if $scene.is_a? Scene_Title
end
def clear(x=0,y=0,width=@width,height=@height)
@contents.fill_rect(x,y,width, height, 0x00000000)
end
end
\ No newline at end of file
class Window_User < Window_List
WLH = 20
def initialize(x, y, user)
@background = Surface.load("graphics/lobby/user.png").display_format
super(x,y,@background.w,@background.h, 300)
@font = TTF.open(Font, 16)
@user = user
@contents = Surface.load("graphics/lobby/user.png").display_format #TODO:调用已经加载了的背景
@close = Surface.load("graphics/lobby/userclose.png")
@avatar_boarder = Surface.load("graphics/lobby/avatar_boader.png")
@items = ["发送消息", "查看资料"]
@items << "加入游戏" if user.status == :waiting
@items << "观战" if user.status == :dueling
@item_max = @items.size
refresh
end
def refresh
@thread.kill if @thread
super
@thread = @user.avatar(:middle) do |avatar|
clear(12,12,@avatar_boarder.w, @avatar_boarder.h)
@contents.put(avatar, 24, 24)
@contents.put(@avatar_boarder, 12, 12)
end
@font.draw_blended_utf8(@contents, @user.name, 172, 24, 0x00,0x00,0x00)
@font.draw_blended_utf8(@contents, "id: #{@user.id}" , 172, 32+WLH, 0x00,0x00,0x00)
@font.draw_blended_utf8(@contents, "#{'房间' + @user.room.id.to_s + '' if @user.room}#{case @user.status;when :lobby;'大厅';when :dueling;'决斗中';when :waiting;'等待中';end}", 172, 32+WLH*2, 0x00,0x00,0x00)
Surface.blit(@close, 0, 0, @close.w/3, @close.h, @contents, @width-24,10)
end
def clear(x=0,y=0,width=@width,height=@height)
Surface.blit(@background, x,y,width,height,@contents,x,y)
end
def draw_item(index, status=0)
@font.draw_blended_utf8(@contents, @items[index] , 172, 96+index*WLH, 0x00,0x00,0x00)
end
def item_rect(index)
[172, 96+index*WLH, 128, WLH]
end
def clicked
case index
when 0
$scene.chat_window.channel = @user
when 1
@user.space
when 2
if @user.status == :waiting
$game.join(@user.room)
elsif @user.status == :dueling
$game.watch(@user.room)
end
end
destroy
end
def mousemoved(x,y)
if x.between?(@x+172, @x+@width) and y.between?(@y+96, @y+96+@item_max*WLH)
self.index = (y - @y - 96) / WLH
else
self.index = nil
end
end
def dispose
@thread.exit
super
end
end
\ No newline at end of file
#encoding: UTF-8
#==============================================================================
# Window_UserInfo
#------------------------------------------------------------------------------
# 游戏大厅显示用户信息的类
#==============================================================================
class Window_UserInfo < Window
def initialize(x, y, user)
@avatar_boarder = Surface.load("graphics/lobby/avatar_boader.png")
super(x,y,280,144)
@font = TTF.open(Font, 16)
@user = user
@background = Surface.load("graphics/lobby/userinfo.png").display_format
refresh
require 'open-uri'
Thread.new{
loop {
open('https://my-card.in/match_count'){|f|self.match = f.read.to_i} rescue self.match = "ERROR"
sleep 60
}
}
end
def refresh
@contents.put(@background, 0, 0)
@thread = @user.avatar(:middle) do |avatar|
clear(0,0,@avatar_boarder.w, @avatar_boarder.h)
@contents.put(avatar, 12, 12)
@contents.put(@avatar_boarder, 0, 0)
end
@font.draw_blended_utf8(@contents, @user.name, 160, 12, 0x00,0x00,0x00) unless @user.name.empty?
#@font.draw_blended_utf8(@contents, @user.id.to_s , 160, 12+16*2, 0x00,0x00,0x00) unless @user.id.to_s.empty?
#@font.draw_blended_utf8(@contents, "Lv: #{@user.level}" , 160, 12+16*3, 0x00,0x00,0x00) if @user.respond_to? :level and @user.level #TODO:规范化,level是iduel专属的,但是又不太想让iduel来重定义这个window
#@font.draw_blended_utf8(@contents, "经验: #{@user.exp}", 160, 12+16*4, 0x00,0x00,0x00) if @user.respond_to? :exp and @user.exp
end
def match=(count)
clear(160, 12+16*5, 120, 16)
@font.draw_blended_utf8(@contents, "匹配:#{count}", 160, 12+16*5, 0x00,0x00,0x00)
end
def rooms=(count)
clear(160, 12+16*6, 120, 16)
@font.draw_blended_utf8(@contents, "房间:#{count}", 160, 12+16*6, 0x00,0x00,0x00)
end
def users=(count)
clear(160, 12+16*7, 120, 16)
@font.draw_blended_utf8(@contents, "在线人数:#{count}", 160, 12+16*7, 0x00,0x00,0x00)
end
def dispose
@thread.exit
super
end
end
#==============================================================================
# ■ Scene_Title
#------------------------------------------------------------------------------
#  title
#==============================================================================
require_relative 'window_user'
require_relative 'window_scrollable'
class Window_UserList < Window_Scrollable
attr_reader :x, :y, :width, :height
WLH = 20
def initialize(x, y, items)
#@contents = Surface.load "graphics/lobby/useritems.png"
#@background = Surface.load "graphics/lobby/useritems.png"
super(x,y,272,$config['screen']['height'] - 220)
@font = TTF.open(Font, 16)
@color = [0x03, 0x11, 0x22]
@color_friend = [0, 128, 0]
@color_over = [200,200,255]
@color_click = [0x03, 0x11, 0x22]
#@contents.set_alpha(RLEACCEL, 80)
@contents.fill_rect(0,0,@width,@height,0xFFFFFFFF)
self.items = items
end
def draw_item(index, status=0)
case status
when 0
@font.draw_blended_utf8(@contents, @items[index].name, 0, item_rect(index)[1], *@items[index].color)
when 1
@font.draw_shaded_utf8(@contents, @items[index].name, 0, item_rect(index)[1], *(@items[index].color+@color_over))
when 2
@font.draw_shaded_utf8(@contents, @items[index].name, 0, item_rect(index)[1], *(@items[index].color+@color_click))
end
end
def clicked
return unless @index
@userwindow = Window_User.new(100,100,@items[@index])
end
def mousemoved(x,y)
return unless include?(x,y)
self.index = (y - @y) / WLH + @scroll
end
end
\ No newline at end of file
class Game_Event
def self.parse(header, data)
case header
#when :login
# if data
# Login.new parse_user data
# else
# Error.new('登录', '用户名或密码错误')
# end
#when :rooms
#AllRooms.new data.collect{|room|parse_room(room)}
#when :rooms_update
#RoomsUpdate.new data.collect{|room|parse_room(room)}
#when :servers
# servers = data.collect{|server|parse_server(server)}
# $game.filter[:servers].concat (servers - $game.servers)
# AllServers.new servers
#when :newuser
#NewUser.new parse_user data
#when :missinguser
#MissingUser.new parse_user data
#when :newroom
# NewRoom.new parse_room data
#when :missingroom
# MissingRoom.new parse_room data
when :chat
case data[:channel]
when :lobby
Chat.new ChatMessage.new User.new(data[:from][:id],data[:from][:name]), data[:message], :lobby
else
Chat.new ChatMessage.new User.new(data[:from][:id],data[:from][:name]), data[:message], User.new(data[:channel])
end
end
end
def self.parse_room(room)
result = Room.new(room['id'], room['name'])
result.private = room['private']
result.pvp = room['pvp']
result.match = room['mode'] == 1
result.tag = room['mode'] == 2
result.ot = room['rule'] || 0
result.status = room['status'].to_sym
result.lp = room['start_lp'] || 8000
result.player1 = room['users'][0] && parse_user(room['users'][0])
result.player2 = room['users'][1] && parse_user(room['users'][1])
result.server = Server.find room['server_id']
result._deleted = room['_deleted']
result
end
def self.parse_user(user)
User.new(user['id'] || user[:id], user['name'] || user[:name], user['certified'] || user[:certified])
end
def self.parse_server(server)
Server.new(server[:id], server[:name], server[:ip], server[:port], server[:auth])
end
end
#encoding: UTF-8
load 'lib/ygocore/window_login.rb'
require 'eventmachine'
require 'em-http'
require 'websocket'
require 'open-uri'
require 'yaml'
require 'json'
require 'date'
class Ygocore < Game
attr_reader :username
attr_accessor :password
@@config = YAML.load_file("lib/ygocore/server.yml")
def initialize
super
load 'lib/ygocore/event.rb'
load 'lib/ygocore/user.rb'
load 'lib/ygocore/room.rb'
load 'lib/ygocore/scene_lobby.rb'
require 'json'
require 'xmpp4r/client'
require 'xmpp4r/muc'
end
def refresh_interval
60
end
def login(username, password)
@username = username
@password = password
@nickname_conflict = []
matched = @username.match Jabber::JID::PATTERN
if matched[1] && matched[2]
@username = matched[1]
jid = Jabber::JID::new @username, matched[2], matched[3] || 'mycard'
else
jid = Jabber::JID::new @username, 'my-card.in', 'mycard'
end
Jabber::Client.module_eval do |klass|
def start
super
send(generate_stream_start(@jid.domain, nil, nil, $config['i18n']['locale'])) { |e| e.name == 'stream' }
end
end
@@im = Jabber::Client.new(jid)
@@im_room = Jabber::MUC::MUCClient.new(@@im)
@@im.on_exception do |exception, c, where|
$log.error('聊天出错') { [exception, c, where] }
Game_Event.push(Game_Event::Chat.new(ChatMessage.new(User.new(:system, 'System'), '聊天服务连接中断: ' + exception.to_s)))
end
@@im_room.add_message_callback do |m|
if m.from.resource.nil? and m.subject
Game_Event.push Game_Event::Chat.new ChatMessage.new(User.new(:subject, '主题'), m.subject, :lobby) rescue $log.error('收到聊天消息') { $! }
end
if m.from.resource and m.body
user = m.from.resource == nickname ? @user : User.new(m.from.resource.to_sym, m.from.resource)
Game_Event.push Game_Event::Chat.new ChatMessage.new(user, m.body, :lobby) rescue $log.error('收到聊天消息') { $! }
end
end
@@im_room.add_private_message_callback do |m|
if m.from.resource and m.body #忽略无消息的正在输入等内容
user = m.from.resource == nickname ? @user : User.new(m.from.resource.to_sym, m.from.resource)
Game_Event.push Game_Event::Chat.new ChatMessage.new(user, m.body, user) rescue $log.error('收到私聊消息') { $! }
end
end
@@im_room.add_join_callback do |m|
user = User.new m.from.resource.to_sym, m.from.resource
user.affiliation = m.x('http://jabber.org/protocol/muc#user').first_element('item').affiliation rescue nil
Game_Event.push Game_Event::NewUser.new user
end
@@im_room.add_leave_callback do |m|
Game_Event.push Game_Event::MissingUser.new User.new m.from.resource.to_sym, m.from.resource
end
connect
im_connect
end
def nickname
return @nickname if @nickname
if @nickname_conflict.include? @username
1.upto(9) do |i|
result = "#{@username}-#{i}"
return result unless @nickname_conflict.include? result
end
raise 'can`t get available nickname'
else
@username
end
end
def connect
@recv = Thread.new do
EventMachine::run {
http = EM::HttpRequest.new("https://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.filter[:servers] = self.servers.clone
rescue
Game_Event.push Game_Event::Error.new('ygocore', '读取服务器列表失败.1', true)
end
ws = WebSocket::EventMachine::Client.connect(:uri => 'wss://my-card.in/rooms.json');
ws.onmessage do |msg, type|
Game_Event.push Game_Event::RoomsUpdate.new JSON.parse(msg).collect { |room| Game_Event.parse_room(room) }
end
ws.onclose do
$log.info('websocket连接断开')
Game_Event.push Game_Event::Error.new('ygocore', '网络连接中断.1', true)
end
}
http.errback {
Game_Event.push Game_Event::Error.new('ygocore', '读取服务器列表失败', true)
}
}
end
end
def im_connect
Thread.new {
begin
@@im.allow_tls = false
@@im.use_ssl = true
connected = false
if @@im.jid.domain == "my-card.in"
begin
@@im.connect("chat.my-card.in", 5223)
connected = true
rescue
Game_Event.push Game_Event::Error.new('登录', '连接服务器失败')
end
else
srv = []
Resolv::DNS.open { |dns|
Jabber::debuglog("RESOLVING:\n_xmpp-client._tcp.#{@@im.jid.domain} (SRV)")
srv = dns.getresources("_xmpp-client._tcp.#{@@im.jid.domain}", Resolv::DNS::Resource::IN::SRV)
}
if srv.empty?
Game_Event.push Game_Event::Error.new('登录', '解析服务器地址失败')
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.each { |record|
begin
@@im.connect(record.target.to_s, 5223)
# Success
connected = true
break
rescue
# Try next SRV record
end
}
end
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('登录', '用户名或密码错误')
Thread.exit
end
@@im.send(Jabber::Presence.new.set_type(:available))
Game_Event.push Game_Event::Login.new User.new(@@im.jid, @username, true)
begin
nickname = nickname()
#@@im_room.join(Jabber::JID.new(I18n.t('lobby.room'), I18n.t('lobby.server'), nickname))
@@im_room.join(Jabber::JID.new('mycard', 'conference.my-card.in', nickname))
rescue Jabber::ServerError => exception
Game_Event.push(Game_Event::Chat.new(ChatMessage.new(User.new(:system, 'System'), exception.message)))
if exception.error.error == 'conflict'
@nickname_conflict << nickname
retry
end
end
end
rescue StandardError => exception
$log.error('聊天连接出错') { exception }
Game_Event.push Game_Event::Error.new('登录', '登录失败')
end
}
end
def chat(chatmessage)
case chatmessage.channel
when :lobby
msg = Jabber::Message::new(nil, chatmessage.message)
@@im_room.send msg
when User
msg = Jabber::Message::new(nil, chatmessage.message)
@@im_room.send msg, chatmessage.channel.id
#send(:chat, channel: chatmessage.channel.id, message: chatmessage.message, time: chatmessage.time)
end
end
#def chat(chatmessage)
# case chatmessage.channel
# when :lobby
# send(:chat, channel: :lobby, message: chatmessage.message, time: chatmessage.time)
# when User
# send(:chat, channel: chatmessage.channel.id, message: chatmessage.message, time: chatmessage.time)
# end
#end
def host(room_name, room_config)
room = Room.new(nil, room_name)
room.pvp = room_config[:pvp]
room.match = room_config[:match]
room.tag = room_config[:tag]
room.password = room_config[:password]
room.ot = room_config[:ot]
room.lp = room_config[:lp]
room.host_server
if $game.rooms.any? { |game_room| game_room.name == room_name }
Widget_Msgbox.new("建立房间", "房间名已存在", :ok => "确定")
false
else
Game_Event.push Game_Event::Join.new(room)
room
end
end
def watch(room)
Widget_Msgbox.new("加入房间", "游戏已经开始", :ok => "确定")
end
def join(room)
Game_Event.push Game_Event::Join.new(room)
end
def refresh
#send(:refresh)
end
def send(header, data=nil)
#$log.info('发送消息') { {header: header, data: data} }
#Client::MycardChannel.push header: header, data: data
end
def exit
@recv.exit if @recv
@recv = nil
end
def self.ygocore_path
Windows ? 'ygocore/ygopro_vs.exe' : 'ygocore/gframe'
end
def self.register
Dialog.web @@config['register']
end
def server
@@config['server']
end
def port
@@config['port']
end
def server=(server)
@@config['server'] = server
end
def port=(port)
@@config['port'] = port
end
def self.write_system_conf(options)
system_conf = {}
begin
IO.readlines(File.join(File.dirname(ygocore_path), 'system.conf')).each do |line|
line.force_encoding "UTF-8"
next if line[0, 1] == '#'
field, contents = line.chomp.split(' = ', 2)
system_conf[field] = contents
end
rescue
end
font, size = system_conf['textfont'].split(' ')
if !File.file?(File.expand_path(font, File.dirname(ygocore_path))) or size.to_i.to_s != size
require 'pathname'
font_path = Pathname.new(Font)
font_path = font_path.relative_path_from(Pathname.new(File.dirname(ygocore_path))) if font_path.relative?
system_conf['textfont'] = "#{font_path} 14"
end
if !File.file?(system_conf['numfont'])
system_conf['numfont'] = Windows ? 'c:/windows/fonts/arialbd.ttf' : '/usr/share/fonts/gnu-free/FreeSansBold.ttf'
end
options.each do |key, value|
system_conf[key] = value
end
open(File.join(File.dirname(ygocore_path), 'system.conf'), 'w') { |file| file.write system_conf.collect { |key, value| "#{key} = #{value}" }.join("\n") }
end
def self.run_ygocore(option, image_downloading=false)
Widget_Msgbox.new("ygocore", "正在启动ygocore") rescue nil
#写入配置文件并运行ygocore
case option
when Room
room = option
room_name = if room.ot != 0 or room.lp != 8000
mode = case when room.match? then
1; when room.tag? then
2
else
0
end
room_name = "#{room.ot}#{mode}FFF#{room.lp},5,1,#{room.name}"
elsif room.tag?
"T#" + room.name
elsif room.pvp? and room.match?
"PM#" + room.name
elsif room.pvp?
"P#" + room.name
elsif room.match?
"M#" + room.name
else
room.name
end
if room.password and !room.password.empty?
room_name += "$" + room.password
end
options = {}
if $game.user
options['nickname'] = $game.user.name
options['nickname'] += '$' + $game.password if $game.password and !$game.password.empty? and room.server.auth
end
options['lastip'] = room.server.ip
options['lastport'] = room.server.port.to_s
options['roompass'] = room_name if room_name and !room_name.empty?
write_system_conf options
args = '-j'
when :replay
args = '-r'
when :deck
args = '-d'
when String
File.rename(File.join(File.dirname(ygocore_path), 'deck', option + '.ydk'), File.join(File.dirname(ygocore_path), 'deck', option.gsub!(' ', '_') + '.ydk')) if option[' ']
write_system_conf 'lastdeck' => option
args = '-d'
end
spawn('./' + File.basename(ygocore_path), args, :chdir => File.dirname(ygocore_path))
WM.iconify rescue nil
Widget_Msgbox.destroy rescue nil
end
def self.replay(file, skip_image_downloading = false)
require 'fileutils'
FileUtils.mv Dir.glob('ygocore/replay/*.yrp'), 'replay/'
FileUtils.copy_file(file, "ygocore/replay/#{File.basename(file)}")
run_ygocore(:replay, skip_image_downloading)
end
private
def self.get_announcements
#公告
$config['ygocore'] ||= {}
$config['ygocore']['announcements'] ||= [Announcement.new("正在读取公告...", nil, nil)]
Thread.new do
begin
open('https://my-card.in/announcements.json') do |file|
$config['ygocore']['announcements'].replace JSON.parse(file.read).collect { |announcement|
Announcement.new(announcement['title'], announcement['url'], Date.parse(announcement['created_at']))
}
Config.save
end
rescue Exception => exception
$log.error('公告读取失败') { [exception.inspect, *exception.backtrace].collect { |str| str.force_encoding("UTF-8") }.join("\n") } if $log
end
end
end
#module Client
# MycardChannel = EM::Channel.new
# include EM::P::ObjectProtocol
#
# def post_init
# send_object header: :login, data: {name: $game.username, password: $game.password}
# MycardChannel.subscribe { |msg| send_object(msg) }
# end
#
# def receive_object obj
# $log.info('收到消息') { obj.inspect }
# Game_Event.push Game_Event.parse obj[:header], obj[:data]
# end
#
# def unbind
# Game_Event.push Game_Event::Error.new('ygocore', '网络连接中断', true)
# end
#end
get_announcements
end
# websocket, due to the author hasn't release separate gem yet
#https://github.com/imanel/websocket-ruby/issues/12
module WebSocket
module EventMachine
class Base < ::EventMachine::Connection
###########
### API ###
###########
def onopen(&blk)
; @onopen = blk;
end
# Called when connection is opened
def onclose(&blk)
; @onclose = blk;
end
# Called when connection is closed
def onerror(&blk)
; @onerror = blk;
end
# Called when error occurs
def onmessage(&blk)
; @onmessage = blk;
end
# Called when message is received from server
def onping(&blk)
; @onping = blk;
end
# Called when ping message is received from server
def onpong(&blk)
; @onpong = blk;
end
# Called when pond message is received from server
# Send data to client
# @param data [String] Data to send
# @param args [Hash] Arguments for send
# @option args [String] :type Type of frame to send - available types are "text", "binary", "ping", "pong" and "close"
# @return [Boolean] true if data was send, otherwise call on_error if needed
def send(data, args = {})
type = args[:type] || :text
unless type == :plain
frame = outgoing_frame.new(:version => @handshake.version, :data => data, :type => type)
if !frame.supported?
trigger_onerror("Frame type '#{type}' is not supported in protocol version #{@handshake.version}")
return false
elsif !frame.require_sending?
return false
end
data = frame.to_s
end
# debug "Sending raw: ", data
send_data(data)
true
end
# Close connection
# @return [Boolean] true if connection is closed immediately, false if waiting for server to close connection
def close
if @state == :open
@state = :closing
return false if send('', :type => :close)
else
send('', :type => :close) if @state == :closing
@state = :closed
end
close_connection_after_writing
true
end
# Send ping message to client
# @return [Boolean] false if protocol version is not supporting ping requests
def ping(data = '')
send(data, :type => :ping)
end
# Send pong message to client
# @return [Boolean] false if protocol version is not supporting pong requests
def pong(data = '')
send(data, :type => :pong)
end
############################
### EventMachine methods ###
############################
def receive_data(data)
# debug "Received raw: ", data
case @state
when :connecting then
handle_connecting(data)
when :open then
handle_open(data)
when :closing then
handle_closing(data)
end
end
def unbind
unless @state == :closed
@state = :closed
close
trigger_onclose('')
end
end
#######################
### Private methods ###
#######################
private
['onopen'].each do |m|
define_method "trigger_#{m}" do
callback = instance_variable_get("@#{m}")
callback.call if callback
end
end
['onerror', 'onping', 'onpong', 'onclose'].each do |m|
define_method "trigger_#{m}" do |data|
callback = instance_variable_get("@#{m}")
callback.call(data) if callback
end
end
def trigger_onmessage(data, type)
@onmessage.call(data, type) if @onmessage
end
def handle_connecting(data)
@handshake << data
return unless @handshake.finished?
if @handshake.valid?
send(@handshake.to_s, :type => :plain) if @handshake.should_respond?
@frame = incoming_frame.new(:version => @handshake.version)
@state = :open
trigger_onopen
handle_open(@handshake.leftovers) if @handshake.leftovers
else
trigger_onerror(@handshake.error)
close
end
end
def handle_open(data)
@frame << data
while frame = @frame.next
case frame.type
when :close
@state = :closing
close
trigger_onclose(frame.to_s)
when :ping
pong(frame.to_s)
trigger_onping(frame.to_s)
when :pong
trigger_onpong(frame.to_s)
when :text
trigger_onmessage(frame.to_s, :text)
when :binary
trigger_onmessage(frame.to_s, :binary)
end
end
unbind if @frame.error?
end
def handle_closing(data)
@state = :closed
close
trigger_onclose
end
def debug(description, data)
puts(description + data.bytes.to_a.collect { |b| '\x' + b.to_s(16).rjust(2, '0') }.join) unless @state == :connecting
end
end
end
end
# Example WebSocket Client (using EventMachine)
# @example
# ws = WebSocket::EventMachine::Client.connect(:host => "0.0.0.0", :port => 8080)
# ws.onmessage { |msg| ws.send "Pong: #{msg}" }
# ws.send "data"
module WebSocket
module EventMachine
class Client < Base
# Connect to websocket server
# @param args [Hash] The request arguments
# @option args [String] :host The host IP/DNS name
# @option args [Integer] :port The port to connect too(default = 80)
# @option args [Integer] :version Version of protocol to use(default = 13)
def self.connect(args = {})
host = nil
port = nil
if args[:uri]
uri = URI.parse(args[:uri])
host = uri.host
port = uri.port
end
host = args[:host] if args[:host]
port = args[:port] if args[:port]
port ||= 80
::EventMachine.connect host, port, self, args
end
# Initialize connection
# @param args [Hash] Arguments for connection
# @option args [String] :host The host IP/DNS name
# @option args [Integer] :port The port to connect too(default = 80)
# @option args [Integer] :version Version of protocol to use(default = 13)
def initialize(args)
@args = args
end
############################
### EventMachine methods ###
############################
# Called after initialize of connection, but before connecting to server
def post_init
@state = :connecting
@handshake = WebSocket::Handshake::Client.new(@args)
end
# Called by EventMachine after connecting.
# Sends handshake to server
def connection_completed
send(@handshake.to_s, :type => :plain)
end
private
def incoming_frame
WebSocket::Frame::Incoming::Client
end
def outgoing_frame
WebSocket::Frame::Outgoing::Client
end
end
end
end
name: ygocore
\ No newline at end of file
class Room
attr_accessor :pvp
attr_accessor :match
attr_accessor :tag
attr_accessor :ot
attr_accessor :lp
attr_accessor :status
attr_accessor :server
alias pvp? pvp
alias match? match
alias tag? tag
def lp
@lp ||= 8000
end
def ot
@ot ||= 0
end
def full?
$game.is_a?(Ygocore) ? (@status == :start) : player2 #不规范修正iduel房间识别问题
end
def extra
result = {}
if pvp?
result["[竞技场]"] = [255,0,0]
end
if tag?
result["[TAG双打]"] = [128,0,255]
elsif match?
result["[三回决斗]"] = [0xff,0x72,0]
end
if ot == 1
result["[TCG]"] = [255,0,0]
elsif ot == 2
result["[O/T混]"] = [255,0,0]
end
if lp != 8000
result["[LP: #{lp}]"] = [255,0,0]
end
result
end
def host_server
servers = $game.servers
servers.select!{|server|server.auth} if @pvp
s = servers & $game.filter[:servers]
servers = s if !s.empty?
server = servers.min_by{|server|$game.rooms.select{|room|room.server == server}.size}
server ||= Server.new(nil, "", $game.server, $game.port, true)
self.server = server
server
end
end
class Scene_Lobby
def join(room)
Ygocore.run_ygocore(room)
end
end
register: https://my-card.in/register
api: http://master.smdcn.net:7922/
index: https://my-card.in/
server: 122.0.65.70
port: 7911
\ No newline at end of file
class User
attr_reader :certified
def initialize(id, name = "", certified = true)
@id = id
@name = name
@certified = certified
end
def set(id, name = :keep, certified = :keep)
@id = id unless id == :keep
@name = name unless name == :keep
@certified = certified unless certified == :keep
end
def color
case @affiliation
when :owner
[220,20,60]
when :admin
[148, 43, 226]
else
@certified ? [0, 0, 0] : [128, 128, 128]
end
end
def space
if @certified
Dialog.web "https://my-card.in/users/#{CGI.escape @id.to_s}"
else
Widget_Msgbox.new("查看资料", "用户#{@name}没有注册", :ok => "确定")
end
end
def avatar(size = :small)
id = (@id.respond_to?(:bare) ? @id.bare : @id).to_s
cache = "graphics/avatars/mycard_#{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
require 'cgi'
$log.info('读取头像') { "https://my-card.in/users/#{CGI.escape id.to_s}.png" }
begin
open("https://my-card.in/users/#{CGI.escape id.to_s}.png", 'rb') { |io| open(cache, 'wb') { |c| c.write io.read } }
rescue Exception => exception
$log.error('下载头像') { [exception.inspect, *exception.backtrace].join("\n").force_encoding("UTF-8") }
cache = "graphics/avatars/error_#{size}.png"
end
(yield Surface.load(cache) if scene == $scene) rescue nil
end
else
result
end
end
end
class Window_Login
def clicked
return if @last_clicked and Time.now - @last_clicked < 3 #防止重复点击
case @index
when :login
Widget_Msgbox.new("ygocore", "正在登录")
$scene.draw #强制重绘一次,下面会阻塞
$game = Ygocore.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
Ygocore.register
@last_clicked = Time.now
when :replay
file = Dialog.get_open_file("播放录像", "ygocore录像 (*.yrp)" => "*.yrp")
if !file.empty? and File.file? file
Ygocore.replay file
end
@last_clicked = Time.now
end
end
end
\ No newline at end of file
en:
lang: "English"
chat:
room: "lobby.en"
server: "conference.my-card.in"
login:
name: "Username"
password: "Password"
login: "Login"
register: "Register"
remember: "Remember Password"
replay: "Replay"
lobby:
faq: "FAQ"
filter: "Filter"
editdeck: "Edit Deck"
newroom: "NewRoom"
match: "QuickMatch"
lobby: "Lobby"
ja:
name: "日本語"
\ No newline at end of file
zh-OS:
name: "中文(OcgSoft)"
\ No newline at end of file
zh-TW:
name: "中文(繁体)"
\ No newline at end of file
zh:
lang: "中文"
chat:
room: "lobby.zh"
server: "conference.my-card.in"
login:
name: "用户名"
password: "密码"
login: "登录"
register: "注册"
remember: "记住密码"
replay: "录像"
lobby:
forum: "论坛"
editdeck: "卡组编辑"
newroom: "建立房间"
lobby: "大厅"
filter: "房间筛选"
match: "自动匹配"
waiting_only: "仅等待中"
normal_only: "仅标准房"
'use strict';
const electron = require('electron');
const app = electron.app; // Module to control application life.
const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
var handleStartupEvent = function () {
if (process.platform !== 'win32') {
return false;
}
var squirrelCommand = process.argv[1];
switch (squirrelCommand) {
case '--squirrel-install':
case '--squirrel-updated':
// Optionally do things such as:
//
// - Install desktop and start menu shortcuts
// - Add your .exe to the PATH
// - Write to the registry for things like file associations and
// explorer context menus
// Always quit when done
const path = require('path');
const reg = require('winreg');
const shortcuts = require('windows-shortcuts');
let pending = 7;
let done = ()=> {
pending--;
if (pending == 0) {
app.quit()
}
};
let key = new reg({
hive: reg.HKCU,
key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders'
});
key.get('Desktop', (error, item)=> {
shortcuts.create(path.join(item.value, 'mycard.lnk'), process.execPath, done)
});
key = new reg({hive: reg.HKCU, key: '\\Software\\Classes\\mycard'});
key.set('URL Protocol', reg.REG_SZ, '"' + process.execPath + '"', done);
key = new reg({hive: reg.HKCU, key: '\\Software\\Classes\\mycard\\shell\\open\\command'});
key.set('', reg.REG_SZ, '"' + process.execPath + '" "%i"', done);
key = new reg({hive: reg.HKCU, key: '\\Software\\Classes\\mycard\\DefaultIcon'});
key.set('', reg.REG_SZ, '"' + process.execPath + '", 0', done);
key = new reg({hive: reg.HKCU, key: '\\Software\\Classes\\.ydk'});
key.set('', reg.REG_SZ, 'mycard', done);
key = new reg({hive: reg.HKCU, key: '\\Software\\Classes\\.ydk'});
key.set('', reg.REG_SZ, 'mycard', done);
key = new reg({hive: reg.HKCU, key: '\\Software\\Classes\\.deck'});
key.set('', reg.REG_SZ, 'mycard', done);
return true;
case '--squirrel-uninstall':
// Undo anything you did in the --squirrel-install and
// --squirrel-updated handlers
// Always quit when done
app.quit();
return true;
case '--squirrel-obsolete':
// This is called on the outgoing version of your app before
// we update to the new version - it's the opposite of
// --squirrel-updated
app.quit();
return true;
}
};
if (handleStartupEvent()) {
return;
}
var shouldQuit = app.makeSingleInstance(function (commandLine, workingDirectory) {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
return true;
});
if (shouldQuit) {
app.quit();
}
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
app.quit();
}
});
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1024,
height: 640,
//frame: false,
'title-bar-style': 'hidden'
});
// and load the index.html of the app.
//mainWindow.loadURL('http://local.mycard.moe:3000');
mainWindow.loadURL('file://' + __dirname + '/index.html#ygopro');
// Open the DevTools.
mainWindow.webContents.openDevTools();
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
});
//const ipcMain = require('electron').ipcMain;
const ygopro = require('./ygopro');
/*ipcMain.on('join', (event, args) => {
ygopro.emit('run', args);
});
ygopro.on('run', (args) => {
});*/
/*
autoUpdater.setFeedUrl('http://localhost:4001?version=' + app.getVersion());
autoUpdater.checkForUpdates();
autoUpdater
.on('checking-for-update', function () {
console.log('Checking for update');
})
.on('update-available', function () {
console.log('Update available');
})
.on('update-not-available', function () {
console.log('Update not available');
})
.on('update-downloaded', function () {
console.log('Update downloaded');
});*/
\ No newline at end of file
#!/bin/sh
echo 'you only need to run from this script once, it will create desktop shortcut automatically after first run.'
cd "$(dirname "$0")"
read -p "Username: " username
if [ $username ]; then
read -p "Password: " password
echo "ygocore:
username: '$username'
password: '$password'
" > config.yml;
fi
ruby -KU lib/main.rb
{
"name": "mycard",
"description":"a game platform",
"version": "2.0.0",
"main": "main.js",
"license": "AGPL-3.0",
"repository": "github:mycard/mycard",
"dependencies": {
"ws": "^1.0.1"
},
"optionalDependencies": {
"windows-shortcuts": "*",
"winreg": "*",
"grunt-appdmg": "*"
},
"devDependencies": {
"electron-prebuilt": "^0.36.5",
"grunt": "^0.4.5",
"grunt-appdmg": "^0.3.2",
"grunt-contrib-clean": "^0.7.0",
"grunt-contrib-copy": "^0.8.2",
"grunt-electron": "^2.0.1",
"grunt-electron-installer": "^1.2.1"
}
}
"use strict";
/**
* Created by zh99998 on 16/1/24.
*/
const child_process = require('child_process');
const http = require('http');
const querystring = require('querystring');
const url = require('url');
const fs = require('fs');
const path = require('path');
const electron = require('electron');
const BrowserWindow = electron.BrowserWindow;
const ygopro_directory = 'ygopro';
const ygopro_main = 'ygopro.app/Contents/MacOS/ygopro';
const ygopro_system_conf = path.join(ygopro_directory, 'system.conf');
const ygopro_decks = path.join(ygopro_directory, 'deck');
const EventEmitter = require('events');
const ygopro = new EventEmitter();
ygopro.on('start', function (system, args) {
fs.readFile(ygopro_system_conf, 'utf8', (error, conf)=> {
if (error) return console.log(error);
let options = {};
for (let line of conf.split("\n")) {
if (line.charAt(0) == '#') continue;
if (!line[1])continue;
line = line.split(' = ');
options[line[0]] = line[1];
}
Object.assign(options, system);
let result = [];
for (let key in options) {
result.push(key + " = " + options[key])
}
fs.writeFile(ygopro_system_conf, result.join("\n"), (error)=> {
if (error) return console.log(error);
if (args) {
for (let window of BrowserWindow.getAllWindows()) {
window.minimize()
}
let child = child_process.spawn(ygopro_main, [args], {cwd: ygopro_directory});
child.on('exit', ()=> {
for (let window of BrowserWindow.getAllWindows()) {
window.restore()
}
})
}
})
});
});
ygopro.on('delete', function (file) {
if (path.dirname(file) == 'deck' && path.extname(file) == '.ydk') {
fs.unlink(path.join(ygopro_directory, file));
let deck = path.basename(file, '.ydk');
for (let i in decks) {
if (decks[i].name === deck) decks.splice(i, 1);
}
} // reject others
});
module.exports = ygopro;
const system = {};
const decks = [];
let pending = 2;
fs.readFile(ygopro_system_conf, 'utf8', (error, file)=> {
if (error) return done();
for (let line of file.split("\n")) {
if (line.charAt(0) == '#') continue;
line = line.split(' = ');
if (!line[1])continue;
system[line[0]] = line[1];
}
done()
});
fs.readdir(ygopro_decks, (error, files)=> {
if (error) return done();
let deckfiles = [];
for (let filename of files) {
if (path.extname(filename) == '.ydk') {
deckfiles.push(filename);
}
}
for (let filename of deckfiles) {
let deck = {name: path.basename(filename, '.ydk'), cards: []};
pending++;
fs.stat(path.join(ygopro_decks, filename), (error, stats)=> {
if (error)return done();
deck.created_at = stats.birthtime;
deck.updated_at = stats.mtime;
fs.readFile(path.join(ygopro_decks, filename), 'utf8', (error, file)=> {
if (error)return done();
let side = false;
let cards = {};
for (let line of file.split("\n")) {
if (line.charAt(0) == '#') continue;
if (line.slice(0, 5) == '!side') {
for (let card_id in cards) {
deck.cards.push(cards[card_id]);
}
cards = {};
}
let id = parseInt(line);
if (!id)continue;
if (cards[id]) {
cards[id].count++;
} else {
cards[id] = {id: id, count: 1, side: side};
}
}
for (let card_id in cards) {
deck.cards.push(cards[card_id]);
}
decks.push(deck);
done()
})
})
}
done()
});
function done() {
pending--;
if (pending == 0) {
start_server();
}
}
function start_server() {
const WebSocketServer = require('ws').Server;
const server = new WebSocketServer({host: '127.0.0.1', port: 9999});
server.on('connection', (connection) => {
connection.send(JSON.stringify({
event: 'init',
data: {
system: system,
decks: decks
}
}));
connection.on('message', (message) => {
message = JSON.parse(message);
ygopro.emit(message.event, ...message.data);
});
});
}
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