Subversion Repositories Games.Carmageddon

Rev

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

Rev Author Line No. Line
1 pmbaty 1
#include "spark.h"
18 pmbaty 2
#include "brender.h"
1 pmbaty 3
#include "car.h"
18 pmbaty 4
#include "crush.h"
1 pmbaty 5
#include "depth.h"
18 pmbaty 6
#include "displays.h"
1 pmbaty 7
#include "errors.h"
18 pmbaty 8
#include "formats.h"
1 pmbaty 9
#include "globvars.h"
10
#include "globvrkm.h"
11
#include "graphics.h"
12
#include "harness/hooks.h"
13
#include "harness/trace.h"
14
#include "loading.h"
15
#include "opponent.h"
16
#include "piping.h"
17
#include "replay.h"
18
#include "trig.h"
19
#include "utility.h"
20
#include "world.h"
18 pmbaty 21
#include <math.h>
1 pmbaty 22
#include <stdlib.h>
23
 
24
int gNext_spark;
25
int gSpark_flags;
26
int gNext_shrapnel;
27
int gShrapnel_flags;
28
br_model* gShrapnel_model[2];
29
int gSmoke_flags;
30
int gSmoke_num;
31
int gOffset = 0;
32
int gColumn_flags;
33
int gNext_column;
34
br_pixelmap* gBlack_smoke_shade_table;
35
br_pixelmap* gDark_smoke_shade_table;
36
br_pixelmap* gGrey_smoke_shade_table;
37
int gSmoke_on = 1;
38
int gNum_splash_types;
39
int gIt_type;
40
br_pixelmap* gIt_shade_table;
41
br_pixelmap** gDust_table = &gShade_list[8];
42
br_pixelmap* gFlame_map[20];
43
tBRender_smoke* gBR_smoke_pointers[30];
44
tSplash gSplash[32];
45
br_material* gSplash_material[20];
46
tBRender_smoke gBR_smoke_structs[30];
47
tSmoke_column gSmoke_column[25];
48
br_matrix4 gCameraToScreen;
49
tSpark gSparks[32];
50
br_pixelmap* gShade_list[16];
51
int gN_BR_smoke_structs;
52
tSmoke gSmoke[25];
53
tU32 gSplash_flags;
54
tU32 gNext_splash;
55
br_model* gLollipop_model;
56
int gNum_dust_tables;
57
br_model* gSplash_model;
58
int gDust_rotate;
59
br_camera* gSpark_cam;
60
br_material* gBlack_material;
61
tShrapnel gShrapnel[15];
62
 
63
// gSmoke_column has 25 elements but all the code just checks the first 5 elements
64
#define MAX_SMOKE_COLUMNS 5
65
 
66
// Bugfix: At higher FPS, flame animation runs too quickly, so introduce a new frame timer
67
#define FLAME_ANIMATION_FRAME_INTERVAL 40
68
 
69
// Bugfix: At higher FPS, `CreatePuffOfSmoke` is called too often and causes smoke cirlces to be recycled too quickly so assume around 25fps
70
#define SMOKE_COLUMN_NEW_PUFF_INTERVAL 30
71
 
72
// IDA: void __cdecl DrawDot(br_scalar z, tU8 *scr_ptr, tU16 *depth_ptr, tU8 *shade_ptr)
73
void DrawDot(br_scalar z, tU8* scr_ptr, tU16* depth_ptr, tU8* shade_ptr) {
74
    LOG_TRACE("(%f, %p, %p, %p)", z, scr_ptr, depth_ptr, shade_ptr);
75
 
76
    if (*depth_ptr > (1.0 - z) * 32768.0f) {
77
        *depth_ptr = (1.0 - z) * 32768.0f;
78
        *scr_ptr = shade_ptr[*scr_ptr];
79
    }
80
}
81
 
82
// IDA: void __usercall SetWorldToScreen(br_pixelmap *pScreen@<EAX>)
83
void SetWorldToScreen(br_pixelmap* pScreen) {
84
    br_matrix4 mat;
85
    br_matrix4 mat2;
86
    LOG_TRACE("(%p)", pScreen);
87
 
88
    BrMatrix4Perspective(&mat, gSpark_cam->field_of_view, gSpark_cam->aspect, -gSpark_cam->hither_z, -gSpark_cam->yon_z);
89
    BrMatrix4Scale(&mat2, pScreen->width / 2, pScreen->height / 2, 1.0f);
90
    BrMatrix4Mul(&gCameraToScreen, &mat, &mat2);
91
}
92
 
93
// IDA: void __usercall DrawLine3DThroughBRender(br_vector3 *pStart@<EAX>, br_vector3 *pEnd@<EDX>)
94
void DrawLine3DThroughBRender(br_vector3* pStart, br_vector3* pEnd) {
95
    LOG_TRACE("(%p, %p)", pStart, pEnd);
96
    NOT_IMPLEMENTED();
97
}
98
 
99
// IDA: int __usercall DrawLine3D@<EAX>(br_vector3 *start@<EAX>, br_vector3 *end@<EDX>, br_pixelmap *pScreen@<EBX>, br_pixelmap *pDepth_buffer@<ECX>, br_pixelmap *shade_table)
100
int DrawLine3D(br_vector3* start, br_vector3* end, br_pixelmap* pScreen, br_pixelmap* pDepth_buffer, br_pixelmap* shade_table) {
101
    br_vector3 o;
102
    br_vector3 p;
103
    //br_vector3 tv; // Pierre-Marie Baty -- unused variable
104
    br_vector4 o2;
105
    br_vector4 p2;
106
    br_scalar ts;
107
    LOG_TRACE("(%p, %p, %p, %p, %p)", start, end, pScreen, pDepth_buffer, shade_table);
108
 
109
    o = *start;
110
    p = *end;
111
    if (-gSpark_cam->hither_z < o.v[2] || -gSpark_cam->hither_z < p.v[2]) {
112
        if (-gSpark_cam->hither_z < o.v[2] && -gSpark_cam->hither_z < p.v[2]) {
113
            return 0;
114
        }
115
        ts = (p.v[2] + gSpark_cam->hither_z) / (p.v[2] - o.v[2]);
116
        if (-gSpark_cam->hither_z < o.v[2]) {
117
            o.v[0] = p.v[0] - (p.v[0] - o.v[0]) * ts;
118
            o.v[1] = p.v[1] - (p.v[1] - o.v[1]) * ts;
119
            o.v[2] = -gSpark_cam->hither_z;
120
        }
121
        if (-gSpark_cam->hither_z < p.v[2]) {
122
            p.v[0] = p.v[0] - (p.v[0] - o.v[0]) * ts;
123
            p.v[1] = p.v[1] - (p.v[1] - o.v[1]) * ts;
124
            p.v[2] = -gSpark_cam->hither_z;
125
        }
126
    }
127
    BrMatrix4ApplyP(&o2, &o, &gCameraToScreen);
128
    BrMatrix4ApplyP(&p2, &p, &gCameraToScreen);
129
    o.v[0] = o2.v[0] / o2.v[3];
130
    o.v[1] = o2.v[1] / o2.v[3];
131
    o.v[2] = o2.v[2] / o2.v[3];
132
    p.v[0] = p2.v[0] / p2.v[3];
133
    p.v[1] = p2.v[1] / p2.v[3];
134
    p.v[2] = p2.v[2] / p2.v[3];
135
    return DrawLine2D(&o, &p, pScreen, pDepth_buffer, 1.0, shade_table);
136
}
137
 
138
// IDA: int __usercall DrawLine2D@<EAX>(br_vector3 *o@<EAX>, br_vector3 *p@<EDX>, br_pixelmap *pScreen@<EBX>, br_pixelmap *pDepth_buffer@<ECX>, br_scalar brightness, br_pixelmap *shade_table)
139
int DrawLine2D(br_vector3* o, br_vector3* p, br_pixelmap* pScreen, br_pixelmap* pDepth_buffer, br_scalar brightness, br_pixelmap* shade_table) {
140
    tU8* scr_ptr;
141
    tU16* depth_ptr;
142
    tU8* shade_ptr;
143
    int x1;
144
    int x2;
145
    int y1;
146
    int y2;
147
    int d;
148
    int dx;
149
    int dy;
150
    int ax;
151
    int sx;
152
    int ay;
153
    int sy;
154
    int x;
155
    int y;
156
    br_scalar zbuff;
157
    br_scalar zbuff_inc;
158
    int darken_count;
159
    int darken_init;
160
    LOG_TRACE("(%p, %p, %p, %p, %f, %p)", o, p, pScreen, pDepth_buffer, brightness, shade_table);
161
 
162
    scr_ptr = (tU8*)pScreen->pixels + pScreen->base_x + pScreen->base_y * pScreen->row_bytes;
163
    depth_ptr = (tU16*)pDepth_buffer->pixels;
164
    shade_ptr = (tU8*)shade_table->pixels + shade_table->base_y * shade_table->row_bytes;
165
    x1 = pScreen->width / 2 + o->v[0];
166
    x2 = pScreen->width / 2 + p->v[0];
167
    y1 = pScreen->height / 2 - o->v[1];
168
    y2 = pScreen->height / 2 - p->v[1];
169
    if (brightness < 0.001 || brightness > 1.0) {
170
        return 0;
171
    }
172
    if (x1 < 0 || x2 < 0) {
173
        if (x1 < 0 && x2 < 0) {
174
            return 0;
175
        }
176
        if (x1 >= 0) {
177
            y2 = y1 - x1 * (y1 - y2) / (x1 - x2);
178
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (float)x1 / (float)(x1 - x2);
179
            x2 = 0;
180
        } else {
181
            y1 = y2 - x2 * (y2 - y1) / (x2 - x1);
182
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (float)x2 / (float)(x2 - x1);
183
            x1 = 0;
184
        }
185
    }
186
    if (pScreen->width <= x1 || pScreen->width <= x2) {
187
        if (pScreen->width <= x1 && pScreen->width <= x2) {
188
            return 0;
189
        }
190
        if (pScreen->width > x1) {
191
            y2 = y1 - (y1 - y2) * (x1 - (pScreen->width - 1)) / (x1 - x2);
192
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (x1 - (float)(pScreen->width - 1)) / (float)(x1 - x2);
193
            x2 = pScreen->width - 1;
194
        } else {
195
            y1 = y2 - (y2 - y1) * (x2 - (pScreen->width - 1)) / (x2 - x1);
196
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (x2 - (float)(pScreen->width - 1)) / (float)(x2 - x1);
197
            x1 = pScreen->width - 1;
198
        }
199
    }
200
    if (y1 < 0 || y2 < 0) {
201
        if (y1 < 0 && y2 < 0) {
202
            return 0;
203
        }
204
        if (y1 >= 0) {
205
            x2 = x1 - y1 * (x1 - x2) / (y1 - y2);
206
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (float)y1 / (float)(y1 - y2);
207
            y2 = 0;
208
        } else {
209
            x1 = x2 - y2 * (x2 - x1) / (y2 - y1);
210
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (float)y2 / (float)(y2 - y1);
211
            y1 = 0;
212
        }
213
    }
214
    if (pScreen->height <= y1 || pScreen->height <= y2) {
215
        if (pScreen->height <= y1 && pScreen->height <= y2) {
216
            return 0;
217
        }
218
        if (pScreen->height > y1) {
219
            x2 = x1 - (x1 - x2) * (y1 - (pScreen->height - 1)) / (y1 - y2);
220
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (float)(y1 - (pScreen->height - 1)) / (float)(y1 - y2);
221
            y2 = pScreen->height - 1;
222
        } else {
223
            x1 = x2 - (x2 - x1) * (y2 - (pScreen->height - 1)) / (y2 - y1);
224
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (float)(y2 - (pScreen->height - 1)) / (float)(y2 - y1);
225
            y1 = pScreen->height - 1;
226
        }
227
    }
228
    zbuff = o->v[2];
229
    dx = x2 - x1;
230
    dy = y2 - y1;
231
    ax = 2 * abs(dx);
232
    if (x2 - x1 < 0) {
233
        sx = -1;
234
    } else {
235
        sx = 1;
236
    }
237
    ay = 2 * abs(dy);
238
    if (dy < 0) {
239
        sy = -1;
240
    } else {
241
        sy = 1;
242
    }
243
    x = x1;
244
    y = y1;
245
    scr_ptr += x1 + y1 * pScreen->row_bytes;
246
    depth_ptr += x1 + y1 * (pDepth_buffer->row_bytes / 2);
247
    darken_init = (brightness - 0.001) * (float)shade_table->height;
248
    if (ay >= ax) {
249
        d = ax - ay / 2;
250
        darken_init = 500 * ay / darken_init;
251
        darken_count = darken_init;
252
        zbuff_inc = (p->v[2] - o->v[2]) * 2.0 / (float)ay;
253
        while (1) {
254
            DrawDot(zbuff, scr_ptr, depth_ptr, shade_ptr);
255
            if (y == y2) {
256
                break;
257
            }
258
            if (d >= 0) {
259
                scr_ptr += sx;
260
                depth_ptr += sx;
261
                d -= ay;
262
            }
263
            y += sy;
264
            d += ax;
265
            scr_ptr += sy * pScreen->row_bytes;
266
            depth_ptr += sy * (pDepth_buffer->row_bytes / 2);
267
            zbuff = zbuff_inc + zbuff;
268
            darken_count -= 1000;
269
            while (darken_count <= 0) {
270
                darken_count += darken_init;
271
                shade_ptr += shade_table->row_bytes;
272
            }
273
        }
274
    } else {
275
        d = ay - ax / 2;
276
        darken_init = 500 * ax / darken_init;
277
        darken_count = darken_init;
278
        zbuff_inc = (p->v[2] - o->v[2]) * 2.0 / (float)ax;
279
        while (1) {
280
            DrawDot(zbuff, scr_ptr, depth_ptr, shade_ptr);
281
            if (x == x2) {
282
                break;
283
            }
284
            if (d >= 0) {
285
                scr_ptr += sy * pScreen->row_bytes;
286
                depth_ptr += sy * (pDepth_buffer->row_bytes / 2);
287
                d -= ax;
288
            }
289
            x += sx;
290
            scr_ptr += sx;
291
            depth_ptr += sx;
292
            d += ay;
293
            zbuff = zbuff_inc + zbuff;
294
            darken_count -= 1000;
295
            while (darken_count <= 0) {
296
                darken_count += darken_init;
297
                shade_ptr += shade_table->row_bytes;
298
            }
299
        }
300
    }
301
    return 1;
302
}
303
 
304
// IDA: void __usercall SetLineModelCols(tU8 pCol@<EAX>)
305
void SetLineModelCols(tU8 pCol) {
306
    LOG_TRACE("(%d)", pCol);
307
    NOT_IMPLEMENTED();
308
}
309
 
310
// IDA: void __usercall ReplaySparks(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, tU32 pTime@<ECX>)
311
void ReplaySparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera, tU32 pTime) {
312
    int i;
313
    br_vector3 pos;
314
    br_vector3 o;
315
    br_vector3 p;
316
    br_vector3 tv;
317
    br_vector3 new_pos;
318
    LOG_TRACE("(%p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pTime);
319
 
320
    for (i = 0; i < COUNT_OF(gSparks); i++) {
321
        if (gSpark_flags & (1 << i)) {
322
            if (gSparks[i].car == NULL) {
323
                BrVector3Copy(&pos, &gSparks[i].pos);
324
            } else {
325
                BrMatrix34ApplyP(&tv, &o, &gSparks[i].car->car_master_actor->t.t.mat);
326
                BrVector3Copy(&o, &tv);
327
                BrMatrix34ApplyP(&pos, &gSparks[i].pos, &gSparks[i].car->car_master_actor->t.t.mat);
328
            }
329
            BrVector3Add(&o, &pos, &gSparks[i].length);
330
            BrVector3Sub(&tv, &pos, (br_vector3*)gCamera_to_world.m[3]);
331
            BrMatrix34TApplyV(&new_pos, &tv, &gCamera_to_world);
332
            BrVector3Sub(&tv, &o, (br_vector3*)gCamera_to_world.m[3]);
333
            BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
334
            if (gSparks[i].colour) {
335
                DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gFog_shade_table);
336
            } else {
337
                DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gAcid_shade_table);
338
            }
339
        }
340
    }
341
}
342
 
343
// IDA: void __usercall RenderSparks(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, br_matrix34 *pCamera_to_world@<ECX>, tU32 pTime)
344
void RenderSparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera, br_matrix34* pCamera_to_world, tU32 pTime) {
345
    int i;
346
    int time;
347
    br_vector3 tv;
348
    br_vector3 o;
349
    br_vector3 p;
350
    br_vector3 pos;
351
    br_vector3 new_pos;
352
    br_scalar ts;
353
    LOG_TRACE("(%p, %p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pCamera_to_world, pTime);
354
 
355
    gSpark_cam = pCamera->type_data;
356
    SetWorldToScreen(pRender_screen);
357
 
358
    if (!gSpark_flags) {
359
        return;
360
    }
361
 
362
    if (gAction_replay_mode) {
363
        ReplaySparks(pRender_screen, pDepth_buffer, pCamera, pTime);
364
        return;
365
    }
366
    StartPipingSession(ePipe_chunk_spark);
367
    for (i = 0; i < COUNT_OF(gSparks); i++) {
368
        if (((1u << i) & gSpark_flags) == 0) {
369
            continue;
370
        }
371
        if (gSparks[i].count <= 0) {
372
            gSparks[i].count = 0;
373
            gSpark_flags &= ~(1u << i);
374
        }
375
        ts = BrVector3Dot(&gSparks[i].normal, &gSparks[i].v);
376
        BrVector3Scale(&tv, &gSparks[i].normal, ts);
377
        BrVector3Sub(&gSparks[i].v, &gSparks[i].v, &tv);
378
        if (gSparks[i].time_sync) {
379
            BrVector3Scale(&o, &gSparks[i].v, gSparks[i].time_sync / 1000.0);
380
            gSparks[i].count = gSparks[i].time_sync + gSparks[i].count - pTime;
381
            gSparks[i].time_sync = 0;
382
        } else {
383
            BrVector3Scale(&o, &gSparks[i].v, pTime / 1000.0);
384
            gSparks[i].count -= pTime;
385
        }
386
        BrVector3Accumulate(&gSparks[i].pos, &o);
387
        time = 1000 - gSparks[i].count;
388
        if (time > 150) {
389
            time = 150;
390
        }
391
        ts = -time / 1000.0;
392
        if (gSparks[i].colour) {
393
            ts = ts / 2.0;
394
        }
395
        BrVector3Scale(&gSparks[i].length, &gSparks[i].v, ts);
396
        ts = pTime * 10.0 / 6900.0;
397
        if (gSparks[i].car) {
398
            BrMatrix34ApplyV(&tv, &gSparks[i].length, &gSparks[i].car->car_master_actor->t.t.mat);
399
            BrVector3Copy(&gSparks[i].length, &tv);
400
            BrMatrix34ApplyP(&pos, &gSparks[i].pos, &gSparks[i].car->car_master_actor->t.t.mat);
401
            o = tv;
402
            gSparks[i].v.v[0] = gSparks[i].v.v[0] - gSparks[i].car->car_master_actor->t.t.mat.m[0][1] * ts;
403
            gSparks[i].v.v[1] = gSparks[i].v.v[1] - gSparks[i].car->car_master_actor->t.t.mat.m[1][1] * ts;
404
            gSparks[i].v.v[2] = gSparks[i].v.v[2] - gSparks[i].car->car_master_actor->t.t.mat.m[2][1] * ts;
405
        } else {
406
            BrVector3Copy(&pos, &gSparks[i].pos);
407
            gSparks[i].v.v[1] = gSparks[i].v.v[1] - ts;
408
        }
409
        AddSparkToPipingSession(i + (gSparks[i].colour << 8), &pos, &gSparks[i].length);
410
        BrVector3Add(&o, &gSparks[i].length, &pos);
411
        BrVector3Sub(&tv, &pos, (br_vector3*)gCamera_to_world.m[3]);
412
        BrMatrix34TApplyV(&new_pos, &tv, &gCamera_to_world);
413
        BrVector3Sub(&tv, &o, (br_vector3*)gCamera_to_world.m[3]);
414
        BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
415
        BrVector3SetFloat(&tv, FRandomBetween(-0.1f, 0.1f), FRandomBetween(-0.1f, 0.1f), FRandomBetween(-0.1f, 0.1f));
416
        BrVector3Accumulate(&gSparks[i].v, &tv);
417
        ts = 1.0f - BrVector3Length(&gSparks[i].v) / 1.4f * pTime / 1000.0f;
418
        if (ts < 0.1f) {
419
            ts = 0.1f;
420
        }
421
        BrVector3Scale(&gSparks[i].v, &gSparks[i].v, ts);
422
        if (gSparks[i].colour) {
423
            DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gFog_shade_table);
424
        } else {
425
            DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gAcid_shade_table);
426
        }
427
    }
428
    EndPipingSession();
429
}
430
 
431
// IDA: void __usercall CreateSingleSpark(tCar_spec *pCar@<EAX>, br_vector3 *pPos@<EDX>, br_vector3 *pVel@<EBX>)
432
void CreateSingleSpark(tCar_spec* pCar, br_vector3* pPos, br_vector3* pVel) {
433
    LOG_TRACE("(%p, %p, %p)", pCar, pPos, pVel);
434
 
435
    BrVector3Copy(&gSparks[gNext_spark].pos, pPos);
436
    BrVector3SetFloat(&gSparks[gNext_spark].normal, 0.0f, 0.0f, 0.0f);
437
    BrVector3Copy(&gSparks[gNext_spark].v, pVel);
438
    gSparks[gNext_spark].count = 500;
439
    gSparks[gNext_spark].car = pCar;
440
    gSpark_flags |= 1u << gNext_spark;
441
    gSparks[gNext_spark].time_sync = 1;
442
    gSparks[gNext_spark].colour = 1;
443
    gNext_spark++;
444
    if (gNext_spark >= COUNT_OF(gSparks)) {
445
        gNext_spark = 0;
446
    }
447
}
448
 
449
// IDA: void __usercall CreateSparks(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_vector3 *pForce@<EBX>, br_scalar sparkiness, tCar_spec *pCar)
450
void CreateSparks(br_vector3* pos, br_vector3* v, br_vector3* pForce, br_scalar sparkiness, tCar_spec* pCar) {
451
    br_vector3 norm;
452
    br_vector3 normal;
453
    br_vector3 tv;
454
    br_vector3 tv2;
455
    br_vector3 pos2;
456
    br_scalar ts;
457
    br_scalar ts2;
458
    int num;
459
    int i;
460
    LOG_TRACE("(%p, %p, %p, %f, %p)", pos, v, pForce, sparkiness, pCar);
461
 
462
    ts = BrVector3Length(pForce);
463
    BrVector3InvScale(&normal, pForce, ts);
464
    ts2 = BrVector3Dot(pForce, v);
465
    if (ts2 >= 0) {
466
        ts2 = 1.f / (10.f * ts);
467
    } else {
468
        ts2 = 1.f / (10.f * ts) - ts2 / (ts * ts);
469
    }
470
 
471
    BrVector3Scale(&norm, pForce, ts2);
472
    BrVector3Accumulate(v, &norm);
473
    num = FRandomBetween(0.f, BrVector3Length(v) / 2.f + 0.7f) * sparkiness;
474
 
475
    if (num > 10) {
476
        num = 10;
477
    }
478
    for (i = 0; i < num; i++) {
479
        BrVector3Copy(&gSparks[gNext_spark].pos, pos);
480
        BrVector3Copy(&gSparks[gNext_spark].normal, &normal);
481
        BrVector3Copy(&gSparks[gNext_spark].v, v);
482
        gSparks[gNext_spark].v.v[0] *= FRandomBetween(.5f, .9f);
483
        gSparks[gNext_spark].v.v[1] *= FRandomBetween(.5f, .9f);
484
        gSparks[gNext_spark].v.v[2] *= FRandomBetween(.5f, .9f);
485
        gSparks[gNext_spark].count = 1000;
486
        gSparks[gNext_spark].car = NULL;
487
        gSpark_flags |= 1u << gNext_spark;
488
        gSparks[gNext_spark].time_sync = gMechanics_time_sync;
489
        gSparks[gNext_spark].colour = 0;
490
        gNext_spark++;
491
        if (gNext_spark >= COUNT_OF(gSparks)) {
492
            gNext_spark = 0;
493
        }
494
    }
495
    if ((ts * sparkiness) >= 10.f) {
496
        tv.v[0] = pos->v[0] - pCar->car_master_actor->t.t.translate.t.v[0] / WORLD_SCALE;
497
        tv.v[1] = pos->v[1] - pCar->car_master_actor->t.t.translate.t.v[1] / WORLD_SCALE;
498
        tv.v[2] = pos->v[2] - pCar->car_master_actor->t.t.translate.t.v[2] / WORLD_SCALE;
499
        BrMatrix34TApplyV(&pos2, &tv, &pCar->car_master_actor->t.t.mat);
500
        BrMatrix34TApplyV(&norm, &normal, &pCar->car_master_actor->t.t.mat);
501
        BrVector3Scale(&tv, &norm, .1f);
502
        BrVector3Accumulate(&pos2, &tv);
503
        num = (ts * sparkiness / 10.f) + 3;
504
        if (num > 10) {
505
            num = 10;
506
        }
507
        for (i = 0; i < num; i++) {
508
            BrVector3Copy(&gSparks[gNext_spark].pos, &pos2);
509
            BrVector3Copy(&gSparks[gNext_spark].normal, &norm);
510
            BrVector3SetFloat(&tv, FRandomBetween(-1.f, 1.f), FRandomBetween(-.2f, 1.f), FRandomBetween(-1.f, 1.f));
511
            ts2 = BrVector3Dot(&norm, &tv);
512
            BrVector3Scale(&tv2, &norm, ts2);
513
            BrVector3Sub(&gSparks[gNext_spark].v, &tv, &tv2);
514
            gSparks[gNext_spark].count = 1000;
515
            gSparks[gNext_spark].car = pCar;
516
            gSpark_flags |= 1u << gNext_spark;
517
            gSparks[gNext_spark].time_sync = gMechanics_time_sync;
518
            gSparks[gNext_spark].colour = 0;
519
            gNext_spark++;
520
            if (gNext_spark >= COUNT_OF(gSparks)) {
521
                gNext_spark = 0;
522
            }
523
        }
524
        CreateShrapnelShower(pos, v, &normal, ts, pCar, pCar);
525
    }
526
}
527
 
528
// IDA: void __usercall CreateSparkShower(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_vector3 *pForce@<EBX>, tCar_spec *pCar1@<ECX>, tCar_spec *pCar2)
529
void CreateSparkShower(br_vector3* pos, br_vector3* v, br_vector3* pForce, tCar_spec* pCar1, tCar_spec* pCar2) {
530
    br_scalar ts;
531
    br_scalar ts2;
532
    int num;
533
    int i;
534
    tCar_spec* c;
535
    br_vector3 tv;
536
    //br_vector3 tv2; // Pierre-Marie Baty -- unused variable
537
    br_vector3 normal;
538
    LOG_TRACE("(%p, %p, %p, %p, %p)", pos, v, pForce, pCar1, pCar2);
539
 
540
    ts = BrVector3Length(pForce);
541
    if (pCar1->driver == eDriver_local_human) {
542
        c = pCar1;
543
    } else {
544
        c = pCar2;
545
    }
546
    BrVector3InvScale(&tv, pForce, ts);
547
    if (ts < 10.f) {
548
        return;
549
    }
550
    CreateShrapnelShower(pos, v, &tv, ts, pCar1, pCar2);
551
    ts2 = BrVector3Dot(pForce, v) / (ts * ts);
552
    BrVector3Scale(v, pForce, ts2);
553
    normal.v[0] = pos->v[0] - c->car_master_actor->t.t.translate.t.v[0] / WORLD_SCALE;
554
    normal.v[1] = pos->v[1] - c->car_master_actor->t.t.translate.t.v[1] / WORLD_SCALE;
555
    normal.v[2] = pos->v[2] - c->car_master_actor->t.t.translate.t.v[2] / WORLD_SCALE;
556
    BrMatrix34TApplyV(pos, &normal, &c->car_master_actor->t.t.mat);
557
    BrMatrix34TApplyV(&normal, pForce, &c->car_master_actor->t.t.mat);
558
    num = (ts / 10.f) + 3;
559
    for (i = 0; i < num; i++) {
560
        BrVector3Copy(&gSparks[gNext_spark].pos, pos);
561
        BrVector3SetFloat(&gSparks[gNext_spark].normal, 0.f, 0.f, 0.f);
562
        BrVector3SetFloat(&normal, FRandomBetween(-1.f, 1.f), FRandomBetween(-.2f, 1.f), FRandomBetween(-1.f, 1.f));
563
        ts2 = BrVector3LengthSquared(&normal) / (ts * ts);
564
        BrVector3Scale(&tv, &normal, ts2);
565
        BrVector3Sub(&gSparks[gNext_spark].v, &normal, &tv);
566
        BrVector3Accumulate(&gSparks[gNext_spark].v, v);
567
        gSparks[gNext_spark].count = 1000;
568
        gSparks[gNext_spark].car = c;
569
        gSpark_flags |= 1u << gNext_spark;
570
        gSparks[gNext_spark].time_sync = gMechanics_time_sync;
571
        gSparks[gNext_spark].colour = 0;
572
        gNext_spark++;
573
        if (gNext_spark >= COUNT_OF(gSparks)) {
574
            gNext_spark = 0;
575
        }
576
    }
577
}
578
 
579
// IDA: void __usercall AdjustSpark(int pSpark_num@<EAX>, br_vector3 *pos@<EDX>, br_vector3 *length@<EBX>)
580
void AdjustSpark(int pSpark_num, br_vector3* pos, br_vector3* length) {
581
    br_vector3 tv;
582
    br_matrix34* mat;
583
    int i;
584
    LOG_TRACE("(%d, %p, %p)", pSpark_num, pos, length);
585
 
586
    i = pSpark_num & 0xff;
587
    gSpark_flags |= 1u << pSpark_num;
588
    if (gSparks[i].car != NULL) {
589
        mat = &gSparks[i].car->car_master_actor->t.t.mat;
590
        tv.v[0] = pos->v[0] - mat->m[3][0];
591
        tv.v[1] = pos->v[0] - mat->m[3][1];
592
        tv.v[2] = pos->v[0] - mat->m[3][2];
593
        BrMatrix34TApplyV(&gSparks[i].pos, &tv, mat);
594
    } else {
595
        gSparks[i].pos.v[0] = pos->v[0];
596
        gSparks[i].pos.v[1] = pos->v[1];
597
        gSparks[i].pos.v[2] = pos->v[2];
598
    }
599
    gSparks[i].length.v[0] = length->v[0];
600
    gSparks[i].length.v[1] = length->v[1];
601
    gSparks[i].length.v[2] = length->v[2];
602
    gSparks[i].colour = pSpark_num >> 8;
603
}
604
 
605
// IDA: void __usercall AdjustShrapnel(int pShrapnel_num@<EAX>, br_vector3 *pos@<EDX>, tU16 pAge@<EBX>, br_material *pMaterial@<ECX>)
606
void AdjustShrapnel(int pShrapnel_num, br_vector3* pos, tU16 pAge, br_material* pMaterial) {
607
    int i;
608
    LOG_TRACE("(%d, %p, %d, %p)", pShrapnel_num, pos, pAge, pMaterial);
609
 
610
    i = pShrapnel_num & 0x7fff;
611
    if (!(gShrapnel_flags & (1u << i))) {
612
        BrActorAdd(gNon_track_actor, gShrapnel[i].actor);
613
    }
614
    gShrapnel_flags |= 1u << i;
615
    gShrapnel[i].actor->t.t.translate.t.v[0] = pos->v[0];
616
    gShrapnel[i].actor->t.t.translate.t.v[1] = pos->v[1];
617
    gShrapnel[i].actor->t.t.translate.t.v[2] = pos->v[2];
618
    if (pShrapnel_num & 0x8000) {
619
        gShrapnel[i].age = pAge;
620
        gShrapnel[i].actor->material = pMaterial;
621
    }
622
}
623
 
624
// IDA: void __cdecl ResetSparks()
625
void ResetSparks(void) {
626
    LOG_TRACE("()");
627
 
628
    gSpark_flags = 0;
629
}
630
 
631
// IDA: void __cdecl ResetShrapnel()
632
void ResetShrapnel(void) {
633
    int i;
634
    LOG_TRACE("()");
635
 
636
    if (gShrapnel_flags == 0) {
637
        return;
638
    }
639
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
640
        if (gShrapnel_flags & (1u << i)) {
641
            BrActorRemove(gShrapnel[i].actor);
642
        }
643
    }
644
    gShrapnel_flags = 0;
645
}
646
 
647
// IDA: void __usercall CreateShrapnelShower(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_vector3 *pNormal@<EBX>, br_scalar pForce, tCar_spec *c1, tCar_spec *c2)
648
void CreateShrapnelShower(br_vector3* pos, br_vector3* v, br_vector3* pNormal, br_scalar pForce, tCar_spec* c1, tCar_spec* c2) {
649
    br_scalar ts;
650
    br_scalar ts2;
651
    br_scalar rnd;
652
    int num;
653
    int i;
654
    tCar_spec* c;
655
    br_vector3 tv;
656
    br_vector3 tv2;
657
    br_vector3 vel;
658
    LOG_TRACE("(%p, %p, %p, %f, %p, %p)", pos, v, pNormal, pForce, c1, c2);
659
 
660
    if (pForce < 10.f) {
661
        return;
662
    }
663
    ts = .3f;
664
    if (v->v[1] < 0.f) {
665
        ts = .3f - v->v[1];
666
    }
667
    ts2 = pNormal->v[1] * ts;
668
 
669
    tv.v[0] = v->v[0] - ts2 * pNormal->v[0];
670
    tv.v[1] = v->v[1] + ts - pNormal->v[1] * ts2;
671
    tv.v[2] = v->v[2] - pNormal->v[2] * ts2;
672
 
673
    num = (pForce / 10.f) * 3;
674
    rnd = ((pForce + 20.f) * 3.f) / 200.f;
675
    for (i = 0; i < num; i++) {
676
        if ((gShrapnel_flags & (1u << gNext_shrapnel)) == 0) {
677
            BrActorAdd(gNon_track_actor, gShrapnel[gNext_shrapnel].actor);
678
        }
679
        gShrapnel_flags |= 1u << gNext_shrapnel;
680
        BrVector3Copy(&gShrapnel[gNext_shrapnel].actor->t.t.translate.t, pos);
681
        BrVector3SetFloat(&vel, FRandomBetween(-rnd, rnd), FRandomBetween(0.3f - tv.v[1], rnd), FRandomBetween(-rnd, rnd));
682
        ts2 = BrVector3Dot(pNormal, &vel);
683
        BrVector3Scale(&tv2, pNormal, ts2);
684
        BrVector3Sub(&gShrapnel[gNext_shrapnel].v, &vel, &tv2);
685
        BrVector3Accumulate(&gShrapnel[gNext_shrapnel].v, &tv);
686
        gShrapnel[gNext_shrapnel].time_sync = gMechanics_time_sync;
687
        gShrapnel[gNext_shrapnel].age = 0;
688
        if (IRandomBetween(0, 2) != 0) {
689
            c = (IRandomBetween(0, 1) != 0) ? c1 : c2;
690
            gShrapnel[gNext_shrapnel].actor->material = c->shrapnel_material[IRandomBetween(0, c->max_shrapnel_material - 1)];
691
        } else {
692
            gShrapnel[gNext_shrapnel].actor->material = gBlack_material;
693
        }
694
        gNext_shrapnel++;
695
        if (gNext_shrapnel >= COUNT_OF(gShrapnel)) {
696
            gNext_shrapnel = 0;
697
        }
698
    }
699
}
700
 
701
// IDA: void __cdecl InitShrapnel()
702
void InitShrapnel(void) {
703
    int i;
704
    //int j; // Pierre-Marie Baty -- unused variable
705
    LOG_TRACE("()");
706
 
707
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
708
        gShrapnel[i].actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
709
        gShrapnel[i].actor->parent = NULL;
710
        gShrapnel[i].actor->model = gShrapnel_model[1];
711
        gShrapnel[i].actor->render_style = BR_RSTYLE_DEFAULT;
712
        gShrapnel[i].actor->t.type = BR_TRANSFORM_MATRIX34;
713
        gShrapnel[i].actor->material = BrMaterialFind("DEBRIS.MAT");
714
        gShrapnel[i].age = 0;
715
        gShrapnel[i].shear1 = FRandomBetween(-2.f, 2.f);
716
        gShrapnel[i].shear2 = FRandomBetween(-2.f, 2.f);
717
        BrVector3SetFloat(&gShrapnel[i].axis,
718
            FRandomBetween(-1.f, 1.f), FRandomBetween(-1.f, 1.f), FRandomBetween(-1.f, 1.f));
719
        BrVector3Normalise(&gShrapnel[i].axis, &gShrapnel[i].axis);
720
    }
721
}
722
 
723
// IDA: void __cdecl LoadInShrapnel()
724
void LoadInShrapnel(void) {
725
    LOG_TRACE("()");
726
 
727
    gShrapnel_model[0] = LoadModel("FRAG4.DAT");
728
    gShrapnel_model[1] = LoadModel("FRAG5.DAT");
729
    BrModelAdd(gShrapnel_model[0]);
730
    BrModelAdd(gShrapnel_model[1]);
731
    gBlack_material = BrMaterialFind("M14.MAT");
732
}
733
 
734
// IDA: void __usercall KillShrapnel(int i@<EAX>)
735
void KillShrapnel(int i) {
736
    LOG_TRACE("(%d)", i);
737
 
738
    BrActorRemove(gShrapnel[i].actor);
739
    gShrapnel_flags &= ~(1u << i);
740
}
741
 
742
// IDA: void __cdecl DisposeShrapnel()
743
void DisposeShrapnel(void) {
744
    int i;
745
    LOG_TRACE("()");
746
 
747
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
748
        if (gShrapnel_flags & (1u << i)) {
749
            BrActorRemove(gShrapnel[i].actor);
750
        }
751
        BrActorFree(gShrapnel[i].actor);
752
    }
753
    gShrapnel_flags = 0;
754
    for (i = 0; i < COUNT_OF(gShrapnel_model); i++) {
755
        BrModelRemove(gShrapnel_model[i]);
756
        BrModelFree(gShrapnel_model[i]);
757
    }
758
}
759
 
760
// IDA: void __usercall ReplayShrapnel(tU32 pTime@<EAX>)
761
void ReplayShrapnel(tU32 pTime) {
762
    int i;
763
    br_matrix34* mat;
764
    LOG_TRACE("(%d)", pTime);
765
 
766
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
767
        mat = &gShrapnel[i].actor->t.t.mat;
768
        if (gShrapnel_flags & (1 << i)) {
769
            gShrapnel[i].age += GetReplayRate() * pTime;
770
            DrMatrix34Rotate(mat, gShrapnel[i].age * BrDegreeToAngle(1), &gShrapnel[i].axis);
771
            BrMatrix34PreShearX(mat, gShrapnel[i].shear1, gShrapnel[i].shear2);
772
        }
773
    }
774
}
775
 
776
// IDA: void __usercall MungeShrapnel(tU32 pTime@<EAX>)
777
void MungeShrapnel(tU32 pTime) {
778
    br_vector3 disp;
779
    int i;
780
    br_matrix34* mat;
781
    br_scalar ts;
782
    LOG_TRACE("(%d)", pTime);
783
 
784
    MungeSmokeColumn(pTime);
785
    MungeSplash(pTime);
786
 
787
    if (gAction_replay_mode) {
788
        ReplayShrapnel(pTime);
789
        return;
790
    }
791
 
792
    StartPipingSession(ePipe_chunk_shrapnel);
793
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
794
        mat = &gShrapnel[i].actor->t.t.mat;
795
        if (((1u << i) & gShrapnel_flags) == 0) {
796
            continue;
797
        }
798
        if (gShrapnel[i].age == -1) {
799
            KillShrapnel(i);
800
        } else {
801
            if (gShrapnel[i].time_sync) {
802
                BrVector3Scale(&disp, &gShrapnel[i].v, gShrapnel[i].time_sync / 1000.0f);
803
                gShrapnel[i].time_sync = 0;
804
            } else {
805
                BrVector3Scale(&disp, &gShrapnel[i].v, pTime / 1000.0f);
806
                gShrapnel[i].age += pTime;
807
            }
808
            mat->m[3][0] = mat->m[3][0] + disp.v[0];
809
            mat->m[3][1] = mat->m[3][1] + disp.v[1];
810
            mat->m[3][2] = mat->m[3][2] + disp.v[2];
811
            gShrapnel[i].v.v[1] -= (10 * pTime) * 0.00014492753f;
812
            DrMatrix34Rotate(mat, 182 * gShrapnel[i].age, &gShrapnel[i].axis);
813
            BrMatrix34PreShearX(mat, gShrapnel[i].shear1, gShrapnel[i].shear2);
814
            // bug: should this be using "&gShrapnel[i].v"??
815
            ts = 1.0 - BrVector3Length(&gSparks[i].v) / 1.4 * pTime / 1000.0;
816
            if (ts < 0.1) {
817
                ts = (br_scalar) 0.1; // Pierre-Marie Baty -- added type cast
818
            }
819
            BrVector3Scale(&gShrapnel[i].v, &gShrapnel[i].v, ts);
820
            AddShrapnelToPipingSession(i + ((gShrapnel[i].age > 1000 || gShrapnel[i].age < pTime) << 15), (br_vector3*)mat->m[3], gShrapnel[i].age - pTime, gShrapnel[i].actor->material);
821
            if (gShrapnel[i].age > 1000) {
822
                gShrapnel[i].age = -1;
823
            }
824
        }
825
    }
826
    EndPipingSession();
827
}
828
 
829
// IDA: void __usercall DrMatrix34Rotate(br_matrix34 *mat@<EAX>, br_angle r@<EDX>, br_vector3 *a@<EBX>)
830
void DrMatrix34Rotate(br_matrix34* mat, br_angle r, br_vector3* a) {
831
    br_scalar t;
832
    br_scalar s;
833
    br_scalar c;
834
    br_scalar txy;
835
    br_scalar txz;
836
    br_scalar tyz;
837
    br_scalar sx;
838
    br_scalar sy;
839
    br_scalar sz;
840
    LOG_TRACE("(%p, %d, %p)", mat, r, a);
841
 
842
    s = FastScalarSinAngle(r);
843
    c = FastScalarCosAngle(r);
844
    t = 1.0f - c;
845
    txy = t * a->v[0] * a->v[1];
846
    txz = t * a->v[0] * a->v[2];
847
    tyz = t * a->v[1] * a->v[2];
848
    sx = a->v[0] * s;
849
    sy = a->v[1] * s;
850
    sz = a->v[2] * s;
851
    mat->m[0][0] = a->v[0] * a->v[0] * t + c;
852
    mat->m[0][1] = sz + txy;
853
    mat->m[0][2] = txz - sy;
854
    mat->m[1][0] = txy - sz;
855
    mat->m[1][1] = a->v[1] * a->v[1] * t + c;
856
    mat->m[1][2] = sx + tyz;
857
    mat->m[2][0] = sy + txz;
858
    mat->m[2][1] = tyz - sx;
859
    mat->m[2][2] = a->v[2] * a->v[2] * t + c;
860
}
861
 
862
// IDA: void __usercall SmokeLine(int l@<EAX>, int x@<EDX>, br_scalar zbuff, int r_squared, tU8 *scr_ptr, tU16 *depth_ptr, tU8 *shade_ptr, br_scalar r_multiplier, br_scalar z_multiplier, br_scalar shade_offset)
863
void SmokeLine(int l, int x, br_scalar zbuff, int r_squared, tU8* scr_ptr, tU16* depth_ptr, tU8* shade_ptr, br_scalar r_multiplier, br_scalar z_multiplier, br_scalar shade_offset) {
864
    int i;
865
    int offset; /* Added by dethrace. */
866
    int r_multiplier_int;
867
    int shade_offset_int;
868
    tU16 z;
869
    LOG_TRACE("(%d, %d, %f, %d, %p, %p, %p, %f, %f, %f)", l, x, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
870
 
871
    scr_ptr += gOffset;
872
    if (gProgram_state.cockpit_on) {
873
        depth_ptr += gOffset;
874
    }
875
    z = (1.f - zbuff) * 32768.0f;
876
    r_multiplier_int = r_multiplier * 65536.0f;
877
    shade_offset_int = shade_offset * 65536.0f;
878
 
879
    for (i = 0; i < l; i++) {
880
        if (*depth_ptr > z) {
881
            offset = ((shade_offset_int - r_squared * r_multiplier_int) >> 8) & 0xffffff00;
882
#if defined(DETHRACE_FIX_BUGS)
883
            /* Prevent buffer underflows by capping negative offsets. */
884
            offset = MAX(0, offset);
885
#endif
886
            *scr_ptr = shade_ptr[*scr_ptr + offset];
887
        }
888
        r_multiplier = x + r_squared;
889
        scr_ptr++;
890
        x++;
891
        depth_ptr++;
892
        r_squared = x + r_multiplier;
893
    }
894
}
895
 
896
// IDA: void __usercall SmokeCircle(br_vector3 *o@<EAX>, br_scalar r, br_scalar extra_z, br_scalar strength, br_scalar pAspect, br_pixelmap *pRender_screen, br_pixelmap *pDepth_buffer, br_pixelmap *pShade_table)
897
void SmokeCircle(br_vector3* o, br_scalar r, br_scalar extra_z, br_scalar strength, br_scalar pAspect, br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_pixelmap* pShade_table) {
898
    tU8* scr_ptr;
899
    tU16* depth_ptr;
900
    tU8* shade_ptr;
901
    tU8* osp;
902
    tU16* odp;
903
    int ox;
904
    int oy;
905
    //int i; // Pierre-Marie Baty -- unused variable
906
    int r_squared;
907
    int max_r_squared;
908
    int l;
909
    int l2;
910
    int x;
911
    int x2;
912
    //int sx; // Pierre-Marie Baty -- unused variable
913
    int y;
914
    int inc;
915
    int y_limit;
916
    int max_x;
917
    int min_x;
918
    br_scalar shade_offset;
919
    br_scalar r_multiplier;
920
    br_scalar ry;
921
    br_scalar z_multiplier;
922
    br_scalar zbuff;
923
    br_scalar aspect_squared;
924
    void (*line)(int, int, br_scalar, int, tU8*, tU16*, tU8*, br_scalar, br_scalar, br_scalar);
925
    LOG_TRACE("(%p, %f, %f, %f, %f, %p, %p, %p)", o, r, extra_z, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table);
926
 
927
    line = SmokeLine;
928
    ox = pRender_screen->width / 2 + o->v[0];
929
    oy = pRender_screen->height / 2 + o->v[1];
930
    max_r_squared = r * r;
931
    zbuff = o->v[2];
932
    aspect_squared = pAspect * pAspect;
933
    if (pRender_screen->width / 4 <= r
934
        || pRender_screen->width <= ox - r
935
        || ox + r < 0.0f) {
936
        return;
937
    }
938
    shade_ptr = (tU8*)pShade_table->pixels + pShade_table->row_bytes * (pShade_table->base_y + 1);
939
    shade_offset = strength * 14.99f;
940
    r_multiplier = shade_offset / (double)max_r_squared;
941
    z_multiplier = extra_z / (double)max_r_squared;
942
    max_x = pRender_screen->width - ox - 1;
943
    min_x = -ox;
944
    ry = r / pAspect;
945
    l = pRender_screen->height - oy - 1;
946
    scr_ptr = pRender_screen->pixels;
947
    scr_ptr += pRender_screen->base_x + pRender_screen->base_y * pRender_screen->row_bytes + ox + l * pRender_screen->row_bytes;
948
    depth_ptr = (tU16*)pDepth_buffer->pixels + ox + l * (pDepth_buffer->row_bytes / 2);
949
    osp = scr_ptr;
950
    odp = depth_ptr;
951
    if (pRender_screen->height > oy && oy + ry >= 0.0) {
952
        r_squared = r * r;
953
        inc = -r;
954
        y = 0;
955
        y_limit = ry;
956
        if (oy < 0) {
957
            y = -oy;
958
            r_squared = (y * y) * aspect_squared;
959
            scr_ptr += oy * pRender_screen->row_bytes;
960
            depth_ptr += oy * (pDepth_buffer->row_bytes / 2);
961
            inc = -sqrtf(max_r_squared - r_squared);
962
            r_squared += inc * inc;
963
        }
964
        if (pRender_screen->height < oy + ry) {
965
            y_limit = pRender_screen->height - oy - 1;
966
        }
967
        l = -2 * inc;
968
        scr_ptr += inc;
969
        depth_ptr += inc;
970
        gOffset = 0;
971
        while (1) {
972
 
973
            x = inc + gOffset;
974
            if (min_x <= inc + gOffset && l + x - 1 <= max_x) {
975
                line(l, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
976
            } else {
977
                if (max_x < x || l + x < min_x) {
978
                    break;
979
                }
980
                x2 = l;
981
                if (l + x - 1 - max_x > 0) {
982
                    x2 = max_x - (x - 1);
983
                }
984
                if (min_x - x <= 0) {
985
                    line(x2, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
986
                } else {
987
                    line(x2 - (min_x - x), min_x - x + inc, zbuff, y * y + (min_x - x + inc) * (min_x - x + inc), &scr_ptr[min_x - x], &depth_ptr[min_x - x], shade_ptr, r_multiplier, z_multiplier, shade_offset);
988
                }
989
            }
990
            if (y_limit <= y) {
991
                break;
992
            }
993
            y++;
994
            scr_ptr -= pRender_screen->row_bytes;
995
            depth_ptr -= pDepth_buffer->row_bytes / 2;
996
            for (r_squared += (2 * y - 1) * aspect_squared; max_r_squared < r_squared && inc < 0; r_squared += 2 * inc - 1) {
997
                inc++;
998
                scr_ptr++;
999
                depth_ptr++;
1000
                l -= 2;
1001
            }
1002
            gOffset += IRandomBetween(-1, 1);
1003
            if (gOffset > r / 5.f) {
1004
                gOffset = r / 5.f;
1005
            }
1006
            if (gOffset < -(r / 5.f)) {
1007
                gOffset = -(r / 5.f);
1008
            }
1009
        }
1010
    }
1011
    if (pAspect < 1.0) {
1012
        aspect_squared = 9.f;
1013
        ry = r / 3.f;
1014
    }
1015
    if (oy > 0 && oy <= pRender_screen->height + ry - 2.f) {
1016
        r_squared = (r * r);
1017
        inc = -r;
1018
        y = 0;
1019
        scr_ptr = osp;
1020
        depth_ptr = odp;
1021
        y_limit = ry;
1022
        if (pRender_screen->height < oy) {
1023
            l2 = oy - pRender_screen->height;
1024
            y = pRender_screen->height - oy;
1025
            r_squared = y * y * aspect_squared;
1026
            scr_ptr = &osp[l2 * pRender_screen->row_bytes];
1027
            depth_ptr = &odp[l2 * (pDepth_buffer->row_bytes / 2)];
1028
            inc = -sqrtf(max_r_squared - r_squared);
1029
            r_squared += inc * inc;
1030
        }
1031
        if (oy - ry < 0.f) {
1032
            y_limit = oy;
1033
        }
1034
        l = -2 * inc;
1035
        scr_ptr += inc;
1036
        depth_ptr += inc;
1037
        gOffset = 0;
1038
        do {
1039
            y--;
1040
            scr_ptr += pRender_screen->row_bytes;
1041
            depth_ptr += pDepth_buffer->row_bytes / 2;
1042
            for (r_squared -= (2 * y - 1) * aspect_squared; max_r_squared < r_squared && inc < 0; r_squared += 2 * inc - 1) {
1043
                inc++;
1044
                scr_ptr++;
1045
                depth_ptr++;
1046
                l -= 2;
1047
            }
1048
            x = inc + gOffset;
1049
            if (min_x <= inc + gOffset && l + x - 1 <= max_x) {
1050
                line(l, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
1051
            } else {
1052
                if (max_x < x || l + x < min_x) {
1053
                    return;
1054
                }
1055
                x2 = l;
1056
                if (l + x - 1 - max_x > 0) {
1057
                    x2 = max_x - (x - 1);
1058
                }
1059
                if (min_x - x <= 0) {
1060
                    line(x2, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
1061
                } else {
1062
                    line(x2 - (min_x - x), min_x - x + inc, zbuff, y * y + (min_x - x + inc) * (min_x - x + inc), &scr_ptr[min_x - x], &depth_ptr[min_x - x], shade_ptr, r_multiplier, z_multiplier, shade_offset);
1063
                }
1064
            }
1065
            gOffset += IRandomBetween(-1, 1);
1066
            if (gOffset > r / 5.0) {
1067
                gOffset = r / 5.0;
1068
            }
1069
            if (gOffset < -(r / 5.0)) {
1070
                gOffset = -r / 5.0;
1071
            }
1072
        } while (-y < y_limit);
1073
    }
1074
}
1075
 
1076
// IDA: int __cdecl CmpSmokeZ(void *p1, void *p2)
1077
int CmpSmokeZ(void* p1, void* p2) {
1078
    //tBRender_smoke** a; // Pierre-Marie Baty -- unused variable
1079
    //tBRender_smoke** b; // Pierre-Marie Baty -- unused variable
1080
    LOG_TRACE("(%p, %p)", p1, p2);
1081
    NOT_IMPLEMENTED();
1082
}
1083
 
1084
// IDA: void __cdecl RenderRecordedSmokeCircles()
1085
void RenderRecordedSmokeCircles(void) {
1086
    //int i; // Pierre-Marie Baty -- unused variable
1087
    //tBRender_smoke* smoke; // Pierre-Marie Baty -- unused variable
1088
    //tU8 red; // Pierre-Marie Baty -- unused variable
1089
    //tU8 grn; // Pierre-Marie Baty -- unused variable
1090
    //tU8 blu; // Pierre-Marie Baty -- unused variable
1091
    LOG_TRACE("()");
1092
    NOT_IMPLEMENTED();
1093
}
1094
 
1095
// IDA: void __usercall RecordSmokeCircle(br_vector3 *pCent@<EAX>, br_scalar pR, br_scalar pStrength, br_pixelmap *pShade, br_scalar pAspect)
1096
void RecordSmokeCircle(br_vector3* pCent, br_scalar pR, br_scalar pStrength, br_pixelmap* pShade, br_scalar pAspect) {
1097
    //tU8 shade_index; // Pierre-Marie Baty -- unused variable
1098
    //br_colour shade_rgb; // Pierre-Marie Baty -- unused variable
1099
    LOG_TRACE("(%p, %f, %f, %p, %f)", pCent, pR, pStrength, pShade, pAspect);
1100
    NOT_IMPLEMENTED();
1101
}
1102
 
1103
// IDA: void __usercall SmokeCircle3D(br_vector3 *o@<EAX>, br_scalar r, br_scalar strength, br_scalar pAspect, br_pixelmap *pRender_screen, br_pixelmap *pDepth_buffer, br_pixelmap *pShade_table, br_actor *pCam)
1104
void SmokeCircle3D(br_vector3* o, br_scalar r, br_scalar strength, br_scalar pAspect, br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_pixelmap* pShade_table, br_actor* pCam) {
1105
    br_vector3 tv;
1106
    br_vector3 p;
1107
    br_vector4 o2;
1108
    br_camera* cam;
1109
    int scaled_r;
1110
    br_scalar extra_z;
1111
    LOG_TRACE("(%p, %f, %f, %f, %p, %p, %p, %p)", o, r, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table, pCam);
1112
 
1113
    cam = pCam->type_data;
1114
    srand(o->v[2] * 16777216.0f + o->v[1] * 65536.0f + o->v[0] * 256.0f + r);
1115
    BrVector3Sub(&tv, o, (br_vector3*)gCamera_to_world.m[3]);
1116
    BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
1117
 
1118
    if (-p.v[2] >= cam->hither_z && -p.v[2] <= cam->yon_z) {
1119
        scaled_r = gCameraToScreen.m[0][0] * r / -p.v[2];
1120
        extra_z = gCameraToScreen.m[3][2] * r / (p.v[2] * p.v[2]);
1121
        BrMatrix4ApplyP(&o2, &p, &gCameraToScreen);
1122
        BrVector3InvScale(&p, &o2, o2.v[3]);
1123
        SmokeCircle(&p, (br_scalar)scaled_r, extra_z, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table);
1124
    }
1125
}
1126
 
1127
// IDA: void __usercall ReplaySmoke(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>)
1128
void ReplaySmoke(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera) {
1129
    br_scalar aspect;
1130
    int i;
1131
    LOG_TRACE("(%p, %p, %p)", pRender_screen, pDepth_buffer, pCamera);
1132
 
1133
    for (i = 0; i < COUNT_OF(gSmoke_column); i++) {
1134
        if (gSmoke_flags & (1 << i)) {
1135
            aspect = 1.f + (gSmoke[i].radius - .05f) / .25f * .5f;
1136
            if (gSmoke[i].type & 0x10) {
1137
                SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.f,
1138
                    pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1139
            } else {
1140
                SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength, aspect,
1141
                    pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1142
            }
1143
        }
1144
    }
1145
}
1146
 
1147
// IDA: void __usercall GenerateContinuousSmoke(tCar_spec *pCar@<EAX>, int wheel@<EDX>, tU32 pTime@<EBX>)
1148
void GenerateContinuousSmoke(tCar_spec* pCar, int wheel, tU32 pTime) {
1149
    br_vector3 pos;
1150
    br_vector3 v;
1151
    br_vector3 vcs;
1152
    br_vector3 tv;
1153
    br_scalar decay_factor;
1154
    br_scalar ts;
1155
    br_scalar alpha;
1156
    br_scalar beta;
1157
    int colour;
1158
    LOG_TRACE("(%p, %d, %d)", pCar, wheel, pTime);
1159
 
1160
    if (pCar->dust_time[wheel] > (int) pTime) { // Pierre-Marie Baty -- added type cast
1161
        pCar->dust_time[wheel] -= pTime;
1162
        return;
1163
    }
1164
    pCar->dust_time[wheel] += IRandomBetween(200, 400) - pTime;
1165
    if (pCar->dust_time[wheel] < 0) {
1166
        pCar->dust_time[wheel] = 0;
1167
    }
1168
    BrVector3Cross(&tv, &pCar->omega, &pCar->wpos[wheel]);
1169
    BrVector3Scale(&vcs, &pCar->velocity_car_space, WORLD_SCALE * 1000.0f);
1170
    BrVector3Accumulate(&vcs, &tv);
1171
    ts = BrVector3LengthSquared(&vcs);
1172
    if (ts < 25.0f) {
1173
        return;
1174
    }
1175
    decay_factor = sqrtf(ts) / 25.0f;
1176
    if (decay_factor > 1.0f) {
1177
        decay_factor = 1.0f;
1178
    }
1179
    BrVector3InvScale(&tv, &pCar->wpos[wheel], WORLD_SCALE);
1180
    tv.v[1] -= pCar->oldd[wheel] / WORLD_SCALE;
1181
 
1182
    alpha = -1000.0f;
1183
    if (vcs.v[2] > 0.0f) {
1184
        alpha = (pCar->bounds[0].min.v[2] - tv.v[2]) / vcs.v[2];
1185
    } else if (vcs.v[2] < 0.0f) {
1186
        alpha = (pCar->bounds[0].max.v[2] - tv.v[2]) / vcs.v[2];
1187
    }
1188
 
1189
    beta = -1000.0f;
1190
    if (vcs.v[0] > 0.0f) {
1191
        beta = (pCar->bounds[0].min.v[0] - tv.v[0]) / vcs.v[0];
1192
    } else if (vcs.v[0] < 0.0f) {
1193
        beta = (pCar->bounds[0].max.v[0] - tv.v[0]) / vcs.v[0];
1194
    }
1195
 
1196
    ts = MAX(alpha, beta);
1197
    BrVector3Scale(&pos, &vcs, ts);
1198
    BrVector3Accumulate(&tv, &pos);
1199
    BrMatrix34ApplyP(&pos, &tv, &pCar->car_master_actor->t.t.mat);
1200
    BrMatrix34ApplyV(&v, &vcs, &pCar->car_master_actor->t.t.mat);
1201
 
1202
    colour = gDust_rotate + gCurrent_race.material_modifiers[pCar->material_index[wheel]].smoke_type - 2;
1203
    while (colour >= gNum_dust_tables) {
1204
        colour -= gNum_dust_tables;
1205
    }
1206
    CreatePuffOfSmoke(&pos, &v, decay_factor, decay_factor * 2, colour + 8, pCar);
1207
}
1208
 
1209
// IDA: void __cdecl DustRotate()
1210
void DustRotate(void) {
1211
    LOG_TRACE("()");
18 pmbaty 1212
 
1213
    gDust_rotate += 1;
1214
    if (gDust_rotate >= gNum_dust_tables) {
1215
        gDust_rotate = 0;
1216
    }
1217
    NewTextHeadupSlot(4, 0, 1000, -4, "Dust colour rotated");
1 pmbaty 1218
}
1219
 
1220
// IDA: void __usercall RenderSmoke(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, br_matrix34 *pCamera_to_world@<ECX>, tU32 pTime)
1221
void RenderSmoke(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera, br_matrix34* pCamera_to_world, tU32 pTime) {
1222
    int i;
1223
    int j;
1224
    br_vector3 tv;
1225
    br_scalar aspect;
1226
    br_scalar ts;
1227
    tU32 seed;
1228
    tU32 not_lonely;
1229
    LOG_TRACE("(%p, %p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pCamera_to_world, pTime);
1230
 
1231
    not_lonely = 0;
1232
    DrawTheGlow(pRender_screen, pDepth_buffer, pCamera);
1233
 
1234
    if (gSmoke_flags != 0) {
1235
        seed = rand();
1236
        if (gAction_replay_mode) {
1237
            ReplaySmoke(pRender_screen, pDepth_buffer, pCamera);
1238
            srand(seed);
1239
        } else {
1240
            StartPipingSession(ePipe_chunk_smoke);
1241
            for (i = 0; i < COUNT_OF(gSmoke); i++) {
1242
                if ((gSmoke_flags & (1u << i)) != 0) {
1243
                    if (gSmoke[i].strength > 0.0) {
1244
                        if (gSmoke[i].time_sync) {
1245
                            BrVector3Scale(&tv, &gSmoke[i].v, gSmoke[i].time_sync / 1000.0);
1246
                            gSmoke[i].time_sync = 0;
1247
                        } else {
1248
                            BrVector3Scale(&tv, &gSmoke[i].v, pTime / 1000.0);
1249
                        }
1250
                        BrVector3Accumulate(&gSmoke[i].pos, &tv);
1251
                    } else {
1252
                        gSmoke_flags &= ~(1u << i);
1253
                    }
1254
                }
1255
            }
1256
            for (i = 0; i < COUNT_OF(gSmoke); i++) {
1257
                if ((gSmoke_flags & (1u << i)) != 0) {
1258
                    if ((gSmoke[i].type & 0xf) == 7) {
1259
                        not_lonely |= 1u << i;
1260
                    } else if ((not_lonely & (1u << i)) == 0) {
1261
                        for (j = i + 1; j < COUNT_OF(gSmoke); j++) {
1262
                            if ((gSmoke_flags & (1u << j)) != 0) {
1263
                                BrVector3Sub(&tv, &gSmoke[i].pos, &gSmoke[i].pos);
1264
                                ts = BrVector3LengthSquared(&tv);
1265
                                if ((gSmoke[i].radius + gSmoke[j].radius) * (gSmoke[i].radius + gSmoke[j].radius) > ts) {
1266
                                    not_lonely |= (1u << j) | (1u << i);
1267
                                    break;
1268
                                }
1269
                            }
1270
                        }
1271
                    }
1272
                    if (((1u << i) & not_lonely) == 0) {
1273
                        gSmoke[i].strength = gSmoke[i].strength / 2.0;
1274
                    }
1275
                    aspect = (gSmoke[i].radius - 0.05f) / 0.25f * 0.5f + 1.0f;
1276
                    if ((gSmoke[i].type & 0x10) != 0) {
1277
                        SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.0, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1278
                    } else {
1279
                        SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength, aspect, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1280
                    }
1281
                    if (gSmoke[i].pipe_me) {
1282
                        AddSmokeToPipingSession(i, gSmoke[i].type, &gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength);
1283
                    }
1284
                    gSmoke[i].radius = (double)pTime / 1000.0 * gSmoke[i].strength * 0.5 + gSmoke[i].radius;
1285
                    gSmoke[i].strength = gSmoke[i].strength - (double)pTime * gSmoke[i].decay_factor / 1000.0;
1286
                    if (gSmoke[i].radius > 0.3f) {
1287
                        gSmoke[i].radius = 0.3f;
1288
                    }
1289
                    if (gSmoke[i].strength > 0.0) {
1290
                        ts = 1.0f - (double)pTime * 0.002f;
1291
                        if (ts < 0.5f) {
1292
                            ts = 0.5f;
1293
                        }
1294
                        BrVector3Scale(&gSmoke[i].v, &gSmoke[i].v, ts);
1295
                        if (fabs(gSmoke[i].v.v[1]) < 0.43478259f && (gSmoke[i].type & 0xFu) < 7) {
1296
                            if (gSmoke[i].v.v[1] >= 0.0) {
1297
                                gSmoke[i].v.v[1] = 0.43478259f;
1298
                            } else {
1299
                                gSmoke[i].v.v[1] += 0.43478259f;
1300
                            }
1301
                        }
1302
                    } else {
1303
                        gSmoke_flags &= ~(1u << i);
1304
                    }
1305
                }
1306
            }
1307
            EndPipingSession();
1308
            srand(seed);
1309
        }
1310
    }
1311
}
1312
 
1313
// IDA: void __usercall CreatePuffOfSmoke(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_scalar strength, br_scalar pDecay_factor, int pType, tCar_spec *pC)
1314
void CreatePuffOfSmoke(br_vector3* pos, br_vector3* v, br_scalar strength, br_scalar pDecay_factor, int pType, tCar_spec* pC) {
1315
    br_vector3 tv;
1316
    //int pipe_me; // Pierre-Marie Baty -- unused variable
1317
    LOG_TRACE("(%p, %p, %f, %f, %d, %p)", pos, v, strength, pDecay_factor, pType, pC);
1318
 
1319
    if (!gSmoke_on) {
1320
        return;
1321
    }
1322
    // if we are too far away from the current car...
1323
    BrVector3Sub(&tv, pos, &gProgram_state.current_car.pos);
1324
    if (BrVector3LengthSquared(&tv) > 625.0) {
1325
        // check the distance from the car we are viewing and if it is too far away also, just return
1326
        BrVector3Sub(&tv, pos, &gCar_to_view->pos);
1327
        if (&gProgram_state.current_car != gCar_to_view && BrVector3LengthSquared(&tv) > 625.0) {
1328
            return;
1329
        }
1330
    }
1331
 
1332
    BrVector3InvScale(&gSmoke[gSmoke_num].v, v, WORLD_SCALE);
1333
    gSmoke[gSmoke_num].v.v[1] += (1.0f / WORLD_SCALE);
1334
    BrVector3Copy(&gSmoke[gSmoke_num].pos, pos);
1335
    gSmoke[gSmoke_num].radius = 0.05f;
1336
    if ((pType & 0xF) == 7) {
1337
        gSmoke[gSmoke_num].radius *= 2.0f;
1338
    } else {
1339
        gSmoke[gSmoke_num].pos.v[1] += (br_scalar) 0.04; // Pierre-Marie Baty -- added type cast
1340
    }
1341
    gSmoke[gSmoke_num].pos.v[1] += (br_scalar) 0.04; // Pierre-Marie Baty -- added type cast
1342
    if (strength > 1.0) {
1343
        strength = 1.0;
1344
    }
1345
    gSmoke[gSmoke_num].strength = strength;
1346
    gSmoke_flags |= 1u << gSmoke_num;
1347
    gSmoke[gSmoke_num].time_sync = gMechanics_time_sync;
1348
    gSmoke[gSmoke_num].type = pType;
1349
    gSmoke[gSmoke_num].decay_factor = pDecay_factor;
1350
    gSmoke[gSmoke_num].pipe_me = 1;
1351
    gSmoke_num++;
1352
    if (gSmoke_num >= COUNT_OF(gSmoke)) {
1353
        gSmoke_num = 0;
1354
    }
1355
}
1356
 
1357
// IDA: void __cdecl ResetSmoke()
1358
void ResetSmoke(void) {
1359
    LOG_TRACE("()");
1360
 
1361
    gSmoke_flags = 0;
1362
    ;
1363
}
1364
 
1365
// IDA: void __usercall AdjustSmoke(int pIndex@<EAX>, tU8 pType@<EDX>, br_vector3 *pPos@<EBX>, br_scalar pRadius, br_scalar pStrength)
1366
void AdjustSmoke(int pIndex, tU8 pType, br_vector3* pPos, br_scalar pRadius, br_scalar pStrength) {
1367
    LOG_TRACE("(%d, %d, %p, %f, %f)", pIndex, pType, pPos, pRadius, pStrength);
1368
 
1369
    gSmoke[pIndex].type = pType;
1370
    gSmoke[pIndex].radius = pRadius;
1371
    gSmoke[pIndex].strength = pStrength;
1372
    BrVector3Copy(&gSmoke[pIndex].pos, pPos);
1373
    gSmoke_flags |= 1 << pIndex;
1374
}
1375
 
1376
// IDA: void __cdecl ActorError()
1377
void ActorError(void) {
1378
    LOG_TRACE("()");
1379
}
1380
 
1381
// IDA: void __usercall AdjustSmokeColumn(int pIndex@<EAX>, tCar_spec *pCar@<EDX>, int pVertex@<EBX>, int pColour@<ECX>)
1382
void AdjustSmokeColumn(int pIndex, tCar_spec* pCar, int pVertex, int pColour) {
1383
    int i;
1384
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
1385
    LOG_TRACE("(%d, %p, %d, %d)", pIndex, pCar, pVertex, pColour);
1386
 
1387
    gColumn_flags ^= 1 << pIndex;
1388
    gSmoke_column[pIndex].car = pCar;
1389
    gSmoke_column[pIndex].vertex_index = pVertex;
1390
    gSmoke_column[pIndex].colour = pColour;
1391
    for (i = 0; i < COUNT_OF(gSmoke_column->frame_count); i++) {
1392
        gSmoke_column[pIndex].frame_count[i] = 100;
1393
    }
1394
    if (pColour == 0) {
1395
        if ((gColumn_flags & (1 << pIndex)) != 0) {
1396
            if (gSmoke_column[pIndex].flame_actor->depth != 0) {
1397
                ActorError();
1398
            }
1399
            BrActorAdd(gNon_track_actor, gSmoke_column[pIndex].flame_actor);
1400
        } else {
1401
            if (gSmoke_column[pIndex].flame_actor->depth == 0) {
1402
                ActorError();
1403
            }
1404
            BrActorRemove(gSmoke_column[pIndex].flame_actor);
1405
        }
1406
    }
1407
}
1408
 
1409
// IDA: void __usercall CreateSmokeColumn(tCar_spec *pCar@<EAX>, int pColour@<EDX>, int pVertex_index@<EBX>, tU32 pLifetime@<ECX>)
1410
void CreateSmokeColumn(tCar_spec* pCar, int pColour, int pVertex_index, tU32 pLifetime) {
1411
    int i;
1412
    br_actor* actor;
1413
    tSmoke_column* col;
1414
    LOG_TRACE("(%p, %d, %d, %d)", pCar, pColour, pVertex_index, pLifetime);
1415
 
1416
    col = &gSmoke_column[gNext_column];
1417
    if (pCar->last_special_volume != NULL && pCar->last_special_volume->gravity_multiplier < 1.0f) {
1418
        return;
1419
    }
1420
    SmudgeCar(pCar, pVertex_index);
1421
    if (pCar->knackered) {
1422
        SmudgeCar(pCar, pCar->fire_vertex[IRandomBetween(0, 11)]);
1423
    }
1424
    if (!gSmoke_on) {
1425
        return;
1426
    }
1427
    if (((1u << gNext_column) & gColumn_flags) != 0) {
1428
        if (gSmoke_column[gNext_column].car != NULL) {
1429
            gSmoke_column[gNext_column].car->num_smoke_columns--;
1430
        }
1431
        StartPipingSession(ePipe_chunk_smoke_column);
1432
        AddSmokeColumnToPipingSession(gNext_column, gSmoke_column[gNext_column].car, gSmoke_column[gNext_column].vertex_index, gSmoke_column[gNext_column].colour);
1433
        EndPipingSession();
1434
        if (gSmoke_column[gNext_column].colour == 0) {
1435
            StartPipingSession(ePipe_chunk_flame);
1436
            actor = col->flame_actor->children;
1437
            for (i = 0; i < 3; i++) {
1438
                if (actor->type == BR_ACTOR_MODEL) {
1439
                    AddFlameToPipingSession(i + 16 * gNext_column, col->frame_count[i], col->scale_x[i], col->scale_y[i], col->offset_x[i], col->offset_z[i]);
1440
                }
1441
                actor->type = BR_ACTOR_NONE;
1442
                actor = actor->next;
1443
            }
1444
            EndPipingSession();
1445
        }
1446
    }
1447
    if (pColour == 0 && (((1u << gNext_column) & gColumn_flags) == 0 || gSmoke_column[gNext_column].colour != 0)) {
1448
        BrActorAdd(gNon_track_actor, gSmoke_column[gNext_column].flame_actor);
1449
    }
1450
    if (pColour != 0 && ((1u << gNext_column) & gColumn_flags) != 0 && gSmoke_column[gNext_column].colour == 0) {
1451
        BrActorRemove(gSmoke_column[gNext_column].flame_actor);
1452
    }
1453
    StartPipingSession(ePipe_chunk_smoke_column);
1454
    AddSmokeColumnToPipingSession(gNext_column, pCar, pVertex_index, pColour);
1455
    EndPipingSession();
1456
    gSmoke_column[gNext_column].car = pCar;
1457
    gSmoke_column[gNext_column].colour = pColour;
1458
    gSmoke_column[gNext_column].lifetime = pLifetime;
1459
    gSmoke_column[gNext_column].time = 0;
1460
    gSmoke_column[gNext_column].smudge_timer = 1000;
1461
    gSmoke_column[gNext_column].vertex_index = pVertex_index;
1462
    gSmoke_column[gNext_column].upright = 1;
1463
    gColumn_flags |= 1u << gNext_column;
1464
    pCar->num_smoke_columns++;
1465
    for (i = 0; i < COUNT_OF(gSmoke_column[gNext_column].frame_count); i++) {
1466
        gSmoke_column[gNext_column].frame_count[i] = 100;
1467
    }
1468
    gNext_column++;
1469
    if (gNext_column >= MAX_SMOKE_COLUMNS) {
1470
        gNext_column = 0;
1471
    }
1472
}
1473
 
1474
// IDA: void __cdecl GenerateSmokeShades()
1475
void GenerateSmokeShades(void) {
1476
    static int rb = 0x00;
1477
    static int gb = 0x00;
1478
    static int bb = 0x00;
1479
    static int rd = 0x40;
1480
    static int gd = 0x40;
1481
    static int bd = 0x40;
1482
    static int rg = 0x80;
1483
    static int gg = 0x80;
1484
    static int bg = 0x80;
1485
    LOG_TRACE("()");
1486
 
1487
    gBlack_smoke_shade_table = GenerateShadeTable(16, gRender_palette, rb, gb, bb, .25f, .6f, .9f);
1488
    gDark_smoke_shade_table = GenerateShadeTable(16, gRender_palette, rd, gd, bd, .25f, .6f, .9f);
1489
    gGrey_smoke_shade_table = GenerateShadeTable(16, gRender_palette, rg, gg, bg, .25f, .6f, .9f);
1490
    gIt_shade_table = GenerateDarkenedShadeTable(16, gRender_palette, 0, 255, 254, .25f, .5f, .75f, .6f);
1491
 
1492
    gShade_list[0] = gBlack_smoke_shade_table;
1493
    gShade_list[1] = gDark_smoke_shade_table;
1494
    gShade_list[2] = gGrey_smoke_shade_table;
1495
    gShade_list[3] = gFog_shade_table;
1496
    gShade_list[4] = gFog_shade_table;
1497
    gShade_list[7] = gAcid_shade_table;
1498
}
1499
 
1500
// IDA: void __cdecl GenerateItFoxShadeTable()
1501
void GenerateItFoxShadeTable(void) {
1502
    LOG_TRACE("()");
1503
 
1504
    if (gIt_shade_table == NULL) {
1505
        gIt_shade_table = GenerateDarkenedShadeTable(16, gRender_palette, 0, 255, 254, .25f, .5f, .75f, .6f);
1506
    }
1507
}
1508
 
1509
// IDA: void __usercall AdjustFlame(int pIndex@<EAX>, int pFrame_count@<EDX>, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z)
1510
void AdjustFlame(int pIndex, int pFrame_count, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z) {
1511
    int i;
1512
    int j;
1513
    tSmoke_column* col;
1514
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
1515
    LOG_TRACE("(%d, %d, %f, %f, %f, %f)", pIndex, pFrame_count, pScale_x, pScale_y, pOffset_x, pOffset_z);
1516
 
1517
    i = pIndex >> 4;
1518
    j = pIndex & 0xf;
1519
    col = &gSmoke_column[i];
1520
    col->frame_count[j] = pFrame_count;
1521
    col->scale_x[j] = pScale_x;
1522
    col->scale_y[j] = pScale_y;
1523
    col->offset_x[j] = pOffset_x;
1524
    col->offset_z[j] = pOffset_z;
1525
}
1526
 
1527
// IDA: void __usercall ReplayFlame(tSmoke_column *col@<EAX>, br_actor *actor@<EDX>)
1528
void ReplayFlame(tSmoke_column* col, br_actor* actor) {
1529
    int i;
1530
    LOG_TRACE("(%p, %p)", col, actor);
1531
 
1532
    for (i = 0; i < COUNT_OF(col->frame_count); i++, actor = actor->next) {
1533
        col->frame_count[i] += GetReplayRate();
1534
        if (col->frame_count[i] < 0 || col->frame_count[i] >= COUNT_OF(gFlame_map)) {
1535
            actor->type = BR_ACTOR_NONE;
1536
        } else {
1537
            actor->type = BR_ACTOR_MODEL;
1538
            actor->material->colour_map = gFlame_map[col->frame_count[i]];
1539
            BrMaterialUpdate(actor->material, BR_MATU_ALL);
1540
            BrMatrix34Scale(&actor->t.t.mat,
1541
                col->scale_x[i] * gFlame_map[col->frame_count[i]]->width,
1542
                col->scale_y[i] * gFlame_map[col->frame_count[i]]->height,
1543
                1.f);
1544
            actor->t.t.translate.t.v[0] = col->offset_x[i];
1545
            actor->t.t.translate.t.v[2] = col->offset_z[i];
1546
        }
1547
    }
1548
}
1549
 
1550
// IDA: void __usercall FlameAnimate(int c@<EAX>, br_vector3 *pPos@<EDX>, tU32 pTime@<EBX>)
1551
void FlameAnimate(int c, br_vector3* pPos, tU32 pTime) {
1552
    tSmoke_column* col;
1553
    br_actor* actor;
1554
    int i;
1555
    LOG_TRACE("(%d, %p, %d)", c, pPos, pTime);
1556
 
1557
    col = &gSmoke_column[c];
1558
    actor = col->flame_actor;
1559
    DRMatrix34RotateY(&actor->t.t.mat, FastScalarArcTan2Angle(gCamera_to_world.m[2][0], gCamera_to_world.m[2][2]));
1560
    actor->t.t.translate.t = *pPos;
1561
    actor = actor->children;
1562
 
1563
    if (gAction_replay_mode) {
1564
        ReplayFlame(col, actor);
1565
        return;
1566
    }
1567
    for (i = 0; i < COUNT_OF(col->frame_count); i++) {
1568
 
1569
#ifdef DETHRACE_FIX_BUGS
1570
        col->frame_time[i] += pTime;
1571
        if (col->frame_time[i] > FLAME_ANIMATION_FRAME_INTERVAL) {
1572
            col->frame_time[i] = 0;
1573
            col->frame_count[i]++;
1574
        }
1575
#else
1576
        col->frame_count[i]++;
1577
#endif
1578
 
1579
        if (col->frame_count[i] >= COUNT_OF(gFlame_map)) {
1580
            StartPipingSession(ePipe_chunk_flame);
1581
            AddFlameToPipingSession(i + 16 * c, col->frame_count[i] + 1, col->scale_x[i], col->scale_y[i], col->offset_x[i], col->offset_z[i]);
1582
            EndPipingSession();
1583
            col->frame_count[i] = IRandomBetween(-5, -1);
1584
            col->scale_x[i] = (2 * IRandomBetween(0, 1) - 1) * SRandomBetween(1.0f, 1.5f) * 0.003f;
1585
            col->scale_y[i] = SRandomBetween(0.5f, 1.0f) * 0.003f;
1586
            col->offset_x[i] = SRandomPosNeg(0.03f);
1587
            col->offset_z[i] = SRandomBetween(-0.03f, 0.0);
1588
            actor->type = BR_ACTOR_NONE;
1589
        }
1590
        if (col->frame_count[i] == 0) {
1591
            if (BrVector3LengthSquared(&col->car->v) >= 80.0f || col->lifetime <= 30 * pTime) {
1592
                col->frame_count[i] = -5;
1593
            } else {
1594
                actor->type = BR_ACTOR_MODEL;
1595
                StartPipingSession(ePipe_chunk_flame);
1596
                AddFlameToPipingSession(i + 16 * c, col->frame_count[i] - 1, col->scale_x[i], col->scale_y[i], col->offset_x[i], col->offset_z[i]);
1597
                EndPipingSession();
1598
            }
1599
        }
1600
        if (col->frame_count[i] >= 0) {
1601
            actor->material->colour_map = gFlame_map[col->frame_count[i]];
1602
            BrMaterialUpdate(actor->material, BR_MATU_ALL);
1603
            BrMatrix34Scale(&actor->t.t.mat, gFlame_map[col->frame_count[i]]->width * col->scale_x[i], gFlame_map[col->frame_count[i]]->height * col->scale_y[i], 1.0);
1604
            actor->t.t.mat.m[3][0] = col->offset_x[i];
1605
            actor->t.t.mat.m[3][2] = col->offset_z[i];
1606
        }
1607
        actor = actor->next;
1608
    }
1609
}
1610
 
1611
// IDA: void __usercall DoSmokeColumn(int i@<EAX>, tU32 pTime@<EDX>, br_vector3 *pRet_car_pos@<EBX>)
1612
void DoSmokeColumn(int i, tU32 pTime, br_vector3* pRet_car_pos) {
1613
    tCar_spec* c;
1614
    br_actor* actor;
1615
    br_actor* bonny;
1616
    //int group; // Pierre-Marie Baty -- unused variable
1617
    LOG_TRACE("(%d, %d, %p)", i, pTime, pRet_car_pos);
1618
 
1619
    c = gSmoke_column[i].car;
1620
    if (c->car_master_actor->t.t.mat.m[1][1] > 0.1f) {
1621
        gSmoke_column[i].upright = 1;
1622
    }
1623
    if (c->car_master_actor->t.t.mat.m[1][1] < -0.1f) {
1624
        gSmoke_column[i].upright = 0;
1625
    }
1626
    actor = c->car_model_actors[c->principal_car_actor].actor;
1627
    bonny = c->car_model_actors[c->car_actor_count - 1].actor;
1628
 
18 pmbaty 1629
    BrVector3Add(pRet_car_pos, &V11MODEL(actor->model)->groups[0].position[gSmoke_column[i].vertex_index], &actor->t.t.translate.t);
1 pmbaty 1630
    if (gProgram_state.cockpit_on && c->driver == eDriver_local_human) {
1631
        if (c->driver_z_offset + 0.2f <= pRet_car_pos->v[2]) {
1632
            pRet_car_pos->v[1] -= -0.07f;
1633
        } else {
18 pmbaty 1634
            BrMatrix34ApplyP(pRet_car_pos, &V11MODEL(actor->model)->groups[0].position[gSmoke_column[i].vertex_index], &bonny->t.t.mat);
1 pmbaty 1635
        }
1636
    }
1637
    if (!gSmoke_column[i].upright) {
1638
        pRet_car_pos->v[1] = c->bounds[1].min.v[1] / WORLD_SCALE;
1639
    }
1640
    BrMatrix34ApplyP(&gSmoke_column[i].pos, pRet_car_pos, &c->car_master_actor->t.t.mat);
1641
    gSmoke_column[i].pos.v[1] -= 0.03f;
1642
}
1643
 
1644
// IDA: void __usercall ReplaySmokeColumn(tU32 pTime@<EAX>)
1645
void ReplaySmokeColumn(tU32 pTime) {
1646
    int i;
1647
    br_vector3 dummy;
1648
    LOG_TRACE("(%d)", pTime);
1649
 
1650
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1651
        if ((gColumn_flags & (1 << i)) != 0) {
1652
            DoSmokeColumn(i, pTime, &dummy);
1653
            if (gSmoke_column[i].colour == 0) {
1654
                FlameAnimate(i, &gSmoke_column[i].pos, pTime);
1655
            }
1656
        }
1657
    }
1658
}
1659
 
1660
// IDA: void __usercall MungeSmokeColumn(tU32 pTime@<EAX>)
1661
void MungeSmokeColumn(tU32 pTime) {
1662
    int i;
1663
    int plane;
1664
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
1665
    //br_actor* bonny; // Pierre-Marie Baty -- unused variable
1666
    br_vector3 car_pos;
1667
    br_vector3 pos;
1668
    br_vector3 v;
1669
    //br_vector3 up; // Pierre-Marie Baty -- unused variable
1670
    //br_vector3 start; // Pierre-Marie Baty -- unused variable
1671
    //br_vector3 end; // Pierre-Marie Baty -- unused variable
1672
    //br_scalar ts; // Pierre-Marie Baty -- unused variable
1673
    br_scalar decay_factor;
1674
    tCar_spec* c;
1675
    LOG_TRACE("(%d)", pTime);
1676
 
1677
    if (gColumn_flags == 0) {
1678
        return;
1679
    }
1680
    if (gAction_replay_mode) {
1681
        ReplaySmokeColumn(pTime);
1682
        return;
1683
    }
1684
 
1685
    gMechanics_time_sync = 1;
1686
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1687
        if (((1u << i) & gColumn_flags) == 0) {
1688
            continue;
1689
        }
1690
        if (gSmoke_column[i].lifetime >= pTime) {
1691
            gSmoke_column[i].lifetime -= pTime;
1692
            c = gSmoke_column[i].car;
1693
            DoSmokeColumn(i, pTime, &car_pos);
1694
            if (gSmoke_column[i].colour == 0) {
1695
                FlameAnimate(i, &gSmoke_column[i].pos, pTime);
1696
                if (gSmoke_column[i].smudge_timer >= pTime) {
1697
                    gSmoke_column[i].smudge_timer -= pTime;
1698
                } else {
1699
                    gSmoke_column[i].smudge_timer += 2000;
1700
                    SmudgeCar(gSmoke_column[i].car, gSmoke_column[i].vertex_index);
1701
                    if (gSmoke_column[i].car->knackered) {
1702
                        plane = IRandomBetween(0, COUNT_OF(gSmoke_column[i].car->fire_vertex) - 1);
1703
                        SmudgeCar(gSmoke_column[i].car, gSmoke_column[i].car->fire_vertex[plane]);
1704
                    }
1705
                }
1706
            }
1707
            gSmoke_column[i].time += pTime;
1708
            if (gSmoke_column[i].time > 200) {
1709
#ifdef DETHRACE_FIX_BUGS
1710
                gSmoke_column[i].time -= fmaxf(SMOKE_COLUMN_NEW_PUFF_INTERVAL, pTime);
1711
#else
1712
                gSmoke_column[i].time -= pTime;
1713
#endif
1714
                gSmoke_column[i].count++;
1715
                BrVector3Cross(&v, &c->omega, &car_pos);
1716
                BrMatrix34ApplyV(&car_pos, &v, &c->car_master_actor->t.t.mat);
1717
                BrVector3Add(&v, &c->v, &car_pos);
1718
                v.v[1] = v.v[1] + 2.898550724637681f; // 100 / 34.5 ?
1719
                pos.v[0] = SRandomBetween(-0.03f, 0.03f) + gSmoke_column[i].pos.v[0];
1720
                pos.v[1] = (gSmoke_column[i].colour == 0) * 0.05f + gSmoke_column[i].pos.v[1];
1721
                pos.v[2] = SRandomBetween(-0.03f, 0.03f) + gSmoke_column[i].pos.v[2];
1722
                if ((gSmoke_column[i].whiter & 2) == 0 || IRandomBetween(0, 3)) {
1723
                    if (gSmoke_column[i].whiter < 1) {
1724
                        gSmoke_column[i].whiter = -1;
1725
                    } else {
1726
                        gSmoke_column[i].whiter = 2;
1727
                    }
1728
                } else {
1729
                    gSmoke_column[i].whiter &= 1;
1730
                }
1731
                decay_factor = ((gSmoke_column[i].whiter > 0) + 1.0f) / 2.0f;
1732
                if (gSmoke_column[i].lifetime < 4000) {
1733
                    decay_factor = gSmoke_column[i].lifetime * decay_factor / 4000.0f;
1734
                }
1735
                CreatePuffOfSmoke(&pos, &v, decay_factor, decay_factor, gSmoke_column[i].colour + 16, c);
1736
            }
1737
        } else {
1738
            if (gSmoke_column[i].car != NULL) {
1739
                StartPipingSession(ePipe_chunk_smoke_column);
1740
                AddSmokeColumnToPipingSession(i, gSmoke_column[i].car, gSmoke_column[i].vertex_index, gSmoke_column[i].colour);
1741
                EndPipingSession();
1742
            }
1743
            gColumn_flags &= ~(1u << i);
1744
            if (gSmoke_column[i].colour == 0) {
1745
                BrActorRemove(gSmoke_column[i].flame_actor);
1746
            }
1747
            if (gSmoke_column[i].car != NULL) {
1748
                if (gSmoke_column[i].car->num_smoke_columns != 0) {
1749
                    gSmoke_column[i].car->num_smoke_columns--;
1750
                }
1751
            }
1752
        }
1753
    }
1754
}
1755
 
1756
// IDA: void __cdecl DisposeFlame()
1757
void DisposeFlame(void) {
1758
    int i;
1759
    int j;
1760
    br_actor* actor;
1761
    //br_material* material; // Pierre-Marie Baty -- unused variable
1762
    LOG_TRACE("()");
1763
 
1764
    for (i = 0; i < COUNT_OF(gFlame_map); i++) {
1765
        BrMapRemove(gFlame_map[i]);
1766
        BrPixelmapFree(gFlame_map[i]);
1767
    }
1768
 
1769
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1770
        if ((gColumn_flags & (1u << i)) && (gSmoke_column[i].colour == 0)) {
1771
            BrActorRemove(gSmoke_column[i].flame_actor);
1772
        }
1773
        actor = gSmoke_column[i].flame_actor->children;
1774
        for (j = 0; j < COUNT_OF(gSmoke_column[0].frame_count); j++) {
1775
            BrMaterialRemove(actor->material);
1776
            BrMaterialFree(actor->material);
1777
            actor = actor->next;
1778
        }
1779
        BrActorFree(gSmoke_column[i].flame_actor);
1780
    }
1781
    BrModelRemove(gLollipop_model);
1782
    BrModelFree(gLollipop_model);
1783
}
1784
 
1785
// IDA: void __cdecl InitFlame()
1786
void InitFlame(void) {
1787
    int i;
1788
    int j;
1789
    int num;
1790
    char the_path[256];
1791
    br_actor* actor;
1792
    br_material* material;
1793
    LOG_TRACE("()");
1794
 
1795
    gColumn_flags = 0;
1796
    gLollipop_model = BrModelAllocate("Lollipop", 4, 2);
1797
    PathCat(the_path, gApplication_path, "PIXELMAP");
1798
    PathCat(the_path, the_path, "FLAMES.PIX");
1799
    num = DRPixelmapLoadMany(the_path, gFlame_map, COUNT_OF(gFlame_map));
1800
    if (num != COUNT_OF(gFlame_map)) {
1801
        FatalError(kFatalError_LoadPixelmapFile_S, the_path);
1802
    }
1803
    BrMapAddMany(gFlame_map, COUNT_OF(gFlame_map));
1804
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1805
        gSmoke_column[i].flame_actor = BrActorAllocate(BR_ACTOR_NONE, NULL);
1806
        for (j = 0; j < COUNT_OF(gSmoke_column[0].frame_count); j++) {
1807
            actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
1808
            material = BrMaterialAllocate(NULL);
1809
            BrActorAdd(gSmoke_column[i].flame_actor, actor);
1810
            actor->model = gLollipop_model;
1811
            actor->material = material;
1812
            material->flags &= ~BR_MATF_LIGHT;
1813
            material->flags |= BR_MATF_ALWAYS_VISIBLE;
1814
            material->colour_map = gFlame_map[0];
1815
            BrMaterialAdd(material);
1816
            gSmoke_column[i].frame_count[j] = 100;
1817
        }
1818
    }
1819
    gLollipop_model->nvertices = 4;
1820
    BrVector3SetFloat(&gLollipop_model->vertices[0].p, -.5f, 0.f, .0f);
1821
    BrVector3SetFloat(&gLollipop_model->vertices[1].p, .5f, 0.f, .0f);
1822
    BrVector3SetFloat(&gLollipop_model->vertices[2].p, .5f, 1.f, .0f);
1823
    BrVector3SetFloat(&gLollipop_model->vertices[3].p, -.5f, 1.f, .0f);
1824
    gLollipop_model->vertices[0].map.v[0] = 0.f;
1825
    gLollipop_model->vertices[0].map.v[1] = 1.f;
1826
    gLollipop_model->vertices[1].map.v[0] = 1.f;
1827
    gLollipop_model->vertices[1].map.v[1] = 1.f;
1828
    gLollipop_model->vertices[2].map.v[0] = 1.f;
1829
    gLollipop_model->vertices[2].map.v[1] = 0.f;
1830
    gLollipop_model->vertices[3].map.v[0] = 0.f;
1831
    gLollipop_model->vertices[3].map.v[1] = 0.f;
1832
 
1833
    gLollipop_model->nfaces = 2;
1834
    gLollipop_model->faces[0].vertices[0] = 0;
1835
    gLollipop_model->faces[0].vertices[1] = 1;
1836
    gLollipop_model->faces[0].vertices[2] = 2;
1837
    gLollipop_model->faces[1].vertices[0] = 0;
1838
    gLollipop_model->faces[1].vertices[1] = 2;
1839
    gLollipop_model->faces[1].vertices[2] = 3;
1840
    gLollipop_model->faces[0].smoothing = 1;
1841
    gLollipop_model->faces[1].smoothing = 1;
1842
    BrModelAdd(gLollipop_model);
1843
}
1844
 
1845
// IDA: void __usercall InitSplash(FILE *pF@<EAX>)
1846
void InitSplash(FILE* pF) {
1847
    int i;
1848
    int num_files;
1849
    int num;
1850
    br_actor* actor;
1851
    char the_path[256];
1852
    char s[256];
1853
    br_pixelmap* splash_maps[20];
1854
    LOG_TRACE("(%p)", pF);
1855
 
1856
    gSplash_flags = 0;
1857
    gSplash_model = BrModelAllocate("Splash", 4, 2);
1858
    if (pF != NULL) {
1859
        num = GetAnInt(pF);
1860
        gNum_splash_types = 0;
1861
        for (i = 0; num > i; ++i) {
1862
            GetAString(pF, s);
1863
            PathCat(the_path, gApplication_path, "PIXELMAP");
1864
            PathCat(the_path, the_path, s);
1865
            num_files = DRPixelmapLoadMany(the_path, &splash_maps[gNum_splash_types], 20 - gNum_splash_types);
1866
            if (num_files == 0) {
1867
                FatalError(kFatalError_LoadPixelmapFile_S, the_path);
1868
            }
1869
            gNum_splash_types += num_files;
1870
        }
1871
    } else {
1872
        PathCat(the_path, gApplication_path, "PIXELMAP");
1873
        PathCat(the_path, the_path, "SPLSHBLU.PIX");
1874
        gNum_splash_types = DRPixelmapLoadMany(the_path, splash_maps, 0x14u);
1875
    }
1876
    BrMapAddMany(splash_maps, gNum_splash_types);
1877
    for (i = 0; i < gNum_splash_types; ++i) {
1878
        gSplash_material[i] = BrMaterialAllocate(0);
1879
        gSplash_material[i]->flags &= ~(BR_MATF_LIGHT | BR_MATF_PRELIT);
1880
        gSplash_material[i]->flags |= BR_MATF_ALWAYS_VISIBLE | BR_MATF_PERSPECTIVE;
1881
        gSplash_material[i]->index_blend = LoadSingleShadeTable(&gTrack_storage_space, "BLEND50.TAB");
1882
        gSplash_material[i]->colour_map = splash_maps[i];
1883
        BrMaterialAdd(gSplash_material[i]);
1884
    }
1885
    gSplash_model->nvertices = 4;
1886
    BrVector3SetFloat(&gSplash_model->vertices[0].p, -0.5f, 0.0f, 0.0f);
1887
    BrVector3SetFloat(&gSplash_model->vertices[1].p, 0.5f, 0.0f, 0.0f);
1888
    BrVector3SetFloat(&gSplash_model->vertices[2].p, 0.5f, 1.0f, 0.0f);
1889
    BrVector3SetFloat(&gSplash_model->vertices[3].p, -0.5f, 1.0f, 0.0f);
1890
    gSplash_model->vertices[0].map.v[0] = 0.0f;
1891
    gSplash_model->vertices[0].map.v[1] = 1.0f;
1892
    gSplash_model->vertices[1].map.v[0] = 1.0f;
1893
    gSplash_model->vertices[1].map.v[1] = 1.0f;
1894
    gSplash_model->vertices[2].map.v[0] = 1.0f;
1895
    gSplash_model->vertices[2].map.v[1] = 0.0f;
1896
    gSplash_model->vertices[3].map.v[0] = 0.0f;
1897
    gSplash_model->vertices[3].map.v[1] = 0.0f;
1898
    gSplash_model->nfaces = 2;
1899
    gSplash_model->faces[0].vertices[0] = 0;
1900
    gSplash_model->faces[0].vertices[1] = 1;
1901
    gSplash_model->faces[0].vertices[2] = 2;
1902
    gSplash_model->faces[1].vertices[0] = 0;
1903
    gSplash_model->faces[1].vertices[1] = 2;
1904
    gSplash_model->faces[1].vertices[2] = 3;
1905
    gSplash_model->faces[0].smoothing = 1;
1906
    gSplash_model->faces[1].smoothing = 1;
1907
    BrModelAdd(gSplash_model);
1908
    for (i = 0; i < COUNT_OF(gSplash); i++) {
1909
        gSplash[i].actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
1910
        actor = gSplash[i].actor;
1911
        actor->model = gSplash_model;
1912
        if (gNum_splash_types != 0) {
1913
            actor->material = gSplash_material[IRandomBetween(0, gNum_splash_types - 1)];
1914
        } else {
1915
            actor->material = NULL;
1916
        }
1917
        gSplash[i].scale_x = SRandomBetween(0.9f, 1.1f) * (2 * IRandomBetween(0, 1) - 1);
1918
    }
1919
}
1920
 
1921
// IDA: void __cdecl DisposeSplash()
1922
void DisposeSplash(void) {
1923
    int i;
1924
    LOG_TRACE("()");
1925
 
1926
    for (i = 0; i < gNum_splash_types; i++) {
1927
        BrMapRemove(gSplash_material[i]->colour_map);
1928
        BrPixelmapFree(gSplash_material[i]->colour_map);
1929
        BrMaterialRemove(gSplash_material[i]);
1930
        BrMaterialFree(gSplash_material[i]);
1931
    }
1932
    for (i = 0; i < COUNT_OF(gSplash); i++) {
1933
        if (gSplash_flags & (1u << i)) {
1934
            BrActorRemove(gSplash[i].actor);
1935
        }
1936
        BrActorFree(gSplash[i].actor);
1937
    }
1938
    BrModelRemove(gSplash_model);
1939
    BrModelFree(gSplash_model);
1940
}
1941
 
1942
// IDA: void __usercall DrawTheGlow(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>)
1943
void DrawTheGlow(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera) {
1944
    int i;
1945
    br_scalar strength;
1946
    br_vector3 tv;
1947
    tU32 seed;
1948
    LOG_TRACE("(%p, %p, %p)", pRender_screen, pDepth_buffer, pCamera);
1949
 
1950
    if (gColumn_flags) {
1951
        seed = rand();
1952
        srand(GetTotalTime());
1953
        for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1954
            if (((1u << i) & gColumn_flags) != 0 && gSmoke_column[i].colour <= 1) {
1955
                strength = 0.5f;
1956
                if (gSmoke_column[i].lifetime < 4000) {
1957
                    strength = gSmoke_column[i].lifetime * 0.5f / 4000.f;
1958
                }
1959
                BrVector3Set(&tv, gSmoke_column[i].pos.v[0], gSmoke_column[i].pos.v[1] + 0.02f, gSmoke_column[i].pos.v[2]);
1960
                SmokeCircle3D(&tv, 0.07f, strength, SRandomBetween(0.5f, 0.99f), pRender_screen, pDepth_buffer, gAcid_shade_table, pCamera);
1961
            }
1962
        }
1963
        srand(seed);
1964
    }
1965
}
1966
 
1967
// IDA: void __usercall PipeInstantUnSmudge(tCar_spec *pCar@<EAX>)
1968
void PipeInstantUnSmudge(tCar_spec* pCar) {
1969
    br_model* model;
1970
    br_model* b_model;
1971
    br_actor* actor;
1972
    br_actor* bonny;
1973
    int j;
1974
    int n;
1975
    int v;
1976
    int group;
1977
    tSmudged_vertex data[1000];
1978
    LOG_TRACE("(%p)", pCar);
1979
 
1980
    if (!gAction_replay_mode) {
1981
        return;
1982
    }
1983
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
1984
    model = actor->model;
1985
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
1986
    n = 0;
1987
    if ((model->flags & BR_MODF_KEEP_ORIGINAL) != 0 || (model->flags & BR_MODF_UPDATEABLE) != 0) {
1988
        StartPipingSession(ePipe_chunk_smudge);
1989
        j = 0;
1990
        for (group = 0; group < V11MODEL(model)->ngroups; group++) {
1991
            for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) {
1992
                if ((V11MODEL(model)->groups[group].vertex_colours[v] >> 24) != 0) {
1993
                    data[n].vertex_index = j;
1994
                    data[n].light_index = 0 -(V11MODEL(model)->groups[group].vertex_colours[v] >> 24); // Pierre-Marie Baty -- prefixed 0 to silence signedness warning
1995
                    n += 1;
1996
                    V11MODEL(model)->groups[group].vertex_colours[v] = 0;
1997
                    if ((model->flags & 0x80) != 0) {
1998
                        model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = 0;
1999
                    }
2000
                    if (n >= COUNT_OF(data)) {
2001
                        group = V11MODEL(model)->ngroups;
2002
                        break;
2003
                    }
2004
                }
2005
                j += 1;
2006
            }
2007
        }
2008
        if (n != 0) {
2009
            AddSmudgeToPipingSession(pCar->car_ID, pCar->principal_car_actor, n, data);
2010
        }
2011
        if (bonny != actor) {
2012
            b_model = bonny->model;
2013
            n = 0;
2014
            j = 0;
2015
            for (group = 0; group < V11MODEL(model)->ngroups; group++) {
2016
                for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) {
2017
                    if ((V11MODEL(model)->groups[group].vertex_colours[v] >> 24) != 0) {
2018
                        data[n].vertex_index = j;
2019
                        data[n].light_index = -V11MODEL(model)->groups[group].nvertices;
2020
                        n += 1;
2021
                        V11MODEL(model)->groups[group].vertex_colours[v] = 0;
2022
                        if ((b_model->flags & BR_MODF_UPDATEABLE) != 0) {
2023
                            b_model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = 0;
2024
                        }
2025
                        if (n >= COUNT_OF(data)) {
2026
                            group = V11MODEL(model)->groups[group].nvertices;
2027
                            break;
2028
                        }
2029
                    }
2030
                    j += 1;
2031
                }
2032
            }
2033
            if (n != 0) {
2034
                AddSmudgeToPipingSession(pCar->car_ID, pCar->car_actor_count - 1, n, data);
2035
            }
2036
        }
2037
        EndPipingSession();
2038
    }
2039
}
2040
 
2041
// IDA: void __usercall SmudgeCar(tCar_spec *pCar@<EAX>, int fire_point@<EDX>)
2042
void SmudgeCar(tCar_spec* pCar, int fire_point) {
2043
    int v;
2044
    int j;
2045
    int real_vertex_number;
2046
    br_model* model;
2047
    br_model* b_model;
2048
    br_actor* actor;
2049
    br_actor* bonny;
2050
    br_scalar ts;
2051
    br_vector3 tv;
2052
    br_vector3 bonny_pos;
2053
    int group;
2054
    br_vector3 point;
2055
    tSmudged_vertex data[30];
2056
    int n;
2057
    LOG_TRACE("(%p, %d)", pCar, fire_point);
2058
 
2059
    if (gAusterity_mode) {
2060
        return;
2061
    }
2062
 
2063
    v = fire_point;
2064
    group = 0;
2065
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
2066
    model = actor->model;
2067
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
2068
    n = 0;
2069
    real_vertex_number = 0;
2070
    if ((model->flags & BR_MODF_KEEP_ORIGINAL) != 0 || (model->flags & BR_MODF_UPDATEABLE) != 0) {
18 pmbaty 2071
        point = V11MODEL(model)->groups[group].position[fire_point];
1 pmbaty 2072
        StartPipingSession(ePipe_chunk_smudge);
2073
        for (group = 0; group < V11MODEL(model)->ngroups; group++) {
2074
            for (j = 0; j < V11MODEL(model)->groups[group].nvertices; j++) {
18 pmbaty 2075
                BrVector3Sub(&tv, &V11MODEL(model)->groups[group].position[j], &point);
1 pmbaty 2076
                ts = (.0144f - BrVector3LengthSquared(&tv) / SRandomBetween(.5f, 1.f)) / .0144f * 127.f;
2077
                if (ts > 0.f) {
2078
                    ts += BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]);
2079
                    if (ts > 255.f) {
2080
                        ts = 255.f;
2081
                    }
2082
                    if (BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]) != (int)ts) {
2083
                        data[n].vertex_index = real_vertex_number;
2084
                        data[n].light_index = (int)ts - BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]);
2085
                        V11MODEL(model)->groups[group].vertex_colours[j] = (int)ts << 24;
2086
                        if ((model->flags & BR_MODF_UPDATEABLE) != 0) {
2087
                            model->vertices[V11MODEL(model)->groups[group].vertex_user[j]].index = (int)ts;
2088
                        }
2089
                        n++;
2090
                        if (n >= COUNT_OF(data)) {
2091
                            break;
2092
                        }
2093
                    }
2094
                }
2095
                real_vertex_number++;
2096
            }
2097
            if (n >= COUNT_OF(data)) {
2098
                break;
2099
            }
2100
        }
2101
        if (n > 0) {
2102
            AddSmudgeToPipingSession(pCar->car_ID, pCar->principal_car_actor, n, data);
18 pmbaty 2103
            // FIXME?
1 pmbaty 2104
            // Added by dethrace to update gpu-buffered vertices
18 pmbaty 2105
            // model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
1 pmbaty 2106
        }
2107
 
2108
        n = 0;
2109
        real_vertex_number = 0;
2110
        if (actor != bonny) {
2111
            b_model = bonny->model;
2112
            BrVector3Add(&tv, &actor->t.t.translate.t, &point);
2113
            BrVector3Accumulate(&tv, &bonny->t.t.translate.t);
2114
            BrMatrix34TApplyV(&bonny_pos, &tv, &bonny->t.t.mat);
2115
            for (group = 0; group < V11MODEL(b_model)->ngroups; group++) {
2116
                j = 0;
2117
                for (j = 0; j < V11MODEL(b_model)->groups[group].nvertices; j++) {
18 pmbaty 2118
                    BrVector3Sub(&tv, &V11MODEL(b_model)->groups[group].position[j], &bonny_pos);
1 pmbaty 2119
                    ts = (.0144f - BrVector3LengthSquared(&tv) / SRandomBetween(.5f, 1.f)) / .0144f * 127.f;
2120
                    if (ts > 0.f) {
2121
                        ts += BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]);
2122
                        if (ts > 255.f) {
2123
                            ts = 255.f;
2124
                        }
2125
                        if (BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]) != (int)ts) {
2126
                            data[n].vertex_index = real_vertex_number;
2127
                            data[n].light_index = (int)ts - BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]);
2128
                            V11MODEL(b_model)->groups[group].vertex_colours[j] = (int)ts << 24;
2129
                            if ((b_model->flags & BR_MODF_UPDATEABLE) != 0) {
2130
                                b_model->vertices[V11MODEL(b_model)->groups[group].vertex_user[j]].index = (int)ts;
2131
                            }
2132
                            n++;
2133
                            if (n >= COUNT_OF(data)) {
2134
                                break;
2135
                            }
2136
                        }
2137
                    }
2138
                    real_vertex_number++;
2139
                }
2140
                if (n >= COUNT_OF(data)) {
2141
                    break;
2142
                }
2143
            }
2144
            if (n > 0) {
2145
                AddSmudgeToPipingSession(pCar->car_ID, pCar->car_actor_count - 1, n, data);
18 pmbaty 2146
                // FIXME?
1 pmbaty 2147
                // Added by dethrace to update gpu-buffered vertices
18 pmbaty 2148
                // b_model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
1 pmbaty 2149
            }
2150
        }
2151
        EndPipingSession();
2152
    }
2153
}
2154
 
2155
// IDA: void __cdecl ResetSmokeColumns()
2156
void ResetSmokeColumns(void) {
18 pmbaty 2157
    int i;
1 pmbaty 2158
    LOG_TRACE("()");
18 pmbaty 2159
 
2160
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2161
        if (gColumn_flags & (1 << i)) {
2162
            BrActorRemove(gSmoke_column[i].flame_actor);
2163
        }
2164
    }
2165
    gColumn_flags = 0;
1 pmbaty 2166
}
2167
 
2168
// IDA: void __usercall SetSmokeOn(int pSmoke_on@<EAX>)
2169
void SetSmokeOn(int pSmoke_on) {
2170
    LOG_TRACE("(%d)", pSmoke_on);
2171
 
2172
    gSmoke_on = pSmoke_on;
2173
}
2174
 
2175
// IDA: void __usercall ReallySetSmokeOn(int pSmoke_on@<EAX>)
2176
void ReallySetSmokeOn(int pSmoke_on) {
2177
    LOG_TRACE("(%d)", pSmoke_on);
18 pmbaty 2178
 
2179
    ResetSmoke();
2180
    ResetSmokeColumns();
1 pmbaty 2181
}
2182
 
2183
// IDA: void __usercall SetSmoke(int pSmoke_on@<EAX>)
2184
void SetSmoke(int pSmoke_on) {
2185
    LOG_TRACE("(%d)", pSmoke_on);
2186
 
2187
    ReallySetSmokeOn(pSmoke_on);
2188
    SetSmokeOn(pSmoke_on);
2189
}
2190
 
2191
// IDA: int __cdecl GetSmokeOn()
2192
int GetSmokeOn(void) {
2193
    LOG_TRACE("()");
2194
 
2195
    return gSmoke_on;
2196
}
2197
 
2198
// IDA: void __usercall StopCarSmoking(tCar_spec *pCar@<EAX>)
2199
void StopCarSmoking(tCar_spec* pCar) {
2200
    int i;
2201
    LOG_TRACE("(%p)", pCar);
2202
 
2203
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2204
        if (gSmoke_column[i].car == pCar && gSmoke_column[i].lifetime > 2000) {
2205
            gSmoke_column[i].lifetime = 2000;
2206
        }
2207
    }
2208
}
2209
 
2210
// IDA: void __usercall StopCarSmokingInstantly(tCar_spec *pCar@<EAX>)
2211
void StopCarSmokingInstantly(tCar_spec* pCar) {
2212
    int i;
2213
    LOG_TRACE("(%p)", pCar);
2214
 
2215
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2216
        if (gSmoke_column[i].car == pCar) {
2217
            gSmoke_column[i].lifetime = 0;
2218
        }
2219
    }
2220
}
2221
 
2222
// IDA: void __usercall ConditionalSmokeColumn(tCar_spec *pCar@<EAX>, int pDamage_index@<EDX>, int pColour@<EBX>)
2223
void ConditionalSmokeColumn(tCar_spec* pCar, int pDamage_index, int pColour) {
2224
    int i;
2225
    LOG_TRACE("(%p, %d, %d)", pCar, pDamage_index, pColour);
2226
 
2227
    if (!pColour) {
2228
        pColour = pCar->driver < eDriver_net_human;
2229
    }
2230
    if (pCar->num_smoke_columns != 0) {
2231
        for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2232
            if (gSmoke_column[i].car == pCar) {
2233
                if (((1u << i) & gColumn_flags) != 0 && gSmoke_column[i].colour <= pColour && gSmoke_column[i].lifetime) {
2234
                    return;
2235
                }
2236
                gSmoke_column[i].lifetime = 2000;
2237
            }
2238
        }
2239
    }
2240
    CreateSmokeColumn(pCar, pColour, pCar->fire_vertex[pDamage_index], 10000000u);
2241
}
2242
 
2243
// IDA: void __usercall SingleSplash(tCar_spec *pCar@<EAX>, br_vector3 *sp@<EDX>, br_vector3 *normal@<EBX>, tU32 pTime@<ECX>)
2244
void SingleSplash(tCar_spec* pCar, br_vector3* sp, br_vector3* normal, tU32 pTime) {
2245
    br_matrix34* mat;
2246
    br_matrix34* c_mat;
2247
    br_vector3 tv;
2248
    br_vector3 vel;
2249
    br_scalar size;
2250
    br_scalar speed;
2251
    br_scalar ts;
2252
    LOG_TRACE("(%p, %p, %p, %d)", pCar, sp, normal, pTime);
2253
 
2254
    mat = &gSplash[gNext_splash].actor->t.t.mat;
2255
    c_mat = &pCar->car_master_actor->t.t.mat;
2256
    BrMatrix34ApplyP(&gSplash[gNext_splash].actor->t.t.euler.t, sp, c_mat);
2257
    tv.v[0] = sp->v[2] * pCar->omega.v[1] - pCar->omega.v[2] * sp->v[1];
2258
    tv.v[1] = pCar->omega.v[2] * sp->v[0] - sp->v[2] * pCar->omega.v[0];
2259
    tv.v[2] = sp->v[1] * pCar->omega.v[0] - pCar->omega.v[1] * sp->v[0];
2260
    BrMatrix34ApplyV(&vel, &tv, c_mat);
2261
    BrVector3Accumulate(&vel, &pCar->v);
2262
    ts = BrVector3Length(&vel);
2263
    size = (fabs(BrVector3Dot(normal, &vel)) * 5.0 + ts) / 150.0 + 0.047826085;
2264
    if (size > 0.5) {
2265
        size = 0.5;
2266
    }
2267
    if (BrVector3Dot(&pCar->velocity_car_space, sp) < 0.0) {
2268
        size = size / 2.0;
2269
    }
2270
 
2271
    gSplash[gNext_splash].size = SRandomBetween(size / 2.0, size);
2272
    if (((1u << gNext_splash) & gSplash_flags) == 0) {
2273
        BrActorAdd(gDont_render_actor, gSplash[gNext_splash].actor);
2274
    }
2275
    gSplash_flags |= 1u << gNext_splash;
2276
    gSplash[gNext_splash].just_done = 1;
2277
    if ((double)pTime * 0.003 > SRandomBetween(0.0, 1.0) && !gAction_replay_mode) {
2278
        BrVector3InvScale(&vel, &vel, WORLD_SCALE);
2279
        BrVector3Scale(&tv, &vel, 0.1f);
2280
        speed = sqrt(ts / 70.0) * 15.0;
2281
        if (speed > 15.0f) {
2282
            speed = 15.0f;
2283
        }
2284
        tv.v[1] += SRandomBetween(5.0, speed) / WORLD_SCALE;
2285
        BrMatrix34TApplyV(&vel, &tv, &pCar->car_master_actor->t.t.mat);
2286
 
2287
        BrVector3Cross(&tv, &vel, &pCar->water_normal);
2288
        BrVector3Scale(&tv, &tv, 0.5f);
2289
        if (BrVector3Dot(sp, &tv) <= 0.0) {
2290
            BrVector3Sub(&vel, &vel, &tv);
2291
        } else {
2292
            BrVector3Accumulate(&vel, &tv);
2293
        }
2294
        CreateSingleSpark(pCar, sp, &vel);
2295
    }
2296
    gNext_splash++;
2297
    if (gNext_splash >= COUNT_OF(gSplash)) {
2298
        gNext_splash = 0;
2299
    }
2300
}
2301
 
2302
// IDA: void __usercall CreateSplash(tCar_spec *pCar@<EAX>, tU32 pTime@<EDX>)
2303
void CreateSplash(tCar_spec* pCar, tU32 pTime) {
2304
    br_vector3 normal_car_space;
2305
    br_vector3 pos2;
2306
    br_vector3 v_plane;
2307
    br_vector3 p;
2308
    br_vector3 tv;
2309
    br_vector3 tv2;
2310
    br_vector3 cm;
2311
    int i;
2312
    int j;
2313
    int mask;
2314
    int axis1;
2315
    int axis2;
2316
    int axis3;
2317
    //br_bounds bnds; // Pierre-Marie Baty -- unused variable
2318
    br_scalar min;
2319
    br_scalar max;
2320
    br_scalar d;
2321
    //br_scalar d2; // Pierre-Marie Baty -- unused variable
2322
    br_scalar dist;
2323
    br_scalar dist2;
2324
    br_scalar ts;
2325
    br_vector3 back_point[2];
2326
    br_scalar back_val[2];
2327
    LOG_TRACE("(%p, %d)", pCar, pTime);
2328
 
2329
    back_val[0] = 0.0;
2330
    back_val[1] = 0.0;
2331
#ifdef DETHRACE_FIX_BUGS
2332
    BrVector3Set(&p, 0.f, 0.f, 0.f);
2333
#endif
2334
    if (pCar->v.v[2] * pCar->v.v[2] + pCar->v.v[1] * pCar->v.v[1] + pCar->v.v[0] * pCar->v.v[0] >= 1.0) {
2335
        BrMatrix34TApplyV(&normal_car_space, &pCar->water_normal, &pCar->car_master_actor->t.t.mat);
2336
        BrMatrix34ApplyP(&tv, &pCar->bounds[0].min, &pCar->car_master_actor->t.t.mat);
2337
        min = BrVector3Dot(&pCar->water_normal, &tv) - pCar->water_d;
2338
        max = min;
2339
        for (i = 0; i < 3; ++i) {
2340
            if (normal_car_space.v[i] <= 0.0) {
2341
                max = (pCar->bounds[0].max.v[i] - pCar->bounds[0].min.v[i]) * normal_car_space.v[i] + max;
2342
            } else {
2343
                min = (pCar->bounds[0].max.v[i] - pCar->bounds[0].min.v[i]) * normal_car_space.v[i] + min;
2344
            }
2345
        }
2346
        if (min * max <= 0.0) {
2347
            BrVector3InvScale(&back_point[0], &pCar->bounds[1].min, WORLD_SCALE);
2348
            BrVector3InvScale(&back_point[1], &pCar->bounds[1].max, WORLD_SCALE);
2349
            back_point[0].v[1] = (br_scalar) 0.01; // Pierre-Marie Baty -- added type cast
2350
            ts = BrVector3Dot(&pCar->velocity_car_space, &normal_car_space);
2351
            BrVector3Scale(&tv, &normal_car_space, ts);
2352
            BrVector3Sub(&v_plane, &pCar->velocity_car_space, &tv);
2353
            d = pCar->water_d
2354
                - (pCar->car_master_actor->t.t.mat.m[3][1] * pCar->water_normal.v[1]
2355
                    + pCar->car_master_actor->t.t.mat.m[3][2] * pCar->water_normal.v[2]
2356
                    + pCar->car_master_actor->t.t.mat.m[3][0] * pCar->water_normal.v[0]);
2357
            mask = IRandomBetween(0, 3);
2358
            axis2 = 2;
2359
            for (axis1 = 0; axis1 < 3; ++axis1) {
2360
                axis3 = 3 - axis1 - axis2;
2361
                for (j = 0; j < 4; ++j) {
2362
                    i = j ^ mask;
2363
                    if (((j ^ mask) & 1) != 0) {
2364
                        tv2.v[axis3] = back_point[0].v[axis3];
2365
                    } else {
2366
                        tv2.v[axis3] = back_point[1].v[axis3];
2367
                    }
2368
                    if (((j ^ mask) & 1) != 0) {
2369
                        tv2.v[axis2] = back_point[0].v[axis2];
2370
                    } else {
2371
                        tv2.v[axis2] = back_point[1].v[axis2];
2372
                    }
2373
 
2374
                    ts = d - tv2.v[axis3] * normal_car_space.v[axis3] - tv2.v[axis2] * normal_car_space.v[axis2];
2375
                    ts = ts / normal_car_space.v[axis1];
2376
                    if (ts >= back_point[0].v[axis1] && back_point[1].v[axis1] >= ts) {
2377
                        tv2.v[axis1] = ts;
2378
                        ts = BrVector3Dot(&pCar->velocity_car_space, &tv2);
2379
                        if (ts >= back_val[0]) {
2380
                            if (back_val[1] <= ts) {
2381
                                SingleSplash(pCar, &tv2, &normal_car_space, pTime);
2382
                            } else {
2383
                                if (back_val[1] < 0.0) {
2384
                                    SingleSplash(pCar, &pos2, &normal_car_space, pTime);
2385
                                }
2386
                                back_val[1] = ts;
2387
                                pos2 = tv2;
2388
                            }
2389
                        } else {
2390
                            if (back_val[1] < 0.0) {
2391
                                SingleSplash(pCar, &pos2, &normal_car_space, pTime);
2392
                            }
2393
                            back_val[1] = back_val[0];
2394
                            back_val[0] = ts;
2395
                            pos2 = p;
2396
                            p = tv2;
2397
                        }
2398
                    }
2399
                }
2400
                axis2 = axis1;
2401
            }
2402
            if (back_val[1] >= 0.0) {
2403
                if (back_val[0] < 0.0) {
2404
                    SingleSplash(pCar, &p, &normal_car_space, pTime);
2405
                }
2406
            } else {
2407
                tv.v[0] = pos2.v[0] - p.v[0];
2408
                tv.v[1] = pos2.v[1] - p.v[1];
2409
                tv.v[2] = pos2.v[2] - p.v[2];
2410
                BrVector3Sub(&tv, &pos2, &p);
2411
                ts = SRandomBetween((br_scalar) 0.4, (br_scalar) 0.6); // Pierre-Marie Baty -- added type casts
2412
                BrVector3Scale(&tv2, &tv, ts);
2413
                BrVector3Accumulate(&tv2, &p);
2414
                ts = SRandomBetween((br_scalar) 0.2, (br_scalar) 0.3); // Pierre-Marie Baty -- added type casts
2415
                BrVector3Scale(&cm, &tv, ts);
2416
                BrVector3Accumulate(&p, &cm);
2417
                ts = -SRandomBetween((br_scalar) 0.2, (br_scalar) 0.3); // Pierre-Marie Baty -- added type casts
2418
                BrVector3Scale(&cm, &tv, ts);
2419
                BrVector3Accumulate(&pos2, &cm);
2420
                ts = BrVector3Dot(&pCar->velocity_car_space, &normal_car_space);
2421
                BrVector3Scale(&tv, &normal_car_space, -ts);
2422
                BrVector3Add(&v_plane, &pCar->velocity_car_space, &tv);
2423
                BrVector3Normalise(&tv, &v_plane);
2424
                BrVector3Scale(&tv, &tv, -0.028985508f);
2425
                BrVector3Accumulate(&tv2, &tv);
2426
                BrVector3Scale(&tv, &tv, 0.5f);
2427
                BrVector3Accumulate(&p, &tv);
2428
                BrVector3Accumulate(&pos2, &tv);
2429
                SingleSplash(pCar, &tv2, &normal_car_space, pTime);
2430
                SingleSplash(pCar, &p, &normal_car_space, pTime);
2431
                SingleSplash(pCar, &pos2, &normal_car_space, pTime);
2432
            }
2433
            d = d * WORLD_SCALE;
2434
            dist = d - BrVector3Dot(&pCar->cmpos, &normal_car_space);
2435
            for (i = 0; pCar->extra_point_num > i; ++i) {
2436
                dist2 = d
2437
                    - (pCar->extra_points[i].v[1] * normal_car_space.v[1]
2438
                        + pCar->extra_points[i].v[2] * normal_car_space.v[2]
2439
                        + pCar->extra_points[i].v[0] * normal_car_space.v[0]);
2440
                if ((dist > 0.0) != (dist2 > 0.0)) {
2441
                    ts = dist / (dist - dist2);
2442
                    BrVector3Sub(&tv, &pCar->extra_points[i], &pCar->cmpos);
2443
                    BrVector3Scale(&tv, &tv, ts);
2444
                    BrVector3Accumulate(&tv, &pCar->cmpos);
2445
                    if (pCar->bounds[1].max.v[1] - 0.028985508 > tv.v[1]
2446
                        || pCar->bounds[1].min.v[0] > tv.v[0]
2447
                        || pCar->bounds[1].max.v[0] < tv.v[1]
2448
                        || pCar->bounds[1].min.v[2] > tv.v[2]
2449
                        || pCar->bounds[1].max.v[2] < tv.v[2]) {
2450
                        BrVector3InvScale(&tv, &tv, WORLD_SCALE);
2451
                        SingleSplash(pCar, &tv, &normal_car_space, pTime);
2452
                    }
2453
                }
2454
            }
2455
            for (i = 0; i < 4; ++i) {
2456
                if ((i & 1) != 0) {
2457
                    tv.v[0] = pCar->bounds[1].max.v[0];
2458
                } else {
2459
                    tv.v[0] = pCar->bounds[1].min.v[0];
2460
                }
2461
                tv.v[1] = pCar->bounds[1].max.v[1];
2462
                tv.v[2] = pCar->wpos[i].v[2];
2463
                dist = d - BrVector3Dot(&tv, &normal_car_space);
2464
                dist2 = (pCar->bounds[1].max.v[1] - 0.01) * normal_car_space.v[1] + dist;
2465
                if ((dist > 0.0) != (dist2 > 0.0)) {
2466
                    ts = dist / (dist - dist2);
2467
                    tv.v[1] -= (pCar->bounds[1].max.v[1] - 0.01) * ts;
2468
                    BrVector3InvScale(&tv, &tv, WORLD_SCALE);
2469
                    SingleSplash(pCar, &tv, &normal_car_space, pTime);
2470
                }
2471
            }
2472
        } else {
2473
            min = min + 1.0;
2474
        }
2475
    }
2476
}
2477
 
2478
// IDA: void __usercall MungeSplash(tU32 pTime@<EAX>)
2479
void MungeSplash(tU32 pTime) {
2480
    int i;
2481
    //br_vector3 tv; // Pierre-Marie Baty -- unused variable
2482
    br_scalar dt;
2483
    br_scalar ts;
2484
    tCar_spec* car;
2485
    tVehicle_type type;
2486
    LOG_TRACE("(%d)", pTime);
2487
 
2488
    if (gNum_splash_types == 0) {
2489
        return;
2490
    }
2491
    if (!gAction_replay_mode || GetReplayRate() == 0.0) {
2492
        if (!gAction_replay_mode) {
2493
            for (i = 0; i < gNum_cars_and_non_cars; i++) {
2494
                if (gActive_car_list[i]->water_d != 10000.0 && gActive_car_list[i]->driver != eDriver_local_human) {
2495
                    CreateSplash(gActive_car_list[i], pTime);
2496
                }
2497
            }
2498
            if (gProgram_state.current_car.water_d != 10000.0) {
2499
                CreateSplash(&gProgram_state.current_car, 100);
2500
            }
2501
        }
2502
    } else {
2503
        for (type = eVehicle_net_player; type <= eVehicle_rozzer; type++) {
2504
            for (i = 0;; i++) {
2505
                if (i >= type ? GetCarCount(type) : 1) {
2506
                    break;
2507
                }
2508
                if (type) {
2509
                    car = GetCarSpec(type, i);
2510
                } else {
2511
                    car = &gProgram_state.current_car;
2512
                }
2513
                if (car->water_d != 10000.0 && car->driver != eDriver_local_human) {
2514
                    CreateSplash(car, pTime);
2515
                }
2516
            }
2517
        }
2518
        if (gProgram_state.current_car.water_d != 10000.0) {
2519
            CreateSplash(&gProgram_state.current_car, 0x64u);
2520
        }
2521
    }
2522
    if (!gSplash_flags) {
2523
        return;
2524
    }
2525
    for (i = 0; i < COUNT_OF(gSplash); i++) {
2526
        if (((1u << i) & gSplash_flags) == 0) {
2527
            continue;
2528
        }
2529
        if (gSplash[i].just_done || (gAction_replay_mode && GetReplayRate() == 0.0f)) {
2530
            dt = gSplash[i].size * gSplash[i].scale_x;
2531
            gSplash[i].actor->t.t.mat.m[0][0] = gCamera_to_world.m[0][0] * dt;
2532
            gSplash[i].actor->t.t.mat.m[0][1] = gCamera_to_world.m[0][1] * dt;
2533
            gSplash[i].actor->t.t.mat.m[0][2] = gCamera_to_world.m[0][2] * dt;
2534
            gSplash[i].actor->t.t.mat.m[1][0] = gSplash[i].size * gCamera_to_world.m[1][0];
2535
            gSplash[i].actor->t.t.mat.m[1][1] = gSplash[i].size * gCamera_to_world.m[1][1];
2536
            gSplash[i].actor->t.t.mat.m[1][2] = gSplash[i].size * gCamera_to_world.m[1][2];
2537
            gSplash[i].actor->t.t.mat.m[2][0] = gSplash[i].size * gCamera_to_world.m[2][0];
2538
            gSplash[i].actor->t.t.mat.m[2][1] = gSplash[i].size * gCamera_to_world.m[2][1];
2539
            gSplash[i].actor->t.t.mat.m[2][2] = gSplash[i].size * gCamera_to_world.m[2][2];
2540
            if (gProgram_state.cockpit_on) {
2541
                ts = sqrt(gCamera_to_world.m[0][2] * gCamera_to_world.m[0][2] + gCamera_to_world.m[0][0] * gCamera_to_world.m[0][0]);
2542
                DRMatrix34PreRotateZ(&gSplash[i].actor->t.t.mat, -FastScalarArcTan2Angle(gCamera_to_world.m[0][1], ts));
2543
            }
2544
            gSplash[i].just_done = 0;
2545
        } else {
2546
            gSplash_flags &= ~(1u << i);
2547
            BrActorRemove(gSplash[i].actor);
2548
        }
2549
    }
2550
}
2551
 
2552
// IDA: void __cdecl RenderSplashes()
2553
void RenderSplashes(void) {
2554
    int i;
2555
    LOG_TRACE("()");
2556
 
2557
    for (i = 0; i < COUNT_OF(gSplash); i++) {
2558
        if (gSplash_flags & (1u << i)) {
2559
            BrActorRelink(gNon_track_actor, gSplash[i].actor);
2560
            BrZbSceneRenderAdd(gSplash[i].actor);
2561
            BrActorRelink(gDont_render_actor, gSplash[i].actor);
2562
        }
2563
    }
2564
}
2565
 
2566
// IDA: void __usercall GetSmokeShadeTables(FILE *f@<EAX>)
2567
void GetSmokeShadeTables(FILE* f) {
2568
    int i;
2569
    int red;
2570
    int green;
2571
    int blue;
2572
    br_scalar quarter;
2573
    br_scalar half;
2574
    br_scalar three_quarter;
2575
    LOG_TRACE("(%p)", f);
2576
 
2577
    gNum_dust_tables = GetAnInt(f);
2578
    if (gNum_dust_tables > 8) {
2579
        gNum_dust_tables = 8;
2580
    }
2581
    for (i = 0; gNum_dust_tables > i; ++i) {
2582
        PossibleService();
2583
        GetThreeInts(f, &red, &green, &blue);
2584
        GetThreeScalars(f, &quarter, &half, &three_quarter);
2585
        gDust_table[i] = GenerateShadeTable(16, gRender_palette, red, green, blue, quarter, half, three_quarter);
2586
    }
2587
}
2588
 
2589
// IDA: void __cdecl FreeSmokeShadeTables()
2590
void FreeSmokeShadeTables(void) {
2591
    int i;
2592
    LOG_TRACE("()");
2593
 
2594
    for (i = 0; i < gNum_dust_tables; i++) {
2595
        PossibleService();
2596
        BrTableRemove(gDust_table[i]);
2597
        BrPixelmapFree(gDust_table[i]);
2598
    }
2599
}
2600
 
2601
// IDA: void __usercall LoadInKevStuff(FILE *pF@<EAX>)
2602
void LoadInKevStuff(FILE* pF) {
2603
    LOG_TRACE("(%p)", pF);
2604
 
2605
    PossibleService();
2606
    LoadInShrapnel();
2607
    PossibleService();
2608
    InitShrapnel();
2609
    PossibleService();
2610
    InitFlame();
2611
    PossibleService();
2612
    InitSplash(pF);
2613
}
2614
 
2615
// IDA: void __cdecl DisposeKevStuff()
2616
void DisposeKevStuff(void) {
2617
    LOG_TRACE("()");
2618
 
2619
    DisposeShrapnel();
2620
    DisposeFlame();
2621
    DisposeSplash();
2622
}
2623
 
2624
// IDA: void __usercall DisposeKevStuffCar(tCar_spec *pCar@<EAX>)
2625
void DisposeKevStuffCar(tCar_spec* pCar) {
2626
    int i;
2627
    LOG_TRACE("(%p)", pCar);
2628
 
2629
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2630
        if (gSmoke_column[i].car == pCar) {
2631
            gSmoke_column[i].lifetime = 0;
2632
            gSmoke_column[i].car = NULL;
2633
        }
2634
    }
2635
    for (i = 0; i < COUNT_OF(gSparks); i++) {
2636
        if ((gSpark_flags & (1u << i)) && gSparks[i].car == pCar) {
2637
            gSparks[i].count = 0;
2638
            gSpark_flags &= ~(1u << i);
2639
        }
2640
        if (gCar_to_view == pCar) {
2641
            gCamera_yaw = 0;
2642
            gCar_to_view = &gProgram_state.current_car;
2643
            InitialiseExternalCamera();
2644
            PositionExternalCamera(gCar_to_view, 200);
2645
            gCar_to_view = &gProgram_state.current_car;
2646
        }
2647
    }
2648
}
2649
 
2650
// IDA: void __cdecl DoTrueColModelThing(br_actor *actor, br_model *pModel, br_material *material, void *render_data, br_uint_8 style, int on_screen)
2651
void DoTrueColModelThing(br_actor* actor, br_model* pModel, br_material* material, void* render_data, br_uint_8 style, int on_screen) {
18 pmbaty 2652
    int group;
2653
    int j;
2654
    int val;
1 pmbaty 2655
    LOG_TRACE("(%p, %p, %p, %p, %d, %d)", actor, pModel, material, render_data, style, on_screen);
2656
    NOT_IMPLEMENTED();
2657
}
2658
 
2659
// IDA: void __cdecl DoModelThing(br_actor *actor, br_model *pModel, br_material *material, void *render_data, br_uint_8 style, int on_screen)
2660
void DoModelThing(br_actor* actor, br_model* pModel, br_material* material, void* render_data, br_uint_8 style, int on_screen) {
18 pmbaty 2661
    int j;
2662
    int i;
2663
    int group;
2664
    tU32 t;
2665
    int val;
1 pmbaty 2666
    LOG_TRACE("(%p, %p, %p, %p, %d, %d)", actor, pModel, material, render_data, style, on_screen);
18 pmbaty 2667
 
2668
    GetRaceTime();
2669
    for (group = 0; group < V11MODEL(pModel)->ngroups; group++) {
2670
        for (j = 0; j < V11MODEL(pModel)->groups[group].nvertices; j++) {
2671
            if (!(((V11MODEL(pModel)->groups[group].vertex_colours[j]) >> 24) & 1)) {
2672
                if (((V11MODEL(pModel)->groups[group].vertex_colours[j]) >> 24) < 0xc9) {
2673
                    val = ((V11MODEL(pModel)->groups[group].vertex_colours[j]) >> 24) + 2 * IRandomBetween(5, 10);
2674
                    V11MODEL(pModel)->groups[group].vertex_colours[j] = BR_COLOUR_RGBA(0, 0, 0, val);
2675
                    if (pModel->flags & BR_MODF_UPDATEABLE) {
2676
                        pModel->vertices[V11MODEL(pModel)->groups[group].vertex_user[j]].index = val;
2677
                    }
2678
                } else {
2679
                    V11MODEL(pModel)->groups[group].vertex_colours[j] = BR_COLOUR_RGBA(0, 0, 0, 0xc9);
2680
                    if (pModel->flags & BR_MODF_UPDATEABLE) {
2681
                        pModel->vertices[V11MODEL(pModel)->groups[group].vertex_user[j]].index = 0xc9;
2682
                    }
2683
                }
2684
            } else if ((V11MODEL(pModel)->groups[group].vertex_colours[j] >> 24) < 20) {
2685
                V11MODEL(pModel)->groups[group].vertex_colours[j] = 0;
2686
                if (pModel->flags & BR_MODF_UPDATEABLE) {
2687
                    pModel->vertices[V11MODEL(pModel)->groups[group].vertex_user[j]].index = 0;
2688
                }
2689
            } else {
2690
                val = ((V11MODEL(pModel)->groups[group].vertex_colours[j] >> 24) - 2 * IRandomBetween(5, 10)) << 24;
2691
                V11MODEL(pModel)->groups[group].vertex_colours[j] = BR_COLOUR_RGBA(0, 0, 0, val);
2692
                if (pModel->flags & BR_MODF_UPDATEABLE) {
2693
                    pModel->vertices[V11MODEL(pModel)->groups[group].vertex_user[j]].index = val;
2694
                }
2695
            }
2696
        }
2697
    }
2698
    if ((pModel->flags & BR_MODF_UPDATEABLE) && pModel->user != NULL) {
2699
        BrModelUpdate(pModel, BR_MODU_VERTEX_POSITIONS);
2700
    }
2701
    pModel->user = NULL;
2702
    BrZbModelRender(actor, pModel, material, style, BrOnScreenCheck(&pModel->bounds), 0);
1 pmbaty 2703
}
2704
 
2705
// IDA: void __usercall SetModelShade(br_actor *pActor@<EAX>, br_pixelmap *pShade@<EDX>)
2706
void SetModelShade(br_actor* pActor, br_pixelmap* pShade) {
2707
    int i;
2708
    br_material* material;
2709
    br_model* model;
2710
    LOG_TRACE("(%p, %p)", pActor, pShade);
2711
 
2712
    model = pActor->model;
2713
    if (pActor->material != NULL && pActor->material->index_shade != pShade) {
2714
        pActor->material->index_shade = pShade;
2715
        BrMaterialUpdate(pActor->material, BR_MATU_ALL);
2716
    }
2717
    for (i = 0; i < model->nfaces; i++) {
2718
        material = model->faces[i].material;
2719
        if (material != NULL && material->index_shade != pShade) {
2720
            material->index_shade = pShade;
2721
            BrMaterialUpdate(material, BR_MATU_ALL);
2722
        }
2723
    }
2724
}
2725
 
2726
// IDA: void __usercall MakeCarIt(tCar_spec *pCar@<EAX>)
2727
void MakeCarIt(tCar_spec* pCar) {
2728
    br_actor* actor;
2729
    br_actor* bonny;
2730
    br_pixelmap* shade[6];
2731
    static int shade_num = 0;
2732
    //int i; // Pierre-Marie Baty -- unused variable
2733
    LOG_TRACE("(%p)", pCar);
2734
 
2735
    shade[0] = gIt_shade_table;
2736
    shade[1] = gFog_shade_table;
2737
    shade[2] = gShade_list[0];
2738
    shade[3] = gShade_list[1];
2739
    shade[4] = gShade_list[2];
2740
    shade[5] = NULL;
2741
 
2742
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
2743
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
18 pmbaty 2744
    if (!(actor->model->flags & BR_MODF_CUSTOM) || actor->model->custom != DoModelThing) {
1 pmbaty 2745
        SetModelShade(actor, shade[shade_num]);
18 pmbaty 2746
        actor->model->user = DoModelThing;
2747
        actor->model->custom = DoModelThing;
1 pmbaty 2748
        actor->model->flags |= BR_MODF_CUSTOM;
2749
        if (bonny != actor) {
18 pmbaty 2750
            bonny->model->user = DoModelThing;
2751
            bonny->model->custom = DoModelThing;
1 pmbaty 2752
            bonny->model->flags |= BR_MODF_CUSTOM;
2753
            SetModelShade(bonny, shade[shade_num]);
2754
        }
2755
    }
2756
}
2757
 
2758
// IDA: void __usercall StopCarBeingIt(tCar_spec *pCar@<EAX>)
2759
void StopCarBeingIt(tCar_spec* pCar) {
18 pmbaty 2760
    int i;
2761
    int group;
2762
    br_actor* actor;
2763
    br_actor* bonny;
1 pmbaty 2764
    LOG_TRACE("(%p)", pCar);
18 pmbaty 2765
 
2766
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
2767
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
2768
    if (actor->model->custom == DoModelThing) {
2769
        actor->model->flags &= ~BR_MODF_CUSTOM;
2770
        SetModelShade(actor, gShade_list[0]);
2771
        for (group = 0; group < V11MODEL(actor->model)->ngroups; group++) {
2772
            for (i = 0; i < V11MODEL(actor->model)->groups[group].nvertices; i++) {
2773
                V11MODEL(actor->model)->groups[group].vertex_colours[i] = 0;
2774
                if (actor->model->flags & BR_MODF_UPDATEABLE) {
2775
                    actor->model->vertices[V11MODEL(actor->model)->groups[group].vertex_user[i]].index = 0;
2776
                }
2777
            }
2778
        }
2779
        SetModelForUpdate(actor->model, pCar, 0);
2780
        if (bonny != actor) {
2781
            bonny->model->flags &= ~BR_MODF_CUSTOM;
2782
            SetModelShade(bonny, gShade_list[0]);
2783
            for (group = 0; group < V11MODEL(bonny->model)->ngroups; group++) {
2784
                for (i = 0; i < V11MODEL(bonny->model)->groups[group].nvertices; i++) {
2785
                    V11MODEL(bonny->model)->groups[group].vertex_colours[i] = 0;
2786
                    if (bonny->model->flags & BR_MODF_UPDATEABLE) {
2787
                        bonny->model->vertices[V11MODEL(bonny->model)->groups[group].vertex_user[i]].index = 0;
2788
                    }
2789
                }
2790
            }
2791
            SetModelForUpdate(bonny->model, pCar, 0);
2792
        }
2793
    }
1 pmbaty 2794
}