Subversion Repositories Games.Carmageddon

Rev

Rev 15 | 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 "crush.h"
18 pmbaty 2
#include "brender.h"
1 pmbaty 3
#include "car.h"
4
#include "displays.h"
5
#include "globvars.h"
6
#include "globvrkm.h"
7
#include "globvrpb.h"
8
#include "graphics.h"
9
#include "harness/trace.h"
10
#include "loading.h"
11
#include "mainloop.h"
12
#include "netgame.h"
13
#include "network.h"
14
#include "oil.h"
15
#include "opponent.h"
16
#include "pd/sys.h"
17
#include "pedestrn.h"
18
#include "piping.h"
19
#include "pratcam.h"
20
#include "raycast.h"
21
#include "replay.h"
22
#include "spark.h"
23
#include "structur.h"
24
#include "utility.h"
25
#include "world.h"
26
#include <stdlib.h>
27
 
28
float gWobble_spam_y[8] = { 0.0f, -0.15f, 0.4f, 0.15f, -0.4f, 0.25f, 0.0f, -0.25f };
29
float gWobble_spam_z[8] = { 0.4f, -0.25f, 0.0f, 0.25f, 0.0f, 0.15f, -0.4f, -0.15f };
30
br_scalar gWheel_circ_to_width = 0.16f;
31
tU8 gSmoke_damage_step[12] = { 20u, 20u, 0u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 10u };
15 pmbaty 32
int gSteal_ranks[5] = { 89, 72, 55, 38, 21 };
1 pmbaty 33
 
15 pmbaty 34
#define BIGAPC_OPPONENT_INDEX 4
35
 
1 pmbaty 36
// IDA: int __usercall ReadCrushData@<EAX>(FILE *pF@<EAX>, tCrush_data *pCrush_data@<EDX>)
37
int ReadCrushData(FILE* pF, tCrush_data* pCrush_data) {
38
    //char s[256]; // Pierre-Marie Baty -- unused variable
39
    //char* str; // Pierre-Marie Baty -- unused variable
40
    int i;
41
    int j;
42
    //int k; // Pierre-Marie Baty -- unused variable
43
    tCrush_point_spec* the_spec;
44
    tCrush_neighbour* the_neighbour;
45
    LOG_TRACE("(%p, %p)", pF, pCrush_data);
46
 
47
    pCrush_data->softness_factor = GetAFloat(pF);
48
    GetPairOfFloats(pF, &pCrush_data->min_fold_factor, &pCrush_data->max_fold_factor);
49
    pCrush_data->wibble_factor = GetAFloat(pF);
50
    pCrush_data->limit_deviant = GetAFloat(pF);
51
    pCrush_data->split_chance = GetAFloat(pF);
52
    pCrush_data->min_y_fold_down = GetAFloat(pF);
53
    pCrush_data->number_of_crush_points = GetAnInt(pF);
54
    pCrush_data->crush_points = (tCrush_point_spec*)BrMemAllocate(sizeof(tCrush_point_spec) * pCrush_data->number_of_crush_points, kMem_crush_data);
55
 
56
    for (i = 0, the_spec = pCrush_data->crush_points; i < pCrush_data->number_of_crush_points; i++, the_spec++) {
57
        the_spec->vertex_index = GetAnInt(pF);
58
        GetThreeFloats(pF, &the_spec->limits_neg.v[0], &the_spec->limits_neg.v[1], &the_spec->limits_neg.v[2]);
59
        GetThreeFloats(pF, &the_spec->limits_pos.v[0], &the_spec->limits_pos.v[1], &the_spec->limits_pos.v[2]);
60
        GetThreeFloats(pF, &the_spec->softness_neg.v[0], &the_spec->softness_neg.v[1], &the_spec->softness_neg.v[2]);
61
        GetThreeFloats(pF, &the_spec->softness_pos.v[0], &the_spec->softness_pos.v[1], &the_spec->softness_pos.v[2]);
62
        the_spec->number_of_neighbours = GetAnInt(pF);
63
        the_spec->neighbours = BrMemAllocate(sizeof(tCrush_neighbour) * the_spec->number_of_neighbours, kMem_crush_neighbours);
64
 
65
        for (j = 0, the_neighbour = the_spec->neighbours; j < the_spec->number_of_neighbours; j++, the_neighbour++) {
66
            the_neighbour->vertex_index = GetAnInt(pF);
67
            the_neighbour->factor = GetAnInt(pF);
68
        }
69
    }
70
    return 0;
71
}
72
 
73
// IDA: float __usercall SkipCrushData@<ST0>(FILE *pF@<EAX>)
74
float SkipCrushData(FILE* pF) {
75
    int i;
76
    int j;
77
    int count_1;
78
    int count_2;
79
    char s[256];
80
    float softness;
81
    LOG_TRACE("(%p)", pF);
82
 
83
    softness = GetAFloat(pF);
84
    for (i = 0; i < 5; ++i) {
85
        GetALineAndDontArgue(pF, s);
86
    }
87
    count_1 = GetAnInt(pF);
88
    for (i = 0; i < count_1; i++) {
89
        for (j = 0; j < 5; j++) {
90
            GetALineAndDontArgue(pF, s);
91
        }
92
        count_2 = GetAnInt(pF);
93
        for (j = 0; j < 2 * count_2; j++) {
94
            GetALineAndDontArgue(pF, s);
95
        }
96
    }
97
    return softness;
98
}
99
 
100
// IDA: int __usercall WriteCrushData@<EAX>(FILE *pF@<EAX>, tCrush_data *pCrush_data@<EDX>)
101
int WriteCrushData(FILE* pF, tCrush_data* pCrush_data) {
102
    int i;
103
    int j;
104
    //int k; // Pierre-Marie Baty -- unused variable
105
    tCrush_point_spec* the_spec;
106
    tCrush_neighbour* the_neighbour;
107
    LOG_TRACE("(%p, %p)", pF, pCrush_data);
108
 
109
    fprintf(pF, "%f\n\r", pCrush_data->softness_factor);
110
    fprintf(pF, "%f,%f\n\r", pCrush_data->min_fold_factor, pCrush_data->max_fold_factor);
111
    fprintf(pF, "%f\n\r", pCrush_data->wibble_factor);
112
    fprintf(pF, "%f\n\r", pCrush_data->limit_deviant);
113
    fprintf(pF, "%f\n\r", pCrush_data->split_chance);
114
    fprintf(pF, "%f\n\r", pCrush_data->min_y_fold_down);
115
    fprintf(pF, "%d\n\r", pCrush_data->number_of_crush_points);
116
    for (i = 0, the_spec = pCrush_data->crush_points; i < pCrush_data->number_of_crush_points; i++, the_spec++) {
117
        fprintf(pF, "%d\n\r", the_spec->vertex_index);
118
        fprintf(pF, "%f, %f, %f\n\r", the_spec->limits_neg.v[0], the_spec->limits_neg.v[1], the_spec->limits_neg.v[2]);
119
        fprintf(pF, "%f, %f, %f\n\r", the_spec->limits_pos.v[0], the_spec->limits_pos.v[1], the_spec->limits_pos.v[2]);
120
        fprintf(pF, "%f, %f, %f\n\r", the_spec->softness_neg.v[0], the_spec->softness_neg.v[1], the_spec->softness_neg.v[2]);
121
        fprintf(pF, "%f, %f, %f\n\r", the_spec->softness_pos.v[0], the_spec->softness_pos.v[1], the_spec->softness_pos.v[2]);
122
        fprintf(pF, "%d\n\r", the_spec->number_of_neighbours);
123
        for (j = 0, the_neighbour = the_spec->neighbours; j < the_spec->number_of_neighbours; j++, the_neighbour++) {
124
            fprintf(pF, "%d\n\r", the_neighbour->vertex_index);
125
            fprintf(pF, "%d\n\r", the_neighbour->factor);
126
        }
127
    }
128
    return 0;
129
}
130
 
131
// IDA: void __usercall DisposeCrushData(tCrush_data *pCrush_data@<EAX>)
132
void DisposeCrushData(tCrush_data* pCrush_data) {
133
    int i;
134
    LOG_TRACE("(%p)", pCrush_data);
135
 
136
    for (i = 0; i < pCrush_data->number_of_crush_points; i++) {
137
        if (pCrush_data->crush_points[i].neighbours != NULL) {
138
            BrMemFree(pCrush_data->crush_points[i].neighbours);
139
        }
140
    }
141
    if (pCrush_data->crush_points != NULL) {
142
        BrMemFree(pCrush_data->crush_points);
143
    }
144
}
145
 
146
// IDA: void __usercall CrushModelPoint(tCar_spec *pCar@<EAX>, int pModel_index@<EDX>, br_model *pModel@<EBX>, int pCrush_point_index@<ECX>, br_vector3 *pEnergy_vector, br_scalar total_energy, tCrush_data *pCrush_data)
147
void CrushModelPoint(tCar_spec* pCar, int pModel_index, br_model* pModel, int pCrush_point_index, br_vector3* pEnergy_vector, br_scalar total_energy, tCrush_data* pCrush_data) {
148
    int i;
149
    int j;
150
    //int k; // Pierre-Marie Baty -- unused variable
151
    int pipe_vertex_count;
152
    int neighbour_index;
153
    int bend_axis;
154
    int default_bend_axis[3];
155
    tCrush_point_spec* the_crush_point;
156
    tCrush_neighbour* the_neighbour;
157
    br_vector3* target_point;
158
    br_vector3 old_vector;
159
    br_vector3 softnesss;
160
    br_vector3 movement;
161
    br_scalar random_range;
162
    br_scalar bend_amount;
163
    br_scalar min_y_fold_down;
164
    float default_bend_factor[3];
165
    float working_min_fold;
166
    float working_max_fold;
167
    float working_wibble;
168
    float working_limit_deviant;
169
    float working_split_chance;
170
    tChanged_vertex pipe_array[600];
171
    //tCar_spec* car; // Pierre-Marie Baty -- unused variable
172
    LOG_TRACE("(%p, %d, %p, %d, %p, %f, %p)", pCar, pModel_index, pModel, pCrush_point_index, pEnergy_vector, total_energy, pCrush_data);
173
 
174
    pipe_vertex_count = 0;
175
    if (gNet_mode == eNet_mode_host && pCar->car_model_actors[pModel_index].min_distance_squared == 0.0f) {
176
        NetSendPointCrush(pCar, pCrush_point_index, pEnergy_vector);
177
    }
178
    working_min_fold = pCrush_data->min_fold_factor * gCar_crush_min_fold;
179
    working_max_fold = pCrush_data->max_fold_factor * gCar_crush_max_fold;
180
    working_wibble = pCrush_data->wibble_factor * gCar_crush_wibble;
181
    working_limit_deviant = pCrush_data->limit_deviant * gCar_crush_limit_deviant;
182
    working_split_chance = pCrush_data->split_chance * gCar_crush_split_chance;
183
    min_y_fold_down = pCrush_data->min_y_fold_down;
184
    the_crush_point = &pCrush_data->crush_points[pCrush_point_index];
185
    if (pModel->nvertices <= the_crush_point->vertex_index) {
186
        return;
187
    }
188
    target_point = &pModel->vertices[the_crush_point->vertex_index].p;
189
    old_vector = *target_point;
190
    for (i = 0; i < 3; i++) {
191
        pEnergy_vector->v[i] = SRandomPosNeg(working_wibble * total_energy) + pEnergy_vector->v[i];
192
        random_range = (the_crush_point->limits_pos.v[i] - the_crush_point->limits_neg.v[i]) * working_limit_deviant;
193
        if (pEnergy_vector->v[i] >= 0.0f) {
194
            softnesss.v[i] = the_crush_point->softness_pos.v[i];
195
        } else {
196
            softnesss.v[i] = the_crush_point->softness_neg.v[i];
197
        }
198
        movement.v[i] = target_point->v[i];
199
        target_point->v[i] += pEnergy_vector->v[i] * softnesss.v[i];
200
        if (the_crush_point->limits_neg.v[i] <= target_point->v[i]) {
201
            if (target_point->v[i] > the_crush_point->limits_pos.v[i]) {
202
                target_point->v[i] = SRandomPosNeg(random_range) + the_crush_point->limits_pos.v[i];
203
            }
204
        } else {
205
            target_point->v[i] = SRandomPosNeg(random_range) + the_crush_point->limits_neg.v[i];
206
        }
207
        movement.v[i] = target_point->v[i] - movement.v[i];
208
        if (pEnergy_vector->v[i] * movement.v[i] < 0.0f) {
209
            movement.v[i] = 0.0f;
210
            target_point->v[i] = old_vector.v[i];
211
        }
212
    }
213
 
214
    if (IsActionReplayAvailable()) {
215
        pipe_array[pipe_vertex_count].vertex_index = the_crush_point->vertex_index;
216
        BrVector3Sub(&pipe_array[pipe_vertex_count].delta_coordinates, target_point, &old_vector);
217
        pipe_vertex_count++;
218
    }
219
    neighbour_index = -1;
220
    for (bend_axis = 0; bend_axis < 3; bend_axis++) {
221
        default_bend_axis[bend_axis] = (bend_axis + IRandomBetween(1, 2)) % 3;
222
        default_bend_factor[bend_axis] = FRandomBetween(working_min_fold, working_max_fold);
223
    }
224
 
225
    the_neighbour = the_crush_point->neighbours;
226
    for (j = 0; j < the_crush_point->number_of_neighbours; j++, the_neighbour++) {
227
        if (the_neighbour->vertex_index) {
228
            neighbour_index += the_neighbour->vertex_index;
229
            if (neighbour_index < 0 || pModel->nvertices <= neighbour_index) {
230
                return;
231
            }
232
            target_point = &pModel->vertices[neighbour_index].p;
233
            old_vector = *target_point;
234
            for (bend_axis = 0; bend_axis < 3; bend_axis++) {
235
                target_point->v[bend_axis] += (1.0f - the_neighbour->factor / 256.0f) * movement.v[bend_axis];
236
                float v12;
237
                if (the_neighbour->factor <= 128) {
238
                    v12 = the_neighbour->factor / 128.0f;
239
                } else {
240
                    v12 = 2.0 - the_neighbour->factor / 128.0f;
241
                }
242
                if (((int)((target_point->v[2] + target_point->v[1] + target_point->v[0]) * 63.0f) & 1) * v12 == 0.0) {
243
                    bend_amount = -default_bend_factor[bend_axis];
244
                } else {
245
                    bend_amount = default_bend_factor[bend_axis];
246
                }
247
                int axis_tmp = (((int)((target_point->v[2] + target_point->v[1] + target_point->v[0]) * 100.0f) + bend_axis - 1) & 1) % 3;
248
                target_point->v[axis_tmp] += fabs(movement.v[bend_axis]) * bend_amount;
249
            }
250
            if (IsActionReplayAvailable() && pipe_vertex_count < 600) {
251
                pipe_array[pipe_vertex_count].vertex_index = neighbour_index;
252
                BrVector3Sub(&pipe_array[pipe_vertex_count].delta_coordinates, target_point, &old_vector);
253
                pipe_vertex_count++;
254
            }
255
        } else {
256
            neighbour_index += the_neighbour->factor;
257
        }
258
    }
259
    if (IsActionReplayAvailable() && pipe_vertex_count) {
260
        PipeSingleModelGeometry(pCar->car_ID, pModel_index, pipe_vertex_count, pipe_array);
261
    }
262
}
263
 
264
// IDA: void __usercall CrushModel(tCar_spec *pCar@<EAX>, int pModel_index@<EDX>, br_actor *pActor@<EBX>, br_vector3 *pImpact_point@<ECX>, br_vector3 *pEnergy_vector, tCrush_data *pCrush_data)
265
void CrushModel(tCar_spec* pCar, int pModel_index, br_actor* pActor, br_vector3* pImpact_point, br_vector3* pEnergy_vector, tCrush_data* pCrush_data) {
266
    br_scalar this_distance;
267
    br_scalar total_energy;
268
    br_scalar nearest_so_far;
269
    br_vector3 impact_point_model;
270
    br_vector3 energy_vector_scaled;
271
    br_vector3 energy_vector_model;
272
    int i;
273
    int nearest_index;
274
    br_vertex* vertices;
275
    br_vertex* the_vertex;
276
    //br_matrix34 inverse_transform; // Pierre-Marie Baty -- unused variable
277
    LOG_TRACE("(%p, %d, %p, %p, %p, %p)", pCar, pModel_index, pActor, pImpact_point, pEnergy_vector, pCrush_data);
278
 
279
    if (gArrow_mode) {
280
        return;
281
    }
282
    if (pCrush_data->number_of_crush_points == 0) {
283
        return;
284
    }
285
    BrVector3Sub(&impact_point_model, pImpact_point, (br_vector3*)pActor->t.t.mat.m[3]);
286
    BrVector3Scale(&energy_vector_model, pEnergy_vector, pCrush_data->softness_factor * gCar_crush_softness);
287
    total_energy = BrVector3Length(&energy_vector_model);
288
    if (total_energy < 0.06f) {
289
        return;
290
    }
291
    BrVector3Scale(&energy_vector_scaled, &energy_vector_model, (total_energy - 0.06f) / total_energy);
292
    nearest_so_far = BR_SCALAR_MAX;
293
    vertices = pActor->model->vertices;
294
    nearest_index = -1;
295
    for (i = 0; i < pCrush_data->number_of_crush_points; i++) {
296
        the_vertex = &vertices[pCrush_data->crush_points[i].vertex_index];
297
        this_distance = (impact_point_model.v[2] - the_vertex->p.v[2]) * (impact_point_model.v[2] - the_vertex->p.v[2]) + (impact_point_model.v[1] - the_vertex->p.v[1]) * (impact_point_model.v[1] - the_vertex->p.v[1]) + (impact_point_model.v[0] - the_vertex->p.v[0]) * (impact_point_model.v[0] - the_vertex->p.v[0]);
298
        if (this_distance < nearest_so_far) {
299
            nearest_so_far = this_distance;
300
            nearest_index = i;
301
        }
302
    }
303
    if (nearest_index >= 0) {
304
        CrushModelPoint(pCar, pModel_index, pActor->model, nearest_index, &energy_vector_scaled, total_energy, pCrush_data);
305
        SetModelForUpdate(pActor->model, pCar, 1);
306
    }
307
}
308
 
309
// IDA: void __cdecl JitModelUpdate(br_actor *actor, br_model *model, br_material *material, void *render_data, br_uint_8 style, int on_screen)
310
void JitModelUpdate(br_actor* actor, br_model* model, br_material* material, void* render_data, br_uint_8 style, int on_screen) {
311
    LOG_TRACE("(%p, %p, %p, %p, %d, %d)", actor, model, material, render_data, style, on_screen);
312
 
313
    BrModelUpdate(model, BR_MODU_VERTEX_POSITIONS);
314
    model->flags &= ~(BR_MODF_CUSTOM);
315
    BrZbModelRender(actor, model, material, style, BrOnScreenCheck(&model->bounds), 0);
316
}
317
 
318
// IDA: void __usercall SetModelForUpdate(br_model *pModel@<EAX>, tCar_spec *pCar@<EDX>, int crush_only@<EBX>)
319
void SetModelForUpdate(br_model* pModel, tCar_spec* pCar, int crush_only) {
320
    LOG_TRACE("(%p, %p, %d)", pModel, pCar, crush_only);
321
 
322
    if (crush_only && pCar != NULL && pCar->car_model_actors[pCar->principal_car_actor].actor->model == pModel) {
323
        CrushBoundingBox(pCar, crush_only);
324
    }
325
    if ((pModel->flags & BR_MODF_CUSTOM) != 0) {
326
        pModel->user = JitModelUpdate;
327
    } else {
328
        pModel->custom = JitModelUpdate;
329
        pModel->flags |= BR_MODF_CUSTOM;
330
    }
331
}
332
 
333
// IDA: void __usercall TotallySpamTheModel(tCar_spec *pCar@<EAX>, int pModel_index@<EDX>, br_actor *pActor@<EBX>, tCrush_data *pCrush_data@<ECX>, br_scalar pMagnitude)
334
void TotallySpamTheModel(tCar_spec* pCar, int pModel_index, br_actor* pActor, tCrush_data* pCrush_data, br_scalar pMagnitude) {
335
    //br_scalar total_energy; // Pierre-Marie Baty -- unused variable
336
    br_vector3 energy_vector_model;
337
    int i;
338
    int the_index;
339
    br_vertex* the_vertex;
340
    //br_vertex* vertices; // Pierre-Marie Baty -- unused variable
341
    LOG_TRACE("(%p, %d, %p, %p, %f)", pCar, pModel_index, pActor, pCrush_data, pMagnitude);
342
 
343
    if (gArrow_mode || pCrush_data->number_of_crush_points == 0) {
344
        return;
345
    }
346
    the_vertex = pActor->model->vertices;
347
    for (i = 0; i < 15; i++) {
348
        the_index = IRandomBetween(0, pCrush_data->number_of_crush_points - 1);
349
        energy_vector_model = the_vertex[pCrush_data->crush_points[the_index].vertex_index].p;
350
        BrVector3Normalise(&energy_vector_model, &energy_vector_model);
351
        BrVector3Scale(&energy_vector_model, &energy_vector_model, -pMagnitude);
352
        CrushModelPoint(pCar, pModel_index, pActor->model, the_index, &energy_vector_model, pMagnitude, pCrush_data);
353
    }
354
    SetModelForUpdate(pActor->model, pCar, 1);
355
}
356
 
357
// IDA: br_scalar __usercall RepairModel@<ST0>(tCar_spec *pCar@<EAX>, int pModel_index@<EDX>, br_actor *pActor@<EBX>, br_vertex *pUndamaged_vertices@<ECX>, br_scalar pAmount, br_scalar *pTotal_deflection)
358
br_scalar RepairModel(tCar_spec* pCar, int pModel_index, br_actor* pActor, br_vertex* pUndamaged_vertices, br_scalar pAmount, br_scalar* pTotal_deflection) {
359
    int i;
360
    int j;
361
    int pipe_vertex_count;
362
    br_vector3 old_point;
363
    br_vertex* model_vertex;
364
    br_scalar amount;
365
    //br_scalar deviation; // Pierre-Marie Baty -- unused variable
366
    tChanged_vertex pipe_array[600];
367
    LOG_TRACE("(%p, %d, %p, %p, %f, %p)", pCar, pModel_index, pActor, pUndamaged_vertices, pAmount, pTotal_deflection);
368
 
369
    pipe_vertex_count = 0;
370
    amount = 0.0f;
371
    *pTotal_deflection = 0.0f;
372
 
373
    for (i = 0; i < pActor->model->nvertices; i++) {
374
        model_vertex = &pActor->model->vertices[i];
375
        old_point = model_vertex->p;
376
        for (j = 0; j < 3; ++j) {
377
            *pTotal_deflection = fabsf(pUndamaged_vertices->p.v[j] - old_point.v[j]) + *pTotal_deflection;
378
            if (pUndamaged_vertices->p.v[j] >= old_point.v[j]) {
379
                if (pUndamaged_vertices->p.v[j] > old_point.v[j]) {
380
                    model_vertex->p.v[j] = model_vertex->p.v[j] + pAmount;
381
                    if (pUndamaged_vertices->p.v[j] < model_vertex->p.v[j]) {
382
                        model_vertex->p.v[j] = pUndamaged_vertices->p.v[j];
383
                    }
384
                    amount = model_vertex->p.v[j] - old_point.v[j] + amount;
385
                }
386
            } else {
387
                model_vertex->p.v[j] = model_vertex->p.v[j] - pAmount;
388
                if (pUndamaged_vertices->p.v[j] > model_vertex->p.v[j]) {
389
                    model_vertex->p.v[j] = pUndamaged_vertices->p.v[j];
390
                }
391
                amount = old_point.v[j] - model_vertex->p.v[j] + amount;
392
            }
393
        }
394
        if (amount != 0.0 && IsActionReplayAvailable() && pipe_vertex_count < COUNT_OF(pipe_array)) {
395
            pipe_array[pipe_vertex_count].vertex_index = i;
396
            BrVector3Sub(&pipe_array[pipe_vertex_count].delta_coordinates, &model_vertex->p, &old_point);
397
            pipe_vertex_count++;
398
        }
399
        pUndamaged_vertices++;
400
    }
401
    SetModelForUpdate(pActor->model, pCar, 0);
402
    if (IsActionReplayAvailable() && pipe_vertex_count) {
403
        PipeSingleModelGeometry(pCar->car_ID, pModel_index, pipe_vertex_count, pipe_array);
404
    }
405
    return amount;
406
}
407
 
408
// IDA: float __usercall RepairCar2@<ST0>(tCar_spec *pCar@<EAX>, tU32 pFrame_period@<EDX>, br_scalar *pTotal_deflection@<EBX>)
409
float RepairCar2(tCar_spec* pCar, tU32 pFrame_period, br_scalar* pTotal_deflection) {
410
    int i;
411
    tCar_actor* the_car_actor;
412
    br_scalar amount;
413
    br_scalar dummy;
414
    LOG_TRACE("(%p, %d, %p)", pCar, pFrame_period, pTotal_deflection);
415
 
416
    if (gArrow_mode) {
417
        return 0.0f;
418
    }
419
    *pTotal_deflection = 0.0;
420
    amount = 0.0;
421
 
422
    for (i = 0; i < gProgram_state.current_car.car_actor_count; i++) {
423
        the_car_actor = &pCar->car_model_actors[i];
424
        if (the_car_actor->min_distance_squared == 0.0 || !the_car_actor->undamaged_vertices) {
425
            if (the_car_actor->undamaged_vertices) {
426
                amount = RepairModel(pCar, i, the_car_actor->actor, the_car_actor->undamaged_vertices, pFrame_period * 0.00005f, pTotal_deflection);
427
            }
428
        } else {
429
            RepairModel(pCar, i, the_car_actor->actor, the_car_actor->undamaged_vertices, pFrame_period * 0.00005f, &dummy);
430
        }
431
    }
432
    pCar->repair_time += pFrame_period;
433
    return amount;
434
}
435
 
436
// IDA: float __usercall RepairCar@<ST0>(tU16 pCar_ID@<EAX>, tU32 pFrame_period@<EDX>, br_scalar *pTotal_deflection@<EBX>)
437
float RepairCar(tU16 pCar_ID, tU32 pFrame_period, br_scalar* pTotal_deflection) {
438
    LOG_TRACE("(%d, %d, %p)", pCar_ID, pFrame_period, pTotal_deflection);
439
 
440
    if (VEHICLE_TYPE_FROM_ID(pCar_ID) == eVehicle_self) {
441
        return RepairCar2(&gProgram_state.current_car, pFrame_period, pTotal_deflection);
442
    }
443
 
444
    return RepairCar2(GetCarSpec(VEHICLE_TYPE_FROM_ID(pCar_ID), VEHICLE_INDEX_FROM_ID(pCar_ID)), pFrame_period, pTotal_deflection);
445
}
446
 
447
// IDA: void __usercall TotallyRepairACar(tCar_spec *pCar@<EAX>)
448
void TotallyRepairACar(tCar_spec* pCar) {
449
    int i;
450
    int j;
451
    int k;
452
    int pipe_vertex_count;
453
    tCar_actor* the_car_actor;
454
    tChanged_vertex pipe_array[600];
455
    br_bounds storage_bounds;
456
    LOG_TRACE("(%p)", pCar);
457
 
458
    StopCarSmokingInstantly(pCar);
459
    if (IsActionReplayAvailable()) {
460
        PipeInstantUnSmudge(pCar);
461
    }
462
    pCar->repair_time += 100000;
463
    for (i = 0; i < COUNT_OF(pCar->damage_units); i++) {
464
        pCar->damage_units[i].damage_level = 0;
465
        pCar->damage_units[i].last_level = 0;
466
        pCar->damage_units[i].smoke_last_level = 0;
467
    }
468
    memcpy(&storage_bounds, &pCar->bounds[1], sizeof(br_bounds));
469
    memcpy(&pCar->bounds[1], &pCar->max_bounds[1], sizeof(br_bounds));
470
    if (TestForCarInSensiblePlace(pCar)) {
471
        for (j = 0; j < pCar->car_actor_count; j++) {
472
            the_car_actor = &pCar->car_model_actors[j];
473
            if (the_car_actor->undamaged_vertices != NULL) {
474
                pipe_vertex_count = 0;
475
                for (k = 0; k < the_car_actor->actor->model->nvertices; k++) {
476
                    if (pipe_vertex_count < COUNT_OF(pipe_array)) {
477
                        BrVector3Sub(&pipe_array[pipe_vertex_count].delta_coordinates,
478
                            &the_car_actor->undamaged_vertices[k].p, &the_car_actor->actor->model->vertices[k].p);
479
                        if (!Vector3IsZero(&pipe_array[pipe_vertex_count].delta_coordinates)) {
480
                            pipe_array[pipe_vertex_count].vertex_index = k;
481
                            pipe_vertex_count++;
482
                        }
483
                    }
484
                }
485
                memcpy(the_car_actor->actor->model->vertices,
486
                    the_car_actor->undamaged_vertices,
487
                    the_car_actor->actor->model->nvertices * sizeof(br_vertex));
18 pmbaty 488
                BrModelUpdate(the_car_actor->actor->model, BR_MODU_VERTEX_COLOURS | BR_MODU_VERTEX_POSITIONS);
1 pmbaty 489
                if (pipe_vertex_count != 0 && IsActionReplayAvailable()) {
490
                    PipeSingleModelGeometry(pCar->car_ID, j, pipe_vertex_count, pipe_array);
491
                }
492
            }
493
        }
494
    } else {
495
        memcpy(&pCar->bounds[1], &storage_bounds, sizeof(br_bounds));
496
    }
497
}
498
 
499
// IDA: void __cdecl TotallyRepairCar()
500
void TotallyRepairCar(void) {
501
    LOG_TRACE("()");
502
 
503
    if (!gArrow_mode) {
504
        TotallyRepairACar(&gProgram_state.current_car);
505
    }
506
}
507
 
508
// IDA: void __cdecl CheckLastCar()
509
void CheckLastCar(void) {
510
    LOG_TRACE("()");
511
 
512
    if (gNet_mode == eNet_mode_none && GetCarCount(eVehicle_opponent) != 0 && NumberOfOpponentsLeft() == 0) {
513
        NewTextHeadupSlot(4, 0, 5000, -4, GetMiscString(kMiscString_EveryOpponentWasted));
514
        RaceCompleted(eRace_over_opponents);
515
    }
516
}
517
 
518
// IDA: void __usercall KnackerThisCar(tCar_spec *pCar@<EAX>)
519
void KnackerThisCar(tCar_spec* pCar) {
520
    LOG_TRACE("(%p)", pCar);
521
 
522
    pCar->knackered = 1;
523
    QueueWastedMassage(pCar->index);
524
    CheckLastCar();
525
    QueueOilSpill(pCar);
526
    if (gNet_mode == eNet_mode_none) {
527
        KillGroovadelic(pCar->index);
528
        KillFunkotronic(pCar->index);
529
    }
530
}
531
 
532
// IDA: void __usercall SetKnackeredFlag(tCar_spec *pCar@<EAX>)
533
void SetKnackeredFlag(tCar_spec* pCar) {
534
    LOG_TRACE("(%p)", pCar);
535
 
536
    if (gNet_mode != eNet_mode_client
537
        && !pCar->knackered
538
        && (pCar->damage_units[eDamage_engine].damage_level >= 99
539
            || pCar->damage_units[eDamage_transmission].damage_level >= 99
540
            || pCar->damage_units[eDamage_driver].damage_level >= 99
541
            || (pCar->damage_units[eDamage_lf_wheel].damage_level >= 99
542
                && pCar->damage_units[eDamage_rf_wheel].damage_level >= 99
543
                && pCar->damage_units[eDamage_lr_wheel].damage_level >= 99
544
                && pCar->damage_units[eDamage_rr_wheel].damage_level >= 99))) {
545
        KnackerThisCar(pCar);
546
        if (gNet_mode == eNet_mode_none) {
547
            if (IRandomBetween(0, 1)) {
548
                if (gNet_mode == eNet_mode_none) {
549
                    StopCarSmoking(pCar);
550
                    CreateSmokeColumn(pCar, 0, IRandomBetween(0, 11), 20000);
551
                }
552
            }
553
            CreateSmokeColumn(pCar, 0, IRandomBetween(0, 11), 180000);
554
        }
555
    }
556
}
557
 
558
// IDA: void __usercall DamageUnit2(tCar_spec *pCar@<EAX>, int pUnit_type@<EDX>, int pDamage_amount@<EBX>)
559
void DamageUnit2(tCar_spec* pCar, int pUnit_type, int pDamage_amount) {
560
    tDamage_unit* the_damage;
561
    int last_level;
562
    LOG_TRACE("(%p, %d, %d)", pCar, pUnit_type, pDamage_amount);
563
 
564
    the_damage = &pCar->damage_units[pUnit_type];
565
    if ((pCar->driver < eDriver_net_human || pUnit_type != eDamage_driver) && pDamage_amount >= 5 && !pCar->invulnerable) {
566
        last_level = the_damage->damage_level;
567
        the_damage->damage_level = pDamage_amount + last_level;
568
        if (the_damage->damage_level >= 99) {
569
            if (pDamage_amount >= 10) {
570
                the_damage->damage_level = 99;
571
            } else {
572
                the_damage->damage_level = last_level;
573
            }
574
        }
575
        if (pCar->driver == eDriver_oppo || gNet_mode != eNet_mode_none) {
576
            SetKnackeredFlag(pCar);
577
        } else if ((pCar->damage_units[eDamage_engine].damage_level >= 99 && pCar->damage_units[eDamage_engine].last_level < 99 && pCar->damage_units[eDamage_transmission].last_level < 99)
578
            || (pCar->damage_units[eDamage_transmission].damage_level >= 99 && pCar->damage_units[eDamage_engine].last_level < 99 && pCar->damage_units[eDamage_transmission].last_level < 99)) {
579
            QueueOilSpill(pCar);
580
        }
581
    }
582
}
583
 
584
// IDA: void __usercall RecordLastDamage(tCar_spec *pCar@<EAX>)
585
void RecordLastDamage(tCar_spec* pCar) {
586
    int i;
587
    LOG_TRACE("(%p)", pCar);
588
 
589
    for (i = 0; i < COUNT_OF(pCar->damage_units); i++) {
590
        pCar->damage_units[i].last_level = pCar->damage_units[i].damage_level;
591
    }
592
    pCar->damage_magnitude_accumulator = 0.0;
593
    pCar->last_impact_location = eImpact_unknown;
594
    pCar->pre_car_col_mat = pCar->car_master_actor->t.t.mat;
595
    pCar->pre_car_col_speed = pCar->speed;
596
    pCar->pre_car_col_knackered = pCar->knackered;
597
    pCar->pre_car_col_direction = pCar->direction;
598
    pCar->pre_car_col_velocity = pCar->v;
599
    pCar->pre_car_col_velocity_car_space = pCar->velocity_car_space;
600
}
601
 
602
// IDA: void __usercall DoDamage(tCar_spec *pCar@<EAX>, tDamage_type pDamage_type@<EDX>, float pMagnitude, float pNastiness)
603
void DoDamage(tCar_spec* pCar, tDamage_type pDamage_type, float pMagnitude, float pNastiness) {
604
    LOG_TRACE("(%p, %d, %f, %f)", pCar, pDamage_type, pMagnitude, pNastiness);
605
 
606
    if (pCar->driver < eDriver_net_human) {
607
        DamageUnit2(pCar, pDamage_type, ((gCurrent_race.suggested_rank < 10 ? 0.5f : gCurrent_race.suggested_rank) / 20.0f + 1.0f) * (pNastiness * pMagnitude * 10.0f));
608
    } else if (gNet_mode != eNet_mode_none) {
609
        DamageUnit2(pCar, pDamage_type, pNastiness * pMagnitude * 15.0f);
610
    } else if (PercentageChance(pNastiness * pMagnitude * 1500.0f)) {
611
        DamageUnit2(pCar, pDamage_type, pNastiness * pMagnitude * 30.0f);
612
    }
613
}
614
 
615
// IDA: void __usercall CheckPiledriverBonus(tCar_spec *pCar@<EAX>, br_vector3 *pImpact_point@<EDX>, br_vector3 *pEnergy@<EBX>)
616
void CheckPiledriverBonus(tCar_spec* pCar, br_vector3* pImpact_point, br_vector3* pEnergy) {
617
    br_actor* child;
618
    br_vector3 norm_impact;
619
    br_vector3 norm_child;
620
    br_vector3 norm_energy;
621
    //br_scalar dp; // Pierre-Marie Baty -- unused variable
622
    LOG_TRACE("(%p, %p, %p)", pCar, pImpact_point, pEnergy);
623
 
624
    if (pCar->current_car_actor < 0) {
625
        return;
626
    }
627
 
628
    BrVector3Normalise(&norm_impact, pImpact_point);
629
    norm_impact.v[1] = 0.f;
630
    BrVector3Normalise(&norm_energy, pEnergy);
631
 
632
    for (child = pCar->car_master_actor->children; child != NULL; child = child->next) {
633
        if (ActorIsPedestrian(child) && PedestrianActorIsPerson(child) && pCar->speed > 0.001f) {
634
            BrVector3Normalise(&norm_child, &child->t.t.translate.t);
635
            norm_child.v[1] = 0.f;
636
            if (BrVector3Dot(&norm_child, &norm_impact) > 0.8f && BrVector3Dot(&norm_energy, &norm_child) < -.65) {
637
                DoFancyHeadup(kFancyHeadupPileDriverBonus);
638
                EarnCredits(((GetPedestrianValue(child) / 2 + 12) / 25) * 25);
639
                return;
640
            }
641
        }
642
    }
643
}
644
 
645
// IDA: tImpact_location __usercall CalcModifiedLocation@<EAX>(tCar_spec *pCar@<EAX>)
646
tImpact_location CalcModifiedLocation(tCar_spec* pCar) {
647
    LOG_TRACE("(%p)", pCar);
648
 
649
    if (pCar->last_impact_location != eImpact_left && pCar->last_impact_location != eImpact_right && pCar->last_impact_location != eImpact_top && pCar->last_impact_location != eImpact_bottom) {
650
        return pCar->last_impact_location;
651
    }
652
    if (pCar->last_col_prop_z < 0.25f) {
653
        return eImpact_front;
654
    }
655
    if (pCar->last_col_prop_z > 0.75f) {
656
        return eImpact_back;
657
    } else {
658
        return pCar->last_impact_location;
659
    }
660
}
661
 
662
// IDA: void __usercall DoPratcamHit(br_vector3 *pHit_vector@<EAX>)
663
void DoPratcamHit(br_vector3* pHit_vector) {
18 pmbaty 664
    int strength_modifier;
665
    br_scalar strength;
1 pmbaty 666
    LOG_TRACE("(%p)", pHit_vector);
667
 
18 pmbaty 668
    strength = BrVector3LengthSquared(pHit_vector);
669
    if (strength > 0.2f) {
670
        strength_modifier = 8;
671
    } else if (strength > 0.015f) {
672
        strength_modifier = 4;
673
    } else if (strength >= 0.001f) {
674
        strength_modifier = 0;
675
    } else {
676
        return;
677
    }
678
    if (fabsf(pHit_vector->v[2]) >= fabsf(pHit_vector->v[0])) {
679
        if (pHit_vector->v[2] >= 0.f) {
680
            PratcamEvent(14 + strength_modifier);
681
        } else {
682
            PratcamEvent(13 + strength_modifier);
683
        }
684
    } else {
685
        if (pHit_vector->v[0] >= 0.f) {
686
            PratcamEvent(15 + strength_modifier);
687
        } else {
688
            PratcamEvent(16 + strength_modifier);
689
        }
690
    }
1 pmbaty 691
}
692
 
693
// IDA: void __usercall DamageSystems(tCar_spec *pCar@<EAX>, br_vector3 *pImpact_point@<EDX>, br_vector3 *pEnergy_vector@<EBX>, int pWas_hitting_a_car@<ECX>)
694
void DamageSystems(tCar_spec* pCar, br_vector3* pImpact_point, br_vector3* pEnergy_vector, int pWas_hitting_a_car) {
695
    int i;
696
    int j;
697
    int result;
698
    br_bounds crushed_car_bounds;
699
    float proportion_x;
700
    float proportion_y;
701
    float proportion_z;
702
    float energy_magnitude;
703
    float pure_energy_magnitude;
704
    br_scalar x;
705
    br_scalar y;
706
    br_scalar z;
707
    br_scalar x1;
708
    br_scalar x2;
709
    br_scalar y1;
710
    br_scalar y2;
711
    br_scalar z1;
712
    br_scalar z2;
713
    //br_scalar distance; // Pierre-Marie Baty -- unused variable
714
    tImpact_location impact_location;
715
    tDamage_program* the_program;
716
    tDamage_clause* the_clause;
717
    tDamage_condition* the_condition;
718
    tDamage_effect* the_effect;
719
    //tImpact_location modified_location; // Pierre-Marie Baty -- unused variable
720
    LOG_TRACE("(%p, %p, %p, %d)", pCar, pImpact_point, pEnergy_vector, pWas_hitting_a_car);
721
 
722
#if defined(DETHRACE_FIX_BUGS)
723
    proportion_x = 0;
724
    proportion_y = 0;
725
    proportion_z = 0;
726
#endif
727
 
728
    pure_energy_magnitude = BrVector3Length(pEnergy_vector);
729
    if (pure_energy_magnitude == 0.0f && !pWas_hitting_a_car) {
730
        return;
731
    }
732
 
733
    energy_magnitude = pCar->car_model_actors[pCar->principal_car_actor].crush_data.softness_factor * pure_energy_magnitude / 0.7f;
734
    BrVector3InvScale(&crushed_car_bounds.min, &pCar->bounds[1].min, WORLD_SCALE);
735
    BrVector3InvScale(&crushed_car_bounds.max, &pCar->bounds[1].max, WORLD_SCALE);
736
 
737
    x1 = pImpact_point->v[0] - crushed_car_bounds.min.v[0];
738
    x2 = crushed_car_bounds.max.v[0] - pImpact_point->v[0];
739
    if (x1 >= x2) {
740
        x = x2;
741
    } else {
742
        x = x1;
743
    }
744
    y1 = pImpact_point->v[1] - crushed_car_bounds.min.v[1];
745
    y2 = crushed_car_bounds.max.v[1] - pImpact_point->v[1];
746
    if (y1 >= y2) {
747
        y = y2;
748
    } else {
749
        y = y1;
750
    }
751
    z1 = pImpact_point->v[2] - crushed_car_bounds.min.v[2];
752
    z2 = crushed_car_bounds.max.v[2] - pImpact_point->v[2];
753
    if (z1 >= z2) {
754
        z = z2;
755
    } else {
756
        z = z1;
757
    }
758
    if (z > x || z > y) {
759
        if (x > y || x > z) {
760
            impact_location = y1 < y2 ? eImpact_bottom : eImpact_top;
761
            proportion_z = z1 / (crushed_car_bounds.max.v[2] - crushed_car_bounds.min.v[2]);
762
            proportion_x = x1 / (crushed_car_bounds.max.v[0] - crushed_car_bounds.min.v[0]);
763
        } else {
764
            impact_location = x1 >= x2 ? eImpact_right : eImpact_left;
765
            proportion_z = z1 / (crushed_car_bounds.max.v[2] - crushed_car_bounds.min.v[2]);
766
            proportion_y = y1 / (crushed_car_bounds.max.v[1] - crushed_car_bounds.min.v[1]);
767
        }
768
    } else {
769
        impact_location = z1 >= z2 ? eImpact_back : eImpact_front;
770
        proportion_x = x1 / (crushed_car_bounds.max.v[0] - crushed_car_bounds.min.v[0]);
771
        proportion_y = y1 / (crushed_car_bounds.max.v[1] - crushed_car_bounds.min.v[1]);
772
    }
773
    if (pWas_hitting_a_car && pCar->last_impact_location == eImpact_unknown) {
774
        pCar->last_impact_location = impact_location;
775
        pCar->last_col_prop_x = proportion_x;
776
        pCar->last_col_prop_y = proportion_y;
777
        pCar->last_col_prop_z = proportion_z;
778
    }
779
 
780
    if (energy_magnitude != 0.0f && !pCar->invulnerable) {
781
        if (!pWas_hitting_a_car && impact_location == eImpact_bottom) {
782
            energy_magnitude = energy_magnitude / 2.0f;
783
        }
784
 
785
        the_program = &pCar->damage_programs[impact_location];
786
        the_clause = the_program->clauses;
787
        for (i = 0; i < the_program->clause_count; i++) {
788
            result = 1;
789
            the_condition = the_clause->conditions;
790
            for (j = 0; j < the_clause->condition_count; j++) {
791
                switch (the_condition->axis_comp) {
792
                case eAxis_x:
793
                    if (the_condition->condition_operator == eCondition_greater_than) {
794
                        if (the_condition->comparitor >= proportion_x) {
795
                            result = 0;
796
                        }
797
                    } else if (the_condition->comparitor <= proportion_x) {
798
                        result = 0;
799
                    }
800
                    break;
801
 
802
                case eAxis_y:
803
                    if (the_condition->condition_operator == eCondition_greater_than) {
804
                        if (the_condition->comparitor >= proportion_y) {
805
                            result = 0;
806
                        }
807
                    } else if (the_condition->comparitor <= proportion_y) {
808
                        result = 0;
809
                    }
810
                    break;
811
 
812
                case eAxis_z:
813
                    if (the_condition->condition_operator == eCondition_greater_than) {
814
                        if (the_condition->comparitor >= proportion_z) {
815
                            result = 0;
816
                        }
817
                    } else if (the_condition->comparitor <= proportion_z) {
818
                        result = 0;
819
                    }
820
                    break;
821
                }
822
 
823
                if (!result) {
824
                    break;
825
                }
826
                the_condition++;
827
            }
828
            if (result) {
829
                for (j = 0; j < the_clause->effect_count; j++) {
830
                    the_effect = &the_clause->effects[j];
831
                    DoDamage(pCar, the_effect->type, energy_magnitude, the_effect->weakness_factor);
832
                }
833
            }
834
            the_clause++;
835
        }
836
        if (pCar->driver == eDriver_local_human) {
837
            switch (impact_location) {
838
            case eImpact_top:
839
            case eImpact_bottom:
840
                NewScreenWobble(
841
                    FRandomBetween(energy_magnitude * 5.0f, energy_magnitude * 20.0f),
842
                    FRandomBetween(energy_magnitude * 30.0f, energy_magnitude * 60.0f),
843
                    FRandomBetween(1.0f / energy_magnitude, 5.0f / energy_magnitude));
844
                break;
845
            case eImpact_left:
846
                NewScreenWobble(
847
                    FRandomBetween(energy_magnitude * 50.0f, energy_magnitude * 100.0f),
848
                    FRandomBetween(energy_magnitude * 5.0f, energy_magnitude * 20.0f),
849
                    FRandomBetween(4.0f / energy_magnitude, 7.0 / energy_magnitude));
850
                break;
851
            case eImpact_right:
852
                NewScreenWobble(
853
                    -FRandomBetween(energy_magnitude * 50.0f, energy_magnitude * 100.0f),
854
                    FRandomBetween(energy_magnitude * 5.0f, energy_magnitude * 20.0f),
855
                    FRandomBetween(4.0f / energy_magnitude, 7.0f / energy_magnitude));
856
 
857
                break;
858
            case eImpact_front:
859
                NewScreenWobble(
860
                    FRandomBetween(energy_magnitude * 5.0f, energy_magnitude * 20.0f),
861
                    FRandomBetween(energy_magnitude * 50.0f, energy_magnitude * 150.0f),
862
                    FRandomBetween(7.0f / energy_magnitude, 25.0f / energy_magnitude));
863
                break;
864
            case eImpact_back:
865
                NewScreenWobble(
866
                    FRandomBetween(energy_magnitude * 5.0f, energy_magnitude * 20.0f),
867
                    FRandomBetween(-energy_magnitude * 50.0f, energy_magnitude * 150.0f),
868
                    FRandomBetween(7.0f / energy_magnitude, 25.0f / energy_magnitude));
869
                break;
870
            default:
871
                break;
872
            }
873
            CheckPiledriverBonus(pCar, pImpact_point, pEnergy_vector);
874
        }
875
    }
876
}
877
 
878
// IDA: tImpact_location __usercall GetDirection@<EAX>(br_vector3 *pVelocity@<EAX>)
879
tImpact_location GetDirection(br_vector3* pVelocity) {
880
    br_scalar mag_x;
881
    br_scalar mag_y;
882
    br_scalar mag_z;
883
    LOG_TRACE("(%p)", pVelocity);
884
 
885
    mag_x = fabsf(pVelocity->v[0]);
886
    mag_y = fabsf(pVelocity->v[1]);
887
    mag_z = fabsf(pVelocity->v[2]);
888
    if (mag_y >= mag_x || mag_z >= mag_x) {
889
        if (mag_y <= mag_x || mag_z >= mag_y) {
890
            if (pVelocity->v[2] >= 0.0f) {
891
                return eImpact_back;
892
            } else {
893
                return eImpact_front;
894
            }
895
        } else {
896
            return pVelocity->v[1] < 0.0;
897
        }
898
    } else if (pVelocity->v[0] >= 0.0) {
899
        return eImpact_right;
900
    } else {
901
        return eImpact_left;
902
    }
903
}
904
 
905
// IDA: void __usercall SetSmokeLastDamageLevel(tCar_spec *pCar@<EAX>)
906
void SetSmokeLastDamageLevel(tCar_spec* pCar) {
907
    int i;
908
    LOG_TRACE("(%p)", pCar);
909
 
910
    for (i = 0; i < COUNT_OF(pCar->damage_units); i++) {
911
        pCar->damage_units[i].smoke_last_level = pCar->damage_units[i].damage_level;
912
    }
913
}
914
 
915
// IDA: void __usercall SortOutSmoke(tCar_spec *pCar@<EAX>)
916
void SortOutSmoke(tCar_spec* pCar) {
917
    int i;
918
    int colour;
919
    int old_colour;
920
    int step;
921
    //int pass; // Pierre-Marie Baty -- unused variable
922
    //int repeat; // Pierre-Marie Baty -- unused variable
923
    LOG_TRACE("(%p)", pCar);
924
 
925
    if (!pCar || pCar->driver <= eDriver_non_car) {
926
        return;
927
    }
928
    for (i = 0; i < COUNT_OF(pCar->damage_units); i++) {
929
        if (pCar->damage_units[i].damage_level != pCar->damage_units[i].smoke_last_level) {
930
            step = gSmoke_damage_step[i];
931
            if (step) {
932
                if (pCar->damage_units[i].damage_level > pCar->damage_units[i].smoke_last_level) {
933
                    old_colour = (99 - pCar->damage_units[i].smoke_last_level) / step;
934
                    colour = (99 - pCar->damage_units[i].damage_level) / step;
935
                    if (old_colour != colour && colour <= 2) {
936
                        ConditionalSmokeColumn(pCar, i, colour);
937
                    }
938
                }
939
            }
940
        }
941
    }
942
    SetSmokeLastDamageLevel(pCar);
943
}
944
 
945
// IDA: void __usercall StealCar(tCar_spec *pCar@<EAX>)
946
void StealCar(tCar_spec* pCar) {
947
    LOG_TRACE("(%p)", pCar);
948
 
949
    pCar->has_been_stolen = 1;
950
    gProgram_state.cars_available[gProgram_state.number_of_cars] = pCar->index;
951
    gProgram_state.number_of_cars++;
952
    gOpponents[pCar->index].dead = 1;
953
}
954
 
955
// IDA: int __usercall DoCrashEarnings@<EAX>(tCar_spec *pCar1@<EAX>, tCar_spec *pCar2@<EDX>)
956
int DoCrashEarnings(tCar_spec* pCar1, tCar_spec* pCar2) {
957
    tCar_spec* culprit;
958
    tCar_spec* victim;
959
    int i;
960
    int net_loop;
961
    int head_on;
962
    int bonus_level;
963
    int credits;
964
    int impact_in_moving_direction_1;
965
    int impact_in_moving_direction_2;
966
    int car_off_ground_1;
967
    int car_off_ground_2;
968
    int total_units_of_damage;
969
    int inherited_damage;
970
    int dam_acc_1;
971
    int dam_acc_2;
972
    int car_1_culpable;
973
    int car_2_culpable;
974
    int mutual_culpability;
975
    tU32 the_time;
976
    tU32 time;
977
    float credits_squared;
978
    static tU32 last_earn_time;
979
    //char s[256]; // Pierre-Marie Baty -- unused variable
980
    tImpact_location modified_location_1;
981
    tImpact_location modified_location_2;
982
    tImpact_location car_direction_1;
983
    tImpact_location car_direction_2;
984
    br_scalar car_1_height;
985
    br_scalar car_2_height;
986
    br_scalar dp;
987
    br_vector3 car_1_pos;
988
    br_vector3 car_2_pos;
989
    br_vector3 car_1_offset;
990
    br_vector3 car_2_offset;
991
    tNet_message* message;
992
    LOG_TRACE("(%p, %p)", pCar1, pCar2);
993
 
994
    culprit = 0;
995
    victim = 0;
996
    head_on = 0;
997
    bonus_level = 1;
998
    car_1_culpable = 0;
999
    car_2_culpable = 0;
1000
    mutual_culpability = 0;
1001
    the_time = PDGetTotalTime();
1002
    inherited_damage = 0;
1003
#if defined(DETHRACE_FIX_BUGS)
1004
    total_units_of_damage = 0;
1005
#endif
1006
    if (pCar1->driver <= eDriver_non_car) {
1007
        dam_acc_1 = 0;
1008
    } else {
1009
        dam_acc_1 = pCar1->damage_magnitude_accumulator;
1010
    }
1011
 
1012
    dam_acc_2 = 0;
1013
    if (pCar2 != NULL) {
1014
        if (pCar2->driver <= eDriver_non_car) {
1015
            dam_acc_2 = 0;
1016
        } else {
1017
            dam_acc_2 = pCar2->damage_magnitude_accumulator != 0;
1018
        }
1019
    }
1020
 
1021
    if (pCar1->driver <= eDriver_non_car) {
1022
        if (pCar2 == NULL || pCar2->driver <= eDriver_non_car) {
1023
            return 0;
1024
        }
1025
        pCar1 = pCar2;
1026
        pCar2 = NULL;
1027
    }
1028
    if (pCar2 != NULL && pCar2->driver <= eDriver_non_car) {
1029
        pCar2 = NULL;
1030
    }
1031
 
1032
    if (pCar1->pre_car_col_knackered || (pCar2 && pCar2->pre_car_col_knackered) || (pCar2 && pCar2->damage_magnitude_accumulator <= 0.00005f && pCar1->damage_magnitude_accumulator <= 0.00005f)) {
1033
        return dam_acc_1 || (pCar2 && dam_acc_2);
1034
    }
1035
 
1036
    modified_location_1 = CalcModifiedLocation(pCar1);
1037
    car_direction_1 = GetDirection(&pCar1->pre_car_col_velocity_car_space);
1038
    impact_in_moving_direction_1 = car_direction_1 == modified_location_1;
1039
    if (pCar2 != NULL) {
1040
        modified_location_2 = CalcModifiedLocation(pCar2);
1041
        car_direction_2 = GetDirection(&pCar2->pre_car_col_velocity_car_space);
1042
        impact_in_moving_direction_2 = car_direction_2 == modified_location_2;
1043
    }
1044
    if (pCar1->driver >= eDriver_net_human && pCar2) {
1045
        if (impact_in_moving_direction_1 && (pCar1->driver < eDriver_net_human || (pCar1->pre_car_col_velocity_car_space.v[2] != 0.0 && (pCar1->pre_car_col_velocity_car_space.v[2] > 0.0) != (pCar1->gear > 0) && (pCar1->keys.acc != 0 || pCar1->joystick.acc > 0x8000)))) {
1046
            pCar2->time_last_hit = the_time;
1047
            pCar2->last_hit_by = pCar1;
1048
        }
1049
    } else if (pCar2 && pCar2->driver >= eDriver_net_human && impact_in_moving_direction_2 && (pCar2->driver < eDriver_net_human || (pCar2->pre_car_col_velocity_car_space.v[2] != 0.0f && (pCar2->pre_car_col_velocity_car_space.v[2] > 0.0f) != (pCar2->gear > 0) && (pCar2->keys.acc != 0 || pCar2->joystick.acc > 0x8000)))) {
1050
        pCar1->time_last_hit = the_time;
1051
        pCar1->last_hit_by = pCar2;
1052
    }
1053
    if (pCar2) {
1054
        if (impact_in_moving_direction_1
1055
            && pCar1->pre_car_col_speed * 5.0f > pCar2->pre_car_col_speed
1056
            && pCar1->pre_car_col_speed > 0.0005f
1057
            && (pCar1->driver < eDriver_net_human
1058
                || (pCar1->pre_car_col_velocity_car_space.v[2] != 0.0f
1059
                    && (pCar1->pre_car_col_velocity_car_space.v[2] > 0.0f) != (pCar1->gear > 0)
1060
                    && (pCar1->keys.acc != 0 || pCar1->joystick.acc > 0x8000)))) {
1061
            car_1_culpable = 1;
1062
        }
1063
        if (impact_in_moving_direction_2
1064
            && pCar2->pre_car_col_speed * 5.0f > pCar1->pre_car_col_speed
1065
            && pCar2->pre_car_col_speed > 0.0005f
1066
            && (pCar2->driver < eDriver_net_human
1067
                || (pCar2->pre_car_col_velocity_car_space.v[2] != 0.0f
1068
                    && (pCar2->pre_car_col_velocity_car_space.v[2] > 0.0f) != (pCar2->gear > 0)
1069
                    && (pCar2->keys.acc != 0 || pCar2->joystick.acc > 0x8000)))) {
1070
            car_2_culpable = 1;
1071
        }
1072
        if (gNet_mode && car_1_culpable && car_2_culpable) {
1073
            mutual_culpability = 1;
1074
        } else {
1075
            if (car_2_culpable && pCar2->driver == eDriver_local_human) {
1076
                car_1_culpable = 0;
1077
            }
1078
            if (car_1_culpable) {
1079
                culprit = pCar1;
1080
                victim = pCar2;
1081
                dp = BrVector3Dot(&pCar1->pre_car_col_direction, &pCar2->pre_car_col_direction);
1082
                if (modified_location_1 == eImpact_front && modified_location_2 == eImpact_front && pCar1->pre_car_col_speed > 0.001f && pCar2->pre_car_col_speed > 0.001f && dp < -0.7f) {
1083
                    head_on = 1;
1084
                    bonus_level = 2;
1085
                } else {
1086
                    bonus_level = 1;
1087
                }
1088
            } else if (car_2_culpable) {
1089
                culprit = pCar2;
1090
                victim = pCar1;
1091
                dp = BrVector3Dot(&pCar1->pre_car_col_direction, &pCar2->pre_car_col_direction);
1092
                if (modified_location_1 == eImpact_front && modified_location_2 == eImpact_front && pCar1->pre_car_col_speed > 0.001f && pCar2->pre_car_col_speed > 0.001f && dp < -0.7f) {
1093
                    head_on = 1;
1094
                    bonus_level = 2;
1095
                } else {
1096
                    bonus_level = 1;
1097
                }
1098
            }
1099
        }
1100
    } else {
1101
        if (the_time - pCar1->time_last_hit >= 3000) {
1102
            return 1;
1103
        }
1104
        culprit = pCar1->last_hit_by;
1105
        victim = pCar1;
1106
        bonus_level = 1;
1107
        inherited_damage = 1;
1108
    }
1109
    if (!mutual_culpability && (!victim || culprit->driver < eDriver_net_human)) {
1110
        if (pCar2 && pCar2->last_culprit == pCar1 && the_time - pCar2->time_last_victim < 750) {
1111
            inherited_damage = 1;
1112
            culprit = pCar1;
1113
            victim = pCar2;
1114
        } else if (pCar2 && pCar1->last_culprit == pCar2 && the_time - pCar1->time_last_victim < 750) {
1115
            inherited_damage = 1;
1116
            culprit = pCar2;
1117
            victim = pCar1;
1118
        } else if (!pCar2 && the_time - pCar1->time_last_victim < 750) {
1119
            inherited_damage = 1;
1120
            culprit = pCar1->last_culprit;
1121
            victim = pCar1;
1122
        }
1123
    }
1124
    if (culprit && victim) {
1125
        RecordOpponentTwattageOccurrence(culprit, victim);
1126
        total_units_of_damage = 0;
1127
        for (i = 0; i < COUNT_OF(victim->damage_units); i++) {
1128
            if (victim->damage_units[i].damage_level > victim->damage_units[i].last_level) {
1129
                victim->damage_units[i].damage_level = (victim->damage_units[i].damage_level - victim->damage_units[i].last_level) * 2.0f + victim->damage_units[i].last_level;
1130
                if (victim->damage_units[i].damage_level >= 99) {
1131
                    victim->damage_units[i].damage_level = 99;
1132
                }
1133
                total_units_of_damage += victim->damage_units[i].damage_level - victim->damage_units[i].last_level;
1134
            }
1135
            if (culprit->damage_units[i].damage_level > culprit->damage_units[i].last_level) {
1136
                culprit->damage_units[i].damage_level = (culprit->damage_units[i].damage_level - culprit->damage_units[i].last_level) * 0.1f + (double)culprit->damage_units[i].last_level;
1137
                if (culprit->damage_units[i].damage_level < 0) {
1138
                    culprit->damage_units[i].damage_level = 0;
1139
                }
1140
            }
1141
        }
1142
    }
1143
    // TODO: tidy this up
1144
    for (net_loop = 0; 2 - (mutual_culpability == 0) > net_loop; net_loop++) {
1145
        if (mutual_culpability) {
1146
            if (net_loop) {
1147
                culprit = pCar1;
1148
                victim = pCar2;
1149
            } else {
1150
                culprit = pCar2;
1151
                victim = pCar1;
1152
            }
1153
            total_units_of_damage = 0;
1154
            for (i = 0; i < COUNT_OF(victim->damage_units); i++) {
1155
                if (victim->damage_units[i].damage_level > victim->damage_units[i].last_level) {
1156
                    total_units_of_damage += victim->damage_units[i].damage_level - victim->damage_units[i].last_level;
1157
                }
1158
            }
1159
        }
1160
        if (culprit && (culprit->driver == eDriver_local_human || gNet_mode) && victim) {
1161
            SetKnackeredFlag(victim);
1162
            if (victim->knackered && !victim->pre_car_col_knackered) {
1163
                victim->pre_car_col_knackered = 1;
1164
                credits_squared = sqr(0.7f / victim->car_model_actors[victim->principal_car_actor].crush_data.softness_factor) * gWasted_creds[gProgram_state.skill_level] + 50.0f;
1165
                credits = 100 * (int)(credits_squared / 100.0f);
1166
                if (gNet_mode) {
1167
                    message = NetBuildMessage(0x18u, 0);
1168
                    message->contents.data.wasted.victim = NetPlayerFromCar(victim)->ID;
1169
                    if (NetPlayerFromCar(culprit)) {
1170
                        message->contents.data.wasted.culprit = NetPlayerFromCar(culprit)->ID;
1171
                    } else {
1172
                        message->contents.data.wasted.culprit = -2;
1173
                    }
1174
                    NetGuaranteedSendMessageToEverybody(gCurrent_net_game, message, NULL);
1175
                    NetEarnCredits(NetPlayerFromCar(culprit), credits);
1176
                } else {
1177
                    PratcamEvent(32);
18 pmbaty 1178
                    DoFancyHeadup(kFancyHeadupYouWastedEm);
1 pmbaty 1179
                    credits_squared = sqr(0.7f / victim->car_model_actors[victim->principal_car_actor].crush_data.softness_factor) * gWasted_creds[gProgram_state.skill_level] + 50.0f;
1180
                    credits = 100 * (int)(credits_squared / 100.0);
1181
                    AwardTime(gWasted_time[gProgram_state.skill_level]);
1182
                    EarnCredits(credits);
15 pmbaty 1183
                    if (victim->can_be_stolen && !gOpponents[victim->index].dead
1184
                        // strength_rating is between 1 and 5
1185
                        && ((PercentageChance(50) && gProgram_state.rank <= gSteal_ranks[gOpponents[victim->index].strength_rating - 1]) || victim->index == BIGAPC_OPPONENT_INDEX)) {
1 pmbaty 1186
                        StealCar(victim);
1187
                    }
1188
                }
1189
            }
1190
            victim->time_last_hit = the_time;
1191
            victim->last_hit_by = culprit;
1192
            if (!inherited_damage) {
1193
                victim->time_last_victim = the_time;
1194
                victim->last_culprit = culprit;
1195
            }
1196
            if (victim && (fabs(victim->omega.v[0]) > 4.0f || fabs(victim->omega.v[1]) > 6.0f || fabs(victim->omega.v[2]) > 4.0f)) {
1197
                bonus_level *= 2;
1198
            }
1199
            if (pCar1->number_of_wheels_on_ground) {
1200
                car_off_ground_1 = 0;
1201
            } else {
1202
                BrVector3InvScale(&car_1_pos, &pCar1->car_master_actor->t.t.translate.t, WORLD_SCALE);
1203
                BrMatrix34ApplyV(&car_1_offset, &pCar1->car_model_actors[pCar1->principal_car_actor].actor->t.t.translate.t, &pCar1->car_master_actor->t.t.mat);
1204
                BrVector3Accumulate(&car_1_pos, &car_1_offset);
1205
                car_1_pos.v[1] += 0.15f;
1206
                car_1_height = FindYVerticallyBelow2(&car_1_pos);
1207
                car_off_ground_1 = car_1_height > -100.0f
1208
                    && pCar1->car_model_actors[pCar1->principal_car_actor].actor->t.t.translate.t.v[1] * 4.0f <= car_1_pos.v[1] - car_1_height - 0.15f;
1209
            }
1210
            if (!pCar2 || pCar2->number_of_wheels_on_ground) {
1211
                car_off_ground_2 = 0;
1212
            } else {
1213
                BrVector3InvScale(&car_2_pos, &pCar2->car_master_actor->t.t.translate.t, WORLD_SCALE);
1214
                BrMatrix34ApplyV(&car_2_offset, &pCar2->car_model_actors[pCar2->principal_car_actor].actor->t.t.translate.t, &pCar2->car_master_actor->t.t.mat);
1215
                BrVector3Accumulate(&car_2_pos, &car_2_offset);
1216
                car_2_pos.v[1] += 0.15f;
1217
                car_2_height = FindYVerticallyBelow2(&car_2_pos);
1218
                car_off_ground_2 = car_2_height > -100.0f
1219
                    && pCar2->car_model_actors[pCar2->principal_car_actor].actor->t.t.translate.t.v[1] * 4.0f <= car_2_pos.v[1] - car_2_height - 0.15f;
1220
            }
1221
            if (car_off_ground_1) {
1222
                bonus_level *= 2;
1223
            }
1224
            if (car_off_ground_2) {
1225
                bonus_level *= 2;
1226
            }
1227
            total_units_of_damage = 0.7f / victim->car_model_actors[victim->principal_car_actor].crush_data.softness_factor * total_units_of_damage;
1228
            if (!victim->has_been_stolen) {
1229
                credits = 100 * (int)((gCar_cred_value[gProgram_state.skill_level] * MIN(bonus_level, 8) * total_units_of_damage + 50.0f) / 100.0f);
1230
                if (credits || victim->knackered) {
1231
                    if (!victim->knackered) {
1232
                        if (gNet_mode) {
1233
                            NetEarnCredits(NetPlayerFromCar(culprit), MIN(credits, 2000));
1234
                        } else {
1235
                            EarnCredits(MIN(credits, 2000));
1236
                        }
1237
                        last_earn_time = the_time;
1238
                        if (gNet_mode == eNet_mode_none) {
1239
                            time = 5 * (int)((total_units_of_damage * gCar_time_value[gProgram_state.skill_level] + 2.5f) / 5.0f);
1240
                            AwardTime(MIN(time, 90));
1241
                            if (pCar2) {
1242
                                if (head_on) {
18 pmbaty 1243
                                    DoFancyHeadup(kFancyHeadupHeadOnBonus);
1 pmbaty 1244
                                } else if (bonus_level <= 2) {
1245
                                    if (bonus_level > 1) {
18 pmbaty 1246
                                        DoFancyHeadup(kFancyHeadupExtraStyleBonus);
1 pmbaty 1247
                                    }
1248
                                } else {
18 pmbaty 1249
                                    DoFancyHeadup(kFancyHeadupBonusForArtisticImpression);
1 pmbaty 1250
                                }
1251
                            }
1252
                        }
1253
                    }
1254
                    for (i = 0; i < COUNT_OF(victim->damage_units); i++) {
1255
                        victim->damage_units[i].last_level = victim->damage_units[i].damage_level;
1256
                    }
1257
                }
1258
            }
1259
        } else {
1260
            pCar1->time_last_hit = 0;
1261
            if (pCar2) {
1262
                pCar2->time_last_hit = 0;
1263
            }
1264
        }
1265
    }
1266
    pCar1->damage_magnitude_accumulator = 0.0f;
1267
    if (pCar2) {
1268
        pCar2->damage_magnitude_accumulator = 0.0f;
1269
    }
1270
    return 1;
1271
}
1272
 
1273
// IDA: void __usercall DoWheelDamage(tU32 pFrame_period@<EAX>)
1274
void DoWheelDamage(tU32 pFrame_period) {
1275
    int i;
1276
    int j;
1277
    int damage;
1278
    tCar_spec* car;
1279
    br_scalar y_amount;
1280
    br_scalar z_amount;
1281
    br_scalar wheel_circum;
1282
    br_scalar old_offset;
1283
    br_vector3 temp_vector;
1284
    br_vector3 wonky_vector;
1285
    static int kev_index[4];
1286
    LOG_TRACE("(%d)", pFrame_period);
1287
 
1288
    if (gAction_replay_mode && ReplayIsPaused()) {
1289
        return;
1290
    }
1291
 
1292
    for (i = 0; i < gNum_active_cars; i++) {
1293
        car = gActive_car_list[i];
1294
        for (j = 0; j < COUNT_OF(car->wheel_dam_offset); j++) {
1295
            if (car->wheel_actors[j] == NULL) {
1296
                continue;
1297
            }
1298
            old_offset = car->wheel_dam_offset[j];
1299
            damage = car->damage_units[j + 8].damage_level;
1300
            if (damage <= 30 || gRace_finished) {
1301
                car->wheel_dam_offset[j] = 0.0f;
1302
                continue;
1303
            }
1304
            if (PointOutOfSight(&car->pos, 32.0f)) {
1305
                break;
1306
            }
1307
            y_amount = (damage - 30) * gWobble_spam_y[damage & 7];
1308
            z_amount = (damage - 30) * gWobble_spam_z[damage & 7];
1309
            BrMatrix34PreRotateY(&car->wheel_actors[j]->t.t.mat, BrDegreeToAngle(y_amount < 0 ? y_amount + 360.f : y_amount));
1310
            BrMatrix34PreRotateZ(&car->wheel_actors[j]->t.t.mat, BrDegreeToAngle(z_amount < 0 ? z_amount + 360.f : z_amount));
1311
            if (j < 2 && car->wheel_actors[j + 4] != NULL) {
1312
                BrMatrix34PreRotateY(&car->wheel_actors[j + 4]->t.t.mat, BrDegreeToAngle(y_amount < 0 ? y_amount + 360.f : y_amount));
1313
                BrMatrix34PreRotateZ(&car->wheel_actors[j + 4]->t.t.mat, BrDegreeToAngle(z_amount < 0 ? z_amount + 360.f : z_amount));
1314
            }
1315
            switch (j) {
1316
            case 0:
1317
                if (car->driven_wheels_spin_ref_1 < 0) {
1318
                    wheel_circum = car->non_driven_wheels_circum;
1319
                } else {
1320
                    wheel_circum = car->driven_wheels_circum;
1321
                }
1322
                break;
1323
            case 1:
1324
                if (car->driven_wheels_spin_ref_2 < 0) {
1325
                    wheel_circum = car->non_driven_wheels_circum;
1326
                } else {
1327
                    wheel_circum = car->driven_wheels_circum;
1328
                }
1329
                break;
1330
            case 2:
1331
                if (car->driven_wheels_spin_ref_3 < 0) {
1332
                    wheel_circum = car->non_driven_wheels_circum;
1333
                } else {
1334
                    wheel_circum = car->driven_wheels_circum;
1335
                }
1336
                break;
1337
            case 3:
1338
                if (car->driven_wheels_spin_ref_4 < 0) {
1339
                    wheel_circum = car->non_driven_wheels_circum;
1340
                } else {
1341
                    wheel_circum = car->driven_wheels_circum;
1342
                }
1343
                break;
1344
            default:
1345
                TELL_ME_IF_WE_PASS_THIS_WAY();
1346
                break;
1347
            }
1348
            if (gNet_mode == eNet_mode_none || car->driver == eDriver_local_human) {
1349
                BrVector3Set(&temp_vector, wheel_circum * gWheel_circ_to_width, 0.f, 0.f);
1350
                BrMatrix34ApplyV(&wonky_vector, &temp_vector, &car->wheel_actors[j]->t.t.mat);
1351
                car->wheel_dam_offset[j] = fabsf(wonky_vector.v[1]);
1352
            }
1353
        }
1354
    }
1355
}
1356
 
1357
// IDA: void __usercall CrashEarnings(tCar_spec *pCar1@<EAX>, tCar_spec *pCar2@<EDX>)
1358
void CrashEarnings(tCar_spec* pCar1, tCar_spec* pCar2) {
1359
    LOG_TRACE("(%p, %p)", pCar1, pCar2);
1360
 
1361
    if (DoCrashEarnings(pCar1, pCar2)) {
1362
        SortOutSmoke(pCar1);
1363
        SortOutSmoke(pCar2);
1364
    }
1365
}