Subversion Repositories Games.Carmageddon

Rev

Rev 20 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
#include "structur.h"
2
#include "car.h"
3
#include "controls.h"
4
#include "crush.h"
5
#include "cutscene.h"
6
#include "displays.h"
7
#include "drmem.h"
8
#include "finteray.h"
9
#include "flicplay.h"
10
#include "globvars.h"
11
#include "globvrkm.h"
12
#include "globvrpb.h"
13
#include "graphics.h"
14
#include "harness/config.h"
15
#include "harness/trace.h"
16
#include "init.h"
17
#include "loading.h"
18
#include "loadsave.h"
19
#include "mainloop.h"
20
#include "mainmenu.h"
21
#include "netgame.h"
22
#include "network.h"
23
#include "opponent.h"
24
#include "piping.h"
25
#include "pratcam.h"
26
#include "racestrt.h"
27
#include "racesumm.h"
28
#include "sound.h"
29
#include "utility.h"
30
#include <stdlib.h>
31
 
32
int gLast_wrong_checkpoint;
33
int gMirror_on__structur = 1; // suffix added to avoid duplicate symbol
34
int gPratcam_on = 1;
35
int gCockpit_on = 1;
36
int gOpponent_mix[10][5] = {
37
    { 3, 4, 4, 5, 5 },
38
    { 2, 3, 4, 5, 5 },
39
    { 2, 3, 4, 4, 5 },
40
    { 2, 2, 4, 4, 5 },
41
    { 2, 2, 3, 4, 5 },
42
    { 1, 2, 3, 4, 4 },
43
    { 1, 2, 3, 3, 4 },
44
    { 1, 2, 2, 3, 4 },
45
    { 1, 1, 2, 3, 3 },
46
    { 1, 1, 2, 2, 3 }
47
};
48
tU32 gLast_checkpoint_time;
49
tRace_over_reason gRace_over_reason;
50
 
51
// IDA: int __cdecl NumberOfOpponentsLeft()
52
int NumberOfOpponentsLeft(void) {
53
    int i;
54
    int car_count;
55
    int result;
56
    tCar_spec* the_car;
57
    LOG_TRACE("()");
58
 
59
    result = 0;
60
    car_count = GetCarCount(eVehicle_opponent);
61
    for (i = 0; car_count > i; ++i) {
62
        the_car = GetCarSpec(eVehicle_opponent, i);
63
        if (!the_car->knackered) {
64
            result++;
65
        }
66
    }
67
    return result;
68
}
69
 
70
// IDA: void __usercall RaceCompleted(tRace_over_reason pReason@<EAX>)
71
void RaceCompleted(tRace_over_reason pReason) {
72
    LOG_TRACE("(%d)", pReason);
73
 
74
    if (!gRace_finished) {
75
        if (gNet_mode == eNet_mode_host && pReason < eRace_over_network_victory) {
76
            NetFinishRace(gCurrent_net_game, pReason);
77
        }
78
        if (pReason == eRace_over_out_of_time || pReason == eRace_over_demo) {
79
            ChangeAmbientPratcam(35);
80
        } else if (pReason < eRace_over_abandoned) {
81
            ChangeAmbientPratcam(34);
82
        }
83
        gRace_over_reason = pReason;
84
        if (gMap_mode) {
85
            ToggleMap();
86
        }
87
        switch (gRace_over_reason) {
88
        case eRace_over_laps:
89
        case eRace_over_peds:
90
        case eRace_over_opponents:
91
            ChangeAmbientPratcam(34);
92
            DoFancyHeadup(kFancyHeadupRaceCompleted);
93
            DRS3StartSound(gPedestrians_outlet, 8011);
94
            break;
95
        case eRace_over_abandoned:
96
            if (gNet_mode == eNet_mode_client) {
97
                gHost_abandon_game = 1;
20 pmbaty 98
                NetFullScreenMessage(87, 0);
1 pmbaty 99
            }
100
            break;
101
        case eRace_over_out_of_time:
102
            ChangeAmbientPratcam(35);
103
            DoFancyHeadup(kFancyHeadupOutOfTime);
104
            DRS3StartSound(gPedestrians_outlet, 8010);
105
            break;
106
        case eRace_over_demo:
107
            ChangeAmbientPratcam(35);
108
            DoFancyHeadup(kFancyHeadupDemoTimeout);
109
            break;
110
        case eRace_over_network_victory:
111
            ChangeAmbientPratcam(34);
112
            DoFancyHeadup(kFancyHeadupNetworkVictory);
113
            break;
114
        case eRace_over_network_loss:
115
            ChangeAmbientPratcam(36);
116
            DoFancyHeadup(kFancyHeadupNetworkRaceOverNetworkLoss);
117
            break;
118
        default:
119
            break;
120
        }
121
        if (gNet_mode != eNet_mode_none) {
122
            gRace_finished = 8000;
123
        } else {
124
            gRace_finished = 4000;
125
        }
126
    }
127
}
128
 
129
// IDA: void __usercall Checkpoint(int pCheckpoint_index@<EAX>, int pDo_sound@<EDX>)
130
void Checkpoint(int pCheckpoint_index, int pDo_sound) {
131
    LOG_TRACE("(%d, %d)", pCheckpoint_index, pDo_sound);
132
 
133
    PratcamEvent(33);
134
    DoFancyHeadup(kFancyHeadupCheckpoint);
135
    if (pDo_sound) {
136
        DRS3StartSound(gPedestrians_outlet, 8012);
137
    }
138
}
139
 
140
// IDA: void __cdecl IncrementCheckpoint()
141
void IncrementCheckpoint(void) {
142
    int done_voice;
143
    LOG_TRACE("()");
144
 
145
    done_voice = 0;
146
    if (gRace_finished) {
147
        return;
148
    }
149
    gLast_checkpoint_time = GetTotalTime();
150
    if (gCheckpoint < gCheckpoint_count) {
151
        gCheckpoint++;
152
    } else {
153
        gCheckpoint = 1;
154
        gLap++;
155
        if (gLap == gTotal_laps) {
156
            PratcamEvent(33); // FIXME: or PratcamEventNow
21 pmbaty 157
            NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, GetMiscString(kMiscString_FinalLap));
1 pmbaty 158
            DRS3StartSound(gPedestrians_outlet, 8014);
159
            done_voice = 1;
160
        } else if (gLap > gTotal_laps) {
161
            gLap = gTotal_laps;
162
            gCheckpoint = gCheckpoint_count;
163
            RaceCompleted(eRace_over_laps);
164
        }
165
    }
166
    if (!gRace_finished) {
167
        Checkpoint(gCheckpoint, !done_voice);
168
        if (gCheck_point_cash[gProgram_state.skill_level] != 0) {
169
            EarnCredits(gCheck_point_cash[gProgram_state.skill_level]);
170
        }
171
    }
172
}
173
 
174
// IDA: void __cdecl IncrementLap()
175
void IncrementLap(void) {
176
    int i;
177
    LOG_TRACE("()");
178
 
179
    for (i = gCheckpoint; i <= gCheckpoint_count; i++) {
180
        IncrementCheckpoint();
181
    }
182
}
183
 
184
// IDA: int __usercall RayHitFace@<EAX>(br_vector3 *pV0@<EAX>, br_vector3 *pV1@<EDX>, br_vector3 *pV2@<EBX>, br_vector3 *pNormal@<ECX>, br_vector3 *pStart, br_vector3 *pDir)
185
int RayHitFace(br_vector3* pV0, br_vector3* pV1, br_vector3* pV2, br_vector3* pNormal, br_vector3* pStart, br_vector3* pDir) {
186
    tFace_ref the_face;
187
    br_scalar rt;
188
    LOG_TRACE("(%p, %p, %p, %p, %p, %p)", pV0, pV1, pV2, pNormal, pStart, pDir);
189
 
190
    the_face.material = NULL;
191
    BrVector3Copy(&the_face.v[0], pV0);
192
    BrVector3Copy(&the_face.v[1], pV1);
193
    BrVector3Copy(&the_face.v[2], pV2);
194
    BrVector3Copy(&the_face.normal, pNormal);
195
    CheckSingleFace(&the_face, pStart, pDir, &the_face.normal, &rt);
196
    return rt >= 0.f && rt <= 1.f;
197
}
198
 
199
// IDA: void __usercall WrongCheckpoint(int pCheckpoint_index@<EAX>)
200
void WrongCheckpoint(int pCheckpoint_index) {
201
    LOG_TRACE("(%d)", pCheckpoint_index);
202
 
203
    if ((pCheckpoint_index == gLast_wrong_checkpoint && GetTotalTime() - gLast_checkpoint_time > 20000) || (pCheckpoint_index != gLast_wrong_checkpoint && GetTotalTime() - gLast_checkpoint_time > 2000)) {
204
        if (gNet_mode == eNet_mode_none) {
205
            if (gCheckpoint == ((gCurrent_race.check_point_count < pCheckpoint_index + 2) ? ((gLap == 1) ? -1 : 1) : (pCheckpoint_index + 2))) {
206
                return;
207
            }
208
        }
21 pmbaty 209
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, GetMiscString(kMiscString_WrongCheckpoint));
1 pmbaty 210
        DRS3StartSound(gPedestrians_outlet, 8013);
211
        gLast_checkpoint_time = GetTotalTime();
212
        gLast_wrong_checkpoint = pCheckpoint_index;
213
    }
214
}
215
 
216
// IDA: void __cdecl CheckCheckpoints()
217
void CheckCheckpoints(void) {
218
    tCar_spec* car;
219
    br_vector3 orig;
220
    br_vector3 dir;
221
    int i;
222
    int j;
223
    int cat;
224
    int car_count;
225
    int car_index;
226
    tNet_game_player_info* net_player;
227
    LOG_TRACE("()");
228
 
229
    if (gNet_mode == eNet_mode_client) {
230
        return;
231
    }
232
    if (gNet_mode == eNet_mode_host && gCurrent_net_game->type != eNet_game_type_checkpoint && gCurrent_net_game->type != eNet_game_type_sudden_death) {
233
        return;
234
    }
235
    // in single-player mode (=eNet_mode_none), only the player will be checked,
236
    // in multi-player, the host + clients will be tested, there are no drone opponents there
237
    for (cat = 0; cat <= gNet_mode; cat++) {
238
        if (cat == eVehicle_self) {
239
            car_count = 1;
240
        } else {
241
            car_count = GetCarCount(cat);
242
        }
243
        for (car_index = 0; car_index < car_count; car_index++) {
244
            if (cat == eVehicle_self) {
245
                car = &gProgram_state.current_car;
246
            } else {
247
                car = GetCarSpec(cat, car_index);
248
            }
249
            BrVector3Copy(&orig, (br_vector3*)car->old_frame_mat.m[3]);
250
            BrVector3Sub(&dir, &car->car_master_actor->t.t.translate.t, &orig);
251
            for (i = 0; i < gCurrent_race.check_point_count; i++) {
252
                for (j = 0; j < gCurrent_race.checkpoints[i].quad_count; j++) {
253
                    if (
254
                        RayHitFace(&gCurrent_race.checkpoints[i].vertices[j][0],
255
                            &gCurrent_race.checkpoints[i].vertices[j][1],
256
                            &gCurrent_race.checkpoints[i].vertices[j][2],
257
                            &gCurrent_race.checkpoints[i].normal[j],
258
                            &orig, &dir)
259
                        || RayHitFace(&gCurrent_race.checkpoints[i].vertices[j][0],
260
                            &gCurrent_race.checkpoints[i].vertices[j][2],
261
                            &gCurrent_race.checkpoints[i].vertices[j][3],
262
                            &gCurrent_race.checkpoints[i].normal[j],
263
                            &orig,
264
                            &dir)) {
265
                        if (gNet_mode == eNet_mode_none) {
266
                            if (i + 1 == gCheckpoint) {
267
                                IncrementCheckpoint();
268
                            } else {
269
                                WrongCheckpoint(i);
270
                            }
271
                        } else {
272
                            net_player = NetPlayerFromCar(car);
273
                            if (gCurrent_net_game->type == eNet_game_type_checkpoint) {
274
                                if (net_player->score & (1 << i)) {
275
                                    net_player->score &= ~(1 << i);
276
                                    SendGameplay(net_player->ID, eNet_gameplay_checkpoint, i, 0, 0, 0);
277
                                } else {
278
                                    SendGameplay(net_player->ID, eNet_gameplay_wrong_checkpoint, i, 0, 0, 0);
279
                                }
280
                            } else if (net_player->score % gCurrent_race.check_point_count == i) {
281
                                net_player->score++;
282
                                SendGameplay(net_player->ID, eNet_gameplay_checkpoint, i, 0, 0, 0);
283
                            } else {
284
                                SendGameplay(net_player->ID, eNet_gameplay_wrong_checkpoint, i, 0, 0, 0);
285
                            }
286
                        }
287
                        break;
288
                    }
289
                }
290
            }
291
        }
292
        car->old_frame_mat = car->car_master_actor->t.t.mat;
293
    }
294
}
295
 
296
// IDA: void __cdecl TotalRepair()
297
void TotalRepair(void) {
298
    LOG_TRACE("()");
299
 
300
    TotallyRepairCar();
21 pmbaty 301
    NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, GetMiscString(kMiscString_InstantRepair));
1 pmbaty 302
}
303
 
304
// IDA: void __cdecl DoLogos()
305
void DoLogos(void) {
306
    ClearEntireScreen();
307
    DoSCILogo();
308
    DoOpeningAnimation();
309
    DoStainlessLogo();
21 pmbaty 310
#ifdef DETHRACE_FIX_BUGS
311
    /* StartMusic is only called in PlaySmackerFile when sound and cutscenes are enabled */
312
    if (!gSound_override && gCut_scene_override) {
313
        if (!harness_game_config.no_music) {
314
            StartMusic();
315
        }
316
    }
317
#endif
1 pmbaty 318
    gProgram_state.prog_status = eProg_opening;
319
}
320
 
321
// IDA: void __cdecl DoProgOpeningAnimation()
322
void DoProgOpeningAnimation(void) {
323
    LOG_TRACE("()");
324
 
325
    gProgram_state.prog_status = eProg_idling;
326
    DRS3StopOutletSound(gEffects_outlet);
327
}
328
 
329
// IDA: void __cdecl DoProgramDemo()
330
void DoProgramDemo(void) {
331
    LOG_TRACE("()");
332
 
333
    DoLogos();
334
    gProgram_state.prog_status = eProg_idling;
335
    DRS3StopOutletSound(gEffects_outlet);
336
}
337
 
338
// IDA: int __usercall ChooseOpponent@<EAX>(int pNastiness@<EAX>, int *pHad_scum@<EDX>)
339
int ChooseOpponent(int pNastiness, int* pHad_scum) {
340
    int i;
341
    int count;
342
    int temp_array[40];
343
    LOG_TRACE("(%d, %p)", pNastiness, pHad_scum);
344
 
345
    count = 0;
346
    for (i = 0; i < gNumber_of_racers; ++i) {
347
        if (gOpponents[i].strength_rating == pNastiness
348
            && gProgram_state.current_car.index != i
349
            && !gOpponents[i].picked
350
            && (gOpponents[i].car_number >= 0 || !*pHad_scum)) {
351
            temp_array[count++] = i;
352
        }
353
    }
354
    i = temp_array[IRandomBetween(0, count - 1)];
355
    gOpponents[i].picked = 1;
356
    if (gOpponents[i].car_number < 0) {
357
        *pHad_scum = 1;
358
    }
359
    return i;
360
}
361
 
362
// IDA: void __usercall SelectOpponents(tRace_info *pRace_info@<EAX>)
363
void SelectOpponents(tRace_info* pRace_info) {
364
    int i;
365
    int rank_band;
366
    int nastiness;
367
    int had_scum;
368
    LOG_TRACE("(%p)", pRace_info);
369
 
370
    if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
371
        pRace_info->number_of_racers = OPPONENT_COUNT;
372
        for (i = 0; i < OPPONENT_COUNT; i++) {
373
            pRace_info->opponent_list[i].index = gDemo_opponents[i];
374
            pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
375
        }
376
        return;
377
    }
378
 
379
    had_scum = 0;
380
    if (gNet_mode == eNet_mode_none) {
381
        pRace_info->number_of_racers = OPPONENT_COUNT;
382
        for (i = 0; i < gNumber_of_racers; ++i) {
383
            gOpponents[i].picked = 0;
384
        }
385
        if (gRace_list[gProgram_state.current_race_index].suggested_rank < 0) {
386
            rank_band = 0;
387
        } else {
388
            rank_band = gRace_list[gProgram_state.current_race_index].suggested_rank / 10;
389
        }
390
        for (i = 0; i < OPPONENT_COUNT; i++) {
391
            nastiness = gOpponent_mix[rank_band][i];
392
            pRace_info->opponent_list[i].index = ChooseOpponent(nastiness, &had_scum);
393
            pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
394
        }
395
    }
396
}
397
 
398
// IDA: int __usercall PickNetRace@<EAX>(int pCurrent_race@<EAX>, tNet_sequence_type pNet_race_sequence@<EDX>)
399
int PickNetRace(int pCurrent_race, tNet_sequence_type pNet_race_sequence) {
400
    int i;
401
    int new_index;
402
    int races_count;
403
    int most_seldom_seen;
404
    int races_to_pick_from[50];
405
    LOG_TRACE("(%d, %d)", pCurrent_race, pNet_race_sequence);
406
 
407
    if (pNet_race_sequence == eNet_sequence_sequential) {
408
        pCurrent_race++;
409
        if (pCurrent_race >= gNumber_of_races) {
410
            pCurrent_race = 0;
411
        }
412
    } else {
413
        most_seldom_seen = 10000;
414
        for (i = 0; i < gNumber_of_races; i++) {
415
            if (gRace_list[i].been_there_done_that < most_seldom_seen) {
416
                most_seldom_seen = gRace_list[i].been_there_done_that;
417
            }
418
        }
419
        races_count = 0;
420
        for (i = 0; i < gNumber_of_races; i++) {
421
            if (gRace_list[i].been_there_done_that == most_seldom_seen && (i != pCurrent_race)) {
422
                races_to_pick_from[races_count] = i;
423
                races_count++;
424
            }
425
        }
426
        new_index = IRandomBetween(0, races_count - 1);
427
        pCurrent_race = races_to_pick_from[new_index];
428
        gRace_list[pCurrent_race].been_there_done_that++;
429
    }
430
    return pCurrent_race;
431
}
432
 
433
// IDA: void __cdecl SwapNetCarsLoad()
434
void SwapNetCarsLoad(void) {
435
    int i;
436
    int switched_res;
437
    LOG_TRACE("()");
438
 
439
    DisableNetService();
440
    AboutToLoadFirstCar();
441
    switched_res = SwitchToRealResolution();
442
    for (i = 0; i < gNumber_of_net_players; i++) {
443
        if (gNet_players[i].next_car_index >= 0) {
444
            gNet_players[i].car_index = gNet_players[i].next_car_index;
445
        }
446
        gNet_players[i].next_car_index = -1;
447
        LoadCar(gOpponents[gNet_players[i].car_index].car_file_name,
448
            (gThis_net_player_index == i) ? eDriver_local_human : eDriver_net_human,
449
            gNet_players[i].car, gNet_players[i].car_index, gNet_players[i].player_name,
450
            &gNet_cars_storage_space);
451
    }
452
    if (switched_res) {
453
        SwitchToLoresMode();
454
    }
455
    ReenableNetService();
456
}
457
 
458
// IDA: void __cdecl SwapNetCarsDispose()
459
void SwapNetCarsDispose(void) {
20 pmbaty 460
    //int i; // Pierre-Marie Baty -- unused variable
1 pmbaty 461
    LOG_TRACE("()");
20 pmbaty 462
    NOT_IMPLEMENTED();
1 pmbaty 463
}
464
 
465
// IDA: void __cdecl DoGame()
466
void DoGame(void) {
467
    tSO_result options_result;
468
    tRace_result race_result;
469
    int second_select_race;
470
    int first_summary_done;
471
    int i;
472
    LOG_TRACE("()");
473
 
474
    gAbandon_game = 0;
475
    gDisallow_abandon_race = 0;
476
    gCar_to_view = &gProgram_state.current_car;
477
    StartLoadingScreen();
478
    gProgram_state.prog_status = eProg_game_ongoing;
479
    second_select_race = 0;
480
    if (gNet_mode == gNet_mode_of_last_game) {
481
        PrintMemoryDump(0, "BEFORE START RACE SCREEN");
482
        SelectOpponents(&gCurrent_race);
483
        if (gNet_mode != eNet_mode_none) {
484
            LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
485
            FillInRaceInfo(&gCurrent_race);
486
            DisposeRaceInfo(&gCurrent_race);
487
        } else {
488
            do {
489
                options_result = DoSelectRace(&second_select_race);
490
                if (options_result == eSO_main_menu_invoked) {
491
                    DoMainMenuScreen(0, 1, 1);
492
                }
493
                gDisallow_abandon_race = 0;
494
            } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing && !gAbandon_game);
495
        }
496
        if (gProgram_state.prog_status == eProg_game_starting
497
            || gProgram_state.prog_status == eProg_quit
498
            || gProgram_state.prog_status == eProg_idling
499
            || gAbandon_game) {
500
            PrintMemoryDump(0, "AFTER ABANDONING START RACE SCREEN");
501
            if (gProgram_state.prog_status == eProg_game_ongoing) {
502
                gProgram_state.prog_status = eProg_game_starting;
503
            }
504
        } else {
505
            PrintMemoryDump(0, "AFTER START RACE SCREEN");
506
            DoNewGameAnimation();
507
            StartLoadingScreen();
508
            if (gNet_mode != eNet_mode_none) {
509
                if (gCurrent_net_game->options.random_car_choice
510
                    && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)
511
                    && !gNo_races_yet) {
512
                    SwapNetCarsLoad();
513
                }
514
            } else {
515
                LoadOpponentsCars(&gCurrent_race);
516
            }
517
            PrintMemoryDump(0, "AFTER LOADING OPPONENTS IN");
518
            InitRace();
519
            if (gNet_mode_of_last_game == gNet_mode) {
520
                if (gProgram_state.prog_status == eProg_game_starting
521
                    || gProgram_state.prog_status == eProg_quit
522
                    || gProgram_state.prog_status == eProg_idling
523
                    || gAbandon_game) {
524
                    DisposeRace();
525
                    if (gNet_mode == eNet_mode_none && gNet_mode_of_last_game == eNet_mode_none) {
526
                        DisposeOpponentsCars(&gCurrent_race);
527
                    }
528
                    DisposeTrack();
529
                    if (gProgram_state.prog_status == eProg_game_ongoing) {
530
                        gProgram_state.prog_status = eProg_game_starting;
531
                    }
532
                } else {
533
                    if (gNet_mode != eNet_mode_none) {
534
                        do {
535
                            options_result = NetSynchRaceStart();
536
                            if (options_result == eSO_main_menu_invoked) {
537
                                DoMainMenuScreen(0, 1, 1);
538
                            }
539
                        } while (options_result == eSO_main_menu_invoked
540
                            && gProgram_state.prog_status == eProg_game_ongoing
541
                            && !gAbandon_game);
542
                    } else {
543
                        do {
544
                            options_result = DoGridPosition();
545
                            if (options_result == eSO_main_menu_invoked) {
546
                                DoMainMenuScreen(0, 1, 1);
547
                            }
548
                        } while (options_result == eSO_main_menu_invoked
549
                            && gProgram_state.prog_status == eProg_game_ongoing
550
                            && !gAbandon_game);
551
                        SetInitialPositions(&gCurrent_race);
552
                    }
553
                    if (gProgram_state.prog_status == eProg_game_starting
554
                        || gProgram_state.prog_status == eProg_quit
555
                        || gProgram_state.prog_status == eProg_idling
556
                        || gAbandon_game) {
557
                        DisposeRace();
558
                        if (!gNet_mode && !gNet_mode_of_last_game) {
559
                            DisposeOpponentsCars(&gCurrent_race);
560
                        }
561
                        DisposeTrack();
562
                        if (gProgram_state.prog_status == eProg_game_ongoing) {
563
                            gProgram_state.prog_status = eProg_game_starting;
564
                        }
565
                    } else {
566
                        SwitchToRealResolution();
567
                        InitOpponents(&gCurrent_race);
568
                        InitialiseCarsEtc(&gCurrent_race);
569
                        SetInitialCopPositions();
570
                        InitSoundSources();
571
                        InitLastDamageArrayEtc();
572
                        race_result = DoRace();
573
                        SwitchToLoresMode();
574
                        DisposeRace();
575
                        if (gNet_mode != eNet_mode_none) {
576
                            gProgram_state.current_race_index = gPending_race;
577
                            gCurrent_net_game->start_race = gPending_race;
578
                            gPending_race = -1;
579
                        }
580
                        if (race_result == eRace_completed || race_result == eRace_timed_out) {
581
                            DoEndRaceAnimation();
582
                            first_summary_done = 0;
583
                            do {
584
                                options_result = DoEndRaceSummary(&first_summary_done, race_result);
585
                                if (options_result == eSO_main_menu_invoked) {
586
                                    DoMainMenuScreen(0, 1, 1);
587
                                }
588
                            } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing);
589
                        }
590
                        if (gNet_mode) {
591
                            for (i = 0; i < gNumber_of_net_players; i++) {
592
                                TotallyRepairACar(gNet_players[i].car);
593
                            }
594
                        } else {
595
                            TotallyRepairCar();
596
                        }
597
                        if (gNet_mode) {
598
                            if (gCurrent_net_game->options.random_car_choice
599
                                && (gCurrent_net_game->options.car_choice == eNet_car_all
600
                                    || gCurrent_net_game->options.car_choice == eNet_car_both)
601
                                && !gNo_races_yet) {
602
                                SwapNetCarsDispose();
603
                            }
604
                        } else {
605
                            DisposeOpponentsCars(&gCurrent_race);
606
                        }
607
                        DisposeTrack();
608
                        if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
609
                            DoFullVersionPowerpoint();
610
                        }
611
                        gProgram_state.loaded = 0;
612
                        if (gProgram_state.prog_status == eProg_game_ongoing) {
613
                            gProgram_state.prog_status = eProg_game_starting;
614
                        }
615
                    }
616
                }
617
            } else {
618
                gProgram_state.prog_status = eProg_idling;
619
            }
620
        }
621
    } else {
622
        gProgram_state.prog_status = eProg_idling;
623
    }
624
}
625
 
626
// IDA: void __cdecl InitialiseProgramState()
627
void InitialiseProgramState(void) {
628
    gProgram_state.loaded = 0;
629
    gProgram_state.last_slot = 0;
630
    gProgram_state.frank_or_anniness = eFrankie;
631
    gProgram_state.skill_level = 1;
632
    gProgram_state.view_type = eVT_Scene;
633
    gProgram_state.auto_parts_reply = eAP_auto;
634
    gProgram_state.racing = 0;
635
    gProgram_state.cut_scene = 0;
636
    gProgram_state.saving = 0;
637
    gProgram_state.loading = 0;
638
    gProgram_state.dont_save_or_load = 0;
639
    gProgram_state.dont_load = 0;
640
    gProgram_state.mirror_on = gMirror_on__structur;
641
    gProgram_state.prog_status = eProg_intro;
642
    if (gAusterity_mode) {
643
        gProgram_state.prat_cam_on = 0;
644
        gPratcam_on = 0;
645
    } else {
646
        gProgram_state.prat_cam_on = gPratcam_on;
647
    }
648
    gProgram_state.cockpit_on = gCockpit_on;
649
    gProgram_state.car_name[0] = 0;
650
    SetSoundVolumes();
651
    AllocateRearviewPixelmap();
652
}
653
 
654
// IDA: void __cdecl DoProgram()
655
void DoProgram(void) {
656
    InitialiseProgramState();
657
    while (gProgram_state.prog_status != eProg_quit) {
658
        switch (gProgram_state.prog_status) {
659
        case eProg_intro:
660
            DisposeGameIfNecessary();
661
            DoLogos();
662
            break;
663
        case eProg_opening:
664
            DisposeGameIfNecessary();
665
            DoProgOpeningAnimation();
666
            break;
667
        case eProg_idling:
668
            DisposeGameIfNecessary();
669
            if (gGame_to_load < 0) {
670
                DoMainMenuScreen(30000u, 0, 0);
671
            } else {
672
                DoLoadGame();
673
            }
674
            break;
675
        case eProg_demo:
676
            DoProgramDemo();
677
            break;
678
        case eProg_game_starting:
679
            DoGame();
680
            break;
681
        default:
682
            break;
683
        }
684
    }
685
}
686
 
687
// IDA: void __cdecl JumpTheStart()
688
void JumpTheStart(void) {
689
    char s[256];
690
    LOG_TRACE("()");
691
 
692
    if (gNet_mode == eNet_mode_none
693
        || gProgram_state.credits_earned - gProgram_state.credits_lost >= gJump_start_fine[gProgram_state.skill_level]) {
694
        WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(gCountdown);
695
        gCountdown = 0;
696
        DRS3StopOutletSound(gPedestrians_outlet);
697
        DRS3StartSound(gPedestrians_outlet, 8016);
698
        SpendCredits(gJump_start_fine[gProgram_state.skill_level]);
699
        sprintf(s, "%s %d %s", GetMiscString(gProgram_state.frank_or_anniness == eFrankie ? kMiscString_BadBoy : kMiscString_BadGirl), gJump_start_fine[gProgram_state.skill_level], GetMiscString(kMiscString_CreditFine));
21 pmbaty 700
        NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, s);
1 pmbaty 701
    }
702
}
703
 
704
// IDA: void __cdecl GoingToInterfaceFromRace()
705
void GoingToInterfaceFromRace(void) {
706
    LOG_TRACE("()");
707
 
708
    gInterface_within_race_mode = 1;
709
    PlayFlicsFromDisk();
710
    SwitchToLoresMode();
711
    if (gNet_mode == eNet_mode_host) {
712
        SendGameplayToAllPlayers(eNet_gameplay_host_paused, 0, 0, 0, 0);
713
    }
714
}
715
 
716
// IDA: void __cdecl GoingBackToRaceFromInterface()
717
void GoingBackToRaceFromInterface(void) {
718
    LOG_TRACE("()");
719
 
720
    gInterface_within_race_mode = 0;
721
    PlayFlicsFromMemory();
722
    SwitchToRealResolution();
723
    if (gNet_mode == eNet_mode_host) {
724
        SendGameplayToAllPlayers(eNet_gameplay_host_unpaused, 0, 0, 0, 0);
725
    }
726
}