Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Go to most recent revision | 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
157
            NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_FinalLap));
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
        }
209
        NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_WrongCheckpoint));
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();
301
    NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_InstantRepair));
302
}
303
 
304
// IDA: void __cdecl DoLogos()
305
void DoLogos(void) {
306
    ClearEntireScreen();
307
    DoSCILogo();
308
    DoOpeningAnimation();
309
    DoStainlessLogo();
310
    gProgram_state.prog_status = eProg_opening;
311
}
312
 
313
// IDA: void __cdecl DoProgOpeningAnimation()
314
void DoProgOpeningAnimation(void) {
315
    LOG_TRACE("()");
316
 
317
    gProgram_state.prog_status = eProg_idling;
318
    DRS3StopOutletSound(gEffects_outlet);
319
}
320
 
321
// IDA: void __cdecl DoProgramDemo()
322
void DoProgramDemo(void) {
323
    LOG_TRACE("()");
324
 
325
    DoLogos();
326
    gProgram_state.prog_status = eProg_idling;
327
    DRS3StopOutletSound(gEffects_outlet);
328
}
329
 
330
// IDA: int __usercall ChooseOpponent@<EAX>(int pNastiness@<EAX>, int *pHad_scum@<EDX>)
331
int ChooseOpponent(int pNastiness, int* pHad_scum) {
332
    int i;
333
    int count;
334
    int temp_array[40];
335
    LOG_TRACE("(%d, %p)", pNastiness, pHad_scum);
336
 
337
    count = 0;
338
    for (i = 0; i < gNumber_of_racers; ++i) {
339
        if (gOpponents[i].strength_rating == pNastiness
340
            && gProgram_state.current_car.index != i
341
            && !gOpponents[i].picked
342
            && (gOpponents[i].car_number >= 0 || !*pHad_scum)) {
343
            temp_array[count++] = i;
344
        }
345
    }
346
    i = temp_array[IRandomBetween(0, count - 1)];
347
    gOpponents[i].picked = 1;
348
    if (gOpponents[i].car_number < 0) {
349
        *pHad_scum = 1;
350
    }
351
    return i;
352
}
353
 
354
// IDA: void __usercall SelectOpponents(tRace_info *pRace_info@<EAX>)
355
void SelectOpponents(tRace_info* pRace_info) {
356
    int i;
357
    int rank_band;
358
    int nastiness;
359
    int had_scum;
360
    LOG_TRACE("(%p)", pRace_info);
361
 
362
    if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
363
        pRace_info->number_of_racers = OPPONENT_COUNT;
364
        for (i = 0; i < OPPONENT_COUNT; i++) {
365
            pRace_info->opponent_list[i].index = gDemo_opponents[i];
366
            pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
367
        }
368
        return;
369
    }
370
 
371
    had_scum = 0;
372
    if (gNet_mode == eNet_mode_none) {
373
        pRace_info->number_of_racers = OPPONENT_COUNT;
374
        for (i = 0; i < gNumber_of_racers; ++i) {
375
            gOpponents[i].picked = 0;
376
        }
377
        if (gRace_list[gProgram_state.current_race_index].suggested_rank < 0) {
378
            rank_band = 0;
379
        } else {
380
            rank_band = gRace_list[gProgram_state.current_race_index].suggested_rank / 10;
381
        }
382
        for (i = 0; i < OPPONENT_COUNT; i++) {
383
            nastiness = gOpponent_mix[rank_band][i];
384
            pRace_info->opponent_list[i].index = ChooseOpponent(nastiness, &had_scum);
385
            pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
386
        }
387
    }
388
}
389
 
390
// IDA: int __usercall PickNetRace@<EAX>(int pCurrent_race@<EAX>, tNet_sequence_type pNet_race_sequence@<EDX>)
391
int PickNetRace(int pCurrent_race, tNet_sequence_type pNet_race_sequence) {
392
    int i;
393
    int new_index;
394
    int races_count;
395
    int most_seldom_seen;
396
    int races_to_pick_from[50];
397
    LOG_TRACE("(%d, %d)", pCurrent_race, pNet_race_sequence);
398
 
399
    if (pNet_race_sequence == eNet_sequence_sequential) {
400
        pCurrent_race++;
401
        if (pCurrent_race >= gNumber_of_races) {
402
            pCurrent_race = 0;
403
        }
404
    } else {
405
        most_seldom_seen = 10000;
406
        for (i = 0; i < gNumber_of_races; i++) {
407
            if (gRace_list[i].been_there_done_that < most_seldom_seen) {
408
                most_seldom_seen = gRace_list[i].been_there_done_that;
409
            }
410
        }
411
        races_count = 0;
412
        for (i = 0; i < gNumber_of_races; i++) {
413
            if (gRace_list[i].been_there_done_that == most_seldom_seen && (i != pCurrent_race)) {
414
                races_to_pick_from[races_count] = i;
415
                races_count++;
416
            }
417
        }
418
        new_index = IRandomBetween(0, races_count - 1);
419
        pCurrent_race = races_to_pick_from[new_index];
420
        gRace_list[pCurrent_race].been_there_done_that++;
421
    }
422
    return pCurrent_race;
423
}
424
 
425
// IDA: void __cdecl SwapNetCarsLoad()
426
void SwapNetCarsLoad(void) {
427
    int i;
428
    int switched_res;
429
    LOG_TRACE("()");
430
 
431
    DisableNetService();
432
    AboutToLoadFirstCar();
433
    switched_res = SwitchToRealResolution();
434
    for (i = 0; i < gNumber_of_net_players; i++) {
435
        if (gNet_players[i].next_car_index >= 0) {
436
            gNet_players[i].car_index = gNet_players[i].next_car_index;
437
        }
438
        gNet_players[i].next_car_index = -1;
439
        LoadCar(gOpponents[gNet_players[i].car_index].car_file_name,
440
            (gThis_net_player_index == i) ? eDriver_local_human : eDriver_net_human,
441
            gNet_players[i].car, gNet_players[i].car_index, gNet_players[i].player_name,
442
            &gNet_cars_storage_space);
443
    }
444
    if (switched_res) {
445
        SwitchToLoresMode();
446
    }
447
    ReenableNetService();
448
}
449
 
450
// IDA: void __cdecl SwapNetCarsDispose()
451
void SwapNetCarsDispose(void) {
20 pmbaty 452
    //int i; // Pierre-Marie Baty -- unused variable
1 pmbaty 453
    LOG_TRACE("()");
20 pmbaty 454
    NOT_IMPLEMENTED();
1 pmbaty 455
}
456
 
457
// IDA: void __cdecl DoGame()
458
void DoGame(void) {
459
    tSO_result options_result;
460
    tRace_result race_result;
461
    int second_select_race;
462
    int first_summary_done;
463
    int i;
464
    LOG_TRACE("()");
465
 
466
    gAbandon_game = 0;
467
    gDisallow_abandon_race = 0;
468
    gCar_to_view = &gProgram_state.current_car;
469
    StartLoadingScreen();
470
    gProgram_state.prog_status = eProg_game_ongoing;
471
    second_select_race = 0;
472
    if (gNet_mode == gNet_mode_of_last_game) {
473
        PrintMemoryDump(0, "BEFORE START RACE SCREEN");
474
        SelectOpponents(&gCurrent_race);
475
        if (gNet_mode != eNet_mode_none) {
476
            LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
477
            FillInRaceInfo(&gCurrent_race);
478
            DisposeRaceInfo(&gCurrent_race);
479
        } else {
480
            do {
481
                options_result = DoSelectRace(&second_select_race);
482
                if (options_result == eSO_main_menu_invoked) {
483
                    DoMainMenuScreen(0, 1, 1);
484
                }
485
                gDisallow_abandon_race = 0;
486
            } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing && !gAbandon_game);
487
        }
488
        if (gProgram_state.prog_status == eProg_game_starting
489
            || gProgram_state.prog_status == eProg_quit
490
            || gProgram_state.prog_status == eProg_idling
491
            || gAbandon_game) {
492
            PrintMemoryDump(0, "AFTER ABANDONING START RACE SCREEN");
493
            if (gProgram_state.prog_status == eProg_game_ongoing) {
494
                gProgram_state.prog_status = eProg_game_starting;
495
            }
496
        } else {
497
            PrintMemoryDump(0, "AFTER START RACE SCREEN");
498
            DoNewGameAnimation();
499
            StartLoadingScreen();
500
            if (gNet_mode != eNet_mode_none) {
501
                if (gCurrent_net_game->options.random_car_choice
502
                    && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)
503
                    && !gNo_races_yet) {
504
                    SwapNetCarsLoad();
505
                }
506
            } else {
507
                LoadOpponentsCars(&gCurrent_race);
508
            }
509
            PrintMemoryDump(0, "AFTER LOADING OPPONENTS IN");
510
            InitRace();
511
            if (gNet_mode_of_last_game == gNet_mode) {
512
                if (gProgram_state.prog_status == eProg_game_starting
513
                    || gProgram_state.prog_status == eProg_quit
514
                    || gProgram_state.prog_status == eProg_idling
515
                    || gAbandon_game) {
516
                    DisposeRace();
517
                    if (gNet_mode == eNet_mode_none && gNet_mode_of_last_game == eNet_mode_none) {
518
                        DisposeOpponentsCars(&gCurrent_race);
519
                    }
520
                    DisposeTrack();
521
                    if (gProgram_state.prog_status == eProg_game_ongoing) {
522
                        gProgram_state.prog_status = eProg_game_starting;
523
                    }
524
                } else {
525
                    if (gNet_mode != eNet_mode_none) {
526
                        do {
527
                            options_result = NetSynchRaceStart();
528
                            if (options_result == eSO_main_menu_invoked) {
529
                                DoMainMenuScreen(0, 1, 1);
530
                            }
531
                        } while (options_result == eSO_main_menu_invoked
532
                            && gProgram_state.prog_status == eProg_game_ongoing
533
                            && !gAbandon_game);
534
                    } else {
535
                        do {
536
                            options_result = DoGridPosition();
537
                            if (options_result == eSO_main_menu_invoked) {
538
                                DoMainMenuScreen(0, 1, 1);
539
                            }
540
                        } while (options_result == eSO_main_menu_invoked
541
                            && gProgram_state.prog_status == eProg_game_ongoing
542
                            && !gAbandon_game);
543
                        SetInitialPositions(&gCurrent_race);
544
                    }
545
                    if (gProgram_state.prog_status == eProg_game_starting
546
                        || gProgram_state.prog_status == eProg_quit
547
                        || gProgram_state.prog_status == eProg_idling
548
                        || gAbandon_game) {
549
                        DisposeRace();
550
                        if (!gNet_mode && !gNet_mode_of_last_game) {
551
                            DisposeOpponentsCars(&gCurrent_race);
552
                        }
553
                        DisposeTrack();
554
                        if (gProgram_state.prog_status == eProg_game_ongoing) {
555
                            gProgram_state.prog_status = eProg_game_starting;
556
                        }
557
                    } else {
558
                        SwitchToRealResolution();
559
                        InitOpponents(&gCurrent_race);
560
                        InitialiseCarsEtc(&gCurrent_race);
561
                        SetInitialCopPositions();
562
                        InitSoundSources();
563
                        InitLastDamageArrayEtc();
564
                        race_result = DoRace();
565
                        SwitchToLoresMode();
566
                        DisposeRace();
567
                        if (gNet_mode != eNet_mode_none) {
568
                            gProgram_state.current_race_index = gPending_race;
569
                            gCurrent_net_game->start_race = gPending_race;
570
                            gPending_race = -1;
571
                        }
572
                        if (race_result == eRace_completed || race_result == eRace_timed_out) {
573
                            DoEndRaceAnimation();
574
                            first_summary_done = 0;
575
                            do {
576
                                options_result = DoEndRaceSummary(&first_summary_done, race_result);
577
                                if (options_result == eSO_main_menu_invoked) {
578
                                    DoMainMenuScreen(0, 1, 1);
579
                                }
580
                            } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing);
581
                        }
582
                        if (gNet_mode) {
583
                            for (i = 0; i < gNumber_of_net_players; i++) {
584
                                TotallyRepairACar(gNet_players[i].car);
585
                            }
586
                        } else {
587
                            TotallyRepairCar();
588
                        }
589
                        if (gNet_mode) {
590
                            if (gCurrent_net_game->options.random_car_choice
591
                                && (gCurrent_net_game->options.car_choice == eNet_car_all
592
                                    || gCurrent_net_game->options.car_choice == eNet_car_both)
593
                                && !gNo_races_yet) {
594
                                SwapNetCarsDispose();
595
                            }
596
                        } else {
597
                            DisposeOpponentsCars(&gCurrent_race);
598
                        }
599
                        DisposeTrack();
600
                        if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
601
                            DoFullVersionPowerpoint();
602
                        }
603
                        gProgram_state.loaded = 0;
604
                        if (gProgram_state.prog_status == eProg_game_ongoing) {
605
                            gProgram_state.prog_status = eProg_game_starting;
606
                        }
607
                    }
608
                }
609
            } else {
610
                gProgram_state.prog_status = eProg_idling;
611
            }
612
        }
613
    } else {
614
        gProgram_state.prog_status = eProg_idling;
615
    }
616
}
617
 
618
// IDA: void __cdecl InitialiseProgramState()
619
void InitialiseProgramState(void) {
620
    gProgram_state.loaded = 0;
621
    gProgram_state.last_slot = 0;
622
    gProgram_state.frank_or_anniness = eFrankie;
623
    gProgram_state.skill_level = 1;
624
    gProgram_state.view_type = eVT_Scene;
625
    gProgram_state.auto_parts_reply = eAP_auto;
626
    gProgram_state.racing = 0;
627
    gProgram_state.cut_scene = 0;
628
    gProgram_state.saving = 0;
629
    gProgram_state.loading = 0;
630
    gProgram_state.dont_save_or_load = 0;
631
    gProgram_state.dont_load = 0;
632
    gProgram_state.mirror_on = gMirror_on__structur;
633
    gProgram_state.prog_status = eProg_intro;
634
    if (gAusterity_mode) {
635
        gProgram_state.prat_cam_on = 0;
636
        gPratcam_on = 0;
637
    } else {
638
        gProgram_state.prat_cam_on = gPratcam_on;
639
    }
640
    gProgram_state.cockpit_on = gCockpit_on;
641
    gProgram_state.car_name[0] = 0;
642
    SetSoundVolumes();
643
    AllocateRearviewPixelmap();
644
}
645
 
646
// IDA: void __cdecl DoProgram()
647
void DoProgram(void) {
648
    InitialiseProgramState();
649
    while (gProgram_state.prog_status != eProg_quit) {
650
        switch (gProgram_state.prog_status) {
651
        case eProg_intro:
652
            DisposeGameIfNecessary();
653
            DoLogos();
654
            break;
655
        case eProg_opening:
656
            DisposeGameIfNecessary();
657
            DoProgOpeningAnimation();
658
            break;
659
        case eProg_idling:
660
            DisposeGameIfNecessary();
661
            if (gGame_to_load < 0) {
662
                DoMainMenuScreen(30000u, 0, 0);
663
            } else {
664
                DoLoadGame();
665
            }
666
            break;
667
        case eProg_demo:
668
            DoProgramDemo();
669
            break;
670
        case eProg_game_starting:
671
            DoGame();
672
            break;
673
        default:
674
            break;
675
        }
676
    }
677
}
678
 
679
// IDA: void __cdecl JumpTheStart()
680
void JumpTheStart(void) {
681
    char s[256];
682
    LOG_TRACE("()");
683
 
684
    if (gNet_mode == eNet_mode_none
685
        || gProgram_state.credits_earned - gProgram_state.credits_lost >= gJump_start_fine[gProgram_state.skill_level]) {
686
        WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(gCountdown);
687
        gCountdown = 0;
688
        DRS3StopOutletSound(gPedestrians_outlet);
689
        DRS3StartSound(gPedestrians_outlet, 8016);
690
        SpendCredits(gJump_start_fine[gProgram_state.skill_level]);
691
        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));
692
        NewTextHeadupSlot(4, 0, 1000, -4, s);
693
    }
694
}
695
 
696
// IDA: void __cdecl GoingToInterfaceFromRace()
697
void GoingToInterfaceFromRace(void) {
698
    LOG_TRACE("()");
699
 
700
    gInterface_within_race_mode = 1;
701
    PlayFlicsFromDisk();
702
    SwitchToLoresMode();
703
    if (gNet_mode == eNet_mode_host) {
704
        SendGameplayToAllPlayers(eNet_gameplay_host_paused, 0, 0, 0, 0);
705
    }
706
}
707
 
708
// IDA: void __cdecl GoingBackToRaceFromInterface()
709
void GoingBackToRaceFromInterface(void) {
710
    LOG_TRACE("()");
711
 
712
    gInterface_within_race_mode = 0;
713
    PlayFlicsFromMemory();
714
    SwitchToRealResolution();
715
    if (gNet_mode == eNet_mode_host) {
716
        SendGameplayToAllPlayers(eNet_gameplay_host_unpaused, 0, 0, 0, 0);
717
    }
718
}