kolibrios/programs/games/21days/game.cpp

1585 lines
61 KiB
C++
Raw Permalink Normal View History

/******************************************************************
* 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 <menuet/os.h>
#define vector vector21
extern "C" {
void *__dso_handle = 0;
void *__cxa_atexit = 0;
void *__stack_chk_fail = 0;
}
#else
#define printf2 printf
#include <stdlib.h> //srand
#include <vector>
using std::vector;
#include <unistd.h> // usleep
#endif
#include <time.h>
#include <math.h>
using std::string;
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 charityUnlocked = 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);
string 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;
charityUnlocked = 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, "[R]");
wait('r','R');
}
achievesShowed[i] = true;
}
else if (!achievesShowed[i])
allAchievesShowed = false;
}
}
void showMainMenu() {
string s = days21;
s+=mainMenuText;
drawWindow(s, gameName, "", txt("%s %s", one_or_two, developer));
char menu = getAnswer('1', '2');
switch(menu) {
case '1':
startNewGame();
break;
case '2': // TODO: Save?
break;
}
}
void showAchievesScreen() {
string s;
for (unsigned short i = 0; i < achievesNumber; i++)
s += txt("[%c] %s \n", achievesPicked[i]?'X':' ', achieves[i]);
s += txt(finalCardsUnlockedText, finalCardsUnlocked, 5);
char totalCoursesCount = 12;
s += txt(coursesFinishedText, (int)finishedCoursesCount, (int)totalCoursesCount);
drawWindow(s, achievementsTitle, getStatusLine(), enter);
}
string printExactTask(int course, int task, int word) {
switch (course) {
case 1:
case 2:
return string(programmingTasks[task]);
break;
case 3:
case 4:
return string(mobileTasks[task]);
break;
case 5:
return string(webProgrammingTasks[task]);
break;
case 6:
return string(systemProgrammingTask[task]);
break;
case 7:
return string(adminTasks[task]);
break;
case 0:
default:
return txt(level1Task[task], level1TaskWords[word]);
break;
}
}
void showLearnDialog() {
string s = selectCourseText;
for (unsigned short i = 0; i < coursesNumber; i++) {
if (!coursesUnlocked[i])
continue;
if (i == 2) {
s+=txt("3.%s\n", courses[2]);
for (int j = 0; j < advancedProgrammingCourses; j++) {
s+=txt(" %c.%s [%c]", (char)('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+="\n";
}
continue;
}
s+=txt("%c.%s [%c]", (char)('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+="\n";
}
drawWindow(s, coursesTitle ,"", number_or_enter);
unsigned char answer = 0;
answer = getKey();
if (answer == ENTER_KEY)
return;
else if (answer >='1' && answer <=(char)('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, "", enter, true);
wait();
drawWindow(gameIntroTextPart2, introTitle, "", enter, true);
wait();
drawWindow(gameIntroTextPart3, introTitle, "", enter, true);
wait();
// Goals
drawWindow(gameIntroPlan, 0, "", enter, true);
wait();
// Desktop
drawWindow("", introDesktop, "", enter);
wait();
// Top line
drawWindow(introStatusLine,"", getStatusLine(), enter);
wait();
// Keys
drawWindow("", "", "", 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<long unsigned int>(playerSpentRealDays),
static_cast<long unsigned int>(minutesInGame),
playerMessagesRead,
playerHelped,
playerDidntHelped,
static_cast<long unsigned int>(playerMoneyEarned),
static_cast<long unsigned int>(playerMoneySpent),
static_cast<long unsigned int>(playerMoneySpentForCharity),
playerTimeHops};
string s = "\n<c>";
s += playerStatsTitle;
for (int i = 0; i < 9; i++) {
s += txt(playerStats[i], static_cast<int>(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::string s = youWin;
s += gameOver;
drawWindow(s, congratsMsg, "", 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)) {
string s = gameOverLogo;
s += gameOverText;
drawWindow(s, gameOverTitle, "", 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::string getStatusLine() {
string s = txt(statusLine, (int)gameTime, (int)playerMoney, karma(), (int)floor(unreadMessages));
if (showCoursesTab)
s += txt(statusLineCoursesNotFinished, getAvailableCoursesCount());
return s;
}
string getBottomLine() {
string s;
if (showCoursesTab)
s += bottomLineCourses;
s += bottomLineMsgAchieves;
if (charityUnlocked) {
s += charityTitle;
s += "[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;
string s = printExactTask(newTaskLevel, task, word);
s += txt(yourAnswer,levelYesAnswer[yes],levelNoAnswer[no]);
string 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;
char 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(string e = "") {
string s;
if (dt >= 1.0)
s = txt(daysPerSecond, (int)dt);
else {
s = txt(daysPerSecond, 0);
s += txt(".%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);
char 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(" 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(" min");
}
return showChangeSpeedDialog();
}
return dt;
}
// returns 1 if program have to be closed
bool keyPressed(char 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 (!charityUnlocked)
break;
if (playerMoney <= 0) {
drawModalWindow(notEnoughMoneyForCharity, 0, enter);
wait();
break;
}
drawModalWindow(charityQuestion, charityTitle, yesNoDialog);
char 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);
char 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<event> 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: {
string 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;
string s;
int letterIndex = e->idata*2;
s += specialLetters[letterIndex];
s += shitCodeYourAnswer;
drawWindow(s, new_letter, getStatusLine(), one_or_two);
char 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) {
string 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) {
string s = specialLetters[7];
s += shitCodeYourAnswer;
drawWindow(s, new_letter, getStatusLine(), one_or_two);
char 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: {
string 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);
char 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: {
string 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;
string s = txt(specialLetters[14], needMoney);
s += unnamedStudio3Answer;
drawWindow(s, new_letter, getStatusLine(), one_or_two);
char 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: {
string 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);
char 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: {
string s = specialLetters[11];
s += angryBossAnswer;
drawWindow(s, new_letter, getStatusLine(), one_or_two);
char 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 {
string 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);
char 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
string getDesktop(int &lines) {
string s = "\n\n";
// Information about courses
if (!pq.hasCourses()) {
s += noCurrentCurses;
}
else {
// Check courses in pq
s += " ";
s += coursesTitle;
s += ":\n";
for(int i = 0; i< pq.n(); i++) {
event* e = pq.get(i);
if (e->type == COURSE) {
s += " ";
if (e->idata >= 0 && e->idata <= 7) {
s += courses[e->idata];
s += " ";
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 += " ";
else
s += pseudoEqual;
}
s += txt(" %d%\n", percent);
}
else if (e->idata >= 'a' && e->idata <= 'e') {
s += advProgCourses[e->idata - 'a'];
s += " ";
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+=" ";
else
s+=pseudoEqual;
}
s += txt(" %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();
string status = playerStatus;
status += helpDesker;
int currentPath = playerPath;
playerStartedPlaying = time(NULL);
string 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;
string s = getDesktop(lines);
for (int i = 0; i < 11-lines; i++)
s += "\n";
s += " ";
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 += " ";
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
string 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()) {
char 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 = "";
oldStatusLine = "";
}
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<event> 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;
}