Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5299 igevorse 1
/******************************************************************
2
*   21 days: a game for programmers
3
*   Copyright (C) 2014 Maxim Grishin
4
*
5
*   This program is free software; you can redistribute it and/or
6
*   modify it under the terms of the GNU General Public License
7
*   as published by the Free Software Foundation; either version 2
8
*   of the License, or (at your option) any later version.
9
*
10
*   This program is distributed in the hope that it will be useful,
11
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
*   GNU General Public License for more details.
14
*
15
*   You should have received a copy of the GNU General Public License
16
*   along with this program; if not, write to the Free Software
17
*   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
*   MA  02110-1301, USA.
19
*******************************************************************/
20
 
21
#define DEFAULT_PATH  0
22
#define WORK_PATH     1
23
#define STARTUP_PATH  2
24
 
25
#include "sys.h"
26
#include "strings.h"
27
#include "interface.h"
28
#include "pq.h"
29
 
30
#ifdef _KOS32
31
    #include 
32
    #define vector vector21
33
extern "C" {
34
    void *__dso_handle = 0;
35
    void *__cxa_atexit = 0;
36
    void *__stack_chk_fail = 0;
37
    }
38
#else
39
    #define printf2 printf
40
    #include   //srand
41
    #include 
42
    using std::vector;
43
    #include   // usleep
44
#endif
45
 
46
#include 
47
#include 
48
using std::string;
49
 
50
Undo2 history;
51
 
52
/**************************************
53
*   Player
54
*****************************************/
55
long double playerMoney = 100;
56
int playerSalary = 0;
57
int playerSalaryFirstDay = 0;
58
int playerPath = DEFAULT_PATH;
59
int playerKarma = 20;
60
double dt = 1.0; // days per secons
61
long double gameTime = 0;   // Time in days
62
double unreadMessages = 0;
63
unsigned int timeHops = 0;
64
bool allAchievesShowed = false;
65
bool charityUnlocked = false;
66
 
67
int  playerFreelanceAttempts = 0;
68
bool controlFreelanceAttempts = false;
69
 
70
bool sentBotsmannLetter = false;
71
bool astraLetterSent = false;
72
bool shitCodeDetected = false;
73
bool shitLettersSent[3] = {0,0,0};
74
bool shitLettersFinished[3] = {0,0,0};
75
bool showCoursesTab = false;
76
PQ3 pq;  // events priority queue
77
bool  noPopularity = false;
78
short finalCardsUnlocked = 0;
79
bool  finalCardUnlocked[5] = {0,0,0,0,0};
80
bool  returnTo21HintShowed = false;
81
// Player Stats
82
time_t playerStartedPlaying;
83
unsigned long playerTimeHops = 0;
84
unsigned long playerMessagesRead = 0;
85
unsigned long playerHelped = 0;
86
unsigned long playerDidntHelped = 0;
87
long double playerSpentRealDays = 0;
88
long double playerPrevUndo = 0;
89
long double playerMoneyEarned = 100;
90
long double playerMoneySpent = 0;
91
double playerMoneySpentForCharity = 0;
92
 
93
 
94
void mainGameCycle();
95
int  showSettingsScreen();
96
int  chooseCourse();
97
void showAchievesScreen();
98
 
99
void startNewGame();
100
void initSendingMessage();
101
void undo(long double toTime);
102
 
103
string getStatusLine();
104
 
105
void addTimer(event e) {
106
    if (e.type == SPECIAL_LETTER &&
107
        (e.idata == LETTER_SHITCODE_1
108
        || e.idata == LETTER_SHITCODE_2
109
        || e.idata == LETTER_SHITCODE_3)
110
    && pq.containsType(e.type, e.idata))
111
        return;
112
    if (e.type == MESSAGE && pq.containsType((int)e.type))
113
        return;
114
 
115
    pq.insert(e);
116
    history.insert(event(e.time + gameTime, e.type, e.idata));
117
    }
118
 
119
void unlockFinalCards(short p) {
120
    if (!finalCardUnlocked[p])
121
        finalCardsUnlocked++;
122
 
123
    finalCardUnlocked[p] = true;
124
    }
125
 
126
void increaseMoney(long double m) {
127
    playerMoney += m;
128
    if (m > 0)
129
        playerMoneyEarned += m;
130
    if (m < 0)
131
        playerMoneySpent -= m;
132
    }
133
 
134
void increaseKarma(int k, bool addTimerMsg = true) {
135
    if (playerKarma <= 0 && playerKarma+k>0)
136
        initSendingMessage();
137
    int oldKarma = playerKarma;
138
    playerKarma += k;
139
    if (playerKarma < -100)
140
        playerKarma = -100;
141
 
142
    if (playerKarma > 100)
143
        playerKarma = 100;
144
 
145
    if (addTimerMsg && playerKarma - oldKarma != 0)
146
        history.insert(event(2*dt, INCREASEPOPULARITY, playerKarma - oldKarma));
147
    }
148
 
149
void changePath(int to) {
150
    history.insert(event(gameTime, CHANGEPATH, playerPath));
151
    playerPath = to;
152
    }
153
 
154
void changeSalary(int to) {
155
    history.insert(event(gameTime, CHANGESALARY, playerSalary));
156
    playerSalary = to;
157
    }
158
 
159
void changeSalaryFirstDay(int to) {
160
    history.insert(event(gameTime, CHANGESALARYFIRSTDAY, playerSalaryFirstDay));
161
    playerSalaryFirstDay = to;
162
    }
163
 
164
int karma() {
165
    return playerKarma;
166
    }
167
 
168
void initSendingMessage() {
169
    double newTime = (karma() <= 0)?0.0:0.5 ;//* karma() / 100.0;
170
    if (newTime > 0) {
171
        addTimer(event(newTime, MESSAGE));
172
        }
173
    else {
174
        noPopularity = true;
175
        addTimer(event(0, NOPOPULARITY));
176
        }
177
    }
178
 
179
void checkAchieves() {
180
    /// No popularity
181
    if (!achievesPicked[1] && noPopularity)
182
        achievesPicked[1] = true;
183
    /// Millionaire
184
    if (!achievesPicked[3] && playerMoney >= 1000000.0) {
185
        achievesPicked[3] = true;
186
        charityUnlocked = true;
187
        }
188
    /// Time jumper
189
    if (!achievesPicked[8] && timeHops >= 3)
190
        achievesPicked[8] = true;
191
 
192
    allAchievesShowed = true;
193
    /// Show achieve notification
194
    for (unsigned int i = 0; i < achievesNumber; i++) {
195
        if (achievesPicked[i] && !achievesShowed[i]) {
196
            drawModalWindow(achieves[i], newAchievement);
197
            wait();
198
            if (i == 3) {
199
                drawModalWindow(pressRForCharity, 0, "[R]");
200
                wait('r','R');
201
                }
202
            achievesShowed[i] = true;
203
            }
204
        else if (!achievesShowed[i])
205
            allAchievesShowed = false;
206
        }
207
    }
208
 
209
void showMainMenu() {
210
    string s = days21;
211
    s+=mainMenuText;
212
    drawWindow(s, gameName, "", txt("%s    %s", one_or_two, developer));
213
    char menu = getAnswer('1', '2');
214
 
215
    switch(menu) {
216
        case '1':
217
            startNewGame();
218
        break;
219
        case '2': // TODO: Save?
220
 
221
        break;
222
        }
223
    }
224
 
225
void showAchievesScreen() {
226
    string s;
227
    for (unsigned short i = 0; i < achievesNumber; i++)
228
        s += txt("[%c] %s \n", achievesPicked[i]?'X':' ', achieves[i]);
229
 
230
    s += txt(finalCardsUnlockedText, finalCardsUnlocked, 5);
231
    char totalCoursesCount = 12;
232
    s += txt(coursesFinishedText, (int)finishedCoursesCount, (int)totalCoursesCount);
233
    drawWindow(s, achievementsTitle, getStatusLine(), enter);
234
    }
235
 
236
string printExactTask(int course, int task, int word) {
237
    switch (course) {
238
        case 1:
239
        case 2:
240
            return string(programmingTasks[task]);
241
            break;
242
        case 3:
243
        case 4:
244
            return string(mobileTasks[task]);
245
            break;
246
        case 5:
247
            return string(webProgrammingTasks[task]);
248
            break;
249
        case 6:
250
            return string(systemProgrammingTask[task]);
251
            break;
252
        case 7:
253
            return string(adminTasks[task]);
254
            break;
255
        case 0:
256
        default:
257
            return txt(level1Task[task], level1TaskWords[word]);
258
            break;
259
        }
260
    }
261
 
262
void showLearnDialog() {
263
    string s = selectCourseText;
264
    for (unsigned short i = 0; i < coursesNumber; i++) {
265
        if (!coursesUnlocked[i])
266
            continue;
267
 
268
        if (i == 2) {
269
            s+=txt("3.%s\n", courses[2]);
270
            for (int j = 0; j < advancedProgrammingCourses; j++) {
271
                s+=txt("     %c.%s [%c]", (char)('a'+j), advProgCourses[j], (advProgCoursesFinished[j])?'X':' ');
272
                if (!advProgCoursesFinished[j] && !pq.hasCourse('a'+j))
273
                    s+=txt(takeCourseCost, advProgLearningCost[j]);
274
                else if (pq.hasCourse('a'+j))
275
                    s+=takingThisCourse;
276
                else
277
                    s+="\n";
278
            }
279
            continue;
280
        }
281
 
282
        s+=txt("%c.%s [%c]", (char)('1'+i), courses[i], (coursesFinished[i])?'X':' ');
283
        if (!coursesFinished[i] && !pq.hasCourse(i))
284
            s+=txt(takeCourseCost, learningCost[i]);
285
        else if (pq.hasCourse(i)) {
286
            s+=takingThisCourse;
287
        }
288
        else
289
            s+="\n";
290
    }
291
 
292
    drawWindow(s, coursesTitle ,"", number_or_enter);
293
    unsigned char answer = 0;
294
    answer = getKey();
295
 
296
    if (answer == ENTER_KEY)
297
        return;
298
 
299
    else if (answer >='1' && answer <=(char)('0'+coursesNumber) && answer != '3') {
300
        answer -- ;
301
        answer -='0';
302
 
303
        if (coursesUnlocked[answer]){
304
            if (coursesFinished[answer]) {
305
                drawModalWindow(courseAlreadyPassed, errorMsg);
306
                wait();
307
                showLearnDialog();
308
                return;
309
                }
310
            else if (playerMoney > learningCost[answer] && !pq.hasCourse(answer)) {
311
                drawModalWindow(txt(successfullyEnrolled, courses[answer]).c_str(), congratsMsg);
312
                wait();
313
                increaseMoney(-learningCost[answer]);
314
                addTimer(event(learningTime[answer], COURSE, answer));
315
                }
316
            else if (playerMoney <= learningCost[answer] && !pq.hasCourse(answer)){
317
                drawModalWindow(notEnoughMoney, errorMsg);
318
                wait();
319
                showLearnDialog();
320
                }
321
            else
322
                showLearnDialog();
323
            return;
324
            }
325
        else
326
            showLearnDialog();
327
        }
328
    else if (answer == '3' && coursesUnlocked[2]) {
329
        drawModalWindow(ae_advanced_courses,errorMsg);
330
        wait();
331
        showLearnDialog();
332
        }
333
    /// only small letters
334
    else if (answer >='a' && answer <= 'e') {
335
        answer -= 'a';
336
        if (coursesUnlocked[2] && playerMoney > advProgLearningCost[answer] && !advProgCoursesFinished[answer] && !pq.hasCourse('a'+answer)) {
337
            drawModalWindow(txt(successfullyEnrolled,advProgCourses[answer]).c_str(), congratsMsg);
338
            wait();
339
 
340
            increaseMoney(-advProgLearningCost[answer]);
341
            int learnCourseNumber = answer+'a';
342
            if (advProgLearningTime[answer] != 0 && learnCourseNumber != -1)
343
                addTimer(event(advProgLearningTime[answer], COURSE, learnCourseNumber));
344
            return;
345
            }
346
        else if (coursesUnlocked[2] && playerMoney <= advProgLearningCost[answer] && !advProgCoursesFinished[answer] && !pq.hasCourse('a'+answer)) {
347
            drawModalWindow(notEnoughMoney, errorMsg);
348
            wait();
349
            showLearnDialog();
350
            }
351
        else if (advProgCoursesFinished[answer]) {
352
            drawModalWindow(courseAlreadyPassed, errorMsg);
353
            wait();
354
            showLearnDialog();
355
            return;
356
            }
357
        else
358
            showLearnDialog();
359
        }
360
    else if (answer == '3') {
361
        showLearnDialog();
362
        }
363
    else if (coursesUnlocked[2]) {
364
        drawModalWindow(ae_advanced_courses,errorMsg);
365
        wait();
366
        showLearnDialog();
367
        }
368
    else
369
        showLearnDialog();
370
    return;
371
    }
372
 
373
void startNewGame() {
374
    // Intro
375
    drawWindow(gameIntroTextPart1, introTitle, "", enter, true);
376
    wait();
377
    drawWindow(gameIntroTextPart2, introTitle, "", enter, true);
378
    wait();
379
    drawWindow(gameIntroTextPart3, introTitle, "", enter, true);
380
    wait();
381
    // Goals
382
    drawWindow(gameIntroPlan, 0, "", enter, true);
383
    wait();
384
    // Desktop
385
    drawWindow("", introDesktop, "", enter);
386
    wait();
387
    // Top line
388
    drawWindow(introStatusLine,"", getStatusLine(), enter);
389
    wait();
390
    // Keys
391
    drawWindow("", "", "", introAllowedKeys);
392
    wait('1');
393
    // Dialog window
394
    drawModalWindow(introFinished, introLetsBegin);
395
    wait();
396
    // Start game!
397
    mainGameCycle();
398
    }
399
 
400
void showFinalStats() {
401
    // Show player stats
402
    int minutesInGame = (time(NULL) - playerStartedPlaying)/60;
403
    playerSpentRealDays += (gameTime - playerPrevUndo);
404
    long unsigned int val[9] = {
405
        static_cast(playerSpentRealDays),
406
        static_cast(minutesInGame),
407
        playerMessagesRead,
408
        playerHelped,
409
        playerDidntHelped,
410
        static_cast(playerMoneyEarned),
411
        static_cast(playerMoneySpent),
412
        static_cast(playerMoneySpentForCharity),
413
        playerTimeHops};
414
    string s = "\n";
415
    s += playerStatsTitle;
416
    for (int i = 0; i < 9; i++) {
417
        s += txt(playerStats[i], static_cast(val[i]));
418
        if (i == 0 || i == 3 || i == 4)
419
            s += getWordEnding(val[i], i);
420
        }
421
    s += playerStatsEnd;
422
    drawWindow(s);
423
    wait(ESCAPE_KEY, ESCAPE_KEY);
424
    }
425
 
426
bool isGameOver() {
427
    if (noPopularity && !finalCardUnlocked[3] && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_NOPOPULARITY)) {
428
        addTimer(event(2.0, SPECIAL_LETTER, LETTER_FINALPATH_NOPOPULARITY));
429
        return false;
430
        }
431
 
432
    if ((playerPath == DEFAULT_PATH || playerPath == STARTUP_PATH) && playerMoney < 0.0 && !finalCardUnlocked[4]  && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_NOMONEY)) {
433
        addTimer(event(2.0, SPECIAL_LETTER, LETTER_FINALPATH_NOMONEY));
434
        return false;
435
        }
436
    // You win!
437
    if (finishedCoursesCount >= 12 && finalCardsUnlocked >= 5 && gameTime <= 22.0 && allAchievesShowed) {
438
        std::string s = youWin;
439
        s += gameOver;
440
        drawWindow(s, congratsMsg, "", pressF);
441
        wait('f','F');
442
        showFinalStats();
443
        return true;
444
        }
445
    // Game over
446
    if (playerMoney < 0 && karma() < 0 && noPopularity && floor(unreadMessages) == 0 && gameTime > 22.0 &&
447
        achievesShowed[1] && finalCardUnlocked[3] && finalCardUnlocked[4] &&
448
        !pq.containsType(SPECIAL_LETTER) && !pq.containsType(MESSAGE)) {
449
        string s = gameOverLogo;
450
        s += gameOverText;
451
        drawWindow(s, gameOverTitle, "", pressF);
452
        wait('f','F');
453
        showFinalStats();
454
        return true;
455
        }
456
    return false;
457
    }
458
 
459
int getAvailableCoursesCount() {
460
    int a = 0;
461
    for (unsigned int i = 0; i < coursesNumber; i++) {
462
        if (i!= 2 && coursesUnlocked[i] && !coursesFinished[i])
463
            a++;
464
        }
465
    // Anvanced programming
466
    if (coursesUnlocked[2]) {
467
        for (int i = 0; i < advancedProgrammingCourses; i++)
468
            if (!advProgCoursesFinished[i])
469
                a++;
470
        }
471
    return a;
472
    }
473
 
474
std::string getStatusLine() {
475
    string s = txt(statusLine, (int)gameTime, (int)playerMoney, karma(), (int)floor(unreadMessages));
476
    if (showCoursesTab)
477
        s += txt(statusLineCoursesNotFinished, getAvailableCoursesCount());
478
    return s;
479
    }
480
 
481
string getBottomLine() {
482
    string s;
483
    if (showCoursesTab)
484
        s += bottomLineCourses;
485
    s += bottomLineMsgAchieves;
486
    if (charityUnlocked) {
487
        s += charityTitle;
488
        s += "[R]    ";
489
        }
490
    s += bottomLineSpeedAndExit;
491
    return s;
492
    }
493
 
494
int showUnreadMessages() {
495
    int newTaskLevel = chooseCourse();
496
 
497
    unsigned int task = rand() % taskCount[newTaskLevel];
498
    unsigned int word = rand() % level1TaskWordNumber;
499
    unsigned int yes =  rand() % levelYesAnswerNumber;
500
    unsigned int no  =  rand() % levelNoAnswerNumber;
501
 
502
    string s = printExactTask(newTaskLevel, task, word);
503
    s += txt(yourAnswer,levelYesAnswer[yes],levelNoAnswer[no]);
504
    string buttons = one_or_two;
505
    buttons += escToBreakReading;
506
    drawWindow(s, unread_message, getStatusLine(), buttons);
507
    return newTaskLevel;
508
    }
509
 
510
bool messageGetAnswer(int level) {
511
    long double reward = taskReward[level] + rand() % 20;
512
    char answerKey = getAnswer('1', '2', ESCAPE_KEY);
513
 
514
    switch (answerKey) {
515
        case '1':
516
            // Player Stats
517
            playerHelped++;
518
            increaseMoney(reward);
519
            increaseKarma(reward/5.0, false);
520
            if (playerPath == WORK_PATH && controlFreelanceAttempts) {
521
                playerFreelanceAttempts++;
522
                if (playerFreelanceAttempts > 5) {
523
                    addTimer(event(1.0, SPECIAL_LETTER, LETTER_ANGRYBOSS_2));
524
                    controlFreelanceAttempts = false;
525
                    }
526
                }
527
            // Repairing computer takes 0.5 days
528
            gameTime += 0.5;
529
            break;
530
        case '2':
531
            // Player Stats
532
            playerDidntHelped++;
533
            increaseKarma(-reward/2.0, false);
534
            break;
535
        case ESCAPE_KEY:
536
            return true;
537
            break;
538
        }
539
    return false;
540
    }
541
 
542
double showChangeSpeedDialog(string e = "") {
543
    string s;
544
    if (dt >= 1.0)
545
        s = txt(daysPerSecond, (int)dt);
546
    else {
547
        s = txt(daysPerSecond, 0);
548
        s += txt(".%d", (int)(dt*10)%10); // Actually not the best way of printing double values
549
        }
550
    if (!e.empty())
551
        s += e;
552
    drawModalWindow(s.c_str(), changeSpeedTitle, changeSpeedButtons);
553
    char ch = getAnswer('+','-','Y','y');
554
    if (ch == 'y' || ch == 'Y')
555
        return dt;
556
    else if (ch == '+') {
557
        if (dt >= 1.0)
558
            dt += 1.0;
559
        else
560
            dt += 0.10;
561
        if (dt >= 10.0) {
562
            dt = 10.0;
563
            return showChangeSpeedDialog(" max");
564
            }
565
        return showChangeSpeedDialog();
566
        }
567
    else if (ch == '-') {
568
        if (dt >= 2.0)
569
            dt -= 1.0;
570
        else
571
            dt -= 0.10;
572
        if (dt <= 0.10) {
573
            dt = 0.10;
574
            return showChangeSpeedDialog(" min");
575
            }
576
        return showChangeSpeedDialog();
577
        }
578
    return dt;
579
    }
580
 
581
// returns 1 if program have to be closed
582
bool keyPressed(char k) {
583
    switch (k) {
584
        // Courses
585
        case 'c':
586
        case 'C':
587
            if (!showCoursesTab)
588
                break;
589
            showLearnDialog();
590
            break;
591
        // Achieves
592
        case 'a':
593
        case 'A':
594
            showAchievesScreen();
595
            wait();
596
            break;
597
        // Messages
598
        case 'm':
599
        case 'M': {
600
            int oldPopularity = karma();
601
            // Player Stats
602
            bool breakReading = false;
603
            while (floor(unreadMessages) > 0) {
604
                breakReading = messageGetAnswer(showUnreadMessages());
605
                if (breakReading) {
606
                    break;
607
                    }
608
                else {
609
                    if (floor(unreadMessages) > 0.0) unreadMessages--;
610
                    playerMessagesRead++;
611
                    }
612
                }
613
            if (!breakReading) {
614
                drawModalWindow(noUnreadMessages);
615
                wait();
616
                }
617
            if (karma() != oldPopularity)
618
                history.insert(event(gameTime, INCREASEPOPULARITY, karma()-oldPopularity));
619
            // Hack
620
            #ifdef _KOS32
621
            initSendingMessage();
622
            #endif
623
            }
624
            break;
625
        case 'u':
626
        case 'U':
627
            undo(gameTime - 10);
628
            break;
629
        case 'r':
630
        case 'R': {
631
            if (!charityUnlocked)
632
                break;
633
 
634
            if (playerMoney <= 0) {
635
                drawModalWindow(notEnoughMoneyForCharity, 0, enter);
636
                wait();
637
                break;
638
                }
639
            drawModalWindow(charityQuestion, charityTitle, yesNoDialog);
640
            char ch = getAnswer('y', 'n', 'Y', 'N');
641
            if (ch == 'y' || ch == 'Y') {
642
                // Player Stats
643
                playerMoneySpentForCharity += (playerMoney / 2.0);
644
 
645
                increaseMoney(-playerMoney / 2.0);
646
                increaseKarma(abs(karma()));
647
                }
648
            }
649
            break;
650
        case 's':
651
        case 'S':
652
            dt = showChangeSpeedDialog();
653
            break;
654
        // Escape
655
        case ESCAPE_KEY: {
656
            drawModalWindow(doYouReallyWantToExit,0 , yesNoDialog);
657
            char ans = getAnswer('y','Y','n','N');
658
            if (ans == 'y' || ans == 'Y')
659
                return true;
660
            }
661
            break;
662
        }
663
    return false;
664
    }
665
 
666
void breakingNews(int newsID) {
667
    drawWindow(news[newsID], breaking_news, getStatusLine(), enter);
668
    wait();
669
    }
670
 
671
void checkPq() {
672
    vector lateEvents;
673
    // Messages in PQ are in reverse order
674
    for (int i = pq.n()-1; i >= 0; i--) {
675
        event* e = pq.get(i);
676
        if (e->time > dt) {
677
            e->time -= dt;
678
            continue;
679
            }
680
        switch (e->type) {
681
            case COURSE: {
682
                string s;
683
 
684
                if (e->idata >= 0 && e->idata < (int)coursesNumber) {
685
                    s += txt(courseSuccessfullyFinished,courses[e->idata]);
686
                    // Not the best fix for the bug with mobile dev
687
                    if (!coursesFinished[e->idata])
688
                        finishedCoursesCount++;
689
                    coursesFinished[e->idata] = true;
690
                    }
691
                // Advanced programming
692
                else if (e->idata >='a' && e->idata <= 'e') {
693
                    e->idata -= 'a';
694
                    s += txt(courseSuccessfullyFinished,advProgCourses[e->idata]);
695
                    if (!advProgCoursesFinished[e->idata])
696
                        finishedCoursesCount++;
697
                    advProgCoursesFinished[e->idata] = true;
698
                    }
699
                increaseKarma(+5);
700
 
701
                pq.delMin();
702
                drawModalWindow(s.c_str(), congratsMsg);
703
                wait();
704
                }
705
                break;
706
            case SPECIAL_LETTER:
707
                switch (e->idata) {
708
                    case LETTER_RETURN_TO_DAY_21:
709
                        drawModalWindow(returnToDay21, 0, pressP);
710
                        wait('p','P');
711
                        returnTo21HintShowed = true;
712
                        break;
713
                    case LETTER_SHITCODE_1:
714
                    case LETTER_SHITCODE_2:
715
                    case LETTER_SHITCODE_3: {
716
                        if (playerPath == WORK_PATH)
717
                            break;
718
                        string s;
719
                        int letterIndex = e->idata*2;
720
                        s += specialLetters[letterIndex];
721
                        s += shitCodeYourAnswer;
722
                        drawWindow(s, new_letter, getStatusLine(), one_or_two);
723
 
724
                        char ch = getAnswer('1', '2');
725
                        if (ch == '1') {
726
                            if (e->idata == LETTER_SHITCODE_1 && advProgCoursesFinished[0] && advProgCoursesFinished[1]) {
727
                                // sussessfull
728
                                increaseMoney(300.0);
729
                                increaseKarma(+5);
730
                                drawWindow(txt(specialLetters[24], (int)gameTime, 300), answerLetter, getStatusLine(), enter);
731
                                shitLettersFinished[0] = true;
732
                                }
733
                            else if (e->idata == LETTER_SHITCODE_2 && advProgCoursesFinished[2]) {
734
                                // sussessfull
735
                                increaseMoney(500.0);
736
                                increaseKarma(+5);
737
                                drawWindow(txt(specialLetters[24], (int)gameTime, 500), answerLetter, getStatusLine(), enter);
738
                                shitLettersFinished[1] = true;
739
                                }
740
                            else if (e->idata == LETTER_SHITCODE_3 && advProgCoursesFinished[3]) {
741
                                // sussessfull
742
                                increaseMoney(800.0);
743
                                increaseKarma(+5);
744
                                drawWindow(txt(specialLetters[24], (int)gameTime, 800), answerLetter, getStatusLine(), enter);
745
                                shitLettersFinished[2] = true;
746
                                }
747
                            else {
748
                                drawModalWindow(specialLetters[letterIndex+1], answerLetter);
749
                                coursesUnlocked[2] = true;
750
                                shitCodeDetected = true;
751
                                increaseKarma(-5);
752
                                }
753
 
754
                            if (shitLettersFinished[0] && shitLettersFinished[1] && shitLettersFinished[2])
755
                                achievesPicked[0] = true;
756
 
757
                            wait();
758
                            }
759
                        else if (ch == '2') {
760
                            increaseKarma(-2);
761
                            }
762
                        }
763
                        break;
764
                    case LETTER_BOTSMANN:
765
                        if (playerPath == DEFAULT_PATH) {
766
                            string s = specialLetters[6];
767
                            drawWindow(s, new_letter, getStatusLine(), enter);
768
                            wait();
769
                            lateEvents.push_back(event(10.0, SPECIAL_LETTER, LETTER_ASTRA));
770
                            }
771
                        break;
772
                    case LETTER_ASTRA:
773
                        if (playerPath == DEFAULT_PATH) {
774
                            string s = specialLetters[7];
775
                            s += shitCodeYourAnswer;
776
 
777
                            drawWindow(s, new_letter, getStatusLine(), one_or_two);
778
                            char ch = getAnswer('1', '2');
779
                            if (ch == '1') {
780
                                s = specialLetters[8];
781
                                drawWindow(s, new_letter, getStatusLine(), enter);
782
                                increaseKarma(-10);
783
                                }
784
                            else if (ch == '2') {
785
                                s = specialLetters[9];
786
                                drawWindow(s, new_letter, getStatusLine(), enter);
787
                                // Unlock mobile development
788
                                coursesUnlocked[3] = true;
789
                                if (!coursesFinished[3] && !pq.hasCourse(3));
790
                                    lateEvents.push_back(event(learningTime[3], COURSE, 3));
791
                                changePath(WORK_PATH);
792
                                changeSalary(800);
793
                                changeSalaryFirstDay((int)gameTime);
794
                                increaseKarma(+5);
795
                                }
796
                            wait();
797
                            }
798
                        break;
799
                    case LETTER_UNNAMEDSTUDIO_1: {
800
                        string s = specialLetters[10];
801
                        if (playerPath == WORK_PATH) {
802
                            lateEvents.push_back(event(15.0, SPECIAL_LETTER, LETTER_ANGRYBOSS_1));
803
                            drawWindow(s, new_letter, getStatusLine(), enter);
804
                            wait();
805
                            }
806
                        else if(playerPath == DEFAULT_PATH) {
807
                            s += unnamedStudio1Answer;
808
                            drawWindow(s, new_letter, getStatusLine(), one_or_two);
809
                            char ch = getAnswer('1', '2');
810
                            if (ch == '1') {
811
                                changePath(STARTUP_PATH);
812
                                increaseMoney(-500);
813
                                lateEvents.push_back(event(12.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2));
814
                                }
815
                            }
816
                        }
817
                        break;
818
                    case LETTER_UNNAMEDSTUDIO_2: {
819
                        string s = specialLetters[13];
820
                        if (coursesFinished[4])
821
                            s+=specialLetters[27];
822
                        else
823
                            s+=specialLetters[26];
824
                        drawWindow(s, new_letter, getStatusLine(), enter);
825
                        // Design basics course unlocked
826
                        coursesUnlocked[4] = true;
827
                        unnamedStudioLettersSent[1] = true;
828
                        wait();
829
                        }
830
                        break;
831
                    case LETTER_UNNAMEDSTUDIO_3: {
832
                        int needMoney = (int)(playerMoney * 0.5);
833
                        if (needMoney > 50000)
834
                            needMoney = 50000;
835
                        else if (needMoney < 0)
836
                            needMoney *= -1;
837
                        string s = txt(specialLetters[14], needMoney);
838
                        s += unnamedStudio3Answer;
839
                        drawWindow(s, new_letter, getStatusLine(), one_or_two);
840
 
841
                        char ch = getAnswer('1', '2');
842
                        if (ch == '1') {
843
                            increaseMoney(-needMoney);
844
                            lateEvents.push_back(event(15.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4));
845
                            }
846
                        else if(ch == '2') {
847
                            changePath(DEFAULT_PATH);
848
                            achievesPicked[5] = true;
849
                            increaseKarma(-10);
850
                            }
851
                        }
852
                        break;
853
                    case LETTER_UNNAMEDSTUDIO_4: {
854
                        string s = specialLetters[15];
855
                        // Creating web-sites
856
                        if (coursesFinished[5]) {
857
                            s+=specialLetters[29];
858
                            drawWindow(s, new_letter, getStatusLine(), enter);
859
                            wait();
860
                            // Waiting for 40 days
861
                            lateEvents.push_back(event(40.0, SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO));
862
                            break;
863
                            }
864
                        int needMoney = (int)(playerMoney * 0.5);
865
                        if (needMoney > 50000)
866
                            needMoney = 50000;
867
                        else if (needMoney < 0)
868
                            needMoney *= -1;
869
                        s += txt(specialLetters[28], needMoney);
870
                        s += unnamedStudio4Answer;
871
                        drawWindow(s, new_letter, getStatusLine(), one_two_or_three);
872
                        char ch = getAnswer('1', '2', '3');
873
                        if (ch == '1') {
874
                            increaseMoney(-needMoney);
875
                            increaseKarma(+5);
876
                            // Pay for the webmaster
877
                            lateEvents.push_back(event(40.0, NEWS, 3));
878
                            }
879
                        else if (ch == '2') {
880
                            coursesUnlocked[5] = true;
881
                            lateEvents.push_back(event(40.0, SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO));
882
                            }
883
                        else if (ch == '3') {
884
                            changePath(DEFAULT_PATH);
885
                            achievesPicked[5] = true;
886
                            increaseKarma(-5);
887
                            }
888
                        }
889
                        break;
890
                    case LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO:
891
                        if (coursesFinished[5]) {
892
                            lateEvents.push_back(event(1.0, NEWS, 3));
893
                            }
894
                        else {
895
                            // SHow letter about fail
896
                            drawWindow(specialLetters[16], new_letter, getStatusLine(), enter);
897
                            wait();
898
                            drawModalWindow(startupFailedTip);
899
                            wait();
900
                            increaseMoney(1000);
901
                            achievesPicked[5] = true;
902
                            increaseKarma(-15);
903
                            }
904
                        break;
905
                    case LETTER_UNNAMEDSTUDIO_5: {
906
                        drawWindow(specialLetters[17], new_letter, getStatusLine(), enter);
907
                        increaseMoney(200000);
908
                        increaseKarma(+20);
909
                        wait();
910
                        achievesPicked[2] = true;
911
                        lateEvents.push_back(event(5.0, SPECIAL_LETTER, LETTER_FINALPATH_STARTUP));
912
                        }
913
                        break;
914
                    case LETTER_ANGRYBOSS_1: {
915
                        string s = specialLetters[11];
916
                        s += angryBossAnswer;
917
                        drawWindow(s, new_letter, getStatusLine(), one_or_two);
918
                        char ch = getAnswer('1', '2');
919
                        if (ch == '1') {
920
                            changeSalary(1200);
921
                            coursesUnlocked[5] = true;
922
                            coursesUnlocked[6] = true;
923
                            coursesUnlocked[7] = true;
924
                            playerFreelanceAttempts = 0;
925
                            controlFreelanceAttempts = true;
926
                            lateEvents.push_back(event(20.0, SPECIAL_LETTER, NO_POPULARITY_HINT));
927
                            }
928
                        else if(ch == '2') {
929
                            changePath(STARTUP_PATH);
930
                            changeSalary(0);
931
                            lateEvents.push_back(event(9.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2));
932
                            achievesPicked[4] = true;
933
                            increaseKarma(-10);
934
                            }
935
                        }
936
                        break;
937
                    case NO_POPULARITY_HINT:
938
                        if (!noPopularity && controlFreelanceAttempts && playerFreelanceAttempts < 5) {
939
                            drawWindow(specialLetters[25], new_letter, getStatusLine(), enter);
940
                            wait();
941
                            }
942
                        break;
943
                    case LETTER_ANGRYBOSS_2:
944
                        drawWindow(specialLetters[12], new_letter, getStatusLine(), enter);
945
                        changePath(DEFAULT_PATH);
946
                        changeSalary(0);
947
                        wait();
948
                        increaseKarma(-20);
949
                        break;
950
                    case LETTER_BORING_WORK:
951
                        if (playerPath == WORK_PATH) {
952
                            drawWindow(specialLetters[18], new_letter, getStatusLine(), enter);
953
                            wait();
954
                            lateEvents.push_back(event(15.0, SPECIAL_LETTER, LETTER_PERSISTENT_AND_PATIENT));
955
                            }
956
                        break;
957
                    case LETTER_BORING_DEFAULT_PATH:
958
                        if (playerPath == DEFAULT_PATH && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_DEF
959
                            && !pq.containsType(SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH))) {
960
                            drawWindow(specialLetters[19], new_letter, getStatusLine(), enter);
961
                            wait();
962
                            lateEvents.push_back(event(5.0, SPECIAL_LETTER, LETTER_FINALPATH_DEF));
963
                            }
964
                        break;
965
                    case LETTER_FINALPATH_DEF:
966
                        if (playerPath == DEFAULT_PATH) {
967
                            drawWindow(defaultFinalCard, finalCard, getStatusLine(), pressF);
968
                            unlockFinalCards(0);
969
                            wait('f', 'F');
970
                            }
971
                        break;
972
                    case LETTER_FINALPATH_WORK:
973
                        if (playerPath == WORK_PATH) {
974
                            drawWindow(workFinalCard, finalCard, getStatusLine(), pressF);
975
                            unlockFinalCards(1);
976
                            wait('f', 'F');
977
                            }
978
                    break;
979
                    case LETTER_FINALPATH_STARTUP:
980
                        if (playerPath == STARTUP_PATH) {
981
                            drawWindow(startupFinalCard, finalCard, getStatusLine(), pressF);
982
                            unlockFinalCards(2);
983
                            wait('f', 'F');
984
                            }
985
                        break;
986
                    case LETTER_FINALPATH_NOPOPULARITY:
987
                        drawWindow(zeroKarmaFinalCard, finalCard, getStatusLine(), pressF);
988
                        unlockFinalCards(3);
989
                        wait('f', 'F');
990
                        break;
991
                    case LETTER_FINALPATH_NOMONEY:
992
                        drawWindow(noMoneyFinalCard, finalCard, getStatusLine(), pressF);
993
                        unlockFinalCards(4);
994
                        wait('f', 'F');
995
                        break;
996
                    case LETTER_PERSISTENT_AND_PATIENT:
997
                        achievesPicked[7] = true;
998
                        lateEvents.push_back(event(2.0, SPECIAL_LETTER, LETTER_FINALPATH_WORK));
999
                        break;
1000
                    case LETTER_TEST_OF_KNOWLEDGE:
1001
                        drawWindow(specialLetters[20], new_letter, getStatusLine(), enter);
1002
                        wait();
1003
                        nextKnowledgeLetterIndex = 0;
1004
                        lateEvents.push_back(event(5.0, SPECIAL_LETTER, LETTER_KNOWLEDGE_QUESTION));
1005
                        break;
1006
                    case LETTER_KNOWLEDGE_QUESTION: {
1007
                        if (playerPath != WORK_PATH) {
1008
                            break;
1009
                            }
1010
 
1011
                        if (klowledgeCorrectAnswers == 5) {
1012
                            /// премия
1013
                            drawWindow(specialLetters[21], new_letter, getStatusLine(), enter);
1014
                            increaseMoney(400);
1015
                            wait();
1016
                            klowledgeCorrectAnswers = 0;
1017
                            }
1018
                        if (klowledgeUncorrectAnswers >= 5) {
1019
                            /// Didn't pass the knowledge check
1020
                            drawWindow(specialLetters[22], new_letter, getStatusLine(), enter);
1021
                            wait();
1022
                            changePath(DEFAULT_PATH);
1023
                            changeSalary(0);
1024
                            break;
1025
                            }
1026
                        if (nextKnowledgeLetterIndex > 27) {
1027
                            /// Knowledge check finished
1028
                            /// TODO: add a timer for this
1029
                            drawWindow(specialLetters[23], new_letter, getStatusLine(), enter);
1030
                            achievesPicked[6] = true;
1031
                            wait();
1032
                            lateEvents.push_back(event(30.0, SPECIAL_LETTER, LETTER_BORING_WORK));
1033
                            }
1034
                        else {
1035
                            string s = knowledgeCheck[nextKnowledgeLetterIndex];
1036
                            short yes = rand() % 2 + 1; // 1 or 2
1037
                            short no = (yes == 1)?2:1;
1038
                            s+=txt(yourAnswer, knowledgeCheck[nextKnowledgeLetterIndex+yes], knowledgeCheck[nextKnowledgeLetterIndex+no]);
1039
                            drawWindow(s, new_letter, getStatusLine(), one_or_two);
1040
                            char ch = getAnswer('1', '2');//getch();
1041
                            if ((ch - '0') == yes) {
1042
                                drawModalWindow(rightAnswer, congratsMsg);
1043
                                klowledgeCorrectAnswers++;
1044
                                }
1045
                            else {
1046
                                drawModalWindow(wrongAnswer, failMsg);
1047
                                klowledgeUncorrectAnswers++;
1048
                                }
1049
                            wait();
1050
                            nextKnowledgeLetterIndex+=3;
1051
                            lateEvents.push_back(event(7.0, SPECIAL_LETTER, LETTER_KNOWLEDGE_QUESTION));
1052
                            }
1053
                        }
1054
                        break;
1055
                    default:
1056
                    break;
1057
                }
1058
                pq.delMin();
1059
                break;
1060
            case MESSAGE:
1061
                unreadMessages+=(karma() > 0)?dt * karma()/50:0;
1062
                pq.delMin();
1063
                lateEvents.push_back(event(2*dt, MESSAGE));
1064
                break;
1065
            case NEWS: {
1066
                switch (e->idata) {
1067
                    // Programming in the world
1068
                    case 0:
1069
                        showCoursesTab = true;
1070
                        coursesUnlocked[1] = true;
1071
                        break;
1072
                    // Java programmers
1073
                    case 1:
1074
                        coursesUnlocked[2] = true;
1075
                        break;
1076
                    // Mobile development
1077
                    case 2:
1078
                        coursesUnlocked[3] = true;
1079
                        break;
1080
                    case 3:
1081
                        // Your game is very popular
1082
                        lateEvents.push_back(event(3.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5));
1083
                        break;
1084
                    default:
1085
                        break;
1086
                    }
1087
                breakingNews(e->idata);
1088
                pq.delMin();
1089
                }
1090
                break;
1091
            case NOPOPULARITY:
1092
            case INCREASEPOPULARITY:
1093
                // nop
1094
                pq.delMin();
1095
                break;
1096
            default:
1097
                break;
1098
            }
1099
        }
1100
    // Add new messages only after checking all existing messages
1101
    for (unsigned short i = 0; i< lateEvents.size(); i++) {
1102
        event e = lateEvents[i];
1103
        if (e.type == MESSAGE) {
1104
            initSendingMessage();
1105
            continue;
1106
            }
1107
        else if (e.type == SPECIAL_LETTER && e.idata == LETTER_ASTRA)
1108
            astraLetterSent = true;
1109
        addTimer(e);
1110
        }
1111
    }
1112
 
1113
void makeStory() {
1114
    switch (playerPath) {
1115
        case DEFAULT_PATH:
1116
            if (playerMoney > 200.0 && !showCoursesTab && !pq.containsType(NEWS, 0)) { // Basics of programming
1117
                addTimer(event(10.0, NEWS, 0));
1118
                newsShowed[0] = true;
1119
                }
1120
            else if (playerMoney > 300.0 && !shitCodeDetected && !shitLettersSent[0] && coursesFinished[1]) {
1121
                addTimer(event(9.0, SPECIAL_LETTER, LETTER_SHITCODE_1));
1122
                shitLettersSent[0] = true;
1123
                }
1124
            else if (playerMoney > 400.0 && !newsShowed[2] && coursesFinished[1] && !pq.containsType(NEWS, 2)) {   // Frappy Perd
1125
                addTimer(event(9.0, NEWS, 2));
1126
                newsShowed[2] = true;
1127
                }
1128
            else if (playerMoney > 500 && !sentBotsmannLetter && coursesFinished[1]) {   // Letter from Botsmann
1129
                addTimer(event(17.0, SPECIAL_LETTER, LETTER_BOTSMANN));
1130
                sentBotsmannLetter = true;
1131
                }
1132
            else if (playerMoney > 600.0 && !shitCodeDetected && !shitLettersSent[1] && coursesFinished[1] && !pq.containsType(SPECIAL_LETTER, LETTER_SHITCODE_1)) {
1133
                addTimer(event(13.0, SPECIAL_LETTER, LETTER_SHITCODE_2));
1134
                shitLettersSent[1] = true;
1135
                }
1136
            else if (playerMoney > 800.0 && sentBotsmannLetter && !unnamedStudioLettersSent[0] && coursesFinished[3] && astraLetterSent && !pq.containsType(SPECIAL_LETTER, LETTER_ASTRA)) { // Startup
1137
                addTimer(event(15.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1));
1138
                unnamedStudioLettersSent[0] = true;
1139
                }
1140
            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
1141
                addTimer(event(14.0, SPECIAL_LETTER, LETTER_SHITCODE_3));
1142
                shitLettersSent[2] = true;
1143
                }
1144
            else if (getAvailableCoursesCount() == 0 && playerMoney > 2000 && sentBotsmannLetter
1145
                && unnamedStudioLettersSent[0] && !finalCardUnlocked[0] && !pq.containsType(SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH)) {
1146
                addTimer(event(10.0, SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH));
1147
                }
1148
            break;
1149
        case WORK_PATH:
1150
            if (playerSalary == 1200 && noPopularity && !knowledgeLetterSent && unnamedStudioLettersSent[0] && controlFreelanceAttempts && playerFreelanceAttempts <= 5) {
1151
                addTimer(event(5.0, SPECIAL_LETTER, LETTER_TEST_OF_KNOWLEDGE));
1152
                knowledgeLetterSent = true;
1153
                }
1154
            else if (playerMoney > 500.0 && !newsShowed[2] && !pq.containsType(NEWS, 2)) {  // Frappy Perd
1155
                addTimer(event(7.0, NEWS, 2));
1156
                newsShowed[2] = true;
1157
                }
1158
            else if (playerMoney > 600.0 && !unnamedStudioLettersSent[0] && coursesFinished[3]) { // Startup
1159
                addTimer(event(14.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1));
1160
                unnamedStudioLettersSent[0] = true;
1161
                }
1162
            break;
1163
        case STARTUP_PATH:
1164
            if (unnamedStudioLettersSent[1] && !unnamedStudioLettersSent[2] && coursesFinished[4]) {
1165
                addTimer(event(15.0, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_3));
1166
                unnamedStudioLettersSent[2] = true;
1167
                }
1168
            else if (playerMoney > 600.0 && !shitCodeDetected && !shitLettersSent[1] && coursesFinished[1] && !pq.containsType(SPECIAL_LETTER, LETTER_SHITCODE_1)) {
1169
                addTimer(event(14.0, SPECIAL_LETTER, LETTER_SHITCODE_2));
1170
                shitLettersSent[1] = true;
1171
                }
1172
            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
1173
                addTimer(event(19.0, SPECIAL_LETTER, LETTER_SHITCODE_3));
1174
                shitLettersSent[2] = true;
1175
                }
1176
            break;
1177
        }
1178
    // News about JAVA programmers
1179
    if (playerMoney > 1500.0 && !newsShowed[1] && shitLettersSent[0] && shitLettersSent[1]
1180
        && shitLettersSent[2] && !shitCodeDetected && coursesFinished[1]  && !coursesUnlocked[2]) { // JAVA programmers
1181
        addTimer(event(5.0, NEWS, 1));
1182
        newsShowed[1] = true;
1183
        }
1184
    if (!returnTo21HintShowed && finishedCoursesCount == 12 && finalCardsUnlocked == 5 && gameTime > 21.0 && allAchievesShowed && !pq.containsType(SPECIAL_LETTER, LETTER_RETURN_TO_DAY_21)) {
1185
        addTimer(event(2.0, SPECIAL_LETTER, LETTER_RETURN_TO_DAY_21));
1186
        }
1187
    }
1188
 
1189
// Desktop is your main screen
1190
string getDesktop(int &lines) {
1191
    string s = "\n\n";
1192
    // Information about courses
1193
    if (!pq.hasCourses()) {
1194
        s += noCurrentCurses;
1195
        }
1196
    else {
1197
        // Check courses in pq
1198
        s += "    ";
1199
        s += coursesTitle;
1200
        s += ":\n";
1201
        for(int i = 0; i< pq.n(); i++) {
1202
            event* e = pq.get(i);
1203
            if (e->type == COURSE) {
1204
                s += "     ";
1205
                if (e->idata >= 0 && e->idata <= 7) {
1206
                    s += courses[e->idata];
1207
                    s += " ";
1208
                    int percent = (int)(100 * (learningTime[e->idata] - e->time) / learningTime[e->idata]);
1209
                    for (int i = 0; i < 20; i++) {
1210
                        if (i > 2*percent/10)
1211
                            s += " ";
1212
                        else
1213
                            s += pseudoEqual;
1214
                        }
1215
                    s += txt(" %d%\n", percent);
1216
                    }
1217
                else if (e->idata >= 'a' && e->idata <= 'e') {
1218
                    s += advProgCourses[e->idata - 'a'];
1219
                    s += " ";
1220
                    int percent = (int)(100 * (advProgLearningTime[e->idata-'a'] - e->time) / advProgLearningTime[e->idata - 'a']);
1221
                    for (int i = 0; i < 20; i++) {
1222
                        if (i > 2*percent/10)
1223
                            s+=" ";
1224
                        else
1225
                            s+=pseudoEqual;
1226
                        }
1227
                    s += txt(" %d%\n", percent);
1228
                    }
1229
                }
1230
            }
1231
        }
1232
 
1233
    for(unsigned int i = 0; i < s.length(); i++)
1234
        if (s[i] == '\n')
1235
            lines++;
1236
 
1237
    return s;
1238
    }
1239
 
1240
void mainGameCycle() {
1241
    float hintBegins = 0.0; //days
1242
    unsigned short hintIndex = 0;
1243
 
1244
    playerMoney = 100;
1245
    dt = 0.5;
1246
    int update = 0;
1247
 
1248
    // Add initial letter
1249
    initSendingMessage();
1250
 
1251
    string status = playerStatus;
1252
    status += helpDesker;
1253
    int currentPath = playerPath;
1254
 
1255
    playerStartedPlaying = time(NULL);
1256
    string buffer, oldStatusLine;
1257
    while (1) {
1258
        // We update screen 10 times a second
1259
        if (update % 10 == 0)
1260
            increaseMoney(-dt * 1.0); // Money needs per day
1261
 
1262
        int lines = 0;
1263
 
1264
        string s = getDesktop(lines);
1265
        for (int i = 0; i < 11-lines; i++)
1266
            s += "\n";
1267
        s += "    ";
1268
        s += hintOfTheMonth;
1269
        if (gameTime > hintBegins + 30) {
1270
            hintBegins = gameTime;
1271
            hintIndex++;
1272
            if (hintIndex >= hintsCount)
1273
                hintIndex = 0;
1274
            }
1275
        // This could happen when jumping in the past
1276
        else if (hintBegins > gameTime)
1277
            hintBegins = gameTime;
1278
 
1279
        s += "    ";
1280
        s += hints[hintIndex];
1281
 
1282
        if (playerPath != currentPath) {
1283
            currentPath = playerPath;
1284
            status=playerStatus;
1285
            if (playerPath == DEFAULT_PATH)      status += helpDesker;
1286
            else if (playerPath == WORK_PATH)    status += worker;
1287
            else if (playerPath == STARTUP_PATH) status += startupper;
1288
            }
1289
        // redraw screen only if necessary
1290
        string newStatusLine = getStatusLine();
1291
        if (!(s == buffer) || !(oldStatusLine == newStatusLine))
1292
            drawWindow(s, status.c_str(), newStatusLine, getBottomLine());
1293
        buffer = s;
1294
        oldStatusLine = newStatusLine;
1295
        if (update % 10 == 0) {
1296
            checkAchieves();
1297
            if (isGameOver())
1298
                break;
1299
 
1300
            if (playerSalary != 0 && gameTime-playerSalaryFirstDay >= 30) {
1301
                increaseMoney(playerSalary);
1302
                changeSalaryFirstDay(playerSalaryFirstDay+30);
1303
                }
1304
            makeStory();
1305
            checkPq();
1306
            }
1307
 
1308
        // Some key was pressed
1309
        if (kbhit()) {
1310
            char k = getch();
1311
            #ifdef _KOS32
1312
            // We have to make a second call for special keys in KolibriOS
1313
            if (k == 0)
1314
                k = getch();
1315
            #endif
1316
            if (keyPressed(k))
1317
                return;
1318
            // Force redraw desktop
1319
            buffer = "";
1320
            oldStatusLine = "";
1321
            }
1322
 
1323
        if (update % 10 == 0)
1324
            gameTime += dt;
1325
 
1326
        int delayTime = 10;
1327
        #ifdef _KOS32
1328
        __menuet__delay100(delayTime);
1329
        #else
1330
        usleep(delayTime*10000);
1331
        #endif
1332
        update++;
1333
        }
1334
    }
1335
 
1336
// Jump in the past to a certain time
1337
void undo(long double toTime) {
1338
    if (toTime < 0 )
1339
        return;
1340
 
1341
    if (playerMoney < 100) {
1342
        drawModalWindow(need100ForUndo, errorMsg);
1343
        wait();
1344
        return;
1345
        }
1346
    // Jump cost: $100
1347
    increaseMoney(-100);
1348
    drawModalWindow(prepareForTimeJump);
1349
    wait();
1350
 
1351
    while(!pq.empty())
1352
        pq.delMin();
1353
 
1354
    // Player Stats
1355
    playerTimeHops++;
1356
    playerSpentRealDays += (gameTime - playerPrevUndo);
1357
    playerPrevUndo = toTime;
1358
 
1359
    vector lateEvents;
1360
    history.prepareForUndo();
1361
    while (!history.empty() && history.getMax().time > toTime) {
1362
        event e = history.getMax();
1363
        switch (e.type) {
1364
            case INCREASEPOPULARITY:
1365
                increaseKarma(-e.idata, false);
1366
                break;
1367
            case NOPOPULARITY:
1368
                noPopularity = false;
1369
                break;
1370
            case CHANGEPATH:
1371
                playerPath = e.idata; // Previous path
1372
                break;
1373
            case CHANGESALARY:
1374
                playerSalary = e.idata;
1375
                break;
1376
            case CHANGESALARYFIRSTDAY:
1377
                playerSalaryFirstDay = e.idata;
1378
                break;
1379
        // Letters' time is time of sending a letter + time to end timer = time to get letter in the future
1380
        case SPECIAL_LETTER:
1381
            switch(e.idata) {
1382
                case LETTER_BOTSMANN:
1383
                    sentBotsmannLetter = false;
1384
                    astraLetterSent = false;
1385
                    break;
1386
                case LETTER_ASTRA:
1387
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_BOTSMANN, toTime))
1388
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_ASTRA));
1389
                    break;
1390
                case LETTER_UNNAMEDSTUDIO_2:
1391
                    // Unnamed letter 1 -> AngryBoss -> Unnamed letter 2
1392
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_ANGRYBOSS_1, e.time)) {
1393
                        if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_ANGRYBOSS_1, toTime))
1394
                            lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2));
1395
                        }
1396
                    // Unnamed letter 1 -> Unnamed letter 2
1397
                    else {
1398
                        if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1, toTime))
1399
                            lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_2));
1400
                        }
1401
                    break;
1402
                case NO_POPULARITY_HINT:
1403
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_ANGRYBOSS_1, toTime))
1404
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, NO_POPULARITY_HINT));
1405
                    break;
1406
                case LETTER_ANGRYBOSS_1:
1407
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_1, toTime)) {
1408
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_ANGRYBOSS_1));
1409
                        controlFreelanceAttempts = false;
1410
                        }
1411
                    break;
1412
                case LETTER_UNNAMEDSTUDIO_1:
1413
                    unnamedStudioLettersSent[0] = false;
1414
                    break;
1415
                case LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO:
1416
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4, toTime))
1417
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO));
1418
                    break;
1419
                case LETTER_UNNAMEDSTUDIO_4:
1420
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_3, toTime))
1421
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4));
1422
                    break;
1423
                case LETTER_UNNAMEDSTUDIO_3:
1424
                        unnamedStudioLettersSent[2] = false;
1425
                    break;
1426
                case LETTER_UNNAMEDSTUDIO_5:
1427
                    if (history.containsTypeBefore(NEWS, 3, toTime) && !pq.containsType(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5))
1428
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5));
1429
                    break;
1430
                case LETTER_FINALPATH_DEF:
1431
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH, toTime))
1432
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_FINALPATH_DEF));
1433
                    break;
1434
                case LETTER_FINALPATH_STARTUP:
1435
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_5, toTime) && !pq.containsType(SPECIAL_LETTER, LETTER_FINALPATH_STARTUP))
1436
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_FINALPATH_STARTUP));
1437
                    break;
1438
                case LETTER_FINALPATH_WORK:
1439
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_PERSISTENT_AND_PATIENT, toTime))
1440
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_FINALPATH_WORK));
1441
                    break;
1442
                case LETTER_ANGRYBOSS_2:
1443
                    if (e.time - toTime <= 1)
1444
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_ANGRYBOSS_2));
1445
                    break;
1446
                case LETTER_PERSISTENT_AND_PATIENT:
1447
                    if (history.containsTypeBefore(SPECIAL_LETTER,LETTER_BORING_WORK, toTime))
1448
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_PERSISTENT_AND_PATIENT));
1449
                    break;
1450
                case LETTER_BORING_WORK:
1451
                    if (e.time - toTime <= 40)
1452
                        lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_BORING_WORK));
1453
                    break;
1454
                case LETTER_TEST_OF_KNOWLEDGE:
1455
                    knowledgeLetterSent = false;
1456
                    playerFreelanceAttempts = 0;
1457
                    klowledgeCorrectAnswers = 0;
1458
                    klowledgeUncorrectAnswers = 0;
1459
                    break;
1460
                case LETTER_SHITCODE_1:
1461
                    shitCodeDetected = false;
1462
                    lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_SHITCODE_1));
1463
                    shitLettersSent[1] = false;
1464
                    shitLettersSent[2] = false;
1465
                    break;
1466
                case LETTER_SHITCODE_2:
1467
                    shitCodeDetected = false;
1468
                    lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_SHITCODE_2));
1469
                    shitLettersSent[2] = false;
1470
                    break;
1471
                case LETTER_SHITCODE_3:
1472
                    shitCodeDetected = false;
1473
                    lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_SHITCODE_3));
1474
                    break;
1475
                case LETTER_BORING_DEFAULT_PATH:
1476
                    lateEvents.push_back(event(e.time - toTime, SPECIAL_LETTER, LETTER_BORING_DEFAULT_PATH));
1477
                    break;
1478
                }
1479
                break;
1480
            case NEWS:
1481
                // News about JAVA programmers
1482
                newsShowed[e.idata] = false;
1483
                if (e.idata == 3) {
1484
                    // Unnamed letter 3 -> Unnamed letter 4 -> Need webmaster -> news about Java
1485
                    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO, e.time)) {
1486
                        if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_WEBMASTER_CHECK_UNNAMEDSTUDIO, toTime))
1487
                            lateEvents.push_back(event(e.time - toTime, NEWS, 3));
1488
                        }
1489
                    // Unnamed letter 3 -> Unnamed letter 4 -> news about Java
1490
                    else {
1491
                        if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_UNNAMEDSTUDIO_4, toTime))
1492
                            lateEvents.push_back(event(e.time - toTime, NEWS, 3));
1493
                        }
1494
                    }
1495
            break;
1496
            case COURSE: {
1497
                // e.time  - course finished
1498
                // e.idata - course number
1499
                long double courseStarted = 0;
1500
                bool courseFinished = false;
1501
                if (e.idata >='a' && e.idata <= 'e') {
1502
                    courseStarted = e.time - advProgLearningTime[e.idata-'a'];
1503
                    courseFinished = advProgCoursesFinished[e.idata-'a'];
1504
                    }
1505
                else if(e.idata >= 0 && e.idata < (int)coursesNumber) {
1506
                    courseStarted = e.time - learningTime[e.idata];
1507
                    courseFinished = coursesFinished[e.idata];
1508
                    }
1509
 
1510
                if (!courseFinished && toTime >= courseStarted)
1511
                    // Player is currently taking a course
1512
                    lateEvents.push_back(event(e.time - toTime, COURSE, e.idata));
1513
                    }
1514
                break;
1515
            case MESSAGE:
1516
                break;
1517
            }
1518
        history.delMax();
1519
        }
1520
    unreadMessages = 0;
1521
    gameTime = toTime;
1522
    timeHops++;
1523
 
1524
    for (unsigned short i = 0; i< lateEvents.size(); i++)
1525
        addTimer(lateEvents[i]);
1526
 
1527
 
1528
    // Messages would not arive without this:
1529
    if (!pq.containsType(MESSAGE))
1530
        initSendingMessage();
1531
 
1532
    // Undo shouldn't stop during the knowledge check
1533
    if (history.containsTypeBefore(SPECIAL_LETTER, LETTER_TEST_OF_KNOWLEDGE, toTime)
1534
        && !history.containsTypeBefore(SPECIAL_LETTER, LETTER_BORING_WORK, toTime)) {
1535
        double newTime = toTime-10;
1536
        for (int i = 0; i < history.n(); i++) {
1537
            event* e = history.get(i);
1538
            if (e->type == SPECIAL_LETTER && e->idata == LETTER_TEST_OF_KNOWLEDGE) {
1539
                newTime = e->time - 10;
1540
                break;
1541
                }
1542
            }
1543
        drawModalWindow(cantStopDuringKnowledgeCheck, errorMsg);
1544
        wait();
1545
        undo(newTime);
1546
        }
1547
    }
1548
 
1549
int chooseCourse() {
1550
    double p[coursesNumber];
1551
    long double sum = 0.0;
1552
    for (unsigned int i = 0; i < coursesNumber; i++) {
1553
        if (coursesFinished[i])
1554
            p[i] = probability[i];
1555
        else
1556
            p[i] = 0.0;
1557
        sum += p[i];
1558
        }
1559
    if (sum <= 0.001)
1560
        return 0;
1561
    int N = (int) (sum * 100);
1562
    int r = rand() % N;
1563
 
1564
    int n1 = (p[0] / sum) * N;
1565
    int n2 = (p[1] / sum) * N;
1566
    int n3 = (p[2] / sum) * N;
1567
    int n4 = (p[3] / sum) * N;
1568
    int n5 = (p[4] / sum) * N;
1569
 
1570
    if (r >= 0 && r < n1)
1571
        return 0;
1572
    else if (coursesFinished[1] && r >= n1 && r < n1+n2)
1573
        return 1;
1574
    else if (coursesFinished[2] && r >= n1+n2 && r < n1+n2+n3)
1575
        return 2;
1576
    else if (coursesFinished[3] && r >= n1+n2+n3 && r < n1+n2+n3+n4)
1577
        return 3;
1578
    else if (coursesFinished[4] && r >= n1+n2+n3+n4 && r < n1+n2+n3+n4+n5)
1579
        return 4;
1580
    else if (coursesFinished[5] && r >= n1+n2+n3+n4+n5 && r <= N)
1581
        return 5;
1582
 
1583
    return 0;
1584
    }