forked from KolibriOS/kolibrios
d6631a482a
git-svn-id: svn://kolibrios.org@5299 a494cfbc-eb01-0410-851d-a64ba20cac60
1585 lines
61 KiB
C++
Executable File
1585 lines
61 KiB
C++
Executable File
/******************************************************************
|
|
* 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;
|
|
}
|