From 60dbc84f013f6a9420fd93129f7644619faa452d Mon Sep 17 00:00:00 2001 From: Feshchukov Aristarh Date: Fri, 8 Nov 2019 09:28:33 +0300 Subject: [PATCH] Ported code --- COPYING | 339 +++++++++++ Makefile | 26 + README.md | 2 + Tupfile.lua | 7 + game.cpp | 1585 +++++++++++++++++++++++++++++++++++++++++++++++++ interface.cpp | 345 +++++++++++ interface.h | 53 ++ kos_vector.h | 91 +++ main.cpp | 45 ++ pq.h | 204 +++++++ strings.h | 540 +++++++++++++++++ sys.cpp | 400 +++++++++++++ sys.h | 53 ++ 13 files changed, 3690 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 Tupfile.lua create mode 100644 game.cpp create mode 100644 interface.cpp create mode 100644 interface.h create mode 100644 kos_vector.h create mode 100644 main.cpp create mode 100644 pq.h create mode 100644 strings.h create mode 100644 sys.cpp create mode 100644 sys.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + 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 +this service 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. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b93cdc7 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +OUTFILE = bin/21days +CXX = g++ +OBJS = obj/main.o obj/game.o obj/interface.o obj/sys.o + +CXXFLAGS = -Wno-write-strings + +all: $(OUTFILE) + +dirs: + test -d bin || mkdir -p bin + test -d obj || mkdir -p obj + +$(OUTFILE): dirs $(OBJS) + $(CXX) -o $(OUTFILE) $(OBJS) + +obj/%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ -D__linux__ + +clean: + rm -f $(OBJS) $(OUTFILE) + rm -rf bin + rm -rf obj + +rebuild: clean $(OUTFILE) + +.PHONY: dirs clean rebuild diff --git a/README.md b/README.md index b2ecf23..de43e3f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # 21days Game "21days" ported to Linux from KolibriOS +Game for programmers, only on Russian +It's needed to change font size for better looking diff --git a/Tupfile.lua b/Tupfile.lua new file mode 100644 index 0000000..425b3c0 --- /dev/null +++ b/Tupfile.lua @@ -0,0 +1,7 @@ +if tup.getconfig("NO_GCC") ~= "" then return end +HELPERDIR = (tup.getconfig("HELPERDIR") == "") and "../.." or tup.getconfig("HELPERDIR") +tup.include(HELPERDIR .. "/use_gcc.lua") +tup.include(HELPERDIR .. "/use_menuetlibc.lua") +CFLAGS = CFLAGS .. " -Wno-write-strings -D _KOS32 " +compile_gcc{"main.cpp", "game.cpp", "interface.cpp", "sys.cpp"} +link_gcc("21days") diff --git a/game.cpp b/game.cpp new file mode 100644 index 0000000..3058330 --- /dev/null +++ b/game.cpp @@ -0,0 +1,1585 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#define DEFAULT_PATH 0 +#define WORK_PATH 1 +#define STARTUP_PATH 2 + +#include "sys.h" +#include "strings.h" +#include "interface.h" +#include "pq.h" + +#ifdef _KOS32 + #include + #define vector vector21 +extern "C" { + void *__dso_handle = 0; + void *__cxa_atexit = 0; + void *__stack_chk_fail = 0; + } +#else + #define wprintf2 printf + #include //srand + #include + using std::vector; + #include // usleep +#endif + +#include +#include +using std::wstring; + +Undo2 history; + +/************************************** +* Player +*****************************************/ +long double playerMoney = 100; +int playerSalary = 0; +int playerSalaryFirstDay = 0; +int playerPath = DEFAULT_PATH; +int playerKarma = 20; +double dt = 1.0; // days per secons +long double gameTime = 0; // Time in days +double unreadMessages = 0; +unsigned int timeHops = 0; +bool allAchievesShowed = false; +bool wchar_tityUnlocked = false; + +int playerFreelanceAttempts = 0; +bool controlFreelanceAttempts = false; + +bool sentBotsmannLetter = false; +bool astraLetterSent = false; +bool shitCodeDetected = false; +bool shitLettersSent[3] = {0,0,0}; +bool shitLettersFinished[3] = {0,0,0}; +bool showCoursesTab = false; +PQ3 pq; // events priority queue +bool noPopularity = false; +short finalCardsUnlocked = 0; +bool finalCardUnlocked[5] = {0,0,0,0,0}; +bool returnTo21HintShowed = false; +// Player Stats +time_t playerStartedPlaying; +unsigned long playerTimeHops = 0; +unsigned long playerMessagesRead = 0; +unsigned long playerHelped = 0; +unsigned long playerDidntHelped = 0; +long double playerSpentRealDays = 0; +long double playerPrevUndo = 0; +long double playerMoneyEarned = 100; +long double playerMoneySpent = 0; +double playerMoneySpentForCharity = 0; + + +void mainGameCycle(); +int showSettingsScreen(); +int chooseCourse(); +void showAchievesScreen(); + +void startNewGame(); +void initSendingMessage(); +void undo(long double toTime); + +wstring getStatusLine(); + +void addTimer(event e) { + if (e.type == SPECIAL_LETTER && + (e.idata == LETTER_SHITCODE_1 + || e.idata == LETTER_SHITCODE_2 + || e.idata == LETTER_SHITCODE_3) + && pq.containsType(e.type, e.idata)) + return; + if (e.type == MESSAGE && pq.containsType((int)e.type)) + return; + + pq.insert(e); + history.insert(event(e.time + gameTime, e.type, e.idata)); + } + +void unlockFinalCards(short p) { + if (!finalCardUnlocked[p]) + finalCardsUnlocked++; + + finalCardUnlocked[p] = true; + } + +void increaseMoney(long double m) { + playerMoney += m; + if (m > 0) + playerMoneyEarned += m; + if (m < 0) + playerMoneySpent -= m; + } + +void increaseKarma(int k, bool addTimerMsg = true) { + if (playerKarma <= 0 && playerKarma+k>0) + initSendingMessage(); + int oldKarma = playerKarma; + playerKarma += k; + if (playerKarma < -100) + playerKarma = -100; + + if (playerKarma > 100) + playerKarma = 100; + + if (addTimerMsg && playerKarma - oldKarma != 0) + history.insert(event(2*dt, INCREASEPOPULARITY, playerKarma - oldKarma)); + } + +void changePath(int to) { + history.insert(event(gameTime, CHANGEPATH, playerPath)); + playerPath = to; + } + +void changeSalary(int to) { + history.insert(event(gameTime, CHANGESALARY, playerSalary)); + playerSalary = to; + } + +void changeSalaryFirstDay(int to) { + history.insert(event(gameTime, CHANGESALARYFIRSTDAY, playerSalaryFirstDay)); + playerSalaryFirstDay = to; + } + +int karma() { + return playerKarma; + } + +void initSendingMessage() { + double newTime = (karma() <= 0)?0.0:0.5 ;//* karma() / 100.0; + if (newTime > 0) { + addTimer(event(newTime, MESSAGE)); + } + else { + noPopularity = true; + addTimer(event(0, NOPOPULARITY)); + } + } + +void checkAchieves() { + /// No popularity + if (!achievesPicked[1] && noPopularity) + achievesPicked[1] = true; + /// Millionaire + if (!achievesPicked[3] && playerMoney >= 1000000.0) { + achievesPicked[3] = true; + wchar_tityUnlocked = true; + } + /// Time jumper + if (!achievesPicked[8] && timeHops >= 3) + achievesPicked[8] = true; + + allAchievesShowed = true; + /// Show achieve notification + for (unsigned int i = 0; i < achievesNumber; i++) { + if (achievesPicked[i] && !achievesShowed[i]) { + drawModalWindow(achieves[i], newAchievement); + wait(); + if (i == 3) { + drawModalWindow(pressRForCharity, 0, L"[R]"); + wait('r','R'); + } + achievesShowed[i] = true; + } + else if (!achievesShowed[i]) + allAchievesShowed = false; + } + } + +void showMainMenu() { + wstring s = days21; + s+=mainMenuText; + drawWindow(s, gameName, L"", txt(L"%ls %ls", one_or_two, developer)); + wchar_t menu = getAnswer('1', '2'); + + switch(menu) { + case '1': + startNewGame(); + break; + case '2': // TODO: Save? + + break; + } + } + +void showAchievesScreen() { + wstring s; + for (unsigned short i = 0; i < achievesNumber; i++) + s += txt(L"[%lc] %ls \n", achievesPicked[i]?'X':' ', achieves[i]); + + s += txt(finalCardsUnlockedText, finalCardsUnlocked, 5); + wchar_t totalCoursesCount = 12; + s += txt(coursesFinishedText, (int)finishedCoursesCount, (int)totalCoursesCount); + drawWindow(s, achievementsTitle, getStatusLine(), enter); + } + +wstring printExactTask(int course, int task, int word) { + switch (course) { + case 1: + case 2: + return wstring(programmingTasks[task]); + break; + case 3: + case 4: + return wstring(mobileTasks[task]); + break; + case 5: + return wstring(webProgrammingTasks[task]); + break; + case 6: + return wstring(systemProgrammingTask[task]); + break; + case 7: + return wstring(adminTasks[task]); + break; + case 0: + default: + return txt(level1Task[task], level1TaskWords[word]); + break; + } + } + +void showLearnDialog() { + wstring s = selectCourseText; + for (unsigned short i = 0; i < coursesNumber; i++) { + if (!coursesUnlocked[i]) + continue; + + if (i == 2) { + s+=txt(L"3.%ls\n", courses[2]); + for (int j = 0; j < advancedProgrammingCourses; j++) { + s+=txt(L" %lc.%ls [%lc]", (wchar_t)('a'+j), advProgCourses[j], (advProgCoursesFinished[j])?'X':' '); + if (!advProgCoursesFinished[j] && !pq.hasCourse('a'+j)) + s+=txt(takeCourseCost, advProgLearningCost[j]); + else if (pq.hasCourse('a'+j)) + s+=takingThisCourse; + else + s+=L"\n"; + } + continue; + } + + s+=txt(L"%lc.%ls [%lc]", (wchar_t)('1'+i), courses[i], (coursesFinished[i])?'X':' '); + if (!coursesFinished[i] && !pq.hasCourse(i)) + s+=txt(takeCourseCost, learningCost[i]); + else if (pq.hasCourse(i)) { + s+=takingThisCourse; + } + else + s+=L"\n"; + } + + drawWindow(s, coursesTitle ,L"", number_or_enter); + unsigned wchar_t answer = 0; + answer = getKey(); + + if (answer == ENTER_KEY) + return; + + else if (answer >='1' && answer <=(wchar_t)('0'+coursesNumber) && answer != '3') { + answer -- ; + answer -='0'; + + if (coursesUnlocked[answer]){ + if (coursesFinished[answer]) { + drawModalWindow(courseAlreadyPassed, errorMsg); + wait(); + showLearnDialog(); + return; + } + else if (playerMoney > learningCost[answer] && !pq.hasCourse(answer)) { + drawModalWindow(txt(successfullyEnrolled, courses[answer]).c_str(), congratsMsg); + wait(); + increaseMoney(-learningCost[answer]); + addTimer(event(learningTime[answer], COURSE, answer)); + } + else if (playerMoney <= learningCost[answer] && !pq.hasCourse(answer)){ + drawModalWindow(notEnoughMoney, errorMsg); + wait(); + showLearnDialog(); + } + else + showLearnDialog(); + return; + } + else + showLearnDialog(); + } + else if (answer == '3' && coursesUnlocked[2]) { + drawModalWindow(ae_advanced_courses,errorMsg); + wait(); + showLearnDialog(); + } + /// only small letters + else if (answer >='a' && answer <= 'e') { + answer -= 'a'; + if (coursesUnlocked[2] && playerMoney > advProgLearningCost[answer] && !advProgCoursesFinished[answer] && !pq.hasCourse('a'+answer)) { + drawModalWindow(txt(successfullyEnrolled,advProgCourses[answer]).c_str(), congratsMsg); + wait(); + + increaseMoney(-advProgLearningCost[answer]); + int learnCourseNumber = answer+'a'; + if (advProgLearningTime[answer] != 0 && learnCourseNumber != -1) + addTimer(event(advProgLearningTime[answer], COURSE, learnCourseNumber)); + return; + } + else if (coursesUnlocked[2] && playerMoney <= advProgLearningCost[answer] && !advProgCoursesFinished[answer] && !pq.hasCourse('a'+answer)) { + drawModalWindow(notEnoughMoney, errorMsg); + wait(); + showLearnDialog(); + } + else if (advProgCoursesFinished[answer]) { + drawModalWindow(courseAlreadyPassed, errorMsg); + wait(); + showLearnDialog(); + return; + } + else + showLearnDialog(); + } + else if (answer == '3') { + showLearnDialog(); + } + else if (coursesUnlocked[2]) { + drawModalWindow(ae_advanced_courses,errorMsg); + wait(); + showLearnDialog(); + } + else + showLearnDialog(); + return; + } + +void startNewGame() { + // Intro + drawWindow(gameIntroTextPart1, introTitle, L"", enter, true); + wait(); + drawWindow(gameIntroTextPart2, introTitle, L"", enter, true); + wait(); + drawWindow(gameIntroTextPart3, introTitle, L"", enter, true); + wait(); + // Goals + drawWindow(gameIntroPlan, 0, L"", enter, true); + wait(); + // Desktop + drawWindow(L"", introDesktop, L"", enter); + wait(); + // Top line + drawWindow(introStatusLine,L"", getStatusLine(), enter); + wait(); + // Keys + drawWindow(L"", L"", L"", introAllowedKeys); + wait('1'); + // Dialog window + drawModalWindow(introFinished, introLetsBegin); + wait(); + // Start game! + mainGameCycle(); + } + +void showFinalStats() { + // Show player stats + int minutesInGame = (time(NULL) - playerStartedPlaying)/60; + playerSpentRealDays += (gameTime - playerPrevUndo); + long unsigned int val[9] = { + static_cast(playerSpentRealDays), + static_cast(minutesInGame), + playerMessagesRead, + playerHelped, + playerDidntHelped, + static_cast(playerMoneyEarned), + static_cast(playerMoneySpent), + static_cast(playerMoneySpentForCharity), + playerTimeHops}; + wstring s = L"\n"; + s += playerStatsTitle; + for (int i = 0; i < 9; i++) { + s += txt(playerStats[i], static_cast(val[i])); + if (i == 0 || i == 3 || i == 4) + s += getWordEnding(val[i], i); + } + s += playerStatsEnd; + drawWindow(s); + wait(ESCAPE_KEY, ESCAPE_KEY); + } + +bool isGameOver() { + if (noPopularity && !finalCardUnlocked[3] && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_NOPOPULARITY)) { + addTimer(event(2.0, SPECIAL_LETTER, LETTER_FINALPATH_NOPOPULARITY)); + return false; + } + + if ((playerPath == DEFAULT_PATH || playerPath == STARTUP_PATH) && playerMoney < 0.0 && !finalCardUnlocked[4] && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_NOMONEY)) { + addTimer(event(2.0, SPECIAL_LETTER, LETTER_FINALPATH_NOMONEY)); + return false; + } + // You win! + if (finishedCoursesCount >= 12 && finalCardsUnlocked >= 5 && gameTime <= 22.0 && allAchievesShowed) { + std::wstring s = youWin; + s += gameOver; + drawWindow(s, congratsMsg, L"", pressF); + wait('f','F'); + showFinalStats(); + return true; + } + // Game over + if (playerMoney < 0 && karma() < 0 && noPopularity && floor(unreadMessages) == 0 && gameTime > 22.0 && + achievesShowed[1] && finalCardUnlocked[3] && finalCardUnlocked[4] && + !pq.containsType(SPECIAL_LETTER) && !pq.containsType(MESSAGE)) { + wstring s = gameOverLogo; + s += gameOverText; + drawWindow(s, gameOverTitle, L"", pressF); + wait('f','F'); + showFinalStats(); + return true; + } + return false; + } + +int getAvailableCoursesCount() { + int a = 0; + for (unsigned int i = 0; i < coursesNumber; i++) { + if (i!= 2 && coursesUnlocked[i] && !coursesFinished[i]) + a++; + } + // Anvanced programming + if (coursesUnlocked[2]) { + for (int i = 0; i < advancedProgrammingCourses; i++) + if (!advProgCoursesFinished[i]) + a++; + } + return a; + } + +std::wstring getStatusLine() { + wstring s = txt(statusLine, (int)gameTime, (int)playerMoney, karma(), (int)floor(unreadMessages)); + if (showCoursesTab) + s += txt(statusLineCoursesNotFinished, getAvailableCoursesCount()); + return s; + } + +wstring getBottomLine() { + wstring s; + if (showCoursesTab) + s += bottomLineCourses; + s += bottomLineMsgAchieves; + if (wchar_tityUnlocked) { + s += charityTitle; + s += L"[R] "; + } + s += bottomLineSpeedAndExit; + return s; + } + +int showUnreadMessages() { + int newTaskLevel = chooseCourse(); + + unsigned int task = rand() % taskCount[newTaskLevel]; + unsigned int word = rand() % level1TaskWordNumber; + unsigned int yes = rand() % levelYesAnswerNumber; + unsigned int no = rand() % levelNoAnswerNumber; + + wstring s = printExactTask(newTaskLevel, task, word); + s += txt(yourAnswer,levelYesAnswer[yes],levelNoAnswer[no]); + wstring buttons = one_or_two; + buttons += escToBreakReading; + drawWindow(s, unread_message, getStatusLine(), buttons); + return newTaskLevel; + } + +bool messageGetAnswer(int level) { + long double reward = taskReward[level] + rand() % 20; + wchar_t answerKey = getAnswer('1', '2', ESCAPE_KEY); + + switch (answerKey) { + case '1': + // Player Stats + playerHelped++; + increaseMoney(reward); + increaseKarma(reward/5.0, false); + if (playerPath == WORK_PATH && controlFreelanceAttempts) { + playerFreelanceAttempts++; + if (playerFreelanceAttempts > 5) { + addTimer(event(1.0, SPECIAL_LETTER, LETTER_ANGRYBOSS_2)); + controlFreelanceAttempts = false; + } + } + // Repairing computer takes 0.5 days + gameTime += 0.5; + break; + case '2': + // Player Stats + playerDidntHelped++; + increaseKarma(-reward/2.0, false); + break; + case ESCAPE_KEY: + return true; + break; + } + return false; + } + +double showChangeSpeedDialog(wstring e = L"") { + wstring s; + if (dt >= 1.0) + s = txt(daysPerSecond, (int)dt); + else { + s = txt(daysPerSecond, 0); + s += txt(L".%d", (int)(dt*10)%10); // Actually not the best way of printing double values + } + if (!e.empty()) + s += e; + drawModalWindow(s.c_str(), changeSpeedTitle, changeSpeedButtons); + wchar_t ch = getAnswer('+','-','Y','y'); + if (ch == 'y' || ch == 'Y') + return dt; + else if (ch == '+') { + if (dt >= 1.0) + dt += 1.0; + else + dt += 0.10; + if (dt >= 10.0) { + dt = 10.0; + return showChangeSpeedDialog(L" max"); + } + return showChangeSpeedDialog(); + } + else if (ch == '-') { + if (dt >= 2.0) + dt -= 1.0; + else + dt -= 0.10; + if (dt <= 0.10) { + dt = 0.10; + return showChangeSpeedDialog(L" min"); + } + return showChangeSpeedDialog(); + } + return dt; + } + +// returns 1 if program have to be closed +bool keyPressed(wchar_t k) { + switch (k) { + // Courses + case 'c': + case 'C': + if (!showCoursesTab) + break; + showLearnDialog(); + break; + // Achieves + case 'a': + case 'A': + showAchievesScreen(); + wait(); + break; + // Messages + case 'm': + case 'M': { + int oldPopularity = karma(); + // Player Stats + bool breakReading = false; + while (floor(unreadMessages) > 0) { + breakReading = messageGetAnswer(showUnreadMessages()); + if (breakReading) { + break; + } + else { + if (floor(unreadMessages) > 0.0) unreadMessages--; + playerMessagesRead++; + } + } + if (!breakReading) { + drawModalWindow(noUnreadMessages); + wait(); + } + if (karma() != oldPopularity) + history.insert(event(gameTime, INCREASEPOPULARITY, karma()-oldPopularity)); + // Hack + #ifdef _KOS32 + initSendingMessage(); + #endif + } + break; + case 'u': + case 'U': + undo(gameTime - 10); + break; + case 'r': + case 'R': { + if (!wchar_tityUnlocked) + break; + + if (playerMoney <= 0) { + drawModalWindow(notEnoughMoneyForCharity, 0, enter); + wait(); + break; + } + drawModalWindow(charityQuestion, charityTitle, yesNoDialog); + wchar_t ch = getAnswer('y', 'n', 'Y', 'N'); + if (ch == 'y' || ch == 'Y') { + // Player Stats + playerMoneySpentForCharity += (playerMoney / 2.0); + + increaseMoney(-playerMoney / 2.0); + increaseKarma(abs(karma())); + } + } + break; + case 's': + case 'S': + dt = showChangeSpeedDialog(); + break; + // Escape + case ESCAPE_KEY: { + drawModalWindow(doYouReallyWantToExit,0 , yesNoDialog); + wchar_t ans = getAnswer('y','Y','n','N'); + if (ans == 'y' || ans == 'Y') + return true; + } + break; + } + return false; + } + +void breakingNews(int newsID) { + drawWindow(news[newsID], breaking_news, getStatusLine(), enter); + wait(); + } + +void checkPq() { + vector lateEvents; + // Messages in PQ are in reverse order + for (int i = pq.n()-1; i >= 0; i--) { + event* e = pq.get(i); + if (e->time > dt) { + e->time -= dt; + continue; + } + switch (e->type) { + case COURSE: { + wstring s; + + if (e->idata >= 0 && e->idata < (int)coursesNumber) { + s += txt(courseSuccessfullyFinished,courses[e->idata]); + // Not the best fix for the bug with mobile dev + if (!coursesFinished[e->idata]) + finishedCoursesCount++; + coursesFinished[e->idata] = true; + } + // Advanced programming + else if (e->idata >='a' && e->idata <= 'e') { + e->idata -= 'a'; + s += txt(courseSuccessfullyFinished,advProgCourses[e->idata]); + if (!advProgCoursesFinished[e->idata]) + finishedCoursesCount++; + advProgCoursesFinished[e->idata] = true; + } + increaseKarma(+5); + + pq.delMin(); + drawModalWindow(s.c_str(), congratsMsg); + wait(); + } + break; + case SPECIAL_LETTER: + switch (e->idata) { + case LETTER_RETURN_TO_DAY_21: + drawModalWindow(returnToDay21, 0, pressP); + wait('p','P'); + returnTo21HintShowed = true; + break; + case LETTER_SHITCODE_1: + case LETTER_SHITCODE_2: + case LETTER_SHITCODE_3: { + if (playerPath == WORK_PATH) + break; + wstring s; + int letterIndex = e->idata*2; + s += specialLetters[letterIndex]; + s += shitCodeYourAnswer; + drawWindow(s, new_letter, getStatusLine(), one_or_two); + + wchar_t ch = getAnswer('1', '2'); + if (ch == '1') { + if (e->idata == LETTER_SHITCODE_1 && advProgCoursesFinished[0] && advProgCoursesFinished[1]) { + // sussessfull + increaseMoney(300.0); + increaseKarma(+5); + drawWindow(txt(specialLetters[24], (int)gameTime, 300), answerLetter, getStatusLine(), enter); + shitLettersFinished[0] = true; + } + else if (e->idata == LETTER_SHITCODE_2 && advProgCoursesFinished[2]) { + // sussessfull + increaseMoney(500.0); + increaseKarma(+5); + drawWindow(txt(specialLetters[24], (int)gameTime, 500), answerLetter, getStatusLine(), enter); + shitLettersFinished[1] = true; + } + else if (e->idata == LETTER_SHITCODE_3 && advProgCoursesFinished[3]) { + // sussessfull + increaseMoney(800.0); + increaseKarma(+5); + drawWindow(txt(specialLetters[24], (int)gameTime, 800), answerLetter, getStatusLine(), enter); + shitLettersFinished[2] = true; + } + else { + drawModalWindow(specialLetters[letterIndex+1], answerLetter); + coursesUnlocked[2] = true; + shitCodeDetected = true; + increaseKarma(-5); + } + + if (shitLettersFinished[0] && shitLettersFinished[1] && shitLettersFinished[2]) + achievesPicked[0] = true; + + wait(); + } + else if (ch == '2') { + increaseKarma(-2); + } + } + break; + case LETTER_BOTSMANN: + if (playerPath == DEFAULT_PATH) { + wstring s = specialLetters[6]; + drawWindow(s, new_letter, getStatusLine(), enter); + wait(); + lateEvents.push_back(event(10.0, SPECIAL_LETTER, LETTER_ASTRA)); + } + break; + case LETTER_ASTRA: + if (playerPath == DEFAULT_PATH) { + wstring s = specialLetters[7]; + s += shitCodeYourAnswer; + + drawWindow(s, new_letter, getStatusLine(), one_or_two); + wchar_t ch = getAnswer('1', '2'); + if (ch == '1') { + s = specialLetters[8]; + drawWindow(s, new_letter, getStatusLine(), enter); + increaseKarma(-10); + } + else if (ch == '2') { + s = specialLetters[9]; + drawWindow(s, new_letter, getStatusLine(), enter); + // Unlock mobile development + coursesUnlocked[3] = true; + if (!coursesFinished[3] && !pq.hasCourse(3)); + lateEvents.push_back(event(learningTime[3], COURSE, 3)); + changePath(WORK_PATH); + changeSalary(800); + changeSalaryFirstDay((int)gameTime); + increaseKarma(+5); + } + wait(); + } + break; + case LETTER_UNNAMEDSTUDIO_1: { + wstring s = specialLetters[10]; + if (playerPath == WORK_PATH) { + lateEvents.push_back(event(15.0, SPECIAL_LETTER, LETTER_ANGRYBOSS_1)); + drawWindow(s, new_letter, getStatusLine(), enter); + wait(); + } + else if(playerPath == DEFAULT_PATH) { + s += unnamedStudio1Answer; + drawWindow(s, new_letter, getStatusLine(), one_or_two); + wchar_t ch = getAnswer('1', '2'); + if (ch == '1') { + changePath(STARTUP_PATH); + increaseMoney(-500); + lateEvents.push_back(event(12.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2)); + } + } + } + break; + case LETTER_UNNAMEDSTUDIO_2: { + wstring s = specialLetters[13]; + if (coursesFinished[4]) + s+=specialLetters[27]; + else + s+=specialLetters[26]; + drawWindow(s, new_letter, getStatusLine(), enter); + // Design basics course unlocked + coursesUnlocked[4] = true; + unnamedStudioLettersSent[1] = true; + wait(); + } + break; + case LETTER_UNNAMEDSTUDIO_3: { + int needMoney = (int)(playerMoney * 0.5); + if (needMoney > 50000) + needMoney = 50000; + else if (needMoney < 0) + needMoney *= -1; + wstring s = txt(specialLetters[14], needMoney); + s += unnamedStudio3Answer; + drawWindow(s, new_letter, getStatusLine(), one_or_two); + + wchar_t ch = getAnswer('1', '2'); + if (ch == '1') { + increaseMoney(-needMoney); + lateEvents.push_back(event(15.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4)); + } + else if(ch == '2') { + changePath(DEFAULT_PATH); + achievesPicked[5] = true; + increaseKarma(-10); + } + } + break; + case LETTER_UNNAMEDSTUDIO_4: { + wstring s = specialLetters[15]; + // Creating web-sites + if (coursesFinished[5]) { + s+=specialLetters[29]; + drawWindow(s, new_letter, getStatusLine(), enter); + wait(); + // Waiting for 40 days + lateEvents.push_back(event(40.0, SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO)); + break; + } + int needMoney = (int)(playerMoney * 0.5); + if (needMoney > 50000) + needMoney = 50000; + else if (needMoney < 0) + needMoney *= -1; + s += txt(specialLetters[28], needMoney); + s += unnamedStudio4Answer; + drawWindow(s, new_letter, getStatusLine(), one_two_or_three); + wchar_t ch = getAnswer('1', '2', '3'); + if (ch == '1') { + increaseMoney(-needMoney); + increaseKarma(+5); + // Pay for the webmaster + lateEvents.push_back(event(40.0, NEWS, 3)); + } + else if (ch == '2') { + coursesUnlocked[5] = true; + lateEvents.push_back(event(40.0, SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO)); + } + else if (ch == '3') { + changePath(DEFAULT_PATH); + achievesPicked[5] = true; + increaseKarma(-5); + } + } + break; + case LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO: + if (coursesFinished[5]) { + lateEvents.push_back(event(1.0, NEWS, 3)); + } + else { + // SHow letter about fail + drawWindow(specialLetters[16], new_letter, getStatusLine(), enter); + wait(); + drawModalWindow(startupFailedTip); + wait(); + increaseMoney(1000); + achievesPicked[5] = true; + increaseKarma(-15); + } + break; + case LETTER_UNNAMEDSTUDIO_5: { + drawWindow(specialLetters[17], new_letter, getStatusLine(), enter); + increaseMoney(200000); + increaseKarma(+20); + wait(); + achievesPicked[2] = true; + lateEvents.push_back(event(5.0, SPECIAL_LETTER, LETTER_FINALPATH_STARTUP)); + } + break; + case LETTER_ANGRYBOSS_1: { + wstring s = specialLetters[11]; + s += angryBossAnswer; + drawWindow(s, new_letter, getStatusLine(), one_or_two); + wchar_t ch = getAnswer('1', '2'); + if (ch == '1') { + changeSalary(1200); + coursesUnlocked[5] = true; + coursesUnlocked[6] = true; + coursesUnlocked[7] = true; + playerFreelanceAttempts = 0; + controlFreelanceAttempts = true; + lateEvents.push_back(event(20.0, SPECIAL_LETTER, NO_POPULARITY_HINT)); + } + else if(ch == '2') { + changePath(STARTUP_PATH); + changeSalary(0); + lateEvents.push_back(event(9.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2)); + achievesPicked[4] = true; + increaseKarma(-10); + } + } + break; + case NO_POPULARITY_HINT: + if (!noPopularity && controlFreelanceAttempts && playerFreelanceAttempts < 5) { + drawWindow(specialLetters[25], new_letter, getStatusLine(), enter); + wait(); + } + break; + case LETTER_ANGRYBOSS_2: + drawWindow(specialLetters[12], new_letter, getStatusLine(), enter); + changePath(DEFAULT_PATH); + changeSalary(0); + wait(); + increaseKarma(-20); + break; + case LETTER_BORING_WORK: + if (playerPath == WORK_PATH) { + drawWindow(specialLetters[18], new_letter, getStatusLine(), enter); + wait(); + lateEvents.push_back(event(15.0, SPECIAL_LETTER, LETTER_PERSISTENT_AND_PATIENT)); + } + break; + case LETTER_BORING_DEFAULT_PATH: + if (playerPath == DEFAULT_PATH && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_DEF + && !pq.containsType(SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH))) { + drawWindow(specialLetters[19], new_letter, getStatusLine(), enter); + wait(); + lateEvents.push_back(event(5.0, SPECIAL_LETTER, LETTER_FINALPATH_DEF)); + } + break; + case LETTER_FINALPATH_DEF: + if (playerPath == DEFAULT_PATH) { + drawWindow(defaultFinalCard, finalCard, getStatusLine(), pressF); + unlockFinalCards(0); + wait('f', 'F'); + } + break; + case LETTER_FINALPATH_WORK: + if (playerPath == WORK_PATH) { + drawWindow(workFinalCard, finalCard, getStatusLine(), pressF); + unlockFinalCards(1); + wait('f', 'F'); + } + break; + case LETTER_FINALPATH_STARTUP: + if (playerPath == STARTUP_PATH) { + drawWindow(startupFinalCard, finalCard, getStatusLine(), pressF); + unlockFinalCards(2); + wait('f', 'F'); + } + break; + case LETTER_FINALPATH_NOPOPULARITY: + drawWindow(zeroKarmaFinalCard, finalCard, getStatusLine(), pressF); + unlockFinalCards(3); + wait('f', 'F'); + break; + case LETTER_FINALPATH_NOMONEY: + drawWindow(noMoneyFinalCard, finalCard, getStatusLine(), pressF); + unlockFinalCards(4); + wait('f', 'F'); + break; + case LETTER_PERSISTENT_AND_PATIENT: + achievesPicked[7] = true; + lateEvents.push_back(event(2.0, SPECIAL_LETTER, LETTER_FINALPATH_WORK)); + break; + case LETTER_TEST_OF_KNOWLEDGE: + drawWindow(specialLetters[20], new_letter, getStatusLine(), enter); + wait(); + nextKnowledgeLetterIndex = 0; + lateEvents.push_back(event(5.0, SPECIAL_LETTER, LETTER_KNOWLEDGE_QUESTION)); + break; + case LETTER_KNOWLEDGE_QUESTION: { + if (playerPath != WORK_PATH) { + break; + } + + if (klowledgeCorrectAnswers == 5) { + /// ╨┐╤А╨╡╨╝╨╕╤П + drawWindow(specialLetters[21], new_letter, getStatusLine(), enter); + increaseMoney(400); + wait(); + klowledgeCorrectAnswers = 0; + } + if (klowledgeUncorrectAnswers >= 5) { + /// Didn't pass the knowledge check + drawWindow(specialLetters[22], new_letter, getStatusLine(), enter); + wait(); + changePath(DEFAULT_PATH); + changeSalary(0); + break; + } + if (nextKnowledgeLetterIndex > 27) { + /// Knowledge check finished + /// TODO: add a timer for this + drawWindow(specialLetters[23], new_letter, getStatusLine(), enter); + achievesPicked[6] = true; + wait(); + lateEvents.push_back(event(30.0, SPECIAL_LETTER, LETTER_BORING_WORK)); + } + else { + wstring s = knowledgeCheck[nextKnowledgeLetterIndex]; + short yes = rand() % 2 + 1; // 1 or 2 + short no = (yes == 1)?2:1; + s+=txt(yourAnswer, knowledgeCheck[nextKnowledgeLetterIndex+yes], knowledgeCheck[nextKnowledgeLetterIndex+no]); + drawWindow(s, new_letter, getStatusLine(), one_or_two); + wchar_t ch = getAnswer('1', '2');//getch(); + if ((ch - '0') == yes) { + drawModalWindow(rightAnswer, congratsMsg); + klowledgeCorrectAnswers++; + } + else { + drawModalWindow(wrongAnswer, failMsg); + klowledgeUncorrectAnswers++; + } + wait(); + nextKnowledgeLetterIndex+=3; + lateEvents.push_back(event(7.0, SPECIAL_LETTER, LETTER_KNOWLEDGE_QUESTION)); + } + } + break; + default: + break; + } + pq.delMin(); + break; + case MESSAGE: + unreadMessages+=(karma() > 0)?dt * karma()/50:0; + pq.delMin(); + lateEvents.push_back(event(2*dt, MESSAGE)); + break; + case NEWS: { + switch (e->idata) { + // Programming in the world + case 0: + showCoursesTab = true; + coursesUnlocked[1] = true; + break; + // Java programmers + case 1: + coursesUnlocked[2] = true; + break; + // Mobile development + case 2: + coursesUnlocked[3] = true; + break; + case 3: + // Your game is very popular + lateEvents.push_back(event(3.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5)); + break; + default: + break; + } + breakingNews(e->idata); + pq.delMin(); + } + break; + case NOPOPULARITY: + case INCREASEPOPULARITY: + // nop + pq.delMin(); + break; + default: + break; + } + } + // Add new messages only after checking all existing messages + for (unsigned short i = 0; i< lateEvents.size(); i++) { + event e = lateEvents[i]; + if (e.type == MESSAGE) { + initSendingMessage(); + continue; + } + else if (e.type == SPECIAL_LETTER && e.idata == LETTER_ASTRA) + astraLetterSent = true; + addTimer(e); + } + } + +void makeStory() { + switch (playerPath) { + case DEFAULT_PATH: + if (playerMoney > 200.0 && !showCoursesTab && !pq.containsType(NEWS, 0)) { // Basics of programming + addTimer(event(10.0, NEWS, 0)); + newsShowed[0] = true; + } + else if (playerMoney > 300.0 && !shitCodeDetected && !shitLettersSent[0] && coursesFinished[1]) { + addTimer(event(9.0, SPECIAL_LETTER, LETTER_SHITCODE_1)); + shitLettersSent[0] = true; + } + else if (playerMoney > 400.0 && !newsShowed[2] && coursesFinished[1] && !pq.containsType(NEWS, 2)) { // Frappy Perd + addTimer(event(9.0, NEWS, 2)); + newsShowed[2] = true; + } + else if (playerMoney > 500 && !sentBotsmannLetter && coursesFinished[1]) { // Letter from Botsmann + addTimer(event(17.0, SPECIAL_LETTER, LETTER_BOTSMANN)); + sentBotsmannLetter = true; + } + else if (playerMoney > 600.0 && !shitCodeDetected && !shitLettersSent[1] && coursesFinished[1] && !pq.containsType(SPECIAL_LETTER, LETTER_SHITCODE_1)) { + addTimer(event(13.0, SPECIAL_LETTER, LETTER_SHITCODE_2)); + shitLettersSent[1] = true; + } + else if (playerMoney > 800.0 && sentBotsmannLetter && !unnamedStudioLettersSent[0] && coursesFinished[3] && astraLetterSent && !pq.containsType(SPECIAL_LETTER, LETTER_ASTRA)) { // Startup + addTimer(event(15.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1)); + unnamedStudioLettersSent[0] = true; + } + else if (playerMoney > 1000.0 && !shitCodeDetected && !shitLettersSent[2] && coursesFinished[1] && !pq.containsType(SPECIAL_LETTER, LETTER_SHITCODE_2)) { // Send 3 letters with checking for a shit code + addTimer(event(14.0, SPECIAL_LETTER, LETTER_SHITCODE_3)); + shitLettersSent[2] = true; + } + else if (getAvailableCoursesCount() == 0 && playerMoney > 2000 && sentBotsmannLetter + && unnamedStudioLettersSent[0] && !finalCardUnlocked[0] && !pq.containsType(SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH)) { + addTimer(event(10.0, SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH)); + } + break; + case WORK_PATH: + if (playerSalary == 1200 && noPopularity && !knowledgeLetterSent && unnamedStudioLettersSent[0] && controlFreelanceAttempts && playerFreelanceAttempts <= 5) { + addTimer(event(5.0, SPECIAL_LETTER, LETTER_TEST_OF_KNOWLEDGE)); + knowledgeLetterSent = true; + } + else if (playerMoney > 500.0 && !newsShowed[2] && !pq.containsType(NEWS, 2)) { // Frappy Perd + addTimer(event(7.0, NEWS, 2)); + newsShowed[2] = true; + } + else if (playerMoney > 600.0 && !unnamedStudioLettersSent[0] && coursesFinished[3]) { // Startup + addTimer(event(14.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1)); + unnamedStudioLettersSent[0] = true; + } + break; + case STARTUP_PATH: + if (unnamedStudioLettersSent[1] && !unnamedStudioLettersSent[2] && coursesFinished[4]) { + addTimer(event(15.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_3)); + unnamedStudioLettersSent[2] = true; + } + else if (playerMoney > 600.0 && !shitCodeDetected && !shitLettersSent[1] && coursesFinished[1] && !pq.containsType(SPECIAL_LETTER, LETTER_SHITCODE_1)) { + addTimer(event(14.0, SPECIAL_LETTER, LETTER_SHITCODE_2)); + shitLettersSent[1] = true; + } + else if (playerMoney > 1000.0 && !shitCodeDetected && !shitLettersSent[2] && coursesFinished[1] && !pq.containsType(SPECIAL_LETTER, LETTER_SHITCODE_2)) { // Send 3 letters with checking for a shit code + addTimer(event(19.0, SPECIAL_LETTER, LETTER_SHITCODE_3)); + shitLettersSent[2] = true; + } + break; + } + // News about JAVA programmers + if (playerMoney > 1500.0 && !newsShowed[1] && shitLettersSent[0] && shitLettersSent[1] + && shitLettersSent[2] && !shitCodeDetected && coursesFinished[1] && !coursesUnlocked[2]) { // JAVA programmers + addTimer(event(5.0, NEWS, 1)); + newsShowed[1] = true; + } + if (!returnTo21HintShowed && finishedCoursesCount == 12 && finalCardsUnlocked == 5 && gameTime > 21.0 && allAchievesShowed && !pq.containsType(SPECIAL_LETTER, LETTER_RETURN_TO_DAY_21)) { + addTimer(event(2.0, SPECIAL_LETTER, LETTER_RETURN_TO_DAY_21)); + } + } + +// Desktop is your main screen +wstring getDesktop(int &lines) { + wstring s = L"\n\n"; + // Information about courses + if (!pq.hasCourses()) { + s += noCurrentCurses; + } + else { + // Check courses in pq + s += L" "; + s += coursesTitle; + s += L":\n"; + for(int i = 0; i< pq.n(); i++) { + event* e = pq.get(i); + if (e->type == COURSE) { + s += L" "; + if (e->idata >= 0 && e->idata <= 7) { + s += courses[e->idata]; + s += L" "; + int percent = (int)(100 * (learningTime[e->idata] - e->time) / learningTime[e->idata]); + for (int i = 0; i < 20; i++) { + if (i > 2*percent/10) + s += L" "; + else + s += pseudoEqual; + } + s += txt(L" %d%\n", percent); + } + else if (e->idata >= 'a' && e->idata <= 'e') { + s += advProgCourses[e->idata - 'a']; + s += L" "; + int percent = (int)(100 * (advProgLearningTime[e->idata-'a'] - e->time) / advProgLearningTime[e->idata - 'a']); + for (int i = 0; i < 20; i++) { + if (i > 2*percent/10) + s+=L" "; + else + s+=pseudoEqual; + } + s += txt(L" %d%\n", percent); + } + } + } + } + + for(unsigned int i = 0; i < s.length(); i++) + if (s[i] == '\n') + lines++; + return s; + } + +void mainGameCycle() { + float hintBegins = 0.0; //days + unsigned short hintIndex = 0; + + playerMoney = 100; + dt = 0.5; + int update = 0; + + // Add initial letter + initSendingMessage(); + + wstring status = playerStatus; + status += helpDesker; + int currentPath = playerPath; + + playerStartedPlaying = time(NULL); + wstring buffer, oldStatusLine; + while (1) { + // We update screen 10 times a second + if (update % 10 == 0) + increaseMoney(-dt * 1.0); // Money needs per day + + int lines = 0; + + wstring s = getDesktop(lines); + for (int i = 0; i < 11-lines; i++) + s += L"\n"; + s += L" "; + s += hintOfTheMonth; + if (gameTime > hintBegins + 30) { + hintBegins = gameTime; + hintIndex++; + if (hintIndex >= hintsCount) + hintIndex = 0; + } + // This could happen when jumping in the past + else if (hintBegins > gameTime) + hintBegins = gameTime; + + s += L" "; + s += hints[hintIndex]; + + if (playerPath != currentPath) { + currentPath = playerPath; + status=playerStatus; + if (playerPath == DEFAULT_PATH) status += helpDesker; + else if (playerPath == WORK_PATH) status += worker; + else if (playerPath == STARTUP_PATH) status += startupper; + } + // redraw screen only if necessary + wstring newStatusLine = getStatusLine(); + if (!(s == buffer) || !(oldStatusLine == newStatusLine)){ + drawWindow(s, status.c_str(), newStatusLine, getBottomLine()); + } + + buffer = s; + oldStatusLine = newStatusLine; + if (update % 10 == 0) { + checkAchieves(); + if (isGameOver()) + break; + + if (playerSalary != 0 && gameTime-playerSalaryFirstDay >= 30) { + increaseMoney(playerSalary); + changeSalaryFirstDay(playerSalaryFirstDay+30); + } + makeStory(); + checkPq(); + } + + // Some key was pressed + if (kbhit()) { + wchar_t k = getch(); + #ifdef _KOS32 + // We have to make a second call for special keys in KolibriOS + if (k == 0) + k = getch(); + #endif + if (keyPressed(k)) + return; + // Force redraw desktop + buffer = L""; + oldStatusLine = L""; + } + + if (update % 10 == 0) + gameTime += dt; + + int delayTime = 10; + #ifdef _KOS32 + __menuet__delay100(delayTime); + #else + usleep(delayTime*10000); + #endif + update++; + } + } + +// Jump in the past to a certain time +void undo(long double toTime) { + if (toTime < 0 ) + return; + + if (playerMoney < 100) { + drawModalWindow(need100ForUndo, errorMsg); + wait(); + return; + } + // Jump cost: $100 + increaseMoney(-100); + drawModalWindow(prepareForTimeJump); + wait(); + + while(!pq.empty()) + pq.delMin(); + + // Player Stats + playerTimeHops++; + playerSpentRealDays += (gameTime - playerPrevUndo); + playerPrevUndo = toTime; + + vector lateEvents; + history.prepareForUndo(); + while (!history.empty() && history.getMax().time > toTime) { + event e = history.getMax(); + switch (e.type) { + case INCREASEPOPULARITY: + increaseKarma(-e.idata, false); + break; + case NOPOPULARITY: + noPopularity = false; + break; + case CHANGEPATH: + playerPath = e.idata; // Previous path + break; + case CHANGESALARY: + playerSalary = e.idata; + break; + case CHANGESALARYFIRSTDAY: + playerSalaryFirstDay = e.idata; + break; + // Letters' time is time of sending a letter + time to end timer = time to get letter in the future + case SPECIAL_LETTER: + switch(e.idata) { + case LETTER_BOTSMANN: + sentBotsmannLetter = false; + astraLetterSent = false; + break; + case LETTER_ASTRA: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_BOTSMANN, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_ASTRA)); + break; + case LETTER_UNNAMEDSTUDIO_2: + // Unnamed letter 1 -> AngryBoss -> Unnamed letter 2 + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_ANGRYBOSS_1, e.time)) { + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_ANGRYBOSS_1, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2)); + } + // Unnamed letter 1 -> Unnamed letter 2 + else { + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2)); + } + break; + case NO_POPULARITY_HINT: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_ANGRYBOSS_1, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, NO_POPULARITY_HINT)); + break; + case LETTER_ANGRYBOSS_1: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1, toTime)) { + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_ANGRYBOSS_1)); + controlFreelanceAttempts = false; + } + break; + case LETTER_UNNAMEDSTUDIO_1: + unnamedStudioLettersSent[0] = false; + break; + case LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO)); + break; + case LETTER_UNNAMEDSTUDIO_4: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_3, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4)); + break; + case LETTER_UNNAMEDSTUDIO_3: + unnamedStudioLettersSent[2] = false; + break; + case LETTER_UNNAMEDSTUDIO_5: + if (history.containsTypeBefore(NEWS, 3, toTime) && !pq.containsType(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5)); + break; + case LETTER_FINALPATH_DEF: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_FINALPATH_DEF)); + break; + case LETTER_FINALPATH_STARTUP: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5, toTime) && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_STARTUP)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_FINALPATH_STARTUP)); + break; + case LETTER_FINALPATH_WORK: + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_PERSISTENT_AND_PATIENT, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_FINALPATH_WORK)); + break; + case LETTER_ANGRYBOSS_2: + if (e.time - toTime <= 1) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_ANGRYBOSS_2)); + break; + case LETTER_PERSISTENT_AND_PATIENT: + if (history.containsTypeBefore(SPECIAL_LETTER,LETTER_BORING_WORK, toTime)) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_PERSISTENT_AND_PATIENT)); + break; + case LETTER_BORING_WORK: + if (e.time - toTime <= 40) + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_BORING_WORK)); + break; + case LETTER_TEST_OF_KNOWLEDGE: + knowledgeLetterSent = false; + playerFreelanceAttempts = 0; + klowledgeCorrectAnswers = 0; + klowledgeUncorrectAnswers = 0; + break; + case LETTER_SHITCODE_1: + shitCodeDetected = false; + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_SHITCODE_1)); + shitLettersSent[1] = false; + shitLettersSent[2] = false; + break; + case LETTER_SHITCODE_2: + shitCodeDetected = false; + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_SHITCODE_2)); + shitLettersSent[2] = false; + break; + case LETTER_SHITCODE_3: + shitCodeDetected = false; + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_SHITCODE_3)); + break; + case LETTER_BORING_DEFAULT_PATH: + lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH)); + break; + } + break; + case NEWS: + // News about JAVA programmers + newsShowed[e.idata] = false; + if (e.idata == 3) { + // Unnamed letter 3 -> Unnamed letter 4 -> Need webmaster -> news about Java + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO, e.time)) { + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO, toTime)) + lateEvents.push_back(event(e.time - toTime, NEWS, 3)); + } + // Unnamed letter 3 -> Unnamed letter 4 -> news about Java + else { + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4, toTime)) + lateEvents.push_back(event(e.time - toTime, NEWS, 3)); + } + } + break; + case COURSE: { + // e.time - course finished + // e.idata - course number + long double courseStarted = 0; + bool courseFinished = false; + if (e.idata >='a' && e.idata <= 'e') { + courseStarted = e.time - advProgLearningTime[e.idata-'a']; + courseFinished = advProgCoursesFinished[e.idata-'a']; + } + else if(e.idata >= 0 && e.idata < (int)coursesNumber) { + courseStarted = e.time - learningTime[e.idata]; + courseFinished = coursesFinished[e.idata]; + } + + if (!courseFinished && toTime >= courseStarted) + // Player is currently taking a course + lateEvents.push_back(event(e.time - toTime, COURSE, e.idata)); + } + break; + case MESSAGE: + break; + } + history.delMax(); + } + unreadMessages = 0; + gameTime = toTime; + timeHops++; + + for (unsigned short i = 0; i< lateEvents.size(); i++) + addTimer(lateEvents[i]); + + + // Messages would not arive without this: + if (!pq.containsType(MESSAGE)) + initSendingMessage(); + + // Undo shouldn't stop during the knowledge check + if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_TEST_OF_KNOWLEDGE, toTime) + && !history.containsTypeBefore(SPECIAL_LETTER, LETTER_BORING_WORK, toTime)) { + double newTime = toTime-10; + for (int i = 0; i < history.n(); i++) { + event* e = history.get(i); + if (e->type == SPECIAL_LETTER && e->idata == LETTER_TEST_OF_KNOWLEDGE) { + newTime = e->time - 10; + break; + } + } + drawModalWindow(cantStopDuringKnowledgeCheck, errorMsg); + wait(); + undo(newTime); + } + } + +int chooseCourse() { + double p[coursesNumber]; + long double sum = 0.0; + for (unsigned int i = 0; i < coursesNumber; i++) { + if (coursesFinished[i]) + p[i] = probability[i]; + else + p[i] = 0.0; + sum += p[i]; + } + if (sum <= 0.001) + return 0; + int N = (int) (sum * 100); + int r = rand() % N; + + int n1 = (p[0] / sum) * N; + int n2 = (p[1] / sum) * N; + int n3 = (p[2] / sum) * N; + int n4 = (p[3] / sum) * N; + int n5 = (p[4] / sum) * N; + + if (r >= 0 && r < n1) + return 0; + else if (coursesFinished[1] && r >= n1 && r < n1+n2) + return 1; + else if (coursesFinished[2] && r >= n1+n2 && r < n1+n2+n3) + return 2; + else if (coursesFinished[3] && r >= n1+n2+n3 && r < n1+n2+n3+n4) + return 3; + else if (coursesFinished[4] && r >= n1+n2+n3+n4 && r < n1+n2+n3+n4+n5) + return 4; + else if (coursesFinished[5] && r >= n1+n2+n3+n4+n5 && r <= N) + return 5; + + return 0; + } diff --git a/interface.cpp b/interface.cpp new file mode 100644 index 0000000..39bde61 --- /dev/null +++ b/interface.cpp @@ -0,0 +1,345 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#include "sys.h" + +#ifndef _KOS32 +#include // wprintf() +#include +#define wprintf2 wprintf +#endif + +#include // strlen +//#include +using std::wstring; + +wchar_t getAnswer(char a1, char a2) { + while(1) { + if (kbhit()) { + wchar_t ch = getch(); + #ifdef _KOS32 + if (ch == 0) + ch = getch(); + #endif + if (ch == a1 || ch == a2) + return ch; + } + } + } + +wchar_t getAnswer(char a1, char a2, char a3) { + while(1) { + if (kbhit()) { + wchar_t ch = getch(); + #ifdef _KOS32 + if (ch == 0) + ch = getch(); + #endif + if (ch == a1 || ch == a2 || ch == a3) + return ch; + } + } + } + +wchar_t getAnswer(char a1, char a2, char a3, char a4) { + while(1) { + if (kbhit()) { + wchar_t ch = getch(); + #ifdef _KOS32 + if (ch == 0) + ch = getch(); + #endif + if (ch == a1 || ch == a2 || ch == a3 || ch == a4) + return ch; + } + } + } + +void wait(wchar_t a = ENTER_KEY, char b = ENTER_KEY) { + getAnswer(a, b); + } + +wchar_t getKey() { +#ifdef _KOS32 + while(1) { + if (kbhit()) { + wchar_t ch = getch(); + if (ch == 0) + ch = getch(); + return ch; + } + } +#else + return getch(); +#endif + } + +void blankline(int line, int width) { + wprintf2(L"║ "); + consoleGoto(line, width); + wprintf2(L"║\n"); + } + +void drawHBorder(int line, int left, int right, wchar_t l = L'╔', + wchar_t c = L'═', wchar_t r = L'╗', wchar_t e = '\n') { + consoleGoto(line, left); + wprintf2(L"%lc", l); + for (int i = 0; i< right - left; i++) + wprintf2(L"%lc", c); + consoleGoto(line, right); + wprintf2(L"%lc%lc", r, e); + } + +void drawStringLine(int line, int left, int right, const wchar_t* str, + int pos = -1, wchar_t border = L'║', bool shadow = false) { + consoleGoto(line, left); + if (pos == -1) + wprintf2(L"%lc %ls", border, str); + else { + wprintf2(L"%lc ", border); + consoleGoto(line, pos); + wprintf2(L"%ls", str); + } + consoleGoto(line, right); + if (shadow) + wprintf2(L"%lc█", border); + else + wprintf2(L"%lc\n", border); + } + +void drawModalWindow(const wchar_t* content, const wchar_t* title, const wchar_t* buttons) { + cls(); + int winWidth, winHeight; + getWinWH(winWidth, winHeight); + int lines = winHeight/3; + int msgWidth = wcslen(content)+2; + + if (msgWidth >= winWidth-4) + msgWidth = winWidth-5; + int msgLeft = winWidth/2 - msgWidth/2; + int msgRight = winWidth/2 + msgWidth/2+1; + if (msgWidth%2!=0) + msgRight++; + + int titleWidth = 0; + if (title != 0) + titleWidth = wcslen(title)+2; + + drawHBorder(lines, msgLeft, msgRight,L'╒',L'═', L'╕'); + + // title + if (title != 0) { + consoleGoto(lines, msgLeft+msgWidth/2-titleWidth/2); + wprintf2(L" %ls ", title); + } + + lines++; + drawHBorder(lines, msgLeft, msgRight, L'│',L' ',L'│',L'█'); + lines++; + + //======Splitting content into pieces========================= + int maxTextWidth = msgRight-msgLeft; // 2 borders + 2 spaces + int start = 0; + int len = wcslen(content); + + for (int i = 0; i < len; i++) { + if (content[i] == '\n') { + wstring tmp = content; + wstring t1 = tmp.substr(start, i-start+1); + drawStringLine(lines, msgLeft, msgRight, t1.c_str(), -1, L'│', true); + + start = i+1; + lines++; + } + else if (i - start >= maxTextWidth-3) { + wstring tmp = content; + wstring t1 = tmp.substr(start, i-start); + t1+=L"\n"; + drawStringLine(lines, msgLeft, msgRight, t1.c_str(), -1, L'│', true); + start = i; + lines++; + } + } + if (start != len) { + wstring tmp = content; + wstring t1 = tmp.substr(start, len-start); + drawStringLine(lines, msgLeft, msgRight, t1.c_str(), -1, L'│', true); + lines++; + } + //=============================== + drawHBorder(lines, msgLeft, msgRight, L'│',L' ',L'│',L'█'); + lines++; + + consoleGoto(lines, msgLeft); + wprintf2(L"│"); + if (!buttons) { + for (int q = 0; q< msgWidth/2-4;q++) + wprintf2(L" "); + wprintf2(L"[Enter]"); + for (int i = 0; i< msgWidth/2-2; i++) + wprintf2(L" "); + } + else { + int buttonsLen = wcslen(buttons); + for (int i = 0; i < msgWidth/2-buttonsLen/2; i++) + wprintf2(L" "); + wprintf2(L"%ls", buttons); + } + consoleGoto(lines, msgRight); + wprintf2(L"│█"); + lines++; + + drawHBorder(lines, msgLeft, msgRight,L'└',L'─', L'┘',L'█'); + lines++; + + consoleGoto(lines, msgLeft+1); + for (int i = 0; i< msgWidth+2; i++) + wprintf2(L"▀"); + wprintf2(L" "); + } + +void drawWindow(wstring content, const wchar_t* title, wstring topline , wstring bottomline, bool usePagesForLongText = false) { + cls(); + int winWidth, winHeight; + getWinWH(winWidth, winHeight); + +#if defined(_WIN32) && !defined(_KOS32) + int deltaX = 0; +#else + int deltaX = 1; +#endif + + // Total lines +#if defined(_WIN32) && !defined(_KOS32) + int lines = 0; +#else + int lines = 1; +#endif + + // Top + drawHBorder(lines, deltaX, winWidth); + lines++; + + if (topline != L"") { + drawStringLine(lines, deltaX, winWidth, topline.c_str()); + lines++; + } + // Title + if (title != 0) { + if (topline != L"") { + blankline(lines, winWidth); + lines++; + } + drawStringLine(lines, deltaX, winWidth, title, (int)(winWidth/2.0-wcslen(title)/2.0)); + lines++; + blankline(lines, winWidth); + lines++; + } + + //==========Splitting content into pieces===================== + int maxTextWidth = winWidth-4; // 2 borders + 2 spaces + int start = 0; + unsigned int i = 0; + // Let's split content into pieces if it too long or doesn't fit the screen + while (i < content.length()) { + if (usePagesForLongText && lines > winHeight-3) { + consoleGoto(lines, 0); + blankline(lines, winWidth); + lines++; + drawStringLine(lines, deltaX, winWidth, L"Нажмите [Enter] для продолжения..."); + // Bottom line + drawHBorder(winHeight, deltaX, winWidth,L'╚',L'═',L'╝'); + wait(); + // Print next page + cls(); + lines = 0; + // Top line + drawHBorder(lines, deltaX, winWidth); + #if defined(_WIN32) && !defined(_KOS32) + lines = 1; + #else + lines = 2; + #endif + } + // Use tag to place text in the center + if (content[i] == '<') { + // Checking for the tag. tag should be prepended with '\n' + if (i+2 < content.length()) { + if (content[i+1] == 'c' && content[i+2] == '>') { + // Tag found. Looking for the end of a wstring + int cLineEnd = content.length()-1; + for (unsigned int j = i; j < content.length(); j++) + if (content[j] == '\n') { + cLineEnd = j; + break; + } + int cLineWidth = cLineEnd - i-1; + drawStringLine(lines, deltaX, winWidth, + content.substr(i+3, cLineWidth-2).c_str(), winWidth/2-cLineWidth/2); + i = cLineEnd+1; + start = i; + lines++; + continue; + } + } + } + else if (content[i] == '\n') { + consoleGoto(lines, 0); + wstring t1 = content.substr(start, i-start+1); + drawStringLine(lines, deltaX, winWidth, t1.c_str()); + start = i+1; + lines++; + } + else if ((int)i - start >= maxTextWidth-1) { + consoleGoto(lines, 0); + wstring t1 = content.substr(start, i-start); + t1+=L"\n"; + drawStringLine(lines, deltaX, winWidth, t1.c_str()); + start = i; + lines++; + } + i++; + } + if (start <= (int)content.length()) { + consoleGoto(lines, 0); + wstring t1 = content.substr(start, content.length()-start); + drawStringLine(lines, deltaX, winWidth, t1.c_str()); + lines++; + } + //=============================== + + for (int i = lines; i< winHeight; i++) { + blankline(lines, winWidth); + lines++; + } + + if (!bottomline.empty()) { + ///Fix for long bottomline + if ((int)bottomline.length() >= winWidth) { + drawStringLine(winHeight-2, deltaX, winWidth, bottomline.substr(0, winWidth-4).c_str()); + drawStringLine(winHeight-1, deltaX, winWidth, bottomline.substr(winWidth-4).c_str()); + } + else { + drawStringLine(winHeight-1, deltaX, winWidth, bottomline.c_str()); + } + } + // Bottom + drawHBorder(winHeight, deltaX, winWidth,L'╚',L'═',L'╝'); + } diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..3271196 --- /dev/null +++ b/interface.h @@ -0,0 +1,53 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#ifndef H_INTERFACE +#define H_INTERFACE +#include + +wchar_t getAnswer(char a1, char a2); +wchar_t getAnswer(char a1, char a2, char a3); +wchar_t getAnswer(char a1, char a2, char a3, char a4); +wchar_t getKey(); +void clearBuffer(); + +#ifdef _KOS32 +using std::wstring; +wstring itos(int n); +wstring txt(string s, int d); +wstring txt(string s, string s2); +wstring txt(string s, string s2, string s3); + +wstring txt(string s, wchar_t c); +wstring txt(string s, wchar_t ch, string s2); +wstring txt(string s, wchar_t ch, string s2, char ch2); + +wstring txt(string s, int d1, int d2); +wstring txt(string s, int d1, int d2, int d3); +wstring txt(string s, int d1, int d2, int d3, int d4); + +#else +std::wstring txt(const wchar_t* s, ...); +#endif + +void wait(wchar_t a = ENTER_KEY, char b = ENTER_KEY); +void drawWindow(std::wstring content, const wchar_t* title = 0, std::wstring topline = L"", std::wstring bottomline = L"", bool usePagesForLongText = false); +void drawModalWindow(const wchar_t* content , const wchar_t* title = 0, const wchar_t* buttons = 0); +#endif diff --git a/kos_vector.h b/kos_vector.h new file mode 100644 index 0000000..1e5d0e1 --- /dev/null +++ b/kos_vector.h @@ -0,0 +1,91 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +* +* +* This file contains a part of file "/include/vector" from menuetlibc +* adapted to the developer's needs. +* Changes: +* 1. "*__cdecl" replaced with "* __attribute__((cdecl))" in order +* to compile a C++ project. +* 2. Added front() methods with the following code: +* T& front() {return data[0];} +* 3. Code reformatted +* +********************************************************************/ + +#ifndef KOS_VECTOR_H +#define KOS_VECTOR_H + +extern void * __attribute__((cdecl)) operator new(size_t); +inline void * __attribute__((cdecl)) operator new(size_t, void *_P) { + return (_P); + } + +template class vector21 { + unsigned length; + unsigned allocated; + T* data; + public: + typedef unsigned size_type; + typedef T* iterator; + vector21():length(0),allocated(0),data(NULL) {} + ~vector21() {for (unsigned i=length;i--;)data[i].~T();free(data);} + unsigned size() const {return length;} + void clear() {length=0;} + T& operator[](unsigned pos) {return data[pos];} + T* begin() {return data;} + T* end() {return data+length;} + void push_back(const T& x) { + if (length==allocated){ + allocated+=16; + data=(T*)realloc(data,allocated*sizeof(T)); + } + new (data+length++) T(x); + } + bool empty() const {return length==0;} + void pop_back() {data[--length].~T();} + T& back() {return data[length-1];} + T& front() {return data[0];} + iterator erase(iterator it) { + T* a=it; + while (++a != data+length) { + a[-1] = *a; + } + length--; + return it; + } + /*iterator*/T* insert(iterator where, const T& what = T()) { + int z=where-data; + if (length==allocated) { + allocated+=16; + data=(T*)realloc(data,allocated*sizeof(T)); + } + T* a=data+length; + T* b=data+z; + length++; + while (a != b) { + *a = a[-1]; + --a; + } + *a = what; + return /*iterator*/a; + } + }; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..ee30742 --- /dev/null +++ b/main.cpp @@ -0,0 +1,45 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#include "sys.h" + +#ifndef _KOS32 +#include // wprintf() +#define wprintf2 printf +#endif + +#include // srand() +#include // time() + +int showMainMenu(); + +int main() { + initConsole(); + hideCursor(); + srand(time(NULL)); + showMainMenu(); + showCursor(); +#ifdef _KOS32 + // Close console window and kill the main thread + con_exit(true); + kol_exit(); +#endif + return 0; + } diff --git a/pq.h b/pq.h new file mode 100644 index 0000000..828fbeb --- /dev/null +++ b/pq.h @@ -0,0 +1,204 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#ifndef H_PQ +#define H_PQ + +#define LETTER_RETURN_TO_DAY_21 100001 +#define LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO 100002 +#define LETTER_BORING_WORK 100003 +#define LETTER_PERSISTENT_AND_PATIENT 100004 +#define LETTER_BORING_DEFAULT_PATH 100005 +#define LETTER_TEST_OF_KNOWLEDGE 100006 +#define LETTER_KNOWLEDGE_QUESTION 100007 + +#ifdef _KOS32 + #include "kos_vector.h" + #define vector vector21 +#else + #include + #include // std::sort + #define wprintf2 printf + using std::vector; +#endif + +enum Letter { LETTER_SHITCODE_1 = 0, LETTER_SHITCODE_2, LETTER_SHITCODE_3, + LETTER_BOTSMANN, LETTER_ASTRA, LETTER_UNNAMEDSTUDIO_1, LETTER_UNNAMEDSTUDIO_2, + LETTER_UNNAMEDSTUDIO_3, LETTER_UNNAMEDSTUDIO_4, LETTER_UNNAMEDSTUDIO_5, + LETTER_ANGRYBOSS_1, LETTER_ANGRYBOSS_2, NO_POPULARITY_HINT, LETTER_FINALPATH_DEF, + LETTER_FINALPATH_STARTUP, LETTER_FINALPATH_WORK, LETTER_FINALPATH_NOMONEY, LETTER_FINALPATH_NOPOPULARITY}; +enum EventType { COURSE = 1000, SPECIAL_LETTER, MESSAGE, NEWS, INCREASEPOPULARITY, + CHANGEPATH, CHANGESALARY, CHANGESALARYFIRSTDAY, NOPOPULARITY}; + +class event { + public: + double time; + EventType type; + int idata; + event() : time(0.0), idata(-1) {} + event(double t,EventType ty, int data = -1) : time(t), type(ty), idata(data) {} + bool operator>(const event a) const { + return this->time > a.time; + } + bool operator<(const event ev) const { + return time < ev.time; + } + }; + +#ifdef _KOS32 +static void exch (event* A, event* B) { + event t = *A; + *A = *B; + *B = t; + } +#endif + +static void sort21(vector* v, bool desc = false) { +#ifdef _KOS32 + // Sorry for the bubble sort + for (unsigned int i = 0; i < v->size()-1; i++) + for (unsigned int j = 0; j < v->size()-i-1; j++) { + if (desc && (*v)[j] < (*v)[j+1]) + exch(&((*v)[j]), &(*v)[j+1]); + else if (!desc && (*v)[j] > (*v)[j+1]) + exch(&(*v)[j], &(*v)[j+1]); + } +#else + if (desc) + std::sort(v->begin(), v->end(), std::greater()); + else + std::sort(v->begin(), v->end()); +#endif + } + +class PQ3 { + private: + vector v; + bool sorted; + public: + void sort() { + sort21(&v, true); + sorted = true; + } + PQ3() : sorted(false) {} + int n() { + return v.size(); + } + bool empty() const { + return v.empty(); + } + void insert(event item) { + v.push_back(item); + sorted = false; + } + event delMin() { + if (empty()) + return event(); + if (!sorted) + sort(); + event e = v.back(); + v.pop_back(); + return e; + } + event* getMin() { + if (empty()) + return 0; + if (!sorted) + sort(); + return &v.back(); + } + event* get(int i) { + if (empty() || i >= (int)v.size()) + return 0; + if (!sorted) + sort(); + return &v[i]; + } + bool hasCourse(int c) { + for(unsigned int i = 0; i < v.size(); i++) + if (v[i].type == COURSE && v[i].idata == c) + return true; + return false; + } + bool hasCourses() { + for(unsigned int i = 0; i < v.size(); i++) + if (v[i].type == COURSE) + return true; + return false; + } + bool containsType(int et) { + for (int i = v.size()-1; i >= 0; i--) + if (v[i].type == et) + return true; + return false; + } + bool containsType(int et, int data) { + for (int i = v.size()-1; i >= 0; i--) + if (v[i].type == et && v[i].idata == data) + return true; + return false; + } + }; + + +class Undo2 { +private: + vector v; +public: + Undo2() {} + int n() { + return v.size(); + } + bool empty() const { + return v.empty(); + } + void insert(event item) { + if (item.type == MESSAGE) + return; + v.push_back(item); + } + event delMax() { + if (empty()) + return event(); + event e = v.back(); + v.pop_back(); + return e; + } + event getMax() { + if (empty()) + return event(); + return v.back(); + } + event* get(int i) { + if (empty() || i >= (int)v.size()) + return 0; + return &v[i]; + } + void prepareForUndo() { + sort21(&v, false); + } + bool containsTypeBefore(EventType et, int data, long double t) { + for (unsigned int i = 0; i < v.size(); i++) + if (v[i].type == et && v[i].idata == data && v[i].time <= t) + return true; + return false; + } + }; +#endif diff --git a/strings.h b/strings.h new file mode 100644 index 0000000..230cf5f --- /dev/null +++ b/strings.h @@ -0,0 +1,540 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#ifndef H_STRINGS +#define H_STRINGS + +/************************************************************************* +* Messages from friends +*************************************************************************/ + +const unsigned int levelYesAnswerNumber = 10; +const wchar_t* levelYesAnswer[levelYesAnswerNumber] = { + L"Выезжаю!", + L"Да, сейчас удалённо сделаю.", + L"Называй адрес.", + L"Уже еду.", + L"Кофе и печеньки будут? Уже еду!", + L"Конечно!", + L"Для тебя - в любое время", + L"Я как раз свободен.", + L"За минут десять сделаю.", + L"Без проблем." + }; + +const unsigned int levelNoAnswerNumber = 10; +const wchar_t* levelNoAnswer[levelNoAnswerNumber] = { + L"Прости, сегодня не смогу.", + L"Не, я занят.", + L"Я на работе безвылазно, позвони-ка через месяц.", + L"Как-нибудь в другой раз.", + L"Я по средам не эникействую.", + L"Я такого не умею.", + L"Вы ошиблись номером :D", + L"Ты кто вообще?", + L"Не могу, извини.", + L"Нет." + }; + + +const unsigned int level1TasksNumber = 6; +const wchar_t* level1Task [level1TasksNumber] = { + L"Привет! Можешь посмотреть мой компьютер? Кажется, у меня не работает %ls.", + L"Нигде не могу тебя найти. У меня тут %ls барахлит, переустановишь винду?", + L"Катастрофа! Работа встала! Не работает %ls. Поможешь?", + L"Слушай, у моего друга не работает %ls, не знаю, что делать. Ты свободен?", + L"Странно, не запускается %ls, всё перепробовал - ничего не помогает.", + L"Дароф! Я тут кофе на комп пролил, теперь %ls не работает. С меня кофе и печеньки." + }; + +const unsigned int level1TaskWordNumber = 11; +const wchar_t* level1TaskWords[level1TaskWordNumber] = { + L"аудиокарта", + L"блок питания", + L"браузер", + L"видеокарта", + L"жесткий диск", + L"клавиатура", + L"модем", + L"монитор", + L"мышь", + L"один наушник", + L"проектор" + }; + +const unsigned short programmingTaskNumber = 5; +const wchar_t* programmingTasks[programmingTaskNumber] = { + L"Привет! Мне тут лабораторную надо написать на Pascal, не знаю, к кому обратиться. Умеешь?", + L"В универе запарили с заданиями на Basic, сделаешь?", + L"Нужна твоя помощь, завтра сдавать лабораторную на Turbo Pascal, а я впервые слышу, что это. Выручай!", + L"Дали на работе задание... взял, не посмотрел, а там надо писать на QBASIC, а я не умею. Посоветуешь кого?", + L"Слышал, ты пишешь на Pascal? У нас тут пара проектов недописанных, я ответственного человека ищу. Работы - на пару часов." + }; + +const unsigned short webProgrammingTaskNumber = 4; +const wchar_t* webProgrammingTasks[webProgrammingTaskNumber] = { + L"Привет, ты сайтами-визитками занимаешься? Дело есть.", + L"Слушай, нашей компании нужен сайт по дешёвке. Просто страница с информацией о компании. Это же просто для тебя, да?", + L"В нашей соц. сети пару фишек доделать нужно, мне тебя порекомендовали. Напиши, как освободишься.", + L"Хай, мы тут большой сайт-портал пишем, нам срочно нужен кодер на пару дней. Гоу?", + }; + +const unsigned short systemProgrammingTaskNumber = 3; +const wchar_t* systemProgrammingTask[systemProgrammingTaskNumber] = { + L"Слышал об Arduino? Мы тут на асме пишем, пара заданий есть.", + L"Мне сказали, ты на асме пишешь? Давно фрилансера ищем, работа с микроконтроллерами, думаю, тебе понравится.", + L"Тут игру для конкурса под KolibriOS надо написать, а я асм не знаю. Напишешь?" + }; + +const unsigned short adminTasksNumber = 4; +const wchar_t* adminTasks[adminTasksNumber] = { + L"Сервера новые завезли, настраивать некому. Оплатим вдвойне! Перезвони.", + L"А ты Apache + nginx сможешь поставить и настроить? Не спрашивай, зачем :).", + L"Не поверишь, тут машину привезли на Windows Server 2003, что делать с ней, не знаем. Выручай!", + L"Простите, как мне пропатчить KDE2 под FreeBSD?" + }; + +const unsigned short mobileTaskNumber = 3; +const wchar_t* mobileTasks[mobileTaskNumber] = { + L"Мы тут разработку на Swift продвигаем, можешь пару демок написать?", + L"Хотим написать мобильное приложение на Java, говорят, сейчас это круто. Ты умеешь?", + L"Тут никто на C++ не хочет нативные приложения под Android делать, ты возьмешься?" + }; + +/************************************************************************* +* Courses +*************************************************************************/ + +const unsigned int coursesNumber = 8; +const wchar_t* courses[coursesNumber] = { + L"Эникейство", + L"Азы программирования", + L"Продвинутое программирование:", + L"Разработка мобильных приложений", + L"Основы дизайна", + L"Создание сайтов", + L"Системное программирование", + L"Администрирование серверов", + }; +bool coursesUnlocked[coursesNumber] = {1, 0, 0, 0, 0, 0, 0, 0}; +const unsigned short advancedProgrammingCourses = 5; +wchar_t* advProgCourses[advancedProgrammingCourses] = { + L"C++", + L"Java", + L"Assembler", + L"C#", + L"Delphi" + }; + +bool coursesFinished[coursesNumber] = {1, 0, 0, 0, 0, 0, 0, 0}; +bool advProgCoursesFinished[advancedProgrammingCourses] = {0, 0, 0, 0, 0}; +unsigned int learningTime[coursesNumber] = {0, 10, 80, 30, 45, 35, 45, 50}; // in days +unsigned int advProgLearningTime[advancedProgrammingCourses] = { 10, 10, 15, 10, 10}; // in days +int learningCost[coursesNumber] = {0, 100, 125, 100, 125, 120, 130, 120}; +int advProgLearningCost[advancedProgrammingCourses] = {125, 120, 150, 123, 100}; + +unsigned short finishedCoursesCount = 1; + +/************************************************************************* +* Probability of getting a message from friend related to course 0, +* 1, 2, etc. (courses). Prob. of getting a simple work is higher +* than getting a hard one. +*************************************************************************/ +double probability[coursesNumber] = { 0.7, 0.5, 0.25, 0.6, 0.25, 0.4, 0.3, 0.4}; + +int taskReward[coursesNumber] = {2, 5, 10, 13, 19, 25, 30, 35}; +int taskCount[coursesNumber] = {level1TasksNumber, programmingTaskNumber, + programmingTaskNumber, mobileTaskNumber, mobileTaskNumber, + webProgrammingTaskNumber, systemProgrammingTaskNumber, adminTasksNumber}; + +/************************************************************************* +* News +*************************************************************************/ + +const unsigned short newsCount = 4; +const wchar_t* news[newsCount] = { + L"Программирование начинает захватывать мир! Скорее начните учиться программированию!\n", + L"По информации с hh.ru вскоре повысится спрос на Java - программистов\n", + L"Невероятно! Инди-игра одного французского разработчика Frappy Perd побила все рекорды и принесла ему $50 000 в первый же день! Наверное, все сейчас кинутся писать свои мобильные игры.\n", + L"Однозначно, сейчас расцвет инди-игр. Очередная успешная игра, на этот раз от Unnamed Studio, на первом месте в топе игр. У игры уже миллионы фанатов и поклонников, люди просто сходят с ума!\n" + }; +bool newsShowed[newsCount] = {0, 0, 0, 0}; + +/************************************************************************* +* Special letters +*************************************************************************/ + +const unsigned short specialLetterCount = 30; +const wchar_t* specialLetters[specialLetterCount] = { + L"Привет. У нас тут мегакрутой проект на C++ и Java, никто не берется, сделаешь?", + L"Что это такое? Да лучше бы совсем не брался. Это же говнокод! Иди-ка поучи C++, а заодно и Java.", + L"Я слышал, ты программированием на Assembler занимаешься? Тут суперсложный микроконтроллер есть один, никто не берется. Возьмешься?", + L"И это ты называешь выполненной работой? Да мой шестилетний сын на Assembler лучше пишет, чем ты! Купи книгу по Asm, не позорься.", + L"Привет! К нам тут заказ поступил, нужно ядро базы данных на C# написать. Мы сами не можем - очень сложный проект. Умеешь?", + L"Эээ, да ты совсем, оказывается, программировать не умеешь. Пройди пару курсов что-ли, не позорься.", + L"Письмо от ООО 'Botsmann'\n Здравствуйте! Нам посоветовали Вас, как специалиста в области программирования. Мы занимаемся разработкой мобильных приложений, и сейчас делим рынок с ООО 'Астра'. Для захвата рынка нам нужны интересные люди и квалифицированные специалисты. Мы предлагаем интересную работу и оклад $800 в месяц. Подумайте, мы свяжемся с Вами позже.", + L"Письмо от ООО 'Астра'\n Добрый день! Мы - команда разработчиков мобильных приложений, у нас есть гениальные идеи и мы хотим изменить мир! Мы ищем талантливых разработчиков и предлагаем $1000 в месяц. Нам нужен твой срочный ответ, ты с нами?", + L"Письмо от ООО 'Botsmann'\n К сожалению, вы не оправдали наших надежд. Это наши HR-специалисты отправили вам письмо от имени наших конкурентов. Нам не нужны люди, готовые уйти в другую компанию из-за $200.", + L"Письмо от ООО 'Botsmann'\n Здравствуйте! Извините, это наши HR-специалисты отправили Вам письмо от имени наших конкурентов в качестве проверки. Мы решили, что вы нам подходите! Если Вы еще не писали под мобильные платформы, мы Вас научим.", + L"Хэй, привет! Мы - молодая команда под названием Unnamed Studio и мы очень любим играть, а также создавать игры. Мы видели твои работы, они реально крутые! В отличие от крупных компаний у нас меньше контроля и больше возможностей для самореализации. Мы всё рассчитали, и уже скинулись. С тебя пока денег брать не будем, оставим на форс-мажор. Но у нас же всё будет гладко, верно? Больше трат не предвидится. Сообщи, если согласен. ", +/*11*/ L"Письмо от ООО 'Botsmann'\n Слушай, я терпел всё это время, но всему есть предел. Ты отвлекаешься от основной работы на фриланс и подработку. Ты определись, что тебе больше по душе и бросай этот фриланс. Работу надо делать качественно. \nСкучно на работе? Я подниму оклад до $1200 и пошлю тебя на дополнительные курсы. Ну, продолжаем работать?", + L"Письмо от ООО 'Botsmann'\n Я тебя предупреждал: концентрируйся на работе! Думал, я не узнаю о твоей подработке?! Ты уволен!", + L"Мы рады, что ты присоединился к нам, теперь ты член команды Unnamed Studio! Сейчас работа пойдёт раза в три быстрее! Раз ты с нами, надо бы научиться рисовать. ", + L"Твои работы просто впечатляют! Это не просто мои слова, все ребята в Unnamed Studio так думают! ...знаешь, у нас тут проблема. Наш второй программист пропил часть денег из фонда, до релиза никак не хватит теперь :(. Мы собираем по $%d. Обещаю, это в последний раз! За идею!", + L"Письмо от Unnamed Studio:\n Катастрофа! Через 40 дней релиз, а у нас сайта до сих пор нет! Мы изначально как-то и не думали об этом, только сейчас узнали, что это - важная часть рекламы. ", + L"Письмо от Unnamed Studio:\n Что? Переоценил себя? Ты говорил, успеешь сделать сайт вовремя! Из-за тебя всё пошло к чертям, мы могли бы выручить в 1000 раз больше! Вот твои $1000 из общего дохода и убирайся!", + L"Письмо от Unnamed Studio:\n Чувак, ты смотрел новости? ТЫ ЭТО ВИДЕЛ?! Да нашу игру расхватывают, как горячие пирожки! Это успех! Это слава! Я тут до потолка прыгаю! Перевожу тебе $200 000 - твоя доля. Теперь проект завершён, мы все в отпуска! И тебе надо отдохнуть. Лови кайф.", + L"Скучно? А ты что думал! На работе сидят и зарабатывают деньги, это тебе не экшен. Если нужны деньги - продолжай работать, но дальше уже ничего интересного не будет. Миллион ты так, конечно, не заработаешь, но подкопить денег на пару прыжков во времени можно.", + L"Скучно, да? Конечно, от самых интересных предложений о работе ты отказался, а новых уже не будет. Что, нереалистично? В жизни не всем каждый день предлагают работу. Да, будешь перебиваться звонками знакомых и переустанавливать им винду. И курсов новых тоже не будет, зачем они тебе, если всё равно ты только ЭНИКЕЙЩИК. Эх, было бы круто, если бы можно было всё отмотать назад и однажды сделать другой выбор!", + L"Письмо от ООО 'Botsmann'\n Здравствуй. Совет директоров решил тратить меньше средств на дополнительное обучение новых работников. Также сокращаем расходы на проверку знаний опытных сотрудников. Решили объединить это всё в одно. Ты знаешь, мы сейчас буквально на всём экономим. В общем, решено, что 'старшее' поколение будет передавать опыт 'младшему'. Ты уже успел зарекомендовать себя как опытный сотрудник, так что тебе поручается напутствовать Junior'ов и отвечать на все их глупые вопросы. Не бесплатно, конечно. За каждые 5 правильных ответов мы начисляем премию. А если наберешь 5 неправильных, то считаем, что проверку знаний не прошёл, и тогда место освобождаешь. Я в тебе не сомневаюсь, просто должен был тебе рассказать о новом решении. Я-то знаю, что ты справишься!", + L"Письмо от ООО 'Botsmann'\n Как и обещал, твоя премия за правильные ответы: $400", + L"Письмо от ООО 'Botsmann'\n Ты не прошёл проверку знаний. Ну что же ты так, я в тебе не сомневался! А ты, оказывается, просто место на работе просиживаешь. Хоть ты мне и понравился, но уволить я тебя обязан. Без обид.", + L"Письмо от ООО 'Botsmann'\n Как я и думал, эту странную процедуру проверки знаний отменили: Junior'ы совсем обнаглели, сами ничему учиться не хотят, только спрашивают у старших. Платить опытным сотрудникам за советы оказалось невыгодно. В общем, теперь можешь спокойно работать, никто тебя отвлекать не будет.", + L"Слушай, да это же просто идеальный код! Не поверил бы, что ты за %d дней научился этому! У тебя сколько часов в сутках? Держи свои $%d, заслужил.", + L"Письмо от Andrew Johnson \n Хэй, чувак, я из соседнего отдела. Мне босс тоже устраивал проверку и ты правильно делаешь, что не фрилансишь на стороне. Но, поверь, твой телефон на прослушке, а там всё ещё есть непрочитанные письма. Было бы лучше избавиться от них, а то босс может подумать, что ты всё еще поддерживаешь контакт с другими заказчиками.", +/*un2 var1*/ L"Ты понимаешь, нас мало, все загружены... Денег нет, поэтому помочь можем только советом: было бы неплохо пройти какой-нибудь курс об основах дизайна.", +/*un2 var2*/ L"Что? Уже умеешь? Как же нам с тобой повезло! Окей, тогда ты знаешь, что делать. Мы не будем ограничивать твою фантазию.", +/*un4 var1*/ L"Мы решили скинуться по $%d на веб-мастера, ведь все наши прогеры заняты.", +/*un4 var2*/ L"По тебе сразу видно, что ты толковый специалист. Думаю, создание сайтов для тебя - сущий пустяк." + }; + +bool unnamedStudioLettersSent[3] = {0,0,0,}; + +bool knowledgeLetterSent = false; +short nextKnowledgeLetterIndex = 0; +short klowledgeCorrectAnswers = 0; +short klowledgeUncorrectAnswers = 0; +const unsigned short knowledgeQuestionsCount = 10*3; +const wchar_t* knowledgeCheck[knowledgeQuestionsCount] = { + L"Я тут вообще новичок, какой язык программирования лучше, C++ или Delphi?", L"Это зависит от поставленной задачи", L"Конечно C++, на Delphi уже никто не пишет", + L"Мне тут один из программистов сказал, что шутка про UDP не дошла...что он имел в виду?", L"UDP - это протокол, не гарантирующий доставку пакетов.", L"UDP - надёжный протокол, гарантирующий доставку данных, в этом и шутка.", + L"Мне задание дали, сайт на C++ написать, это вообще возможно или они просто издеваются над новичком?", L"В принципе, не важно, на какой языке генерировать HTML-страницы.", L"Нет, это прикол. Сайты пишут на PHP, Python, ASP.Net и других", + L"Мы тут поспорили, я говорю, что с использованием JavaScipt сайты пишутся, а не на Java. Java - для мобильных приложений. Верно?", L"Ты проиграл.", L"Ну да, почти все приложения для твоего Андроида на Java написаны ведь.", + L"Коллега на перекуре сказал, что он до работы в нашей компании зарабатывал на opensource-проектах. Это возможно?",L"Да, он мог заниматься платной поддержкой.", L"Ха, повёлся!. Opensource-проекты же бесплатные.", + L"Разгребаю код одного проекта под iOS. Никак не пойму, в чем разница между #include и #import?", L"#import гарантирует, что файл будет включён один раз, в отличие от #include", L"Ты путаешь, #include используется в коде на C, а 'import' - в ассемблерных вставках.", + L"Собираюсь писать проект на Ruby on Rails. Слышал, этот язык интерпретируемый и высокоуровневый, это так?",L"Хм... Да это и не язык вовсе, а фреймворк.", L"Нет, это компилируемый язык, а не интерпретируемый.", + L"Я теперь буду писать под Linux, можно его поставить на NTFS-раздел? Эмуляцию live cd не предлагать.", L"Нет, ядро с него загрузиться не сможет.", L"В принципе, да, но нужно патчить ядро, ставить дополнительные утилиты... оно тебе надо?", + L"Вчера позвонили из компании конкурентов, сказали, что пишут на обрезанном PHP без ООП и eval(). Предлагали работу, я отказал. Правильно сделал? ", L"Поздравляю, это была компания 'Вконтакте'.", L"Правильно, на этом же невозможно ничего стоящего написать", + L"Захожу на удалённый сервер, нужно узнать, сколько строк в файле log.txt. А там, как назло, Linux и только консоль...Что вводить?", L"wc -l log.txt", L"ls -l log.txt" + }; + +/************************************************************************* +* Tips for users when waiting for an input +*************************************************************************/ + +const wchar_t* ae_advanced_courses = L"Что вы вводите? Для того, чтобы выбрать один из 'продвинутых' курсов, нажмите клавиши 'a'-'e'"; +const wchar_t* bottomLineCourses = L"Курсы[C] "; +const wchar_t* bottomLineMsgAchieves = L"Сообщения[M] Достижения[A] Прыжок в прошлое[U] "; +const wchar_t* bottomLineSpeedAndExit = L"Скорость[S] Выйти[Esc]\n"; +const wchar_t* changeSpeedButtons = L"Изменить[+][-] Принять[Y]"; +const wchar_t* enter = L"Нажмите [Enter] для продолжения..."; +const wchar_t* escToBreakReading = L", прервать чтение [Esc]"; +const wchar_t* number_or_enter = L"Введите номер курса или [Enter] чтобы выйти"; +const wchar_t* one_or_two = L"Варианты ответов: [1] или [2]"; +const wchar_t* one_two_or_three = L"Варианты ответов: [1], [2] или [3]"; +const wchar_t* pressF = L"Нажмите [F] для продолжения..."; +const wchar_t* pressP = L"Нажмите [P] для продолжения..."; +const wchar_t* yesNoDialog = L"Да[Y] Нет[N]"; + + +/************************************************************************* +* Windows' title +*************************************************************************/ + +const wchar_t* achievementsTitle = L"Достижения"; +const wchar_t* breaking_news = L"Мировые новости"; +const wchar_t* changeSpeedTitle = L"Скорость игры"; +const wchar_t* coursesTitle = L"Курсы"; +const wchar_t* courseAlreadyPassed = L"Курс уже пройден!"; +const wchar_t* finalCard = L"КОНЕЦ"; +const wchar_t* gameOverTitle = L"Вы проиграли!"; +const wchar_t* introTitle = L"Вступление"; +const wchar_t* new_letter = L"Новое письмо"; +const wchar_t* newAchievement = L"Новое достижение!"; +const wchar_t* notEnoughMoney = L"У вас недостаточно средств :("; +const wchar_t* playerStatsTitle = L"Статистика игрока\n\n\n"; +const wchar_t* unread_message = L"Непрочитанное сообщение"; + +/************************************************************************* +* Windows' text +*************************************************************************/ + +const wchar_t* coursesFinishedText = L"\nКурсов пройдено: %d/%d\n \n"; +const wchar_t* finalCardsUnlockedText = L"\nКонцовок открыто: %d/%d\n"; +const wchar_t* gameName = L"21 days"; +const wchar_t* gameOverText = L"\nЭто был единственный способ проиграть, и вы нашли его!\nНа еду денег нет, ваша репутация так низка, что никто не звонит и не предлагает работу.\n\nКонец!\n"; +const wchar_t* helpDesker = L"эникейщик"; +const wchar_t* mainMenuText = L"\n\n1.Новая игра\n\n2.Выход\n"; +const wchar_t* noCurrentCurses = L"\tВы ничему не обучаетесь...\n"; +const wchar_t* playerStatsEnd = L"\n\nНе забудьте поделиться скриншотом статистики с друзьями!\n\nНажмите [Esc] для выхода.\n"; +const wchar_t* playerStatus = L"Ваш статус: "; +const wchar_t* pseudoEqual = L"═"; +const wchar_t* selectCourseText = L"Выберите одну из следующих категорий, которой вы бы хотели научиться:\n"; +const wchar_t* startupper = L"стартапер"; +const wchar_t* takeCourseCost = L", пройти курс: $%d\n"; +const wchar_t* takingThisCourse = L"\tВы сейчас обучаетесь этому курсу..\n"; +const wchar_t* worker = L"трудоустроенный"; + +const wchar_t* days21 = +L"\n .oooo. .o .o8 \n" +".dP\"\"Y88b o888 \"888 \n" +" ]8P' 888 .oooo888 .oooo. oooo ooo .oooo.o \n" +" .d8P' 888 d88' `888 `P )88b `88. .8' d88( \"8 \n" +" .dP' 888 888 888 .oP\"888 `88..8' `\"Y88b. \n" +".oP .o 888 888 888 d8( 888 `888' o. )88b \n" +"8888888888 o888o `Y8bod88P\" `Y888\"\"8o .8' 8\"\"888P' \n" +" .o..P' \n" +" `Y8P' \n"; + +const wchar_t* youWin = +L"\n.___________. __ __ _______ _______ .__ __. _______ \n" +"| || | | | | ____| | ____|| \\ | | | \\ \n" +"`---| |----`| |__| | | |__ | |__ | \\| | | .--. |\n" +" | | | __ | | __| | __| | . ` | | | | |\n" +" | | | | | | | |____ | |____ | |\\ | | '--' |\n" +" |__| |__| |__| |_______| |_______||__| \\__| |_______/ \n\n\n"; + +const wchar_t* gameOverLogo = +L" __ _ __ _ _ __ ___ ___ _____ _____ _ __ \n" +" / _` |/ _` | '_ ` _ \\ / _ \\ / _ \\ \\ / / _ \\ '__|\n" +"| (_| | (_| | | | | | | __/ | (_) \\ V / __/ | \n" +" \\__, |\\__,_|_| |_| |_|\\___| \\___/ \\_/ \\___|_| \n" +" |___/ \n\n"; + +const wchar_t* playerStats[9] = { + L"Игра была бы пройдена без прыжков во времени за %d ", + L"Потрачено настоящих, не игровых минут на прохождение игры: %d\n", + L"Прочитано сообщений: %d\n", + L"Помог знакомым разобраться с компьютером: %d ", + L"Не помог: %d ", + L"Денег заработано: $%d\n", + L"Денег потрачено: $%d\n", + L"Из них на благотворительность: $%d\n", + L"Совершено прыжков во времени: %d\n"}; + +/*const wchar_t* gameIntro = L"\n Урраа! Диплом успешно получен и уже как несколько недель пылится на полке. Голова до сих пор раскалывается от огромной радости... не стоило всё же так бурно отмечать окончание универа. К счастью, этот 'выпускной вечер', длящийся уже недели две, закончился.\n Что там у нас в холодильнике? Пусто. Хм. Ладно, позвоню Валере.\n\n\ +<трясущимися пальцами набирает номер>\n-Хэй, Валер, у тебя есть что?\n-Да ладно, неужели ты проспался!\n-Не надо ля-ля, я самым трезвым был.\n-Ну да, конечно, рассказывай тут! Фотографии позже скину, как с работы приду.\n-Откуда?\n-Да, меня дядя быстренько устроил. Программистом.\n-Круто, а он может меня тоже устроить?\n-Ну... вряд ли, тут все места заняты.\n-Но ведь ты как-то устроился!\n-Прости, ничем помочь не могу. Попробуй устроиться куда-нибудь.\n-Да я же ничего не умею!\n-Ахаха, смешно слышать это от выпускника ВУЗа... Хм, хотя я и сам ничего не умею. Придётся самому пробиваться. Я видел в магазине книги с заголовками 'C++ за 21 день', 'Java за две недели' и т.п., попробуй почитать что-нибудь, быстренько выучись и отправляй резюме куда-нибудь. Только не в нашу компанию.\n-Почему?.\n-Ну я-то знаю, какой ты раздолбай. Зачем ты тут, если ты на самом деле ничего не умеешь?\n-Что?! Да я смотрю, ты такой умный сразу стал, как работу нашёл. Не было бы дяди, ты сейчас бы дворником был!\n-Да пошёл ты, не тебе решать!\n\n\ + Чёрт! Деньги кончаются, а кушать хочется. Хорошо, что хоть винду умею переустанавливать - знакомых много, как-нибудь продержусь. А этому нахалу я ещё покажу, чему можно выучитья за 21 день! И пусть этот гадёныш работает на своего дядю, пока я строю свою компанию. Интересно, дедушкин квантовый сворачиватель пространства и времени ещё работает? Он мне пригодится. Ахаха, я уже представляю лицо этого придурка, когда он меня увидит! Он же останется таким же тупым болваном!\n\n";*/ +/*const wchar_t* gameIntroPlan = L" Попробую выучиться всему, чему можно, достигнуть всего, о чём можно только подумать, а ещё, а ещё... заработать МИЛЛИАРД! Хм, ладно, это уже слишком. Пусть будет миллион. А потом встречусь с Валерой и посмотрим, кто чего добился.\n\n Итак, цель поставлена, отсчёт времени пошёл!\n Что он там писал? C++ за 21 день? Где-то в шкафу есть такая книга, надо поискать. Я же когда-то начинал её читать... за 21 день её точно не пройти. \nПридётся усердно заниматься и через пару-тройку лет я его выучу. А потом возьму дедушкин квантовый сворачиватель пространства и времени и вернусь в прошлое, до 21-го дня, пусть все думают, что я добился всего за 3 недели. Именно так я и сделаю!";*/ +const wchar_t* gameIntroPlan = L"Цели:\n\nОткрыть все 9 достижений\nПройти 12 курсов обучения\nОткрыть 5 концовок:\n 3 из них связаны с выбором своего пути (эникейство, работа в компании, свой стартап)\n 2 из них связаны с изменением вашего положения в обществе (карма / количество денег)\n\nВаше игровое пространство - время. По сравнению с другими играми, где вы исследуете игровое пространство, здесь необходимо исследовать время.\n\nВыбрав путь, вы можете вернуться в прошлое и выбрать другой: этом вам поможет квантовый сворачиватель пространства и времени.\n\nПосле выполнения всех заданий вернитесь к начальной точке: к 21 дню. Подумайте хорошенько, порой вернуться не так просто: может не хватить средств.\n\nИгра считается законченной, когда вы увидите надпись 'The End'(вы выиграли) либо 'game over'(проиграли, не выполнили задание)."; + +const wchar_t* yourAnswer = L"\n\nВаш ответ:\n\t1:%ls\n\t2:%ls\n"; +const wchar_t* shitCodeYourAnswer = L"\n\nВаш ответ:\n\t1:Да, конечно.\n\t2:Не, я такого не умею.\n"; +const wchar_t* statusLine = L"День: %d, $: %d, карма: %d, сообщения: %d"; +const wchar_t* statusLineCoursesNotFinished = L", непройденных курсов: %d"; +const wchar_t* unnamedStudio1Answer = L"Смотри, второй раз предлагать не будем.\n\nВаш ответ:\n\t1:Да, ребят, я согласен!\n\t2:Хм..я не такой смелый, чтобы вкладывать последние деньги.\n"; +const wchar_t* unnamedStudio3Answer = L"\n\nВаш ответ:\n\t1:Да, от сердца отрываю!\n\t2:Нет, ну сколько можно клянчить деньги?\n"; +const wchar_t* unnamedStudio4Answer = L"\n\nВаш ответ:\n\t1:Да, давайте наймём веб-мастера.\n\t2:Нет, давайте я лучше сам быстренько выучусь и сделаю нам сайт, м?\n\t3:Что, опять тратить деньги? Да пошли вы!\n"; +const wchar_t* angryBossAnswer = L"\n\nВаш ответ:\n\t1:Да, я больше не буду фрилансить.\n\t2:Нет, всё же это не по мне. Я следую за духом свободы.\n"; + +/************************************************************************* +* Modal windows' text +*************************************************************************/ + +const wchar_t* answerLetter = L"Ответное письмо"; +const wchar_t* cantStopDuringKnowledgeCheck= L"Нет, вы не можете переместиться в середину проверки знаний. Проходите заново!"; +const wchar_t* charityTitle = L"Благотворительность"; +const wchar_t* charityQuestion = L"Вы действительно хотите пожертвовать половину ваших денег на благотворительность?"; +const wchar_t* congratsMsg = L"Поздравляем!"; +const wchar_t* courseSuccessfullyFinished = L"Курс '%ls' успешно завершен! Теперь вы можете выполнять задания по выбранному направлению."; +const wchar_t* daysPerSecond = L"Игровых дней в секунду: %d"; +const wchar_t* doYouReallyWantToExit = L"Вы действительно хотите выйти?"; +const wchar_t* errorMsg = L"Ошибка!"; +const wchar_t* failMsg = L"Неудача!"; +const wchar_t* need100ForUndo = L"Прыжки во времени дороги. Вам необходимо иметь хотя бы $100!"; +const wchar_t* notEnoughMoneyForCharity = L"Вам нечего жертвовать на благотворительность!"; +const wchar_t* noUnreadMessages = L"Нет непрочитанных сообщений!"; +const wchar_t* prepareForTimeJump = L"Сейчас будет произведён прыжок в прошлое, приготовьтесь!"; +const wchar_t* pressRForCharity = L"Теперь, имея достаточно средств, вы можете тратить деньги на благотворительность!"; +const wchar_t* returnToDay21 = L"Вы выучились всему, и добились всего, что хотели. Не забудьте вернуться в прошлое к 21-му дню, чтобы достичь поставленной цели. Подумайте хорошенько, порой это сделать не так просто."; +const wchar_t* rightAnswer = L"Правильный ответ!"; +const wchar_t* startupFailedTip = L"Вы завалили стартап :(. Лучше вернуться в прошлое и всё исправить. Дальше ничего интересного уже не будет, это тупик."; +const wchar_t* successfullyEnrolled = L"Вы успешно записаны на курс '%ls'!"; +const wchar_t* wrongAnswer = L"Неверный ответ :("; + +/************************************************************************* +* Achievements +*************************************************************************/ + +const unsigned int achievesNumber = 9; +wchar_t* achieves[achievesNumber] = { + L"Гуру программирования: выполнить три супер-сложных проекта.", + L"Отверженный: ваша карма опустилась ниже нуля!", + L"Успешный стартапер: запустить прибыльный стартап.", + L"Миллионер: накопить $1000 000.", + L"Непоседа: следовать за духом свободы.", + L"Не твой день: променять престижную работу на стартап и не взлететь.", + L"Знающий своё дело: успешно пройти проверку знаний.", + L"Настойчивый и терпеливый: скучной работы не чурается.", + L"Путешественник во времени: совершать прыжки, не распадаясь на молекулы." + }; + +bool achievesPicked[achievesNumber] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; +bool achievesShowed[achievesNumber] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/************************************************************************* +* Final cards +*************************************************************************/ + +const wchar_t* defaultFinalCard = L"В общем, это всё. Ты, конечно, можешь продолжать и дальше, но никаких особых \ +событий не предвидится: ты просто всю жизнь будешь переустанавливать винду и заменять перегоревшие видеокарты. Ха! \ +Знаешь, а такой образ жизни кому-то даже нравится: меньше ответственности и работа непыльная. На еду хватает, \ +на интернет тоже. Зачем нам эти стартапы и работа в крупных компаниях? Пфф, ты сделал свой выбор. Что ещё нужно? \ +Иногда, правда, никто не звонит, приходится трояны ставить некоторым, чтобы почаще звонили, а то так и с голоду \ +помереть можно! Только тссс, никому, слышишь?\n"; + +const wchar_t* workFinalCard = L"Я смотрю, ты терпеливый. Сидишь, работаешь. Вижу же, что скучаешь. А ты всё равно \ +работаешь, на стороне заказов не берёшь. Деньги не нужны? Или боишься, уволят? Да не важно! То, что ты такой - \ +это похвально. Вот такие и вырываются вперёд - сидят себе молча, работают, работают, а потом лет через пять РРАЗ -\ +и становятся генеральными директорами компании, в которой работают. Вот такая у тебя перспектива, дружище. Кому-то \ +больше нравится стабильное место в компании, чем свой стартап, который забирает кучу сил и может не взлететь. \ +Что, хочешь свой стартап? Ну, знаешь, надо было раньше делать другой выбор!\n"; + +const wchar_t* startupFinalCard = L"Согласись, это было круто! Такие бабки сколотить... хотя знаешь, это не самое главное. \ +Главное ведь то, что то время, которое ты потратил на этот стартап, потрачено не зря. Да и не это главное, чёрт возьми! \ +Твою игру скачивают миллионы. МИЛЛИОНЫ, СЛЫШИШЬ? Вот это главное - то, что твой продукт нужен людям. То, что тебя ценят. \ +Потому ведь ты и ушёл в стартап, что не хочешь быть серой мышью в толпе программистов в какой-то крупной компании, где \ +твоё видение никого не интересует. Возможность самореализации и полёт мысли - вот она, СВОБОДА! Конечно, завистники \ +скажут, что тебе просто повезло, но мы-то знаем, сколько труда было вложено! Ты прекрасно знал, на что идёшь, иначе \ +выбрал бы что-то другое. Но ты сделал правильный выбор. Хотя знаешь, нет неправильного выбора. Есть правильный, просто другой."; + +const wchar_t* zeroKarmaFinalCard = L"Вы настолько испортили отношения с людьми, что даже незнакомые люди не хотят иметь с вами дела. Вам никто не звонит. Это конец :(.\n\nНа самом деле нет :D. Постарайтесь любыми способами поднять карму, иначе, когда кончатся деньги, проиграете.\n"; + +const wchar_t* noMoneyFinalCard = L"У вас нет денег, чтобы дотянуть до следующего звонка с предложением о работе. Как вариант - начните продавать капусту, может быть, это у вас лучше получится.\n\nНа самом деле это ещё не конец. Постарайтесь как-нибудь заработать денег, иначе, когда карма будет меньше нуля, вы проиграете!\n"; + +const wchar_t* gameOver = L"Вы достигли поставленной цели и успешно завершили игру! Все курсы обучения пройдены, все концовки раскрыты, а сколько достижений вы накопили за это время! Добиться такого всего за 21 день! Опыт, который вы приобрели, бесценен, а выбор, который вы делали - правильный. Ведь нет неправильного выбора, есть просто другой. А то, что будет дальше, зависит от нас. А не от НЛО или какой-то машины времени, верно?"; + +/************************************************************************* +* Hints +*************************************************************************/ + +const wchar_t* hintOfTheMonth = L"Подсказка месяца:\n"; +const int hintsCount = 12; +const wchar_t* hints[hintsCount] = { + L"'Постоянный источник доходов' - это не только официальная работа или собственная компания, но также и выполнение заказов и просьб знакомых, если вы зарекомендовали себя как специалиста и имеете хорошую репутацию.", + L"Если отказывать всем знакомым переустановить винду или починить компьютер, то вскоре они перестанут звонить совсем.", + L"Залезать в долги неприятно, но запоминается надолго.", + L"Один мой знакомый говорил: чтобы стать миллионером, нужно основать несколько стартапов. Или только один, но несколько раз.", + L"Другой мой знакомый имел кучу денег, но был несчастлив. Он нигде не работал, и просто тратил деньги, пока они не закончились. Так он и закончил.", + L"Ходите кругами? Кажется, что здесь вы уже были и других путей нет? Вдумайтесь, вчитайтесь - всегда можно сделать какой-нибудь прорыв или достижение.", + L"Если ваша жизнь скучна и в ней ничего не происходит, попробуйте скопить побольше денег. Говорят, они дают больше возможностей.", + L"Если вы видите свой 'Конец'...гм.., то уже ничего не поможет. Это значит, что всё. Эх, если бы у нас была машина времени!", + L"Не знаете, чему бы новому поучиться? Вспомните, быть может, однажды вам предлагали такую возможность, а вы отказались.", + L"Благотворительность - хороший способ избавиться от денег, а заодно и заработать людское доверие.", + L"При прыжках во времени пространственно-временной континуум иногда не выдерживает и прорывается: этим можно объяснить все баги в этой игре.", + L"После прохождения всех курсов и открытия всех достижений и концовок не забудьте вернуться в прошлое к 21-му дню. Это может быть не так просто, как кажется: денег может банально не хватить." + }; + +const wchar_t* getWordEnding(int n, int k) { + int ld = n % 10; // Last digit + if (ld == 1) + return (k == 0)?L"день\n":L"раз\n"; + else if (ld == 2 || ld == 3 || ld == 4) + return (k == 0)?L"дня\n":L"раза\n"; + else + return (k == 0)?L"дней\n":L"раз\n"; + } + +/************************************************************************* +* Intro +*************************************************************************/ + +const wchar_t* developer = L"Copyright (C) 2014 Maxim Grishin (igevorse)"; +const wchar_t* introAllowedKeys = L"А снизу - клавиши, которые вы можете нажать. Например, [1] или [Enter]"; +const wchar_t* introDesktop = L"Это - ваш рабочий стол"; +const wchar_t* introFinished = L"Ознакомление с интерфейсом завершено, начинаем игру!"; +const wchar_t* introLetsBegin = L"Начнём!"; +const wchar_t* introStatusLine = L"Сверху отображается важная информация:\n1.Текущий день.\n2.Количество денег (каждый день становится всё меньше, ведь вам нужно питаться).\n3.Карма - отношение людей к вам. \n4.Сообщения - количество непрочитанных сообщений.\n"; + +const wchar_t* gameIntroTextPart1 = L"1. Случай\n\n\ +- Разве это сервировка? Ты в какой деревне этому учился? Почему у меня ложка лежит не с той стороны, где положено?! Что молчишь? Отвечай!\n- ...\n\ +- <дергает меня за край рукава> Ты слышишь? Да чёрт с ней, с этой ложкой, ты наши заказы перепутал! Понимаешь? Ушами хлопал, когда заказ принимал? На жену мою засмотрелся, нет?\n\ +<снова дергает за рукав> На меня смотри! Я сюда с дамой пришёл, а ты испортил весь вечер!\n\ +Пошёл отсюда!\n\n\ +Развернувшись, я быстрым шагом направился к двери подсобки, ворвался внутрь, резко закрыл за собой дверь и прижался спиной к стене.\nТак больше не может продолжаться.\n\ +Во рту появилась горечь от обиды. Ну почему всё так?\n\ +Ну почему он придрался к этой мелочи, разве нельзя просто сделать вид, что ты её не заметил? Да он же сам испортил вечер! Да, я поставил крем-брюле не ему, а его спутнице, потому что подумал, что он заказывает не себе. Обычно ведь его заказывают дамам! Он мог бы вежливо указать на ошибку, зачем сразу устраивать истерику?\n\ +Правила приличия, хорошие манеры? Где это всё?\n\ +Они лицемерят и любезничают друг с другом, но как только кто-то ниже их по социальному положению ошибётся, так они стараются излить тонну гнева на бедного официанта!\n\ +Я так больше не могу. \n\ +Уже третий раз за неделю клиенты устраивают истерики на пустом месте! Ругаются, унижают, указывают мне на моё положение. Да и кто решил, что ЭТО - моё положение? Они? Да у них мозгов хватает только на походы в кафе и безмерные траты денег. Кто же тогда это решает?!\nУже нет сил терпеть всё это!\n\ +Шеф узнает, начнётся такая ругань... нужно просто перетерпеть и всё. НО Я НЕ МОГУ.\n\n\ +Нужно уходить отсюда. Нет, не потому что шеф уволит, узнав о произошедшем. Нет. Потому что я когда-нибудь кинусь с кулаками на очередного придирающегося клиента... Либо я просто сойду с ума, пытаясь терпеть всё это.\n\n\ +Я поднял глаза и увидел себя в зеркале, висевшем напротив: на лице застыла какая-то странная безобразная ухмылка.\n\ +Видимо, это нервное. Надо это прекращать, иначе я так долго не проживу в этом постоянном стрессе.\n\ +Я с силой сорвал фартук и небрежно кинул на пол.\n\ +Всё кончено!"; + +const wchar_t* gameIntroTextPart2 = L"2. Цель\n\n\ +Легко сжигать мосты, когда тебе есть, на что жить! А что делать, если в холодильнике еды на три дня? Ладно, максимум на неделю, если почти ничего не есть. Я так умею, в общаге и не такое проходили. Но если в университетской общаге можно было ходить 'в гости' к добрым студентам с открытыми сердцами, завтракать у одного, а обедать у другого, то сейчас я могу рассчитывать только на себя. В универе вообще многое было совсем по-другому. Нам говорили, что мы выйдем хорошими специалистами, получим престижную работу! \n\ +Тень нервной ухмылки снова пробежала по лицу. Ха! И это они называют престижной работой?\n\ +Из всех одногруппников, насколько я знаю, только пятеро смогли устроиться в хорошие компании, потому что действительно учились.\n\ +А чем я хуже?\n\ +А мне приходилось работать днями и ночами, чтобы хватало на еду и оплату учёбы в универе. Учёбы? Да разве это учёба? С такой работой я и в универе-то редко появлялся.\n\ +Мне просто не повезло. Неудивительно, что я ничему и не выучился.\n\ +В животе раздался тревожный гул, напоминая, что уже более десяти часов я ничего не ел.\n\ +Да я скорее умру с голоду, чем вернусь работать официантом! Мне же гордость просто не позволит сломать себя и вернуться, поджав хвост.\nНужно что-то делать.\n\n\ +Мысли мешают думать. Голова просто разрывается от мыслей!\n\ +Я же ничего не умею!\n\ +Единственное, что у меня неплохо получается - это давать советы. Ха! А ещё мне нравится возиться с компьютерами, когда у друзей что-то не работает. \n\ +Нет, нет, это же не может быть работой. Это так, хобби. Надо отогнать эту мысль.\n\ +Желудок настойчивее дал понять, что у него есть другое мнение по поводу работы. Я сжался от боли.\n\n\ +Прошло.\nО чём я думал? Ах, да, на этом нельзя заработать. \n\ +Хотя...\nА, собственно, почему нет? Что я ещё умею?\n...\n\ +Вот то-то и оно. Кажется, у меня просто нет другого выхода. Лишь бы не возвращаться в этот ресторан.\n\ +Я взял первый попавшийся смятый листок со стола, заваленного всяким хламом, карандаш, и набросал примерные расчёты.\n\ +Так, если заниматься только эникейством, то на еду мне будет хватать... а если потом расширять круг знакомых... так, можно попробовать сотрудничать с ТОО... тогда получается...это... и это...\n\ +Почему я не думал об этом раньше!\n\ +С силой кинув карандаш на стол, я откинулся на спинку стула. Это же проще простого!\n\ +Идиот, нужно было пробовать раньше!\n\n\ +Теперь я смогу заработать хотя бы на еду. Отличная перспектива! Кстати, о перспективе: надо бы заниматься чем-то ещё кроме эникейства, иначе к зиме будет не на что купить ботинки. В универе нам читали какие-то лекции по программированию. Говорили, это вскоре станет очень востребовано.\n\ +Что там у нас есть? Кинув взгляд на полупустую книжную полку, я первым делом замечаю книгу 'C++ за 21 день'. Мне её подарили на первом курсе. Она до сих пор стоит нетронутой. Я тогда целыми днями обслуживал столики, и совершенно не было времени её прочесть.\n\ +Да разве это возможно? За 21 день я даже просто прочитать её не успею.\n\ +Что там рядом? Книга 'Teach Yourself Perl in 21 days'. Ничего не понятно, но снова говорят о двадцать одном дне. Взял у преподавателя, когда готовился к дипломной, и забыл отдать. Тоже не открывал.\n\ +Да это же смешно!\n\ +Если бы это было так просто, то все вокруг уже писали на C++ и Perl, ведь нужно потратить всего 21 день на обучение!\n\ +В голове снова возникло лицо разъярённого клиента: '...ты наши заказы перепутал! Понимаешь? Ушами хлопал, когда заказ принимал?'.\n\ +Нет, таким людям и в голову не придёт чему-либо учиться.\n\n\ +У меня же ситуация просто безвыходная, поэтому я просто ОБЯЗАН выучиться за 21 день. Любой ценой.\n\ +Нет пути назад."; + +const wchar_t* gameIntroTextPart3 = L"3. Находка\n\n\ +Лень. Где-то я услышал фразу 'Ты не наладишь свою жизнь, пока не наведёшь порядок в своём доме'. Вот так и я вылизал всю комнату, лишь бы не брать эти книги в руки. Из этой комнаты-свалки я сделал просто конфетку.\n\ +Нужно ещё вытащить всё из-под кровати и выкинуть к чёртовой матери. Я даже боюсь разгребать всё это, даже не представляю, что там может быть.\n\n<...>\n\n\ +Что это за коробка? Я не помню такой. Да, собственно, что я мог помнить, когда приходил домой усталый после работы и сразу спать? А рано утром в 'любимый' ресторан.\n\ +Странно. Металлическая коробка не хочет открываться. Ну-ка, поднажмём...\n\ +Крышка отлетела обратно под кровать, из коробки выпала какая-то бумажка с надписью. \n\ +Что там написано? 'Это тебе'.\n\ +Что это? Какой-то механизм, микросхемы, провода и блестящий красный камень...\n\n\ +Внезапно я всё понял. Я плюхнулся на колени перед кроватью и застыл на несколько мгновений.\n\ +Он существует. ЭТО ОН!\n\ +Ещё в детстве дед мне рассказывал сказки про то, как он якобы во время своих путешествий общался со знаменитостями разных времён, охотился на динозавров и вообще хорошо проводил время на светских балах. Это были увлекательные и интересные сказки на ночь. Именно так мы относились к этим рассказам.\n\ +Он был преклонного возраста и, хотя его рассказы были интересными, мы не воспринимали его слова всерьёз. Мало ли, что там бормочет выживший из ума старик?\n\n\ +А потом он пропал. Исчез. Совсем.\n\ +Его так и не нашли.\n\ +А теперь эта коробка. Я не верю своим глазам. Это же невозможно!\n\ +Как он там называл это?\n\n\ +Ква... квантовый сворачиватель пространства и времени! Да, именно так.\n\n\ +Колени стали невыносимо ныть. Очнувшись, я понял, что просидел много времени. Куда там отлетела крышка? А, вот она. Закрыв коробку, я положил её на полку.\n\n\ +Надо уже начинать что-то делать!\n\ +Как там было... успеть всё за 21 день?\n\ +Вызов принят!"; + +#endif diff --git a/sys.cpp b/sys.cpp new file mode 100644 index 0000000..9eef8a2 --- /dev/null +++ b/sys.cpp @@ -0,0 +1,400 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +wchar_t* windowTitle = L"21 days by igevorse"; + +#ifdef _KOS32 +#define kbhit kbhit // Prevent redeclaring kbhit() in pc.h +#include +using std::wstring; + +#pragma pack(push,1) +typedef struct { + wchar_t *name; + void *data; +} kol_struct_import; +#pragma pack(pop) + +void kol_exit() { + asm volatile ("int $0x40"::"a"(-1)); + } + +void kol_screen_get_size(unsigned *w, unsigned *h) { + unsigned size; + asm volatile ("int $0x40":"=a"(size):"a"(14)); + *w = size / 65536; + *h = size % 65536; + } + +void (* __attribute__((stdcall)) con_init)(unsigned w_w, unsigned w_h, unsigned s_w, unsigned s_h, const wchar_t* t); +void (* __attribute__((cdecl)) wprintf2)(const wchar_t* format,...); +void (* __attribute__((stdcall)) con_exit)(wchar_t bCloseWindow); + int (* __attribute__((stdcall)) getch)(); + int (* __attribute__((stdcall)) kbhit)(); +void (* __attribute__((stdcall)) con_set_cursor_pos) (int x, int y); + int (* __attribute__((stdcall)) con_set_cursor_height)(int new_height); +void (* __attribute__((stdcall)) cls)(); + +int strcmp(const wchar_t* wstring1, const char* string2) { + while (1) { + if (*wstring1<*string2) + return -1; + if (*wstring1>*string2) + return 1; + + if (*wstring1=='\0') + return 0; + wstring1++; + wstring2++; + } + } + +void* kol_cofflib_procload (kol_struct_import *imp, wchar_t *name) { + for (int i=0;;i++) + if (NULL == ((imp+i) -> name)) + break; + else + if (0 == strcmp(name, (imp+i)->name) ) + return (imp+i)->data; + return NULL; + } + +kol_struct_import* kol_cofflib_load(wchar_t *name) { + kol_struct_import* val; + asm volatile ("int $0x40":"=a"(val):"a"(68), "b"(19), "c"(name)); + return val; + } + +unsigned _kWinWidth = 100; +unsigned _kWinHeight = 30; + +void getWinWH(int& width, int& height) { + width = _kWinWidth; + height = _kWinHeight-1; + } + +void CONSOLE_INIT(wchar_t* title) { + kol_struct_import *imp = kol_cofflib_load("/sys/lib/console.obj"); + if (imp == 0) + kol_exit(); + + con_init = ( __attribute__((stdcall)) void (*)(unsigned, unsigned, unsigned, unsigned, const wchar_t*)) + kol_cofflib_procload (imp, "con_init"); + if (con_init == NULL) + kol_exit(); + + wprintf2 = ( __attribute__((cdecl)) void (*)(const wchar_t*,...)) + kol_cofflib_procload (imp, "con_wprintf"); + if (wprintf2 == NULL) + kol_exit(); + + con_exit = ( __attribute__((stdcall)) void (*)(wchar_t)) + kol_cofflib_procload (imp, "con_exit"); + if (con_exit == NULL) + kol_exit(); + + getch = ( __attribute__((stdcall)) int (*)()) + kol_cofflib_procload (imp, "con_getch"); + if (getch == NULL) + kol_exit(); + + cls = ( __attribute__((stdcall)) void (*)()) + kol_cofflib_procload (imp, "con_cls"); + if (cls == NULL) + kol_exit(); + + kbhit = ( __attribute__((stdcall)) int (*)()) + kol_cofflib_procload (imp, "con_kbhit"); + if (kbhit == NULL) + kol_exit(); + + con_set_cursor_pos = ( __attribute__((stdcall)) void (*) (int, int)) + kol_cofflib_procload (imp, "con_set_cursor_pos"); + if (con_set_cursor_pos == NULL) + kol_exit(); + + con_set_cursor_height = ( __attribute__((stdcall)) int (*)(int)) + kol_cofflib_procload (imp, "con_set_cursor_height"); + if (con_set_cursor_height == NULL) + kol_exit(); + + unsigned sw, sh; + + kol_screen_get_size(&sw, &sh); + // Change window w/h depending on screen w/h + // Sorry for magic numbers + if (_kWinWidth*8 >= sw) + _kWinWidth = sw / 8; + + if (_kWinHeight*17 >= sh) + _kWinHeight = sh / 17; + + con_init(_kWinWidth, _kWinHeight, _kWinWidth, _kWinHeight, title); + } + +void hideCursor() { + con_set_cursor_height(0); + } + +void showCursor() { + //nop; + } + +void consoleGoto(int line, int column) { + con_set_cursor_pos(column-1, line-1); + } + +wstring itos(int n) { + wstring s; + int i, sign; + + if ((sign = n) < 0) + n = -n; + i = 0; + do { + s[i++] = n % 10 + '0'; + } while ((n /= 10) > 0); + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + + int j,k; + for (k = 0, j = i-1; k // system +#include +#include // wprintf, vsprintf +#include // get window width/height +#include +#include +#include +#include +static struct termios old, new1; + +std::wstring txt(const wchar_t* s, ...) { + va_list args; + va_start(args,s); + wchar_t x[100000]; + vswprintf(x,100000, s, args); + va_end(args); + std::wstring str(x); + return str; + } + +void cls() { + system("clear"); + } + +void hideCursor() { + wprintf2("\033[?25l"); + } + +void showCursor() { + wprintf2("\033[?25h"); + } + +void consoleGoto(int line, int column) { + wprintf2("\033[%d;%dH", line, column); + } + +void consoleSetColor(short text = 30, short background = 47) { + wprintf2("\033[%d;%dm", text, background); + } + +/* Initialize new terminal i/o settings */ +void initTermios() { + tcgetattr(0, &old); /* grab old terminal i/o settings */ + new1 = old; /* make new settings same as old settings */ + new1.c_lflag &= ~ICANON; /* disable buffered i/o */ + new1.c_lflag &= ~ECHO; /* set echo mode */ + tcsetattr(0, TCSANOW, &new1); /* use these new terminal i/o settings now */ + } + +/* Restore old terminal i/o settings */ +void resetTermios() { + tcsetattr(0, TCSANOW, &old); + } + +int kbhit () { + struct termios oldt, newt; + int ch; + int oldf; + + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + oldf = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); + + ch = getwchar(); + + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + fcntl(STDIN_FILENO, F_SETFL, oldf); + + if(ch != EOF) { + ungetc(ch, stdin); + return 1; + } + return 0; + } + +/* Read 1 wchar_tacter without echo */ +wchar_t getch() { + wchar_t ch; + initTermios(); + ch = getwchar(); + resetTermios(); + return ch; + } + +void getWinWH(int& width, int& height) { + struct winsize w; + ioctl(0, TIOCGWINSZ, &w); + width = w.ws_col; + height = w.ws_row-1; + } + +void initConsole() { + wprintf2("\033]0;%s\007", windowTitle); + } +#elif defined _WIN32 +#define wprintf2 printf +#include // wprintf, vsprintf +#include +#include // system +#define _NO_OLDNAMES _NO_OLDNAMES +#include // kbhit(), getch(), getche() +#include + +wchar_t getch() { + return _getch(); + } + +int kbhit (void) { + return _kbhit(); + } + +std::wstring txt(const wchar_t* s, ...) { + va_list args; + va_start(args,s); + wchar_t x[100000]; + vswprintf(x,100000, s, args); + va_end(args); + std::wstring str(x); + return str; + } + +void cls() { + system("cls"); + } + +void showCursor() { + HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_CURSOR_INFO info; + info.bVisible = TRUE; + SetConsoleCursorInfo(consoleHandle, &info); + } + +void hideCursor() { + HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_CURSOR_INFO info; + info.bVisible = FALSE; + info.dwSize = 1; + SetConsoleCursorInfo(consoleHandle, &info); + } + +void consoleGoto(int line, int column) { + COORD coord; + coord.X = column; + coord.Y = line; + SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); + } + +void consoleSetColor(short text = 30, short background = 47) { + wprintf2("\033[%d;%dm", text, background); + } + +void getWinWH(int& width, int& height) { + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + width = csbi.srWindow.Right - csbi.srWindow.Left - 1; + height = csbi.srWindow.Bottom - csbi.srWindow.Top - 1; + } + +void initConsole() { + SetConsoleTitle(windowTitle); + system("mode 100, 30"); + } +#endif diff --git a/sys.h b/sys.h new file mode 100644 index 0000000..f6c2e9e --- /dev/null +++ b/sys.h @@ -0,0 +1,53 @@ +/****************************************************************** +* 21 days: a game for programmers +* Copyright (C) 2014 Maxim Grishin +* +* 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 the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301, USA. +*******************************************************************/ + +#ifndef H_SYS +#define H_SYS + +#define ESCAPE_KEY 27 + +#if defined(__linux__) && !defined(_KOS32) + #define ENTER_KEY '\n' +#else + #define ENTER_KEY 13 +#endif + +#ifdef _KOS32 +#define kbhit kbhit // Prevent redeclaring kbhit() in pc.h +void CONSOLE_INIT(wchar_t* title); +void kol_exit(); +extern void (* __attribute__((stdcall)) con_exit)(wchar_t bCloseWindow); +extern void (* __attribute__((cdecl)) wprintf2)(const wchar_t* format,...); +extern int (* __attribute__((stdcall)) getch)(); +extern int (* __attribute__((stdcall)) kbhit)(); +extern void (* __attribute__((stdcall)) cls)(); + +#else +void cls(); +wchar_t getch(); +int kbhit (); +#endif + +void initConsole(); +void hideCursor(); +void showCursor(); +void consoleGoto(int line, int column); +void getWinWH(int& width, int &height); +#endif