Subversion Repositories Games.Carmageddon

Rev

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

Rev Author Line No. Line
1 pmbaty 1
#include "netgame.h"
18 pmbaty 2
#include "brender.h"
3
#include "brucetrk.h"
1 pmbaty 4
#include "car.h"
18 pmbaty 5
#include "controls.h"
6
#include "crush.h"
1 pmbaty 7
#include "displays.h"
8
#include "errors.h"
9
#include "globvars.h"
10
#include "globvrpb.h"
11
#include "grafdata.h"
12
#include "graphics.h"
13
#include "harness/trace.h"
14
#include "loading.h"
15
#include "network.h"
16
#include "newgame.h"
17
#include "opponent.h"
18
#include "pd/sys.h"
19
#include "pedestrn.h"
20
#include "powerup.h"
18 pmbaty 21
#include "pratcam.h"
1 pmbaty 22
#include "racestrt.h"
18 pmbaty 23
#include "spark.h"
1 pmbaty 24
#include "structur.h"
25
#include "utility.h"
18 pmbaty 26
#include <limits.h>
1 pmbaty 27
#include <stdlib.h>
28
#include <string.h>
29
 
30
int gPowerup_cost[4] = { 1500, 2500, 4000, 6000 };
18 pmbaty 31
int gGame_scores[6] = { 1, 2, 3, 4, 6, 10 };
1 pmbaty 32
int gPed_target;
33
int gNot_shown_race_type_headup;
34
tU32 gLast_it_change;
35
tU32 gTime_for_punishment;
36
tNet_game_player_info* gLast_lepper;
37
int gInitialised_grid;
38
int gIt_or_fox;
39
 
18 pmbaty 40
#define PACK_POWERUPS(car) (car->power_up_levels[0] & 0xff) + ((car->power_up_levels[2] & 0xff) << 6) + ((car->power_up_levels[1] & 0xff) << 3);
41
 
1 pmbaty 42
// IDA: void __usercall SendCarData(tU32 pNext_frame_time@<EAX>)
43
void SendCarData(tU32 pNext_frame_time) {
18 pmbaty 44
    tNet_contents* contents;
45
    tCar_spec* car;
46
    tCollision_info* ncar;
47
    int i;
48
    int j;
49
    static tU32 last_time;
50
    tU32 time;
51
    int damaged_wheels;
1 pmbaty 52
    LOG_TRACE("(%d)", pNext_frame_time);
53
 
18 pmbaty 54
    time = GetRaceTime();
55
    if (gNet_mode == eNet_mode_none || (time > last_time && last_time + 80 > time)) {
56
        return;
1 pmbaty 57
    }
18 pmbaty 58
    last_time = time;
59
    contents = NetGetBroadcastContents(NETMSGID_TIMESYNC, 0);
60
    contents->data.time_sync.race_start_time = gRace_start;
61
 
62
    if (gNet_mode == eNet_mode_host) {
63
        for (i = 0; i < gNumber_of_net_players; i++) {
64
            car = gNet_players[i].car;
65
            if (car->disabled) {
66
                continue;
67
            }
68
            damaged_wheels = car->damage_units[eDamage_lf_wheel].damage_level > 30 || car->damage_units[eDamage_rf_wheel].damage_level > 30 || car->damage_units[eDamage_lr_wheel].damage_level > 30 || car->damage_units[eDamage_rr_wheel].damage_level > 30;
69
            contents = NetGetBroadcastContents(NETMSGID_MECHANICS, damaged_wheels);
70
            GetReducedMatrix(&contents->data.mech.mat, &car->car_master_actor->t.t.mat);
71
            contents->data.mech.ID = gNet_players[i].ID;
72
            contents->data.mech.time = pNext_frame_time;
73
            BrVector3Copy(&contents->data.mech.omega, &car->omega);
74
            BrVector3Copy(&contents->data.mech.v, &car->v);
75
            contents->data.mech.curvature = (car->curvature / car->maxcurve * 32767.0f);
76
            contents->data.mech.keys = car->keys;
77
            contents->data.mech.keys.joystick_acc = (tU8)(car->joystick.acc >> 9);
78
            contents->data.mech.keys.joystick_dec = (tU8)(car->joystick.dec >> 9);
79
            contents->data.mech.revs = car->revs;
80
            for (j = 0; j < COUNT_OF(contents->data.mech.d); j++) {
81
                contents->data.mech.d[j] = (int)(car->oldd[j] / car->susp_height[j >> 1] * 255.0f);
82
            }
83
            for (j = 0; j < COUNT_OF(contents->data.mech.damage); j++) {
84
                contents->data.mech.damage[j] = car->damage_units[j].damage_level;
85
            }
86
            contents->data.mech.front = car->bounds[1].min.v[2];
87
            contents->data.mech.back = car->bounds[1].max.v[2];
88
            contents->data.mech.powerups = PACK_POWERUPS(car);
89
            contents->data.mech.repair_time = car->repair_time;
90
            contents->data.mech.cc_coll_time = car->last_car_car_collision;
91
            if (damaged_wheels) {
92
                for (j = 0; j < COUNT_OF(contents->data.mech.wheel_dam_offset); j++) {
93
                    contents->data.mech.wheel_dam_offset[j] = car->wheel_dam_offset[j];
94
                }
95
            }
96
            if (car->time_to_recover != 0) {
97
                if (car->time_to_recover - 500 < pNext_frame_time) {
98
                    contents = NetGetBroadcastContents(NETMSGID_RECOVER, 0);
99
                    contents->data.recover.ID = gNet_players[i].ID;
100
                    contents->data.recover.time_to_recover = car->time_to_recover;
101
                }
102
            }
103
        }
104
        for (i = 0; i < gNum_active_non_cars; i++) {
105
            contents = NetGetBroadcastContents(NETMSGID_NONCAR_INFO, 0);
106
            ncar = (tCollision_info*)gActive_non_car_list[i];
107
            GetReducedMatrix(&contents->data.mech.mat, &ncar->car_master_actor->t.t.mat);
108
            contents->data.non_car.ID = ncar->car_ID;
109
            contents->data.non_car.time = pNext_frame_time;
110
            BrVector3Copy(&contents->data.non_car.omega, &ncar->omega);
111
            BrVector3Copy(&contents->data.non_car.v, &ncar->v);
112
            contents->data.non_car.flags = ncar->car_master_actor->identifier[3] == 2 * ncar->doing_nothing_flag + '!';
113
        }
114
        for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
115
            if (!gProgram_state.AI_vehicles.cops[i].finished_for_this_race) {
116
                contents = NetGetBroadcastContents(NETMSGID_COPINFO, 0);
117
                car = gProgram_state.AI_vehicles.cops[i].car_spec;
118
                GetReducedMatrix(&contents->data.mech.mat, &car->car_master_actor->t.t.mat);
119
                contents->data.cop_info.ID = car->car_ID;
120
                contents->data.cop_info.time = pNext_frame_time;
121
                BrVector3Copy(&contents->data.cop_info.omega, &car->omega);
122
                BrVector3Copy(&contents->data.cop_info.v, &car->v);
123
                for (j = 0; j < COUNT_OF(contents->data.cop_info.damage); ++j) {
124
                    contents->data.cop_info.damage[j] = car->damage_units[j].damage_level;
125
                }
126
                for (j = 0; j < COUNT_OF(contents->data.cop_info.d); j++) {
127
                    contents->data.cop_info.d[j] = car->oldd[j];
128
                }
129
            }
130
        }
131
    } else if (gNet_mode == eNet_mode_client) {
132
        car = &gProgram_state.current_car;
133
        if (car->disabled) {
134
            return;
135
        }
136
        damaged_wheels = car->damage_units[eDamage_lf_wheel].damage_level > 30 || car->damage_units[eDamage_rf_wheel].damage_level > 30 || car->damage_units[eDamage_lr_wheel].damage_level > 30 || car->damage_units[eDamage_rr_wheel].damage_level > 30;
137
        contents = NetGetToHostContents(NETMSGID_MECHANICS, damaged_wheels);
138
        GetReducedMatrix(&contents->data.mech.mat, &gProgram_state.current_car.car_master_actor->t.t.mat);
139
        contents->data.mech.ID = gNet_players[gThis_net_player_index].ID;
140
        contents->data.mech.time = pNext_frame_time;
141
        BrVector3Copy(&contents->data.mech.omega, &car->omega);
142
        BrVector3Copy(&contents->data.mech.v, &car->v);
143
 
144
        contents->data.mech.curvature = (car->curvature / car->maxcurve * 32767.0f);
145
        contents->data.mech.keys = car->keys;
146
        contents->data.mech.keys.joystick_acc = (tU8)(car->joystick.acc >> 9);
147
        contents->data.mech.keys.joystick_dec = (tU8)(car->joystick.dec >> 9);
148
        contents->data.mech.revs = car->revs;
149
        contents->data.mech.cc_coll_time = car->last_car_car_collision;
150
        for (j = 0; j < COUNT_OF(contents->data.mech.d); j++) {
151
            contents->data.mech.d[j] = (int)(car->oldd[j] / car->susp_height[j >> 1] * 255.f);
152
        }
153
        for (j = 0; j < COUNT_OF(contents->data.mech.damage); j++) {
154
            contents->data.mech.damage[j] = car->damage_units[j].damage_level;
155
        }
156
        contents->data.mech.front = car->bounds[1].min.v[2];
157
        contents->data.mech.back = car->bounds[1].max.v[2];
158
        contents->data.mech.powerups = PACK_POWERUPS(car);
159
        contents->data.mech.repair_time = car->repair_time;
160
        if (damaged_wheels) {
161
            for (j = 0; j < COUNT_OF(contents->data.mech.wheel_dam_offset); j++) {
162
                contents->data.mech.wheel_dam_offset[j] = car->wheel_dam_offset[j];
163
            }
164
        }
165
        if (car->time_to_recover > 0 && car->time_to_recover - 500 < pNext_frame_time) {
166
            contents = NetGetToHostContents(NETMSGID_RECOVER, 0);
167
            contents->data.recover.ID = gNet_players[gThis_net_player_index].ID;
168
            contents->data.recover.time_to_recover = gProgram_state.current_car.time_to_recover;
169
        }
170
    }
1 pmbaty 171
}
172
 
173
// IDA: void __usercall ReceivedRecover(tNet_contents *pContents@<EAX>)
174
void ReceivedRecover(tNet_contents* pContents) {
18 pmbaty 175
    int i;
1 pmbaty 176
    LOG_TRACE("(%p)", pContents);
18 pmbaty 177
 
178
    if (gNet_players[gThis_net_player_index].ID != pContents->data.player_list.number_of_players) {
179
        for (i = 0; i < gNumber_of_net_players; i++) {
180
            if (gNet_players[i].ID == pContents->data.player_list.number_of_players) {
181
                gNet_players[i].car->time_to_recover = pContents->data.mech.time;
182
            }
183
        }
184
    }
1 pmbaty 185
}
186
 
187
// IDA: void __usercall CopyMechanics(tCar_spec *pCar@<EAX>, tNet_contents *pContents@<EDX>)
188
void CopyMechanics(tCar_spec* pCar, tNet_contents* pContents) {
18 pmbaty 189
    int j;
1 pmbaty 190
    LOG_TRACE("(%p, %p)", pCar, pContents);
18 pmbaty 191
 
192
    memcpy(&pCar->message, pContents, pContents->header.contents_size);
193
    // if it is not a full mechanics message...
194
    if (pContents->header.contents_size != sizeof(tNet_message_mechanics_info)) {
195
        for (j = 0; j < COUNT_OF(pCar->message.wheel_dam_offset); j++) {
196
            pCar->message.wheel_dam_offset[j] = 0.0f;
197
        }
198
    }
1 pmbaty 199
}
200
 
201
// IDA: void __usercall ReceivedMechanics(tNet_contents *pContents@<EAX>)
202
void ReceivedMechanics(tNet_contents* pContents) {
18 pmbaty 203
    int i;
204
    tCar_spec* car;
1 pmbaty 205
    LOG_TRACE("(%p)", pContents);
18 pmbaty 206
 
207
    car = NULL;
208
    for (i = 0; i < gNumber_of_net_players; i++) {
209
        if (gNet_players[i].ID == pContents->data.mech.ID) {
210
            car = gNet_players[i].car;
211
            break;
212
        }
213
    }
214
    if (car == NULL || car->message.time >= pContents->data.mech.time) {
215
        return;
216
    }
217
    if (car->disabled) {
218
        EnableCar(car);
219
        GetExpandedMatrix(&car->car_master_actor->t.t.mat, &pContents->data.mech.mat);
220
        BrMatrix34Copy(&car->oldmat, &car->car_master_actor->t.t.mat);
221
        BrVector3InvScale(&car->car_master_actor->t.t.translate.t, &car->car_master_actor->t.t.translate.t, WORLD_SCALE);
222
        // car->car_master_actor->t.t.mat.m[3][0] = car->car_master_actor->t.t.mat.m[3][0] * 0.14492753;
223
        // car->car_master_actor->t.t.mat.m[3][1] = car->car_master_actor->t.t.mat.m[3][1] * 0.14492753;
224
        // car->car_master_actor->t.t.mat.m[3][2] = car->car_master_actor->t.t.mat.m[3][2] * 0.14492753;
225
        car->box_face_ref = gFace_num__car - 2;
226
        car->message.time = pContents->data.mech.time;
227
        car->message.type = NETMSGID_NONE;
228
        BrVector3Copy(&car->v, &pContents->data.mech.v);
229
        BrVector3Copy(&car->omega, &pContents->data.mech.omega);
230
 
231
        for (i = 0; i < COUNT_OF(car->message.d); i++) {
232
            car->message.d[i] = pContents->data.mech.d[i];
233
        }
234
        for (i = 0; i < COUNT_OF(car->message.damage); i++) {
235
            car->message.damage[i] = pContents->data.mech.damage[i];
236
        }
237
        DisableCar(car);
238
        return;
239
    }
240
    if (gNet_mode == eNet_mode_host) {
241
        if (gThis_net_player_index == i) {
242
            return;
243
        }
244
        if (car->last_car_car_collision <= pContents->data.mech.cc_coll_time) {
245
            CopyMechanics(car, pContents);
246
        }
247
        car->power_up_levels[0] = pContents->data.mech.powerups & 7;
248
        car->power_up_levels[1] = (pContents->data.mech.powerups >> 3) & 7;
249
        car->power_up_levels[2] = (pContents->data.mech.powerups >> 6) & 7;
250
        car->keys = car->message.keys;
251
        if (car->message.keys.joystick_acc < 0) {
252
            car->joystick.acc = -1;
253
        } else {
254
            car->joystick.acc = car->message.keys.joystick_acc << 9;
255
        }
256
        if (car->message.keys.joystick_dec < 0) {
257
            car->joystick.dec = -1;
258
        } else {
259
            car->joystick.dec = car->message.keys.joystick_dec << 9;
260
        }
261
    } else if (gNet_mode == eNet_mode_client) {
262
        if (gThis_net_player_index == i) {
263
            if (car->last_car_car_collision < pContents->data.mech.cc_coll_time) {
264
                CopyMechanics(car, pContents);
265
            }
266
        } else {
267
            CopyMechanics(car, pContents);
268
            car->power_up_levels[0] = pContents->data.mech.powerups & 7;
269
            car->power_up_levels[1] = (pContents->data.mech.powerups >> 3) & 7;
270
            car->power_up_levels[2] = (pContents->data.mech.powerups >> 6) & 7;
271
            car->keys = car->message.keys;
272
            if (car->message.keys.joystick_acc < 0) {
273
                car->joystick.acc = -1;
274
            } else {
275
                car->joystick.acc = car->message.keys.joystick_acc << 9;
276
            }
277
            if (car->message.keys.joystick_dec < 0) {
278
                car->joystick.dec = -1;
279
            } else {
280
                car->joystick.dec = car->message.keys.joystick_dec << 9;
281
            }
282
            if (!car->active) {
283
                GetExpandedMatrix(&car->car_master_actor->t.t.mat, &pContents->data.mech.mat);
284
                BrMatrix34Copy(&car->oldmat, &car->car_master_actor->t.t.mat);
285
                BrMatrix34ApplyP(&car->pos, &car->cmpos, &car->car_master_actor->t.t.mat);
286
                BrVector3InvScale(&car->car_master_actor->t.t.translate.t, &car->car_master_actor->t.t.translate.t, WORLD_SCALE);
287
                BrVector3InvScale(&car->pos, &car->pos, WORLD_SCALE);
288
                car->box_face_ref = gFace_num__car - 2;
289
            }
290
        }
291
    }
1 pmbaty 292
}
293
 
294
// IDA: void __usercall ReceivedCopInfo(tNet_contents *pContents@<EAX>)
295
void ReceivedCopInfo(tNet_contents* pContents) {
18 pmbaty 296
    tCar_spec* c;
297
    int i;
1 pmbaty 298
    LOG_TRACE("(%p)", pContents);
18 pmbaty 299
 
300
    if (gNet_mode != eNet_mode_client) {
301
        return;
302
    }
303
    if (pContents->data.cop_info.ID & 0xffffff00) {
304
        c = GetCarSpec(pContents->data.cop_info.ID >> 8, pContents->data.cop_info.ID & 0xff);
305
    } else {
306
        c = &gProgram_state.current_car;
307
    }
308
    if (c == NULL || c->message.time > pContents->data.cop_info.time) {
309
        return;
310
    }
311
    c->message.time = pContents->data.cop_info.time;
312
    if (c->active) {
313
        c->message.type = NETMSGID_MECHANICS;
314
        c->message.mat = pContents->data.cop_info.mat;
315
        BrVector3Copy(&c->message.v, &pContents->data.cop_info.v);
316
        BrVector3Copy(&c->message.omega, &pContents->data.cop_info.omega);
317
        c->message.curvature = (tS16)pContents->data.cop_info.curvature;
318
        for (i = 0; i < COUNT_OF(c->message.d); i++) {
319
            c->message.d[i] = pContents->data.cop_info.d[i];
320
        }
321
        for (i = 0; i < COUNT_OF(c->message.damage); i++) {
322
            c->message.damage[i] = pContents->data.cop_info.damage[i];
323
        }
324
    } else {
325
        GetExpandedMatrix(&c->car_master_actor->t.t.mat, &pContents->data.cop_info.mat);
326
        BrVector3InvScale(&c->car_master_actor->t.t.translate.t, &c->car_master_actor->t.t.translate.t, WORLD_SCALE);
327
        for (i = 0; i < COUNT_OF(c->damage_units); i++) {
328
            c->damage_units[i].damage_level = pContents->data.cop_info.damage[i];
329
        }
330
    }
1 pmbaty 331
}
332
 
333
// IDA: void __cdecl SendAllNonCarPositions()
334
void SendAllNonCarPositions(void) {
335
    int i;
336
    br_actor** list;
337
    tNon_car_spec* non_car;
338
    tNet_contents* contents;
339
    LOG_TRACE("()");
340
 
341
    list = gProgram_state.track_spec.non_car_list;
342
    for (i = 0; i < gProgram_state.track_spec.ampersand_digits; ++i) {
343
        if (list[i]->type_data != NULL) {
344
            non_car = (tNon_car_spec*)list[i]->type_data;
345
            if (non_car->collision_info.driver == eDriver_non_car_unused_slot || non_car->collision_info.car_ID != i) {
346
                contents = NetGetBroadcastContents(NETMSGID_NONCARPOSITION, 0);
347
                BrMatrix34Copy(&contents->data.non_car_position.mat, &list[i]->t.t.mat);
348
                contents->data.non_car_position.ID = i;
349
                contents->data.non_car_position.flags = list[i]->identifier[3] == '!';
350
            }
351
        }
352
    }
353
    NetSendMessageStacks();
354
}
355
 
356
// IDA: void __usercall ReceivedNonCarPosition(tNet_contents *pContents@<EAX>)
357
void ReceivedNonCarPosition(tNet_contents* pContents) {
18 pmbaty 358
    br_actor* actor;
1 pmbaty 359
    LOG_TRACE("(%p)", pContents);
18 pmbaty 360
 
361
    actor = gProgram_state.track_spec.non_car_list[pContents->data.non_car_position.ID];
362
    if (actor != NULL && gNet_mode != eNet_mode_none) {
363
        BrMatrix34Copy(&actor->t.t.mat, &pContents->data.non_car_position.mat);
364
        if (pContents->data.non_car_position.flags) {
365
            actor->identifier[3] = '!';
366
        }
367
    }
1 pmbaty 368
}
369
 
370
// IDA: void __usercall ReceivedNonCar(tNet_contents *pContents@<EAX>)
371
void ReceivedNonCar(tNet_contents* pContents) {
18 pmbaty 372
    br_actor* actor;
373
    br_vector3 tv;
374
    tU8 cx;
375
    tU8 cz;
376
    tTrack_spec* track_spec;
377
    tNon_car_spec* ncar;
378
    tCollision_info* c;
1 pmbaty 379
    LOG_TRACE("(%p)", pContents);
18 pmbaty 380
 
381
    track_spec = &gProgram_state.track_spec;
382
    if (pContents->data.non_car.ID >= track_spec->ampersand_digits) {
383
        return;
384
    }
385
    actor = track_spec->non_car_list[pContents->data.non_car.ID];
386
    if (actor == NULL) {
387
        return;
388
    }
389
    ncar = (tNon_car_spec*)actor->type_data;
390
    if (ncar != NULL && (ncar->collision_info.driver != eDriver_non_car || ncar->collision_info.car_ID != pContents->data.non_car.ID)) {
391
        ncar = NULL;
392
    }
393
    if ((pContents->data.non_car.flags & 1) != 0) {
394
        actor->identifier[3] = '!';
395
    } else {
396
        actor->identifier[3] = 'x';
397
    }
398
    if (ncar == NULL && actor->identifier[1] >= '0' && actor->identifier[1] <= '9') {
399
        BrVector3Sub(&tv, &gProgram_state.current_car.car_master_actor->t.t.translate.t, &actor->t.t.translate.t);
400
        if (BrVector3LengthSquared(&tv) < 900.0f) {
401
            DoPullActorFromWorld(actor);
402
            ncar = (tNon_car_spec*)actor->type_data;
403
        }
404
    }
405
    if (ncar != NULL) {
406
        c = &ncar->collision_info;
407
        if ((pContents->data.non_car.flags & 2) != 0) {
408
            GetExpandedMatrix(&c->car_master_actor->t.t.mat, &pContents->data.non_car.mat);
409
            BrVector3Copy(&c->v, &pContents->data.non_car.v);
410
            BrVector3Copy(&c->omega, &pContents->data.non_car.omega);
411
            c->doing_nothing_flag = 1;
412
        } else {
413
            BrVector3Copy(&c->message.v, &pContents->data.non_car.v);
414
            BrVector3Copy(&c->message.omega, &pContents->data.non_car.omega);
415
            memcpy(&c->message.mat, &pContents->data.non_car.mat, sizeof(c->message.mat));
416
            c->message.time = pContents->data.non_car.time;
417
            c->message.type = NETMSGID_NONCAR_INFO;
418
            c->doing_nothing_flag = 0;
419
        }
420
    } else {
421
        GetExpandedMatrix(&actor->t.t.mat, &pContents->data.mech.mat);
422
        BrVector3InvScale(&actor->t.t.translate.t, &actor->t.t.translate.t, WORLD_SCALE);
423
        XZToColumnXZ(&cx, &cz, actor->t.t.translate.t.v[0], actor->t.t.translate.t.v[2], track_spec);
424
        if (track_spec->columns[cz][cx] != actor->parent) {
425
            if (track_spec->columns[cz][cx] != NULL) {
426
                BrActorRemove(actor);
427
                BrActorAdd(track_spec->columns[cz][cx], actor);
428
            }
429
        }
430
    }
1 pmbaty 431
}
432
 
433
// IDA: void __usercall SignalToStartRace2(int pIndex@<EAX>)
434
void SignalToStartRace2(int pIndex) {
435
    tNet_message* the_message;
436
    int i;
437
    int j;
438
    LOG_TRACE("(%d)", pIndex);
439
 
440
    if (gCurrent_race.number_of_racers > 6) {
441
        FadePaletteUp();
442
        dr_dprintf("AAAARRRGGGHHH!!!! More than 6 racers!!!!");
443
        PDFatalError("AAAARRRGGGHHH!!!! More than 6 racers!!!!");
444
    }
445
    gNeed_to_send_start_race = 0;
446
    gStart_race_sent = 1;
447
    the_message = NetBuildMessage(NETMSGID_STARTRACE, 0);
448
    the_message->contents.data.start_race.racing = gProgram_state.racing;
449
    if (pIndex >= 0) {
450
        gNet_players[pIndex].last_waste_message = 0;
451
        gNet_players[pIndex].wasteage_attributed = 0;
452
        the_message->contents.data.start_race.car_count = -1;
453
        the_message->contents.data.start_race.car_list[0].index = pIndex;
454
        BrMatrix34Copy(&the_message->contents.data.start_race.car_list[0].mat,
455
            &gCurrent_race.opponent_list[gNet_players[pIndex].opponent_list_index].car_spec->car_master_actor->t.t.mat);
456
    } else {
457
        the_message->contents.data.start_race.car_count = gCurrent_race.number_of_racers;
458
        for (i = 0; i < gCurrent_race.number_of_racers; i++) {
459
            BrMatrix34Copy(&the_message->contents.data.start_race.car_list[i].mat,
460
                &gCurrent_race.opponent_list[i].car_spec->car_master_actor->t.t.mat);
461
            for (j = 0; j < gNumber_of_net_players; j++) {
462
                if (gCurrent_race.opponent_list[i].car_spec == gNet_players[j].car) {
463
                    the_message->contents.data.start_race.car_list[i].index = j;
464
                    break;
465
                }
466
            }
467
            if (gCurrent_net_game->options.random_car_choice && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)) {
468
                if (gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index < 0) {
469
                    gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index = PickARandomCar();
470
                    gCar_details[gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index].ownership = eCar_owner_someone;
471
                }
472
                the_message->contents.data.start_race.car_list[i].next_car_index = gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index;
473
            }
474
        }
475
        if (gPending_race < 0) {
476
            gPending_race = PickNetRace(gProgram_state.current_race_index,
477
                gCurrent_net_game->options.race_sequence_type);
478
        }
479
        the_message->contents.data.start_race.next_race = gPending_race;
480
    }
481
    NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
482
    if (gProgram_state.racing) {
483
        SendCurrentPowerups();
484
    }
485
}
486
 
487
// IDA: void __cdecl SignalToStartRace()
488
void SignalToStartRace(void) {
489
    LOG_TRACE("()");
490
 
491
    gCurrent_net_game->no_races_yet = 0;
492
    SignalToStartRace2(-1);
493
}
494
 
495
// IDA: void __cdecl SetUpNetCarPositions()
496
void SetUpNetCarPositions(void) {
497
    int i;
498
    //int j; // Pierre-Marie Baty -- unused variable
499
    int k;
500
    int grid_index;
501
    int racer_count;
502
    LOG_TRACE("()");
503
 
504
    DisableNetService();
505
    if (!gInitialised_grid) {
506
        for (i = 0; i < gNumber_of_net_players; i++) {
507
            gNet_players[i].grid_position_set = 0;
508
            gNet_players[i].last_waste_message = 0;
509
            gNet_players[i].wasteage_attributed = 0;
510
        }
511
    }
512
    for (i = 0; i < gNumber_of_net_players; i++) {
513
        gCurrent_race.opponent_list[i].index = -1;
514
        gCurrent_race.opponent_list[i].ranking = IRandomBetween(0, 99);
515
        gCurrent_race.opponent_list[i].car_spec = gNet_players[i].car;
516
        gCurrent_race.opponent_list[i].net_player_index = i;
517
        gNet_players[i].opponent_list_index = i;
518
    }
519
    if (!gInitialised_grid && gCurrent_net_game->options.grid_start) {
520
        qsort(gCurrent_race.opponent_list, gNumber_of_net_players, sizeof(tOpp_spec), SortGridFunction);
521
    }
522
    gCurrent_race.number_of_racers = 0;
523
    for (i = 0; i < gNumber_of_net_players; i++) {
524
        gNet_players[gCurrent_race.opponent_list[i].net_player_index].opponent_list_index = i;
525
    }
526
    for (i = 0; i < gNumber_of_net_players; i++) {
527
        if ((gCurrent_race.opponent_list[i].car_spec->driver == eDriver_oppo && !gInitialised_grid)
528
            || (gCurrent_race.opponent_list[i].car_spec->driver >= eDriver_net_human && !gNet_players[gCurrent_race.opponent_list[i].net_player_index].grid_position_set)) {
529
            grid_index = -1;
530
            racer_count = 0;
531
            while (racer_count < 6 && grid_index < 0) {
532
                grid_index = racer_count;
533
                for (k = 0; k < gNumber_of_net_players; k++) {
534
                    if (k != i
535
                        && gNet_players[gCurrent_race.opponent_list[k].net_player_index].grid_position_set
536
                        && gNet_players[gCurrent_race.opponent_list[k].net_player_index].grid_index == racer_count) {
537
                        grid_index = -1;
538
                        break;
539
                    }
540
                }
541
                racer_count++;
542
            }
543
            if (grid_index < 0) {
544
                FatalError(kFatalError_NetworkCodeSelfCheck);
545
            }
546
            SetInitialPosition(&gCurrent_race, i, grid_index);
547
            gNet_players[gCurrent_race.opponent_list[i].net_player_index].grid_index = grid_index;
548
            if (gInitialised_grid) {
549
                InitialiseCar2(gCurrent_race.opponent_list[i].car_spec, 0);
550
            } else {
551
                gCurrent_race.number_of_racers = i + 1;
552
            }
553
            gNet_players[gCurrent_race.opponent_list[i].net_player_index].grid_position_set = 1;
554
        }
555
    }
556
    gCurrent_race.number_of_racers = gNumber_of_net_players;
557
    gInitialised_grid = 1;
558
    ReenableNetService();
559
}
560
 
561
// IDA: void __usercall ReinitialiseCar(tCar_spec *pCar@<EAX>)
562
void ReinitialiseCar(tCar_spec* pCar) {
18 pmbaty 563
    int i;
1 pmbaty 564
    LOG_TRACE("(%p)", pCar);
18 pmbaty 565
 
566
    StopCarSmokingInstantly(pCar);
567
    LoseAllLocalPowerups(pCar);
568
    InitialiseCar(pCar);
569
    TotallyRepairACar(pCar);
570
    if (pCar->driver == eDriver_local_human) {
571
        gLast_it_change = PDGetTotalTime() + 2000;
572
    }
1 pmbaty 573
}
574
 
575
// IDA: void __usercall RepositionPlayer(int pIndex@<EAX>)
576
void RepositionPlayer(int pIndex) {
18 pmbaty 577
    tNet_message* the_message;
578
    tCar_spec* car;
1 pmbaty 579
    LOG_TRACE("(%d)", pIndex);
18 pmbaty 580
 
581
    car = gNet_players[pIndex].car;
582
    gNet_players[pIndex].wasted = 0;
583
    gNet_players[pIndex].reposition_time = 0;
584
    if (gCurrent_net_game->type == eNet_game_type_carnage
585
        || gCurrent_net_game->type == eNet_game_type_checkpoint
586
        || gCurrent_net_game->type == eNet_game_type_sudden_death) {
587
        BrMatrix34Copy(&car->car_master_actor->t.t.mat, &gNet_players[pIndex].initial_position);
588
    } else {
589
        SetInitialPosition(&gCurrent_race, gNet_players[pIndex].opponent_list_index, gNet_players[pIndex].grid_index);
590
    }
591
    ReinitialiseCar(car);
592
    SetFlipUpCar(car);
593
    car->last_car_car_collision = GetRaceTime() + 100;
594
    SignalToStartRace2(pIndex);
595
    if (car->driver == eDriver_local_human) {
596
        CancelPendingCunningStunt();
597
        gProgram_state.credits_earned = gInitial_net_credits[gCurrent_net_game->options.starting_money_index];
598
        gProgram_state.credits_lost = 0;
599
    }
1 pmbaty 600
}
601
 
602
// IDA: void __usercall DisableCar(tCar_spec *pCar@<EAX>)
603
void DisableCar(tCar_spec* pCar) {
604
    LOG_TRACE("(%p)", pCar);
605
 
606
    if (pCar->driver_name[0] != '\0') {
607
        if (!pCar->disabled) {
608
            pCar->disabled = 1;
609
            ForceRebuildActiveCarList();
610
        }
611
        if (pCar->car_master_actor->t.t.mat.m[3][0] < 500.0f) {
612
            pCar->car_master_actor->t.t.mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0] + 1000.0f;
613
            pCar->car_master_actor->t.t.mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1] + 1000.0f;
614
            pCar->car_master_actor->t.t.mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2] + 1000.0f;
615
            pCar->old_frame_mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0];
616
            pCar->old_frame_mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1];
617
            pCar->old_frame_mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2];
618
        }
619
    }
620
}
621
 
622
// IDA: void __usercall EnableCar(tCar_spec *pCar@<EAX>)
623
void EnableCar(tCar_spec* pCar) {
624
    LOG_TRACE("(%p)", pCar);
625
 
626
    if (pCar->driver_name[0] != '\0') {
627
        if (pCar->disabled) {
628
            pCar->disabled = 0;
629
            ForceRebuildActiveCarList();
630
        }
631
        if (pCar->car_master_actor->t.t.mat.m[3][0] > 500.0f) {
632
            pCar->car_master_actor->t.t.mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0] - 1000.0f;
633
            pCar->car_master_actor->t.t.mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1] - 1000.0f;
634
            pCar->car_master_actor->t.t.mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2] - 1000.0f;
635
            pCar->old_frame_mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0];
636
            pCar->old_frame_mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1];
637
            pCar->old_frame_mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2];
638
        }
639
    }
640
}
641
 
642
// IDA: void __usercall DoNetworkHeadups(int pCredits@<EAX>)
643
void DoNetworkHeadups(int pCredits) {
644
    char s[256];
645
    char s2[256];
646
    static tU32 last_flash;
647
    static int flash_state;
648
    LOG_TRACE("(%d)", pCredits);
649
 
650
    if (gNot_shown_race_type_headup) {
651
        gNot_shown_race_type_headup = 0;
652
        NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_NetworkGameTypeNames_START + gCurrent_net_game->type));
653
    }
654
    if (gTime_for_punishment && gTime_for_punishment <= (tU32) PDGetTotalTime()) { // Pierre-Marie Baty -- added type cast
655
        gTime_for_punishment = 0;
656
        switch (gCurrent_net_game->type) {
657
        case eNet_game_type_carnage:
658
            NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_THAT_HALVED_YOUR_KILL_COUNT));
659
            break;
660
        case eNet_game_type_checkpoint:
661
            NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_THAT_LOST_YOU_A_CHECKPOINT));
662
            break;
663
        case eNet_game_type_sudden_death:
664
            NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_BACK_TO_THE_START));
665
            break;
666
        case eNet_game_type_foxy:
667
            NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_THAT_HALVED_YOUR_TIME));
668
            break;
669
        default:
670
            break;
671
        }
672
    }
673
    if (gNet_mode == eNet_mode_none || gNet_recovery_cost[gCurrent_net_game->type] <= gProgram_state.credits_earned - gProgram_state.credits_lost || Flash(200, &last_flash, &flash_state)) {
674
        sprintf(s, "\xf8%d\xfa %s", pCredits, GetMiscString(kMiscString_CREDITS));
675
        ChangeHeadupText(gNet_cash_headup, s);
676
    } else {
677
        ChangeHeadupText(gNet_cash_headup, "");
678
    }
679
    switch (gCurrent_net_game->type) {
680
    case eNet_game_type_carnage:
681
        sprintf(s, "%s \xf8%d\xfa", GetMiscString(kMiscString_TARGET_180), gPed_target);
682
        break;
683
    case eNet_game_type_car_crusher:
684
        sprintf(s, "%s \xf8%d\xfa", GetMiscString(kMiscString_TARGET_181), gCurrent_net_game->options.race_end_target);
685
        break;
686
    case eNet_game_type_foxy:
687
        TimerString(gCurrent_net_game->options.race_end_target, s2, 1, 1);
688
        sprintf(s, "%s \xf8%s\xfa", GetMiscString(kMiscString_TARGET_182), s2);
689
        break;
690
    case eNet_game_type_tag:
691
        TimerString(gCurrent_net_game->options.race_end_target, s2, 1, 1);
692
        sprintf(s, "%s \xf8%s\xfa", GetMiscString(kMiscString_LIMIT), s2);
693
        break;
694
    default:
695
        s[0] = '\0';
696
        break;
697
    }
698
    ChangeHeadupText(gNet_ped_headup, s);
699
}
700
 
18 pmbaty 701
#define HEADUP1 ((tHeadup_pair*)pFirst_one)
702
#define HEADUP2 ((tHeadup_pair*)pSecond_one)
703
 
1 pmbaty 704
// IDA: int __usercall SortNetHeadAscending@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
705
int SortNetHeadAscending(void* pFirst_one, void* pSecond_one) {
706
    LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
18 pmbaty 707
 
708
    if (HEADUP1->out_of_game) {
709
        if (HEADUP2->out_of_game) {
710
            return HEADUP1->out_of_game - HEADUP2->out_of_game;
711
        } else {
712
            return INT_MAX;
713
        }
714
    } else if (HEADUP2->out_of_game) {
715
        return -INT_MAX;
716
    } else if (HEADUP2->score == HEADUP1->score) {
717
        return gNet_players[HEADUP1->player_index].last_score_index
718
            - gNet_players[HEADUP2->player_index].last_score_index;
719
    } else {
720
        return HEADUP1->score - HEADUP2->score;
721
    }
1 pmbaty 722
}
723
 
724
// IDA: int __usercall SortNetHeadDescending@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
725
int SortNetHeadDescending(void* pFirst_one, void* pSecond_one) {
726
    LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
18 pmbaty 727
 
728
    if (HEADUP1->out_of_game) {
729
        if (HEADUP2->out_of_game) {
730
            return HEADUP1->out_of_game - HEADUP2->out_of_game;
731
        } else {
732
            return INT_MAX;
733
        }
734
    } else if (HEADUP2->out_of_game) {
735
        return -INT_MAX;
736
    } else if (HEADUP2->score == HEADUP1->score) {
737
        return gNet_players[HEADUP1->player_index].last_score_index
738
            - gNet_players[HEADUP2->player_index].last_score_index;
739
    } else {
740
        return HEADUP2->score - HEADUP1->score;
741
    }
1 pmbaty 742
}
743
 
18 pmbaty 744
#undef HEADUP2
745
#undef HEADUP1
746
 
1 pmbaty 747
// IDA: void __usercall ClipName(char *pName@<EAX>, tDR_font *pFont@<EDX>, int pMax_width@<EBX>)
748
void ClipName(char* pName, tDR_font* pFont, int pMax_width) {
749
    LOG_TRACE("(\"%s\", %p, %d)", pName, pFont, pMax_width);
18 pmbaty 750
 
751
    while (DRTextWidth(pFont, pName) > pMax_width) {
752
        pName[strlen(pName) - 1] = 0;
753
    }
1 pmbaty 754
}
755
 
756
// IDA: void __usercall DoNetScores2(int pOnly_sort_scores@<EAX>)
757
void DoNetScores2(int pOnly_sort_scores) {
18 pmbaty 758
    int i;
759
    int j;
760
    int score;
761
    int flags;
762
    int index;
763
    int right_edge;
764
    int x;
765
    int len;
766
    int ascending_order;
767
    char s[256];
768
    static tU32 last_flash;
769
    static int flash_state;
770
    tHeadup_pair headup_pairs[6];
1 pmbaty 771
    LOG_TRACE("(%d)", pOnly_sort_scores);
18 pmbaty 772
 
773
    ascending_order = gCurrent_net_game->type == eNet_game_type_checkpoint || gCurrent_net_game->type == eNet_game_type_tag;
774
    for (i = 0; i < gNumber_of_net_players; i++) {
775
        if (gNet_players[i].player_status < ePlayer_status_racing) {
776
            headup_pairs[i].player_index = -1;
777
            if (ascending_order) {
778
                headup_pairs[i].score = 1000001;
779
            } else {
780
                headup_pairs[i].score = -1000001;
781
            }
782
            headup_pairs[i].out_of_game = 1000;
783
        } else {
784
            headup_pairs[i].player_index = i;
785
            headup_pairs[i].score = gNet_players[i].score;
786
            if (abs(gNet_players[i].score) != 1000000 && (gNet_players[i].score >= 0 || gCurrent_net_game->type == eNet_game_type_car_crusher)) {
787
                headup_pairs[i].out_of_game = 0;
788
            } else {
789
                headup_pairs[i].out_of_game = gNet_players[i].last_score_index + 1;
790
            }
791
        }
792
    }
793
    for (i = gNumber_of_net_players; i < COUNT_OF(headup_pairs); i++) {
794
        headup_pairs[i].player_index = -1;
795
        if (ascending_order) {
796
            headup_pairs[i].score = 1000001;
797
        } else {
798
            headup_pairs[i].score = -1000001;
799
        }
800
        headup_pairs[i].out_of_game = 1000;
801
    }
802
    qsort(headup_pairs, COUNT_OF(headup_pairs), sizeof(tHeadup_pair), (int (*)(const void*, const void*))(ascending_order ? SortNetHeadAscending : SortNetHeadDescending));
803
 
804
    right_edge = gCurrent_graf_data->net_head_box_x + gCurrent_graf_data->net_head_box_width + 5 * gCurrent_graf_data->net_head_box_pitch;
805
    for (i = 0; i < COUNT_OF(headup_pairs); i++) {
806
        index = headup_pairs[i].player_index;
807
        if (index >= 0) {
808
            gNet_players[index].last_score_index = i;
809
        }
810
        if (pOnly_sort_scores) {
811
            continue;
812
        }
813
        x = gCurrent_graf_data->net_head_box_x + i * gCurrent_graf_data->net_head_box_pitch;
814
        if (gCurrent_graf_data->net_head_box_bot > gProgram_state.current_render_top) {
815
            DimRectangle(gBack_screen, x, gCurrent_graf_data->net_head_box_top, x + gCurrent_graf_data->net_head_box_width, gCurrent_graf_data->net_head_box_bot, 1);
816
        }
817
        if (index >= 0) {
818
            Flash(200, &last_flash, &flash_state);
819
            if (flash_state
820
                || (gCurrent_net_game->type != eNet_game_type_tag && gCurrent_net_game->type != eNet_game_type_foxy)
821
                || index != gIt_or_fox) {
822
                if (gNet_players[index].name_not_clipped) {
823
                    ClipName(gNet_players[index].player_name, &gFonts[6], gCurrent_graf_data->net_head_box_width - gCurrent_graf_data->net_head_name_x_marg - 2);
824
                    gNet_players[index].name_not_clipped = 0;
825
                }
826
                TransDRPixelmapText(gBack_screen, x + gCurrent_graf_data->net_head_name_x_marg, gCurrent_graf_data->net_head_name_y, &gFonts[6], gNet_players[index].player_name, right_edge);
827
            }
828
            if (abs(gNet_players[index].score) == 1000000) {
829
                if (flash_state) {
830
                    strcpy(s, GetMiscString(kMiscString_WASTED_173));
831
                } else {
832
                    s[0] = '\0';
833
                }
834
            } else {
835
                switch (gCurrent_net_game->type) {
836
                case eNet_game_type_fight_to_death:
837
                    sprintf(s, "%d%%", gNet_players[index].score);
838
                    break;
839
                case eNet_game_type_car_crusher:
840
                case eNet_game_type_carnage:
841
                    sprintf(s, "%d", gNet_players[index].score);
842
                    break;
843
                case eNet_game_type_checkpoint:
844
                    sprintf(s, "%d left", gNet_players[index].score >> 16);
845
                    break;
846
                case eNet_game_type_sudden_death:
847
                    if (gNet_players[index].score < 0) {
848
                        if (flash_state) {
849
                            sprintf(s, "%s", GetMiscString(kMiscString_OUT));
850
                        } else {
851
                            s[0] = '\0';
852
                        }
853
                    } else {
854
                        score = gNet_players[index].score;
855
                        sprintf(s, "%s -%d-", GetMiscString(kMiscString_IN), score);
856
                    }
857
                    break;
858
                case eNet_game_type_tag:
859
                case eNet_game_type_foxy:
860
                    if (gNet_players[index].score >= 0) {
861
                        if (index == gIt_or_fox && !flash_state) {
862
                            s[0] = '\0';
863
                        } else {
864
                            TimerString(gNet_players[index].score, s, 0, 1);
865
                        }
866
                    } else {
867
                        sprintf(s, "%s", GetMiscString(kMiscString_OUT));
868
                    }
869
                    break;
870
                default:
871
                    break;
872
                }
873
            }
874
            len = DRTextWidth(&gFonts[6], s);
875
            TransDRPixelmapText(gBack_screen, x + gCurrent_graf_data->net_head_score_x - len, gCurrent_graf_data->net_head_score_y, &gFonts[6], s, right_edge);
876
            DRPixelmapRectangleMaskedCopy(gBack_screen, x + gCurrent_graf_data->net_head_num_x, gCurrent_graf_data->net_head_num_y, gDigits_pix, 0, i * gCurrent_graf_data->net_head_num_height, gDigits_pix->width, gCurrent_graf_data->net_head_num_height);
877
            DRPixelmapRectangleMaskedCopy(gBack_screen, x + gCurrent_graf_data->net_head_icon_x, gCurrent_graf_data->net_head_icon_y, gIcons_pix, 0, gCurrent_graf_data->net_head_icon_height * gNet_players[index].car_index, gIcons_pix->width, gCurrent_graf_data->net_head_icon_height);
878
            if (gNet_players[index].ID == gLocal_net_ID) {
879
                BrPixelmapLine(gBack_screen, x, gCurrent_graf_data->net_head_box_top, x, gCurrent_graf_data->net_head_box_bot - 1, 0);
880
                BrPixelmapLine(gBack_screen, x + gCurrent_graf_data->net_head_box_width - 1, gCurrent_graf_data->net_head_box_top, x + gCurrent_graf_data->net_head_box_width - 1, gCurrent_graf_data->net_head_box_bot - 1, 0);
881
                BrPixelmapLine(gBack_screen, x + 1, gCurrent_graf_data->net_head_box_bot, x + gCurrent_graf_data->net_head_box_width - 2, gCurrent_graf_data->net_head_box_bot, 0);
882
                BrPixelmapLine(gBack_screen, x - 1, gCurrent_graf_data->net_head_box_top, x - 1, gCurrent_graf_data->net_head_box_bot - 1, 255);
883
                BrPixelmapLine(gBack_screen, x + gCurrent_graf_data->net_head_box_width, gCurrent_graf_data->net_head_box_top, x + gCurrent_graf_data->net_head_box_width, gCurrent_graf_data->net_head_box_bot - 1, 255);
884
                BrPixelmapLine(gBack_screen, x + 1, gCurrent_graf_data->net_head_box_bot + 1, x + gCurrent_graf_data->net_head_box_width - 2, gCurrent_graf_data->net_head_box_bot + 1, 255);
885
                BrPixelmapPixelSet(gBack_screen, x, gCurrent_graf_data->net_head_box_bot, 255);
886
                BrPixelmapPixelSet(gBack_screen, x + gCurrent_graf_data->net_head_box_width - 1, gCurrent_graf_data->net_head_box_bot, 255);
887
            }
888
        }
889
    }
1 pmbaty 890
}
891
 
892
// IDA: void __cdecl DoNetScores()
893
void DoNetScores(void) {
894
    LOG_TRACE("()");
18 pmbaty 895
 
896
    DoNetScores2(0);
1 pmbaty 897
}
898
 
899
// IDA: void __cdecl InitNetHeadups()
900
void InitNetHeadups(void) {
901
    LOG_TRACE("()");
902
 
903
    gIcons_pix = LoadPixelmap("CARICONS.PIX");
904
    if (gIcons_pix != NULL) {
905
        BrMapAdd(gIcons_pix);
906
    }
907
    gDigits_pix = LoadPixelmap("HDIGITS.PIX");
908
    if (gDigits_pix != NULL) {
909
        BrMapAdd(gDigits_pix);
910
    }
911
    /* The Windows version does not use gIcons_pix_low_res. */
912
    if (gGraf_data_index != 0) {
913
        SwitchToLoresMode();
914
        gIcons_pix_low_res = LoadPixelmap("CARICONS.PIX");
915
        SwitchToRealResolution();
916
    } else {
917
        gIcons_pix_low_res = gIcons_pix;
918
    }
919
}
920
 
921
// IDA: void __cdecl DisposeNetHeadups()
922
void DisposeNetHeadups(void) {
923
    LOG_TRACE("()");
924
 
925
    /* Windows version does not use gIcons_pix_low_res. */
926
    if (gIcons_pix_low_res != NULL && gIcons_pix_low_res != gIcons_pix) {
927
        BrPixelmapFree(gIcons_pix_low_res);
928
    }
929
 
930
    if (gIcons_pix != NULL) {
931
        BrMapRemove(gIcons_pix);
932
        BrPixelmapFree(gIcons_pix);
933
    }
934
    if (gDigits_pix != NULL) {
935
        BrMapRemove(gDigits_pix);
936
        BrPixelmapFree(gDigits_pix);
937
    }
938
}
939
 
940
// IDA: void __cdecl EverybodysLost()
941
void EverybodysLost(void) {
18 pmbaty 942
    tNet_message* the_message;
943
    int i;
1 pmbaty 944
    LOG_TRACE("()");
18 pmbaty 945
 
946
    for (i = 0; i < gNumber_of_net_players; i++) {
947
        gNet_players[i].played += 1;
948
        the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
949
        the_message->contents.data.race_over.reason = eRace_over_network_loss;
950
        NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, gNet_players[i].ID, NULL);
951
    }
1 pmbaty 952
}
953
 
954
// IDA: void __usercall DeclareWinner(int pWinner_index@<EAX>)
955
void DeclareWinner(int pWinner_index) {
18 pmbaty 956
    tNet_message* the_message;
957
    int i;
958
    int j;
959
    int best_score_index;
960
    char s[256];
1 pmbaty 961
    LOG_TRACE("(%d)", pWinner_index);
18 pmbaty 962
 
963
    DoNetScores2(1);
964
    the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
965
    the_message->contents.data.race_over.reason = eRace_over_network_victory;
966
    NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, gNet_players[pWinner_index].ID, NULL);
967
    gNet_players[pWinner_index].won++;
968
    if (gCurrent_net_game->type == eNet_game_type_sudden_death
969
        || gCurrent_net_game->type == eNet_game_type_tag
970
        || gCurrent_net_game->type == eNet_game_type_fight_to_death) {
971
        gNet_players[pWinner_index].games_score += gGame_scores[5];
972
    }
973
    sprintf(s, "%s %s", gNet_players[pWinner_index].player_name, GetMiscString(kMiscString_IS_THE_WINNER));
974
    for (i = 0; i < gNumber_of_net_players; i++) {
975
        if (gCurrent_net_game->type != eNet_game_type_sudden_death && gCurrent_net_game->type != eNet_game_type_tag && gCurrent_net_game->type != eNet_game_type_fight_to_death) {
976
            best_score_index = gNet_players[i].last_score_index;
977
            for (j = 0; j < gNumber_of_net_players; j++) {
978
                if (gNet_players[j].score == gNet_players[i].score && gNet_players[j].last_score_index < best_score_index) {
979
                    best_score_index = gNet_players[j].last_score_index;
980
                }
981
            }
982
            gNet_players[i].games_score += gGame_scores[5 - best_score_index];
983
        }
984
        gNet_players[i].played++;
985
        if (i != pWinner_index) {
986
            the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
987
            the_message->contents.data.race_over.reason = eRace_over_network_loss;
988
            NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, gNet_players[i].ID, NULL);
989
            NetSendHeadupToPlayer(s, gNet_players[i].ID);
990
        }
991
    }
992
    gReceived_game_scores = 1;
993
    the_message = NetBuildMessage(NETMSGID_GAMESCORES, 0);
994
    for (i = 0; i < gNumber_of_net_players; i++) {
995
        the_message->contents.data.game_scores.scores[i].played = gNet_players[i].played;
996
        the_message->contents.data.game_scores.scores[i].won = gNet_players[i].won;
997
        the_message->contents.data.game_scores.scores[i].score = gNet_players[i].games_score;
998
    }
999
    NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
1 pmbaty 1000
}
1001
 
1002
// IDA: void __usercall PlayerIsIt(tNet_game_player_info *pPlayer@<EAX>)
1003
void PlayerIsIt(tNet_game_player_info* pPlayer) {
18 pmbaty 1004
    int i;
1005
    char s[256];
1 pmbaty 1006
    LOG_TRACE("(%p)", pPlayer);
18 pmbaty 1007
 
1008
    if (pPlayer - gNet_players == gIt_or_fox) {
1009
        return;
1010
    }
1011
    for (i = 0; i < gNumber_of_net_players; i++) {
1012
        StopCarBeingIt(gNet_players[i].car);
1013
    }
1014
    if (gCurrent_net_game->type == eNet_game_type_foxy) {
1015
        pPlayer->car->power_up_levels[1] = 0;
1016
    } else if (gCurrent_net_game->type == eNet_game_type_tag && gIt_or_fox >= 0) {
1017
        gNet_players[gIt_or_fox].car->power_up_levels[1] = 0;
1018
    }
1019
    MakeCarIt(pPlayer->car);
1020
    gIt_or_fox = pPlayer - gNet_players;
1021
    sprintf(s, "%s", GetMiscString(gCurrent_net_game->type == eNet_game_type_tag ? kMiscString_HA_HA_YOU_ARE_IT : kMiscString_YOU_ARE_THE_FOX));
1022
    NetSendHeadupToPlayer(s, pPlayer->ID);
1023
    for (i = 0; i < gNumber_of_net_players; i++) {
1024
        if (&gNet_players[i] != pPlayer) {
1025
            sprintf(s, "%s %s", pPlayer->player_name, GetMiscString(gCurrent_net_game->type == eNet_game_type_tag ? 185 : 187));
1026
            NetSendHeadupToPlayer(s, gNet_players[i].ID);
1027
        }
1028
    }
1 pmbaty 1029
}
1030
 
1031
// IDA: int __usercall FarEnoughAway@<EAX>(tNet_game_player_info *pPlayer_1@<EAX>, tNet_game_player_info *pPlayer_2@<EDX>)
1032
int FarEnoughAway(tNet_game_player_info* pPlayer_1, tNet_game_player_info* pPlayer_2) {
18 pmbaty 1033
    br_vector3 difference;
1 pmbaty 1034
    LOG_TRACE("(%p, %p)", pPlayer_1, pPlayer_2);
18 pmbaty 1035
 
1036
    BrVector3Sub(&difference, &pPlayer_1->car->pos, &pPlayer_2->car->pos);
1037
    return BrVector3LengthSquared(&difference) >= 4.0f;
1 pmbaty 1038
}
1039
 
1040
// IDA: void __usercall CarInContactWithItOrFox(tNet_game_player_info *pPlayer@<EAX>)
1041
void CarInContactWithItOrFox(tNet_game_player_info* pPlayer) {
1042
    LOG_TRACE("(%p)", pPlayer);
18 pmbaty 1043
 
1044
    if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
1045
        if (PDGetTotalTime() - gLast_it_change > 500) {
1046
            gLast_it_change = PDGetTotalTime();
1047
            if (gIt_or_fox >= 0) {
1048
                gLast_lepper = &gNet_players[gIt_or_fox];
1049
            }
1050
            PlayerIsIt(pPlayer);
1051
        }
1052
    }
1 pmbaty 1053
}
1054
 
1055
// IDA: void __usercall SelectRandomItOrFox(int pNot_this_one@<EAX>)
1056
void SelectRandomItOrFox(int pNot_this_one) {
18 pmbaty 1057
    int i;
1058
    int new_choice;
1 pmbaty 1059
    LOG_TRACE("(%d)", pNot_this_one);
18 pmbaty 1060
 
1061
    new_choice = 0;
1062
    gLast_lepper = NULL;
1063
    if (gCurrent_net_game->type == eNet_game_type_tag) {
1064
        for (i = 0; i < gNumber_of_net_players; i++) {
1065
            if (gNet_players[i].last_score_index == 0) {
1066
                new_choice = i;
1067
                break;
1068
            }
1069
        }
1070
    } else {
1071
        for (i = 0; i < gNumber_of_net_players; i++) {
1072
            if (i != pNot_this_one && gNet_players[i].last_score_index == gNumber_of_net_players - 1) {
1073
                PlayerIsIt(&gNet_players[i]);
1074
                return;
1075
            }
1076
        }
1077
        do {
1078
            new_choice = IRandomBetween(0, gNumber_of_net_players - 1);
1079
        } while (new_choice == pNot_this_one && !gNet_players[new_choice].car->knackered);
1080
    }
1081
    PlayerIsIt(&gNet_players[new_choice]);
1 pmbaty 1082
}
1083
 
1084
// IDA: void __cdecl CalcPlayerScores()
1085
void CalcPlayerScores(void) {
18 pmbaty 1086
    int i;
1087
    int j;
1088
    int knock_out_bit;
1089
    int e_dam;
1090
    int t_dam;
1091
    int d_dam;
1092
    int w_dam;
1093
    int cars_left;
1094
    int car_left;
1095
    int flags;
1096
    int score;
1097
    int highest;
1098
    int next_highest;
1099
    int lowest_score;
1100
    int player_left;
1101
    int new_choice;
1102
    tCar_spec* car;
1103
    tNet_message* message;
1104
    tS32 time;
1105
    char s[256];
1106
    tNet_game_player_info* lowest_score_player;
1 pmbaty 1107
    LOG_TRACE("()");
18 pmbaty 1108
 
1109
    time = GetTotalTime();
1110
 
1111
    if (gCurrent_net_game->type == eNet_game_type_carnage) {
1112
        highest = 0;
1113
        next_highest = 0;
1114
        for (i = 0; i < gNumber_of_net_players; ++i) {
1115
            if (gNet_players[i].score > highest) {
1116
                next_highest = highest;
1117
                highest = gNet_players[i].score;
1118
            } else if (gNet_players[i].score > next_highest) {
1119
                next_highest = gNet_players[i].score;
1120
            }
1121
        }
1122
        gPed_target = (gTotal_peds - (gProgram_state.peds_killed - highest - next_highest)) / 2 + 1;
1123
        if (gCurrent_net_game->options.race_end_target < gPed_target) {
1124
            gPed_target = gCurrent_net_game->options.race_end_target;
1125
        }
1126
    } else if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
1127
        if (gIt_or_fox < 0) {
1128
            SelectRandomItOrFox(-1);
1129
        }
1130
        if (gLast_lepper != NULL && gIt_or_fox >= 0 && FarEnoughAway(gLast_lepper, &gNet_players[gIt_or_fox])) {
1131
            gLast_lepper = NULL;
1132
        }
1133
    }
1134
    lowest_score = 9999;
1135
    lowest_score_player = NULL;
1136
    for (i = 0; i < gNumber_of_net_players; i++) {
1137
        car = gNet_players[i].car;
1138
        if (gNet_players[i].reposition_time != 0 && gNet_players[i].reposition_time <= time && (!gRace_finished || gRace_over_reason == -1)) {
1139
            RepositionPlayer(i);
1140
        }
1141
        if (gNet_players[i].last_waste_message != 0
1142
            && !gNet_players[i].wasteage_attributed
1143
            && time - gNet_players[i].last_waste_message > 500) {
1144
            sprintf(s, "%s %s", gNet_players[i].player_name, GetMiscString(kMiscString_COMMITTED_SUICIDE));
1145
            gNet_players[i].last_waste_message = 0;
1146
            gNet_players[i].wasteage_attributed = 0;
1147
            if (gCurrent_net_game->type == eNet_game_type_car_crusher) {
1148
                gNet_players[i].score--;
1149
            }
1150
            NetSendHeadupToEverybody(s);
1151
        }
1152
        SetKnackeredFlag(car);
1153
        if (car->knackered && !gNet_players[i].wasted) {
1154
            gNet_players[i].wasted = 1;
1155
            message = NetBuildMessage(NETMSGID_WASTED, 0);
1156
            message->contents.data.wasted.victim = gNet_players[i].ID;
1157
            message->contents.data.wasted.culprit = -1;
1158
            NetGuaranteedSendMessageToEverybody(gCurrent_net_game, message, 0);
1159
            switch (gCurrent_net_game->type) {
1160
            case eNet_game_type_fight_to_death:
1161
                cars_left = 0;
1162
                for (j = 0; j < gNumber_of_net_players; j++) {
1163
                    if (!gNet_players[j].wasted) {
1164
                        cars_left++;
1165
                        car_left = j;
1166
                    }
1167
                }
1168
                gNet_players[i].games_score += gGame_scores[5 - cars_left];
1169
                if (cars_left == 1) {
1170
                    DeclareWinner(car_left);
1171
                } else if (cars_left <= 0) {
1172
                    EverybodysLost();
1173
                }
1174
                break;
1175
            case eNet_game_type_car_crusher:
1176
            case eNet_game_type_sudden_death:
1177
                gNet_players[i].reposition_time = GetTotalTime() + 5000;
1178
                break;
1179
            case eNet_game_type_carnage:
1180
                gNet_players[i].reposition_time = GetTotalTime() + 5000;
1181
                gNet_players[i].score /= 2;
1182
                break;
1183
            case eNet_game_type_checkpoint:
1184
                if (gNet_players[i].score >> 16 != gCurrent_race.check_point_count) {
1185
                    knock_out_bit = IRandomBetween(0, gCurrent_race.check_point_count - 1);
1186
                    while (((1 << knock_out_bit) & gNet_players[i].score) != 0) {
1187
                        knock_out_bit++;
1188
                        if (gCurrent_race.check_point_count <= knock_out_bit) {
1189
                            knock_out_bit = 0;
1190
                        }
1191
                    }
1192
                    gNet_players[i].score |= 1 << knock_out_bit;
1193
                }
1194
                gNet_players[i].reposition_time = GetTotalTime() + 5000;
1195
                break;
1196
            case eNet_game_type_tag:
1197
                gNet_players[i].reposition_time = GetTotalTime() + 5000;
1198
                PlayerIsIt(&gNet_players[i]);
1199
                break;
1200
            case eNet_game_type_foxy:
1201
                gNet_players[i].reposition_time = GetTotalTime() + 5000;
1202
                gNet_players[i].score /= 2;
1203
                if (gNumber_of_net_players > 1 && i == gIt_or_fox) {
1204
                    SelectRandomItOrFox(i);
1205
                }
1206
                break;
1207
            default:
1208
                break;
1209
            }
1210
        }
1211
        switch (gCurrent_net_game->type) {
1212
        case eNet_game_type_fight_to_death:
1213
            if (car->knackered) {
1214
                if (gCurrent_net_game->type == eNet_game_type_checkpoint
1215
                    || gCurrent_net_game->type == eNet_game_type_tag) {
1216
                    gNet_players[i].score = 1000000;
1217
                } else {
1218
                    gNet_players[i].score = -1000000;
1219
                }
1220
            } else {
1221
                e_dam = car->damage_units[eDamage_engine].damage_level;
1222
                t_dam = car->damage_units[eDamage_transmission].damage_level;
1223
                d_dam = car->damage_units[eDamage_driver].damage_level;
1224
                w_dam = (car->damage_units[eDamage_lr_wheel].damage_level
1225
                            + car->damage_units[eDamage_lf_wheel].damage_level
1226
                            + car->damage_units[eDamage_rr_wheel].damage_level
1227
                            + car->damage_units[eDamage_rf_wheel].damage_level)
1228
                    / 4;
1229
 
1230
                if (e_dam >= t_dam && e_dam >= d_dam && e_dam >= w_dam) {
1231
                    gNet_players[i].score = 100 - e_dam;
1232
                } else if (t_dam >= d_dam && t_dam >= w_dam) {
1233
                    gNet_players[i].score = 100 - t_dam;
1234
                } else if (w_dam >= d_dam) {
1235
                    gNet_players[i].score = 100 - w_dam;
1236
                } else {
1237
                    gNet_players[i].score = 100 - d_dam;
1238
                }
1239
            }
1240
            break;
1241
        case eNet_game_type_carnage:
1242
            if (gNet_players[i].score >= gPed_target && !gRace_finished) {
1243
                DeclareWinner(i);
1244
            }
1245
            break;
1246
        case eNet_game_type_checkpoint:
1247
            score = 0;
1248
            gNet_players[i].score = gNet_players[i].score & 0xffff;
1249
            flags = gNet_players[i].score;
1250
            for (j = 0; j < gCurrent_race.check_point_count; j++) {
1251
                if ((flags & 1) != 0) {
1252
                    score++;
1253
                }
1254
                flags >>= 1;
1255
            }
1256
            gNet_players[i].score |= score << 16;
1257
            if (!score && !gRace_finished) {
1258
                DeclareWinner(i);
1259
            }
1260
            break;
1261
        case eNet_game_type_sudden_death:
1262
            if (gNet_players[i].score >= 0) {
1263
                if (gNet_players[i].score >= lowest_score) {
1264
                    if (gNet_players[i].score == lowest_score) {
1265
                        lowest_score_player = 0;
1266
                    }
1267
                } else {
1268
                    lowest_score = gNet_players[i].score;
1269
                    lowest_score_player = &gNet_players[i];
1270
                }
1271
            }
1272
            break;
1273
        case eNet_game_type_tag:
1274
            if (i == gIt_or_fox && !gCountdown && gNet_players[i].score >= 0) {
1275
                gNet_players[i].score += gFrame_period;
1276
                if (gNet_players[i].score >= gCurrent_net_game->options.race_end_target) {
1277
                    lowest_score_player = &gNet_players[i];
1278
                }
1279
            }
1280
            break;
1281
        case eNet_game_type_foxy:
1282
            if (i == gIt_or_fox && !gCountdown && !gRace_finished) {
1283
                gNet_players[i].score += gFrame_period;
1284
                if (gNet_players[i].score >= gCurrent_net_game->options.race_end_target) {
1285
                    DeclareWinner(i);
1286
                }
1287
            }
1288
            break;
1289
        default:
1290
            continue;
1291
        }
1292
    }
1293
    if ((gCurrent_net_game->type == eNet_game_type_sudden_death || gCurrent_net_game->type == eNet_game_type_tag)
1294
        && lowest_score_player != NULL
1295
        && lowest_score_player->score >= 0) {
1296
        player_left = -1;
1297
        cars_left = 0;
1298
        for (i = 0; i < gNumber_of_net_players; ++i) {
1299
            if (gNet_players[i].score >= 0 && &gNet_players[i] != lowest_score_player) {
1300
                ++cars_left;
1301
                if (player_left == -1) {
1302
                    player_left = i;
1303
                } else {
1304
                    player_left = -2;
1305
                }
1306
            }
1307
        }
1308
        if (cars_left) {
1309
            lowest_score_player->car->knackered = 1;
1310
            lowest_score_player->wasted = 1;
1311
            lowest_score_player->games_score += gGame_scores[5 - cars_left];
1312
            lowest_score_player->score = -1;
1313
            if (player_left == -1) {
1314
                EverybodysLost();
1315
            } else if (player_left < 0) {
1316
                if (gCurrent_net_game->type == eNet_game_type_tag) {
1317
                    SelectRandomItOrFox(i);
1318
                }
1319
                SendGameplay(lowest_score_player->ID, eNet_gameplay_suddenly_death, 0, 0, 0, 0);
1320
                sprintf(s, "%s %s", lowest_score_player->player_name, GetMiscString(kMiscString_IS_OUT));
1321
                NetSendHeadupToEverybody(s);
1322
            } else {
1323
                DeclareWinner(player_left);
1324
            }
1325
        }
1326
    }
1 pmbaty 1327
}
1328
 
1329
// IDA: void __cdecl SendPlayerScores()
1330
void SendPlayerScores(void) {
1331
    tNet_contents* the_contents;
1332
    int i;
1333
    LOG_TRACE("()");
1334
 
1335
    the_contents = NetGetBroadcastContents(NETMSGID_SCORES, 0);
1336
    if (gCurrent_net_game->type == eNet_game_type_carnage) {
1337
        the_contents->data.scores.general_score = gPed_target;
1338
    } else if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
1339
        the_contents->data.scores.general_score = gNet_players[gIt_or_fox].ID;
1340
    }
1341
    for (i = 0; i < gNumber_of_net_players; i++) {
1342
        the_contents->data.scores.scores[i] = gNet_players[i].score;
1343
    }
1344
}
1345
 
1346
// IDA: void __cdecl DoNetGameManagement()
1347
void DoNetGameManagement(void) {
1348
    LOG_TRACE("()");
1349
 
1350
    if (gNet_mode == eNet_mode_host) {
1351
        CalcPlayerScores();
1352
        SendPlayerScores();
1353
    }
1354
}
1355
 
1356
// IDA: void __usercall InitialisePlayerScore(tNet_game_player_info *pPlayer@<EAX>)
1357
void InitialisePlayerScore(tNet_game_player_info* pPlayer) {
1358
    LOG_TRACE("(%p)", pPlayer);
1359
 
1360
    PossibleService();
1361
    switch (gCurrent_net_game->type) {
1362
    case eNet_game_type_fight_to_death:
1363
        pPlayer->score = 100;
1364
        break;
1365
    case eNet_game_type_car_crusher:
1366
    case eNet_game_type_carnage:
1367
        pPlayer->score = 0;
1368
        break;
1369
    case eNet_game_type_checkpoint:
1370
        pPlayer->score = 0xffff;
1371
        break;
1372
    case eNet_game_type_sudden_death:
1373
        pPlayer->score = 0;
1374
        break;
1375
    case eNet_game_type_tag:
1376
        pPlayer->score = 0;
1377
        break;
1378
    case eNet_game_type_foxy:
1379
        pPlayer->score = 0;
1380
        break;
1381
    default:
1382
        TELL_ME_IF_WE_PASS_THIS_WAY();
1383
    }
1384
    pPlayer->credits = gInitial_net_credits[gCurrent_net_game->options.starting_money_index];
1385
    pPlayer->wasted = 0;
1386
    pPlayer->reposition_time = 0;
1387
}
1388
 
1389
// IDA: void __cdecl InitPlayers()
1390
void InitPlayers(void) {
1391
    int i;
1392
    LOG_TRACE("()");
1393
 
1394
    for (i = 0; i < gNumber_of_net_players; i++) {
1395
        InitialisePlayerScore(&gNet_players[i]);
1396
    }
1397
    if (gNet_mode == eNet_mode_host) {
1398
        gLast_it_change = 0;
1399
        gLast_lepper = NULL;
1400
    }
1401
    gTime_for_punishment = 0;
1402
    gNot_shown_race_type_headup = 1;
1403
    gIt_or_fox = -1;
1404
}
1405
 
1406
// IDA: void __usercall BuyPSPowerup(int pIndex@<EAX>)
1407
void BuyPSPowerup(int pIndex) {
1408
    char s[256];
1409
    char s2[256];
1410
    LOG_TRACE("(%d)", pIndex);
1411
 
1412
    if (gNet_mode == eNet_mode_none) {
1413
        NewTextHeadupSlot(4, 0, 3000, -4, GetMiscString(kMiscString_ONLY_AVAILABLE_IN_NET_GAMES));
1414
    } else if (gProgram_state.current_car.power_up_levels[pIndex] < 4) {
1415
        if (gNet_mode == eNet_mode_none || gPowerup_cost[gProgram_state.current_car.power_up_levels[pIndex]] <= (gProgram_state.credits_earned - gProgram_state.credits_lost)) {
1416
            SpendCredits(gPowerup_cost[gProgram_state.current_car.power_up_levels[pIndex]]);
1417
            ImprovePSPowerup(&gProgram_state.current_car, pIndex);
1418
        } else {
1419
            strcpy(s, GetMiscString(kMiscString_CANNOT_AFFORD_IT));
1420
            sprintf(s2, "%d", gPowerup_cost[gProgram_state.current_car.power_up_levels[pIndex]]);
1421
            SubsStringJob(s, s2);
1422
            NewTextHeadupSlot(4, 0, 3008, -4, s);
1423
        }
1424
    } else {
1425
        NewTextHeadupSlot(4, 0, 3000, -4, GetMiscString(kMiscString_YOU_ARE_ALREADY_AT_MAX));
1426
    }
1427
}
1428
 
1429
// IDA: void __cdecl BuyArmour()
1430
void BuyArmour(void) {
1431
    LOG_TRACE("()");
1432
 
1433
    BuyPSPowerup(0);
1434
}
1435
 
1436
// IDA: void __cdecl BuyPower()
1437
void BuyPower(void) {
1438
    LOG_TRACE("()");
1439
 
1440
    if (gNet_mode != eNet_mode_none && gCurrent_net_game->type == eNet_game_type_foxy && gThis_net_player_index == gIt_or_fox) {
1441
        NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_THE_FOX_CANNOT_DO_THAT));
1442
    } else if (gNet_mode != eNet_mode_none && gCurrent_net_game->type == eNet_game_type_tag && gThis_net_player_index != gIt_or_fox) {
1443
        NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_ONLY_IT_CAN_DO_THAT));
1444
    } else {
1445
        BuyPSPowerup(1);
1446
    }
1447
}
1448
 
1449
// IDA: void __cdecl BuyOffense()
1450
void BuyOffense(void) {
1451
    LOG_TRACE("()");
1452
 
1453
    BuyPSPowerup(2);
1454
}
1455
 
1456
// IDA: void __usercall UseGeneralScore(int pScore@<EAX>)
1457
void UseGeneralScore(int pScore) {
18 pmbaty 1458
    int i;
1 pmbaty 1459
    LOG_TRACE("(%d)", pScore);
18 pmbaty 1460
 
1461
    if (gCurrent_net_game->type == eNet_game_type_carnage) {
1462
        gPed_target = pScore;
1463
    } else if ((gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) && gNet_players[gIt_or_fox].ID != pScore) {
1464
        for (i = 0; i < gNumber_of_net_players; i++) {
1465
            StopCarBeingIt(gNet_players[i].car);
1466
        }
1467
        for (i = 0; i < gNumber_of_net_players; i++) {
1468
            if (gNet_players[i].ID == pScore) {
1469
                MakeCarIt(gNet_players[i].car);
1470
                gIt_or_fox = i;
1471
            }
1472
        }
1473
    }
1 pmbaty 1474
}
1475
 
1476
// IDA: void __usercall NetSendEnvironmentChanges(tNet_game_player_info *pPlayer@<EAX>)
1477
void NetSendEnvironmentChanges(tNet_game_player_info* pPlayer) {
1478
    LOG_TRACE("(%p)", pPlayer);
1479
 
1480
    SendAllPedestrianPositions(pPlayer->ID);
1481
    SendAllNonCarPositions();
1482
}
1483
 
1484
// IDA: void __cdecl UpdateEnvironments()
1485
void UpdateEnvironments(void) {
1486
    int i;
1487
    LOG_TRACE("()");
1488
 
1489
    for (i = 1; i < gNumber_of_net_players; i++) {
1490
        if (!gNet_players[i].race_stuff_initialised) {
1491
            NetSendEnvironmentChanges(&gNet_players[i]);
1492
            gNet_players[i].race_stuff_initialised = 1;
1493
        }
1494
        NetSendMessageStacks();
1495
        SendGameplay(gNet_players[i].ID, eNet_gameplay_go_for_it, 0, 0, 0, 0);
1496
    }
1497
}
1498
 
1499
// IDA: void __usercall ReceivedGameplay(tNet_contents *pContents@<EAX>, tNet_message *pMessage@<EDX>, tU32 pReceive_time@<EBX>)
1500
void ReceivedGameplay(tNet_contents* pContents, tNet_message* pMessage, tU32 pReceive_time) {
18 pmbaty 1501
    int must_revert_reentrancy;
1502
    int gPixel_buffer_size;
1503
    char* gPixels_copy;
1504
    char* gPalette_copy;
1505
    static int pause_semaphore;
1 pmbaty 1506
    LOG_TRACE("(%p, %p, %d)", pContents, pMessage, pReceive_time);
18 pmbaty 1507
 
1508
    switch (pContents->data.gameplay.mess) {
1509
    case eNet_gameplay_host_paused:
1510
        if (!pause_semaphore) {
1511
            gPixel_buffer_size = gBack_screen->row_bytes * gBack_screen->height;
1512
            gPixels_copy = BrMemAllocate(gPixel_buffer_size, kMem_quit_vfy_pixels);
1513
            gPalette_copy = BrMemAllocate(1024, kMem_quit_vfy_pal);
1514
            memcpy(gPixels_copy, gBack_screen->pixels, gPixel_buffer_size);
1515
            memcpy(gPalette_copy, gCurrent_palette_pixels, 1024);
1516
            pause_semaphore = 1;
1517
            NetFullScreenMessage(kMiscString_PLEASE_WAIT_HOST_HAS_PAUSED, 1);
1518
            must_revert_reentrancy = PermitNetServiceReentrancy();
1519
            do {
1520
                NetService(0);
1521
                if (CheckQuit()) {
1522
                    NetFullScreenMessage(kMiscString_PLEASE_WAIT_HOST_HAS_PAUSED, 1);
1523
                }
1524
            } while (gWaiting_for_unpause
1525
                && gProgram_state.prog_status != eProg_idling
1526
                && (!gRace_finished || gRace_over_reason != eRace_over_abandoned));
1527
            if (must_revert_reentrancy) {
1528
                HaltNetServiceReentrancy();
1529
            }
1530
            gWaiting_for_unpause = 1;
1531
            FadePaletteDown();
1532
            memcpy(gBack_screen->pixels, gPixels_copy, gPixel_buffer_size);
1533
            memcpy(gCurrent_palette_pixels, gPalette_copy, 1024);
1534
            BrMemFree(gPixels_copy);
1535
            BrMemFree(gPalette_copy);
1536
            PDScreenBufferSwap(0);
1537
            FadePaletteUp();
1538
            pause_semaphore = 0;
1539
        }
1540
        break;
1541
    case eNet_gameplay_earn_credits:
1542
        EarnCredits(pContents->data.gameplay.param_1);
1543
        break;
1544
    case eNet_gameplay_host_unpaused:
1545
        gWaiting_for_unpause = 0;
1546
        break;
1547
    case eNet_gameplay_suicide:
1548
        KnackerThisCar(NetCarFromPlayerID(pMessage->sender));
1549
        break;
1550
    case eNet_gameplay_go_for_it:
1551
        gWait_for_it = 0;
1552
        break;
1553
    default:
1554
        if (gCurrent_net_game->type == eNet_game_type_checkpoint || gCurrent_net_game->type == eNet_game_type_sudden_death || gCurrent_net_game->type == eNet_game_type_tag) {
1555
            switch (pContents->data.gameplay.mess) {
1556
            case eNet_gameplay_checkpoint:
1557
                Checkpoint(pContents->data.gameplay.param_1, 1);
1558
                break;
1559
            case eNet_gameplay_wrong_checkpoint:
1560
                WrongCheckpoint(pContents->data.gameplay.param_1);
1561
                break;
1562
            case eNet_gameplay_suddenly_death:
1563
                DoFancyHeadup(kFancyHeadupNetworkRaceOverNetworkLoss);
1564
                ChangeAmbientPratcam(36);
1565
                gRace_finished = 1;
1566
                break;
1567
            default:
1568
                break;
1569
            }
1570
        }
1571
        break;
1572
    }
1 pmbaty 1573
}
1574
 
1575
// IDA: void __usercall SendGameplay(tPlayer_ID pPlayer@<EAX>, tNet_gameplay_mess pMess@<EDX>, int pParam_1@<EBX>, int pParam_2@<ECX>, int pParam_3, int pParam_4)
1576
void SendGameplay(tPlayer_ID pPlayer, tNet_gameplay_mess pMess, int pParam_1, int pParam_2, int pParam_3, int pParam_4) {
1577
    tNet_message* the_message;
1578
    LOG_TRACE("(%d, %d, %d, %d, %d, %d)", pPlayer, pMess, pParam_1, pParam_2, pParam_3, pParam_4);
1579
 
1580
    the_message = NetBuildMessage(NETMSGID_GAMEPLAY, 0);
1581
    the_message->contents.data.gameplay.mess = pMess;
1582
    the_message->contents.data.gameplay.param_1 = pParam_1;
1583
    the_message->contents.data.gameplay.param_2 = pParam_2;
1584
    the_message->contents.data.gameplay.param_3 = pParam_3;
1585
    the_message->contents.data.gameplay.param_4 = pParam_4;
1586
    NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, pPlayer, 0);
1587
}
1588
 
1589
// IDA: void __usercall SendGameplayToAllPlayers(tNet_gameplay_mess pMess@<EAX>, int pParam_1@<EDX>, int pParam_2@<EBX>, int pParam_3@<ECX>, int pParam_4)
1590
void SendGameplayToAllPlayers(tNet_gameplay_mess pMess, int pParam_1, int pParam_2, int pParam_3, int pParam_4) {
1591
    tNet_message* the_message;
1592
    LOG_TRACE("(%d, %d, %d, %d, %d)", pMess, pParam_1, pParam_2, pParam_3, pParam_4);
1593
 
1594
    the_message = NetBuildMessage(NETMSGID_GAMEPLAY, 0);
1595
    the_message->contents.data.gameplay.mess = pMess;
1596
    the_message->contents.data.gameplay.param_1 = pParam_1;
1597
    the_message->contents.data.gameplay.param_2 = pParam_2;
1598
    the_message->contents.data.gameplay.param_3 = pParam_3;
1599
    the_message->contents.data.gameplay.param_4 = pParam_4;
1600
    NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
1601
}
1602
 
1603
// IDA: void __usercall SendGameplayToHost(tNet_gameplay_mess pMess@<EAX>, int pParam_1@<EDX>, int pParam_2@<EBX>, int pParam_3@<ECX>, int pParam_4)
1604
void SendGameplayToHost(tNet_gameplay_mess pMess, int pParam_1, int pParam_2, int pParam_3, int pParam_4) {
18 pmbaty 1605
    tNet_message* the_message;
1 pmbaty 1606
    LOG_TRACE("(%d, %d, %d, %d, %d)", pMess, pParam_1, pParam_2, pParam_3, pParam_4);
18 pmbaty 1607
 
1608
    if (gNet_mode == eNet_mode_client) {
1609
        the_message = NetBuildMessage(NETMSGID_GAMEPLAY, 0);
1610
        the_message->contents.data.gameplay.mess = pMess;
1611
        the_message->contents.data.gameplay.param_1 = pParam_1;
1612
        the_message->contents.data.gameplay.param_2 = pParam_2;
1613
        the_message->contents.data.gameplay.param_3 = pParam_3;
1614
        the_message->contents.data.gameplay.param_4 = pParam_4;
1615
        NetGuaranteedSendMessageToHost(gCurrent_net_game, the_message, NULL);
1616
    }
1 pmbaty 1617
}
1618
 
1619
// IDA: void __cdecl InitNetGameplayStuff()
1620
void InitNetGameplayStuff(void) {
1621
    LOG_TRACE("()");
1622
}
1623
 
1624
// IDA: void __cdecl DefaultNetName()
1625
void DefaultNetName(void) {
1626
    NetObtainSystemUserName(gNet_player_name, 32);
1627
}
1628
 
1629
// IDA: void __usercall NetSendPointCrush(tCar_spec *pCar@<EAX>, tU16 pCrush_point_index@<EDX>, br_vector3 *pEnergy_vector@<EBX>)
1630
void NetSendPointCrush(tCar_spec* pCar, tU16 pCrush_point_index, br_vector3* pEnergy_vector) {
18 pmbaty 1631
    tNet_contents* contents;
1 pmbaty 1632
    LOG_TRACE("(%p, %d, %p)", pCar, pCrush_point_index, pEnergy_vector);
1633
 
18 pmbaty 1634
    contents = NetGetBroadcastContents(NETMSGID_CRUSHPOINT, 0);
1635
    contents->data.crush.id = NetPlayerFromCar(pCar)->ID;
1636
    contents->data.crush.vertex = pCrush_point_index;
1637
    BrVector3Copy(&contents->data.crush.energy_vector, pEnergy_vector);
1 pmbaty 1638
}
1639
 
1640
// IDA: void __usercall RecievedCrushPoint(tNet_contents *pContents@<EAX>)
1641
void RecievedCrushPoint(tNet_contents* pContents) {
18 pmbaty 1642
    tCar_spec* car;
1 pmbaty 1643
    LOG_TRACE("(%p)", pContents);
18 pmbaty 1644
 
1645
    car = NetCarFromPlayerID(pContents->data.crush.id);
1646
    if (car == NULL || gNet_mode == eNet_mode_host || car->active || gArrow_mode) {
1647
        return;
1648
    }
1649
    if (car->car_model_actors[car->principal_car_actor].crush_data.number_of_crush_points == 0) {
1650
        return;
1651
    }
1652
 
1653
    CrushModelPoint(
1654
        car,
1655
        car->principal_car_actor,
1656
        car->car_model_actors[car->principal_car_actor].actor->model,
1657
        pContents->data.crush.vertex,
1658
        &pContents->data.crush.energy_vector,
1659
        BrVector3Length(&pContents->data.crush.energy_vector) + 0.06f,
1660
        &car->car_model_actors[car->principal_car_actor].crush_data);
1661
    SetModelForUpdate(car->car_model_actors[car->principal_car_actor].actor->model, car, 0);
1 pmbaty 1662
}
1663
 
1664
// IDA: void __usercall GetReducedMatrix(tReduced_matrix *m1@<EAX>, br_matrix34 *m2@<EDX>)
1665
void GetReducedMatrix(tReduced_matrix* m1, br_matrix34* m2) {
1666
    LOG_TRACE("(%p, %p)", m1, m2);
18 pmbaty 1667
 
1668
    m1->row1.v[0] = m2->m[0][0];
1669
    m1->row1.v[1] = m2->m[0][1];
1670
    m1->row1.v[2] = m2->m[0][2];
1671
    m1->row2.v[0] = m2->m[1][0];
1672
    m1->row2.v[1] = m2->m[1][1];
1673
    m1->row2.v[2] = m2->m[1][2];
1674
    m1->translation.v[0] = m2->m[3][0];
1675
    m1->translation.v[1] = m2->m[3][1];
1676
    m1->translation.v[2] = m2->m[3][2];
1 pmbaty 1677
}
1678
 
1679
// IDA: void __usercall GetExpandedMatrix(br_matrix34 *m1@<EAX>, tReduced_matrix *m2@<EDX>)
1680
void GetExpandedMatrix(br_matrix34* m1, tReduced_matrix* m2) {
1681
    LOG_TRACE("(%p, %p)", m1, m2);
18 pmbaty 1682
 
1683
    m1->m[0][0] = m2->row1.v[0];
1684
    m1->m[0][1] = m2->row1.v[1];
1685
    m1->m[0][2] = m2->row1.v[2];
1686
 
1687
    m1->m[1][0] = m2->row2.v[0];
1688
    m1->m[1][1] = m2->row2.v[1];
1689
    m1->m[1][2] = m2->row2.v[2];
1690
 
1691
    m1->m[3][0] = m2->translation.v[0];
1692
    m1->m[3][1] = m2->translation.v[1];
1693
    m1->m[3][2] = m2->translation.v[2];
1694
 
1695
    m1->m[2][0] = m2->row2.v[2] * m2->row1.v[1] - m2->row2.v[1] * m2->row1.v[2];
1696
    m1->m[2][1] = m2->row1.v[2] * m2->row2.v[0] - m2->row2.v[2] * m2->row1.v[0];
1697
    m1->m[2][2] = m2->row2.v[1] * m2->row1.v[0] - m2->row1.v[1] * m2->row2.v[0];
1 pmbaty 1698
}
1699
 
1700
// IDA: void __usercall NetEarnCredits(tNet_game_player_info *pPlayer@<EAX>, tS32 pCredits@<EDX>)
1701
void NetEarnCredits(tNet_game_player_info* pPlayer, tS32 pCredits) {
1702
    LOG_TRACE("(%p, %d)", pPlayer, pCredits);
18 pmbaty 1703
 
1704
    // empty function
1 pmbaty 1705
}