Subversion Repositories Games.Carmageddon

Rev

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

  1. #include "spark.h"
  2. #include "brender.h"
  3. #include "car.h"
  4. #include "crush.h"
  5. #include "depth.h"
  6. #include "displays.h"
  7. #include "errors.h"
  8. #include "formats.h"
  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"
  21. #include <math.h>
  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("()");
  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");
  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.  
  1629.     BrVector3Add(pRet_car_pos, &V11MODEL(actor->model)->groups[0].position[gSmoke_column[i].vertex_index], &actor->t.t.translate.t);
  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 {
  1634.             BrMatrix34ApplyP(pRet_car_pos, &V11MODEL(actor->model)->groups[0].position[gSmoke_column[i].vertex_index], &bonny->t.t.mat);
  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) {
  2071.         point = V11MODEL(model)->groups[group].position[fire_point];
  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++) {
  2075.                 BrVector3Sub(&tv, &V11MODEL(model)->groups[group].position[j], &point);
  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);
  2103.             // FIXME?
  2104.             // Added by dethrace to update gpu-buffered vertices
  2105.             // model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
  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++) {
  2118.                     BrVector3Sub(&tv, &V11MODEL(b_model)->groups[group].position[j], &bonny_pos);
  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);
  2146.                 // FIXME?
  2147.                 // Added by dethrace to update gpu-buffered vertices
  2148.                 // b_model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
  2149.             }
  2150.         }
  2151.         EndPipingSession();
  2152.     }
  2153. }
  2154.  
  2155. // IDA: void __cdecl ResetSmokeColumns()
  2156. void ResetSmokeColumns(void) {
  2157.     int i;
  2158.     LOG_TRACE("()");
  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;
  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);
  2178.  
  2179.     ResetSmoke();
  2180.     ResetSmokeColumns();
  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) {
  2652.     int group;
  2653.     int j;
  2654.     int val;
  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) {
  2661.     int j;
  2662.     int i;
  2663.     int group;
  2664.     tU32 t;
  2665.     int val;
  2666.     LOG_TRACE("(%p, %p, %p, %p, %d, %d)", actor, pModel, material, render_data, style, on_screen);
  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);
  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;
  2744.     if (!(actor->model->flags & BR_MODF_CUSTOM) || actor->model->custom != DoModelThing) {
  2745.         SetModelShade(actor, shade[shade_num]);
  2746.         actor->model->user = DoModelThing;
  2747.         actor->model->custom = DoModelThing;
  2748.         actor->model->flags |= BR_MODF_CUSTOM;
  2749.         if (bonny != actor) {
  2750.             bonny->model->user = DoModelThing;
  2751.             bonny->model->custom = DoModelThing;
  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) {
  2760.     int i;
  2761.     int group;
  2762.     br_actor* actor;
  2763.     br_actor* bonny;
  2764.     LOG_TRACE("(%p)", pCar);
  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.     }
  2794. }
  2795.