Subversion Repositories Games.Carmageddon

Rev

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