Subversion Repositories Games.Carmageddon

Rev

Rev 1 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
#include "racestrt.h"
18 pmbaty 2
#include "brender.h"
1 pmbaty 3
#include "cutscene.h"
4
#include "displays.h"
5
#include "drmem.h"
6
#include "errors.h"
7
#include "flicplay.h"
8
#include "globvars.h"
9
#include "globvrpb.h"
10
#include "grafdata.h"
11
#include "graphics.h"
12
#include "harness/config.h"
13
#include "harness/trace.h"
14
#include "input.h"
15
#include "intrface.h"
16
#include "loading.h"
17
#include "netgame.h"
18
#include "network.h"
19
#include "newgame.h"
20
#include "opponent.h"
21
#include "pd/sys.h"
22
#include "racestrt.h"
23
#include "sound.h"
24
#include "structur.h"
25
#include "utility.h"
26
#include "world.h"
27
#include <stdlib.h>
28
 
29
int gGrid_number_colour[4] = { 49u, 201u, 1u, 201u };
30
int gJust_bought_part;
31
tU32 gLast_host_query;
32
br_pixelmap* gDead_car;
33
int gFade_away_parts_shop;
34
tU32 gDare_start_time;
35
int gRefund_rate;
36
int gSwap_grid_2;
37
int gSwap_grid_1;
38
int gChange_race_net_mode;
39
tParts_category gPart_category;
40
tU32 gNet_synch_start;
41
tNet_game_details* gChoose_car_net_game;
42
int gPart_index;
18 pmbaty 43
int gChallenger_index__racestrt; // suffix added to avoid duplicate symbol
1 pmbaty 44
tGrid_draw gDraw_grid_status;
45
tNet_sequence_type gNet_race_sequence__racestrt; // suffix added to avoid duplicate symbol
46
br_pixelmap* gTaken_image;
47
int gGrid_number_x_coords[31];
48
int gGrid_transition_stage;
49
int gGrid_y_adjust;
50
br_pixelmap* gBullet_image;
51
br_pixelmap* gDeceased_image;
52
int gBest_pos_available;
53
int gChallenger_position;
54
int gOpponent_index;
55
int gChallenge_time;
56
int gOriginal_position;
57
int gCurrent_race_index;
58
tInterface_spec* gStart_interface_spec;
59
int gCurrent_car_index;
60
int gOur_starting_position;
61
 
62
// IDA: void __usercall DrawRaceList(int pOffset@<EAX>)
63
void DrawRaceList(int pOffset) {
64
    int i;
65
    //int font_height; // Pierre-Marie Baty -- unused variable
66
    int pitch;
67
    int y;
68
    int left_most;
69
    int right_most;
70
    int rank_colour;
71
    int text_colour;
72
    //int box_bot; // Pierre-Marie Baty -- unused variable
73
    //int text_width; // Pierre-Marie Baty -- unused variable
74
    int text_x;
75
    char rank_str[256];
76
    LOG_TRACE("(%d)", pOffset);
77
 
78
    left_most = (gCurrent_graf_data->choose_race_rank_right - 2 * gBig_font->width[48] + gCurrent_graf_data->choose_race_left) / 2;
79
    right_most = gCurrent_graf_data->choose_race_right - (left_most - gCurrent_graf_data->choose_race_left);
80
    BrPixelmapRectangleFill(gBack_screen,
81
        left_most,
82
        gCurrent_graf_data->choose_race_y_top,
83
        gCurrent_graf_data->choose_race_right - gCurrent_graf_data->choose_race_left - 2 * (left_most - gCurrent_graf_data->choose_race_left),
84
        gCurrent_graf_data->choose_race_y_bottom - gCurrent_graf_data->choose_race_y_top,
85
        0);
86
    pitch = gCurrent_graf_data->choose_race_y_pitch;
87
    for (i = 0; i < gNumber_of_races; i++) {
88
        y = pitch * i + gCurrent_graf_data->choose_race_curr_y - pOffset;
89
        if (gCurrent_graf_data->choose_race_y_top <= (y - (TranslationMode() ? 2 : 0)) && (y + gBig_font->glyph_y) < gCurrent_graf_data->choose_race_line_y) {
90
            if ((gProgram_state.rank > gRace_list[i].rank_required || gProgram_state.rank < gRace_list[i].best_rank) && !gProgram_state.game_completed && !gChange_race_net_mode) {
91
                rank_colour = 44;
92
                text_colour = 49;
93
            } else if (gCurrent_graf_data->choose_race_curr_y == y) {
94
                rank_colour = 5;
95
                text_colour = 1;
96
            } else {
97
                rank_colour = 198;
98
                text_colour = 201;
99
            }
100
            if (!gChange_race_net_mode) {
101
                sprintf(rank_str, "%2d", gRace_list[i].rank_required);
102
                TransBrPixelmapText(gBack_screen,
103
                    gCurrent_graf_data->choose_race_rank_right - BrPixelmapTextWidth(gBack_screen, gBig_font, rank_str),
104
                    y,
105
                    rank_colour,
106
                    gBig_font,
107
                    rank_str);
108
            }
109
            TransBrPixelmapText(gBack_screen,
110
                gCurrent_graf_data->choose_race_name_left,
111
                y,
112
                text_colour,
113
                gBig_font,
114
                gRace_list[i].name);
115
            if (gRace_list[i].been_there_done_that && gBullet_image != NULL) {
116
                BrPixelmapRectangleCopy(gBack_screen,
117
                    gCurrent_graf_data->choose_race_bullet_left,
118
                    y + (gBig_font->glyph_y - gBullet_image->height) / 2,
119
                    gBullet_image,
120
                    0,
121
                    0,
122
                    gBullet_image->width,
123
                    gBullet_image->height);
124
            }
125
        }
126
    }
127
    if (gChange_race_net_mode) {
128
        strcpy(rank_str, GetMiscString(gNet_race_sequence__racestrt == 1 ? kMiscString_SUBSEQUENT_RACES_WILL_BE_RANDOM : kMiscString_RACES_WILL_CONTINUE_DOWN_THIS_LIST));
129
        TransBrPixelmapText(gBack_screen,
130
            (right_most + left_most - BrPixelmapTextWidth(gBack_screen, gBig_font, rank_str)) / 2,
131
            gCurrent_graf_data->choose_race_current_text_y,
132
            5,
133
            gBig_font,
134
            rank_str);
135
    } else {
136
        sprintf(rank_str, "%s%d", GetMiscString(kMiscString_YourCurrentRankIs), gProgram_state.rank);
137
        text_x = (gCurrent_graf_data->choose_race_left + gCurrent_graf_data->choose_race_right) / 2 - BrPixelmapTextWidth(gBack_screen, gBig_font, rank_str) / 2;
138
        TransBrPixelmapText(gBack_screen,
139
            text_x,
140
            gCurrent_graf_data->choose_race_current_text_y,
141
            3,
142
            gBig_font,
143
            GetMiscString(kMiscString_YourCurrentRankIs));
144
        sprintf(rank_str, "%d", gProgram_state.rank);
145
        TransBrPixelmapText(gBack_screen,
146
            text_x + BrPixelmapTextWidth(gBack_screen, gBig_font, GetMiscString(kMiscString_YourCurrentRankIs)),
147
            gCurrent_graf_data->choose_race_current_text_y,
148
            5,
149
            gBig_font,
150
            rank_str);
151
    }
152
    BrPixelmapLine(gBack_screen,
153
        left_most,
154
        gCurrent_graf_data->choose_race_line_y,
155
        right_most,
156
        gCurrent_graf_data->choose_race_line_y,
157
        6);
158
    DrawRectangle(gBack_screen,
159
        left_most,
160
        gCurrent_graf_data->choose_race_box_top - 1,
161
        right_most,
162
        2 * gCurrent_graf_data->choose_race_curr_y - gCurrent_graf_data->choose_race_box_top + gBig_font->glyph_y,
163
        3);
164
    PDScreenBufferSwap(0);
165
}
166
 
167
// IDA: void __usercall MoveRaceList(int pFrom@<EAX>, int pTo@<EDX>, tS32 pTime_to_move@<EBX>)
168
void MoveRaceList(int pFrom, int pTo, tS32 pTime_to_move) {
169
    tS32 start_time;
170
    tS32 the_time;
171
    //int move_distance; // Pierre-Marie Baty -- unused variable
172
    int pitch;
173
    LOG_TRACE("(%d, %d, %d)", pFrom, pTo, pTime_to_move);
174
 
175
    pitch = gCurrent_graf_data->choose_race_y_pitch;
176
    start_time = PDGetTotalTime();
177
    while (1) {
178
        the_time = PDGetTotalTime();
179
        if (start_time + pTime_to_move <= the_time)
180
            break;
181
        DrawRaceList((the_time - start_time) * (pTo - pFrom) * pitch / pTime_to_move + pitch * pFrom);
182
    }
183
    DrawRaceList(pitch * pTo);
184
}
185
 
186
// IDA: int __usercall UpRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
187
int UpRace(int* pCurrent_choice, int* pCurrent_mode) {
188
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
189
 
190
    AddToFlicQueue(gStart_interface_spec->pushed_flics[2].flic_index,
191
        gStart_interface_spec->pushed_flics[2].x[gGraf_data_index],
192
        gStart_interface_spec->pushed_flics[2].y[gGraf_data_index],
193
        1);
194
    DRS3StartSound(gEffects_outlet, 3000);
195
    if (gCurrent_race_index != 0 && (gRace_list[gCurrent_race_index - 1].best_rank <= gProgram_state.rank || gProgram_state.game_completed || gChange_race_net_mode)) {
196
        RemoveTransientBitmaps(1);
197
        MoveRaceList(gCurrent_race_index, gCurrent_race_index - 1, 150);
198
        gCurrent_race_index--;
199
    }
200
    return 0;
201
}
202
 
203
// IDA: int __usercall DownRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
204
int DownRace(int* pCurrent_choice, int* pCurrent_mode) {
205
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
206
 
207
    AddToFlicQueue(gStart_interface_spec->pushed_flics[3].flic_index,
208
        gStart_interface_spec->pushed_flics[3].x[gGraf_data_index],
209
        gStart_interface_spec->pushed_flics[3].y[gGraf_data_index],
210
        1);
211
    DRS3StartSound(gEffects_outlet, 3000);
212
    if (gCurrent_race_index < gNumber_of_races - 1 && (gProgram_state.rank <= gRace_list[gCurrent_race_index + 1].rank_required || gProgram_state.game_completed || gChange_race_net_mode)) {
213
        RemoveTransientBitmaps(1);
214
        MoveRaceList(gCurrent_race_index, gCurrent_race_index + 1, 150);
215
        gCurrent_race_index++;
216
    }
217
    return 0;
218
}
219
 
220
// IDA: int __usercall ClickOnRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
221
int ClickOnRace(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
222
    int x_coord;
223
    int y_coord;
224
    int race_delta;
225
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
226
 
227
    GetMousePosition(&x_coord, &y_coord);
228
    race_delta = (y_coord - (gCurrent_graf_data->choose_race_curr_y - 5 * (gCurrent_graf_data->choose_race_y_pitch / 2) + gBig_font->glyph_y / 2)) / gCurrent_graf_data->choose_race_y_pitch - 2;
229
    if (race_delta >= -2 && race_delta <= -1) {
230
        do {
231
            UpRace(pCurrent_choice, pCurrent_mode);
232
            race_delta++;
233
        } while (race_delta != 0);
234
    } else if (race_delta > 0 && race_delta < 3) {
235
        do {
236
            DownRace(pCurrent_choice, pCurrent_mode);
237
            race_delta--;
238
        } while (race_delta != 0);
239
    }
240
    return 0;
241
}
242
 
243
// IDA: int __usercall UpClickRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
244
int UpClickRace(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
245
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
246
 
247
    UpRace(pCurrent_choice, pCurrent_mode);
248
    return 0;
249
}
250
 
251
// IDA: int __usercall DownClickRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
252
int DownClickRace(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
253
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
254
 
255
    DownRace(pCurrent_choice, pCurrent_mode);
256
    return 0;
257
}
258
 
259
// IDA: void __cdecl StartChangeRace()
260
void StartChangeRace(void) {
261
    LOG_TRACE("()");
262
 
263
    MoveRaceList(-3, gCurrent_race_index, 400);
264
}
265
 
266
// IDA: int __usercall ChangeRace@<EAX>(int *pRace_index@<EAX>, int pNet_mode@<EDX>, tNet_sequence_type pNet_race_sequence@<EBX>)
267
int ChangeRace(int* pRace_index, int pNet_mode, tNet_sequence_type pNet_race_sequence) {
268
    static tFlicette flicker_on[4] = {
269
        { 43, { 60, 120 }, { 154, 370 } },
270
        { 43, { 221, 442 }, { 154, 370 } },
271
        { 221, { 30, 60 }, { 78, 187 } },
272
        { 221, { 30, 60 }, { 78, 187 } },
273
    };
274
    static tFlicette flicker_off[4] = {
275
        { 42, { 60, 120 }, { 154, 370 } },
276
        { 42, { 221, 442 }, { 154, 370 } },
277
        { 220, { 30, 60 }, { 78, 187 } },
278
        { 220, { 30, 60 }, { 78, 187 } },
279
    };
280
    static tFlicette push[4] = {
281
        { 154, { 60, 120 }, { 154, 370 } },
282
        { 45, { 221, 442 }, { 154, 370 } },
283
        { 222, { 30, 60 }, { 78, 187 } },
284
        { 225, { 30, 60 }, { 118, 283 } },
285
    };
286
    static tMouse_area mouse_areas[5] = {
287
        { { 60, 120 }, { 154, 370 }, { 125, 250 }, { 174, 418 }, 0, 0, 0, NULL },
288
        { { 221, 442 }, { 154, 370 }, { 286, 572 }, { 174, 418 }, 1, 0, 0, NULL },
289
        { { 30, 60 }, { 78, 187 }, { 45, 90 }, { 104, 250 }, -1, 0, 0, UpClickRace },
290
        { { 30, 60 }, { 118, 283 }, { 45, 90 }, { 145, 348 }, -1, 0, 0, DownClickRace },
291
        { { 66, 132 }, { 33, 79 }, { 278, 556 }, { 144, 346 }, -1, 0, 0, ClickOnRace },
292
    };
293
    static tInterface_spec interface_spec = {
294
        0, 230, 60, 231, 231, 231, 6,
295
        { -1, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
296
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
297
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { UpRace, NULL },
298
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { DownRace, NULL },
299
        { 1, 1 }, { NULL, NULL }, { 1, 1 }, { NULL, NULL },
300
        NULL, NULL, 0, NULL, StartChangeRace, NULL, 0, { 0, 0 },
301
        NULL, 1, 1, COUNT_OF(flicker_on), flicker_on, flicker_off, push,
302
        COUNT_OF(mouse_areas), mouse_areas, 0, NULL
303
    };
304
    int result;
305
    LOG_TRACE("(%p, %d, %d)", pRace_index, pNet_mode, pNet_race_sequence);
306
 
307
    gNet_race_sequence__racestrt = pNet_race_sequence;
308
    gChange_race_net_mode = pNet_mode;
309
    gStart_interface_spec = &interface_spec;
310
    gCurrent_race_index = *pRace_index;
311
    if (pNet_mode == 0) {
312
        gBullet_image = LoadPixelmap("BULLET.PIX");
313
    } else {
314
        gBullet_image = NULL;
315
    }
316
    result = DoInterfaceScreen(&interface_spec, pNet_mode != 0, 0);
317
    if (result == 0) {
318
        *pRace_index = gCurrent_race_index;
319
    }
320
    if (gBullet_image != NULL) {
321
        BrPixelmapFree(gBullet_image);
322
    }
323
    return result == 0;
324
}
325
 
326
// IDA: void __cdecl DoChangeRace()
327
void DoChangeRace(void) {
328
    LOG_TRACE("()");
329
 
330
    if (ChangeRace(&gProgram_state.current_race_index, 0, eNet_sequence_sequential) != 0) {
331
        gProgram_state.current_race_index = gCurrent_race_index;
332
    }
333
}
334
 
335
// IDA: void __usercall DrawCar(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
336
void DrawCar(int pCurrent_choice, int pCurrent_mode) {
337
    char s[64];
338
    int text_x;
339
    int text_width;
340
    LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
341
 
342
// Added by dethrace to ignore warnings about using sprintf without a literal format string
343
#ifndef _MSC_VER // Pierre-Marie Baty -- exclude MSVC
344
#pragma GCC diagnostic push
345
#pragma GCC diagnostic ignored "-Wformat-security"
346
#endif // !_MSC_VER
347
 
348
    PollCarDetails(gChoose_car_net_game);
349
    if (gChange_race_net_mode == 0) {
350
        if (gProgram_state.number_of_cars == 1) {
351
            sprintf(s, GetMiscString(kMiscString_NoOtherCarsToChooseFromYet));
352
        } else if (gProgram_state.cars_available[gCurrent_car_index] == gProgram_state.frank_or_anniness) {
353
            sprintf(s, GetMiscString(kMiscString_YourOriginalCar));
354
        } else {
355
            sprintf(s, "%s %s", GetMiscString(kMiscString_OriginalDriverWas), gOpponents[gProgram_state.cars_available[gCurrent_car_index]].name);
356
        }
357
    } else if (gCar_details[gProgram_state.cars_available[gCurrent_car_index]].ownership == eCar_owner_someone) {
358
        sprintf(s, "%s %s", GetMiscString(kMiscString_THIS_CAR_ALREADY_TAKEN_BY), gCar_details[gProgram_state.cars_available[gCurrent_car_index]].name);
359
    } else {
360
        sprintf(s, GetMiscString(kMiscString_AVAILABLE));
361
    }
362
 
363
#ifndef _MSC_VER // Pierre-Marie Baty -- exclude MSVC
364
#pragma GCC diagnostic pop
365
#endif // !_MSC_VER
366
 
367
    text_width = BrPixelmapTextWidth(gBack_screen, gFont_7, s);
368
    text_x = (gCurrent_graf_data->change_car_line_right + gCurrent_graf_data->change_car_line_left - text_width) / 2;
369
    BrPixelmapRectangleFill(gBack_screen,
370
        gCurrent_graf_data->change_car_line_left,
371
        gCurrent_graf_data->change_car_text_y,
372
        gCurrent_graf_data->change_car_line_right - gCurrent_graf_data->change_car_line_left,
373
        gFont_7->glyph_y,
374
        0);
375
    TransBrPixelmapText(gBack_screen,
376
        text_x,
377
        gCurrent_graf_data->change_car_text_y,
378
        3,
379
        gFont_7,
380
        s);
381
    BrPixelmapLine(gBack_screen,
382
        gCurrent_graf_data->change_car_line_left,
383
        gCurrent_graf_data->change_car_line_y,
384
        gCurrent_graf_data->change_car_line_right,
385
        gCurrent_graf_data->change_car_line_y,
386
        6);
387
    if (gChange_race_net_mode && gCar_details[gProgram_state.cars_available[gCurrent_car_index]].ownership == eCar_owner_someone) {
388
        DRPixelmapRectangleMaskedCopy(
389
            gBack_screen,
390
            (gCurrent_graf_data->change_car_panel_left + gCurrent_graf_data->change_car_panel_right - gTaken_image->width) / 2,
391
            (gCurrent_graf_data->change_car_panel_bottom + gCurrent_graf_data->change_car_panel_bottom - gTaken_image->height) / 2,
392
            gTaken_image,
393
            0,
394
            0,
395
            gTaken_image->width,
396
            gTaken_image->height);
397
    }
398
}
399
 
400
// IDA: void __cdecl SetCarFlic()
401
void SetCarFlic(void) {
402
    LOG_TRACE("()");
403
 
404
    ChangePanelFlic(0,
405
        gOpponents[gProgram_state.cars_available[gCurrent_car_index]].stolen_car_image_data,
406
        gOpponents[gProgram_state.cars_available[gCurrent_car_index]].stolen_car_image_data_length);
407
}
408
 
409
// IDA: int __usercall UpCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
410
int UpCar(int* pCurrent_choice, int* pCurrent_mode) {
411
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
412
 
413
    AddToFlicQueue(gStart_interface_spec->pushed_flics[2].flic_index,
414
        gStart_interface_spec->pushed_flics[2].x[gGraf_data_index],
415
        gStart_interface_spec->pushed_flics[2].y[gGraf_data_index],
416
        1);
417
    DRS3StartSound(gEffects_outlet, 3000);
418
    if (gCurrent_car_index != 0) {
419
        RemoveTransientBitmaps(1);
420
        DropOutImageThruBottom(GetPanelPixelmap(0),
421
            gCurrent_graf_data->change_car_panel_left,
422
            gCurrent_graf_data->change_car_panel_top,
423
            gCurrent_graf_data->change_car_panel_top_clip,
424
            gCurrent_graf_data->change_car_panel_bottom_clip);
425
        gCurrent_car_index--;
426
        SetCarFlic();
427
        DropInImageFromTop(GetPanelPixelmap(0),
428
            gCurrent_graf_data->change_car_panel_left,
429
            gCurrent_graf_data->change_car_panel_top,
430
            gCurrent_graf_data->change_car_panel_top_clip,
431
            gCurrent_graf_data->change_car_panel_bottom_clip);
432
    }
433
    return 0;
434
}
435
 
436
// IDA: int __usercall DownCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
437
int DownCar(int* pCurrent_choice, int* pCurrent_mode) {
438
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
439
 
440
    AddToFlicQueue(gStart_interface_spec->pushed_flics[3].flic_index,
441
        gStart_interface_spec->pushed_flics[3].x[gGraf_data_index],
442
        gStart_interface_spec->pushed_flics[3].y[gGraf_data_index],
443
        1);
444
    DRS3StartSound(gEffects_outlet, 3000);
445
    if (gCurrent_car_index < gProgram_state.number_of_cars - 1) {
446
        RemoveTransientBitmaps(1);
447
        DropOutImageThruTop(GetPanelPixelmap(0),
448
            gCurrent_graf_data->change_car_panel_left,
449
            gCurrent_graf_data->change_car_panel_top,
450
            gCurrent_graf_data->change_car_panel_top_clip,
451
            gCurrent_graf_data->change_car_panel_bottom_clip);
452
        gCurrent_car_index++;
453
        SetCarFlic();
454
        DropInImageFromBottom(GetPanelPixelmap(0),
455
            gCurrent_graf_data->change_car_panel_left,
456
            gCurrent_graf_data->change_car_panel_top,
457
            gCurrent_graf_data->change_car_panel_top_clip,
458
            gCurrent_graf_data->change_car_panel_bottom_clip);
459
    }
460
    return 0;
461
}
462
 
463
// IDA: int __usercall UpClickCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
464
int UpClickCar(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
465
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
466
 
467
    UpCar(pCurrent_choice, pCurrent_mode);
468
    return 0;
469
}
470
 
471
// IDA: int __usercall DownClickCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
472
int DownClickCar(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
473
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
474
 
475
    DownCar(pCurrent_choice, pCurrent_mode);
476
    return 0;
477
}
478
 
479
// IDA: int __usercall ChangeCarGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
480
int ChangeCarGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
481
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
482
 
483
    if (gChange_race_net_mode == 0 || gCar_details[gProgram_state.cars_available[gCurrent_car_index]].ownership != eCar_owner_someone) {
484
        return 1;
485
    } else {
486
        DRS3StartSound(gEffects_outlet, 3100);
487
        return 0;
488
    }
489
}
490
 
491
// IDA: int __usercall ChangeCar@<EAX>(int pNet_mode@<EAX>, int *pCar_index@<EDX>, tNet_game_details *pNet_game@<EBX>)
492
int ChangeCar(int pNet_mode, int* pCar_index, tNet_game_details* pNet_game) {
493
    static tFlicette flicker_on[4] = {
494
        { 43, { 60, 120 }, { 154, 370 } },
495
        { 43, { 221, 442 }, { 154, 370 } },
496
        { 221, { 30, 60 }, { 78, 187 } },
497
        { 221, { 30, 60 }, { 78, 187 } },
498
    };
499
    static tFlicette flicker_off[4] = {
500
        { 42, { 60, 120 }, { 154, 370 } },
501
        { 42, { 221, 442 }, { 154, 370 } },
502
        { 220, { 30, 60 }, { 78, 187 } },
503
        { 220, { 30, 60 }, { 78, 187 } },
504
    };
505
    static tFlicette push[4] = {
506
        { 154, { 60, 120 }, { 154, 370 } },
507
        { 45, { 221, 442 }, { 154, 370 } },
508
        { 222, { 30, 60 }, { 78, 187 } },
509
        { 225, { 30, 60 }, { 118, 283 } },
510
    };
511
    static tMouse_area mouse_areas[4] = {
512
        { { 60, 120 }, { 154, 370 }, { 125, 250 }, { 174, 418 }, 0, 0, 0, NULL },
513
        { { 221, 442 }, { 154, 370 }, { 286, 572 }, { 174, 418 }, 1, 0, 0, NULL },
514
        { { 30, 60 }, { 78, 187 }, { 45, 90 }, { 104, 250 }, -1, 0, 0, UpClickCar },
515
        { { 30, 60 }, { 118, 283 }, { 45, 90 }, { 145, 348 }, -1, 0, 0, DownClickCar },
516
    };
517
    static tInterface_spec interface_spec = {
518
        0,
519
        236,
520
        62,
521
        0,
522
        0,
523
        0,
524
        -1,
525
        { -1, 0 },
526
        { -1, 0 },
527
        { 0, 0 },
528
        { 1, 0 },
529
        { NULL, NULL },
530
        { -1, 0 },
531
        { 1, 0 },
532
        { 0, 0 },
533
        { 1, 0 },
534
        { NULL, NULL },
535
        { -1, 0 },
536
        { 0, 0 },
537
        { 0, 0 },
538
        { 0, 0 },
539
        { UpCar, NULL },
540
        { -1, 0 },
541
        { 0, 0 },
542
        { 0, 0 },
543
        { 0, 0 },
544
        { DownCar, NULL },
545
        { 1, 1 },
546
        { ChangeCarGoAhead, NULL },
547
        { 1, 1 },
548
        { NULL, NULL },
549
        NULL,
550
        DrawCar,
551
        0,
552
        NULL,
553
        NULL,
554
        NULL,
555
        0,
556
        { 0, 0 },
557
        NULL,
558
        1,
559
        1,
560
        COUNT_OF(flicker_on),
561
        flicker_on,
562
        flicker_off,
563
        push,
564
        COUNT_OF(mouse_areas),
565
        mouse_areas,
566
        0,
567
        NULL,
568
    };
569
    int i;
570
    int result;
571
    int power_up_levels[3];
572
    LOG_TRACE("(%d, %p, %p)", pNet_mode, pCar_index, pNet_game);
573
 
574
    gChoose_car_net_game = pNet_game;
575
    gChange_race_net_mode = pNet_mode;
576
    gStart_interface_spec = &interface_spec;
577
    for (i = 0; i < gProgram_state.number_of_cars; i++) {
578
        if (gProgram_state.cars_available[i] == *pCar_index) {
579
            gCurrent_car_index = i;
580
            break;
581
        }
582
    }
583
    if (gProgram_state.game_completed) {
584
        gProgram_state.number_of_cars = gNumber_of_racers;
585
        for (i = 0; i < gProgram_state.number_of_cars; i++) {
586
            gProgram_state.cars_available[i] = i;
587
        }
588
    }
589
    for (i = 0; i < gProgram_state.number_of_cars; i++) {
590
        if (gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data == NULL) {
591
            LoadFlicData(gOpponents[gProgram_state.cars_available[i]].stolen_car_flic_name,
592
                &gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data,
593
                &gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data_length);
594
        } else {
595
            MAMSLock((void**)&gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data);
596
        }
597
    }
598
    InitialiseFlicPanel(0,
599
        gCurrent_graf_data->change_car_panel_left,
600
        gCurrent_graf_data->change_car_panel_top,
601
        gCurrent_graf_data->change_car_panel_right - gCurrent_graf_data->change_car_panel_left,
602
        gCurrent_graf_data->change_car_panel_bottom - gCurrent_graf_data->change_car_panel_top);
603
    SetCarFlic();
604
    if (pNet_mode) {
605
        gTaken_image = LoadPixelmap("TAKEN.PIX");
606
    }
607
    result = DoInterfaceScreen(&interface_spec, pNet_mode, 0);
608
    if (pNet_mode) {
609
        FadePaletteDown();
610
    } else {
611
        RunFlic(237);
612
    }
613
    for (i = 0; i < gProgram_state.number_of_cars; i++) {
614
        MAMSUnlock((void**)&gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data);
615
    }
616
    DisposeFlicPanel(0);
617
    if (pNet_mode) {
618
        BrPixelmapFree(gTaken_image);
619
    }
620
    if (result == 0) {
621
        if (pNet_mode) {
18 pmbaty 622
            *pCar_index = gProgram_state.cars_available[gCurrent_car_index];
1 pmbaty 623
        } else {
624
            AboutToLoadFirstCar();
625
            SwitchToRealResolution();
626
            for (i = 0; i < COUNT_OF(power_up_levels); i++) {
627
                power_up_levels[i] = gProgram_state.current_car.power_up_levels[i];
628
            }
629
            LoadCar(gOpponents[gProgram_state.cars_available[gCurrent_car_index]].car_file_name,
630
                eDriver_local_human,
631
                &gProgram_state.current_car,
632
                gProgram_state.cars_available[gCurrent_car_index],
633
                gProgram_state.player_name[gProgram_state.frank_or_anniness],
634
                &gOur_car_storage_space);
635
            for (i = 0; i < COUNT_OF(power_up_levels); i++) {
636
                gProgram_state.current_car.power_up_levels[i] = power_up_levels[i];
637
            }
638
            SwitchToLoresMode();
639
            SetCarStorageTexturingLevel(&gOur_car_storage_space, GetCarTexturingLevel(), eCTL_full);
640
            DisposeRaceInfo(&gCurrent_race);
641
            SelectOpponents(&gCurrent_race);
642
            LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
643
        }
644
        return 1;
645
    } else {
646
        return 0;
647
    }
648
}
649
 
650
// IDA: void __cdecl DoChangeCar()
651
void DoChangeCar(void) {
652
    LOG_TRACE("()");
653
 
654
    ChangeCar(0, &gProgram_state.current_car.index, NULL);
655
}
656
 
657
// IDA: int __cdecl PartsShopRecommended()
658
int PartsShopRecommended(void) {
659
    int running_cost;
660
    int i;
661
    int current_index;
662
    int counter;
663
    LOG_TRACE("()");
664
 
665
    running_cost = 0;
666
    counter = 0;
667
    for (i = 0; i < eParts_count; i++) {
668
        current_index = gProgram_state.current_car.power_up_levels[i];
669
        if (current_index + 1 < gProgram_state.current_car.power_ups[i].number_of_parts && (gProgram_state.rank <= gProgram_state.current_car.power_ups[i].info[current_index + 1].rank_required || gProgram_state.game_completed)) {
670
            running_cost += gProgram_state.current_car.power_ups[i].info[current_index + 1].prices[gProgram_state.skill_level];
671
            counter++;
672
        }
673
    }
674
    return running_cost != 0 && ((float)running_cost / counter * 1.5f <= gProgram_state.credits);
675
}
676
 
677
// IDA: void __usercall CalcPartPrice(int pCategory@<EAX>, int pIndex@<EDX>, int *pPrice@<EBX>, int *pCost@<ECX>)
678
void CalcPartPrice(int pCategory, int pIndex, int* pPrice, int* pCost) {
679
    //int current_value; // Pierre-Marie Baty -- unused variable
680
    LOG_TRACE("(%d, %d, %p, %p)", pCategory, pIndex, pPrice, pCost);
681
 
682
    *pPrice = gProgram_state.current_car.power_ups[pCategory].info[pIndex].prices[gProgram_state.skill_level];
683
    if (gProgram_state.current_car.power_up_levels[pCategory] == pIndex) {
684
        *pCost = 0;
685
    } else {
686
        *pCost = *pPrice - 10 * (gRefund_rate * gProgram_state.current_car.power_ups[pCategory].info[gProgram_state.current_car.power_up_levels[pCategory]].prices[gProgram_state.skill_level] / 1000);
687
    }
688
}
689
 
690
// IDA: int __usercall BuyPart@<EAX>(int pCategory@<EAX>, int pIndex@<EDX>)
691
int BuyPart(int pCategory, int pIndex) {
692
    int price;
693
    int cost;
694
    LOG_TRACE("(%d, %d)", pCategory, pIndex);
695
 
696
    CalcPartPrice(pCategory, pIndex, &price, &cost);
697
    if (cost == 0) {
698
        return 1;
699
    } else if (gProgram_state.credits < cost) {
700
        return 0;
701
    } else {
702
        gProgram_state.credits -= cost;
703
        if (gProgram_state.credits > 999999) {
704
            gProgram_state.credits = 999999;
705
        }
706
        gProgram_state.current_car.power_up_levels[pCategory] = pIndex;
707
        return 1;
708
    }
709
}
710
 
711
// IDA: void __cdecl DoAutoParts()
712
void DoAutoParts(void) {
713
    int i;
714
    int lowest_yet;
715
    int lowest_one;
716
    int price;
717
    int cost;
718
    int current_level;
719
    LOG_TRACE("()");
720
 
721
    while (1) {
722
        if (!PartsShopRecommended()) {
723
            return;
724
        }
725
        lowest_yet = COUNT_OF(((tParts_spec*)NULL)->info);
726
        for (i = 0; i < eParts_count; i++) {
727
            current_level = gProgram_state.current_car.power_up_levels[i];
728
            if (current_level + 1 < gProgram_state.current_car.power_ups[i].number_of_parts && (gProgram_state.rank <= gProgram_state.current_car.power_ups[i].info[current_level + 1].rank_required || gProgram_state.game_completed)) {
729
                CalcPartPrice(i, current_level + 1, &price, &cost);
730
                if (cost != 0 && cost <= gProgram_state.credits && current_level < lowest_yet) {
731
                    lowest_one = i;
732
                    lowest_yet = current_level + 1;
733
                }
734
            }
735
        }
736
        if (lowest_yet == COUNT_OF(((tParts_spec*)NULL)->info)) {
737
            break;
738
        }
739
        if (!BuyPart(lowest_one, lowest_yet)) {
740
            return;
741
        }
742
    }
743
}
744
 
745
// IDA: void __cdecl DrawPartsLabel()
746
void DrawPartsLabel(void) {
747
    LOG_TRACE("()");
748
 
749
    switch (gPart_category) {
750
    case eParts_armour:
751
        RunFlicAt(262, gCurrent_graf_data->parts_label_x, gCurrent_graf_data->parts_label_y);
752
        break;
753
    case eParts_power:
754
        RunFlicAt(263, gCurrent_graf_data->parts_label_x, gCurrent_graf_data->parts_label_y);
755
        break;
756
    case eParts_offensive:
757
        RunFlicAt(264, gCurrent_graf_data->parts_label_x, gCurrent_graf_data->parts_label_y);
758
        break;
759
    default:
760
        break;
761
    }
762
}
763
 
764
// IDA: void __usercall ErasePartsText(int pTotal_as_well@<EAX>)
765
void ErasePartsText(int pTotal_as_well) {
766
    LOG_TRACE("(%d)", pTotal_as_well);
767
 
768
    BrPixelmapRectangleFill(gBack_screen,
769
        gCurrent_graf_data->parts_cost_x,
770
        gCurrent_graf_data->parts_cost_y,
771
        gCurrent_graf_data->parts_image_width - (gCurrent_graf_data->parts_cost_x - gCurrent_graf_data->parts_image_x),
772
        gFont_7->glyph_y + gCurrent_graf_data->parts_net_y - gCurrent_graf_data->parts_cost_y,
773
        0);
774
    if (pTotal_as_well) {
775
        BrPixelmapRectangleFill(gBack_screen,
776
            gCurrent_graf_data->parts_total_x,
777
            gCurrent_graf_data->parts_total_y,
778
            gCurrent_graf_data->parts_image_width - (gCurrent_graf_data->parts_total_x - gCurrent_graf_data->parts_image_x),
779
            gFont_7->glyph_y,
780
            0);
781
    }
782
}
783
 
784
// IDA: void __cdecl DrawPartsText()
785
void DrawPartsText(void) {
786
    int price;
787
    int cost;
788
    LOG_TRACE("()");
789
 
790
    CalcPartPrice(gPart_category, gPart_index, &price, &cost);
791
    TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_cost_x, gCurrent_graf_data->parts_cost_y, 5, gFont_7, GetMiscString(kMiscString_RetailColon));
792
    BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_cost_y - (TranslationMode() ? 2 : 0), 5, gFont_7, "%d", price);
793
    if (cost > 0) {
794
        TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 45, gFont_7, GetMiscString(kMiscString_YouPayColon));
795
        BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_net_y - (TranslationMode() ? 2 : 0), 45, gFont_7, "%d", cost);
796
    } else if (cost < 0) {
797
        TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 201, gFont_7, GetMiscString(kMiscString_RefundColon));
798
        BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_net_y - (TranslationMode() ? 2 : 0), 201, gFont_7, "%d", -cost);
799
    } else if (gJust_bought_part) {
800
        TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 134, gFont_7, GetMiscString(kMiscString_YouGotIt));
801
    } else {
802
        TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 134, gFont_7, GetMiscString(kMiscString_YoursAlready));
803
    }
804
    TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_total_x, gCurrent_graf_data->parts_total_y, 2, gFont_7, GetMiscString(kMiscString_CreditsColon));
805
    BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_total_y - (TranslationMode() ? 2 : 0), 2, gFont_7, "%d", gProgram_state.credits);
806
}
807
 
808
// IDA: void __cdecl SetPartsImage()
809
void SetPartsImage(void) {
810
    LOG_TRACE("()");
811
 
812
    ChangePanelFlic(0,
813
        gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_ptr,
814
        gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_length);
815
    TellyInImage(GetPanelPixelmap(0), gCurrent_graf_data->parts_image_x, gCurrent_graf_data->parts_image_y);
816
    DrawPartsText();
817
}
818
 
819
// IDA: int __cdecl GetPartsMax()
820
int GetPartsMax(void) {
821
    int i;
822
    LOG_TRACE("()");
823
 
824
    for (i = gProgram_state.current_car.power_ups[gPart_category].number_of_parts - 1; i >= 0; i--) {
825
        if (gProgram_state.rank <= gProgram_state.current_car.power_ups[gPart_category].info[i].rank_required) {
826
            return i;
827
        }
828
        if (gProgram_state.game_completed) {
829
            return i;
830
        }
831
    }
832
    return 0;
833
}
834
 
835
// IDA: void __cdecl CalcPartsIndex()
836
void CalcPartsIndex(void) {
837
    //int current_index; // Pierre-Marie Baty -- unused variable
838
    LOG_TRACE("()");
839
 
840
    gPart_index = gProgram_state.current_car.power_up_levels[gPart_category];
841
    if (gPart_index + 1 < gProgram_state.current_car.power_ups[gPart_category].number_of_parts && (gProgram_state.rank <= gProgram_state.current_car.power_ups[gPart_category].info[gPart_index + 1].rank_required || gProgram_state.game_completed)) {
842
        gPart_index += 1;
843
    }
844
}
845
 
846
// IDA: void __cdecl DoExchangePart()
847
void DoExchangePart(void) {
848
    int price;
849
    int cost;
850
    LOG_TRACE("()");
851
 
852
    CalcPartPrice(gPart_category, gPart_index, &price, &cost);
853
    if (cost == 0 || gProgram_state.credits < cost) {
854
        DRS3StartSound(gEffects_outlet, 3100);
855
    } else {
856
        gJust_bought_part = 1;
857
        DRS3StartSound(gEffects_outlet, 3101);
858
        BuyPart(gPart_category, gPart_index);
859
        ErasePartsText(1);
860
        DrawPartsText();
861
    }
862
}
863
 
864
// IDA: int __usercall PartsShopGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
865
int PartsShopGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
866
    //int flic_index; // Pierre-Marie Baty -- unused variable
867
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
868
 
869
    if (*pCurrent_choice < 3 && *pCurrent_mode == 0) {
870
        RemoveTransientBitmaps(1);
871
        DontLetFlicFuckWithPalettes();
872
        TurnFlicTransparencyOn();
873
        RunFlicAt(
874
            gStart_interface_spec->pushed_flics[*pCurrent_choice].flic_index,
875
            gStart_interface_spec->pushed_flics[*pCurrent_choice].x[gGraf_data_index],
876
            gStart_interface_spec->pushed_flics[*pCurrent_choice].y[gGraf_data_index]);
877
        TurnFlicTransparencyOff();
878
        LetFlicFuckWithPalettes();
879
        gJust_bought_part = 0;
880
        ErasePartsText(1);
881
        TellyOutImage(GetPanelPixelmap(0), gCurrent_graf_data->parts_image_x, gCurrent_graf_data->parts_image_y);
882
        gPart_category = *pCurrent_choice;
883
        CalcPartsIndex();
884
        RunFlic(261);
885
        AddToFlicQueue(
886
            gStart_interface_spec->flicker_on_flics[*pCurrent_choice].flic_index,
887
            gStart_interface_spec->flicker_on_flics[*pCurrent_choice].x[gGraf_data_index],
888
            gStart_interface_spec->flicker_on_flics[*pCurrent_choice].y[gGraf_data_index],
889
            1);
890
        DrawPartsLabel();
891
        SetPartsImage();
892
        ProcessFlicQueue(gFrame_period);
893
        DoMouseCursor();
894
        PDScreenBufferSwap(0);
895
        return 0;
896
    } else if (*pCurrent_mode == 1) {
897
        AddToFlicQueue(
898
            gStart_interface_spec->flicker_on_flics[4].flic_index,
899
            gStart_interface_spec->flicker_on_flics[4].x[gGraf_data_index],
900
            gStart_interface_spec->flicker_on_flics[4].y[gGraf_data_index],
901
            1);
902
        DoExchangePart();
903
        return 0;
904
    } else {
905
        return 1;
906
    }
907
}
908
 
909
// IDA: int __usercall UpPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
910
int UpPart(int* pCurrent_choice, int* pCurrent_mode) {
911
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
912
 
913
    gJust_bought_part = 0;
914
    AddToFlicQueue(
915
        gStart_interface_spec->pushed_flics[5].flic_index,
916
        gStart_interface_spec->pushed_flics[5].x[gGraf_data_index],
917
        gStart_interface_spec->pushed_flics[5].y[gGraf_data_index],
918
        1);
919
    DRS3StartSound(gEffects_outlet, 3000);
920
    RemoveTransientBitmaps(1);
921
    ErasePartsText(0);
922
 
923
    gPart_index = (gPart_index == 0) ? GetPartsMax() : (gPart_index - 1);
924
 
925
    DropOutImageThruBottom(
926
        GetPanelPixelmap(0),
927
        gCurrent_graf_data->parts_image_x,
928
        gCurrent_graf_data->parts_image_y,
929
        gCurrent_graf_data->parts_top_clip,
930
        gCurrent_graf_data->parts_bottom_clip);
931
    ChangePanelFlic(0,
932
        gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_ptr,
933
        gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_length);
934
    DropInImageFromTop(
935
        GetPanelPixelmap(0),
936
        gCurrent_graf_data->parts_image_x,
937
        gCurrent_graf_data->parts_image_y,
938
        gCurrent_graf_data->parts_top_clip,
939
        gCurrent_graf_data->parts_bottom_clip);
940
    DrawPartsText();
941
    return 0;
942
}
943
 
944
// IDA: int __usercall DownPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
945
int DownPart(int* pCurrent_choice, int* pCurrent_mode) {
946
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
947
 
948
    gJust_bought_part = 0;
949
    AddToFlicQueue(
950
        gStart_interface_spec->pushed_flics[6].flic_index,
951
        gStart_interface_spec->pushed_flics[6].x[gGraf_data_index],
952
        gStart_interface_spec->pushed_flics[6].y[gGraf_data_index],
953
        1);
954
    DRS3StartSound(gEffects_outlet, 3000);
955
    RemoveTransientBitmaps(1);
956
    ErasePartsText(0);
957
 
958
    gPart_index = (gPart_index == GetPartsMax()) ? 0 : (gPart_index + 1);
959
 
960
    DropOutImageThruTop(
961
        GetPanelPixelmap(0),
962
        gCurrent_graf_data->parts_image_x,
963
        gCurrent_graf_data->parts_image_y,
964
        gCurrent_graf_data->parts_top_clip,
965
        gCurrent_graf_data->parts_bottom_clip);
966
    ChangePanelFlic(0,
967
        gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_ptr,
968
        gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_length);
969
    DropInImageFromBottom(
970
        GetPanelPixelmap(0),
971
        gCurrent_graf_data->parts_image_x,
972
        gCurrent_graf_data->parts_image_y,
973
        gCurrent_graf_data->parts_top_clip,
974
        gCurrent_graf_data->parts_bottom_clip);
975
    DrawPartsText();
976
    return 0;
977
}
978
 
979
// IDA: int __usercall UpClickPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
980
int UpClickPart(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
981
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
982
 
983
    UpPart(pCurrent_choice, pCurrent_mode);
984
    return 0;
985
}
986
 
987
// IDA: int __usercall DownClickPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
988
int DownClickPart(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
989
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
990
 
991
    DownPart(pCurrent_choice, pCurrent_mode);
992
    return 0;
993
}
994
 
995
// IDA: int __usercall PartsArrowsOn@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
996
int PartsArrowsOn(int* pCurrent_choice, int* pCurrent_mode) {
997
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
998
 
999
    AddToFlicQueue(
1000
        gStart_interface_spec->flicker_on_flics[5].flic_index,
1001
        gStart_interface_spec->flicker_on_flics[5].x[gGraf_data_index],
1002
        gStart_interface_spec->flicker_on_flics[5].y[gGraf_data_index],
1003
        1);
1004
    AddToFlicQueue(gStart_interface_spec->flicker_on_flics[6].flic_index,
1005
        gStart_interface_spec->flicker_on_flics[6].x[gGraf_data_index],
1006
        gStart_interface_spec->flicker_on_flics[6].y[gGraf_data_index],
1007
        1);
1008
    return 0;
1009
}
1010
 
1011
// IDA: int __usercall PartsArrowsOff@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
1012
int PartsArrowsOff(int* pCurrent_choice, int* pCurrent_mode) {
1013
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
1014
 
1015
    AddToFlicQueue(gStart_interface_spec->flicker_off_flics[5].flic_index,
1016
        gStart_interface_spec->flicker_off_flics[5].x[gGraf_data_index],
1017
        gStart_interface_spec->flicker_off_flics[5].y[gGraf_data_index],
1018
        1);
1019
    AddToFlicQueue(gStart_interface_spec->flicker_off_flics[6].flic_index,
1020
        gStart_interface_spec->flicker_off_flics[6].x[gGraf_data_index],
1021
        gStart_interface_spec->flicker_off_flics[6].y[gGraf_data_index],
1022
        1);
1023
    return 0;
1024
}
1025
 
1026
// IDA: void __cdecl StartPartsShop()
1027
void StartPartsShop(void) {
1028
    LOG_TRACE("()");
1029
 
1030
    DrawPartsLabel();
1031
    SetPartsImage();
1032
}
1033
 
1034
// IDA: int __usercall DonePartsShop@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
1035
int DonePartsShop(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
1036
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
1037
 
1038
    if (gFade_away_parts_shop) {
1039
        FadePaletteDown();
1040
    } else {
1041
        RunFlic(251);
1042
    }
1043
    return pGo_ahead;
1044
}
1045
 
1046
// IDA: void __usercall DrawPartsShop(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
1047
void DrawPartsShop(int pCurrent_choice, int pCurrent_mode) {
1048
    LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
1049
 
1050
    DrawPartsText();
1051
}
1052
 
1053
// IDA: void __usercall DoPartsShop(int pFade_away@<EAX>)
1054
void DoPartsShop(int pFade_away) {
1055
    static tFlicette flicker_on[7] = {
1056
        { 43, { 225, 450 }, { 30, 72 } },
1057
        { 43, { 225, 450 }, { 60, 144 } },
1058
        { 43, { 225, 450 }, { 89, 214 } },
1059
        { 43, { 225, 450 }, { 152, 365 } },
1060
        { 43, { 85, 170 }, { 152, 365 } },
1061
        { 221, { 30, 60 }, { 79, 190 } },
1062
        { 221, { 30, 60 }, { 79, 190 } },
1063
    };
1064
    static tFlicette flicker_off[7] = {
1065
        { 42, { 225, 450 }, { 30, 72 } },
1066
        { 42, { 225, 450 }, { 60, 144 } },
1067
        { 42, { 225, 450 }, { 89, 214 } },
1068
        { 42, { 225, 450 }, { 152, 365 } },
1069
        { 42, { 85, 170 }, { 152, 365 } },
1070
        { 220, { 30, 60 }, { 79, 190 } },
1071
        { 220, { 30, 60 }, { 79, 190 } },
1072
    };
1073
    static tFlicette push[7] = {
1074
        { 254, { 225, 450 }, { 30, 72 } },
1075
        { 255, { 225, 450 }, { 60, 144 } },
1076
        { 256, { 225, 450 }, { 89, 214 } },
1077
        { 154, { 225, 450 }, { 152, 365 } },
1078
        { 260, { 85, 170 }, { 152, 365 } },
1079
        { 222, { 30, 60 }, { 79, 190 } },
1080
        { 225, { 30, 60 }, { 120, 288 } },
1081
    };
1082
    static tMouse_area mouse_areas[7] = {
1083
        { { 225, 450 }, { 30, 72 }, { 288, 576 }, { 50, 120 }, 0, 0, 0, NULL },
1084
        { { 225, 450 }, { 60, 144 }, { 288, 576 }, { 80, 192 }, 1, 0, 0, NULL },
1085
        { { 225, 450 }, { 89, 214 }, { 288, 576 }, { 109, 262 }, 2, 0, 0, NULL },
1086
        { { 225, 450 }, { 152, 365 }, { 288, 576 }, { 172, 413 }, 3, 0, 0, NULL },
1087
        { { 85, 170 }, { 152, 365 }, { 148, 296 }, { 172, 413 }, 4, 1, 0, NULL },
1088
        { { 30, 60 }, { 79, 190 }, { 45, 90 }, { 106, 254 }, -1, 1, 0, UpClickPart },
1089
        { { 30, 60 }, { 120, 288 }, { 45, 90 }, { 147, 353 }, -1, 1, 0, DownClickPart },
1090
    };
1091
    static tInterface_spec interface_spec = {
1092
        0, 250, 190, 0, 0, 0, 6,
1093
        { 1, 0 }, { 4, -1 }, { 4, 0 }, { 4, 3 }, { PartsArrowsOn, PartsArrowsOff },
1094
        { 1, 0 }, { 4, -1 }, { 4, 0 }, { 4, 3 }, { PartsArrowsOn, PartsArrowsOff },
1095
        { -1, -1 }, { -1, 0 }, { 0, 4 }, { 3, 4 }, { NULL, UpPart },
1096
        { -1, -1 }, { 1, 0 }, { 0, 4 }, { 3, 4 }, { NULL, DownPart },
1097
        { 1, 1 }, { PartsShopGoAhead, PartsShopGoAhead },
1098
        { 1, 1 }, { NULL, NULL },
1099
        NULL, DrawPartsShop, 0, NULL, StartPartsShop, DonePartsShop,
1100
        0, { 0, 0 }, NULL, 3, 1,
1101
        COUNT_OF(flicker_on),
1102
        flicker_on,
1103
        flicker_off,
1104
        push,
1105
        COUNT_OF(mouse_areas),
1106
        mouse_areas,
1107
        0,
1108
        NULL
1109
    };
1110
    //int result; // Pierre-Marie Baty -- unused variable
1111
    LOG_TRACE("(%d)", pFade_away);
1112
 
1113
    LoadParts();
1114
    gFade_away_parts_shop = pFade_away;
1115
    InitialiseFlicPanel(0,
1116
        gCurrent_graf_data->parts_image_x,
1117
        gCurrent_graf_data->parts_image_y,
1118
        gCurrent_graf_data->parts_image_width,
1119
        gCurrent_graf_data->parts_image_height);
1120
    gStart_interface_spec = &interface_spec;
1121
    gPart_category = eParts_armour;
1122
    gJust_bought_part = 0;
1123
    gRefund_rate = 75;
1124
    CalcPartsIndex();
1125
    DoInterfaceScreen(&interface_spec, gFaded_palette, 3);
1126
    DisposeFlicPanel(0);
1127
    UnlockParts();
1128
    gProgram_state.parts_shop_visited = 1;
1129
}
1130
 
1131
// IDA: int __usercall AutoPartsDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
1132
int AutoPartsDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
1133
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
1134
 
1135
    if (pEscaped) {
1136
        pCurrent_choice = -1;
1137
    }
1138
    return pCurrent_choice;
1139
}
1140
 
1141
// IDA: tSO_result __cdecl DoAutoPartsShop()
1142
tSO_result DoAutoPartsShop(void) {
1143
    static tFlicette flicker_on[3] = {
1144
        { 43, { 84, 168 }, { 67, 161 } },
1145
        { 43, { 84, 168 }, { 95, 228 } },
1146
        { 43, { 84, 168 }, { 124, 298 } },
1147
    };
1148
    static tFlicette flicker_off[3] = {
1149
        { 42, { 84, 168 }, { 67, 161 } },
1150
        { 42, { 84, 168 }, { 95, 228 } },
1151
        { 42, { 84, 168 }, { 124, 298 } },
1152
    };
1153
    static tFlicette push[3] = {
1154
        { 284, { 84, 168 }, { 67, 161 } },
1155
        { 284, { 84, 168 }, { 95, 228 } },
1156
        { 284, { 84, 168 }, { 124, 298 } },
1157
    };
1158
    static tMouse_area mouse_areas[3] = {
1159
        { { 84, 168 }, { 32, 77 }, { 147, 294 }, { 87, 209 }, 0, 0, 0, NULL },
1160
        { { 84, 168 }, { 95, 228 }, { 147, 294 }, { 115, 276 }, 1, 0, 0, NULL },
1161
        { { 84, 168 }, { 124, 298 }, { 147, 294 }, { 144, 346 }, 2, 0, 0, NULL },
1162
    };
1163
    static tInterface_spec interface_spec = {
1164
        0, 280, 0, 0, 0, 0, 6,
1165
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
1166
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
1167
        { -1, -1 }, { -1, 0 }, { 0, 0 }, { 2, 0 }, { NULL, NULL },
1168
        { -1, -1 }, { 1, 0 }, { 0, 0 }, { 2, 0 }, { NULL, NULL },
1169
        { 1, 1 }, { NULL, NULL },
1170
        { 1, 1 }, { NULL, NULL },
1171
        NULL, NULL, 0, NULL, NULL, AutoPartsDone, 0,
1172
        { 0, 0 }, NULL, -1, 1,
1173
        COUNT_OF(flicker_on), flicker_on, flicker_off, push,
1174
        COUNT_OF(mouse_areas), mouse_areas,
1175
        0, NULL
1176
    };
1177
    int result;
1178
    LOG_TRACE("()");
1179
 
1180
    gProgram_state.dont_load = 1;
1181
    result = DoInterfaceScreen(&interface_spec, 0, gProgram_state.auto_parts_reply);
1182
    gProgram_state.dont_load = 0;
1183
    if (result < 0) {
1184
        RunFlic(281);
1185
        return eSO_main_menu_invoked;
1186
    } else {
1187
        gProgram_state.auto_parts_reply = result;
1188
        switch (result) {
1189
        case eAP_auto:
1190
            DoAutoParts();
1191
            break;
1192
        case eAP_manual:
1193
            RunFlic(281);
1194
            DoPartsShop(1);
1195
            break;
1196
        }
1197
        FadePaletteDown();
1198
        return eSO_continue;
1199
    }
1200
}
1201
 
1202
// IDA: void __cdecl SetOpponentFlic()
1203
void SetOpponentFlic(void) {
1204
    LOG_TRACE("()");
1205
    ChangePanelFlic(0,
1206
        gOpponents[gCurrent_race.opponent_list[gOpponent_index].index].mug_shot_image_data,
1207
        gOpponents[gCurrent_race.opponent_list[gOpponent_index].index].mug_shot_image_data_length);
1208
}
1209
 
1210
// IDA: void __cdecl DrawSceneyMappyInfoVieweyThing()
1211
void DrawSceneyMappyInfoVieweyThing(void) {
1212
    LOG_TRACE("()");
1213
 
1214
    RemoveTransientBitmaps(1);
1215
    if (gProgram_state.view_type) {
1216
        if (gProgram_state.view_type == eVT_Info) {
1217
            ChangePanelFlic(0, gCurrent_race.info_image_data, gCurrent_race.info_image_data_length);
1218
        } else if (gProgram_state.view_type == eVT_Opponents) {
1219
            SetOpponentFlic();
1220
        }
1221
    } else {
1222
        ChangePanelFlic(0, gCurrent_race.scene_image_data, gCurrent_race.scene_image_data_length);
1223
    }
1224
    TellyInImage(GetPanelPixelmap(0), gCurrent_graf_data->start_race_panel_left, gCurrent_graf_data->start_race_panel_top);
1225
}
1226
 
1227
// IDA: void __cdecl DismissSceneyMappyInfoVieweyThing()
1228
void DismissSceneyMappyInfoVieweyThing(void) {
1229
    LOG_TRACE("()");
1230
 
1231
    RemoveTransientBitmaps(1);
1232
    TellyOutImage(GetPanelPixelmap(0), gCurrent_graf_data->start_race_panel_left, gCurrent_graf_data->start_race_panel_top);
1233
}
1234
 
1235
// IDA: int __usercall SelectRaceDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
1236
int SelectRaceDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
1237
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
1238
 
1239
    DismissSceneyMappyInfoVieweyThing();
1240
    if (pEscaped) {
1241
        return -1;
1242
    }
1243
    return pCurrent_choice;
1244
}
1245
 
1246
// IDA: int __usercall StartRaceGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
1247
int StartRaceGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
1248
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
1249
 
1250
    if (*pCurrent_choice != 1 || *pCurrent_mode) {
1251
        return 1;
1252
    }
1253
    RemoveTransientBitmaps(1);
1254
    DismissSceneyMappyInfoVieweyThing();
1255
    gProgram_state.view_type++;
1256
    if (gProgram_state.view_type > eVT_Opponents) {
1257
        gProgram_state.view_type = eVT_Scene;
1258
    }
1259
    if (gProgram_state.view_type) {
1260
        if (gProgram_state.view_type == eVT_Info) {
1261
            RunFlic(210);
1262
        } else if (gProgram_state.view_type == eVT_Opponents) {
1263
            RunFlic(212);
1264
        }
1265
    } else {
1266
        RunFlic(213);
1267
    }
1268
    DrawSceneyMappyInfoVieweyThing();
1269
    return 0;
1270
}
1271
 
1272
// IDA: int __usercall TryToMoveToArrows@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
1273
int TryToMoveToArrows(int* pCurrent_choice, int* pCurrent_mode) {
1274
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
1275
 
1276
    if (gProgram_state.view_type != eVT_Opponents) {
1277
        return 0;
1278
    }
1279
    *pCurrent_choice = 5;
1280
    *pCurrent_mode = 1;
1281
    DRS3StartSound(gEffects_outlet, 3000);
1282
    return 1;
1283
}
1284
 
1285
// IDA: int __usercall UpOpponent@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
1286
int UpOpponent(int* pCurrent_choice, int* pCurrent_mode) {
1287
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
1288
 
1289
#if defined(DETHRACE_FIX_BUGS)
1290
    // fixes bug where racers could be scrolled in other race menu modes
1291
    if (gProgram_state.view_type != eVT_Opponents) {
1292
        return 0;
1293
    }
1294
#endif
1295
    AddToFlicQueue(gStart_interface_spec->pushed_flics[5].flic_index,
1296
        gStart_interface_spec->pushed_flics[5].x[gGraf_data_index],
1297
        gStart_interface_spec->pushed_flics[5].y[gGraf_data_index],
1298
        1);
1299
    DRS3StartSound(gEffects_outlet, 3000);
1300
    RemoveTransientBitmaps(1);
1301
    DropOutImageThruBottom(GetPanelPixelmap(0),
1302
        gCurrent_graf_data->start_race_panel_left,
1303
        gCurrent_graf_data->start_race_panel_top,
1304
        gCurrent_graf_data->start_race_panel_top_clip,
1305
        gCurrent_graf_data->start_race_panel_bottom_clip);
1306
    if (gOpponent_index == 0) {
1307
        gOpponent_index = gCurrent_race.number_of_racers;
1308
    }
1309
    gOpponent_index--;
1310
    SetOpponentFlic();
1311
    DropInImageFromTop(GetPanelPixelmap(0),
1312
        gCurrent_graf_data->start_race_panel_left,
1313
        gCurrent_graf_data->start_race_panel_top,
1314
        gCurrent_graf_data->start_race_panel_top_clip,
1315
        gCurrent_graf_data->start_race_panel_bottom_clip);
1316
    return 0;
1317
}
1318
 
1319
// IDA: int __usercall DownOpponent@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
1320
int DownOpponent(int* pCurrent_choice, int* pCurrent_mode) {
1321
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
1322
 
1323
#if defined(DETHRACE_FIX_BUGS)
1324
    // fixes bug where racers could be scrolled in other race menu modes
1325
    if (gProgram_state.view_type != eVT_Opponents) {
1326
        return 0;
1327
    }
1328
#endif
1329
    AddToFlicQueue(gStart_interface_spec->pushed_flics[6].flic_index,
1330
        gStart_interface_spec->pushed_flics[6].x[gGraf_data_index],
1331
        gStart_interface_spec->pushed_flics[6].y[gGraf_data_index],
1332
        1);
1333
    DRS3StartSound(gEffects_outlet, 3000);
1334
    RemoveTransientBitmaps(1);
1335
    DropOutImageThruTop(GetPanelPixelmap(0),
1336
        gCurrent_graf_data->start_race_panel_left,
1337
        gCurrent_graf_data->start_race_panel_top,
1338
        gCurrent_graf_data->start_race_panel_top_clip,
1339
        gCurrent_graf_data->start_race_panel_bottom_clip);
1340
    gOpponent_index++;
1341
    if (gOpponent_index == gCurrent_race.number_of_racers) {
1342
        gOpponent_index = 0;
1343
    }
1344
    SetOpponentFlic();
1345
    DropInImageFromBottom(GetPanelPixelmap(0),
1346
        gCurrent_graf_data->start_race_panel_left,
1347
        gCurrent_graf_data->start_race_panel_top,
1348
        gCurrent_graf_data->start_race_panel_top_clip,
1349
        gCurrent_graf_data->start_race_panel_bottom_clip);
1350
    return 0;
1351
}
1352
 
1353
// IDA: int __usercall UpClickOpp@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
1354
int UpClickOpp(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
1355
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
1356
 
1357
    UpOpponent(pCurrent_choice, pCurrent_mode);
1358
    return 0;
1359
}
1360
 
1361
// IDA: int __usercall DownClickOpp@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
1362
int DownClickOpp(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
1363
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
1364
 
1365
    DownOpponent(pCurrent_choice, pCurrent_mode);
1366
    return 0;
1367
}
1368
 
1369
// IDA: void __cdecl SelectRaceStart()
1370
void SelectRaceStart(void) {
1371
    LOG_TRACE("()");
1372
 
1373
    DrawSceneyMappyInfoVieweyThing();
1374
    PrintMemoryDump(0, "INSIDE START RACE");
1375
}
1376
 
1377
// IDA: int __cdecl SuggestRace()
1378
int SuggestRace(void) {
1379
    int i;
1380
    int least_done;
1381
    int suggested_so_far;
1382
    int suggested_race;
1383
    int new_suggestion;
1384
    int number_of_visits;
1385
    LOG_TRACE("()");
1386
 
1387
    suggested_so_far = 32767;
1388
    suggested_race = 0;
1389
    if (gProgram_state.game_completed) {
1390
        return IRandomBetween(0, gNumber_of_races - 1);
1391
    }
1392
    if (gProgram_state.redo_race_index >= 0) {
1393
        return gProgram_state.redo_race_index;
1394
    }
1395
    for (i = 0; i < gNumber_of_races; i++) {
1396
        if (gRace_list[i].suggested_rank <= suggested_so_far && gRace_list[i].suggested_rank >= gProgram_state.rank) {
1397
            suggested_so_far = gRace_list[i].suggested_rank;
1398
            suggested_race = i;
1399
        }
1400
    }
1401
    number_of_visits = gRace_list[suggested_race].been_there_done_that;
1402
    new_suggestion = suggested_race;
1403
 
1404
    if (number_of_visits) {
1405
        // Jeff: if we have already completed the suggested race, look backwards for a race that we haven't completed as many times
1406
        for (i = suggested_race - 1; i >= 0 && i >= suggested_race - 5; i--) {
1407
            if (gRace_list[i].rank_required < gProgram_state.rank || gRace_list[i].best_rank > gProgram_state.rank) {
1408
                continue;
1409
            }
1410
            least_done = gRace_list[i].been_there_done_that < number_of_visits;
1411
            if (!gRace_list[i].been_there_done_that
1412
                || (least_done && suggested_race - 3 <= i)) {
1413
                new_suggestion = i;
1414
                number_of_visits = gRace_list[i].been_there_done_that;
1415
            }
1416
        }
1417
        // Jeff: look forwards for a race that we haven't completed as many times as the previous suggestion
1418
        for (i = suggested_race + 1; i < gNumber_of_races; i++) {
1419
            least_done = gRace_list[i].been_there_done_that < number_of_visits;
1420
            if (!least_done) {
1421
                continue;
1422
            }
1423
 
1424
            if ((gRace_list[i].rank_required >= gProgram_state.rank && gRace_list[i].best_rank <= gProgram_state.rank)
1425
                || gProgram_state.game_completed) {
1426
                new_suggestion = i;
1427
                number_of_visits = gRace_list[i].been_there_done_that;
1428
            }
1429
        }
1430
    }
1431
    return new_suggestion;
1432
}
1433
 
1434
// IDA: void __usercall SelectRaceDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
1435
void SelectRaceDraw(int pCurrent_choice, int pCurrent_mode) {
1436
    tOpponent* the_opponent;
1437
    tText_chunk* the_chunk;
1438
    int j;
1439
    int k;
1440
    int y_coord;
1441
    //char s[256]; // Pierre-Marie Baty -- unused variable
1442
    //char temp_str[256]; // Pierre-Marie Baty -- unused variable
1443
    //char* sub_pt; // Pierre-Marie Baty -- unused variable
1444
    //char sub_str[16]; // Pierre-Marie Baty -- unused variable
1445
    tU32* test;
1446
    static tU32 test2;
1447
    LOG_TRACE8("(%d, %d)", pCurrent_choice, pCurrent_mode);
1448
 
1449
    if (gProgram_state.view_type == eVT_Opponents) {
1450
        the_opponent = &gOpponents[gCurrent_race.opponent_list[gOpponent_index].index];
1451
        for (j = 0; j < the_opponent->text_chunk_count; j++) {
1452
            the_chunk = &the_opponent->text_chunks[j];
1453
            if (GetPanelFlicFrameIndex(0) >= the_chunk->frame_cue && GetPanelFlicFrameIndex(0) < the_chunk->frame_end) {
1454
                y_coord = the_chunk->y_coord * gGraf_specs[gGraf_spec_index].total_height / 200
1455
                    + gCurrent_graf_data->start_race_panel_top;
1456
                for (k = 0; k < the_chunk->line_count; k++) {
1457
                    TransBrPixelmapText(
1458
                        gBack_screen,
1459
                        the_chunk->x_coord * gGraf_specs[gGraf_spec_index].total_width / 320
1460
                            + gCurrent_graf_data->start_race_panel_left,
1461
                        y_coord,
1462
                        201,
1463
                        gFont_7,
1464
                        the_chunk->text[k]);
1465
                    y_coord += gFont_7->glyph_y + gFont_7->glyph_y / 2;
1466
                }
1467
            }
1468
        }
1469
    } else if (gProgram_state.view_type == eVT_Info) {
1470
        for (j = 0; j < gCurrent_race.text_chunk_count; j++) {
1471
            the_chunk = &gCurrent_race.text_chunks[j];
1472
            if (GetPanelFlicFrameIndex(0) >= the_chunk->frame_cue && GetPanelFlicFrameIndex(0) < the_chunk->frame_end) {
1473
                y_coord = the_chunk->y_coord * gGraf_specs[gGraf_spec_index].total_height / 200
1474
                    + gCurrent_graf_data->start_race_panel_top;
1475
                for (k = 0; k < the_chunk->line_count; k++) {
1476
                    TransBrPixelmapText(
1477
                        gBack_screen,
1478
                        the_chunk->x_coord * gGraf_specs[gGraf_spec_index].total_width / 320
1479
                            + gCurrent_graf_data->start_race_panel_left,
1480
                        y_coord,
1481
                        201,
1482
                        gFont_7,
1483
                        the_chunk->text[k]);
1484
                    y_coord += gFont_7->glyph_y + gFont_7->glyph_y / 2;
1485
                }
1486
            }
1487
        }
1488
    }
1489
    test = KevKeyService();
1490
    if (*test) {
1491
        test2 = *test;
1492
    }
1493
    if (test[0] == 0x27645433 && test[1] == 0x758f0015) {
1494
        // cheat code: "KEVWOZEAR"
1495
        gProgram_state.game_completed = 1;
1496
        DRS3StartSound(gEffects_outlet, 3202);
1497
        DRS3StartSound(gEffects_outlet, 3202);
1498
    }
1499
    if (test[0] == 0x33f75455 && test[1] == 0xC10AAAF2) {
1500
        // cheat code: "IWANTTOFIDDLE"
1501
 
1502
        char s[128];
1503
        FILE* f;
1504
        size_t i; // Pierre-Marie Baty -- fixed type
1505
 
1506
        // Jeff
1507
        assert(strlen(gApplication_path) < 120);
1508
 
1509
        PathCat(s, gApplication_path, "ACTORS");
1510
        PathCat(s, s, "PROG.ACT");
1511
        PDFileUnlock(s);
1512
        f = fopen(s, "wb");
1513
        if (f != NULL) {
1514
            DRS3StartSound(gEffects_outlet, 9000);
1515
            if (gDecode_thing) {
1516
                for (i = 0; i < strlen(gDecode_string); i++) {
1517
                    gDecode_string[i] -= 50;
1518
                }
1519
                fputs(gDecode_string, f);
1520
                for (i = 0; i < strlen(gDecode_string); i++) {
1521
                    gDecode_string[i] += 50;
1522
                }
1523
            } else {
1524
                for (i = 0; i < 20; i++) {
1525
                    fputs("*************", f);
1526
                }
1527
            }
1528
            gDecode_thing ^= 0x40u;
1529
            fclose(f);
1530
            EncodeAllFilesInDirectory("");
1531
            EncodeAllFilesInDirectory("CARS");
1532
            EncodeAllFilesInDirectory("NONCARS");
1533
            EncodeAllFilesInDirectory("RACES");
1534
            EncodeAllFilesInDirectory("32X20X8");
1535
            EncodeAllFilesInDirectory("64X48X8");
1536
            PathCat(s, "32X20X8", "CARS");
1537
            EncodeAllFilesInDirectory(s);
1538
            PathCat(s, "64X48X8", "CARS");
1539
            EncodeAllFilesInDirectory(s);
1540
            PathCat(s, "32X20X8", "FONTS");
1541
            EncodeAllFilesInDirectory(s);
1542
            PathCat(s, "64X48X8", "FONTS");
1543
            EncodeAllFilesInDirectory(s);
1544
        }
1545
        DRS3StartSound(gEffects_outlet, 9000);
1546
    }
1547
}
1548
 
1549
// IDA: tSO_result __usercall DoSelectRace@<EAX>(int *pSecond_time_around@<EAX>)
1550
tSO_result DoSelectRace(int* pSecond_time_around) {
1551
    static tFlicette flicker_on[7] = {
1552
        { 43, { 224, 448 }, { 28, 67 } },
1553
        { 43, { 224, 448 }, { 56, 134 } },
1554
        { 43, { 224, 448 }, { 85, 204 } },
1555
        { 43, { 224, 448 }, { 113, 271 } },
1556
        { 43, { 224, 448 }, { 147, 353 } },
1557
        { 221, { 30, 60 }, { 79, 190 } },
1558
        { 221, { 30, 60 }, { 79, 190 } }
1559
    };
1560
 
1561
    static tFlicette flicker_off[7] = {
1562
        { 42, { 224, 448 }, { 28, 67 } },
1563
        { 42, { 224, 448 }, { 56, 134 } },
1564
        { 42, { 224, 448 }, { 85, 204 } },
1565
        { 42, { 224, 448 }, { 113, 271 } },
1566
        { 42, { 224, 448 }, { 147, 353 } },
1567
        { 220, { 30, 60 }, { 79, 190 } },
1568
        { 220, { 30, 60 }, { 79, 190 } }
1569
    };
1570
 
1571
    static tFlicette push[7] = {
1572
        { 195, { 224, 448 }, { 28, 67 } },
1573
        { -1, { 224, 448 }, { 56, 134 } },
1574
        { 200, { 224, 448 }, { 85, 204 } },
1575
        { 202, { 224, 448 }, { 113, 271 } },
1576
        { 201, { 224, 448 }, { 147, 353 } },
1577
        { 222, { 30, 60 }, { 79, 190 } },
1578
        { 225, { 30, 60 }, { 119, 286 } }
1579
    };
1580
 
1581
    static tMouse_area mouse_areas[7] = {
1582
        { { 224, 448 }, { 28, 67 }, { 287, 574 }, { 48, 115 }, 0, 0, 0, NULL },
1583
        { { 224, 448 }, { 56, 134 }, { 287, 574 }, { 76, 182 }, 1, 0, 0, NULL },
1584
        { { 224, 448 }, { 85, 204 }, { 287, 574 }, { 105, 252 }, 2, 0, 0, NULL },
1585
        { { 224, 448 }, { 113, 271 }, { 287, 574 }, { 133, 319 }, 3, 0, 0, NULL },
1586
        { { 224, 448 }, { 147, 353 }, { 287, 574 }, { 167, 401 }, 4, 0, 0, NULL },
1587
        { { 30, 60 },
1588
            { 79, 190 },
1589
            { 45, 90 },
1590
            { 106, 254 },
1591
            -1,
1592
            0,
1593
            0,
1594
            &UpClickOpp },
1595
        { { 30, 60 },
1596
            { 119, 286 },
1597
            { 45, 90 },
1598
            { 146, 350 },
1599
            -1,
1600
            0,
1601
            0,
1602
            &DownClickOpp }
1603
    };
1604
 
1605
    static tInterface_spec interface_spec = {
1606
        0,                            // initial_imode
1607
        191,                          // first_opening_flic
1608
        190,                          // second_opening_flic
1609
        0,                            // end_flic_go_ahead
1610
        0,                            // end_flic_escaped
1611
        0,                            // end_flic_otherwise
1612
        6,                            // flic_bunch_to_load
1613
        { -1, 0 },                    // move_left_new_mode
1614
        { 0, -4 },                    // move_left_delta
1615
        { 0, 1 },                     // move_left_min
1616
        { 0, 1 },                     // move_left_max
1617
        { &TryToMoveToArrows, NULL }, // move_left_proc
1618
        { -1, 0 },                    // move_right_new_mode
1619
        { 0, -4 },                    // move_right_delta
1620
        { 0, 1 },                     // move_right_min
1621
        { 0, 1 },                     // move_right_max
1622
        { &TryToMoveToArrows, NULL }, // move_right_proc
1623
        { -1, -1 },                   // move_up_new_mode
1624
        { -1, 0 },                    // move_up_delta
1625
        { 0, 5 },                     // move_up_min
1626
        { 4, 5 },                     // move_up_max
1627
        { NULL, &UpOpponent },        // move_up_proc
1628
        { -1, -1 },                   // move_down_new_mode
1629
        { 1, 0 },                     // move_down_delta
1630
        { 0, 5 },                     // move_down_min
1631
        { 4, 5 },                     // move_down_max
1632
        { NULL, &DownOpponent },      // move_down_proc
1633
        { 1, 1 },                     // go_ahead_allowed
1634
        { &StartRaceGoAhead, NULL },  // go_ahead_proc
1635
        { 1, 1 },                     // escape_allowed
1636
        { NULL, NULL },               // escape_proc
1637
        NULL,                         // exit_proc
1638
        &SelectRaceDraw,              // draw_proc
1639
        0u,                           // time_out
1640
        NULL,                         // start_proc1
1641
        &SelectRaceStart,             // start_proc2
1642
        &SelectRaceDone,              // done_proc
1643
        0,                            // font_needed
1644
        { 0, 0 },                     // typeable
1645
        NULL,                         // get_original_string
1646
        -1,                           // escape_code
1647
        1,                            // dont_save_or_load
1648
        7,                            // number_of_button_flics
1649
        flicker_on,                   // flicker_on_flics
1650
        flicker_off,                  // flicker_off_flics
1651
        push,                         // pushed_flics
1652
        7,                            // number_of_mouse_areas
1653
        mouse_areas,                  // mouse_areas
1654
        0,                            // number_of_recopy_areas
1655
        NULL                          // recopy_areas
1656
    };
1657
 
1658
    int result;
1659
    //int default_choice; // Pierre-Marie Baty -- unused variable
1660
    int suggested;
1661
    int old_current_race;
1662
    LOG_TRACE("(%p)", pSecond_time_around);
1663
 
1664
    suggested = SuggestRace();
1665
    if (!*pSecond_time_around) {
1666
        gProgram_state.current_race_index = suggested;
1667
        SelectOpponents(&gCurrent_race);
1668
    }
1669
    gProgram_state.parts_shop_visited = 0;
1670
    gProgram_state.dont_load = 1;
1671
    gDeceased_image = LoadPixelmap("DECEASED.PIX");
1672
    InitialiseFlicPanel(
1673
        0,
1674
        gCurrent_graf_data->start_race_panel_left,
1675
        gCurrent_graf_data->start_race_panel_top,
1676
        gCurrent_graf_data->start_race_panel_right - gCurrent_graf_data->start_race_panel_left,
1677
        gCurrent_graf_data->start_race_panel_bottom - gCurrent_graf_data->start_race_panel_top);
1678
    LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
1679
    do {
1680
        gProgram_state.view_type = 0;
1681
        gOpponent_index = 0;
1682
        gStart_interface_spec = &interface_spec;
1683
        if (gFaded_palette || gCurrent_splash) {
1684
            result = DoInterfaceScreen(&interface_spec, 1, 4);
1685
        } else {
1686
            result = DoInterfaceScreen(&interface_spec, 0, 4);
1687
        }
1688
 
1689
        if (result == 0 || result == 2 || result == 3) {
1690
            DisposeFlicPanel(0);
1691
 
1692
            if (result == 2) {
1693
                if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
1694
                    DoFeatureUnavailableInDemo();
1695
                } else {
1696
                    RunFlic(192);
1697
                    DoPartsShop(0);
1698
                }
1699
            } else if (result == 3) {
1700
                RunFlic(192);
1701
                DoChangeCar();
1702
            } else if (result == 0) {
1703
                RunFlic(192);
1704
                old_current_race = gProgram_state.current_race_index;
1705
                DoChangeRace();
1706
                if (gProgram_state.current_race_index != old_current_race) {
1707
                    DisposeRaceInfo(&gCurrent_race);
1708
                    SelectOpponents(&gCurrent_race);
1709
                    LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
1710
                }
1711
            }
1712
            InitialiseFlicPanel(
1713
                0,
1714
                gCurrent_graf_data->start_race_panel_left,
1715
                gCurrent_graf_data->start_race_panel_top,
1716
                gCurrent_graf_data->start_race_panel_right - gCurrent_graf_data->start_race_panel_left,
1717
                gCurrent_graf_data->start_race_panel_bottom - gCurrent_graf_data->start_race_panel_top);
1718
        }
1719
    } while (result >= 0 && result != 4);
1720
    BrPixelmapFree(gDeceased_image);
1721
    FillInRaceInfo(&gCurrent_race);
1722
    DisposeRaceInfo(&gCurrent_race);
1723
    DisposeFlicPanel(0);
1724
    *pSecond_time_around = 1;
1725
    gProgram_state.dont_load = 0;
1726
    if (result >= 0) {
1727
        *pSecond_time_around = 1;
1728
        if (gProgram_state.parts_shop_visited || !PartsShopRecommended()) {
1729
            FadePaletteDown();
1730
            return eSO_continue;
1731
        } else {
1732
            RunFlic(192);
1733
            return DoAutoPartsShop();
1734
        }
1735
    } else {
1736
        RunFlic(192);
1737
        gDisallow_abandon_race = 1;
1738
        return eSO_main_menu_invoked;
1739
    }
1740
}
1741
 
1742
// IDA: void __usercall DrawGridCar(int pX@<EAX>, int pY@<EDX>, br_pixelmap *pImage@<EBX>)
1743
void DrawGridCar(int pX, int pY, br_pixelmap* pImage) {
1744
    LOG_TRACE("(%d, %d, %p)", pX, pY, pImage);
1745
 
1746
    if (gCurrent_graf_data->grid_left_clip <= pX && pX + pImage->width < gCurrent_graf_data->grid_right_clip) {
1747
        DRPixelmapRectangleMaskedCopy(gBack_screen, pX, pY, pImage, 0, 0, pImage->width, pImage->height);
1748
    }
1749
}
1750
 
1751
// IDA: void __usercall DrawGrid(int pOffset@<EAX>, int pDraw_it@<EDX>)
1752
void DrawGrid(int pOffset, int pDraw_it) {
1753
    int i;
1754
    int j;
1755
    int y;
1756
    int x;
1757
    int str_x;
1758
    //int width_job; // Pierre-Marie Baty -- unused variable
1759
    int done_highest;
1760
    int str_index;
1761
    int swap_1_x = 0;
1762
    int swap_1_y = 0;
1763
    int swap_2_x = 0;
1764
    int swap_2_y = 0;
1765
    br_pixelmap* the_image = NULL;
1766
    br_pixelmap* swap_1_image = NULL;
1767
    br_pixelmap* swap_2_image = NULL;
1768
    char numbers_str[4][100];
1769
    char total_str[256];
1770
    tU32 the_time;
1771
    LOG_TRACE("(%d, %d)", pOffset, pDraw_it);
1772
 
1773
    done_highest = 0;
1774
    str_index = 0;
1775
 
1776
    memset(numbers_str, 0, sizeof(numbers_str));
1777
    memset(total_str, 0, sizeof(total_str));
1778
 
1779
    the_time = PDGetTotalTime();
1780
    BrPixelmapRectangleFill(
1781
        gBack_screen,
1782
        gCurrent_graf_data->grid_left_clip,
1783
        gCurrent_graf_data->grid_top_clip,
1784
        gCurrent_graf_data->grid_right_clip - gCurrent_graf_data->grid_left_clip,
1785
        gCurrent_graf_data->grid_bottom_clip - gCurrent_graf_data->grid_top_clip,
1786
        0);
1787
    for (i = 0; i < gCurrent_race.number_of_racers; i++) {
1788
        if (gCurrent_race.opponent_list[i].index == -1) {
1789
            the_image = gProgram_state.current_car.grid_icon_image;
1790
        } else {
1791
            the_image = gCurrent_race.opponent_list[i].car_spec->grid_icon_image;
1792
        }
1793
        y = gCurrent_graf_data->grid_top_y
1794
            + i % 2 * gCurrent_graf_data->grid_y_pitch
1795
            - the_image->height / 2
1796
            - gGrid_y_adjust;
1797
        x = gCurrent_graf_data->grid_left_x
1798
            + i % 2 * gCurrent_graf_data->grid_x_stagger
1799
            + i / 2 * gCurrent_graf_data->grid_x_pitch
1800
            - pOffset;
1801
        if (i == gSwap_grid_1) {
1802
            swap_1_x = x;
1803
            swap_1_y = y;
1804
            swap_1_image = the_image;
1805
        } else if (i == gSwap_grid_2) {
1806
            swap_2_x = x;
1807
            swap_2_y = y;
1808
            swap_2_image = the_image;
1809
        } else if (gCurrent_race.opponent_list[i].index != -1 || the_time % 900 / 300) {
1810
            DrawGridCar(x, y, the_image);
1811
        }
1812
        if (!done_highest && gCurrent_race.opponent_list[i].ranking >= gProgram_state.rank) {
1813
            done_highest = 1;
1814
            if (x - gCurrent_graf_data->grid_marker_margin >= gCurrent_graf_data->grid_left_clip
1815
                && x + the_image->width < gCurrent_graf_data->grid_right_clip) {
1816
                BrPixelmapLine(
1817
                    gBack_screen,
1818
                    x - gCurrent_graf_data->grid_marker_margin,
1819
                    y - gCurrent_graf_data->grid_marker_margin,
1820
                    x - gCurrent_graf_data->grid_marker_margin,
1821
                    y + gCurrent_graf_data->grid_marker_margin + the_image->height,
1822
                    45);
1823
                BrPixelmapLine(
1824
                    gBack_screen,
1825
                    x - gCurrent_graf_data->grid_marker_margin,
1826
                    y - gCurrent_graf_data->grid_marker_margin,
1827
                    x + gCurrent_graf_data->grid_marker_x_len,
1828
                    y - gCurrent_graf_data->grid_marker_margin,
1829
                    45);
1830
                BrPixelmapLine(
1831
                    gBack_screen,
1832
                    x - gCurrent_graf_data->grid_marker_margin,
1833
                    y + gCurrent_graf_data->grid_marker_margin + the_image->height,
1834
                    x + gCurrent_graf_data->grid_marker_x_len,
1835
                    y + gCurrent_graf_data->grid_marker_margin + the_image->height,
1836
                    45);
1837
            }
1838
        }
1839
        if (gCurrent_race.opponent_list[i].index < 0) {
1840
            str_index = 2;
1841
        } else if (gProgram_state.rank <= gCurrent_race.opponent_list[i].ranking) {
1842
            if (str_index >= 2) {
1843
                str_index = 3;
1844
            } else {
1845
                str_index = 1;
1846
            }
1847
        }
1848
        if (gCurrent_race.opponent_list[i].index >= 0) {
1849
            if (gOpponents[gCurrent_race.opponent_list[i].index].car_number <= 0
1850
                || gOpponents[gCurrent_race.opponent_list[i].index].car_number >= 100) {
1851
                if (gOpponents[gCurrent_race.opponent_list[i].index].car_number < 0) {
1852
                    sprintf(&numbers_str[str_index][strlen(numbers_str[str_index])], "%c ", ':');
1853
                }
1854
            } else {
1855
                sprintf(
1856
                    &numbers_str[str_index][strlen(numbers_str[str_index])],
1857
                    "%d ",
1858
                    gOpponents[gCurrent_race.opponent_list[i].index].car_number);
1859
            }
1860
        } else {
1861
            if (gProgram_state.frank_or_anniness == eAnnie) {
1862
                sprintf(&numbers_str[str_index][strlen(numbers_str[str_index])], "%c ", '<');
1863
            } else {
1864
                sprintf(&numbers_str[str_index][strlen(numbers_str[str_index])], "%c ", ';');
1865
            }
1866
        }
1867
    }
1868
    if (gSwap_grid_1 >= 0) {
1869
        DrawGridCar(
1870
            gGrid_transition_stage * (swap_2_x - swap_1_x) / 100 + swap_1_x,
1871
            gGrid_transition_stage * (swap_2_y - swap_1_y) / 100 + swap_1_y,
1872
            swap_1_image);
1873
        DrawGridCar(
1874
            gGrid_transition_stage * (swap_1_x - swap_2_x) / 100 + swap_2_x,
1875
            gGrid_transition_stage * (swap_1_y - swap_2_y) / 100 + swap_2_y,
1876
            swap_2_image);
1877
    }
1878
    if (gDraw_grid_status == eGrid_draw_all) {
1879
        if (strlen(numbers_str[3])) {
1880
            numbers_str[3][strlen(numbers_str[3]) - 1] = '\0';
1881
        } else {
1882
            numbers_str[2][strlen(numbers_str[2]) - 1] = '\0';
1883
        }
1884
        strcpy(total_str, numbers_str[0]);
1885
        for (i = 1; i < COUNT_OF(numbers_str); i++) {
1886
            strcat(total_str, numbers_str[i]);
1887
        }
1888
        str_x = (gCurrent_graf_data->grid_numbers_left + gCurrent_graf_data->grid_numbers_right) / 2
1889
            - (BrPixelmapTextWidth(gBack_screen, gBig_font, total_str) / 2);
1890
        BrPixelmapRectangleFill(
1891
            gBack_screen,
1892
            gCurrent_graf_data->grid_numbers_left,
1893
            gCurrent_graf_data->grid_numbers_top,
1894
            gCurrent_graf_data->grid_numbers_right - gCurrent_graf_data->grid_numbers_left,
1895
            gBig_font->glyph_y,
1896
            0);
1897
        gGrid_number_x_coords[0] = str_x - 3 - gCurrent_graf_data->grid_numbers_left;
1898
        for (i = 0; i < 4; ++i) {
1899
            if (i != 2 || the_time % 900 / 300) {
1900
                TransBrPixelmapText(
1901
                    gBack_screen,
1902
                    str_x,
1903
                    gCurrent_graf_data->grid_numbers_top,
1904
                    gGrid_number_colour[i],
1905
                    gBig_font,
1906
                    numbers_str[i]);
1907
            }
1908
            str_x += BrPixelmapTextWidth(gBack_screen, gBig_font, numbers_str[i]);
1909
        }
1910
        for (i = gCurrent_race.number_of_racers - 1; i > 0; i--) {
1911
            for (j = strlen(total_str) - 2; j >= 0; j--) {
1912
                if (total_str[j] == ' ') {
1913
                    total_str[j + 1] = '\0';
1914
                    break;
1915
                }
1916
            }
1917
            gGrid_number_x_coords[i] = gGrid_number_x_coords[0] + BrPixelmapTextWidth(gBack_screen, gBig_font, total_str);
1918
        }
1919
    }
1920
    if (pDraw_it) {
1921
        PDScreenBufferSwap(0);
1922
    }
1923
}
1924
 
1925
// IDA: void __usercall MoveGrid(int pFrom@<EAX>, int pTo@<EDX>, tS32 pTime_to_move@<EBX>)
1926
void MoveGrid(int pFrom, int pTo, tS32 pTime_to_move) {
1927
    tS32 start_time;
1928
    tS32 the_time;
1929
    //int move_distance; // Pierre-Marie Baty -- unused variable
1930
    int pitch;
1931
    LOG_TRACE("(%d, %d, %d)", pFrom, pTo, pTime_to_move);
1932
 
1933
    pitch = gCurrent_graf_data->grid_x_pitch;
1934
    start_time = PDGetTotalTime();
1935
    while (1) {
1936
        the_time = PDGetTotalTime();
1937
        if (start_time + pTime_to_move <= the_time) {
1938
            break;
1939
        }
1940
        DrawGrid(pitch * pFrom + pitch * (pTo - pFrom) * (the_time - start_time) / pTime_to_move, 1);
1941
    }
1942
    DrawGrid(pitch * pTo, 1);
1943
}
1944
 
1945
// IDA: int __usercall CalcGridOffset@<EAX>(int pPosition@<EAX>)
1946
int CalcGridOffset(int pPosition) {
1947
    LOG_TRACE("(%d)", pPosition);
1948
 
1949
    return pPosition / 2 - 1;
1950
}
1951
 
1952
// IDA: void __usercall GridDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
1953
void GridDraw(int pCurrent_choice, int pCurrent_mode) {
1954
    LOG_TRACE8("(%d, %d)", pCurrent_choice, pCurrent_mode);
1955
 
1956
    if (gDraw_grid_status > eGrid_draw_none) {
1957
        DrawGrid(gCurrent_graf_data->grid_x_pitch * CalcGridOffset(gOur_starting_position), 0);
1958
    }
1959
}
1960
 
1961
// IDA: void __usercall ActuallySwapOrder(int pFirst_index@<EAX>, int pSecond_index@<EDX>)
1962
void ActuallySwapOrder(int pFirst_index, int pSecond_index) {
1963
    tOpp_spec temp_opp;
1964
    LOG_TRACE("(%d, %d)", pFirst_index, pSecond_index);
1965
 
1966
    temp_opp = gCurrent_race.opponent_list[pFirst_index];
1967
    gCurrent_race.opponent_list[pFirst_index] = gCurrent_race.opponent_list[pSecond_index];
1968
    gCurrent_race.opponent_list[pSecond_index] = temp_opp;
1969
    gOur_starting_position = pSecond_index;
1970
 
1971
    // LOG_DEBUG("first %d, second %d", gCurrent_race.opponent_list[pFirst_index].index, gCurrent_race.opponent_list[pSecond_index].index);
1972
}
1973
 
1974
// IDA: void __usercall DoGridTransition(int pFirst_index@<EAX>, int pSecond_index@<EDX>)
1975
void DoGridTransition(int pFirst_index, int pSecond_index) {
1976
    tU32 start_time;
1977
    tU32 the_time;
1978
    LOG_TRACE("(%d, %d)", pFirst_index, pSecond_index);
1979
 
1980
    if (pFirst_index != pSecond_index) {
1981
        start_time = PDGetTotalTime();
1982
        gSwap_grid_1 = pFirst_index;
1983
        gSwap_grid_2 = pSecond_index;
1984
        while (1) {
1985
            the_time = PDGetTotalTime();
1986
            if (start_time + 150 <= the_time) {
1987
                break;
1988
            }
1989
            RemoveTransientBitmaps(1);
1990
            gGrid_transition_stage = 100 * (the_time - start_time) / 150;
1991
            DrawGrid(gCurrent_graf_data->grid_x_pitch * CalcGridOffset(gOur_starting_position), 0);
1992
            ProcessFlicQueue(gFrame_period);
1993
            DoMouseCursor();
1994
            PDScreenBufferSwap(0);
1995
        }
1996
        gSwap_grid_1 = -1;
1997
        gSwap_grid_2 = -1;
1998
        ActuallySwapOrder(pFirst_index, pSecond_index);
1999
        MoveGrid(CalcGridOffset(pFirst_index), CalcGridOffset(pSecond_index), 150);
2000
    }
2001
}
2002
 
2003
// IDA: void __cdecl ChallengeStart()
2004
void ChallengeStart(void) {
2005
    br_pixelmap* the_map;
2006
    int i;
2007
    int j;
2008
    int line_count;
2009
    int dare_index;
2010
    FILE* f;
2011
    tPath_name the_path;
2012
    char s[256];
2013
    LOG_TRACE("()");
2014
 
2015
    InitialiseFlicPanel(
2016
        0,
2017
        gCurrent_graf_data->start_race_panel_left,
2018
        gCurrent_graf_data->start_race_panel_top,
2019
        gCurrent_graf_data->start_race_panel_right - gCurrent_graf_data->start_race_panel_left,
2020
        gCurrent_graf_data->start_race_panel_bottom - gCurrent_graf_data->start_race_panel_top);
2021
    ChangePanelFlic(
2022
        0,
2023
        gOpponents[gChallenger_index__racestrt].mug_shot_image_data,
2024
        gOpponents[gChallenger_index__racestrt].mug_shot_image_data_length);
2025
    if (gScreen->row_bytes < 0) {
2026
        BrFatal("C:\\Msdev\\Projects\\DethRace\\Racestrt.c", 2610, "Bruce bug at line %d, file C:\\Msdev\\Projects\\DethRace\\Racestrt.c", 50);
2027
    }
2028
    the_map = DRPixelmapAllocate(
2029
        gScreen->type,
2030
        gCurrent_graf_data->dare_mugshot_width,
2031
        gCurrent_graf_data->dare_mugshot_height,
2032
        0,
2033
        0);
2034
 
2035
    BrPixelmapRectangleCopy(the_map, 0, 0, GetPanelPixelmap(0), gCurrent_graf_data->dare_mug_left_margin, gCurrent_graf_data->dare_mug_top_margin, gCurrent_graf_data->dare_mugshot_width, gCurrent_graf_data->dare_mugshot_height);
2036
    DisposeFlicPanel(0);
2037
    TellyInImage(the_map, gCurrent_graf_data->dare_mugshot_left, gCurrent_graf_data->dare_mugshot_top);
2038
    BrPixelmapFree(the_map);
2039
    the_map = DRPixelmapAllocate(gScreen->type, gCurrent_graf_data->dare_text_width, gCurrent_graf_data->dare_mugshot_height, 0, 0);
2040
    BrPixelmapFill(the_map, 0);
2041
    TransBrPixelmapText(the_map, 0, 0, 1u, gBig_font, gOpponents[gChallenger_index__racestrt].abbrev_name);
2042
    PathCat(the_path, gApplication_path, "DARES.TXT");
2043
    f = DRfopen(the_path, "rt");
2044
    if (f == NULL) {
2045
        FatalError(kFatalError_OpenDareTxt);
2046
    }
2047
 
2048
    dare_index = IRandomBetween(0, GetAnInt(f) - 1);
2049
    for (i = 0; i < dare_index; i++) {
2050
        line_count = GetAnInt(f);
2051
        for (j = 0; j < line_count; j++) {
2052
            GetALineAndDontArgue(f, s);
2053
        }
2054
    }
2055
    line_count = GetAnInt(f);
2056
    for (i = 0; i < line_count; i++) {
2057
        GetALineAndDontArgue(f, s);
2058
        TransBrPixelmapText(the_map, 0, 2 * (i + 1) * gBig_font->glyph_y, 0x86u, gBig_font, s);
2059
    }
2060
    fclose(f);
2061
    BrPixelmapLine(the_map, 0, gBig_font->glyph_y + 2, the_map->width, gBig_font->glyph_y + 2, 45);
2062
    TellyInImage(the_map, gCurrent_graf_data->dare_text_left, gCurrent_graf_data->dare_mugshot_top);
2063
    BrPixelmapFree(the_map);
2064
    UnlockOpponentMugshot(gChallenger_index__racestrt);
2065
    gDare_start_time = PDGetTotalTime();
2066
}
2067
 
2068
// IDA: int __usercall CheckNextStage@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
2069
int CheckNextStage(int* pCurrent_choice, int* pCurrent_mode) {
2070
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
2071
 
2072
    if (gDare_start_time && (unsigned int)(PDGetTotalTime() - gDare_start_time) >= 7500) {
2073
        BrPixelmapRectangleFill(
2074
            gBack_screen,
2075
            gCurrent_graf_data->grid_left_clip,
2076
            gCurrent_graf_data->dare_mugshot_top,
2077
            gCurrent_graf_data->grid_right_clip - gCurrent_graf_data->grid_left_clip,
2078
            gCurrent_graf_data->dare_mugshot_height,
2079
            0);
2080
        DoGridTransition(gOur_starting_position, gChallenger_position);
2081
        gDraw_grid_status = eGrid_draw_icons_only;
2082
        gDare_start_time = 0;
2083
    }
2084
    return 0;
2085
}
2086
 
2087
// IDA: int __usercall ChallengeDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
2088
int ChallengeDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
2089
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
2090
 
2091
    if (!pEscaped || gDare_start_time) {
2092
        if (!pEscaped && gDare_start_time) {
2093
            ActuallySwapOrder(gOur_starting_position, gChallenger_position);
2094
            ChallengeOccurred(gChallenger_index__racestrt, 1);
2095
        }
2096
    } else {
2097
        DoGridTransition(gOur_starting_position, gOriginal_position);
2098
        ActuallySwapOrder(gOur_starting_position, gOriginal_position);
2099
        ChallengeOccurred(gChallenger_index__racestrt, 0);
2100
    }
2101
    ChallengeOccurred(gChallenger_index__racestrt, pEscaped == 0);
2102
    if (pTimed_out) {
2103
        return 0;
2104
    } else {
2105
        return pCurrent_choice;
2106
    }
2107
}
2108
 
2109
// IDA: void __cdecl DoChallengeScreen()
2110
void DoChallengeScreen(void) {
2111
    static tFlicette flicker_on[2] = { { 43, { 54, 108 }, { 157, 377 } }, { 43, { 218, 436 }, { 157, 377 } } };
2112
    static tFlicette flicker_off[2] = { { 42, { 54, 108 }, { 157, 377 } }, { 42, { 218, 436 }, { 157, 377 } } };
2113
    static tFlicette push[2] = { { 304, { 54, 108 }, { 157, 377 } }, { 305, { 218, 436 }, { 157, 377 } } };
2114
    static tMouse_area mouse_areas[2] = {
2115
        { { 54, 108 }, { 157, 377 }, { 117, 234 }, { 178, 427 }, 0, 0, 0, NULL },
2116
        { { 218, 436 }, { 157, 377 }, { 281, 562 }, { 178, 427 }, 1, 0, 0, NULL }
2117
    };
2118
    static tInterface_spec interface_spec = {
2119
        0,               // initial_imode
2120
        301,             // first_opening_flic
2121
        0,               // second_opening_flic
2122
        -1,              // end_flic_go_ahead
2123
        -1,              // end_flic_escaped
2124
        -1,              // end_flic_otherwise
2125
        0,               // flic_bunch_to_load
2126
        { -1, 0 },       // move_left_new_mode
2127
        { -1, 0 },       // move_left_delta
2128
        { 0, 0 },        // move_left_min
2129
        { 1, 0 },        // move_left_max
2130
        { NULL, NULL },  // move_left_proc
2131
        { -1, 0 },       // move_right_new_mode
2132
        { 1, 0 },        // move_right_delta
2133
        { 0, 0 },        // move_right_min
2134
        { 1, 0 },        // move_right_max
2135
        { NULL, NULL },  // move_right_proc
2136
        { -1, 0 },       // move_up_new_mode
2137
        { 0, 0 },        // move_up_delta
2138
        { 0, 0 },        // move_up_min
2139
        { 0, 0 },        // move_up_max
2140
        { NULL, NULL },  // move_up_proc
2141
        { -1, 0 },       // move_down_new_mode
2142
        { 0, 0 },        // move_down_delta
2143
        { 0, 0 },        // move_down_min
2144
        { 0, 0 },        // move_down_max
2145
        { NULL, NULL },  // move_down_proc
2146
        { 1, 1 },        // go_ahead_allowed
2147
        { NULL, NULL },  // go_ahead_proc
2148
        { 1, 1 },        // escape_allowed
2149
        { NULL, NULL },  // escape_proc
2150
        &CheckNextStage, // exit_proc
2151
        &GridDraw,       // draw_proc
2152
        0u,              // time_out
2153
        NULL,            // start_proc1
2154
        &ChallengeStart, // start_proc2
2155
        &ChallengeDone,  // done_proc
2156
        0,               // font_needed
2157
        { 0, 0 },        // typeable
2158
        NULL,            // get_original_string
2159
        1,               // escape_code
2160
        1,               // dont_save_or_load
2161
        2,               // number_of_button_flics
2162
        flicker_on,      // flicker_on_flics
2163
        flicker_off,     // flicker_off_flics
2164
        push,            // pushed_flics
2165
        2,               // number_of_mouse_areas
2166
        mouse_areas,     // mouse_areas
2167
        0,               // number_of_recopy_areas
2168
        NULL             // recopy_areas
2169
    };
2170
 
2171
    //int result; // Pierre-Marie Baty -- unused variable
2172
    LOG_TRACE("()");
2173
 
2174
    gOriginal_position = gOur_starting_position;
2175
    gChallenger_position = IRandomBetween(0, 1);
2176
    if (gOpponents[gCurrent_race.opponent_list[gChallenger_position].index].car_number < 0) {
2177
        gChallenger_position ^= 1u;
2178
    }
2179
    gChallenger_index__racestrt = gCurrent_race.opponent_list[gChallenger_position].index;
2180
    LoadOpponentMugShot(gChallenger_index__racestrt);
2181
    gDraw_grid_status = eGrid_draw_none;
2182
    gGrid_y_adjust = gCurrent_graf_data->dare_y_adjust;
2183
    DoInterfaceScreen(&interface_spec, 0, 0);
2184
}
2185
 
2186
// IDA: int __usercall GridDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
2187
int GridDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
2188
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
2189
 
2190
    if (pTimed_out) {
2191
        return 0;
2192
    }
2193
    if (pEscaped) {
2194
        return -1;
2195
    }
2196
    return pCurrent_choice;
2197
}
2198
 
2199
// IDA: void __cdecl GridStart()
2200
void GridStart(void) {
2201
    LOG_TRACE("()");
2202
 
2203
    MoveGrid(-2, CalcGridOffset(gOur_starting_position), 400);
2204
    PrintMemoryDump(0, "IN GRID SCREEN");
2205
}
2206
 
2207
// IDA: int __usercall GridMoveLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
2208
int GridMoveLeft(int* pCurrent_choice, int* pCurrent_mode) {
2209
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
2210
 
2211
    if (gOur_starting_position
2212
        && gCurrent_race.opponent_list[gOur_starting_position - 1].ranking >= gProgram_state.rank) {
2213
        AddToFlicQueue(
2214
            gStart_interface_spec->pushed_flics[1].flic_index,
2215
            gStart_interface_spec->pushed_flics[1].x[gGraf_data_index],
2216
            gStart_interface_spec->pushed_flics[1].y[gGraf_data_index],
2217
            1);
2218
        DRS3StartSound(gEffects_outlet, 3000);
2219
        DoGridTransition(gOur_starting_position, gOur_starting_position - 1);
2220
    }
2221
    return 0;
2222
}
2223
 
2224
// IDA: int __usercall GridMoveRight@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
2225
int GridMoveRight(int* pCurrent_choice, int* pCurrent_mode) {
2226
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
2227
 
2228
    if (gOur_starting_position < gCurrent_race.number_of_racers - 1) {
2229
        AddToFlicQueue(
2230
            gStart_interface_spec->pushed_flics[2].flic_index,
2231
            gStart_interface_spec->pushed_flics[2].x[gGraf_data_index],
2232
            gStart_interface_spec->pushed_flics[2].y[gGraf_data_index],
2233
            1);
2234
        DRS3StartSound(gEffects_outlet, 3000);
2235
        DoGridTransition(gOur_starting_position, gOur_starting_position + 1);
2236
    }
2237
    return 0;
2238
}
2239
 
2240
// IDA: int __usercall GridClickCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
2241
int GridClickCar(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
2242
    int rel_pos;
2243
    int new_pos;
2244
    //int base_pos; // Pierre-Marie Baty -- unused variable
2245
    int x_coord;
2246
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
2247
 
2248
    rel_pos = ((gCurrent_graf_data->grid_bottom_clip - gCurrent_graf_data->grid_top_clip) / 2) < pY_offset;
2249
    if (rel_pos) {
2250
        pX_offset -= gCurrent_graf_data->grid_x_stagger;
2251
    }
2252
    x_coord = pX_offset / gCurrent_graf_data->grid_x_pitch;
2253
    if (x_coord > 2) {
2254
        x_coord = 2;
2255
    }
2256
    new_pos = 2 * x_coord + rel_pos + (gOur_starting_position & ~1) - 2;
2257
    if (new_pos >= 0 && new_pos < gCurrent_race.number_of_racers && gProgram_state.rank < gCurrent_race.opponent_list[new_pos].ranking) {
2258
        DRS3StartSound(gEffects_outlet, 3000);
2259
        DoGridTransition(gOur_starting_position, new_pos);
2260
    }
2261
    return 0;
2262
}
2263
 
2264
// IDA: int __usercall GridClickNumbers@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
2265
int GridClickNumbers(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
2266
    int new_pos;
2267
    int i;
2268
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
2269
 
2270
    new_pos = -1;
2271
    for (i = 0; i < gCurrent_race.number_of_racers; i++) {
2272
        if (gGrid_number_x_coords[i] <= pX_offset && (gCurrent_race.number_of_racers - 1 == i || pX_offset < gGrid_number_x_coords[i + 1])) {
2273
            new_pos = i;
2274
            break;
2275
        }
2276
    }
2277
    if (new_pos >= 0 && new_pos < gCurrent_race.number_of_racers && gProgram_state.rank <= gCurrent_race.opponent_list[new_pos].ranking) {
2278
        DRS3StartSound(gEffects_outlet, 3000);
2279
        DoGridTransition(gOur_starting_position, new_pos);
2280
    }
2281
    return 0;
2282
}
2283
 
2284
// IDA: int __usercall GridClickLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
2285
int GridClickLeft(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
2286
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
2287
 
2288
    GridMoveLeft(pCurrent_choice, pCurrent_mode);
2289
    return 0;
2290
}
2291
 
2292
// IDA: int __usercall GridClickRight@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
2293
int GridClickRight(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
2294
    LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
2295
 
2296
    GridMoveRight(pCurrent_choice, pCurrent_mode);
2297
    return 0;
2298
}
2299
 
2300
// IDA: int __usercall CheckChallenge@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
2301
int CheckChallenge(int* pCurrent_choice, int* pCurrent_mode) {
2302
    LOG_TRACE8("(%p, %p)", pCurrent_choice, pCurrent_mode);
2303
 
2304
    if (!gChallenge_time || PDGetTotalTime() < gChallenge_time) {
2305
        return 0;
2306
    }
2307
    *pCurrent_choice = -2;
2308
    return 1;
2309
}
2310
 
2311
// IDA: int __usercall FindBestPos@<EAX>(int pOur_rank@<EAX>)
2312
int FindBestPos(int pOur_rank) {
2313
    int i;
2314
    LOG_TRACE("(%d)", pOur_rank);
2315
 
2316
    for (i = gCurrent_race.number_of_racers - 1; i >= 0; i--) {
2317
        if (gCurrent_race.opponent_list[i].ranking < pOur_rank) {
2318
            return i + 1;
2319
        }
2320
    }
2321
    return 0;
2322
}
2323
 
2324
// IDA: int __usercall SortGridFunction@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
2325
int SortGridFunction(const void* pFirst_one, const void* pSecond_one) {
2326
    LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
2327
 
2328
    return ((tOpp_spec*)pFirst_one)->ranking - ((tOpp_spec*)pSecond_one)->ranking;
2329
}
2330
 
2331
// IDA: void __cdecl SortOpponents()
2332
void SortOpponents(void) {
2333
    int i;
2334
    LOG_TRACE("()");
2335
 
2336
    for (i = 0; gCurrent_race.number_of_racers > i; ++i) {
2337
        if (gCurrent_race.opponent_list[i].index < 0) {
2338
            return;
2339
        }
2340
    }
2341
    qsort(gCurrent_race.opponent_list, gCurrent_race.number_of_racers, sizeof(tOpp_spec), SortGridFunction);
2342
    gBest_pos_available = FindBestPos(gProgram_state.rank);
2343
    gOur_starting_position = gCurrent_race.number_of_racers - 70 * (gCurrent_race.number_of_racers - gBest_pos_available) / 100;
2344
    for (i = gCurrent_race.number_of_racers; i > gOur_starting_position; i--) {
2345
        gCurrent_race.opponent_list[i] = gCurrent_race.opponent_list[i - 1];
2346
    }
2347
    gCurrent_race.opponent_list[gOur_starting_position].index = -1;
2348
    gCurrent_race.opponent_list[gOur_starting_position].ranking = gProgram_state.rank;
2349
    gCurrent_race.opponent_list[gOur_starting_position].car_spec = &gProgram_state.current_car;
2350
    gCurrent_race.number_of_racers++;
2351
}
2352
 
2353
// IDA: tSO_result __cdecl DoGridPosition()
2354
tSO_result DoGridPosition(void) {
2355
    static tFlicette flicker_on[3] = {
2356
        { 43, { 240, 480 }, { 158, 379 } },
2357
        { 293, { 56, 112 }, { 151, 362 } },
2358
        { 296, { 136, 272 }, { 151, 362 } }
2359
    };
2360
 
2361
    static tFlicette flicker_off[3] = {
2362
        { 42, { 240, 480 }, { 158, 379 } },
2363
        { 292, { 56, 112 }, { 151, 362 } },
2364
        { 295, { 136, 272 }, { 151, 362 } }
2365
    };
2366
 
2367
    static tFlicette push[3] = {
2368
        { 154, { 240, 480 }, { 158, 379 } },
2369
        { 294, { 56, 112 }, { 151, 362 } },
2370
        { 297, { 136, 272 }, { 151, 362 } }
2371
    };
2372
    static tMouse_area mouse_areas[5] = {
2373
        { { 240, 480 }, { 158, 379 }, { 305, 610 }, { 178, 427 }, 0, 0, 0, NULL },
2374
        { { 56, 112 },
2375
            { 151, 362 },
2376
            { 91, 182 },
2377
            { 158, 379 },
2378
            -1,
2379
            0,
2380
            0,
2381
            GridClickLeft },
2382
        { { 136, 272 },
2383
            { 151, 362 },
2384
            { 171, 342 },
2385
            { 158, 379 },
2386
            -1,
2387
            0,
2388
            0,
2389
            GridClickRight },
2390
        { { 52, 104 },
2391
            { 56, 134 },
2392
            { 269, 538 },
2393
            { 141, 338 },
2394
            -1,
2395
            0,
2396
            0,
2397
            GridClickCar },
2398
        { { 60, 120 },
2399
            { 163, 391 },
2400
            { 235, 470 },
2401
            { 176, 422 },
2402
            -1,
2403
            0,
2404
            0,
2405
            GridClickNumbers }
2406
    };
2407
 
2408
    static tInterface_spec interface_spec = {
2409
        0,                       // initial_imode
2410
        290,                     // first_opening_flic
2411
        0,                       // second_opening_flic
2412
        0,                       // end_flic_go_ahead
2413
        0,                       // end_flic_escaped
2414
        0,                       // end_flic_otherwise
2415
        8,                       // flic_bunch_to_load
2416
        { -1, 0 },               // move_left_new_mode
2417
        { 0, 0 },                // move_left_delta
2418
        { 0, 0 },                // move_left_min
2419
        { 0, 0 },                // move_left_max
2420
        { GridMoveLeft, NULL },  // move_left_proc
2421
        { -1, 0 },               // move_right_new_mode
2422
        { 0, 0 },                // move_right_delta
2423
        { 0, 0 },                // move_right_min
2424
        { 0, 0 },                // move_right_max
2425
        { GridMoveRight, NULL }, // move_right_proc
2426
        { -1, 0 },               // move_up_new_mode
2427
        { 0, 0 },                // move_up_delta
2428
        { 0, 0 },                // move_up_min
2429
        { 0, 0 },                // move_up_max
2430
        { GridMoveLeft, NULL },  // move_up_proc
2431
        { -1, 0 },               // move_down_new_mode
2432
        { 0, 0 },                // move_down_delta
2433
        { 0, 0 },                // move_down_min
2434
        { 0, 0 },                // move_down_max
2435
        { GridMoveRight, NULL }, // move_down_proc
2436
        { 1, 1 },                // go_ahead_allowed
2437
        { NULL, NULL },          // go_ahead_proc
2438
        { 1, 1 },                // escape_allowed
2439
        { NULL, NULL },          // escape_proc
2440
        CheckChallenge,          // exit_proc
2441
        GridDraw,                // draw_proc
2442
        0u,                      // time_out
2443
        NULL,                    // start_proc1
2444
        GridStart,               // start_proc2
2445
        GridDone,                // done_proc
2446
        0,                       // font_needed
2447
        { 0, 0 },                // typeable
2448
        NULL,                    // get_original_string
2449
        -1,                      // escape_code
2450
        1,                       // dont_save_or_load
2451
        3,                       // number_of_button_flics
2452
        flicker_on,              // flicker_on_flics
2453
        flicker_off,             // flicker_off_flics
2454
        push,                    // pushed_flics
2455
        5,                       // number_of_mouse_areas
2456
        mouse_areas,             // mouse_areas
2457
        0,                       // number_of_recopy_areas
2458
        NULL                     // recopy_areas
2459
    };
2460
 
2461
    int result;
2462
    LOG_TRACE("()");
2463
 
2464
    gStart_interface_spec = &interface_spec;
2465
    if (!gAusterity_mode) {
2466
        LoadGridIcons(&gCurrent_race);
2467
    }
2468
    gSwap_grid_1 = -1;
2469
    gSwap_grid_2 = -1;
2470
    gDraw_grid_status = eGrid_draw_all;
2471
    gGrid_y_adjust = 0;
2472
    SortOpponents();
2473
    if (!gAusterity_mode) {
2474
        if (gBest_pos_available > 1
2475
            && (gOpponents[gCurrent_race.opponent_list[0].index].car_number >= 0
2476
                || gOpponents[gCurrent_race.opponent_list[1].index].car_number >= 0)
2477
            && PercentageChance(10)) {
2478
            gChallenge_time = PDGetTotalTime() + IRandomBetween(1000, 10000);
2479
        } else {
2480
            gChallenge_time = 0;
2481
        }
2482
        result = DoInterfaceScreen(&interface_spec, 0, 0);
2483
        if (result >= 0) {
2484
            FadePaletteDown();
2485
        } else {
2486
            RunFlic(291);
2487
        }
2488
        if (result == -1) {
2489
            return eSO_main_menu_invoked;
2490
        }
2491
        if (result == -2) {
2492
            DoChallengeScreen();
2493
        }
2494
        DisposeGridIcons(&gCurrent_race);
2495
    }
2496
    return eSO_continue;
2497
}
2498
 
2499
// IDA: void __cdecl CheckPlayersAreResponding()
2500
void CheckPlayersAreResponding(void) {
2501
    int i;
2502
    tU32 time;
2503
    tNet_message* message;
2504
    LOG_TRACE("()");
2505
 
2506
    time = PDGetTotalTime();
2507
    for (i = 0; i < gNumber_of_net_players; i++) {
2508
        if (i != gThis_net_player_index && time - gNet_players[i].last_heard_from_him > 20000) {
2509
            gNet_players[i].player_status = ePlayer_status_not_responding;
2510
        }
2511
    }
2512
    if (gNet_mode == eNet_mode_client && gLast_host_query == 0) {
2513
        message = NetBuildMessage(NETMSGID_HOSTQUERY, 0);
2514
        NetGuaranteedSendMessageToHost(gCurrent_net_game, message, NULL);
2515
        gLast_host_query = time;
2516
    }
2517
}
2518
 
2519
// IDA: void __cdecl NetSynchStartStart()
2520
void NetSynchStartStart(void) {
2521
    LOG_TRACE("()");
2522
 
2523
    CheckPlayersAreResponding();
2524
}
2525
 
2526
// IDA: void __usercall DrawAnItem(int pX@<EAX>, int pY_index@<EDX>, int pFont_index@<EBX>, char *pText@<ECX>)
2527
//  Suffix added to avoid duplicate symbol
2528
void DrawAnItem__racestrt(int pX, int pY_index, int pFont_index, char* pText) {
2529
    LOG_TRACE("(%d, %d, %d, \"%s\")", pX, pY_index, pFont_index, pText);
2530
 
2531
    TransBrPixelmapText(gBack_screen,
2532
        pX,
2533
        gCurrent_graf_data->start_synch_top + gCurrent_graf_data->start_synch_y_pitch * pY_index,
2534
        pFont_index,
2535
        gFont_7,
2536
        pText);
2537
}
2538
 
2539
// IDA: void __usercall NetSynchStartDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
2540
void NetSynchStartDraw(int pCurrent_choice, int pCurrent_mode) {
2541
    int i;
2542
    int number_ready;
2543
    char s[256];
2544
    LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
2545
 
2546
    number_ready = 0;
2547
    CheckPlayersAreResponding();
2548
    NetPlayerStatusChanged(ePlayer_status_ready);
2549
    for (i = 0; i < COUNT_OF(gNet_players); i++) {
2550
        BrPixelmapRectangleFill(gBack_screen,
2551
            gCurrent_graf_data->start_synch_x_0,
2552
            gCurrent_graf_data->start_synch_y_pitch * i + gCurrent_graf_data->start_synch_top,
2553
            gCurrent_graf_data->start_synch_x_r - gCurrent_graf_data->start_synch_x_1,
2554
            gFont_7->glyph_y,
2555
            0);
2556
    }
2557
    for (i = 0; i < gNumber_of_net_players; i++) {
2558
        strcpy(s, gNet_players[i].player_name);
2559
        if (gNet_players[i].host) {
2560
            strcat(s, " -");
2561
            strcat(s, GetMiscString(kMiscString_HOST));
2562
            strcat(s, "-");
2563
        }
2564
        TurnOffPaletteConversion();
2565
        DRPixelmapRectangleMaskedCopy(gBack_screen,
2566
            gCurrent_graf_data->start_synch_x_0,
2567
            gCurrent_graf_data->start_synch_top + 1 + gCurrent_graf_data->start_synch_y_pitch * i,
2568
            gIcons_pix_low_res, /* DOS version uses low res, Windows version uses normal res */
2569
            0,
2570
            gNet_players[i].car_index * gCurrent_graf_data->net_head_icon_height,
2571
            gIcons_pix_low_res->width, /* DOS version uses low res, Windows version uses normal res */
2572
            gCurrent_graf_data->net_head_icon_height);
2573
        TurnOnPaletteConversion();
2574
        DrawAnItem__racestrt(
2575
            gCurrent_graf_data->start_synch_x_1,
2576
            i,
2577
            (gNet_players[i].player_status == ePlayer_status_ready) ? 66 : 4,
2578
            s);
2579
        DrawAnItem__racestrt(gCurrent_graf_data->start_synch_x_2,
2580
            i,
2581
            (gNet_players[i].player_status == ePlayer_status_ready) ? 83 : ((gNet_players[i].player_status == ePlayer_status_not_responding) ? 247 : 4),
2582
            GetMiscString(kMiscString_NetworkPlayerStatus_START + gNet_players[i].player_status));
2583
        if (gNet_players[i].player_status == ePlayer_status_ready) {
2584
            number_ready++;
2585
        }
2586
    }
2587
    if (gNet_mode == eNet_mode_host && gNumber_of_net_players == number_ready && gNumber_of_net_players > 1 && (!gNo_races_yet || gNumber_of_net_players == 6)) {
2588
        SignalToStartRace();
2589
        gSynch_race_start = 1;
2590
    }
2591
}
2592
 
2593
// IDA: int __usercall NetSynchStartDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
2594
int NetSynchStartDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
2595
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
2596
 
2597
    if (pEscaped) {
2598
        pCurrent_choice = -1;
2599
    } else if (pCurrent_choice == 0) {
2600
        gProgram_state.prog_status = eProg_idling;
2601
    }
2602
    return pCurrent_choice;
2603
}
2604
 
2605
// IDA: int __usercall NetSynchStartGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
2606
int NetSynchStartGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
2607
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
2608
 
2609
    if (*pCurrent_choice == 0 || (gNet_mode == eNet_mode_host && *pCurrent_choice >= 0)) {
2610
        if (*pCurrent_choice == 0) {
2611
            gProgram_state.prog_status = eProg_idling;
2612
            return 1;
2613
        } else if (gNet_mode == eNet_mode_host && *pCurrent_choice == 1) {
2614
            if (gNumber_of_net_players <= 1) {
2615
                DRS3StartSound(gEffects_outlet, 3100);
2616
                return 0;
2617
            } else {
2618
                SignalToStartRace();
2619
                gSynch_race_start = 1;
2620
                gNo_races_yet = 0;
2621
                return 1;
2622
            }
2623
        } else {
2624
            return 1;
2625
        }
2626
    } else {
2627
        DRS3StartSound(gEffects_outlet, 3100);
2628
        return 0;
2629
    }
2630
}
2631
 
2632
// IDA: int __usercall ExitWhenReady@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
2633
int ExitWhenReady(int* pCurrent_choice, int* pCurrent_mode) {
2634
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
2635
 
2636
    if (!gSynch_race_start && gProgram_state.prog_status != eProg_game_starting) {
2637
        if (gProgram_state.prog_status == eProg_idling) {
2638
            *pCurrent_choice = 0;
2639
            return 1;
2640
        } else {
2641
            return 0;
2642
        }
2643
    } else {
2644
        *pCurrent_choice = 1;
2645
        return 1;
2646
    }
2647
}
2648
 
2649
// IDA: tSO_result __usercall NetSynchRaceStart2@<EAX>(tNet_synch_mode pMode@<EAX>)
2650
tSO_result NetSynchRaceStart2(tNet_synch_mode pMode) {
2651
    static tFlicette flicker_on_hf[2] = {
2652
        { 321, { 219, 112 }, { 172, 362 } },
2653
        { 321, { 39, 480 }, { 172, 379 } },
2654
    };
2655
    static tFlicette flicker_off_hf[2] = {
2656
        { 322, { 219, 112 }, { 172, 362 } },
2657
        { 322, { 39, 480 }, { 172, 379 } },
2658
    };
2659
    static tFlicette push_hf[2] = {
2660
        { 206, { 219, 112 }, { 172, 362 } },
2661
        { 205, { 39, 480 }, { 172, 379 } },
2662
    };
2663
    static tMouse_area mouse_areas_hf[2] = {
2664
        { { 219, 480 }, { 172, 379 }, { 282, 182 }, { 192, 427 }, 0, 0, 0, NULL },
2665
        { { 39, 112 }, { 172, 362 }, { 102, 182 }, { 192, 379 }, 1, 0, 0, NULL },
2666
    };
2667
    static tInterface_spec interface_spec_hf = {
2668
        0,
2669
        203,
2670
        0,
2671
        0,
2672
        0,
2673
        0,
2674
        8,
2675
        { -1, 0 },
2676
        { 1, 0 },
2677
        { 0, 0 },
2678
        { 1, 0 },
2679
        { NULL, NULL },
2680
        { -1, 0 },
2681
        { 1, 0 },
2682
        { 0, 0 },
2683
        { 1, 0 },
2684
        { NULL, NULL },
2685
        { -1, 0 },
2686
        { 1, 0 },
2687
        { 0, 0 },
2688
        { 1, 0 },
2689
        { NULL, NULL },
2690
        { -1, 0 },
2691
        { 1, 0 },
2692
        { 0, 0 },
2693
        { 1, 0 },
2694
        { NULL, NULL },
2695
        { 1, 1 },
2696
        { NetSynchStartGoAhead, NetSynchStartGoAhead },
2697
        { 1, 1 },
2698
        { NULL, NULL },
2699
        ExitWhenReady,
2700
        NetSynchStartDraw,
2701
        0,
2702
        NULL,
2703
        NetSynchStartStart,
2704
        NetSynchStartDone,
2705
        0,
2706
        { 0, 0 },
2707
        NULL,
2708
        -1,
2709
        1,
2710
        COUNT_OF(flicker_on_hf),
2711
        flicker_on_hf,
2712
        flicker_off_hf,
2713
        push_hf,
2714
        COUNT_OF(mouse_areas_hf),
2715
        mouse_areas_hf,
2716
        0,
2717
        NULL,
2718
    };
2719
    static tFlicette flicker_on_hs[1] = {
2720
        { 321, { 219, 112 }, { 172, 362 } },
2721
    };
2722
    static tFlicette flicker_off_hs[1] = {
2723
        { 322, { 219, 112 }, { 172, 362 } },
2724
    };
2725
    static tFlicette push_hs[1] = {
2726
        { 206, { 219, 112 }, { 172, 362 } },
2727
    };
2728
    static tMouse_area mouse_areas_hs[1] = {
2729
        { { 219, 480 }, { 172, 379 }, { 282, 182 }, { 192, 427 }, 0, 0, 0, NULL },
2730
    };
2731
    static tInterface_spec interface_spec_hs = {
2732
        0, 209, 0, 0, 0, 0, 8,
2733
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2734
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2735
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2736
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2737
        { 1, 1 }, { NetSynchStartGoAhead, NetSynchStartGoAhead }, { 1, 1 }, { NULL, NULL },
2738
        ExitWhenReady, NetSynchStartDraw, 0, NULL, NetSynchStartStart, NetSynchStartDone, 0, { 0, 0 },
2739
        NULL, -1, 1,
2740
        COUNT_OF(flicker_on_hs), flicker_on_hs, flicker_off_hs, push_hs,
2741
        COUNT_OF(mouse_areas_hs), mouse_areas_hs, 0, NULL
2742
    };
2743
    static tFlicette flicker_on_c[1] = {
2744
        { 321, { 219, 112 }, { 172, 362 } },
2745
    };
2746
    static tFlicette flicker_off_c[1] = {
2747
        { 322, { 219, 112 }, { 172, 362 } },
2748
    };
2749
    static tFlicette push_c[1] = {
2750
        { 207, { 219, 112 }, { 172, 362 } },
2751
    };
2752
    static tMouse_area mouse_areas_c[1] = {
2753
        { { 219, 112 }, { 172, 362 }, { 282, 182 }, { 192, 379 }, 0, 0, 0, NULL },
2754
    };
2755
    static tInterface_spec interface_spec_c = {
2756
        0, 204, 0, 0, 0, 0, 8,
2757
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2758
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2759
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2760
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
2761
        { 1, 1 }, { NetSynchStartGoAhead, NetSynchStartGoAhead }, { 1, 1 }, { NULL, NULL },
2762
        ExitWhenReady, NetSynchStartDraw, 0, NULL, NetSynchStartStart, NetSynchStartDone, 0, { 0, 0 },
2763
        NULL, -1, 1,
2764
        COUNT_OF(flicker_on_c), flicker_on_c, flicker_off_c, push_c,
2765
        COUNT_OF(mouse_areas_c), mouse_areas_c, 0, NULL
2766
    };
2767
    int result;
2768
    LOG_TRACE("(%d)", pMode);
2769
 
2770
    if (pMode != eNet_synch_client) {
2771
        if (gCurrent_net_game->status.stage == eNet_game_starting) {
2772
            gCurrent_net_game->status.stage = eNet_game_ready;
2773
        }
2774
        SetUpNetCarPositions();
2775
        // gNet_synch_start = PDGetTotalTime();
2776
    }
2777
    TurnOnPaletteConversion();
2778
    switch (pMode) {
2779
    case eNet_synch_host_first:
2780
        result = DoInterfaceScreen(&interface_spec_hf, 0, 1);
2781
        break;
2782
    case eNet_synch_host_subsequent:
2783
        result = DoInterfaceScreen(&interface_spec_hs, 0, -1);
2784
        break;
2785
    case eNet_synch_client:
2786
        result = DoInterfaceScreen(&interface_spec_c, 0, -1);
2787
        break;
2788
    default:
2789
        break;
2790
    }
2791
    TurnOffPaletteConversion();
2792
    FadePaletteDown();
2793
    if (result > -2 && result < 1) {
2794
        NetLeaveGame(gCurrent_net_game);
2795
    }
2796
    return eSO_continue;
2797
}
2798
 
2799
// IDA: tSO_result __cdecl NetSynchRaceStart()
2800
tSO_result NetSynchRaceStart(void) {
2801
    LOG_TRACE("()");
2802
 
2803
    SuspendPendingFlic();
2804
    if (gNet_mode == eNet_mode_host) {
2805
        if (gNo_races_yet) {
2806
            return NetSynchRaceStart2(eNet_synch_host_first);
2807
        } else {
2808
            return NetSynchRaceStart2(eNet_synch_host_subsequent);
2809
        }
2810
    } else {
2811
        return NetSynchRaceStart2(eNet_synch_client);
2812
    }
2813
}