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 "oil.h"
  2. #include "brender.h"
  3. #include "finteray.h"
  4. #include "globvars.h"
  5. #include "globvrpb.h"
  6. #include "harness/trace.h"
  7. #include "loading.h"
  8. #include "network.h"
  9. #include "piping.h"
  10. #include "utility.h"
  11. #include <math.h>
  12. #include <stdlib.h>
  13.  
  14. char* gOil_pixie_names[1] = { "OIL.PIX" };
  15. int gNext_oil_pixie = 0;
  16. br_scalar gZ_buffer_diff;
  17. br_scalar gMin_z_diff;
  18. br_pixelmap* gOil_pixies[1];
  19. tOil_spill_info gOily_spills[15];
  20.  
  21. // IDA: void __cdecl InitOilSpills()
  22. void InitOilSpills(void) {
  23.     int i;
  24.     br_model* the_model;
  25.     br_material* the_material;
  26.     LOG_TRACE("()");
  27.  
  28.     for (i = 0; i < COUNT_OF(gOil_pixie_names); i++) {
  29.         gOil_pixies[i] = LoadPixelmap(gOil_pixie_names[i]);
  30.         BrMapAdd(gOil_pixies[i]);
  31.     }
  32.  
  33.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  34.         the_material = BrMaterialAllocate(NULL);
  35.         BrMaterialAdd(the_material);
  36.         the_material->ka = 0.99f;
  37.         the_material->kd = 0.0f;
  38.         the_material->ks = 0.0f;
  39.         the_material->power = 0.0f;
  40.         the_material->index_base = 0;
  41.         the_material->flags |= BR_MATF_LIGHT;
  42.         the_material->flags |= BR_MATF_PERSPECTIVE;
  43.         the_material->flags |= BR_MATF_SMOOTH;
  44.         the_material->index_range = 0;
  45.         the_material->colour_map = NULL;
  46.         BrMatrix23Identity(&the_material->map_transform);
  47.         the_material->index_shade = BrTableFind("IDENTITY.TAB");
  48.         BrMaterialUpdate(the_material, BR_MATU_ALL);
  49.         the_model = BrModelAllocate(NULL, 4, 2);
  50.         the_model->flags |= BR_MODF_KEEP_ORIGINAL;
  51.  
  52.         the_model->faces[0].vertices[0] = 2;
  53.         the_model->faces[0].vertices[1] = 1;
  54.         the_model->faces[0].vertices[2] = 0;
  55.         the_model->faces[0].material = NULL;
  56.         the_model->faces[0].smoothing = 1;
  57.         the_model->faces[1].vertices[0] = 3;
  58.         the_model->faces[1].vertices[1] = 2;
  59.         the_model->faces[1].vertices[2] = 0;
  60.         the_model->faces[1].material = NULL;
  61.         the_model->faces[1].smoothing = 1;
  62.         BrVector3Set(&the_model->vertices[0].p, -1.f, 0.f, -1.f);
  63.         BrVector2Set(&the_model->vertices[0].map, 0.f, 1.f);
  64.         BrVector3Set(&the_model->vertices[1].p, 1.f, 0.f, 1.f);
  65.         BrVector2Set(&the_model->vertices[1].map, 0.f, 0.f);
  66.         BrVector3Set(&the_model->vertices[2].p, 1.f, 0.f, -1.f);
  67.         BrVector2Set(&the_model->vertices[2].map, 1.f, 0.f);
  68.         BrVector3Set(&the_model->vertices[3].p, -1.f, 0.f, 1.f);
  69.         BrVector2Set(&the_model->vertices[3].map, 1.f, 1.f);
  70.         gOily_spills[i].actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
  71.         gOily_spills[i].actor->model = the_model;
  72.         gOily_spills[i].actor->render_style = BR_RSTYLE_NONE;
  73.         gOily_spills[i].actor->material = the_material;
  74.         BrActorAdd(gNon_track_actor, gOily_spills[i].actor);
  75.     }
  76. }
  77.  
  78. // IDA: void __cdecl ResetOilSpills()
  79. void ResetOilSpills(void) {
  80.     int i;
  81.     LOG_TRACE("()");
  82.  
  83.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  84.         gOily_spills[i].actor->render_style = BR_RSTYLE_NONE;
  85.         gOily_spills[i].car = NULL;
  86.         gOily_spills[i].stop_time = 0;
  87.     }
  88. }
  89.  
  90. // IDA: void __usercall QueueOilSpill(tCar_spec *pCar@<EAX>)
  91. void QueueOilSpill(tCar_spec* pCar) {
  92.     int i;
  93.     int oily_index;
  94.     int oldest_one;
  95.     tU32 the_time;
  96.     tU32 oldest_time;
  97.     LOG_TRACE("(%p)", pCar);
  98.  
  99.     oldest_one = 0;
  100.     oily_index = -1;
  101.     the_time = GetTotalTime();
  102.     oldest_time = GetTotalTime();
  103.  
  104.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  105.         if (gOily_spills[i].car == pCar && the_time < gOily_spills[i].spill_time + 5000) {
  106.             return;
  107.         }
  108.     }
  109.  
  110.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  111.         if (gOily_spills[i].car == NULL) {
  112.             oily_index = i;
  113.             break;
  114.         }
  115.         if (gOily_spills[i].spill_time < oldest_time) {
  116.             oldest_time = gOily_spills[i].spill_time;
  117.             oldest_one = i;
  118.         }
  119.     }
  120.  
  121.     if (oily_index < 0) {
  122.         oily_index = oldest_one;
  123.     }
  124.     gOily_spills[oily_index].car = pCar;
  125.     gOily_spills[oily_index].spill_time = the_time + 500;
  126.     gOily_spills[oily_index].full_size = SRandomBetween(.35f, .6f);
  127.     gOily_spills[oily_index].grow_rate = SRandomBetween(3e-5f, 10e-5f);
  128.     gOily_spills[oily_index].current_size = .1f;
  129.     gOily_spills[oily_index].actor->render_style = BR_RSTYLE_NONE;
  130. }
  131.  
  132. // IDA: int __usercall OKToSpillOil@<EAX>(tOil_spill_info *pOil@<EAX>)
  133. int OKToSpillOil(tOil_spill_info* pOil) {
  134.     //br_scalar temp; // Pierre-Marie Baty -- unused variable
  135.     //br_scalar size_with_margin; // Pierre-Marie Baty -- unused variable
  136.     br_scalar distance;
  137.     br_scalar mr_dotty;
  138.     br_vector3 v;
  139.     br_vector3 ray_pos;
  140.     br_vector3 ray_dir;
  141.     br_vector3 normal;
  142.     tCar_spec* car;
  143.     int i;
  144.     int face_count;
  145.     int found_one;
  146.     br_angle angle_to_rotate_by;
  147.     tBounds kev_bounds;
  148.     tFace_ref the_list[10];
  149.     tFace_ref* face_ref;
  150.     LOG_TRACE("(%p)", pOil);
  151.  
  152.     car = pOil->car;
  153.     if (car->driver >= eDriver_net_human && car->damage_units[eDamage_engine].damage_level <= 98 && car->damage_units[eDamage_transmission].damage_level <= 98) {
  154.         return 0;
  155.     }
  156.     angle_to_rotate_by = IRandomBetween(0, 0xffff);
  157.     kev_bounds.original_bounds.min.v[0] = -pOil->full_size;
  158.     kev_bounds.original_bounds.min.v[1] = 1.5f * car->car_model_actors[car->principal_car_actor].actor->model->bounds.min.v[1];
  159.     kev_bounds.original_bounds.min.v[2] = -pOil->full_size;
  160.     kev_bounds.original_bounds.max.v[0] = pOil->full_size;
  161.     kev_bounds.original_bounds.max.v[1] = car->car_model_actors[car->principal_car_actor].actor->model->bounds.max.v[1];
  162.     kev_bounds.original_bounds.max.v[2] = pOil->full_size;
  163.     BrMatrix34PreRotateY(&pOil->actor->t.t.mat, angle_to_rotate_by);
  164.     kev_bounds.mat = &car->car_master_actor->t.t.mat;
  165.     face_count = FindFacesInBox(&kev_bounds, the_list, COUNT_OF(the_list));
  166.     BrVector3Set(&v, .0f, .2f, .0f);
  167.     BrMatrix34ApplyP(&ray_pos, &v, &car->car_master_actor->t.t.mat);
  168.     BrVector3Set(&ray_dir, 0.f, kev_bounds.original_bounds.min.v[1] - kev_bounds.original_bounds.max.v[1], 0.f);
  169.     if (face_count == 0) {
  170.         return 0;
  171.     }
  172.     found_one = 0;
  173.     for (i = 0; i < face_count; i++) {
  174.         face_ref = &the_list[i];
  175.         if (!found_one) {
  176.             CheckSingleFace(face_ref, &ray_pos, &ray_dir, &normal, &distance);
  177.             if (distance < 100.f) {
  178.                 found_one = 1;
  179.                 BrVector3Copy((br_vector3*)pOil->actor->t.t.mat.m[1], &normal);
  180.                 BrVector3Set(&v, 0.f, 0.f, 1.f);
  181.                 BrVector3Cross((br_vector3*)pOil->actor->t.t.mat.m[0], &normal, &v);
  182.                 BrVector3Set(&v, 1.f, 0.f, 0.f);
  183.                 BrVector3Cross((br_vector3*)pOil->actor->t.t.mat.m[2], &normal, &v);
  184.                 BrVector3Scale(&v, &ray_dir, distance);
  185.                 BrVector3Add(&pOil->pos, &ray_pos, &v);
  186.                 BrMatrix34PreRotateY(&pOil->actor->t.t.mat, angle_to_rotate_by);
  187.             }
  188.         }
  189.     }
  190.     if (!found_one || normal.v[1] < .97f) {
  191.         return 0;
  192.     }
  193.     for (i = 0; i < face_count; i++) {
  194.         face_ref = &the_list[i];
  195.         mr_dotty = BrVector3Dot(&face_ref->normal, &normal);
  196.         if (mr_dotty < .98f && (mr_dotty > .8f || !NormalSideOfPlane(&pOil->actor->t.t.translate.t, &face_ref->normal, face_ref->d))) {
  197.             return 0;
  198.         }
  199.     }
  200.     return 1;
  201. }
  202.  
  203. // IDA: void __usercall Vector3Interpolate(br_vector3 *pDst@<EAX>, br_vector3 *pFrom@<EDX>, br_vector3 *pTo@<EBX>, br_scalar pP)
  204. void Vector3Interpolate(br_vector3* pDst, br_vector3* pFrom, br_vector3* pTo, br_scalar pP) {
  205.     LOG_TRACE("(%p, %p, %p, %f)", pDst, pFrom, pTo, pP);
  206.  
  207.     pDst->v[0] = (pTo->v[0] - pFrom->v[0]) * pP + pFrom->v[0];
  208.     pDst->v[1] = (pTo->v[1] - pFrom->v[1]) * pP + pFrom->v[1];
  209.     pDst->v[2] = (pTo->v[2] - pFrom->v[2]) * pP + pFrom->v[2];
  210. }
  211.  
  212. // IDA: void __usercall EnsureGroundDetailVisible(br_vector3 *pNew_pos@<EAX>, br_vector3 *pGround_normal@<EDX>, br_vector3 *pOld_pos@<EBX>)
  213. void EnsureGroundDetailVisible(br_vector3* pNew_pos, br_vector3* pGround_normal, br_vector3* pOld_pos) {
  214.     br_scalar factor;
  215.     br_scalar s;
  216.     br_scalar dist;
  217.     br_vector3 to_camera;
  218.     LOG_TRACE("(%p, %p, %p)", pNew_pos, pGround_normal, pOld_pos);
  219.  
  220.     to_camera.v[0] = gCamera_to_world.m[3][0] - pOld_pos->v[0];
  221.     to_camera.v[1] = gCamera_to_world.m[3][1] - pOld_pos->v[1];
  222.     to_camera.v[2] = gCamera_to_world.m[3][2] - pOld_pos->v[2];
  223.     dist = BrVector3Length(&to_camera);
  224.     if (dist > BR_SCALAR_EPSILON) {
  225.         factor = BrVector3Dot(pGround_normal, &to_camera) / dist;
  226.         if (fabsf(factor) <= 0.01f) {
  227.             s = 0.01f;
  228.         } else {
  229.             s = 0.01f / factor;
  230.             if (s > 0.1f) {
  231.                 s = 0.1f;
  232.             }
  233.         }
  234.         Vector3Interpolate(pNew_pos, pOld_pos, (br_vector3*)gCamera_to_world.m[3], s);
  235.     }
  236. }
  237.  
  238. // IDA: void __usercall MungeOilsHeightAboveGround(tOil_spill_info *pOil@<EAX>)
  239. void MungeOilsHeightAboveGround(tOil_spill_info* pOil) {
  240.     LOG_TRACE("(%p)", pOil);
  241.  
  242.     EnsureGroundDetailVisible(&pOil->actor->t.t.look_up.t, &pOil->actor->t.t.look_up.up, &pOil->pos);
  243. }
  244.  
  245. // IDA: void __usercall MungeIndexedOilsHeightAboveGround(int pIndex@<EAX>)
  246. void MungeIndexedOilsHeightAboveGround(int pIndex) {
  247.     LOG_TRACE("(%d)", pIndex);
  248.  
  249.     MungeOilsHeightAboveGround(&gOily_spills[pIndex]);
  250. }
  251.  
  252. // IDA: void __usercall SetInitialOilStuff(tOil_spill_info *pOil@<EAX>, br_model *pModel@<EDX>)
  253. void SetInitialOilStuff(tOil_spill_info* pOil, br_model* pModel) {
  254.     LOG_TRACE("(%p, %p)", pOil, pModel);
  255.  
  256.     pModel->vertices[0].p.v[0] = -0.1f;
  257.     pModel->vertices[0].p.v[2] = -0.1f;
  258.     pModel->vertices[1].p.v[0] = 0.1f;
  259.     pModel->vertices[1].p.v[2] = -0.1f;
  260.     pModel->vertices[2].p.v[0] = 0.1f;
  261.     pModel->vertices[2].p.v[2] = 0.1f;
  262.     pModel->vertices[3].p.v[0] = -0.1f;
  263.     pModel->vertices[3].p.v[2] = 0.1f;
  264.     pOil->actor->render_style = BR_RSTYLE_FACES;
  265.     BrMaterialUpdate(pOil->actor->material, BR_MATU_ALL);
  266.     BrModelUpdate(pModel, BR_MODU_ALL);
  267. }
  268.  
  269. // IDA: void __usercall ProcessOilSpills(tU32 pFrame_period@<EAX>)
  270. void ProcessOilSpills(tU32 pFrame_period) {
  271.     int i;
  272.     tU32 time;
  273.     br_model* the_model;
  274.     br_scalar grow_amount;
  275.     //br_scalar initial_size; // Pierre-Marie Baty -- unused variable
  276.     br_scalar this_size;
  277.     br_vector3 v;
  278.     tNet_message* message;
  279.     LOG_TRACE("(%d)", pFrame_period);
  280.  
  281.     time = GetTotalTime();
  282.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  283.         if (gOily_spills[i].car == NULL) {
  284.             gOily_spills[i].actor->render_style = BR_RSTYLE_NONE;
  285.         } else {
  286.             the_model = gOily_spills[i].actor->model;
  287.             if (gOily_spills[i].actor->render_style == BR_RSTYLE_NONE && gOily_spills[i].spill_time <= time && fabsf(gOily_spills[i].car->v.v[0]) < .01f && fabsf(gOily_spills[i].car->v.v[1]) < .01f && fabsf(gOily_spills[i].car->v.v[2]) < .01f) {
  288.                 if (gAction_replay_mode) {
  289.                     SetInitialOilStuff(&gOily_spills[i], the_model);
  290.                 } else {
  291.                     if (!OKToSpillOil(&gOily_spills[i])) {
  292.                         gOily_spills[i].car = NULL;
  293.                     } else {
  294.                         gOily_spills[i].spill_time = time;
  295.                         gOily_spills[i].actor->material->colour_map = gOil_pixies[gNext_oil_pixie];
  296.                         gNext_oil_pixie++;
  297.                         if (gNext_oil_pixie >= COUNT_OF(gOil_pixies)) {
  298.                             gNext_oil_pixie = 0;
  299.                         }
  300.                         BrVector3Copy(&gOily_spills[i].original_pos, &gOily_spills[i].car->pos);
  301.                         PipeSingleOilSpill(i,
  302.                             &gOily_spills[i].actor->t.t.mat,
  303.                             gOily_spills[i].full_size,
  304.                             gOily_spills[i].grow_rate,
  305.                             gOily_spills[i].spill_time,
  306.                             gOily_spills[i].stop_time,
  307.                             gOily_spills[i].car,
  308.                             &gOily_spills[i].original_pos,
  309.                             gOily_spills[i].actor->material->colour_map);
  310.                         gOily_spills[i].stop_time = 0;
  311.                         SetInitialOilStuff(&gOily_spills[i], the_model);
  312.                         if (gNet_mode != eNet_mode_none) {
  313.                             message = NetBuildMessage(30, 0);
  314.                             message->contents.data.oil_spill.player = NetPlayerFromCar(gOily_spills[i].car)->ID;
  315.                             message->contents.data.oil_spill.full_size = gOily_spills[i].full_size;
  316.                             message->contents.data.oil_spill.grow_rate = gOily_spills[i].grow_rate;
  317.                             message->contents.data.oil_spill.current_size = gOily_spills[i].current_size;
  318.                             NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, message, NULL);
  319.                         }
  320.                     }
  321.                 }
  322.             } else {
  323.                 if (gOily_spills[i].actor->render_style == BR_RSTYLE_FACES && (gOily_spills[i].stop_time == 0 || time < gOily_spills[i].stop_time)) {
  324.                     BrVector3Sub(&v, &gOily_spills[i].original_pos, &gOily_spills[i].car->pos);
  325.                     grow_amount = BrVector3LengthSquared(&v);
  326.                     if (gOily_spills[i].stop_time != 0 || grow_amount <= 0.2f) {
  327.                         this_size = 0.1f + (time - gOily_spills[i].spill_time) * gOily_spills[i].grow_rate;
  328.                         if (this_size >= 0.1f) {
  329.                             gOily_spills[i].actor->render_style = BR_RSTYLE_FACES;
  330.                             if (this_size <= gOily_spills[i].full_size) {
  331.                                 the_model->vertices[0].p.v[0] = -this_size;
  332.                                 the_model->vertices[0].p.v[2] = -this_size;
  333.                                 the_model->vertices[1].p.v[0] = this_size;
  334.                                 the_model->vertices[1].p.v[2] = -this_size;
  335.                                 the_model->vertices[2].p.v[0] = this_size;
  336.                                 the_model->vertices[2].p.v[2] = this_size;
  337.                                 the_model->vertices[3].p.v[0] = -this_size;
  338.                                 the_model->vertices[3].p.v[2] = this_size;
  339.                                 gOily_spills[i].current_size = this_size;
  340.                             } else {
  341.                                 the_model->vertices[0].p.v[0] = -gOily_spills[i].full_size;
  342.                                 the_model->vertices[0].p.v[2] = -gOily_spills[i].full_size;
  343.                                 the_model->vertices[1].p.v[0] = gOily_spills[i].full_size;
  344.                                 the_model->vertices[1].p.v[2] = -gOily_spills[i].full_size;
  345.                                 the_model->vertices[2].p.v[0] = gOily_spills[i].full_size;
  346.                                 the_model->vertices[2].p.v[2] = gOily_spills[i].full_size;
  347.                                 the_model->vertices[3].p.v[0] = -gOily_spills[i].full_size;
  348.                                 the_model->vertices[3].p.v[2] = gOily_spills[i].full_size;
  349.                                 gOily_spills[i].current_size = gOily_spills[i].full_size;
  350.                             }
  351.                             BrModelUpdate(the_model, BR_MODU_ALL);
  352.                         } else {
  353.                             gOily_spills[i].actor->render_style = BR_RSTYLE_NONE;
  354.                         }
  355.                     } else {
  356.                         gOily_spills[i].stop_time = time;
  357.                         continue;
  358.                     }
  359.                 }
  360.             }
  361.         }
  362.         if (gOily_spills[i].actor->render_style == BR_RSTYLE_FACES) {
  363.             MungeOilsHeightAboveGround(&gOily_spills[i]);
  364.         }
  365.     }
  366. }
  367.  
  368. // IDA: int __cdecl GetOilSpillCount()
  369. int GetOilSpillCount(void) {
  370.     // LOG_TRACE("()");
  371.  
  372.     return COUNT_OF(gOily_spills);
  373. }
  374.  
  375. // IDA: void __usercall GetOilSpillDetails(int pIndex@<EAX>, br_actor **pActor@<EDX>, br_scalar *pSize@<EBX>)
  376. void GetOilSpillDetails(int pIndex, br_actor** pActor, br_scalar* pSize) {
  377.     LOG_TRACE("(%d, %p, %p)", pIndex, pActor, pSize);
  378.  
  379.     if (gOily_spills[pIndex].car != NULL) {
  380.         *pActor = gOily_spills[pIndex].actor;
  381.         *pSize = gOily_spills[pIndex].full_size;
  382.     } else {
  383.         *pActor = NULL;
  384.     }
  385. }
  386.  
  387. #define SQR(V) ((V) * (V))
  388.  
  389. // IDA: int __usercall PointInSpill@<EAX>(br_vector3 *pV@<EAX>, int pSpill@<EDX>)
  390. int PointInSpill(br_vector3* pV, int pSpill) {
  391.     LOG_TRACE("(%p, %d)", pV, pSpill);
  392.  
  393.     return gOily_spills[pSpill].current_size * gOily_spills[pSpill].current_size * 0.8f > SQR(pV->v[0] / WORLD_SCALE - gOily_spills[pSpill].actor->t.t.translate.t.v[0])
  394.         && gOily_spills[pSpill].current_size * gOily_spills[pSpill].current_size * 0.8f > SQR(pV->v[2] / WORLD_SCALE - gOily_spills[pSpill].actor->t.t.translate.t.v[2])
  395.         && fabsf(pV->v[1] / WORLD_SCALE - gOily_spills[pSpill].actor->t.t.translate.t.v[1]) < 0.1f;
  396. }
  397.  
  398. // IDA: void __usercall GetOilFrictionFactors(tCar_spec *pCar@<EAX>, br_scalar *pFl_factor@<EDX>, br_scalar *pFr_factor@<EBX>, br_scalar *pRl_factor@<ECX>, br_scalar *pRr_factor)
  399. void GetOilFrictionFactors(tCar_spec* pCar, br_scalar* pFl_factor, br_scalar* pFr_factor, br_scalar* pRl_factor, br_scalar* pRr_factor) {
  400.     int i;
  401.     br_vector3 wheel_world;
  402.     LOG_TRACE("(%p, %p, %p, %p, %p)", pCar, pFl_factor, pFr_factor, pRl_factor, pRr_factor);
  403.  
  404.     *pFl_factor = 1.0f;
  405.     *pFr_factor = 1.0f;
  406.     *pRl_factor = 1.0f;
  407.     *pRr_factor = 1.0f;
  408.     switch (pCar->driver) {
  409.     case eDriver_non_car_unused_slot:
  410.     case eDriver_non_car:
  411.         return;
  412.     default:
  413.         break;
  414.     }
  415.     if (pCar->shadow_intersection_flags != 0) {
  416.         for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  417.             if (((1 << i) & pCar->shadow_intersection_flags) != 0 && gOily_spills[i].car != NULL) {
  418.                 BrMatrix34ApplyP(&wheel_world, &pCar->wpos[2], &pCar->car_master_actor->t.t.mat);
  419.                 if (PointInSpill(&wheel_world, i)) {
  420.                     pCar->oil_remaining[2] = SRandomBetween(1.5f, 2.5f);
  421.                 }
  422.                 BrMatrix34ApplyP(&wheel_world, &pCar->wpos[3], &pCar->car_master_actor->t.t.mat);
  423.                 if (PointInSpill(&wheel_world, i)) {
  424.                     pCar->oil_remaining[3] = SRandomBetween(1.5f, 2.5f);
  425.                 }
  426.                 BrMatrix34ApplyP(&wheel_world, &pCar->wpos[0], &pCar->car_master_actor->t.t.mat);
  427.                 if (PointInSpill(&wheel_world, i)) {
  428.                     pCar->oil_remaining[0] = SRandomBetween(1.5f, 2.5f);
  429.                 }
  430.                 BrMatrix34ApplyP(&wheel_world, &pCar->wpos[1], &pCar->car_master_actor->t.t.mat);
  431.                 if (PointInSpill(&wheel_world, i)) {
  432.                     pCar->oil_remaining[1] = SRandomBetween(1.5f, 2.5f);
  433.                 }
  434.             }
  435.         }
  436.     }
  437.     if (pCar->oil_remaining[2] != 0.0f) {
  438.         *pFl_factor = SRandomBetween(0.01f, 0.15f);
  439.     }
  440.     if (pCar->oil_remaining[3] != 0.0f) {
  441.         *pFr_factor = SRandomBetween(0.01f, 0.15f);
  442.     }
  443.     if (pCar->oil_remaining[0] != 0.0f) {
  444.         *pRl_factor = SRandomBetween(0.01f, 0.15f);
  445.     }
  446.     if (pCar->oil_remaining[1] != 0.0f) {
  447.         *pRr_factor = SRandomBetween(0.01f, 0.15f);
  448.     }
  449. }
  450.  
  451. // IDA: void __usercall AdjustOilSpill(int pIndex@<EAX>, br_matrix34 *pMat@<EDX>, br_scalar pFull_size, br_scalar pGrow_rate, tU32 pSpill_time, tU32 pStop_time, tCar_spec *pCar, br_vector3 *pOriginal_pos, br_pixelmap *pPixelmap)
  452. void AdjustOilSpill(int pIndex, br_matrix34* pMat, br_scalar pFull_size, br_scalar pGrow_rate, tU32 pSpill_time, tU32 pStop_time, tCar_spec* pCar, br_vector3* pOriginal_pos, br_pixelmap* pPixelmap) {
  453.     LOG_TRACE("(%d, %p, %f, %f, %d, %d, %p, %p, %p)", pIndex, pMat, pFull_size, pGrow_rate, pSpill_time, pStop_time, pCar, pOriginal_pos, pPixelmap);
  454.  
  455.     BrMatrix34Copy(&gOily_spills[pIndex].actor->t.t.mat, pMat);
  456.     gOily_spills[pIndex].full_size = pFull_size;
  457.     gOily_spills[pIndex].grow_rate = pGrow_rate;
  458.     gOily_spills[pIndex].spill_time = pSpill_time;
  459.     gOily_spills[pIndex].stop_time = pStop_time;
  460.     gOily_spills[pIndex].car = pCar;
  461.     BrVector3Copy(&gOily_spills[pIndex].original_pos, pOriginal_pos);
  462.     gOily_spills[pIndex].actor->material->colour_map = pPixelmap;
  463.     gOily_spills[pIndex].actor->render_style = BR_RSTYLE_NONE;
  464. }
  465.  
  466. // IDA: void __usercall ReceivedOilSpill(tNet_contents *pContents@<EAX>)
  467. void ReceivedOilSpill(tNet_contents* pContents) {
  468.     int i;
  469.     int oily_index;
  470.     int oldest_one;
  471.     tU32 the_time;
  472.     tU32 oldest_time;
  473.     tCar_spec* car;
  474.     LOG_TRACE("(%p)", pContents);
  475.  
  476.     oldest_one = 0;
  477.     car = NetCarFromPlayerID(pContents->data.oil_spill.player);
  478.     if (car == NULL) {
  479.         return;
  480.     }
  481.     oily_index = -1;
  482.     the_time = GetTotalTime();
  483.     oldest_time = GetTotalTime();
  484.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  485.         if (gOily_spills[i].car == car && the_time < gOily_spills[i].spill_time + 5000) {
  486.             return;
  487.         }
  488.     }
  489.     for (i = 0; i < COUNT_OF(gOily_spills); i++) {
  490.         if (gOily_spills[i].car == NULL) {
  491.             oily_index = i;
  492.             break;
  493.         }
  494.         if (gOily_spills[i].spill_time < oldest_time) {
  495.             oldest_time = gOily_spills[i].spill_time;
  496.             oldest_one = i;
  497.         }
  498.     }
  499.     if (oily_index < 0) {
  500.         oily_index = oldest_one;
  501.     }
  502.     gOily_spills[oily_index].car = car;
  503.     gOily_spills[oily_index].spill_time = the_time;
  504.     gOily_spills[oily_index].full_size = pContents->data.oil_spill.full_size;
  505.     gOily_spills[oily_index].grow_rate = pContents->data.oil_spill.grow_rate;
  506.     gOily_spills[oily_index].current_size = pContents->data.oil_spill.current_size;
  507.     gOily_spills[oily_index].actor->render_style = BR_RSTYLE_NONE;
  508. }
  509.