Subversion Repositories Games.Carmageddon

Rev

Rev 20 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "opponent.h"
  2. #include "brender/brender.h"
  3. #include "car.h"
  4. #include "controls.h"
  5. #include "crush.h"
  6. #include "displays.h"
  7. #include "errors.h"
  8. #include "finteray.h"
  9. #include "globvars.h"
  10. #include "globvrkm.h"
  11. #include "globvrme.h"
  12. #include "globvrpb.h"
  13. #include "harness/trace.h"
  14. #include "loading.h"
  15. #include "oppoproc.h"
  16. #include "pd/sys.h"
  17. #include "skidmark.h"
  18. #include "trig.h"
  19. #include "utility.h"
  20.  
  21. #include <float.h>
  22. #include <stdlib.h>
  23.  
  24. br_actor* gOppo_path_actor;
  25. br_model* gOppo_path_model;
  26. br_material* gMat_dk_yel;
  27. br_material* gMat_md_yel;
  28. br_material* gMat_lt_yel;
  29. br_material* gMat_dk_red;
  30. br_material* gMat_lt_red;
  31. br_material* gMat_dk_grn;
  32. br_material* gMat_lt_grn;
  33. br_material* gMat_dk_blu;
  34. br_material* gMat_lt_blu;
  35. br_material* gMat_dk_turq;
  36. br_material* gMat_lt_turq;
  37. br_material* gMat_dk_gry;
  38. br_material* gMat_md_gry;
  39. br_material* gMat_lt_gry;
  40. int gMellow_opponents;
  41. int gTest_toggle;
  42. int gAlready_elasticating;
  43. int gVertices_used_in_non_edit_paths;
  44. int gFaces_used_in_non_edit_paths;
  45. int gMats_allocated;
  46. int gOppo_paths_shown;
  47. int gMade_path_filename;
  48. int gBIG_APC_index = -1;
  49. char* gPath_section_type_names[3];
  50. int gMin_bangness = 100;
  51. int gMax_bangness;
  52. tU32 gNext_elastication;
  53. tU32 gNext_write_during_elastication;
  54. char* gCop_name = "Faceless Cop";
  55. char* gDrone_name = "Innocent Civilian";
  56. int gChallenger_index__opponent = -1; // suffix added to avoid duplicate symbol
  57. int gSFS_count;
  58. int gSFS_total_cycles;
  59. int gSFS_max_cycles;
  60. float gOpponent_nastyness_frigger = 1.f;
  61. char gOppo_path_filename[256];
  62. br_scalar gIn_view_distance;
  63. tU8* gBit_per_node;
  64. int gGrudge_reduction_per_period;
  65. int gSFS_cycles_this_time;
  66. br_scalar gMinimum_yness_before_knackerisation;
  67. int gWanky_arse_tit_fuck;
  68. br_scalar gHead_on_cos_value;
  69. tU32 gNext_grudge_reduction;
  70. br_scalar gCop_pursuit_speed_percentage_multiplier;
  71. br_scalar gDefinite_cop_pursuit_speed;
  72. int gAcknowledged_start;
  73. int gStart_jumped;
  74. int gNum_of_opponents_getting_near;
  75. int gNumber_of_cops_before_faffage;
  76. int gFirst_frame;
  77. tU32 gAcme_frame_count;
  78. br_scalar gDefinite_no_cop_pursuit_speed;
  79. int gNum_of_opponents_completing_race;
  80. int gNum_of_opponents_pursuing;
  81. int gActive_car_list_rebuild_required;
  82. br_scalar gFrame_period_for_this_munging_in_secs;
  83. int gBig_bang;
  84. int gProcessing_opponents;
  85. tU32 gFrame_period_for_this_munging;
  86. tU32 gTime_stamp_for_this_munging;
  87. tS16 gMobile_section;
  88.  
  89. // IDA: void __usercall PointActorAlongThisBloodyVector(br_actor *pThe_actor@<EAX>, br_vector3 *pThe_vector@<EDX>)
  90. void PointActorAlongThisBloodyVector(br_actor* pThe_actor, br_vector3* pThe_vector) {
  91.     br_transform trans;
  92.     LOG_TRACE("(%p, %p)", pThe_actor, pThe_vector);
  93.  
  94.     trans.type = BR_TRANSFORM_LOOK_UP;
  95.     BrVector3Copy(&trans.t.look_up.look, pThe_vector);
  96.     BrVector3Set(&trans.t.look_up.up, 0.f, 1.f, 0.f);
  97.     BrVector3Copy(&trans.t.look_up.t, &pThe_actor->t.t.translate.t);
  98.     BrTransformToTransform(&pThe_actor->t, &trans);
  99. }
  100.  
  101. // IDA: void __usercall ProcessCurrentObjective(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  102. void ProcessCurrentObjective(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  103.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  104.  
  105.     switch (pOpponent_spec->current_objective) {
  106.     case eOOT_complete_race:
  107.         ProcessCompleteRace(pOpponent_spec, pCommand);
  108.         break;
  109.     case eOOT_pursue_and_twat:
  110.         ProcessPursueAndTwat(pOpponent_spec, pCommand);
  111.         break;
  112.     case eOOT_run_away:
  113.         ProcessRunAway(pOpponent_spec, pCommand);
  114.         break;
  115.     case eOOT_get_near_player:
  116.         ProcessGetNearPlayer(pOpponent_spec, pCommand);
  117.         break;
  118.     case eOOT_levitate:
  119.         ProcessLevitate(pOpponent_spec, pCommand);
  120.         break;
  121.     case eOOT_knackered_and_freewheeling:
  122.         // FIXME: is keys correct?
  123.         memset(&pOpponent_spec->car_spec->keys, 0, sizeof(pOpponent_spec->car_spec->keys));
  124.         pOpponent_spec->car_spec->acc_force = 0.f;
  125.         pOpponent_spec->car_spec->brake_force = 0.f;
  126.         pOpponent_spec->car_spec->curvature = 0.f;
  127.         break;
  128.     case eOOT_frozen:
  129.         ProcessFrozen(pOpponent_spec, pCommand);
  130.         break;
  131.     case eOOT_wait_for_some_hapless_sod:
  132.         ProcessWaitForSomeHaplessSod(pOpponent_spec, pCommand);
  133.         break;
  134.     case eOOT_rematerialise:
  135.         break;
  136.     case eOOT_return_to_start:
  137.         ProcessReturnToStart(pOpponent_spec, pCommand);
  138.         break;
  139.     default:
  140.         break;
  141.     }
  142. }
  143.  
  144. // IDA: tS16 __usercall ReallocExtraPathNodes@<AX>(int pHow_many_then@<EAX>)
  145. tS16 ReallocExtraPathNodes(int pHow_many_then) {
  146.     tPath_node* new_nodes;
  147.     tS16 first_new_node;
  148.     LOG_TRACE("(%d)", pHow_many_then);
  149.  
  150.     first_new_node = -1;
  151.     if (pHow_many_then != 0) {
  152.         first_new_node = gProgram_state.AI_vehicles.number_of_path_nodes;
  153.         new_nodes = BrMemAllocate(sizeof(tPath_node) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_nodes), kMem_oppo_new_nodes);
  154.         memcpy(new_nodes, gProgram_state.AI_vehicles.path_nodes, sizeof(tPath_node) * gProgram_state.AI_vehicles.number_of_path_nodes);
  155.         if (gProgram_state.AI_vehicles.path_nodes != NULL) {
  156.             BrMemFree(gProgram_state.AI_vehicles.path_nodes);
  157.         }
  158.         gProgram_state.AI_vehicles.number_of_path_nodes += pHow_many_then;
  159.         gProgram_state.AI_vehicles.path_nodes = new_nodes;
  160.     }
  161.     dr_dprintf(
  162.         "ReallocExtraPathNodes(): Allocated %d bytes for %d path nodes",
  163.         sizeof(tPath_node) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_nodes),
  164.         pHow_many_then);
  165.     return first_new_node;
  166. }
  167.  
  168. // IDA: tS16 __usercall ReallocExtraPathSections@<AX>(int pHow_many_then@<EAX>)
  169. tS16 ReallocExtraPathSections(int pHow_many_then) {
  170.     tPath_section* new_sections;
  171.     tS16 first_new_section;
  172.     LOG_TRACE("(%d)", pHow_many_then);
  173.  
  174.     first_new_section = -1;
  175.     if (pHow_many_then != 0) {
  176.         first_new_section = gProgram_state.AI_vehicles.number_of_path_sections;
  177.         new_sections = BrMemAllocate(sizeof(tPath_section) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_sections), kMem_oppo_new_sections);
  178.         memcpy(new_sections, gProgram_state.AI_vehicles.path_sections, sizeof(tPath_section) * gProgram_state.AI_vehicles.number_of_path_sections);
  179.         if (gProgram_state.AI_vehicles.path_sections != NULL) {
  180.             BrMemFree(gProgram_state.AI_vehicles.path_sections);
  181.         }
  182.         gProgram_state.AI_vehicles.number_of_path_sections += pHow_many_then;
  183.         gProgram_state.AI_vehicles.path_sections = new_sections;
  184.     }
  185.     dr_dprintf(
  186.         "ReallocExtraPathSections(): Allocated %d bytes for %d path sections",
  187.         sizeof(tPath_section) * (pHow_many_then + gProgram_state.AI_vehicles.number_of_path_sections),
  188.         pHow_many_then);
  189.     return first_new_section;
  190. }
  191.  
  192. // IDA: int __usercall PointVisibleFromHere@<EAX>(br_vector3 *pFrom@<EAX>, br_vector3 *pTo@<EDX>)
  193. int PointVisibleFromHere(br_vector3* pFrom, br_vector3* pTo) {
  194.     br_vector3 from;
  195.     br_vector3 dir;
  196.     br_vector3 norm;
  197.     br_scalar t;
  198.     br_material* material;
  199.     LOG_TRACE("(%p, %p)", pFrom, pTo);
  200.  
  201.     BrVector3Sub(&dir, pTo, pFrom);
  202.     BrVector3Copy(&from, pFrom);
  203.     from.v[1] += 0.15f;
  204.     dir.v[1] += 0.15f;
  205.     FindFace(&from, &dir, &norm, &t, &material);
  206.     return t > 1.0;
  207. }
  208.  
  209. // IDA: tS16 __usercall FindNearestPathNode@<AX>(br_vector3 *pActor_coords@<EAX>, br_scalar *pDistance@<EDX>)
  210. tS16 FindNearestPathNode(br_vector3* pActor_coords, br_scalar* pDistance) {
  211.     int i;
  212.     tS16 nearest_node;
  213.     br_scalar distance;
  214.     br_vector3 actor_to_node;
  215.     LOG_TRACE("(%p, %p)", pActor_coords, pDistance);
  216.  
  217.     nearest_node = -1;
  218.     *pDistance = FLT_MAX;
  219.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
  220.         BrVector3Sub(&actor_to_node, &gProgram_state.AI_vehicles.path_nodes[i].p, pActor_coords);
  221.         distance = BrVector3Length(&actor_to_node);
  222.         if (distance < *pDistance && (!gAlready_elasticating || gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] != i)) {
  223.             *pDistance = distance;
  224.             nearest_node = i;
  225.         }
  226.     }
  227.     return nearest_node;
  228. }
  229.  
  230. // IDA: tS16 __usercall FindNearestPathSection@<AX>(br_vector3 *pActor_coords@<EAX>, br_vector3 *pPath_direction@<EDX>, br_vector3 *pIntersect@<EBX>, br_scalar *pDistance@<ECX>)
  231. tS16 FindNearestPathSection(br_vector3* pActor_coords, br_vector3* pPath_direction, br_vector3* pIntersect, br_scalar* pDistance) {
  232.     LOG_TRACE("(%p, %p, %p, %p)", pActor_coords, pPath_direction, pIntersect, pDistance);
  233.  
  234.     return FindNearestGeneralSection(NULL, pActor_coords, pPath_direction, pIntersect, pDistance);
  235. }
  236.  
  237. // IDA: tS16 __usercall FindNearestGeneralSection@<AX>(tCar_spec *pPursuee@<EAX>, br_vector3 *pActor_coords@<EDX>, br_vector3 *pPath_direction@<EBX>, br_vector3 *pIntersect@<ECX>, br_scalar *pDistance)
  238. tS16 FindNearestGeneralSection(tCar_spec* pPursuee, br_vector3* pActor_coords, br_vector3* pPath_direction, br_vector3* pIntersect, br_scalar* pDistance) {
  239.     int section_no;
  240.     int no_sections;
  241.     tS16 nearest_node_section_no;
  242.     tS16 nearest_section;
  243.     br_scalar nearest_node_distance_squared;
  244.     br_scalar closest_distance_squared;
  245.     br_scalar the_distance_squared;
  246.     br_scalar t;
  247.     br_scalar length_squared_a;
  248.     br_vector3 a;
  249.     br_vector3 p;
  250.     //br_vector3 wank; // Pierre-Marie Baty -- unused variable
  251.     br_vector3 intersect;
  252.     br_vector3* start;
  253.     br_vector3* finish;
  254.     br_vector3* nearest_node_v;
  255. #if defined(DETHRACE_FIX_BUGS)
  256.     br_vector3 zero_vector;
  257. #endif
  258.     LOG_TRACE("(%p, %p, %p, %p, %p)", pPursuee, pActor_coords, pPath_direction, pIntersect, pDistance);
  259.  
  260.     nearest_section = -1;
  261.     nearest_node_section_no = -1;
  262.     closest_distance_squared = BR_SCALAR_MAX;
  263.     nearest_node_distance_squared = BR_SCALAR_MAX;
  264. #if defined(DETHRACE_FIX_BUGS)
  265.     BrVector3Set(&zero_vector, 0.f, 0.f, 0.f);
  266.     nearest_node_v = &zero_vector;
  267. #endif
  268.  
  269.     if (pPursuee != NULL) {
  270.         no_sections = pPursuee->my_trail.number_of_nodes - 1;
  271.     } else {
  272.         no_sections = gProgram_state.AI_vehicles.number_of_path_sections;
  273.     }
  274.  
  275.     for (section_no = 0; section_no < no_sections; section_no++) {
  276.         if (pPursuee != NULL) {
  277.             start = &pPursuee->my_trail.trail_nodes[section_no];
  278.             finish = &pPursuee->my_trail.trail_nodes[section_no + 1];
  279.         } else {
  280.             start = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p;
  281.             finish = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p;
  282.         }
  283.         if (!gAlready_elasticating || gMobile_section != section_no) {
  284.             BrVector3Sub(&a, finish, start);
  285.             BrVector3Sub(&p, pActor_coords, start);
  286.             the_distance_squared = Vector3DistanceSquared(&p, &a);
  287.             if (the_distance_squared < closest_distance_squared) {
  288.                 closest_distance_squared = the_distance_squared;
  289.                 nearest_section = section_no;
  290.                 nearest_node_v = finish;
  291.             }
  292.             the_distance_squared = BrVector3LengthSquared(&p);
  293.             if (the_distance_squared < closest_distance_squared) {
  294.                 closest_distance_squared = the_distance_squared;
  295.                 nearest_section = section_no;
  296.                 nearest_node_v = start;
  297.             }
  298.             length_squared_a = BrVector3LengthSquared(&a);
  299.             if (length_squared_a >= 0.0001f) {
  300.                 t = BrVector3Dot(&p, &a) / length_squared_a;
  301.                 if (t >= 0 && t <= 1.f) {
  302.                     p.v[0] -= t * a.v[0];
  303.                     p.v[1] -= t * a.v[1];
  304.                     p.v[2] -= t * a.v[2];
  305.                     the_distance_squared = BrVector3LengthSquared(&p);
  306.                     if (the_distance_squared < nearest_node_distance_squared) {
  307.                         BrVector3Scale(&intersect, &a, t);
  308.                         BrVector3Add(pIntersect, start, &intersect);
  309.                         BrVector3NormaliseQuick(pPath_direction, &a);
  310.                         nearest_node_distance_squared = the_distance_squared;
  311.                         nearest_node_section_no = section_no;
  312.                     }
  313.                 }
  314.             }
  315.         }
  316.     }
  317.     if (nearest_node_distance_squared > closest_distance_squared) {
  318.         nearest_node_section_no = nearest_section;
  319.         if (pPursuee != NULL) {
  320.             start = &pPursuee->my_trail.trail_nodes[nearest_section];
  321.             finish = &pPursuee->my_trail.trail_nodes[nearest_section + 1];
  322.         } else {
  323.             start = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[nearest_section].node_indices[0]].p;
  324.             finish = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[nearest_section].node_indices[1]].p;
  325.         }
  326.         BrVector3Sub(&p, finish, start);
  327.         BrVector3NormaliseQuick(pPath_direction, &p);
  328.         BrVector3Copy(pIntersect, nearest_node_v);
  329.         *pDistance = sqrtf(closest_distance_squared);
  330.     } else {
  331.         *pDistance = sqrtf(nearest_node_distance_squared);
  332.     }
  333.     if (pPursuee != NULL) {
  334.         nearest_node_section_no += 15000;
  335.     }
  336.     return nearest_node_section_no;
  337. }
  338.  
  339. // IDA: void __usercall DeadStopCar(tCar_spec *pCar_spec@<EAX>)
  340. void DeadStopCar(tCar_spec* pCar_spec) {
  341.     LOG_TRACE("(%p)", pCar_spec);
  342.  
  343.     pCar_spec->acc_force = 0.f;
  344.     pCar_spec->brake_force = 0.f;
  345.     pCar_spec->curvature = 0.f;
  346.     pCar_spec->gear = 0;
  347.     pCar_spec->revs = 0.f;
  348.     BrVector3Set(&pCar_spec->omega, 0.f, 0.f, 0.f);
  349.     BrVector3Set(&pCar_spec->v, 0.f, 0.f, 0.f);
  350. }
  351.  
  352. // IDA: void __usercall TurnOpponentPhysicsOn(tOpponent_spec *pOpponent_spec@<EAX>)
  353. void TurnOpponentPhysicsOn(tOpponent_spec* pOpponent_spec) {
  354.     LOG_TRACE("(%p)", pOpponent_spec);
  355.  
  356.     if (pOpponent_spec->physics_me == 0) {
  357.         pOpponent_spec->physics_me = 1;
  358.         gActive_car_list_rebuild_required = 1;
  359.     }
  360. }
  361.  
  362. // IDA: void __usercall TurnOpponentPhysicsOff(tOpponent_spec *pOpponent_spec@<EAX>)
  363. void TurnOpponentPhysicsOff(tOpponent_spec* pOpponent_spec) {
  364.     LOG_TRACE("(%p)", pOpponent_spec);
  365.  
  366.     DeadStopCar(pOpponent_spec->car_spec);
  367.     if (pOpponent_spec->physics_me) {
  368.         pOpponent_spec->physics_me = 0;
  369.         gActive_car_list_rebuild_required = 1;
  370.     }
  371. }
  372.  
  373. // IDA: void __cdecl NewObjective(tOpponent_spec *pOpponent_spec, tOpponent_objective_type pObjective_type, ...)
  374. void NewObjective(tOpponent_spec* pOpponent_spec, tOpponent_objective_type pObjective_type, ...) {
  375.     va_list marker;
  376.     LOG_TRACE("(%p, %d)", pOpponent_spec, pObjective_type);
  377.  
  378.     if (pOpponent_spec->current_objective != eOOT_none) {
  379.         ProcessCurrentObjective(pOpponent_spec, ePOC_die);
  380.     }
  381.     pOpponent_spec->current_objective = pObjective_type;
  382.     pOpponent_spec->time_this_objective_started = gTime_stamp_for_this_munging;
  383.     pOpponent_spec->time_for_this_objective_to_finish = gTime_stamp_for_this_munging + IRandomBetween(30, 180) * 1000;
  384.     if (pObjective_type == eOOT_pursue_and_twat) {
  385.         pOpponent_spec->time_for_this_objective_to_finish += 90000;
  386.     }
  387.     switch (pObjective_type) {
  388.     case eOOT_complete_race:
  389.         gNum_of_opponents_completing_race++;
  390.         break;
  391.     case eOOT_pursue_and_twat:
  392.         va_start(marker, pObjective_type);
  393.         pOpponent_spec->pursue_car_data.pursuee = va_arg(marker, tCar_spec*);
  394.         va_end(marker);
  395.         break;
  396.     case eOOT_get_near_player:
  397.         gNum_of_opponents_getting_near++;
  398.         break;
  399.     default:
  400.         break;
  401.     }
  402.     dr_dprintf("%s: NewObjective() - type %d", pOpponent_spec->car_spec->driver_name, pObjective_type);
  403.     ProcessCurrentObjective(pOpponent_spec, ePOC_start);
  404. }
  405.  
  406. // IDA: void __usercall CalcRaceRoute(tOpponent_spec *pOpponent_spec@<EAX>)
  407. void CalcRaceRoute(tOpponent_spec* pOpponent_spec) {
  408.     tS16 section_no;
  409.     //tS16 section_no_index; // Pierre-Marie Baty -- unused variable
  410.     tS16 node_no;
  411.     tS16 race_section_count;
  412.     tS16 normal_section_ok_direction_count;
  413.     tS16 normal_section_wrong_direction_count;
  414.     tS16 temp_section_array[8];
  415.     br_scalar distance;
  416.     br_vector3 direction_v;
  417.     br_vector3 intersect;
  418.     //char str[256]; // Pierre-Marie Baty -- unused variable
  419.     //char work_str[32]; // Pierre-Marie Baty -- unused variable
  420.     int i;
  421.     LOG_TRACE("(%p)", pOpponent_spec);
  422.  
  423.     if (pOpponent_spec->nnext_sections >= COUNT_OF(pOpponent_spec->next_sections)) {
  424.         dr_dprintf("%s: CalcRaceRoute() - Pissing off 'cos projected route full up", pOpponent_spec->car_spec->driver_name);
  425.         return;
  426.     }
  427.     if (pOpponent_spec->nnext_sections == 0) {
  428.         dr_dprintf("%s: CalcRaceRoute() - Projected route empty; starting from nearest section", pOpponent_spec->car_spec->driver_name);
  429.         pOpponent_spec->complete_race_data.finished_calcing_race_route = 0;
  430.         pOpponent_spec->complete_race_data.found_race_section = 0;
  431.         section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
  432.         if (section_no < 0) {
  433.             return;
  434.         }
  435.         AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
  436.         if (gProgram_state.AI_vehicles.path_sections[section_no].type == ePST_race_path) {
  437.             pOpponent_spec->complete_race_data.found_race_section = 1;
  438.         }
  439.     }
  440.     while (pOpponent_spec->nnext_sections < COUNT_OF(pOpponent_spec->next_sections) && !pOpponent_spec->complete_race_data.finished_calcing_race_route) {
  441.         node_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no].node_indices[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction];
  442.         race_section_count = 0;
  443.         normal_section_ok_direction_count = 0;
  444.         normal_section_wrong_direction_count = 0;
  445.         for (i = 0; i < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; i++) {
  446.             section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[i];
  447.             if (pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no != section_no) {
  448.                 if (gProgram_state.AI_vehicles.path_sections[section_no].type == 1 && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] == node_no) {
  449.                     pOpponent_spec->complete_race_data.found_race_section = 1;
  450.                     temp_section_array[race_section_count] = section_no;
  451.                     race_section_count++;
  452.                 } else if (race_section_count == 0 && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] == node_no) {
  453.                     temp_section_array[normal_section_ok_direction_count] = section_no;
  454.                     normal_section_ok_direction_count++;
  455.                 } else if (race_section_count == 0 && normal_section_ok_direction_count == 0 && (!gProgram_state.AI_vehicles.path_sections[section_no].one_way || gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no)) {
  456.                     temp_section_array[normal_section_wrong_direction_count] = section_no;
  457.                     normal_section_wrong_direction_count++;
  458.                 }
  459.             }
  460.         }
  461.         if (race_section_count != 0) {
  462.             AddToOpponentsProjectedRoute(pOpponent_spec, temp_section_array[IRandomBetween(0, race_section_count - 1)], 1);
  463.         } else if (normal_section_ok_direction_count != 0) {
  464.             AddToOpponentsProjectedRoute(pOpponent_spec, temp_section_array[IRandomBetween(0, normal_section_ok_direction_count - 1)], 1);
  465.         } else if (normal_section_wrong_direction_count != 0) {
  466.             AddToOpponentsProjectedRoute(pOpponent_spec, temp_section_array[IRandomBetween(0, normal_section_wrong_direction_count - 1)], 1);
  467.         } else if (pOpponent_spec->complete_race_data.found_race_section) {
  468.             pOpponent_spec->complete_race_data.finished_calcing_race_route = 1;
  469.         } else {
  470.             AddToOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no, pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction == 0);
  471.         }
  472.     }
  473. }
  474.  
  475. // IDA: void __usercall TopUpRandomRoute(tOpponent_spec *pOpponent_spec@<EAX>, int pSections_to_add@<EDX>)
  476. void TopUpRandomRoute(tOpponent_spec* pOpponent_spec, int pSections_to_add) {
  477.     tS16 section_no;
  478.     tS16 node_no;
  479.     tS16 temp_section_array[8];
  480.     int i;
  481.     int target;
  482.     int num_of_temp_sections;
  483.     int direction;
  484.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSections_to_add);
  485.  
  486.     if (!pSections_to_add) {
  487.         PDEnterDebugger("TopUpRandomRoute() called with no seed (woof, bark, etc.)");
  488.     }
  489.     if (pSections_to_add >= 0) {
  490.         target = MIN(pSections_to_add + pOpponent_spec->nnext_sections, 10);
  491.     } else {
  492.         target = 10;
  493.     }
  494.     while (pOpponent_spec->nnext_sections < target) {
  495.         node_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no].node_indices[pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction];
  496.         if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections <= 1) {
  497.             section_no = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no;
  498.             direction = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction == 0;
  499.         } else {
  500.             num_of_temp_sections = 0;
  501.             for (i = 0; i < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; i++) {
  502.                 section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[i];
  503.                 if (pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no != section_no
  504.                     && (!gProgram_state.AI_vehicles.path_sections[section_no].one_way || gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no)
  505.                     && (pOpponent_spec->cheating || gProgram_state.AI_vehicles.path_sections[section_no].type != ePST_cheat_only)) {
  506.                     temp_section_array[num_of_temp_sections] = section_no;
  507.                     num_of_temp_sections++;
  508.                 }
  509.             }
  510.  
  511.             if (num_of_temp_sections == 0) {
  512.                 section_no = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no;
  513.                 direction = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].direction == 0;
  514.             } else if (num_of_temp_sections == 1) {
  515.                 section_no = temp_section_array[0];
  516.                 direction = gProgram_state.AI_vehicles.path_sections[temp_section_array[0]].node_indices[1] != node_no;
  517.             } else {
  518.                 section_no = temp_section_array[IRandomBetween(0, num_of_temp_sections - 1)];
  519.                 direction = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no;
  520.             }
  521.         }
  522.         AddToOpponentsProjectedRoute(pOpponent_spec, section_no, direction);
  523.     }
  524. }
  525.  
  526. // IDA: int __usercall SearchForSection@<EAX>(tRoute_section *pTemp_store@<EAX>, tRoute_section *pPerm_store@<EDX>, int *pNum_of_perm_store_sections@<EBX>, tS16 pTarget_section@<ECX>, int pDepth, br_scalar pDistance_so_far, tOpponent_spec *pOpponent_spec)
  527. int SearchForSection(tRoute_section* pTemp_store, tRoute_section* pPerm_store, int* pNum_of_perm_store_sections, tS16 pTarget_section, int pDepth, br_scalar pDistance_so_far, tOpponent_spec* pOpponent_spec) {
  528.     static br_scalar shortest_dist;
  529.     static int routes_found;
  530.     //char depth_indent[32]; // Pierre-Marie Baty -- unused variable
  531.     int direction;
  532.     tPath_node* node_ptr;
  533.     tS16 node_no;
  534.     tS16 section_no;
  535.     tS16 section_no_index;
  536.     br_scalar distance_so_far;
  537.     LOG_TRACE("(%p, %p, %p, %d, %d, %f, %p)", pTemp_store, pPerm_store, pNum_of_perm_store_sections, pTarget_section, pDepth, pDistance_so_far, pOpponent_spec);
  538.  
  539.     // added by dethrace for readability (?)
  540.     tS16 section_no_dir_index;
  541.  
  542.     gSFS_cycles_this_time++;
  543.     if (pDepth == 1) {
  544.         memset(gBit_per_node, 0, (gProgram_state.AI_vehicles.number_of_path_nodes + 7) / 8);
  545.         shortest_dist = BR_SCALAR_MAX;
  546.         routes_found = 0;
  547.         *pNum_of_perm_store_sections = 0;
  548.     }
  549.     if (pDepth >= 10) {
  550.         return 0;
  551.     }
  552.     node_no = gProgram_state.AI_vehicles.path_sections[pTemp_store[pDepth - 1].section_no].node_indices[pTemp_store[pDepth - 1].direction];
  553.     node_ptr = &gProgram_state.AI_vehicles.path_nodes[node_no];
  554.     gBit_per_node[node_no / 8] |= 1 << (node_no % 8);
  555.     for (section_no_index = 0; section_no_index < node_ptr->number_of_sections; section_no_index++) {
  556.  
  557.         section_no = node_ptr->sections[section_no_index];
  558.         direction = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no;
  559.         section_no_dir_index = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[direction];
  560.  
  561.         // int b = BYTE4(v8);
  562.         // int y = (int)(((BYTE4(v8) ^ (((BYTE4(v8) ^ v8) - BYTE4(v8)) & 7)) - BYTE4(v8)));
  563.         // int val = valx(v8);
  564.         // LOG_DEBUG("val %d, b %d, y %d", val, b, y);
  565.         // int x = ((BYTE4(v8) ^ (((BYTE4(v8) ^ v8) - BYTE4(v8)) & 7)) - BYTE4(v8));
  566.         // int x2 = v8 & 7;
  567.         // if (x != x2 || val != x) {
  568.         //     TELL_ME_IF_WE_PASS_THIS_WAY();
  569.         // }
  570.         if ((gBit_per_node[section_no_dir_index / 8] & (1 << (section_no_dir_index & 7))) == 0
  571.             && (!gProgram_state.AI_vehicles.path_sections[section_no].one_way || direction)
  572.             && (pOpponent_spec->cheating || gProgram_state.AI_vehicles.path_sections[section_no].type != ePST_cheat_only)) {
  573.  
  574.             pTemp_store[pDepth].section_no = section_no;
  575.             pTemp_store[pDepth].direction = direction;
  576.             distance_so_far = gProgram_state.AI_vehicles.path_sections[section_no].length + pDistance_so_far;
  577.  
  578.             if (pTarget_section == section_no && distance_so_far < shortest_dist) {
  579.                 shortest_dist = distance_so_far;
  580.                 *pNum_of_perm_store_sections = pDepth + 1;
  581.                 memcpy(pPerm_store, pTemp_store, sizeof(tRoute_section) * *pNum_of_perm_store_sections);
  582.                 // dword_530DD4 = ++routes_found
  583.                 routes_found++;
  584.                 if (routes_found >= 2) {
  585.                     return 1;
  586.                 }
  587.                 break;
  588.             }
  589.  
  590.             if (pDepth < 9
  591.                 && SearchForSection(pTemp_store, pPerm_store, pNum_of_perm_store_sections, pTarget_section, pDepth + 1, distance_so_far, pOpponent_spec)) {
  592.                 return 1;
  593.             }
  594.         }
  595.     }
  596.     gBit_per_node[node_no / 8] &= ~(1 << (node_no % 8));
  597.     return 0;
  598. }
  599.  
  600. // IDA: void __usercall CalcGetNearPlayerRoute(tOpponent_spec *pOpponent_spec@<EAX>, tCar_spec *pPlayer@<EDX>)
  601. void CalcGetNearPlayerRoute(tOpponent_spec* pOpponent_spec, tCar_spec* pPlayer) {
  602.     //int i; // Pierre-Marie Baty -- unused variable
  603.     //int pass_2_depth; // Pierre-Marie Baty -- unused variable
  604.     //int sections_away; // Pierre-Marie Baty -- unused variable
  605.     int num_of_perm_store_sections;
  606.     int sections_to_copy;
  607.     int fuck_it;
  608.     tS16 section_no;
  609.     tS16 players_section;
  610.     br_vector3 section_v;
  611.     br_vector3 intersect;
  612.     br_scalar distance;
  613.     tRoute_section temp_store[10];
  614.     tRoute_section perm_store[10];
  615.     //char work_str[32]; // Pierre-Marie Baty -- unused variable
  616.     //char str[256]; // Pierre-Marie Baty -- unused variable
  617.     LOG_TRACE("(%p, %p)", pOpponent_spec, pPlayer);
  618.  
  619.     fuck_it = 0;
  620.     if (pOpponent_spec->nnext_sections >= COUNT_OF(pOpponent_spec->next_sections)) {
  621.         dr_dprintf("%s: CalcGetNearPlayerRoute() - Quitting because route full up", pOpponent_spec->car_spec->driver_name);
  622.         return;
  623.     }
  624.     players_section = FindNearestPathSection(&pPlayer->car_master_actor->t.t.translate.t, &section_v, &intersect, &distance);
  625.     if (players_section < 0) {
  626.         PDEnterDebugger("No path section near player. THIS CAN'T HAPPEN!");
  627.         return;
  628.     }
  629.     if (pOpponent_spec->players_section_when_last_calced_full_path != players_section) {
  630.         dr_dprintf("%s: CalcGetNearPlayerRoute() - Player has moved since last time (section #%d; was #%d)", pOpponent_spec->car_spec->driver_name, players_section, pOpponent_spec->players_section_when_last_calced_full_path);
  631.         ClearOpponentsProjectedRoute(pOpponent_spec);
  632.     }
  633.     if (pOpponent_spec->nnext_sections == 0) {
  634.         pOpponent_spec->players_section_when_last_calced_full_path = players_section;
  635.         dr_dprintf("%s: CalcGetNearPlayerRoute() - Empty route; setting players_section_when_last_calced_full_path to #%d", pOpponent_spec->car_spec->driver_name, pOpponent_spec->players_section_when_last_calced_full_path);
  636.         section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &section_v, &intersect, &distance);
  637.         if (section_no < 0) {
  638.             return;
  639.         }
  640.         AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
  641.     }
  642.     if (pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1].section_no == players_section) {
  643.         dr_dprintf("%s: CalcGetNearPlayerRoute() - Last section is player's section (%d), so tack a random one on t'end", pOpponent_spec->car_spec->driver_name, players_section);
  644.         TopUpRandomRoute(pOpponent_spec, 1);
  645.     }
  646.     while (pOpponent_spec->nnext_sections < 6 && !fuck_it) {
  647.         temp_store[0] = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1];
  648.         dr_dprintf("%s: CalcGetNearPlayerRoute() - In loop; our section #%d, player's section #%d", pOpponent_spec->car_spec->driver_name, temp_store[0].section_no, players_section);
  649.         gSFS_count++;
  650.         gSFS_cycles_this_time = 0;
  651.         SearchForSection(temp_store, perm_store, &num_of_perm_store_sections, players_section, 1, 0.f, pOpponent_spec);
  652.         gSFS_total_cycles += gSFS_cycles_this_time;
  653.         if (gSFS_max_cycles < gSFS_cycles_this_time) {
  654.             gSFS_max_cycles = gSFS_cycles_this_time;
  655.         }
  656.         dr_dprintf(">>>SearchForSection() - max %d, avg %.1f", gSFS_max_cycles, gSFS_total_cycles / (float)gSFS_count);
  657.         if (num_of_perm_store_sections <= 1) {
  658.             dr_dprintf("%s: CalcGetNearPlayerRoute() - SearchForSection() produced bugger all", pOpponent_spec->car_spec->driver_name);
  659.             fuck_it = 1;
  660.             if (pOpponent_spec->nnext_sections <= 4) {
  661.                 TopUpRandomRoute(pOpponent_spec, 4 - pOpponent_spec->nnext_sections + 4);
  662.             }
  663.         } else {
  664.             sections_to_copy = MIN((int) COUNT_OF(pOpponent_spec->next_sections) - pOpponent_spec->nnext_sections, num_of_perm_store_sections - 1); // Pierre-Marie Baty -- added type cast
  665.             memcpy(&pOpponent_spec->next_sections[pOpponent_spec->nnext_sections], &perm_store[1], sizeof(tRoute_section) * sections_to_copy);
  666.             pOpponent_spec->nnext_sections += sections_to_copy;
  667.             TopUpRandomRoute(pOpponent_spec, 1);
  668.         }
  669.     }
  670. }
  671.  
  672. // IDA: void __usercall CalcReturnToStartPointRoute(tOpponent_spec *pOpponent_spec@<EAX>)
  673. void CalcReturnToStartPointRoute(tOpponent_spec* pOpponent_spec) {
  674.     //int i; // Pierre-Marie Baty -- unused variable
  675.     //int pass_2_depth; // Pierre-Marie Baty -- unused variable
  676.     //int sections_away; // Pierre-Marie Baty -- unused variable
  677.     int num_of_perm_store_sections;
  678.     int sections_to_copy;
  679.     tS16 section_no;
  680.     br_vector3 intersect;
  681.     br_vector3 section_v;
  682.     br_scalar distance;
  683.     tRoute_section temp_store[10];
  684.     tRoute_section perm_store[10];
  685.     LOG_TRACE("(%p)", pOpponent_spec);
  686.  
  687.     ClearOpponentsProjectedRoute(pOpponent_spec);
  688.     section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &section_v, &intersect, &distance);
  689.     distance = BrVector3Length(&section_v);
  690.     BrVector3Normalise(&section_v, &section_v);
  691.  
  692.     if (BrVector3Dot(&pOpponent_spec->car_spec->direction, &section_v) <= 0.0f) {
  693.         AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 0);
  694.     } else {
  695.         AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
  696.     }
  697.     temp_store[0] = pOpponent_spec->next_sections[pOpponent_spec->nnext_sections - 1];
  698.     gSFS_count++;
  699.     gSFS_cycles_this_time = 0;
  700.     SearchForSection(temp_store, perm_store, &num_of_perm_store_sections, pOpponent_spec->return_to_start_data.section_no, 1, 0.0f, pOpponent_spec);
  701.     gSFS_total_cycles += gSFS_cycles_this_time;
  702.     if (gSFS_max_cycles < gSFS_cycles_this_time) {
  703.         gSFS_max_cycles = gSFS_cycles_this_time;
  704.     }
  705.     if (num_of_perm_store_sections <= 1) {
  706.         if (pOpponent_spec->nnext_sections <= 6) {
  707.             TopUpRandomRoute(pOpponent_spec, 4 - pOpponent_spec->nnext_sections + 4);
  708.         }
  709.     } else {
  710.         sections_to_copy = 10 - pOpponent_spec->nnext_sections;
  711.         if (sections_to_copy >= num_of_perm_store_sections - 1) {
  712.             sections_to_copy = num_of_perm_store_sections - 1;
  713.         }
  714.         memcpy(&pOpponent_spec->next_sections[pOpponent_spec->nnext_sections], &perm_store[1], sizeof(tRoute_section) * sections_to_copy);
  715.         pOpponent_spec->nnext_sections += sections_to_copy;
  716.         TopUpRandomRoute(pOpponent_spec, 1);
  717.     }
  718. }
  719.  
  720. // IDA: void __usercall ClearOpponentsProjectedRoute(tOpponent_spec *pOpponent_spec@<EAX>)
  721. void ClearOpponentsProjectedRoute(tOpponent_spec* pOpponent_spec) {
  722.     LOG_TRACE("(%p)", pOpponent_spec);
  723.  
  724.     pOpponent_spec->nnext_sections = 0;
  725. }
  726.  
  727. // IDA: int __usercall AddToOpponentsProjectedRoute@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection_no@<EDX>, int pDirection@<EBX>)
  728. int AddToOpponentsProjectedRoute(tOpponent_spec* pOpponent_spec, tS16 pSection_no, int pDirection) {
  729.     LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection_no, pDirection);
  730.  
  731.     if (pOpponent_spec->nnext_sections >= COUNT_OF(pOpponent_spec->next_sections)) {
  732.         return 0;
  733.     }
  734.     pOpponent_spec->next_sections[pOpponent_spec->nnext_sections].section_no = pSection_no;
  735.     pOpponent_spec->next_sections[pOpponent_spec->nnext_sections].direction = pDirection;
  736.     pOpponent_spec->nnext_sections++;
  737.     return 1;
  738. }
  739.  
  740. // IDA: int __usercall ShiftOpponentsProjectedRoute@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pPlaces@<EDX>)
  741. int ShiftOpponentsProjectedRoute(tOpponent_spec* pOpponent_spec, int pPlaces) {
  742.     int i;
  743.     LOG_TRACE("(%p, %d)", pOpponent_spec, pPlaces);
  744.  
  745.     if (pOpponent_spec->nnext_sections <= pPlaces) {
  746.         return 0;
  747.     }
  748.     for (i = 0; i < (int) COUNT_OF(pOpponent_spec->next_sections) - pPlaces; i++) { // Pierre-Marie Baty -- added type cast
  749.         pOpponent_spec->next_sections[i].section_no = pOpponent_spec->next_sections[pPlaces + i].section_no;
  750.         pOpponent_spec->next_sections[i].direction = pOpponent_spec->next_sections[pPlaces + i].direction;
  751.     }
  752.     pOpponent_spec->nnext_sections -= pPlaces;
  753.     return 1;
  754. }
  755.  
  756. // IDA: void __usercall StunTheBugger(tOpponent_spec *pOpponent_spec@<EAX>, int pMilliseconds@<EDX>)
  757. void StunTheBugger(tOpponent_spec* pOpponent_spec, int pMilliseconds) {
  758.     LOG_TRACE("(%p, %d)", pOpponent_spec, pMilliseconds);
  759.  
  760.     pOpponent_spec->car_spec->acc_force = 0.f;
  761.     pOpponent_spec->car_spec->brake_force = 0.f;
  762.     pOpponent_spec->car_spec->curvature = 0.f;
  763.     pOpponent_spec->stun_time_ends = MAX(gTime_stamp_for_this_munging + pMilliseconds, pOpponent_spec->stun_time_ends);
  764. }
  765.  
  766. // IDA: void __usercall UnStunTheBugger(tOpponent_spec *pOpponent_spec@<EAX>)
  767. void UnStunTheBugger(tOpponent_spec* pOpponent_spec) {
  768.     LOG_TRACE("(%p)", pOpponent_spec);
  769.  
  770.     pOpponent_spec->stun_time_ends = 0;
  771. }
  772.  
  773. // IDA: void __usercall ProcessCompleteRace(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  774. void ProcessCompleteRace(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  775.     //br_vector3* initial_pos; // Pierre-Marie Baty -- unused variable
  776.     //br_actor* car_actor; // Pierre-Marie Baty -- unused variable
  777.     //tComplete_race_data* data; // Pierre-Marie Baty -- unused variable
  778.     int res;
  779.     //char str[256]; // Pierre-Marie Baty -- unused variable
  780.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  781.  
  782.     switch (pCommand) {
  783.     case ePOC_start:
  784.         dr_dprintf("%s: ProcessCompleteRace() - new objective started", pOpponent_spec->car_spec->driver_name);
  785.         ClearOpponentsProjectedRoute(pOpponent_spec);
  786.         CalcRaceRoute(pOpponent_spec);
  787.         ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  788.         break;
  789.     case ePOC_run:
  790.         if (pOpponent_spec->follow_path_data.section_no > 20000) {
  791.             ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
  792.             pOpponent_spec->follow_path_data.section_no = 20000;
  793.         }
  794.         res = ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0);
  795.         if (pOpponent_spec->nnext_sections == 0 || res == eFPR_end_of_path) {
  796.             dr_dprintf("%s: Giving up following race path because ran out of race path", pOpponent_spec->car_spec->driver_name);
  797.             NewObjective(pOpponent_spec, eOOT_get_near_player);
  798.         }
  799.         if (res != eFPR_OK) {
  800.             if (res == eFPR_given_up) {
  801.                 dr_dprintf("%s: Giving up complete_race because ProcessFollowPath() gave up", pOpponent_spec->car_spec->driver_name);
  802.             } else {
  803.                 dr_dprintf("%s: Giving up complete_race because reached end", pOpponent_spec->car_spec->driver_name);
  804.             }
  805.             ObjectiveComplete(pOpponent_spec);
  806.         }
  807.         if (gTime_stamp_for_this_munging > pOpponent_spec->time_this_objective_started + 20000) {
  808.             dr_dprintf("%s: Time to give up complete_race. Might be back in a sec, though!", pOpponent_spec->car_spec->driver_name);
  809.             ObjectiveComplete(pOpponent_spec);
  810.         }
  811.         if (pOpponent_spec->nnext_sections < 5 && !pOpponent_spec->complete_race_data.finished_calcing_race_route) {
  812.             CalcRaceRoute(pOpponent_spec);
  813.         }
  814.         break;
  815.     default:
  816.         break;
  817.     }
  818. }
  819.  
  820. // IDA: void __usercall StartRecordingTrail(tCar_spec *pPursuee@<EAX>)
  821. void StartRecordingTrail(tCar_spec* pPursuee) {
  822.     //int i; // Pierre-Marie Baty -- unused variable
  823.     LOG_TRACE("(%p)", pPursuee);
  824.  
  825.     if (pPursuee->no_of_processes_recording_my_trail == 0) {
  826.         dr_dprintf("StartRecordingTrail - starting from scratch");
  827.         pPursuee->no_of_processes_recording_my_trail = 1;
  828.         pPursuee->my_trail.nodes_shifted_this_frame = 0;
  829.         pPursuee->my_trail.has_deviated_recently = 0;
  830.         pPursuee->my_trail.number_of_nodes = 2;
  831.         pPursuee->my_trail.time_of_next_recording = gTime_stamp_for_this_munging + 500;
  832.         BrVector3Copy(&pPursuee->my_trail.base_heading, &pPursuee->direction);
  833.         BrVector3Copy(&pPursuee->my_trail.trail_nodes[0], &pPursuee->car_master_actor->t.t.translate.t);
  834.         BrVector3Copy(&pPursuee->my_trail.trail_nodes[1], &pPursuee->car_master_actor->t.t.translate.t);
  835.         pPursuee->my_trail.trail_nodes[0].v[2] += 0.2f;
  836.     } else {
  837.         dr_dprintf("StartRecordingTrail - another pursuer attaching");
  838.         pPursuee->no_of_processes_recording_my_trail++;
  839.     }
  840. }
  841.  
  842. // IDA: void __usercall RecordNextTrailNode(tCar_spec *pPursuee@<EAX>)
  843. void RecordNextTrailNode(tCar_spec* pPursuee) {
  844.     tPursuee_trail* trail;
  845.     br_vector3 start1;
  846.     br_vector3 finish1;
  847.     br_vector3 start2;
  848.     br_vector3 finish2;
  849.     br_vector3 offset_v;
  850.     br_vector3 car_to_last_point_v;
  851.     br_scalar length;
  852.     int visible;
  853.     LOG_TRACE("(%p)", pPursuee);
  854.  
  855.     trail = &pPursuee->my_trail;
  856.     if (trail->time_of_next_recording >= gTime_stamp_for_this_munging) {
  857.         return;
  858.     }
  859.     trail->time_of_next_recording = gTime_stamp_for_this_munging + 500;
  860.     trail->nodes_shifted_this_frame = 0;
  861.     if (BrVector3Dot(&trail->base_heading, &pPursuee->direction) < FastScalarCos(30)) {
  862.         trail->has_deviated_recently = 1;
  863.     }
  864.     BrVector3Sub(&car_to_last_point_v, &trail->trail_nodes[trail->number_of_nodes - 2], &pPursuee->car_master_actor->t.t.translate.t);
  865.     length = BrVector3Length(&car_to_last_point_v);
  866.     if (length < 0.3f) {
  867.         return;
  868.     }
  869.     CalcNegativeXVector(&offset_v, &trail->trail_nodes[trail->number_of_nodes - 2], &pPursuee->car_master_actor->t.t.translate.t, 0.5f);
  870.  
  871.     BrVector3Add(&start1, &trail->trail_nodes[trail->number_of_nodes - 2], &offset_v);
  872.     BrVector3Add(&finish1, &pPursuee->car_master_actor->t.t.translate.t, &offset_v);
  873.     BrVector3Sub(&start2, &trail->trail_nodes[trail->number_of_nodes - 2], &offset_v);
  874.     BrVector3Sub(&finish2, &pPursuee->car_master_actor->t.t.translate.t, &offset_v);
  875.     visible = 1;
  876.     if ((trail->has_deviated_recently
  877.             || !(visible = PointVisibleFromHere(&start1, &finish1))
  878.             || !(visible = PointVisibleFromHere(&start2, &finish2))
  879.             || !(visible = PointVisibleFromHere(&trail->trail_nodes[trail->number_of_nodes - 2], &pPursuee->car_master_actor->t.t.translate.t)))
  880.         && ((visible && length > 2.0f) || (!visible && length > 1.5f))) {
  881.         if (trail->number_of_nodes >= COUNT_OF(trail->trail_nodes)) {
  882.             memmove(trail->trail_nodes, &trail->trail_nodes[1], (COUNT_OF(trail->trail_nodes) - 1) * sizeof(trail->trail_nodes[0]));
  883.             trail->nodes_shifted_this_frame = 1;
  884.         } else {
  885.             trail->number_of_nodes++;
  886.         }
  887.         trail->has_deviated_recently = 0;
  888.         BrVector3Copy(&trail->base_heading, &pPursuee->direction);
  889.     }
  890.     BrVector3Copy(&trail->trail_nodes[trail->number_of_nodes - 1], &pPursuee->car_master_actor->t.t.translate.t);
  891. }
  892.  
  893. // IDA: tS16 __usercall FindNearestTrailSection@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tCar_spec *pPursuee@<EDX>, br_vector3 *pSection_v@<EBX>, br_vector3 *pIntersect@<ECX>, br_scalar *pDistance)
  894. tS16 FindNearestTrailSection(tOpponent_spec* pOpponent_spec, tCar_spec* pPursuee, br_vector3* pSection_v, br_vector3* pIntersect, br_scalar* pDistance) {
  895.     LOG_TRACE("(%p, %p, %p, %p, %p)", pOpponent_spec, pPursuee, pSection_v, pIntersect, pDistance);
  896.  
  897.     return FindNearestGeneralSection(pPursuee, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, pSection_v, pIntersect, pDistance);
  898. }
  899.  
  900. // IDA: tS16 __usercall CalcNextTrailSection@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, int pSection@<EDX>)
  901. tS16 CalcNextTrailSection(tOpponent_spec* pOpponent_spec, int pSection) {
  902.     int section_no;
  903.     tPursuee_trail* trail;
  904.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
  905.  
  906.     trail = &pOpponent_spec->pursue_car_data.pursuee->my_trail;
  907.     section_no = pSection - 15000;
  908.  
  909.     if (trail->number_of_nodes - 2 > section_no) {
  910.         return pSection + 1;
  911.     }
  912.     return -1;
  913. }
  914.  
  915. // IDA: void __usercall ProcessPursueAndTwat(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  916. void ProcessPursueAndTwat(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  917.     tPursue_car_data* data;
  918.     br_vector3 wank;
  919.     br_vector3 section_v;
  920.     br_vector3 intersect;
  921.     br_scalar d;
  922.     br_scalar s;
  923.     br_scalar t;
  924.     br_scalar distance;
  925.     tFollow_path_result res;
  926.     char str[256];
  927.     tS16 section_no;
  928.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  929.  
  930.     data = &pOpponent_spec->pursue_car_data;
  931.     if (pCommand == ePOC_start) {
  932.         dr_dprintf("%s: ProcessPursueAndTwat() - new objective started", pOpponent_spec->car_spec->driver_name);
  933.         data->direct_line_nodes[0].number_of_sections = 1;
  934.         data->direct_line_nodes[0].sections[0] = 10000;
  935.         data->direct_line_nodes[1].number_of_sections = 1;
  936.         data->direct_line_nodes[1].sections[0] = 10000;
  937.         data->direct_line_section.node_indices[0] = 10000;
  938.         data->direct_line_section.node_indices[1] = 10001;
  939.         data->direct_line_section.min_speed[0] = 0;
  940.         data->direct_line_section.min_speed[1] = 0;
  941.         data->direct_line_section.max_speed[0] = -1;
  942.         data->direct_line_section.max_speed[1] = -1;
  943.         data->direct_line_section.width = 0.5f;
  944.         data->direct_line_section.type = ePST_normal;
  945.         data->start_backup_time = 0;
  946.         data->time_of_next_visibility_check = 0;
  947.         data->time_pursuee_last_visible = 0;
  948.         data->time_last_twatted_em = 0;
  949.         data->time_last_away_from_pursuee = gTime_stamp_for_this_munging;
  950.         data->state = ePCS_what_now;
  951.         return;
  952.     }
  953.  
  954.     if (pCommand != ePOC_run) {
  955.         return;
  956.     }
  957.  
  958.     if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec) && pOpponent_spec->distance_from_home > 75.0f) {
  959.         dr_dprintf("%s: Completing pursuit objective because I'm out of my precinct", pOpponent_spec->car_spec->driver_name);
  960.         NewObjective(pOpponent_spec, eOOT_return_to_start);
  961.         return;
  962.     }
  963.  
  964.     data->direct_line_section.length = MAX(pOpponent_spec->player_to_oppo_d, 3.0f);
  965.     if (pOpponent_spec->player_to_oppo_d > 3.0f) {
  966.         data->time_last_away_from_pursuee = gTime_stamp_for_this_munging;
  967.     }
  968.     if (gOpponents[pOpponent_spec->index].psyche.grudge_against_player < 15u) {
  969.         dr_dprintf("%s: Completing pursuit objective because I'm happy now", pOpponent_spec->car_spec->driver_name);
  970.         ObjectiveComplete(pOpponent_spec);
  971.         return;
  972.     }
  973.     if (data->state != ePCS_backing_up) {
  974.         if (data->time_last_twatted_em + 1000 >= gTime_stamp_for_this_munging || data->time_last_twatted_em + 3000 <= gTime_stamp_for_this_munging || BrVector3Length(&data->pursuee->v) >= 0.3f) {
  975.             if (data->time_last_away_from_pursuee + 7000 >= gTime_stamp_for_this_munging || data->time_last_twatted_em + 7000 >= gTime_stamp_for_this_munging || data->start_backup_time + 10000 >= gTime_stamp_for_this_munging) {
  976.                 if (pOpponent_spec->cheating) {
  977.                     if (pOpponent_spec->player_to_oppo_d < 50.0f
  978.                         && PointVisibleFromHere(&data->pursuee->car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
  979.                         data->time_pursuee_last_visible = gTime_stamp_for_this_munging;
  980.                     } else {
  981.                         data->time_pursuee_last_visible = 0;
  982.                     }
  983.                 } else if (pOpponent_spec->player_in_view_now || (data->time_of_next_visibility_check < gTime_stamp_for_this_munging && pOpponent_spec->player_to_oppo_d < 35.0f && PointVisibleFromHere(&data->pursuee->car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t))) {
  984.                     data->time_pursuee_last_visible = gTime_stamp_for_this_munging;
  985.                     data->time_of_next_visibility_check = gTime_stamp_for_this_munging + 600;
  986.                 }
  987.                 if (data->time_pursuee_last_visible + 3000 <= gTime_stamp_for_this_munging) {
  988.                     if (data->pursuee->my_trail.number_of_nodes < 2) {
  989.                         dr_dprintf("%s: Giving up pursuit - not visible & no trail yet", pOpponent_spec->car_spec->driver_name);
  990.                         NewObjective(pOpponent_spec, eOOT_get_near_player);
  991.                         return;
  992.                     }
  993.                     if (data->state != ePCS_following_trail) {
  994.                         section_no = FindNearestTrailSection(pOpponent_spec, data->pursuee, &section_v, &intersect, &distance);
  995.                         data->state = ePCS_following_trail;
  996.                         if (distance > 20.0f || section_no == -1) {
  997.                             dr_dprintf("%s: Giving up pursuit - not visible & trail ain't close enough (%f)", pOpponent_spec->car_spec->driver_name, distance);
  998.                             NewObjective(pOpponent_spec, eOOT_get_near_player);
  999.                             return;
  1000.                         }
  1001.                         dr_dprintf("%s: Commencing ePCS_following_trail state", pOpponent_spec->car_spec->driver_name);
  1002.                         pOpponent_spec->follow_path_data.section_no = section_no;
  1003.                         ProcessFollowPath(pOpponent_spec, ePOC_start, 1, 0, 0);
  1004.                     }
  1005.                 } else if (data->state != ePCS_following_line_of_sight) {
  1006.                     dr_dprintf("%s: Commencing ePCS_following_line_of_sight state", pOpponent_spec->car_spec->driver_name);
  1007.                     data->state = ePCS_following_line_of_sight;
  1008.                     sprintf(str, "%s: I've spotted you!", pOpponent_spec->car_spec->driver_name);
  1009.                     ProcessFollowPath(pOpponent_spec, ePOC_start, 1, 1, 0);
  1010.                 }
  1011.             } else {
  1012.                 dr_dprintf("%s: Backing up because we're too close to pursuee without having twatted him", pOpponent_spec->car_spec->driver_name);
  1013.                 data->start_backup_time = gTime_stamp_for_this_munging;
  1014.                 data->state = ePCS_backing_up;
  1015.             }
  1016.         } else {
  1017.             dr_dprintf("%s: Backing up because we're 'stationary' after colliding with pursuee", pOpponent_spec->car_spec->driver_name);
  1018.             data->start_backup_time = gTime_stamp_for_this_munging;
  1019.             data->state = ePCS_backing_up;
  1020.         }
  1021.     }
  1022.     switch (data->state) {
  1023.     case ePCS_what_now:
  1024.         PDEnterDebugger("ERROR: what_now state called in ProcessPursueAndTwat()");
  1025.         break;
  1026.     case ePCS_following_trail:
  1027.         if (data->pursuee->my_trail.nodes_shifted_this_frame) {
  1028.             if (pOpponent_spec->follow_path_data.section_no <= 15000) {
  1029.                 data->state = ePCS_following_trail;
  1030.                 section_no = FindNearestTrailSection(pOpponent_spec, data->pursuee, &section_v, &intersect, &distance);
  1031.                 dr_dprintf("%s: Trail got away; found new trail section %d", pOpponent_spec->car_spec->driver_name, section_no);
  1032.                 if (section_no == -1 || distance > 20.0f || !PointVisibleFromHere(&intersect, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
  1033.                     dr_dprintf("%s: ...which unfortunately is too far away (%fBRU) or not visible - end of pursuit", pOpponent_spec->car_spec->driver_name, distance);
  1034.                     NewObjective(pOpponent_spec, eOOT_get_near_player);
  1035.                     return;
  1036.                 }
  1037.                 pOpponent_spec->follow_path_data.section_no = section_no;
  1038.                 ProcessFollowPath(pOpponent_spec, ePOC_start, 1, 0, 0);
  1039.             } else {
  1040.                 pOpponent_spec->follow_path_data.section_no--;
  1041.             }
  1042.             dr_dprintf("%s: Following re-jobbied section %d/%d", pOpponent_spec->car_spec->driver_name, pOpponent_spec->follow_path_data.section_no, data->pursuee->my_trail.number_of_nodes - 1);
  1043.         }
  1044.         sprintf(str, "%s: Trail section %d/%d", pOpponent_spec->car_spec->driver_name, pOpponent_spec->follow_path_data.section_no, data->pursuee->my_trail.number_of_nodes - 1);
  1045.         res = ProcessFollowPath(pOpponent_spec, ePOC_run, 1, 0, 0);
  1046.         if (res == eFPR_given_up || res == eFPR_end_of_path) {
  1047.             NewObjective(pOpponent_spec, eOOT_get_near_player);
  1048.         }
  1049.         break;
  1050.     case ePCS_following_line_of_sight:
  1051.         BrVector3Copy(&data->direct_line_nodes[0].p, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
  1052.         BrVector3Sub(&wank, &data->pursuee->car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
  1053.         s = BrVector3Length(&wank);
  1054.         BrVector3Sub(&wank, &data->pursuee->v, &pOpponent_spec->car_spec->v);
  1055.         t = BrVector3Length(&wank);
  1056.         if (t >= 1.0f) {
  1057.             d = s / t / 2.0;
  1058.  
  1059.         } else {
  1060.             d = 0.0;
  1061.         }
  1062.         BrVector3Scale(&data->direct_line_nodes[1].p, &data->pursuee->v, d);
  1063.         BrVector3Accumulate(&data->direct_line_nodes[1].p, &data->pursuee->car_master_actor->t.t.translate.t);
  1064.         if (s >= 2.0f) {
  1065.             ProcessFollowPath(pOpponent_spec, ePOC_run, 1, 1, 0);
  1066.         } else {
  1067.             ProcessFollowPath(pOpponent_spec, ePOC_run, 1, 1, 1);
  1068.         }
  1069.         break;
  1070.     case ePCS_backing_up:
  1071.         if (data->start_backup_time + 2200 >= gTime_stamp_for_this_munging) {
  1072.             pOpponent_spec->car_spec->curvature = 0.0f;
  1073.             pOpponent_spec->car_spec->brake_force = 0.0f;
  1074.             pOpponent_spec->car_spec->acc_force = pOpponent_spec->car_spec->M * -8.0f;
  1075.         } else {
  1076.             pOpponent_spec->car_spec->acc_force = 0.0;
  1077.             pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
  1078.             if (data->start_backup_time + 3000 < gTime_stamp_for_this_munging) {
  1079.                 pOpponent_spec->car_spec->brake_force = 0.0f;
  1080.                 data->state = ePCS_what_now;
  1081.                 dr_dprintf("%s: Finished backing up.", pOpponent_spec->car_spec->driver_name);
  1082.             }
  1083.         }
  1084.         break;
  1085.     default:
  1086.         return;
  1087.     }
  1088. }
  1089.  
  1090. // IDA: void __usercall ProcessRunAway(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  1091. void ProcessRunAway(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  1092.     //int res; // Pierre-Marie Baty -- unused variable
  1093.     tS16 section_no;
  1094.     br_scalar distance;
  1095.     br_vector3 intersect;
  1096.     br_vector3 direction_v;
  1097.     char str[256];
  1098.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  1099.  
  1100.     switch (pCommand) {
  1101.  
  1102.     case ePOC_run:
  1103.         if (pOpponent_spec->run_away_data.time_to_stop >= gTime_stamp_for_this_munging) {
  1104.             if (pOpponent_spec->follow_path_data.section_no > 20000) {
  1105.                 ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
  1106.                 pOpponent_spec->follow_path_data.section_no = 20000;
  1107.             }
  1108.             if (pOpponent_spec->nnext_sections < 10) {
  1109.                 TopUpRandomRoute(pOpponent_spec, 10 - pOpponent_spec->nnext_sections);
  1110.             }
  1111.             if (ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0) == eFPR_given_up) {
  1112.                 ClearOpponentsProjectedRoute(pOpponent_spec);
  1113.                 section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
  1114.                 if (BrVector3Dot(&pOpponent_spec->car_spec->direction, &direction_v) < 0.0f) {
  1115.                     AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 0);
  1116.                 } else {
  1117.                     AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
  1118.                 }
  1119.                 TopUpRandomRoute(pOpponent_spec, -1);
  1120.                 ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  1121.             }
  1122.         } else {
  1123.             ObjectiveComplete(pOpponent_spec);
  1124.         }
  1125.         break;
  1126.  
  1127.     case ePOC_start:
  1128.         dr_dprintf("%s: ProcessRunAway() - new objective started", pOpponent_spec->car_spec->driver_name);
  1129.         pOpponent_spec->run_away_data.time_to_stop = gTime_stamp_for_this_munging + 1000 * IRandomBetween(30, 90);
  1130.         ClearOpponentsProjectedRoute(pOpponent_spec);
  1131.         section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
  1132.         if (BrVector3Dot(&pOpponent_spec->car_spec->direction, &direction_v) < 0.0f) {
  1133.             AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 0);
  1134.         } else {
  1135.             AddToOpponentsProjectedRoute(pOpponent_spec, section_no, 1);
  1136.         }
  1137.         TopUpRandomRoute(pOpponent_spec, -1);
  1138.         ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  1139.         sprintf(str, "%s: Shit! I'm out of here...", pOpponent_spec->car_spec->driver_name);
  1140.         break;
  1141.  
  1142.     case ePOC_die:
  1143.         break;
  1144.     }
  1145. }
  1146.  
  1147. // IDA: void __usercall ProcessWaitForSomeHaplessSod(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  1148. void ProcessWaitForSomeHaplessSod(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  1149.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  1150.  
  1151.     switch (pCommand) {
  1152.     case ePOC_start:
  1153.     case ePOC_run:
  1154.         pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
  1155.         break;
  1156.     default:
  1157.         break;
  1158.     }
  1159. }
  1160.  
  1161. // IDA: void __usercall ProcessReturnToStart(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  1162. void ProcessReturnToStart(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  1163.     br_vector3 section_v;
  1164.     br_vector3 our_pos_xz;
  1165.     br_vector3 cop_to_start;
  1166.     br_scalar distance;
  1167.     int res;
  1168.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  1169.  
  1170.     switch (pCommand) {
  1171.     case ePOC_run:
  1172.         if (TeleportCopToStart(pOpponent_spec)) {
  1173.             break;
  1174.         }
  1175.         if (pOpponent_spec->return_to_start_data.waiting_near_start) {
  1176.             pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
  1177.         } else {
  1178.             our_pos_xz = pOpponent_spec->car_spec->car_master_actor->t.t.translate.t;
  1179.             our_pos_xz.v[1] = 0.0f;
  1180.             BrVector3Sub(&cop_to_start, &pOpponent_spec->start_pos, &our_pos_xz);
  1181.             if (BrVector3Length(&cop_to_start) >= 10.0) {
  1182.                 if (pOpponent_spec->follow_path_data.section_no > 20000) {
  1183.                     ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
  1184.                     pOpponent_spec->follow_path_data.section_no = 20000;
  1185.                 }
  1186.                 if (pOpponent_spec->nnext_sections <= 4) {
  1187.                     CalcReturnToStartPointRoute(pOpponent_spec);
  1188.                 }
  1189.                 res = ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0);
  1190.                 if (res == eFPR_given_up || res == eFPR_end_of_path) {
  1191.                     if (res == eFPR_given_up) {
  1192.                         dr_dprintf("%s: Restarting return_to_start route because ProcessFollowPath() gave up.", pOpponent_spec->car_spec->driver_name);
  1193.                     } else {
  1194.                         dr_dprintf("%s: Restarting return_to_start route because ran out of path!", pOpponent_spec->car_spec->driver_name);
  1195.                     }
  1196.                     ClearOpponentsProjectedRoute(pOpponent_spec);
  1197.                     CalcReturnToStartPointRoute(pOpponent_spec);
  1198.                     ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  1199.                 }
  1200.             } else {
  1201.                 pOpponent_spec->return_to_start_data.waiting_near_start = 1;
  1202.                 pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
  1203.             }
  1204.         }
  1205.         break;
  1206.     case ePOC_start:
  1207.         dr_dprintf("%s: ProcessReturnToStart() - new objective started", pOpponent_spec->car_spec->driver_name);
  1208.         pOpponent_spec->return_to_start_data.waiting_near_start = 0;
  1209.         pOpponent_spec->return_to_start_data.section_no = FindNearestPathSection(&pOpponent_spec->start_pos, &section_v, &pOpponent_spec->return_to_start_data.nearest_path_point, &distance);
  1210.         pOpponent_spec->return_to_start_data.nearest_path_point.v[1] = 0.0;
  1211.         CalcReturnToStartPointRoute(pOpponent_spec);
  1212.         ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  1213.         break;
  1214.     default:
  1215.         break;
  1216.     }
  1217. }
  1218.  
  1219. // IDA: void __usercall ProcessLevitate(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  1220. void ProcessLevitate(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  1221.     float t;
  1222.     //float terminal_time; // Pierre-Marie Baty -- unused variable
  1223.     float y;
  1224.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  1225.  
  1226.     if (pCommand == ePOC_start) {
  1227.         dr_dprintf("%s: ProcessLevitate() - new objective started", pOpponent_spec->car_spec->driver_name);
  1228.         pOpponent_spec->levitate_data.waiting_to_levitate = 1;
  1229.         pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
  1230.         pOpponent_spec->car_spec->acc_force = 0.f;
  1231.         pOpponent_spec->levitate_data.time_started = gTime_stamp_for_this_munging;
  1232.     } else if (pCommand == ePOC_run) {
  1233.         if (pOpponent_spec->levitate_data.waiting_to_levitate) {
  1234.             if ((BrVector3Length(&pOpponent_spec->car_spec->v) < .01f && BrVector3Length(&pOpponent_spec->car_spec->omega) < 1.f) || gTime_stamp_for_this_munging - pOpponent_spec->levitate_data.time_started > 4000) {
  1235.                 pOpponent_spec->levitate_data.waiting_to_levitate = 0;
  1236.                 pOpponent_spec->levitate_data.time_started = gTime_stamp_for_this_munging;
  1237.                 pOpponent_spec->levitate_data.initial_y = pOpponent_spec->car_spec->car_master_actor->t.t.translate.t.v[1];
  1238.                 if (pOpponent_spec->car_spec->has_been_stolen) {
  1239.                     NewTextHeadupSlot(eHeadupSlot_misc, 250, 2500, -4, GetMiscString(kMiscString_CarAddedToChangeCarList));
  1240.                 }
  1241.             } else {
  1242.                 pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
  1243.                 pOpponent_spec->car_spec->acc_force = 0.f;
  1244.                 BrVector3InvScale(&pOpponent_spec->car_spec->omega, &pOpponent_spec->car_spec->omega,
  1245.                     powf(gFrame_period_for_this_munging / 1000.f, 2.f));
  1246.             }
  1247.         }
  1248.         if (!pOpponent_spec->levitate_data.waiting_to_levitate) {
  1249.             TurnOpponentPhysicsOff(pOpponent_spec);
  1250.             t = (gTime_stamp_for_this_munging - pOpponent_spec->levitate_data.time_started) / 1000.f;
  1251.             if (t < 20.f) {
  1252.                 y = .5f * t * t / 2.f;
  1253.             } else {
  1254.                 y = 10.f * (t - 20.f) + 100.f;
  1255.             }
  1256.             pOpponent_spec->car_spec->car_master_actor->t.t.translate.t.v[1] = pOpponent_spec->levitate_data.initial_y + y;
  1257.             if (y > 200.f) {
  1258.                 pOpponent_spec->finished_for_this_race = 1;
  1259.             }
  1260.         }
  1261.     }
  1262. }
  1263.  
  1264. // IDA: void __usercall ProcessGetNearPlayer(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  1265. void ProcessGetNearPlayer(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  1266.     //br_vector3* initial_pos; // Pierre-Marie Baty -- unused variable
  1267.     //br_actor* car_actor; // Pierre-Marie Baty -- unused variable
  1268.     int res;
  1269.     char str[256];
  1270.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  1271.  
  1272.     if (pCommand == ePOC_start) {
  1273.         dr_dprintf("%s: ProcessGetNearPlayer() - new objective started", pOpponent_spec->car_spec->driver_name);
  1274.         ClearOpponentsProjectedRoute(pOpponent_spec);
  1275.         CalcGetNearPlayerRoute(pOpponent_spec, &gProgram_state.current_car);
  1276.         ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  1277.         return;
  1278.     }
  1279.     if (pCommand == ePOC_run) {
  1280.         if ((pOpponent_spec->car_spec->car_ID & 0xff00) == 768 && pOpponent_spec->distance_from_home > 75.0) {
  1281.             dr_dprintf("%s: Completing get_near objective because I'm out of my precinct", pOpponent_spec->car_spec->driver_name);
  1282.             NewObjective(pOpponent_spec, eOOT_return_to_start);
  1283.             return;
  1284.         }
  1285.         if (pOpponent_spec->follow_path_data.section_no > 20000) {
  1286.             if (pOpponent_spec->player_to_oppo_d < 10.0 || pOpponent_spec->follow_path_data.section_no == pOpponent_spec->players_section_when_last_calced_full_path) {
  1287.                 dr_dprintf("%s: ProcessGetNearPlayer() - giving up 'cos got to player's section", pOpponent_spec->car_spec->driver_name);
  1288.                 ObjectiveComplete(pOpponent_spec);
  1289.                 return;
  1290.             }
  1291.             ShiftOpponentsProjectedRoute(pOpponent_spec, pOpponent_spec->follow_path_data.section_no - 20000);
  1292.             pOpponent_spec->follow_path_data.section_no = 20000;
  1293.         }
  1294.         if (pOpponent_spec->nnext_sections <= 4) {
  1295.             CalcGetNearPlayerRoute(pOpponent_spec, &gProgram_state.current_car);
  1296.         }
  1297.         res = ProcessFollowPath(pOpponent_spec, ePOC_run, 0, 0, 0);
  1298.         sprintf(str, "Get near: %d", GetOpponentsRealSection(pOpponent_spec, pOpponent_spec->follow_path_data.section_no));
  1299.  
  1300.         if (res == eFPR_given_up) {
  1301.             NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1302.         } else if (res == eFPR_end_of_path) {
  1303.             dr_dprintf("%s: Restarting get_near_player route because ran out of path!", pOpponent_spec->car_spec->driver_name);
  1304.             ClearOpponentsProjectedRoute(pOpponent_spec);
  1305.             CalcGetNearPlayerRoute(pOpponent_spec, &gProgram_state.current_car);
  1306.             ProcessFollowPath(pOpponent_spec, ePOC_start, 0, 0, 0);
  1307.         }
  1308.     }
  1309. }
  1310.  
  1311. // IDA: void __usercall ProcessFrozen(tOpponent_spec *pOpponent_spec@<EAX>, tProcess_objective_command pCommand@<EDX>)
  1312. void ProcessFrozen(tOpponent_spec* pOpponent_spec, tProcess_objective_command pCommand) {
  1313.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCommand);
  1314.  
  1315.     switch (pCommand) {
  1316.     case ePOC_start:
  1317.         dr_dprintf("%d ProcessFrozen() - new task started", pOpponent_spec->index);
  1318.         dr_dprintf("%s: Rematerialising from ePOC_start in ProcessFrozen()...", pOpponent_spec->car_spec->driver_name);
  1319.         RematerialiseOpponentOnNearestSection(pOpponent_spec, 0.f);
  1320.         pOpponent_spec->car_spec->acc_force = 0.f;
  1321.         pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
  1322.         break;
  1323.     case ePOC_run:
  1324.         pOpponent_spec->car_spec->brake_force = 15.f * pOpponent_spec->car_spec->M;
  1325.         break;
  1326.     case ePOC_die:
  1327.         pOpponent_spec->car_spec->brake_force = 0.f;
  1328.         break;
  1329.     }
  1330. }
  1331.  
  1332. // IDA: int __usercall HeadOnWithPlayerPossible@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
  1333. int HeadOnWithPlayerPossible(tOpponent_spec* pOpponent_spec) {
  1334.     br_vector3 oppo_to_player_norm;
  1335.     LOG_TRACE("(%p)", pOpponent_spec);
  1336.  
  1337.     oppo_to_player_norm.v[0] = gProgram_state.current_car.car_master_actor->t.t.mat.m[3][0]
  1338.         - pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][0];
  1339.     oppo_to_player_norm.v[1] = gProgram_state.current_car.car_master_actor->t.t.mat.m[3][1]
  1340.         - pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][1];
  1341.     oppo_to_player_norm.v[2] = gProgram_state.current_car.car_master_actor->t.t.mat.m[3][2]
  1342.         - pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][2];
  1343.  
  1344.     BrVector3Normalise(&oppo_to_player_norm, &oppo_to_player_norm);
  1345.     if (gHead_on_cos_value >= BrVector3Dot(&pOpponent_spec->car_spec->direction, &pOpponent_spec->car_spec->direction)
  1346.         || -gHead_on_cos_value <= BrVector3Dot(&pOpponent_spec->car_spec->direction, &pOpponent_spec->car_spec->direction)) {
  1347.         return 0;
  1348.     }
  1349.     dr_dprintf("HOORAY! Head-on imminent");
  1350.     return 1;
  1351. }
  1352.  
  1353. // IDA: int __usercall AlreadyPursuingCar@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tCar_spec *pPursuee@<EDX>)
  1354. int AlreadyPursuingCar(tOpponent_spec* pOpponent_spec, tCar_spec* pPursuee) {
  1355.     LOG_TRACE("(%p, %p)", pOpponent_spec, pPursuee);
  1356.  
  1357.     return pOpponent_spec->current_objective == eOOT_pursue_and_twat && pOpponent_spec->pursue_car_data.pursuee == pPursuee;
  1358. }
  1359.  
  1360. // IDA: int __usercall LastTwatteeAPlayer@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
  1361. int LastTwatteeAPlayer(tOpponent_spec* pOpponent_spec) {
  1362.     LOG_TRACE("(%p)", pOpponent_spec);
  1363.  
  1364.     return pOpponent_spec->car_spec->last_person_we_hit && pOpponent_spec->car_spec->last_person_we_hit->driver == eDriver_local_human;
  1365. }
  1366.  
  1367. // IDA: int __usercall LastTwatterAPlayer@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
  1368. int LastTwatterAPlayer(tOpponent_spec* pOpponent_spec) {
  1369.     LOG_TRACE("(%p)", pOpponent_spec);
  1370.  
  1371.     return pOpponent_spec->car_spec->last_person_to_hit_us && pOpponent_spec->car_spec->last_person_to_hit_us->driver == eDriver_local_human;
  1372. }
  1373.  
  1374. // IDA: void __usercall ObjectiveComplete(tOpponent_spec *pOpponent_spec@<EAX>)
  1375. void ObjectiveComplete(tOpponent_spec* pOpponent_spec) {
  1376.     LOG_TRACE("(%p)", pOpponent_spec);
  1377.  
  1378.     dr_dprintf("%s: Objective Completed", pOpponent_spec->car_spec->driver_name);
  1379.     pOpponent_spec->new_objective_required = 1;
  1380.     switch (pOpponent_spec->current_objective) {
  1381.     case eOOT_complete_race:
  1382.         gNum_of_opponents_completing_race--;
  1383.         break;
  1384.     case eOOT_pursue_and_twat:
  1385.         gNum_of_opponents_pursuing--;
  1386.         break;
  1387.     case eOOT_get_near_player:
  1388.         gNum_of_opponents_getting_near--;
  1389.         break;
  1390.     default:
  1391.         break;
  1392.     }
  1393. }
  1394.  
  1395. // IDA: void __usercall TeleportOpponentToNearestSafeLocation(tOpponent_spec *pOpponent_spec@<EAX>)
  1396. void TeleportOpponentToNearestSafeLocation(tOpponent_spec* pOpponent_spec) {
  1397.     tS16 section_no;
  1398.     tU8 section_direction;
  1399.     br_scalar distance;
  1400.     br_vector3 direction_v;
  1401.     br_vector3 intersect;
  1402.     int section_counter;
  1403.     int found_safe_place;
  1404.     LOG_TRACE("(%p)", pOpponent_spec);
  1405.  
  1406.     found_safe_place = 0;
  1407.     section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
  1408.     if (section_no < 0) {
  1409.         return;
  1410.     }
  1411.     pOpponent_spec->last_in_view = 0;
  1412.     TurnOpponentPhysicsOff(pOpponent_spec);
  1413.     section_direction = BrVector3Dot(&pOpponent_spec->car_spec->direction, &direction_v) < 0.0f;
  1414.     ClearOpponentsProjectedRoute(pOpponent_spec);
  1415.     AddToOpponentsProjectedRoute(pOpponent_spec, section_no, section_direction);
  1416.     TopUpRandomRoute(pOpponent_spec, -1);
  1417.     section_counter = 1;
  1418.     while (!found_safe_place) {
  1419.         BrVector3Copy(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[section_counter].section_no].node_indices[pOpponent_spec->next_sections[section_counter].direction]].p);
  1420.         CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(pOpponent_spec);
  1421.         if (pOpponent_spec->player_to_oppo_d > gIn_view_distance) {
  1422.             found_safe_place = 1;
  1423.         }
  1424.         section_counter++;
  1425.         if (pOpponent_spec->nnext_sections <= section_counter) {
  1426.             ShiftOpponentsProjectedRoute(pOpponent_spec, section_counter - 1);
  1427.             section_counter = 0;
  1428.             TopUpRandomRoute(pOpponent_spec, -1);
  1429.         }
  1430.     }
  1431. }
  1432.  
  1433. // IDA: void __usercall ChooseNewObjective(tOpponent_spec *pOpponent_spec@<EAX>, int pMust_choose_one@<EDX>)
  1434. void ChooseNewObjective(tOpponent_spec* pOpponent_spec, int pMust_choose_one) {
  1435.     char str[255];
  1436.     //tS16 players_section; // Pierre-Marie Baty -- unused variable
  1437.     //br_vector3 wank; // Pierre-Marie Baty -- unused variable
  1438.     //br_vector3 player_to_oppo_v; // Pierre-Marie Baty -- unused variable
  1439.     //br_vector3 section_v; // Pierre-Marie Baty -- unused variable
  1440.     //br_vector3 intersect; // Pierre-Marie Baty -- unused variable
  1441.     //br_scalar dot; // Pierre-Marie Baty -- unused variable
  1442.     //br_scalar distance; // Pierre-Marie Baty -- unused variable
  1443.     int do_it;
  1444.     //int i; // Pierre-Marie Baty -- unused variable
  1445.     //int j; // Pierre-Marie Baty -- unused variable
  1446.     int pursuit_percentage;
  1447.     int percentage;
  1448.     int general_grudge_increase;
  1449.     LOG_TRACE("(%p, %d)", pOpponent_spec, pMust_choose_one);
  1450.  
  1451.     // v3 = pMust_choose_one;
  1452.     if (pOpponent_spec->current_objective == eOOT_knackered_and_freewheeling || pOpponent_spec->knackeredness_detected) {
  1453.         return;
  1454.     }
  1455.     if (gTime_stamp_for_this_munging > pOpponent_spec->next_out_of_world_check) {
  1456.         pOpponent_spec->next_out_of_world_check = gTime_stamp_for_this_munging + 500;
  1457.         if (HasCarFallenOffWorld(pOpponent_spec->car_spec)) {
  1458.             if (pOpponent_spec->car_spec->last_time_we_touched_a_player <= gTime_stamp_for_this_munging - 7000) {
  1459.                 TeleportOpponentToNearestSafeLocation(pOpponent_spec);
  1460.                 NewObjective(pOpponent_spec, eOOT_complete_race);
  1461.             } else {
  1462.                 TurnOpponentPhysicsOff(pOpponent_spec);
  1463.                 pOpponent_spec->finished_for_this_race = 1;
  1464.                 KnackerThisCar(pOpponent_spec->car_spec);
  1465.                 pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[3][1] -= 1000.0f;
  1466.             }
  1467.             return;
  1468.         }
  1469.     }
  1470.     if (pOpponent_spec->car_spec->knackered && !pOpponent_spec->knackeredness_detected) {
  1471.         pOpponent_spec->knackeredness_detected = 1;
  1472.         dr_dprintf("%s: Knackered - dealing with appropriately", pOpponent_spec->car_spec->driver_name);
  1473.         if (pOpponent_spec->car_spec->has_been_stolen) {
  1474.             NewObjective(pOpponent_spec, eOOT_levitate);
  1475.         } else {
  1476.             NewObjective(pOpponent_spec, eOOT_knackered_and_freewheeling);
  1477.         }
  1478.         return;
  1479.     }
  1480.     if (pOpponent_spec->current_objective == eOOT_frozen) {
  1481.         if (CAR_SPEC_GET_SPEED_FACTOR(pOpponent_spec->car_spec) != 0.0f) {
  1482.             dr_dprintf("%s: Time to unfreeze", pOpponent_spec->car_spec->driver_name);
  1483.             if (pOpponent_spec->pursuing_player_before_freeze == 1) {
  1484.                 NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1485.             } else {
  1486.                 NewObjective(pOpponent_spec, eOOT_get_near_player);
  1487.             }
  1488.         }
  1489.         return;
  1490.     } else {
  1491.         if (CAR_SPEC_GET_SPEED_FACTOR(pOpponent_spec->car_spec) == 0.0f) {
  1492.             dr_dprintf("%s: Decided to freeze", pOpponent_spec->car_spec->driver_name);
  1493.             if (pOpponent_spec->current_objective == eOOT_pursue_and_twat && pOpponent_spec->pursue_car_data.pursuee == &gProgram_state.current_car) {
  1494.                 pOpponent_spec->pursuing_player_before_freeze = 1;
  1495.             } else {
  1496.                 pOpponent_spec->pursuing_player_before_freeze = 0;
  1497.             }
  1498.             NewObjective(pOpponent_spec, eOOT_frozen);
  1499.             return;
  1500.         }
  1501.         if (!gFirst_frame) {
  1502.             general_grudge_increase = (pOpponent_spec->nastiness * 40.0f + 10.0f);
  1503.             if (pOpponent_spec->car_spec->scary_bang && pOpponent_spec->player_to_oppo_d < 10.0f) {
  1504.                 if (pOpponent_spec->current_objective == eOOT_pursue_and_twat) {
  1505.                     percentage = 40;
  1506.                 } else {
  1507.                     percentage = 0;
  1508.                 }
  1509.                 if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
  1510.                     if (PercentageChance(20)) {
  1511.                         dr_dprintf("%s: Decided to run away", pOpponent_spec->car_spec->driver_name);
  1512.                         NewObjective(pOpponent_spec, eOOT_run_away);
  1513.                         return;
  1514.                     }
  1515.                 } else if (PercentageChance((percentage + 60) - pOpponent_spec->nastiness * 50.0)) {
  1516.                     dr_dprintf("%s: Decided to run away", pOpponent_spec->car_spec->driver_name);
  1517.                     NewObjective(pOpponent_spec, eOOT_run_away);
  1518.                     return;
  1519.                 }
  1520.             }
  1521.             if (!gMellow_opponents && (pOpponent_spec->current_objective != eOOT_run_away || pOpponent_spec->time_this_objective_started + 15000 <= gTime_stamp_for_this_munging)) {
  1522.                 if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec) && pOpponent_spec->murder_reported && pOpponent_spec->player_to_oppo_d < 20.0f && !AlreadyPursuingCar(pOpponent_spec, &gProgram_state.current_car)) {
  1523.                     gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
  1524.                     sprintf(str, "%s: Furderous melon!", pOpponent_spec->car_spec->driver_name);
  1525.                     dr_dprintf("%s: Decided to pursue after MURDER", pOpponent_spec->car_spec->driver_name);
  1526.                     NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1527.                     return;
  1528.                 }
  1529.                 if (pOpponent_spec->car_spec->big_bang && LastTwatterAPlayer(pOpponent_spec) && !AlreadyPursuingCar(pOpponent_spec, pOpponent_spec->car_spec->last_person_to_hit_us)) {
  1530.                     // v4 = gOpponents[pOpponent_spec->index].psyche.grudge_against_player;
  1531.                     // if (v4 <= 20) {
  1532.                     //     v4 = 20;
  1533.                     // }
  1534.                     // v5 = general_grudge_increase + v4;
  1535.                     // if (v5 >= 100) {
  1536.                     //     LOBYTE(v5) = 100;
  1537.                     // }
  1538.                     gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
  1539.                     sprintf(str, "%s: Christ! What was that?", pOpponent_spec->car_spec->driver_name);
  1540.                     dr_dprintf("%s: Decided to pursue after big bang; last person to twat us was %s", pOpponent_spec->car_spec->driver_name, pOpponent_spec->car_spec->last_person_to_hit_us->driver_name);
  1541.                     NewObjective(pOpponent_spec, eOOT_pursue_and_twat, pOpponent_spec->car_spec->last_person_to_hit_us);
  1542.                     return;
  1543.                 }
  1544.                 if (LastTwatteeAPlayer(pOpponent_spec) && !AlreadyPursuingCar(pOpponent_spec, pOpponent_spec->car_spec->last_person_we_hit)) {
  1545.                     // v6 = gOpponents[pOpponent_spec->index].psyche.grudge_against_player;
  1546.                     // if (v6 <= 20) {
  1547.                     //     v6 = 20;
  1548.                     // }
  1549.                     // v7 = general_grudge_increase + v6;
  1550.                     // if (v7 >= 100) {
  1551.                     //     LOBYTE(v7) = 100;
  1552.                     // }
  1553.                     gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
  1554.                     sprintf(str, "%s: Ha! Bet you weren't expecting that!", pOpponent_spec->car_spec->driver_name);
  1555.                     dr_dprintf("%s: Decided to pursue %s after accidentally hitting them", pOpponent_spec->car_spec->driver_name, pOpponent_spec->car_spec->last_person_we_hit->driver_name);
  1556.                     NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1557.                     return;
  1558.                 }
  1559.                 if (!AlreadyPursuingCar(pOpponent_spec, &gProgram_state.current_car)) {
  1560.                     if (pOpponent_spec->car_spec->grudge_raised_recently && (!CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec) || pOpponent_spec->player_to_oppo_d <= 20.0) && LastTwatterAPlayer(pOpponent_spec) && gOpponents[pOpponent_spec->index].psyche.grudge_against_player > 20) {
  1561.                         // v8 = gOpponents[pOpponent_spec->index].psyche.grudge_against_player;
  1562.                         // if (v8 <= 20) {
  1563.                         //     v8 = 20;
  1564.                         // }
  1565.                         // v9 = general_grudge_increase + v8;
  1566.                         // if (v9 >= 100) {
  1567.                         //     LOBYTE(v9) = 100;
  1568.                         // }
  1569.                         gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
  1570.                         sprintf(str, "%s: Right! That's enough, %s!", pOpponent_spec->car_spec->driver_name, gProgram_state.current_car.driver_name);
  1571.                         dr_dprintf("%s: Decided to pursue after grudginess raised; last person to twat us was %s", pOpponent_spec->car_spec->driver_name, pOpponent_spec->car_spec->last_person_to_hit_us->driver_name);
  1572.                         NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1573.                         return;
  1574.                     }
  1575.  
  1576.                     if ((pOpponent_spec->player_in_view_now) != 0 && (pOpponent_spec->acknowledged_piv) == 0) {
  1577.                         pOpponent_spec->acknowledged_piv = 1;
  1578.                         if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
  1579.                             pursuit_percentage = (BrVector3Length(&gProgram_state.current_car.v) - gDefinite_no_cop_pursuit_speed) * gCop_pursuit_speed_percentage_multiplier;
  1580.                         } else if (gProgram_state.skill_level + 3 > gNum_of_opponents_pursuing) {
  1581.                             pursuit_percentage = gOpponents[pOpponent_spec->index].psyche.grudge_against_player - 20 + pOpponent_spec->nastiness * 30.f;
  1582.                         } else {
  1583.                             pursuit_percentage = 0;
  1584.                         }
  1585.  
  1586.                         pursuit_percentage += 50 * HeadOnWithPlayerPossible(pOpponent_spec);
  1587.                         do_it = PercentageChance(pursuit_percentage);
  1588.                         dr_dprintf("%s: Spotted player; chance of pursuing %d%%: %s", pOpponent_spec->car_spec->driver_name, pursuit_percentage, do_it ? "YES, Decided to pursue" : "NO, Decided NOT to pursue");
  1589.                         if (do_it) {
  1590.                             gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + general_grudge_increase);
  1591.                             sprintf(str, "%s: I've decided to kill you for the fun of it", pOpponent_spec->car_spec->driver_name);
  1592.                             NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1593.                             return;
  1594.                         }
  1595.                     }
  1596.                 }
  1597.             }
  1598.         }
  1599.         if (!pMust_choose_one) {
  1600.             return;
  1601.         }
  1602.         dr_dprintf("%s: Choosing new objective because we have to...", pOpponent_spec->car_spec->driver_name);
  1603.         if (pOpponent_spec->has_moved_at_some_point) {
  1604.             if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
  1605.                 NewObjective(pOpponent_spec, eOOT_return_to_start);
  1606.                 return;
  1607.             }
  1608.             if (gNum_of_opponents_pursuing + gNum_of_opponents_getting_near >= 3 || pOpponent_spec->player_to_oppo_d <= 10.0) {
  1609.                 if (gNum_of_opponents_completing_race >= 2) {
  1610.                     pursuit_percentage = pOpponent_spec->player_to_oppo_d - 15.0f;
  1611.                     if (PercentageChance(pursuit_percentage)) {
  1612.                         dr_dprintf("%s: Choosing to get_near because chance dictated it (%d%%)", pOpponent_spec->car_spec->driver_name, pursuit_percentage);
  1613.                         NewObjective(pOpponent_spec, eOOT_get_near_player);
  1614.                         return;
  1615.                     } else {
  1616.                         dr_dprintf("%s: Choosing to complete_race because chance dictated it (%d%%)", pOpponent_spec->car_spec->driver_name, pursuit_percentage);
  1617.                     }
  1618.                 } else {
  1619.                     dr_dprintf("%s: Choosing to complete_race because not enough oppos are yet (%d/%d)", pOpponent_spec->car_spec->driver_name, gNum_of_opponents_completing_race, 2);
  1620.                 }
  1621.                 NewObjective(pOpponent_spec, eOOT_complete_race);
  1622.                 return;
  1623.             }
  1624.             dr_dprintf("%s: Choosing to get_near because not enough oppos are yet (%d/%d)", pOpponent_spec->car_spec->driver_name, gNum_of_opponents_pursuing + gNum_of_opponents_getting_near, 3);
  1625.             NewObjective(pOpponent_spec, eOOT_get_near_player);
  1626.             return;
  1627.         }
  1628.         if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
  1629.             NewObjective(pOpponent_spec, eOOT_wait_for_some_hapless_sod);
  1630.             return;
  1631.         }
  1632.         if (!pOpponent_spec->pursue_from_start || gMellow_opponents) {
  1633.             NewObjective(pOpponent_spec, eOOT_complete_race);
  1634.             return;
  1635.         }
  1636.  
  1637.         gOpponents[pOpponent_spec->index].psyche.grudge_against_player = MIN(100, pOpponent_spec->nastiness * 40.0 + (MAX(20, gOpponents[pOpponent_spec->index].psyche.grudge_against_player) + 20));
  1638.         NewObjective(pOpponent_spec, eOOT_pursue_and_twat, &gProgram_state.current_car);
  1639.     }
  1640. }
  1641.  
  1642. // IDA: void __usercall ProcessThisOpponent(tOpponent_spec *pOpponent_spec@<EAX>)
  1643. void ProcessThisOpponent(tOpponent_spec* pOpponent_spec) {
  1644.     //int i; // Pierre-Marie Baty -- unused variable
  1645.     LOG_TRACE("(%p)", pOpponent_spec);
  1646.  
  1647.     if ((gMap_mode && gShow_opponents) || pOpponent_spec->last_in_view + 3000 >= gTime_stamp_for_this_munging) {
  1648.         if (pOpponent_spec->cheating) {
  1649.             OiStopCheating(pOpponent_spec);
  1650.         }
  1651.     } else if (pOpponent_spec->cheating == 0) {
  1652.         StartToCheat(pOpponent_spec);
  1653.     }
  1654.     ChooseNewObjective(pOpponent_spec, pOpponent_spec->new_objective_required);
  1655.     pOpponent_spec->new_objective_required = 0;
  1656.     if (gCountdown || gRace_finished) {
  1657.         pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 10.f;
  1658.     }
  1659.     if (!pOpponent_spec->finished_for_this_race && !gStop_opponents_moving && !gRace_finished && pOpponent_spec->stun_time_ends < gTime_stamp_for_this_munging) {
  1660.         ProcessCurrentObjective(pOpponent_spec, ePOC_run);
  1661.     }
  1662.     if (pOpponent_spec->cheating) {
  1663.         BrVector3Copy(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
  1664.     }
  1665. }
  1666.  
  1667. // IDA: int __usercall IsNetCarActive@<EAX>(br_vector3 *pPoint@<EAX>)
  1668. int IsNetCarActive(br_vector3* pPoint) {
  1669.     br_vector3 tv;
  1670.     LOG_TRACE("(%p)", pPoint);
  1671.  
  1672.     BrVector3Sub(&tv, &gProgram_state.current_car.car_master_actor->t.t.translate.t, pPoint);
  1673.     if (BrVector3LengthSquared(&tv) < 100.f) {
  1674.         return 1;
  1675.     }
  1676.     if (gCar_to_view != &gProgram_state.current_car) {
  1677.         BrVector3Sub(&tv, &gCar_to_view->car_master_actor->t.t.translate.t, pPoint);
  1678.         return BrVector3LengthSquared(&tv) < 100.f;
  1679.     }
  1680.     return 0;
  1681. }
  1682.  
  1683. // IDA: void __cdecl RebuildActiveCarList()
  1684. void RebuildActiveCarList(void) {
  1685.     int i;
  1686.     tCar_spec* car_spec;
  1687.     LOG_TRACE("()");
  1688.  
  1689.     if (gActive_car_list_rebuild_required) {
  1690.         gActive_car_list_rebuild_required = 0;
  1691.         gNum_active_cars = 0;
  1692.  
  1693.         if (!gProgram_state.current_car.disabled || gAction_replay_mode) {
  1694.             gActive_car_list[gNum_active_cars] = &gProgram_state.current_car;
  1695.             gNum_active_cars++;
  1696.             gProgram_state.current_car.active = 1;
  1697.         }
  1698.  
  1699.         if (gNet_mode == eNet_mode_host) {
  1700.             for (i = 0; i < GetCarCount(eVehicle_net_player); i++) {
  1701.                 car_spec = GetCarSpec(eVehicle_net_player, i);
  1702.                 if (car_spec->disabled) {
  1703.                     car_spec->active = 0;
  1704.                 } else {
  1705.                     gActive_car_list[gNum_active_cars] = car_spec;
  1706.                     gNum_active_cars++;
  1707.                     car_spec->active = 1;
  1708.                 }
  1709.             }
  1710.         } else if (gNet_mode == eNet_mode_client) {
  1711.             for (i = 0; i < GetCarCount(eVehicle_net_player); i++) {
  1712.                 car_spec = GetCarSpec(eVehicle_net_player, i);
  1713.                 if (car_spec->disabled || !IsNetCarActive(&car_spec->car_master_actor->t.t.translate.t)) {
  1714.                     car_spec->active = 0;
  1715.                 } else {
  1716.                     gActive_car_list[gNum_active_cars] = car_spec;
  1717.                     gNum_active_cars++;
  1718.                     car_spec->active = 1;
  1719.                 }
  1720.             }
  1721.         }
  1722.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  1723.             car_spec = GetCarSpec(eVehicle_opponent, i);
  1724.             if (gProgram_state.AI_vehicles.opponents[i].physics_me || gAction_replay_mode) {
  1725.                 gActive_car_list[gNum_active_cars] = car_spec;
  1726.                 gNum_active_cars++;
  1727.                 car_spec->active = 1;
  1728.             } else {
  1729.                 car_spec->active = 0;
  1730.             }
  1731.         }
  1732.         for (i = 0; gNumber_of_cops_before_faffage > i; ++i) {
  1733.             car_spec = GetCarSpec(eVehicle_rozzer, i);
  1734.             if (gProgram_state.AI_vehicles.cops[i].physics_me || gAction_replay_mode) {
  1735.                 gActive_car_list[gNum_active_cars] = car_spec;
  1736.                 gNum_active_cars++;
  1737.                 car_spec->active = 1;
  1738.             }
  1739.         }
  1740.     }
  1741. }
  1742.  
  1743. // IDA: void __cdecl ForceRebuildActiveCarList()
  1744. void ForceRebuildActiveCarList(void) {
  1745.     LOG_TRACE("()");
  1746.  
  1747.     gActive_car_list_rebuild_required = 1;
  1748.     if (gProgram_state.racing) {
  1749.         RebuildActiveCarList();
  1750.     }
  1751. }
  1752.  
  1753. // IDA: void __usercall StartToCheat(tOpponent_spec *pOpponent_spec@<EAX>)
  1754. void StartToCheat(tOpponent_spec* pOpponent_spec) {
  1755.     LOG_TRACE("(%p)", pOpponent_spec);
  1756.  
  1757.     dr_dprintf("%s: StartToCheat() - Starting to cheat", pOpponent_spec->car_spec->driver_name);
  1758.     pOpponent_spec->cheating = 1;
  1759.     if ((pOpponent_spec->car_spec->car_ID & 0xff00) == 0x300) {
  1760.         dr_dprintf("%s: StartToCheat() - Turning physics OFF", pOpponent_spec->car_spec->driver_name);
  1761.         TurnOpponentPhysicsOff(pOpponent_spec);
  1762.         RebuildActiveCarList();
  1763.     }
  1764. }
  1765.  
  1766. // IDA: void __usercall OiStopCheating(tOpponent_spec *pOpponent_spec@<EAX>)
  1767. void OiStopCheating(tOpponent_spec* pOpponent_spec) {
  1768.     LOG_TRACE("(%p)", pOpponent_spec);
  1769.  
  1770.     dr_dprintf("%s: OiStopCheating() - End of cheating sesh", pOpponent_spec->car_spec->driver_name);
  1771.     pOpponent_spec->cheating = 0;
  1772.     if ((pOpponent_spec->car_spec->car_ID & 0xff00) == 0x300) {
  1773.         dr_dprintf("%s: OiStopCheating() - Turning physics ON", pOpponent_spec->car_spec->driver_name);
  1774.         TurnOpponentPhysicsOn(pOpponent_spec);
  1775.         RebuildActiveCarList();
  1776.     }
  1777. }
  1778.  
  1779. // IDA: int __usercall TeleportCopToStart@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
  1780. int TeleportCopToStart(tOpponent_spec* pOpponent_spec) {
  1781.     br_vector3 wank;
  1782.     LOG_TRACE("(%p)", pOpponent_spec);
  1783.  
  1784.     if (!pOpponent_spec->cheating || !CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
  1785.         return 0;
  1786.     }
  1787.     BrVector3Sub(&wank, &gProgram_state.current_car.car_master_actor->t.t.translate.t, &pOpponent_spec->start_pos);
  1788.     if (BrVector3Length(&wank) <= gIn_view_distance) {
  1789.         return 0;
  1790.     }
  1791.     pOpponent_spec->car_spec->car_master_actor->t.t.euler.t = pOpponent_spec->start_pos;
  1792.     PointActorAlongThisBloodyVector(pOpponent_spec->car_spec->car_master_actor, &pOpponent_spec->start_direction);
  1793.     pOpponent_spec->physics_me = 0;
  1794.     RematerialiseOpponent(pOpponent_spec, 0.0);
  1795.     TurnOpponentPhysicsOff(pOpponent_spec);
  1796.     RebuildActiveCarList();
  1797.     NewObjective(pOpponent_spec, eOOT_wait_for_some_hapless_sod);
  1798.     return 1;
  1799. }
  1800.  
  1801. // IDA: void __usercall CalcDistanceFromHome(tOpponent_spec *pOpponent_spec@<EAX>)
  1802. void CalcDistanceFromHome(tOpponent_spec* pOpponent_spec) {
  1803.     br_vector3 wank;
  1804.     LOG_TRACE("(%p)", pOpponent_spec);
  1805.  
  1806.     BrVector3Sub(&wank, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &pOpponent_spec->start_pos);
  1807.     pOpponent_spec->distance_from_home = BrVector3Length(&wank);
  1808. }
  1809.  
  1810. // IDA: int __usercall MassageOpponentPosition@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pMassage_count@<EDX>)
  1811. int MassageOpponentPosition(tOpponent_spec* pOpponent_spec, int pMassage_count) {
  1812.     br_matrix34* mat;
  1813.     br_vector3* car_trans;
  1814.     br_vector3 displacement;
  1815.     br_vector3 positive_y_vector;
  1816.     br_vector3 direction_v;
  1817.     LOG_TRACE("(%p, %d)", pOpponent_spec, pMassage_count);
  1818.  
  1819.     BrVector3Set(&positive_y_vector, 0.f, 1.f, 0.f);
  1820.     mat = &pOpponent_spec->car_spec->car_master_actor->t.t.mat;
  1821.     car_trans = &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t;
  1822.     if (pMassage_count > 22) {
  1823.         return 0;
  1824.     } else if (pMassage_count > 20) {
  1825.         car_trans->v[1] += (pMassage_count - 20) * 2.0f;
  1826.         return 1;
  1827.     } else {
  1828.         direction_v.v[0] = -pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2][0];
  1829.         direction_v.v[1] = -pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2][1];
  1830.         direction_v.v[2] = -pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2][2];
  1831.         if (pMassage_count % 4 >= 2) {
  1832.             BrVector3Cross(&displacement, &positive_y_vector, &direction_v);
  1833.             BrVector3Normalise(&displacement, &displacement);
  1834.             BrVector3Scale(&displacement, &displacement, (pMassage_count / 4) * 0.1f);
  1835.         } else {
  1836.             BrVector3Normalise(&displacement, &direction_v);
  1837.             BrVector3Scale(&displacement, &displacement, (pMassage_count / 4) * 0.5f);
  1838.         }
  1839.         if (pMassage_count % 2) {
  1840.             BrVector3Negate(&displacement, &displacement);
  1841.         }
  1842.         BrVector3Accumulate(car_trans, &displacement);
  1843.         return 1;
  1844.     }
  1845. }
  1846.  
  1847. // IDA: int __usercall RematerialiseOpponentOnThisSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, br_scalar pSpeed, tS16 pSection_no)
  1848. int RematerialiseOpponentOnThisSection(tOpponent_spec* pOpponent_spec, br_scalar pSpeed, tS16 pSection_no) {
  1849.     br_vector3* start;
  1850.     br_vector3* finish;
  1851.     br_vector3 a;
  1852.     br_vector3 p;
  1853.     br_vector3 section_v;
  1854.     br_vector3 car_to_end;
  1855.     //br_vector3 intersect; // Pierre-Marie Baty -- unused variable
  1856.     br_scalar t;
  1857.     //br_scalar distance_to_end; // Pierre-Marie Baty -- unused variable
  1858.     //br_scalar length; // Pierre-Marie Baty -- unused variable
  1859.     LOG_TRACE("(%p, %f, %d)", pOpponent_spec, pSpeed, pSection_no);
  1860.  
  1861.     if (pOpponent_spec->physics_me) {
  1862.         dr_dprintf("%s: Actually, we're already materialised", pOpponent_spec->car_spec->driver_name);
  1863.         return 1;
  1864.     }
  1865.     start = GetOpponentsSectionStartNodePoint(pOpponent_spec, pSection_no);
  1866.     finish = GetOpponentsSectionFinishNodePoint(pOpponent_spec, pSection_no);
  1867.     BrVector3Sub(&section_v, finish, start);
  1868.     if (BrVector3Length(&section_v) != 0.f) {
  1869.         BrVector3Sub(&a, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, start);
  1870.         t = BrVector3Dot(&section_v, &a) / BrVector3Dot(&section_v, &section_v);
  1871.         if (t < 0.f) {
  1872.             BrVector3Copy(&p, start);
  1873.         } else if (t > 1.f) {
  1874.             BrVector3Copy(&p, finish);
  1875.         } else {
  1876.             p.v[0] = start->v[0] + t * section_v.v[0];
  1877.             p.v[1] = start->v[1] + t * section_v.v[1];
  1878.             p.v[2] = start->v[2] + t * section_v.v[2];
  1879.         }
  1880.         BrVector3Copy(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &p);
  1881.         BrVector3Sub(&a, finish, start);
  1882.         PointActorAlongThisBloodyVector(pOpponent_spec->car_spec->car_master_actor, &a);
  1883.     }
  1884.     if (!RematerialiseOpponent(pOpponent_spec, pSpeed)) {
  1885.         return 0;
  1886.     }
  1887.     BrVector3Sub(&car_to_end, finish, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
  1888.     pOpponent_spec->car_spec->brake_force = 0.f;
  1889.     pOpponent_spec->car_spec->acc_force = 0.f;
  1890.     if (BrVector3Length(&car_to_end) >= 5.f) {
  1891.         pOpponent_spec->car_spec->acc_force = pOpponent_spec->car_spec->M / 2.f;
  1892.     } else {
  1893.         pOpponent_spec->car_spec->acc_force = 15.f * pOpponent_spec->car_spec->M;
  1894.     }
  1895.     pOpponent_spec->last_in_view = gTime_stamp_for_this_munging;
  1896.     return 1;
  1897. }
  1898.  
  1899. // IDA: int __usercall RematerialiseOpponentOnNearestSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, br_scalar pSpeed)
  1900. int RematerialiseOpponentOnNearestSection(tOpponent_spec* pOpponent_spec, br_scalar pSpeed) {
  1901.     br_vector3 intersect;
  1902.     br_vector3 direction_v;
  1903.     br_vector3 car_to_end;
  1904.     //br_vector3* start; // Pierre-Marie Baty -- unused variable
  1905.     br_vector3* finish;
  1906.     br_scalar distance;
  1907.     br_scalar distance_to_end;
  1908.     tS16 section_no;
  1909.     LOG_TRACE("(%p, %f)", pOpponent_spec, pSpeed);
  1910.  
  1911.     if (pOpponent_spec->physics_me) {
  1912.         dr_dprintf("%s: Actually, we're already materialised", pOpponent_spec->car_spec->driver_name);
  1913.         return 1;
  1914.     }
  1915.     section_no = FindNearestPathSection(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &direction_v, &intersect, &distance);
  1916.     finish = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p;
  1917.     BrVector3Copy(&pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &intersect);
  1918.     PointActorAlongThisBloodyVector(pOpponent_spec->car_spec->car_master_actor, &direction_v);
  1919.     BrVector3Sub(&car_to_end, finish, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t);
  1920.     if (RematerialiseOpponent(pOpponent_spec, pSpeed)) {
  1921.         pOpponent_spec->car_spec->brake_force = 0.0f;
  1922.         pOpponent_spec->car_spec->acc_force = 0.0f;
  1923.  
  1924.         distance_to_end = BrVector3Length(&car_to_end);
  1925.         if (distance_to_end >= 5.0f) {
  1926.             pOpponent_spec->car_spec->acc_force = pOpponent_spec->car_spec->M / 2.0f;
  1927.         } else {
  1928.             pOpponent_spec->car_spec->brake_force = pOpponent_spec->car_spec->M * 15.0f;
  1929.         }
  1930.     }
  1931.     return 0;
  1932. }
  1933.  
  1934. // IDA: int __usercall RematerialiseOpponent@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, br_scalar pSpeed)
  1935. int RematerialiseOpponent(tOpponent_spec* pOpponent_spec, br_scalar pSpeed) {
  1936.     static int count;
  1937.     static int total;
  1938.     static int highest;
  1939.     int this_total;
  1940.     br_matrix34* mat;
  1941.     br_matrix34 original_mat;
  1942.     br_vector3 a;
  1943.     br_vector3 b;
  1944.     br_vector3 norm;
  1945.     br_vector3 norm2;
  1946.     br_scalar dist;
  1947.     br_scalar dist2;
  1948.     //br_scalar ts; // Pierre-Marie Baty -- unused variable
  1949.     br_angle phi;
  1950.     int i;
  1951.     //int j; // Pierre-Marie Baty -- unused variable
  1952.     int massage_count;
  1953.     //br_angle theta; // Pierre-Marie Baty -- unused variable
  1954.     int sensible_place;
  1955.     LOG_TRACE("(%p, %f)", pOpponent_spec, pSpeed);
  1956.  
  1957.     this_total = 0;
  1958.     mat = &pOpponent_spec->car_spec->car_master_actor->t.t.mat;
  1959.     massage_count = 0;
  1960.     phi = BrDegreeToAngle(90) - BrRadianToAngle(atan2f(mat->m[2][2], mat->m[2][0]));
  1961.     if (pOpponent_spec->physics_me) {
  1962.         dr_dprintf("%s: Actually, we're already materialised", pOpponent_spec->car_spec->driver_name);
  1963.     } else {
  1964.         total++;
  1965.         BrMatrix34Copy(&original_mat, mat);
  1966.         TurnOpponentPhysicsOn(pOpponent_spec);
  1967.         RebuildActiveCarList();
  1968.         while (1) {
  1969.             count++;
  1970.             BrVector3Scale((br_vector3*)mat->m[3], (br_vector3*)mat->m[3], WORLD_SCALE);
  1971.             BrVector3Copy(&b, (br_vector3*)mat->m[3]);
  1972.             BrMatrix34RotateY(mat, phi);
  1973.             BrVector3Copy((br_vector3*)mat->m[3], &b);
  1974.             BrVector3SetFloat(&b, 0.f, -100.f, 0.f);
  1975.             BrVector3Copy(&a, (br_vector3*)mat->m[3]);
  1976.             a.v[1] += 1.f;
  1977.             findfloor(&a, &b, &norm, &dist);
  1978.             a.v[1] += 100.f;
  1979.             findfloor(&a, &b, &norm2, &dist2);
  1980.             if (dist2 <= 1.f) {
  1981.                 BrVector3SetFloat(&b, 0.f, -5.01f, 0.f);
  1982.                 a.v[1] -= 100.f;
  1983.                 for (i = 0; i < 20; i++) {
  1984.                     a.v[1] += 5.f;
  1985.                     findfloor(&a, &b, &norm2, &dist2);
  1986.                     if (dist2 <= 1.f) {
  1987.                         break;
  1988.                     }
  1989.                 }
  1990.                 dist2 = (i + 1) * 0.05f - dist2 / 20.f;
  1991.             }
  1992.             if (dist2 < dist) {
  1993.                 dist = -dist2;
  1994.                 BrVector3Copy(&norm, &norm2);
  1995.             }
  1996.             if (fabsf(dist) <= 1.f) {
  1997.                 mat->m[3][1] -= dist * 100.f - 2.f;
  1998.                 BrMatrix34PreRotateX(mat, BrRadianToAngle(asinf(BrVector3Dot((br_vector3*)mat->m[2], &norm))));
  1999.                 BrMatrix34PreRotateZ(mat, BrRadianToAngle(asinf(BrVector3Dot((br_vector3*)mat->m[2], &norm))));
  2000.             }
  2001.             BrVector3Negate(&pOpponent_spec->car_spec->direction, (br_vector3*)mat->m[2]);
  2002.             BrMatrix34ApplyP(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->cmpos, mat);
  2003.             BrVector3InvScale(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->pos, WORLD_SCALE);
  2004.             BrVector3InvScale((br_vector3*)mat->m[3], (br_vector3*)mat->m[3], WORLD_SCALE);
  2005.             BrVector3Copy(&pOpponent_spec->car_spec->v, (br_vector3*)pOpponent_spec->car_spec->car_master_actor->t.t.mat.m[2]);
  2006.             BrVector3Negate(&pOpponent_spec->car_spec->v, &pOpponent_spec->car_spec->v);
  2007.             BrVector3Normalise(&pOpponent_spec->car_spec->v, &pOpponent_spec->car_spec->v);
  2008.             BrVector3Scale(&pOpponent_spec->car_spec->v, &pOpponent_spec->car_spec->v, pSpeed * WORLD_SCALE);
  2009.             BrVector3Set(&pOpponent_spec->car_spec->omega, 0.f, 0.f, 0.f);
  2010.             BrMatrix34Copy(&pOpponent_spec->car_spec->oldmat, mat);
  2011.             BrMatrix34Copy(&pOpponent_spec->car_spec->old_frame_mat, mat);
  2012.             BrVector3Scale((br_vector3*)pOpponent_spec->car_spec->oldmat.m[3], (br_vector3*)pOpponent_spec->car_spec->oldmat.m[3], WORLD_SCALE);
  2013.             for (i = 0; i < COUNT_OF(pOpponent_spec->car_spec->oldd); i++) {
  2014.                 pOpponent_spec->car_spec->oldd[i] = pOpponent_spec->car_spec->ride_height;
  2015.             }
  2016.             pOpponent_spec->car_spec->gear = 0;
  2017.             pOpponent_spec->car_spec->revs = 0.f;
  2018.             pOpponent_spec->car_spec->traction_control = 1;
  2019.             BrMatrix34ApplyP(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->cmpos, mat);
  2020.             BrVector3InvScale(&pOpponent_spec->car_spec->pos, &pOpponent_spec->car_spec->pos, WORLD_SCALE);
  2021.             BrVector3Negate(&pOpponent_spec->car_spec->direction, (br_vector3*)pOpponent_spec->car_spec->oldmat.m[3]);
  2022.             pOpponent_spec->car_spec->box_face_ref = gFace_num__car - 2;
  2023.             pOpponent_spec->car_spec->doing_nothing_flag = 0;
  2024.             sensible_place = TestForCarInSensiblePlace(pOpponent_spec->car_spec);
  2025.             if (sensible_place) {
  2026.                 break;
  2027.             } else {
  2028.                 BrMatrix34Copy(mat, &original_mat);
  2029.             }
  2030.             if (!MassageOpponentPosition(pOpponent_spec, massage_count++)) {
  2031.                 break;
  2032.             }
  2033.             this_total++;
  2034.         }
  2035.         count--;
  2036.         if (sensible_place) {
  2037.             dr_dprintf("%s: Rematerialised (took %d attempts, orig. pos. (%7.3f,%7.3f,%7.3f), actual pos. (%7.3f,%7.3f,%7.3f))",
  2038.                 pOpponent_spec->car_spec->driver_name,
  2039.                 this_total + 1,
  2040.                 original_mat.m[3][0], original_mat.m[3][1], original_mat.m[3][2],
  2041.                 mat->m[3][0], mat->m[3][1], mat->m[3][2]);
  2042.         }
  2043.         if (this_total > highest) {
  2044.             highest = this_total;
  2045.         }
  2046.         if (count != 0) {
  2047.             dr_dprintf("MassageOpponentPosition() called an avg of %.1f times (max %d) per ReMaterialisation",
  2048.                 count / total, highest);
  2049.         }
  2050.         if (sensible_place) {
  2051.             ResetCarSpecialVolume((tCollision_info*)pOpponent_spec->car_spec);
  2052.         } else {
  2053.             TurnOpponentPhysicsOff(pOpponent_spec);
  2054.             RebuildActiveCarList();
  2055.             TeleportOpponentToNearestSafeLocation(pOpponent_spec);
  2056.         }
  2057.     }
  2058.     return 1;
  2059. }
  2060.  
  2061. // IDA: void __usercall CalcPlayerConspicuousness(tOpponent_spec *pOpponent_spec@<EAX>)
  2062. void CalcPlayerConspicuousness(tOpponent_spec* pOpponent_spec) {
  2063.     br_vector3 pos_in_cop_space;
  2064.     br_matrix34 inverse_transform;
  2065.     LOG_TRACE("(%p)", pOpponent_spec);
  2066.  
  2067.     if (pOpponent_spec->next_player_visibility_check >= gTime_stamp_for_this_munging) {
  2068.         return;
  2069.     }
  2070.     pOpponent_spec->player_in_view_now = 0;
  2071.     if (CAR_SPEC_IS_ROZZER(pOpponent_spec->car_spec)) {
  2072.         pOpponent_spec->next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 100;
  2073.         if (pOpponent_spec->player_to_oppo_d < 20.f) {
  2074.             BrMatrix34LPInverse(&inverse_transform, &pOpponent_spec->car_spec->car_master_actor->t.t.mat);
  2075.             BrMatrix34ApplyP(&pos_in_cop_space, &gProgram_state.current_car.car_master_actor->t.t.translate.t, &inverse_transform);
  2076.             if (pos_in_cop_space.v[2] < 0.f && PointVisibleFromHere(&gProgram_state.current_car.car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
  2077.                 pOpponent_spec->player_in_view_now = 1;
  2078.                 pOpponent_spec->acknowledged_piv = 0;
  2079.             }
  2080.         }
  2081.     } else {
  2082.         pOpponent_spec->next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 6000;
  2083.         dr_dprintf("%s: Time now: %9.2f; next vis check at %9.2f", pOpponent_spec->car_spec->driver_name, gTime_stamp_for_this_munging / 1000.f, pOpponent_spec->next_player_visibility_check / 1000.0f);
  2084.         if (pOpponent_spec->player_to_oppo_d < 50.f && PointVisibleFromHere(&gProgram_state.current_car.car_master_actor->t.t.translate.t, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t)) {
  2085.             pOpponent_spec->player_in_view_now = 1;
  2086.             pOpponent_spec->acknowledged_piv = 0;
  2087.         }
  2088.     }
  2089. }
  2090.  
  2091. // IDA: void __usercall CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(tOpponent_spec *pOpponent_spec@<EAX>)
  2092. void CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(tOpponent_spec* pOpponent_spec) {
  2093.     LOG_TRACE("(%p)", pOpponent_spec);
  2094.  
  2095.     BrVector3Sub(&pOpponent_spec->player_to_oppo_v, &pOpponent_spec->car_spec->car_master_actor->t.t.translate.t, &gProgram_state.current_car.car_master_actor->t.t.translate.t);
  2096.     pOpponent_spec->player_to_oppo_d = BrVector3Length(&pOpponent_spec->player_to_oppo_v);
  2097.     if (pOpponent_spec->player_to_oppo_d < gIn_view_distance) {
  2098.         pOpponent_spec->last_in_view = gTime_stamp_for_this_munging;
  2099.     }
  2100. }
  2101.  
  2102. // IDA: void __usercall ChallengeOccurred(int pChallenger_index@<EAX>, int pAccepted@<EDX>)
  2103. void ChallengeOccurred(int pChallenger_index, int pAccepted) {
  2104.     LOG_TRACE("(%d, %d)", pChallenger_index, pAccepted);
  2105.  
  2106.     if (pAccepted) {
  2107.         gChallenger_index__opponent = pChallenger_index;
  2108.     }
  2109. }
  2110.  
  2111. // IDA: void __cdecl LoadCopCars()
  2112. void LoadCopCars(void) {
  2113.     int i;
  2114.     LOG_TRACE("()");
  2115.  
  2116.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  2117.         PossibleService();
  2118.         gProgram_state.AI_vehicles.cops[i].car_spec = BrMemAllocate(sizeof(tCar_spec), kMem_cop_car_spec);
  2119.         LoadCar(
  2120.             gBIG_APC_index == i ? "BIGAPC.TXT" : "APC.TXT",
  2121.             eDriver_oppo,
  2122.             gProgram_state.AI_vehicles.cops[i].car_spec,
  2123.             (gBIG_APC_index == i) ? 4 : 3,
  2124.             "The Cops",
  2125.             &gTheir_cars_storage_space);
  2126.     }
  2127. }
  2128.  
  2129. // IDA: void __usercall LoadInOppoPaths(FILE *pF@<EAX>)
  2130. void LoadInOppoPaths(FILE* pF) {
  2131.     char s[256];
  2132.     char* res;
  2133.     int data_errors;
  2134.     int section_no;
  2135.     //int node_no; // Pierre-Marie Baty -- unused variable
  2136.     int i;
  2137.     br_scalar x;
  2138.     br_scalar y;
  2139.     br_scalar z;
  2140.     br_scalar scalars[8];
  2141.     br_scalar distance;
  2142.     tPath_node* node_ptr;
  2143.     br_vector3 section_v;
  2144.     //br_vector3 positive_y_vector; // Pierre-Marie Baty -- unused variable
  2145.     br_vector3 intersect;
  2146.     br_vector3 cop_to_section;
  2147.     int j;
  2148.     int sections_to_delete;
  2149.     int delete_these[1024];
  2150.  
  2151.     //float x_0; // Pierre-Marie Baty -- unused variable
  2152.     //float x_1; // Pierre-Marie Baty -- unused variable
  2153.     //float x_2; // Pierre-Marie Baty -- unused variable
  2154.  
  2155.     LOG_TRACE("(%p)", pF);
  2156.  
  2157.     data_errors = 0;
  2158.     sections_to_delete = 0;
  2159.     dr_dprintf("Start of LoadInOppoPaths()...");
  2160.     gProgram_state.AI_vehicles.number_of_path_nodes = 0;
  2161.     gProgram_state.AI_vehicles.number_of_path_sections = 0;
  2162.     gProgram_state.AI_vehicles.path_nodes = 0;
  2163.     gProgram_state.AI_vehicles.path_sections = 0;
  2164.     gBit_per_node = 0;
  2165.     gBIG_APC_index = -1;
  2166.     do {
  2167.         res = GetALineAndDontArgue(pF, s);
  2168.     } while (res && strcmp("START OF OPPONENT PATHS", s) != 0);
  2169.     if (res) {
  2170.         ReallocExtraPathNodes(GetAnInt(pF));
  2171.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
  2172.             GetThreeFloats(pF, &gProgram_state.AI_vehicles.path_nodes[i].p.v[0], &gProgram_state.AI_vehicles.path_nodes[i].p.v[1], &gProgram_state.AI_vehicles.path_nodes[i].p.v[2]);
  2173.             gProgram_state.AI_vehicles.path_nodes[i].number_of_sections = 0;
  2174.         }
  2175.         ReallocExtraPathSections(GetAnInt(pF));
  2176.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
  2177.             PossibleService();
  2178.             GetNScalars(pF, 8, scalars);
  2179.             gProgram_state.AI_vehicles.path_sections[i].node_indices[0] = scalars[0];
  2180.             gProgram_state.AI_vehicles.path_sections[i].node_indices[1] = scalars[1];
  2181.             gProgram_state.AI_vehicles.path_sections[i].min_speed[0] = scalars[2];
  2182.             gProgram_state.AI_vehicles.path_sections[i].max_speed[0] = scalars[3];
  2183.             gProgram_state.AI_vehicles.path_sections[i].min_speed[1] = scalars[4];
  2184.             gProgram_state.AI_vehicles.path_sections[i].max_speed[1] = scalars[5];
  2185.             gProgram_state.AI_vehicles.path_sections[i].width = scalars[6];
  2186.             x = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[0]
  2187.                 - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[0];
  2188.             y = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[1]
  2189.                 - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[1];
  2190.             z = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[2]
  2191.                 - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[2];
  2192.  
  2193.             gProgram_state.AI_vehicles.path_sections[i].length = sqrtf(x * x + y * y + z * z);
  2194.             if (scalars[7] < 1000.0f) {
  2195.                 gProgram_state.AI_vehicles.path_sections[i].type = (tU8)scalars[7];
  2196.                 gProgram_state.AI_vehicles.path_sections[i].one_way = 0;
  2197.             } else {
  2198.                 gProgram_state.AI_vehicles.path_sections[i].type = (tU8)(scalars[7] - 1000.0f);
  2199.                 gProgram_state.AI_vehicles.path_sections[i].one_way = 1;
  2200.             }
  2201.             for (j = 0; j < 2; j++) {
  2202.                 node_ptr = &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[j]];
  2203.                 if (node_ptr->number_of_sections >= 8u) {
  2204.                     dr_dprintf(
  2205.                         "ERROR: Too many sections (including section #%d) attached to node #%d",
  2206.                         i,
  2207.                         gProgram_state.AI_vehicles.path_sections[i].node_indices[j]);
  2208.                     data_errors = 1;
  2209.                 } else {
  2210.                     node_ptr->sections[node_ptr->number_of_sections] = (tS16)i;
  2211.                     node_ptr->number_of_sections++;
  2212.                 }
  2213.             }
  2214.             x = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[0]
  2215.                 - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[0];
  2216.             y = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[1]
  2217.                 - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[1];
  2218.             z = gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p.v[2]
  2219.                 - gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p.v[2];
  2220.             if (z * z + x * x + y * y == 0.0f) {
  2221.                 dr_dprintf(
  2222.                     "ERROR: Opponent path section #%d has zero length (nodes #%d and #%d are in same position). Secti"
  2223.                     "on and one node will be deleted.",
  2224.                     j,
  2225.                     gProgram_state.AI_vehicles.path_sections[i].node_indices[0],
  2226.                     gProgram_state.AI_vehicles.path_sections[i].node_indices[1]);
  2227.                 delete_these[sections_to_delete] = j;
  2228.                 sections_to_delete++;
  2229.             }
  2230.         }
  2231.  
  2232.         if (data_errors) {
  2233.             PDFatalError("Opponent path data inconsistencies. Unable to rectumify them.");
  2234.         }
  2235.         if (sections_to_delete != 0) {
  2236.             for (j = 0; j < sections_to_delete; j++) {
  2237.                 dr_dprintf("Deleting section #%d (was #%d)", delete_these[j], j + delete_these[j]);
  2238.                 DeleteSection(delete_these[j]);
  2239.                 DeleteOrphanNodes();
  2240.                 for (section_no = j; section_no < sections_to_delete; section_no++) {
  2241.                     delete_these[j]--;
  2242.                 }
  2243.             }
  2244.             WriteOutOppoPaths();
  2245.             sprintf(
  2246.                 s,
  2247.                 "Errors in opponent path data. All have been corrected and written out to '%s'. Refer to diagnostic file "
  2248.                 "for more details.",
  2249.                 gOppo_path_filename);
  2250.             PDFatalError(s);
  2251.         }
  2252.         if (gAusterity_mode || gNet_mode != eNet_mode_none) {
  2253.             gProgram_state.AI_vehicles.number_of_cops = GetAnInt(pF);
  2254.             for (j = 0; j < gProgram_state.AI_vehicles.number_of_cops; j++) {
  2255.                 GetALineAndDontArgue(pF, s);
  2256.             }
  2257.             gProgram_state.AI_vehicles.number_of_cops = 0;
  2258.         } else {
  2259.             gProgram_state.AI_vehicles.number_of_cops = GetAnInt(pF);
  2260.             for (j = 0; j < gProgram_state.AI_vehicles.number_of_cops; j++) {
  2261.                 PossibleService();
  2262.                 GetNScalars(pF, 6, scalars);
  2263.                 BrVector3Set(&gProgram_state.AI_vehicles.cop_start_points[j], scalars[0], scalars[1], scalars[2]);
  2264.  
  2265.                 if (scalars[3] == 9.0f && scalars[4] == 9.0f && scalars[5] == 9.0f) {
  2266.                     gBIG_APC_index = j;
  2267.                 }
  2268.  
  2269.                 FindNearestPathSection(&gProgram_state.AI_vehicles.cop_start_points[j], &cop_to_section, &intersect, &distance);
  2270.                 BrVector3Set(&gProgram_state.AI_vehicles.cop_start_vectors[j],
  2271.                     cop_to_section.v[2] * 1.0f - cop_to_section.v[1] * 0.0f,
  2272.                     cop_to_section.v[0] * 0.0f - cop_to_section.v[2] * 0.0f,
  2273.                     cop_to_section.v[1] * 0.0f - cop_to_section.v[0] * 1.0f);
  2274.                 BrVector3Sub(&section_v, &intersect, &gProgram_state.AI_vehicles.cop_start_points[j]);
  2275.                 if (BrVector3Dot(&gProgram_state.AI_vehicles.cop_start_vectors[j], &section_v) < 0.0f) {
  2276.                     BrVector3Negate(&gProgram_state.AI_vehicles.cop_start_vectors[j], &gProgram_state.AI_vehicles.cop_start_vectors[j]);
  2277.                 }
  2278.             }
  2279.         }
  2280.         do {
  2281.             GetALineAndDontArgue(pF, s);
  2282.         } while (strcmp("END OF OPPONENT PATHS", s) != 0);
  2283.         if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
  2284.             gBit_per_node = BrMemAllocate((gProgram_state.AI_vehicles.number_of_path_nodes + 7) / 8, kMem_oppo_bit_per_node);
  2285.         } else {
  2286.             gBit_per_node = NULL;
  2287.         }
  2288.         dr_dprintf("End of LoadInOppoPaths(), totals:");
  2289.         dr_dprintf("Nodes: %d", gProgram_state.AI_vehicles.number_of_path_nodes);
  2290.         dr_dprintf("Sections: %d", gProgram_state.AI_vehicles.number_of_path_sections);
  2291.         ConsistencyCheck();
  2292.     }
  2293. }
  2294.  
  2295. // IDA: void __cdecl DisposeOpponentPaths()
  2296. void DisposeOpponentPaths(void) {
  2297.     LOG_TRACE("()");
  2298.  
  2299.     if (gProgram_state.AI_vehicles.path_nodes != NULL) {
  2300.         BrMemFree(gProgram_state.AI_vehicles.path_nodes);
  2301.     }
  2302.     if (gProgram_state.AI_vehicles.path_sections != NULL) {
  2303.         BrMemFree(gProgram_state.AI_vehicles.path_sections);
  2304.     }
  2305.     if (gBit_per_node != NULL) {
  2306.         BrMemFree(gBit_per_node);
  2307.     }
  2308.     gBit_per_node = NULL;
  2309.     gProgram_state.AI_vehicles.number_of_path_nodes = 0;
  2310.     gProgram_state.AI_vehicles.number_of_path_sections = 0;
  2311.     gProgram_state.AI_vehicles.path_nodes = NULL;
  2312.     gProgram_state.AI_vehicles.path_sections = NULL;
  2313. }
  2314.  
  2315. // IDA: void __usercall MungeOpponents(tU32 pFrame_period@<EAX>)
  2316. void MungeOpponents(tU32 pFrame_period) {
  2317.     int i;
  2318.     int un_stun_flag;
  2319.     LOG_TRACE("(%d)", pFrame_period);
  2320.  
  2321.     un_stun_flag = 0;
  2322.  
  2323.     if (gProgram_state.AI_vehicles.number_of_opponents == 0 && gNumber_of_cops_before_faffage == 0) {
  2324.         return;
  2325.     }
  2326.     gAcme_frame_count++;
  2327.     gTime_stamp_for_this_munging = GetTotalTime();
  2328.     gFrame_period_for_this_munging = pFrame_period;
  2329.     gFrame_period_for_this_munging_in_secs = pFrame_period / 1000.f;
  2330.     if (!gAcknowledged_start && !gCountdown) {
  2331.         gAcknowledged_start = 1;
  2332.         if (!gStart_jumped) {
  2333.             un_stun_flag = 1;
  2334.         }
  2335.     }
  2336.     if (gProgram_state.current_car.no_of_processes_recording_my_trail == 0) {
  2337.         StartRecordingTrail(&gProgram_state.current_car);
  2338.     } else {
  2339.         RecordNextTrailNode(&gProgram_state.current_car);
  2340.     }
  2341.     TrackElasticateyPath();
  2342.     if (gProcessing_opponents) {
  2343.         gNum_of_opponents_pursuing = 0;
  2344.         gNum_of_opponents_getting_near = 0;
  2345.         gNum_of_opponents_completing_race = 0;
  2346.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  2347.             if (!gProgram_state.AI_vehicles.opponents[i].finished_for_this_race) {
  2348.                 switch (gProgram_state.AI_vehicles.opponents[i].current_objective) {
  2349.                 case eOOT_pursue_and_twat:
  2350.                     gNum_of_opponents_pursuing++;
  2351.                     break;
  2352.                 case eOOT_get_near_player:
  2353.                     gNum_of_opponents_getting_near++;
  2354.                     break;
  2355.                 case eOOT_complete_race:
  2356.                     gNum_of_opponents_completing_race++;
  2357.                     break;
  2358.                 default:
  2359.                     break;
  2360.                 }
  2361.             }
  2362.         }
  2363.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  2364.             if (!gProgram_state.AI_vehicles.opponents[i].finished_for_this_race) {
  2365.                 if (un_stun_flag) {
  2366.                     UnStunTheBugger(&gProgram_state.AI_vehicles.opponents[i]);
  2367.                 }
  2368.                 CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(&gProgram_state.AI_vehicles.opponents[i]);
  2369.                 CalcPlayerConspicuousness(&gProgram_state.AI_vehicles.opponents[i]);
  2370.                 ProcessThisOpponent(&gProgram_state.AI_vehicles.opponents[i]);
  2371.                 ClearTwattageOccurrenceVariables(&gProgram_state.AI_vehicles.opponents[i]);
  2372.             }
  2373.         }
  2374.         for (i = 0; i < gNumber_of_cops_before_faffage; i++) {
  2375.             if (!gProgram_state.AI_vehicles.cops[i].finished_for_this_race) {
  2376.                 if (un_stun_flag) {
  2377.                     UnStunTheBugger(&gProgram_state.AI_vehicles.cops[i]);
  2378.                 }
  2379.                 CalcDistanceFromHome(&gProgram_state.AI_vehicles.cops[i]);
  2380.                 CalcOpponentConspicuousnessWithAViewToCheatingLikeFuck(&gProgram_state.AI_vehicles.cops[i]);
  2381.                 CalcPlayerConspicuousness(&gProgram_state.AI_vehicles.cops[i]);
  2382.                 ProcessThisOpponent(&gProgram_state.AI_vehicles.cops[i]);
  2383.                 ClearTwattageOccurrenceVariables(&gProgram_state.AI_vehicles.cops[i]);
  2384.                 gProgram_state.AI_vehicles.cops[i].murder_reported = 0;
  2385.             }
  2386.         }
  2387.         if (gNext_grudge_reduction < gTime_stamp_for_this_munging) {
  2388.             gNext_grudge_reduction = gTime_stamp_for_this_munging + 3000;
  2389.             for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  2390.                 if (!gProgram_state.AI_vehicles.opponents[i].finished_for_this_race) {
  2391.                     if (gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player >= gGrudge_reduction_per_period) {
  2392.                         gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player -= gGrudge_reduction_per_period;
  2393.                     } else {
  2394.                         gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player = 0;
  2395.                     }
  2396.                 }
  2397.             }
  2398.         }
  2399.         RebuildActiveCarList();
  2400.         gFirst_frame = 0;
  2401.     }
  2402. }
  2403.  
  2404. // IDA: void __cdecl SetInitialCopPositions()
  2405. void SetInitialCopPositions(void) {
  2406.     int i;
  2407.     LOG_TRACE("()");
  2408.  
  2409.     for (i = 0; i < GetCarCount(eVehicle_rozzer); i++) {
  2410.         PossibleService();
  2411.         BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].car_spec->car_master_actor->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
  2412.         PointActorAlongThisBloodyVector(gProgram_state.AI_vehicles.cops[i].car_spec->car_master_actor, &gProgram_state.AI_vehicles.cop_start_vectors[i]);
  2413.         gProgram_state.AI_vehicles.cops[i].physics_me = 0;
  2414.         RematerialiseOpponent(&gProgram_state.AI_vehicles.cops[i], 0.f);
  2415.         InitCarSkidStuff(gProgram_state.AI_vehicles.cops[i].car_spec);
  2416.     }
  2417. }
  2418.  
  2419. // IDA: void __usercall InitOpponents(tRace_info *pRace_info@<EAX>)
  2420. void InitOpponents(tRace_info* pRace_info) {
  2421.     int i;
  2422.     int opponent_number;
  2423.     int rank_dependent_difficulty;
  2424.     //int skill_dependent_difficulty; // Pierre-Marie Baty -- unused variable
  2425.     br_bounds bounds;
  2426.     tCar_spec* car_spec;
  2427.     tOpponent_spec* opponent_spec;
  2428.     LOG_TRACE("(%p)", pRace_info);
  2429.  
  2430.     gNext_grudge_reduction = gTime_stamp_for_this_munging + 8000;
  2431.     gGrudge_reduction_per_period = 3 - gProgram_state.skill_level;
  2432.     gProcessing_opponents = 1;
  2433.     gFirst_frame = 1;
  2434.     gAcknowledged_start = 0;
  2435.     gStart_jumped = 0;
  2436.     gViewable_car_list[0] = &gProgram_state.current_car;
  2437.     gNum_viewable_cars = 1;
  2438.     BrActorToBounds(&bounds, gProgram_state.track_spec.the_actor);
  2439.     gMinimum_yness_before_knackerisation = bounds.min.v[1] - 2.f;
  2440.     gDefinite_no_cop_pursuit_speed = 17.8788f;
  2441.     gDefinite_cop_pursuit_speed = 44.697f;
  2442.     gCop_pursuit_speed_percentage_multiplier = 100.f / (gDefinite_cop_pursuit_speed - gDefinite_no_cop_pursuit_speed);
  2443.     gHead_on_cos_value = cosf(.5235668f);
  2444.     gAcme_frame_count = 0;
  2445.     gProgram_state.current_car.no_of_processes_recording_my_trail = 0;
  2446.     rank_dependent_difficulty = (101.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank));
  2447.     // FIXME: unsure about gBig_bang
  2448.     gBig_bang = 70.f - (float)(3 * rank_dependent_difficulty + 10 * gProgram_state.skill_level) * gOpponent_nastyness_frigger;
  2449.     gIn_view_distance = gCamera_yon + 10.f;
  2450.     if (gCamera_yon + 10.f <= 45.f) {
  2451.         gIn_view_distance = 45.f;
  2452.     }
  2453.     gTime_stamp_for_this_munging = GetTotalTime();
  2454.     gFrame_period_for_this_munging = 1;
  2455.     gFrame_period_for_this_munging_in_secs = gFrame_period_for_this_munging / 1000.f;
  2456.     if (gNet_mode == eNet_mode_none) {
  2457.         gProgram_state.AI_vehicles.number_of_opponents = pRace_info->number_of_racers - 1;
  2458.     } else {
  2459.         gProgram_state.AI_vehicles.number_of_opponents = 0;
  2460.     }
  2461.     gNumber_of_cops_before_faffage = gProgram_state.AI_vehicles.number_of_cops;
  2462.     for (i = 0, opponent_number = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++, opponent_number++) {
  2463.         PossibleService();
  2464.         if (pRace_info->opponent_list[opponent_number].index < 0) {
  2465.             opponent_number++;
  2466.         }
  2467.         gProgram_state.AI_vehicles.opponents[i].car_spec = pRace_info->opponent_list[opponent_number].car_spec;
  2468.         gProgram_state.AI_vehicles.opponents[i].car_spec->car_ID = i | 0x200;
  2469.         dr_dprintf("Car '%s', car_ID %x",
  2470.             gProgram_state.AI_vehicles.opponents[i].car_spec->driver_name,
  2471.             gProgram_state.AI_vehicles.opponents[i].car_spec->car_ID);
  2472.         gProgram_state.AI_vehicles.opponents[i].index = pRace_info->opponent_list[opponent_number].index;
  2473.         gProgram_state.AI_vehicles.opponents[i].time_last_processed = gTime_stamp_for_this_munging;
  2474.         gProgram_state.AI_vehicles.opponents[i].time_this_objective_started = gTime_stamp_for_this_munging;
  2475.         gProgram_state.AI_vehicles.opponents[i].last_moved_ok = gTime_stamp_for_this_munging;
  2476.         gProgram_state.AI_vehicles.opponents[i].last_in_view = 0;
  2477.         gProgram_state.AI_vehicles.opponents[i].stun_time_ends = 0;
  2478.         gProgram_state.AI_vehicles.opponents[i].next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 2000;
  2479.         gProgram_state.AI_vehicles.opponents[i].next_out_of_world_check = gTime_stamp_for_this_munging + 500;
  2480.         gProgram_state.AI_vehicles.opponents[i].cunting_buttfuck_timer = 0;
  2481.         gProgram_state.AI_vehicles.opponents[i].finished_for_this_race = 0;
  2482.         gProgram_state.AI_vehicles.opponents[i].physics_me = 1;
  2483.         gProgram_state.AI_vehicles.opponents[i].pursue_from_start = 0;
  2484.         gProgram_state.AI_vehicles.opponents[i].cheating = 0;
  2485.         gProgram_state.AI_vehicles.opponents[i].knackeredness_detected = 0;
  2486.         gProgram_state.AI_vehicles.opponents[i].players_section_when_last_calced_full_path = -1;
  2487.         gProgram_state.AI_vehicles.opponents[i].car_spec->last_person_to_hit_us = NULL;
  2488.         gProgram_state.AI_vehicles.opponents[i].car_spec->last_person_we_hit = NULL;
  2489.         gProgram_state.AI_vehicles.opponents[i].car_spec->last_collision_time = 0;
  2490.         gProgram_state.AI_vehicles.opponents[i].car_spec->last_time_we_touched_a_player = 0;
  2491.         gProgram_state.AI_vehicles.opponents[i].car_spec->grudge_raised_recently = 1;
  2492.         gProgram_state.AI_vehicles.opponents[i].car_spec->no_of_processes_recording_my_trail = 0;
  2493.         gProgram_state.AI_vehicles.opponents[i].nnext_sections = 0;
  2494.         gProgram_state.AI_vehicles.opponents[i].new_objective_required = 1;
  2495.         gProgram_state.AI_vehicles.opponents[i].current_objective = eOOT_none;
  2496.         gProgram_state.AI_vehicles.opponents[i].has_moved_at_some_point = 0;
  2497.         gProgram_state.AI_vehicles.opponents[i].player_in_view_now = 0;
  2498.         gProgram_state.AI_vehicles.opponents[i].acknowledged_piv = 0;
  2499.         gProgram_state.AI_vehicles.opponents[i].nastiness = (gProgram_state.skill_level / 2.f
  2500.                                                                 + ((float)(gOpponents[gProgram_state.AI_vehicles.opponents[i].index].strength_rating - 1)) / 4.f
  2501.                                                                 + (99.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank)) / 98.f)
  2502.             / 3.f * gOpponent_nastyness_frigger;
  2503.         BrVector3Set(&gProgram_state.AI_vehicles.opponents[i].pos_last_frame, 0.f, 0.f, 0.f);
  2504.         gOpponents[gProgram_state.AI_vehicles.opponents[i].index].psyche.grudge_against_player = 10;
  2505.         gViewable_car_list[gNum_viewable_cars] = gProgram_state.AI_vehicles.opponents[i].car_spec;
  2506.         gNum_viewable_cars++;
  2507.         StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], 10000);
  2508.     }
  2509.     if (gChallenger_index__opponent >= 0) {
  2510.         car_spec = GetCarSpecFromGlobalOppoIndex(gChallenger_index__opponent);
  2511.         opponent_spec = GetOpponentSpecFromCarSpec(car_spec);
  2512.         if (opponent_spec == NULL) {
  2513.             dr_dprintf("ERROR - can't record dare - no opponent_spec for car_spec");
  2514.         } else {
  2515.             opponent_spec->pursue_from_start = 1;
  2516.         }
  2517.         gChallenger_index__opponent = -1;
  2518.     }
  2519.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  2520.         PossibleService();
  2521.         gProgram_state.AI_vehicles.cops[i].car_spec->car_ID = i | 0x300;
  2522.         gProgram_state.AI_vehicles.cops[i].index = 3;
  2523.         gProgram_state.AI_vehicles.cops[i].time_last_processed = gTime_stamp_for_this_munging;
  2524.         gProgram_state.AI_vehicles.cops[i].time_this_objective_started = gTime_stamp_for_this_munging;
  2525.         gProgram_state.AI_vehicles.cops[i].last_moved_ok = gTime_stamp_for_this_munging;
  2526.         gProgram_state.AI_vehicles.cops[i].last_in_view = 0;
  2527.         gProgram_state.AI_vehicles.cops[i].stun_time_ends = 0;
  2528.         gProgram_state.AI_vehicles.cops[i].next_player_visibility_check = gTime_stamp_for_this_munging + IRandomBetween(0, 900) + 5000;
  2529.         gProgram_state.AI_vehicles.cops[i].next_out_of_world_check = gTime_stamp_for_this_munging + 500;
  2530.         gProgram_state.AI_vehicles.cops[i].cunting_buttfuck_timer = 0;
  2531.         gProgram_state.AI_vehicles.cops[i].finished_for_this_race = 0;
  2532.         gProgram_state.AI_vehicles.cops[i].physics_me = 1;
  2533.         gProgram_state.AI_vehicles.cops[i].pursue_from_start = 0;
  2534.         gProgram_state.AI_vehicles.cops[i].cheating = 0;
  2535.         gProgram_state.AI_vehicles.cops[i].knackeredness_detected = 0;
  2536.         gProgram_state.AI_vehicles.cops[i].murder_reported = 0;
  2537.         gProgram_state.AI_vehicles.cops[i].finished_for_this_race = 0;
  2538.         gProgram_state.AI_vehicles.cops[i].players_section_when_last_calced_full_path = -1;
  2539.         gProgram_state.AI_vehicles.cops[i].nnext_sections = 0;
  2540.         gProgram_state.AI_vehicles.cops[i].new_objective_required = 1;
  2541.         gProgram_state.AI_vehicles.cops[i].current_objective = eOOT_none;
  2542.         gProgram_state.AI_vehicles.cops[i].player_in_view_now = 0;
  2543.         gProgram_state.AI_vehicles.cops[i].acknowledged_piv = 0;
  2544.         gProgram_state.AI_vehicles.cops[i].nastiness = (gProgram_state.skill_level / 2.f
  2545.                                                            + (99.f - (gCurrent_race.suggested_rank < 10 ? .5f : (float)gCurrent_race.suggested_rank)) / 98.f
  2546.                                                            + 2.25f)
  2547.             / 3.f * gOpponent_nastyness_frigger;
  2548.         BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].start_pos, &gProgram_state.AI_vehicles.cop_start_points[i]);
  2549.         BrVector3Copy(&gProgram_state.AI_vehicles.cops[i].start_direction, &gProgram_state.AI_vehicles.cop_start_vectors[i]);
  2550.         BrVector3Set(&gProgram_state.AI_vehicles.cops[i].pos_last_frame, 0.f, 0.f, 0.f);
  2551.         gViewable_car_list[gNum_viewable_cars] = gProgram_state.AI_vehicles.cops[i].car_spec;
  2552.         gNum_viewable_cars++;
  2553.         gProgram_state.AI_vehicles.cops[i].car_spec->last_person_to_hit_us = NULL;
  2554.         gProgram_state.AI_vehicles.cops[i].car_spec->last_person_we_hit = NULL;
  2555.         gProgram_state.AI_vehicles.cops[i].car_spec->last_collision_time = 0;
  2556.         gProgram_state.AI_vehicles.cops[i].car_spec->last_time_we_touched_a_player = 0;
  2557.         gProgram_state.AI_vehicles.cops[i].car_spec->grudge_raised_recently = 1;
  2558.         gOpponents[gProgram_state.AI_vehicles.cops[i].index].psyche.grudge_against_player = 10;
  2559.         StunTheBugger(&gProgram_state.AI_vehicles.cops[i], 10000);
  2560.     }
  2561.     gActive_car_list_rebuild_required = 1;
  2562.     RebuildActiveCarList();
  2563. }
  2564.  
  2565. // IDA: void __cdecl DisposeOpponents()
  2566. void DisposeOpponents(void) {
  2567.     int i;
  2568.     LOG_TRACE("()");
  2569.  
  2570.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  2571.         DisposeCar(gProgram_state.AI_vehicles.cops[i].car_spec, (i == gBIG_APC_index) ? 4 : 3);
  2572.         BrMemFree(gProgram_state.AI_vehicles.cops[i].car_spec);
  2573.     }
  2574. }
  2575.  
  2576. // IDA: void __usercall WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(int pWhat_the_countdown_was@<EAX>)
  2577. void WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(int pWhat_the_countdown_was) {
  2578.     int i;
  2579.     LOG_TRACE("(%d)", pWhat_the_countdown_was);
  2580.  
  2581.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  2582.         UnStunTheBugger(&gProgram_state.AI_vehicles.opponents[i]);
  2583.         if (IRandomBetween(1000, 2500) < 1000 * pWhat_the_countdown_was) {
  2584.             StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], IRandomBetween(1000, 2500));
  2585.         } else {
  2586.             StunTheBugger(&gProgram_state.AI_vehicles.opponents[i], 1000 * pWhat_the_countdown_was);
  2587.         }
  2588.     }
  2589.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  2590.         UnStunTheBugger(&gProgram_state.AI_vehicles.cops[i]);
  2591.         if (IRandomBetween(1000, 2500) < 1000 * pWhat_the_countdown_was) {
  2592.             StunTheBugger(&gProgram_state.AI_vehicles.cops[i], IRandomBetween(1000, 2500));
  2593.         } else {
  2594.             StunTheBugger(&gProgram_state.AI_vehicles.cops[i], 1000 * pWhat_the_countdown_was);
  2595.         }
  2596.     }
  2597.     gStart_jumped = 1;
  2598.     gAcknowledged_start = 1;
  2599. }
  2600.  
  2601. // IDA: void __usercall ReportMurderToPoliceDepartment(tCar_spec *pCar_spec@<EAX>)
  2602. void ReportMurderToPoliceDepartment(tCar_spec* pCar_spec) {
  2603.     int i;
  2604.     LOG_TRACE("(%p)", pCar_spec);
  2605.  
  2606.     if (pCar_spec == &gProgram_state.current_car) {
  2607.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  2608.             gProgram_state.AI_vehicles.cops[i].murder_reported = 1;
  2609.         }
  2610.     }
  2611. }
  2612.  
  2613. // IDA: int __usercall GetCarCount@<EAX>(tVehicle_type pCategory@<EAX>)
  2614. int GetCarCount(tVehicle_type pCategory) {
  2615.     LOG_TRACE("(%d)", pCategory);
  2616.  
  2617.     switch (pCategory) {
  2618.     case eVehicle_self:
  2619.         return 1;
  2620.  
  2621.     case eVehicle_net_player:
  2622.         if (gNet_mode) {
  2623.             return gNumber_of_net_players - 1;
  2624.         } else {
  2625.             return 0;
  2626.         }
  2627.         break;
  2628.     case eVehicle_opponent:
  2629.         return gProgram_state.AI_vehicles.number_of_opponents;
  2630.  
  2631.     case eVehicle_rozzer:
  2632.         return gNumber_of_cops_before_faffage;
  2633.  
  2634.     case eVehicle_drone:
  2635.         return 0;
  2636.  
  2637.     case eVehicle_not_really:
  2638.         return gNum_active_non_cars;
  2639.  
  2640.     default:
  2641.         return 0;
  2642.     }
  2643. }
  2644.  
  2645. // IDA: tCar_spec* __usercall GetCarSpec@<EAX>(tVehicle_type pCategory@<EAX>, int pIndex@<EDX>)
  2646. tCar_spec* GetCarSpec(tVehicle_type pCategory, int pIndex) {
  2647.     LOG_TRACE("(%d, %d)", pCategory, pIndex);
  2648.  
  2649.     switch (pCategory) {
  2650.  
  2651.     case eVehicle_self:
  2652.         return &gProgram_state.current_car;
  2653.  
  2654.     case eVehicle_net_player:
  2655.         if (gThis_net_player_index > pIndex) {
  2656.             return gNet_players[pIndex].car;
  2657.         } else {
  2658.             return gNet_players[pIndex + 1].car;
  2659.         }
  2660.  
  2661.     case eVehicle_opponent:
  2662.         return gProgram_state.AI_vehicles.opponents[pIndex].car_spec;
  2663.  
  2664.     case eVehicle_rozzer:
  2665.         return gProgram_state.AI_vehicles.cops[pIndex].car_spec;
  2666.  
  2667.     case eVehicle_drone:
  2668.         PDEnterDebugger("OPPONENT.C: GetCarSpec() can't return drone car_specs");
  2669.         return 0;
  2670.  
  2671.     case eVehicle_not_really:
  2672.         return (tCar_spec*)gActive_non_car_list[pIndex];
  2673.  
  2674.     default:
  2675.         return 0;
  2676.     }
  2677. }
  2678.  
  2679. // IDA: char* __usercall GetDriverName@<EAX>(tVehicle_type pCategory@<EAX>, int pIndex@<EDX>)
  2680. char* GetDriverName(tVehicle_type pCategory, int pIndex) {
  2681.     LOG_TRACE("(%d, %d)", pCategory, pIndex);
  2682.  
  2683.     switch (pCategory) {
  2684.     case eVehicle_self:
  2685.         return gProgram_state.player_name[gProgram_state.frank_or_anniness];
  2686.     case eVehicle_opponent:
  2687.         return gOpponents[gProgram_state.AI_vehicles.opponents[pIndex].index].name;
  2688.     case eVehicle_rozzer:
  2689.         return "Faceless Cop";
  2690.     case eVehicle_drone:
  2691.         return "Innocent Civilian";
  2692.     case eVehicle_not_really:
  2693.     default:
  2694.         return NULL;
  2695.     }
  2696. }
  2697.  
  2698. // IDA: tOpponent_spec* __usercall GetOpponentSpecFromCarSpec@<EAX>(tCar_spec *pCar_spec@<EAX>)
  2699. tOpponent_spec* GetOpponentSpecFromCarSpec(tCar_spec* pCar_spec) {
  2700.     int i;
  2701.     LOG_TRACE("(%p)", pCar_spec);
  2702.  
  2703.     if ((pCar_spec->car_ID & 0xff00) == 0x200) {
  2704.         for (i = 0; i < GetCarCount(eVehicle_opponent); i++) {
  2705.             if (gProgram_state.AI_vehicles.opponents[i].car_spec == pCar_spec) {
  2706.                 return &gProgram_state.AI_vehicles.opponents[i];
  2707.             }
  2708.         }
  2709.     } else if ((pCar_spec->car_ID & 0xff00) == 0x300) {
  2710.         for (i = 0; i < GetCarCount(eVehicle_rozzer); i++) {
  2711.             if (gProgram_state.AI_vehicles.cops[i].car_spec == pCar_spec) {
  2712.                 return &gProgram_state.AI_vehicles.cops[i];
  2713.             }
  2714.         }
  2715.     }
  2716.     return NULL;
  2717. }
  2718.  
  2719. // IDA: tCar_spec* __usercall GetCarSpecFromGlobalOppoIndex@<EAX>(int pIndex@<EAX>)
  2720. tCar_spec* GetCarSpecFromGlobalOppoIndex(int pIndex) {
  2721.     int i;
  2722.     LOG_TRACE("(%d)", pIndex);
  2723.  
  2724.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  2725.         if (gProgram_state.AI_vehicles.opponents[i].index == pIndex) {
  2726.             return gProgram_state.AI_vehicles.opponents[i].car_spec;
  2727.         }
  2728.     }
  2729.     return NULL;
  2730. }
  2731.  
  2732. // IDA: int __usercall GetOpponentsRealSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, int pSection_no@<EDX>)
  2733. int GetOpponentsRealSection(tOpponent_spec* pOpponent_spec, int pSection_no) {
  2734.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection_no);
  2735.  
  2736.     if (pSection_no >= 20000) {
  2737.         return pOpponent_spec->next_sections[pSection_no - 20000].section_no;
  2738.     } else if (pSection_no >= 10000) {
  2739.         return -1;
  2740.     } else {
  2741.         return pSection_no;
  2742.     }
  2743. }
  2744.  
  2745. // IDA: int __usercall GetOpponentsFirstSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>)
  2746. int GetOpponentsFirstSection(tOpponent_spec* pOpponent_spec) {
  2747.     LOG_TRACE("(%p)", pOpponent_spec);
  2748.  
  2749.     if (pOpponent_spec->current_objective != eOOT_pursue_and_twat) {
  2750.         return 20000;
  2751.     }
  2752.     if (pOpponent_spec->pursue_car_data.state == ePCS_following_trail) {
  2753.         return pOpponent_spec->follow_path_data.section_no;
  2754.     }
  2755.     if (pOpponent_spec->pursue_car_data.state == ePCS_following_line_of_sight) {
  2756.         return 10000;
  2757.     }
  2758.     return 20000;
  2759. }
  2760.  
  2761. // IDA: int __usercall GetOpponentsNextSection@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pCurrent_section@<EDX>)
  2762. int GetOpponentsNextSection(tOpponent_spec* pOpponent_spec, tS16 pCurrent_section) {
  2763.     LOG_TRACE("(%p, %d)", pOpponent_spec, pCurrent_section);
  2764.  
  2765.     if (pCurrent_section < 20000) {
  2766.         if (pCurrent_section < 15000) {
  2767.             return -1;
  2768.         } else {
  2769.             return CalcNextTrailSection(pOpponent_spec, pCurrent_section);
  2770.         }
  2771.     } else if (pCurrent_section - 19999 >= pOpponent_spec->nnext_sections || (!pOpponent_spec->cheating && gProgram_state.AI_vehicles.path_sections[pCurrent_section - 19999].type == ePST_cheat_only)) {
  2772.         return -1;
  2773.     } else {
  2774.         return pCurrent_section + 1;
  2775.     }
  2776. }
  2777.  
  2778. // IDA: tS16 __usercall GetOpponentsSectionStartNode@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
  2779. tS16 GetOpponentsSectionStartNode(tOpponent_spec* pOpponent_spec, tS16 pSection) {
  2780.     tS16 section_no;
  2781.     int node_index_index;
  2782.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
  2783.  
  2784.     if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
  2785.       node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction == 0;
  2786.       if (pSection - 20000 > pOpponent_spec->nnext_sections) {
  2787.           section_no = -1;
  2788.       } else {
  2789.           section_no = gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].node_indices[node_index_index];
  2790.           return section_no;
  2791.       }
  2792.     }
  2793.     dr_dprintf("BIG ERROR - GetOpponentsSectionStartNode() - section not found in next_section array for opponent %s",
  2794.         pOpponent_spec->car_spec->driver_name);
  2795.     PDEnterDebugger("BIG ERROR - GetOpponentsSectionStartNode()");
  2796.     return -1;
  2797. }
  2798.  
  2799. // IDA: tS16 __usercall GetOpponentsSectionFinishNode@<AX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
  2800. tS16 GetOpponentsSectionFinishNode(tOpponent_spec* pOpponent_spec, tS16 pSection) {
  2801.     //tS16 section_no; // Pierre-Marie Baty -- unused variable
  2802.     //int node_index_index; // Pierre-Marie Baty -- unused variable
  2803.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
  2804.  
  2805.    if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
  2806.        return gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].node_indices[pOpponent_spec->next_sections[pSection - 20000].direction];
  2807.    }
  2808.    dr_dprintf("BIG ERROR - GetOpponentsSectionFinishNode() - section not found in next_section array for opponent %s",
  2809.        pOpponent_spec->car_spec->driver_name);
  2810.    PDEnterDebugger("BIG ERROR - GetOpponentsSectionFinishNode()");
  2811.    return 0;
  2812. }
  2813.  
  2814. // IDA: br_vector3* __usercall GetOpponentsSectionStartNodePoint@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
  2815. br_vector3* GetOpponentsSectionStartNodePoint(tOpponent_spec* pOpponent_spec, tS16 pSection) {
  2816.     tS16 section_no;
  2817.     tS16 node_no;
  2818.     int node_index_index;
  2819.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
  2820.  
  2821.     if (pSection >= 20000 && pOpponent_spec->nnext_sections > pSection - 20000) {
  2822.         section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
  2823.         node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction;
  2824.         node_no = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[node_index_index == 0];
  2825.         return &gProgram_state.AI_vehicles.path_nodes[node_no].p;
  2826.     }
  2827.  
  2828.     if (pSection >= 15000) {
  2829.         return &pOpponent_spec->pursue_car_data.pursuee->my_trail.trail_nodes[pSection - 15000];
  2830.     }
  2831.     if (pSection == 10000) {
  2832.         return &pOpponent_spec->pursue_car_data.direct_line_nodes[0].p;
  2833.     }
  2834.     dr_dprintf("BIG ERROR - GetOpponentsSectionStartNodePoint() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
  2835.     PDEnterDebugger("BIG ERROR - GetOpponentsSectionStartNodePoint()");
  2836.     return 0;
  2837. }
  2838.  
  2839. // IDA: br_vector3* __usercall GetOpponentsSectionFinishNodePoint@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
  2840. br_vector3* GetOpponentsSectionFinishNodePoint(tOpponent_spec* pOpponent_spec, tS16 pSection) {
  2841.     tS16 section_no;
  2842.     tS16 node_no;
  2843.     int node_index_index;
  2844.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
  2845.  
  2846.     if (pSection >= 20000 && pOpponent_spec->nnext_sections > pSection - 20000) {
  2847.         section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
  2848.         node_index_index = pOpponent_spec->next_sections[pSection - 20000].direction;
  2849.         node_no = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[node_index_index];
  2850.         return &gProgram_state.AI_vehicles.path_nodes[node_no].p;
  2851.     } else if (pSection >= 15000) {
  2852.         return &pOpponent_spec->pursue_car_data.pursuee->my_trail.trail_nodes[(pSection + 1) - 15000];
  2853.     } else if (pSection == 10000) {
  2854.         return &pOpponent_spec->pursue_car_data.direct_line_nodes[1].p;
  2855.     } else {
  2856.         dr_dprintf("BIG ERROR - GetOpponentsSectionFinishNodePoint() - section not found in next_section array for opponent %s",
  2857.             pOpponent_spec->car_spec->driver_name);
  2858.         PDEnterDebugger("BIG ERROR - GetOpponentsSectionFinishNodePoint()");
  2859.         return NULL;
  2860.     }
  2861. }
  2862.  
  2863. // IDA: br_scalar __usercall GetOpponentsSectionWidth@<ST0>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>)
  2864. br_scalar GetOpponentsSectionWidth(tOpponent_spec* pOpponent_spec, tS16 pSection) {
  2865.     LOG_TRACE("(%p, %d)", pOpponent_spec, pSection);
  2866.  
  2867.     if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
  2868.         return gProgram_state.AI_vehicles.path_sections[pOpponent_spec->next_sections[pSection - 20000].section_no].width;
  2869.     }
  2870.     if (pSection >= 15000) {
  2871.         return 0.5f;
  2872.     }
  2873.     if (pSection == 10000) {
  2874.         return pOpponent_spec->pursue_car_data.direct_line_section.width;
  2875.     }
  2876.     return gProgram_state.AI_vehicles.path_sections[pSection].width;
  2877. }
  2878.  
  2879. // IDA: int __usercall GetOpponentsSectionMinSpeed@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>, int pTowards_finish@<EBX>)
  2880. int GetOpponentsSectionMinSpeed(tOpponent_spec* pOpponent_spec, tS16 pSection, int pTowards_finish) {
  2881.     tS16 section_no;
  2882.     int direction;
  2883.     LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection, pTowards_finish);
  2884.  
  2885.     if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
  2886.         section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
  2887.         direction = pOpponent_spec->next_sections[pSection - 20000].direction;
  2888.         return gProgram_state.AI_vehicles.path_sections[section_no].min_speed[pTowards_finish == direction];
  2889.     }
  2890.     if (pSection >= 15000) {
  2891.         return 0;
  2892.     }
  2893.     if (pSection == 10000) {
  2894.         return pOpponent_spec->pursue_car_data.direct_line_section.min_speed[pTowards_finish];
  2895.     }
  2896.     dr_dprintf("WARNING - GetOpponentsSectionMinSpeed() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
  2897.     PDEnterDebugger("WARNING - GetOpponentsSectionMinSpeed()");
  2898.     return 0;
  2899. }
  2900.  
  2901. // IDA: int __usercall GetOpponentsSectionMaxSpeed@<EAX>(tOpponent_spec *pOpponent_spec@<EAX>, tS16 pSection@<EDX>, int pTowards_finish@<EBX>)
  2902. int GetOpponentsSectionMaxSpeed(tOpponent_spec* pOpponent_spec, tS16 pSection, int pTowards_finish) {
  2903.     tS16 section_no;
  2904.     int direction;
  2905.     LOG_TRACE("(%p, %d, %d)", pOpponent_spec, pSection, pTowards_finish);
  2906.  
  2907.     if (pSection >= 20000 && pSection - 20000 < pOpponent_spec->nnext_sections) {
  2908.         section_no = pOpponent_spec->next_sections[pSection - 20000].section_no;
  2909.         direction = pOpponent_spec->next_sections[pSection - 20000].direction;
  2910.         return gProgram_state.AI_vehicles.path_sections[section_no].max_speed[pTowards_finish == direction];
  2911.     }
  2912.     if (pSection >= 15000) {
  2913.         return 255;
  2914.     }
  2915.     if (pSection == 10000) {
  2916.         return pOpponent_spec->pursue_car_data.direct_line_section.max_speed[pTowards_finish];
  2917.     }
  2918.     dr_dprintf("WARNING - GetOpponentsSectionMaxSpeed() - section not found in next_section array for opponent %s", pOpponent_spec->car_spec->driver_name);
  2919.     PDEnterDebugger("WARNING - GetOpponentsSectionMaxSpeed()");
  2920.     return 255;
  2921. }
  2922.  
  2923. // IDA: void __usercall InitOpponentPsyche(int pOpponent_index@<EAX>)
  2924. void InitOpponentPsyche(int pOpponent_index) {
  2925.     gOpponents[pOpponent_index].psyche.grudge_against_player = 0;
  2926. }
  2927.  
  2928. // IDA: void __usercall ClearTwattageOccurrenceVariables(tOpponent_spec *pOpponent_spec@<EAX>)
  2929. void ClearTwattageOccurrenceVariables(tOpponent_spec* pOpponent_spec) {
  2930.     LOG_TRACE("(%p)", pOpponent_spec);
  2931.  
  2932.     pOpponent_spec->car_spec->big_bang = 0;
  2933.     pOpponent_spec->car_spec->scary_bang = 0;
  2934.     pOpponent_spec->car_spec->grudge_raised_recently = 0;
  2935.     pOpponent_spec->car_spec->last_person_to_hit_us = NULL;
  2936.     pOpponent_spec->car_spec->last_person_we_hit = NULL;
  2937. }
  2938.  
  2939. // IDA: void __usercall TwoCarsHitEachOther(tCar_spec *pA_car@<EAX>, tCar_spec *pAnother_car@<EDX>)
  2940. void TwoCarsHitEachOther(tCar_spec* pA_car, tCar_spec* pAnother_car) {
  2941.     LOG_TRACE("(%p, %p)", pA_car, pAnother_car);
  2942.  
  2943.     if (pA_car->driver == eDriver_local_human) {
  2944.         pAnother_car->last_time_we_touched_a_player = gTime_stamp_for_this_munging;
  2945.     }
  2946.     if (pAnother_car->driver == eDriver_local_human) {
  2947.         pA_car->last_time_we_touched_a_player = gTime_stamp_for_this_munging;
  2948.     }
  2949. }
  2950.  
  2951. // IDA: void __usercall RecordOpponentTwattageOccurrence(tCar_spec *pTwatter@<EAX>, tCar_spec *pTwattee@<EDX>)
  2952. void RecordOpponentTwattageOccurrence(tCar_spec* pTwatter, tCar_spec* pTwattee) {
  2953.     int bangness;
  2954.     int twatter_index;
  2955.     int twattee_index;
  2956.     int grudginess_caused_by_damage;
  2957.     int new_grudge_value;
  2958.     float damage;
  2959.     //char str[256]; // Pierre-Marie Baty -- unused variable
  2960.     tOpponent_spec* twattee_opponent_spec;
  2961.     tOpponent_spec* twatter_opponent_spec;
  2962.     LOG_TRACE("(%p, %p)", pTwatter, pTwattee);
  2963.  
  2964.     if (pTwatter->driver != eDriver_oppo && pTwattee->driver != eDriver_oppo) {
  2965.         return;
  2966.     }
  2967.     damage = pTwattee->damage_magnitude_accumulator;
  2968.     bangness = MIN(sqrtf(damage * 300000.0f), 100);
  2969.     grudginess_caused_by_damage = bangness / 10 + 50 * CAR_SPEC_IS_ROZZER(pTwattee);
  2970.     dr_dprintf("Frame %0.6d: %s hit %s, damage %f, bangness %d, gBig_bang %d, grudginess %d",
  2971.         gAcme_frame_count,
  2972.         pTwatter->driver_name,
  2973.         pTwattee->driver_name,
  2974.         damage,
  2975.         bangness,
  2976.         gBig_bang,
  2977.         grudginess_caused_by_damage);
  2978.     if (gMin_bangness <= bangness) {
  2979.         if (gMax_bangness < bangness) {
  2980.             gMax_bangness = bangness;
  2981.             dr_dprintf("(New gMax_bangness - %d)", bangness);
  2982.         }
  2983.     } else {
  2984.         gMin_bangness = bangness;
  2985.         dr_dprintf("(New gMin_bangness - %d)", bangness);
  2986.     }
  2987.     if (bangness >= 5) {
  2988.         pTwatter->last_collision_time = gTime_stamp_for_this_munging;
  2989.         pTwatter->last_person_we_hit = pTwattee;
  2990.         pTwattee->last_collision_time = gTime_stamp_for_this_munging;
  2991.         pTwattee->last_person_to_hit_us = pTwatter;
  2992.         pTwattee->grudge_raised_recently = 1;
  2993.         if (bangness >= gBig_bang || CAR_SPEC_IS_ROZZER(pTwattee)) {
  2994.             pTwattee->big_bang = 1;
  2995.         }
  2996.         if (bangness >= 80) {
  2997.             pTwattee->scary_bang = 1;
  2998.         }
  2999.         if (pTwatter->driver == eDriver_local_human) {
  3000.             twattee_opponent_spec = GetOpponentSpecFromCarSpec(pTwattee);
  3001.             if (pTwattee->scary_bang) {
  3002.                 StunTheBugger(twattee_opponent_spec, 30 * bangness + 1000);
  3003.             }
  3004.             new_grudge_value = grudginess_caused_by_damage + gOpponents[twattee_opponent_spec->index].psyche.grudge_against_player;
  3005.             if (new_grudge_value > 100) {
  3006.                 new_grudge_value = 100;
  3007.             }
  3008.             gOpponents[twattee_opponent_spec->index].psyche.grudge_against_player = new_grudge_value;
  3009.         } else if (pTwattee->driver == eDriver_local_human) {
  3010.             twatter_opponent_spec = GetOpponentSpecFromCarSpec(pTwatter);
  3011.             if (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat && twatter_opponent_spec->pursue_car_data.pursuee == pTwattee) {
  3012.                 twatter_opponent_spec->pursue_car_data.time_last_twatted_em = gTime_stamp_for_this_munging;
  3013.             }
  3014.             twatter_index = twatter_opponent_spec->index;
  3015.             new_grudge_value = gOpponents[twatter_index].psyche.grudge_against_player - (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat ? 0 : 2 * grudginess_caused_by_damage);
  3016.             if (new_grudge_value < 0) {
  3017.                 new_grudge_value = 0;
  3018.             }
  3019.             gOpponents[twatter_index].psyche.grudge_against_player = new_grudge_value;
  3020.         } else {
  3021.             twatter_opponent_spec = GetOpponentSpecFromCarSpec(pTwatter);
  3022.             twattee_opponent_spec = GetOpponentSpecFromCarSpec(pTwattee);
  3023.             if (pTwattee->scary_bang) {
  3024.                 StunTheBugger(twattee_opponent_spec, 30 * bangness + 1000);
  3025.             }
  3026.             twattee_index = twattee_opponent_spec->index;
  3027.             if (twatter_opponent_spec->current_objective == eOOT_pursue_and_twat && twatter_opponent_spec->pursue_car_data.pursuee == pTwattee) {
  3028.                 twatter_opponent_spec->pursue_car_data.time_last_twatted_em = gTime_stamp_for_this_munging;
  3029.             }
  3030.             if (CAR_SPEC_IS_OPPONENT(pTwatter) && CAR_SPEC_IS_ROZZER(pTwattee)) {
  3031.                 new_grudge_value = grudginess_caused_by_damage + gOpponents[twattee_index].psyche.grudge_against_player;
  3032.                 if (new_grudge_value > 100) {
  3033.                     new_grudge_value = 100;
  3034.                 }
  3035.                 gOpponents[twattee_index].psyche.grudge_against_player = new_grudge_value;
  3036.             }
  3037.         }
  3038.     }
  3039. }
  3040.  
  3041. // IDA: void __cdecl ToggleOpponentTest()
  3042. void ToggleOpponentTest(void) {
  3043.     LOG_TRACE("()");
  3044.  
  3045.     gTest_toggle = !gTest_toggle;
  3046. }
  3047.  
  3048. // IDA: void __cdecl ToggleOpponentProcessing()
  3049. void ToggleOpponentProcessing(void) {
  3050.     int i;
  3051.     LOG_TRACE("()");
  3052.  
  3053.     gProcessing_opponents = !gProcessing_opponents;
  3054.     if (gProcessing_opponents) {
  3055.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  3056.             ObjectiveComplete(&gProgram_state.AI_vehicles.opponents[i]);
  3057.         }
  3058.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  3059.             ObjectiveComplete(&gProgram_state.AI_vehicles.cops[i]);
  3060.         }
  3061.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "OPPONENTS SWITCHED ON");
  3062.     } else {
  3063.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  3064.             gProgram_state.AI_vehicles.opponents[i].physics_me = 0;
  3065.         }
  3066.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  3067.             gProgram_state.AI_vehicles.opponents[i].physics_me = 0;
  3068.         }
  3069.         gActive_car_list_rebuild_required = 1;
  3070.         RebuildActiveCarList();
  3071.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "OPPONENTS SWITCHED OFF");
  3072.     }
  3073. }
  3074.  
  3075. // IDA: void __cdecl ToggleMellowOpponents()
  3076. void ToggleMellowOpponents(void) {
  3077.     int i;
  3078.     LOG_TRACE("()");
  3079.  
  3080.     gMellow_opponents = !gMellow_opponents;
  3081.     if (gMellow_opponents) {
  3082.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 3000, -1, "Opponents all nice and fluffy");
  3083.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  3084.             ObjectiveComplete(&gProgram_state.AI_vehicles.opponents[i]);
  3085.         }
  3086.     } else {
  3087.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Opponents hostile again");
  3088.     }
  3089. }
  3090.  
  3091. // IDA: void __cdecl RepairOpponentsSystems()
  3092. void RepairOpponentsSystems(void) {
  3093.     int i;
  3094.     LOG_TRACE("()");
  3095.  
  3096.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_opponents; i++) {
  3097.         if (!gProgram_state.AI_vehicles.opponents[i].pursue_from_start) {
  3098.             TotallyRepairACar(gProgram_state.AI_vehicles.opponents[i].car_spec);
  3099.             TurnOpponentPhysicsOff(&gProgram_state.AI_vehicles.opponents[i]);
  3100.             gProgram_state.AI_vehicles.opponents[i].knackeredness_detected = 0;
  3101.         }
  3102.     }
  3103.     NewTextHeadupSlot(eHeadupSlot_misc, 0, 3000, -1, "Opponents systems repaired (but not bodywork)");
  3104. }
  3105.  
  3106. // IDA: void __usercall CopyVertex(br_vertex *pDest_vertex@<EAX>, br_vertex *pSrc_vertex@<EDX>)
  3107. //  Suffix added to avoid duplicate symbol
  3108. void CopyVertex__opponent(br_vertex* pDest_vertex, br_vertex* pSrc_vertex) {
  3109.     LOG_TRACE("(%p, %p)", pDest_vertex, pSrc_vertex);
  3110.  
  3111.     BrVector3Copy(&pDest_vertex->p, &pSrc_vertex->p);
  3112.     pDest_vertex->map.v[0] = pSrc_vertex->map.v[0];
  3113.     pDest_vertex->map.v[1] = pSrc_vertex->map.v[1];
  3114.     pDest_vertex->index = pSrc_vertex->index;
  3115.     pDest_vertex->red = pSrc_vertex->red;
  3116.     pDest_vertex->grn = pSrc_vertex->grn;
  3117.     pDest_vertex->blu = pSrc_vertex->blu;
  3118. }
  3119.  
  3120. // IDA: void __usercall CopyFace(br_face *pDest_face@<EAX>, br_face *pSrc_face@<EDX>)
  3121. //  Suffix added to avoid duplicate symbol
  3122. void CopyFace__opponent(br_face* pDest_face, br_face* pSrc_face) {
  3123.     LOG_TRACE("(%p, %p)", pDest_face, pSrc_face);
  3124.  
  3125.     pDest_face->vertices[0] = pSrc_face->vertices[0];
  3126.     pDest_face->vertices[1] = pSrc_face->vertices[1];
  3127.     pDest_face->vertices[2] = pSrc_face->vertices[2];
  3128.     pDest_face->material = pSrc_face->material;
  3129.     pDest_face->smoothing = pSrc_face->smoothing;
  3130.     pDest_face->flags = pSrc_face->flags;
  3131. }
  3132.  
  3133. // IDA: void __usercall DeleteSection(tS16 pSection_to_delete@<EAX>)
  3134. void DeleteSection(tS16 pSection_to_delete) {
  3135.     tS16 section_no;
  3136.     tS16 section_no_index;
  3137.     tS16 node_no;
  3138.     tS16 node_no_index;
  3139.     tS16 found_it;
  3140.     LOG_TRACE("(%d)", pSection_to_delete);
  3141.  
  3142.     for (node_no = 0; node_no < 2; node_no++) {
  3143.         node_no_index = gProgram_state.AI_vehicles.path_sections[pSection_to_delete].node_indices[node_no];
  3144.         if (node_no_index >= 0) {
  3145.             found_it = 0;
  3146.             for (section_no = 0; section_no < (gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections - 1); section_no++) {
  3147.                 if (gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no] == pSection_to_delete) {
  3148.                     found_it = 1;
  3149.                 }
  3150.                 if (found_it) {
  3151.                     gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no] = gProgram_state.AI_vehicles.path_nodes[node_no_index].sections[section_no + 1];
  3152.                 }
  3153.             }
  3154.             if (gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections != 0) {
  3155.                 gProgram_state.AI_vehicles.path_nodes[node_no_index].number_of_sections--;
  3156.             }
  3157.         }
  3158.     }
  3159.     for (section_no = pSection_to_delete; section_no < (gProgram_state.AI_vehicles.number_of_path_sections - 1); section_no++) {
  3160.         gProgram_state.AI_vehicles.path_sections[section_no] = gProgram_state.AI_vehicles.path_sections[section_no + 1];
  3161.     }
  3162.     gProgram_state.AI_vehicles.number_of_path_sections--;
  3163.     for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
  3164.         for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index++) {
  3165.             if (pSection_to_delete < gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index]) {
  3166.                 gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index]--;
  3167.             }
  3168.         }
  3169.     }
  3170. }
  3171.  
  3172. // IDA: void __usercall DeleteNode(tS16 pNode_to_delete@<EAX>, int pAnd_sections@<EDX>)
  3173. void DeleteNode(tS16 pNode_to_delete, int pAnd_sections) {
  3174.     tS16 node_no;
  3175.     tS16 section_no;
  3176.     tS16 section1;
  3177.     tS16 section2;
  3178.     LOG_TRACE("(%d, %d)", pNode_to_delete, pAnd_sections);
  3179.  
  3180.     dr_dprintf("Node to be deleted #%d", pNode_to_delete);
  3181.     if (pAnd_sections) {
  3182.         while (gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].number_of_sections != 0) {
  3183.             DeleteSection(gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0]);
  3184.         }
  3185.     } else {
  3186.         if (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0]].node_indices[0] == pNode_to_delete) {
  3187.             section1 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[1];
  3188.             section2 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0];
  3189.         } else {
  3190.             section1 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[0];
  3191.             section2 = gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].sections[1];
  3192.         }
  3193.         dr_dprintf("Section 1#%d(#%d,#%d), section 2#%d(#%d,#%d)", section1,
  3194.             gProgram_state.AI_vehicles.path_sections[section1].node_indices[0],
  3195.             gProgram_state.AI_vehicles.path_sections[section1].node_indices[1],
  3196.             section2,
  3197.             gProgram_state.AI_vehicles.path_sections[section2].node_indices[0],
  3198.             gProgram_state.AI_vehicles.path_sections[section2].node_indices[1]);
  3199.         gProgram_state.AI_vehicles.path_sections[section1].min_speed[1] = gProgram_state.AI_vehicles.path_sections[section2].min_speed[1];
  3200.         gProgram_state.AI_vehicles.path_sections[section1].max_speed[1] = gProgram_state.AI_vehicles.path_sections[section2].max_speed[1];
  3201.         node_no = gProgram_state.AI_vehicles.path_sections[section2].node_indices[1];
  3202.         gProgram_state.AI_vehicles.path_sections[section1].node_indices[1] = node_no;
  3203.         dr_dprintf("Section 1's new end node is #%d", node_no);
  3204.         if (gProgram_state.AI_vehicles.path_nodes[node_no].sections[0] == section2) {
  3205.             gProgram_state.AI_vehicles.path_nodes[node_no].sections[0] = section1;
  3206.         } else {
  3207.             gProgram_state.AI_vehicles.path_nodes[node_no].sections[1] = section1;
  3208.         }
  3209.         gProgram_state.AI_vehicles.path_nodes[pNode_to_delete].number_of_sections = 0;
  3210.         gProgram_state.AI_vehicles.path_sections[section2].node_indices[0] = -1;
  3211.         gProgram_state.AI_vehicles.path_sections[section2].node_indices[1] = -1;
  3212.         DeleteSection(section2);
  3213.     }
  3214.     for (node_no = pNode_to_delete; node_no < (gProgram_state.AI_vehicles.number_of_path_nodes - 1); node_no++) {
  3215.         gProgram_state.AI_vehicles.path_nodes[node_no] = gProgram_state.AI_vehicles.path_nodes[node_no + 1];
  3216.     }
  3217.     for (section_no = 0; section_no < gProgram_state.AI_vehicles.number_of_path_sections; section_no++) {
  3218.         if (pNode_to_delete < gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]) {
  3219.             gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]--;
  3220.         }
  3221.         if (pNode_to_delete < gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]) {
  3222.             gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]--;
  3223.         }
  3224.     }
  3225.     gProgram_state.AI_vehicles.number_of_path_nodes--;
  3226. }
  3227.  
  3228. // IDA: void __cdecl DeleteOrphanNodes()
  3229. void DeleteOrphanNodes(void) {
  3230.     tS16 node_no;
  3231.     LOG_TRACE("()");
  3232.  
  3233.     for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
  3234.         while (node_no < gProgram_state.AI_vehicles.number_of_path_nodes && gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections == 0) {
  3235.             DeleteNode(node_no, 1);
  3236.         }
  3237.     }
  3238. }
  3239.  
  3240. // IDA: void __usercall InsertThisNodeInThisSectionHere(tS16 pInserted_node@<EAX>, tS16 pSection_no@<EDX>, br_vector3 *pWhere@<EBX>)
  3241. void InsertThisNodeInThisSectionHere(tS16 pInserted_node, tS16 pSection_no, br_vector3* pWhere) {
  3242.     tS16 new_section;
  3243.     tS16 section_no_index;
  3244.     tS16 node1;
  3245.     //tS16 node2; // Pierre-Marie Baty -- unused variable
  3246.     //tS16 node3; // Pierre-Marie Baty -- unused variable
  3247.     LOG_TRACE("(%d, %d, %p)", pInserted_node, pSection_no, pWhere);
  3248.  
  3249.     section_no_index = gProgram_state.AI_vehicles.path_sections[pSection_no].node_indices[1];
  3250.     new_section = ReallocExtraPathSections(1);
  3251.     gProgram_state.AI_vehicles.path_sections[new_section].node_indices[0] = pInserted_node;
  3252.     gProgram_state.AI_vehicles.path_sections[new_section].node_indices[1] = section_no_index;
  3253.     gProgram_state.AI_vehicles.path_sections[new_section].min_speed[0] = 0;
  3254.     gProgram_state.AI_vehicles.path_sections[new_section].max_speed[0] = 255;
  3255.     gProgram_state.AI_vehicles.path_sections[new_section].min_speed[1] =
  3256.         gProgram_state.AI_vehicles.path_sections[pSection_no].min_speed[1];
  3257.     gProgram_state.AI_vehicles.path_sections[new_section].max_speed[1] =
  3258.         gProgram_state.AI_vehicles.path_sections[pSection_no].max_speed[1];
  3259.     gProgram_state.AI_vehicles.path_sections[new_section].width =
  3260.         gProgram_state.AI_vehicles.path_sections[pSection_no].width;
  3261.     gProgram_state.AI_vehicles.path_sections[new_section].type =
  3262.         gProgram_state.AI_vehicles.path_sections[pSection_no].type;
  3263.     gProgram_state.AI_vehicles.path_sections[new_section].one_way =
  3264.         gProgram_state.AI_vehicles.path_sections[pSection_no].one_way;
  3265.     gProgram_state.AI_vehicles.path_sections[pSection_no].node_indices[1] = pInserted_node;
  3266.     gProgram_state.AI_vehicles.path_sections[pSection_no].min_speed[1] = 0;
  3267.     gProgram_state.AI_vehicles.path_sections[pSection_no].max_speed[1] = 255;
  3268.     BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[pInserted_node].p, pWhere);
  3269.     gProgram_state.AI_vehicles.path_nodes[pInserted_node].sections
  3270.         [gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections] = pSection_no;
  3271.     gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections =
  3272.         gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections + 1;
  3273.     gProgram_state.AI_vehicles.path_nodes[pInserted_node].sections
  3274.         [gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections] = new_section;
  3275.     gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections =
  3276.         gProgram_state.AI_vehicles.path_nodes[pInserted_node].number_of_sections + 1;
  3277.     for (node1 = 0; node1 < gProgram_state.AI_vehicles.path_nodes[section_no_index].number_of_sections; node1++) {
  3278.         if (gProgram_state.AI_vehicles.path_nodes[section_no_index].sections[node1] == pSection_no) {
  3279.             gProgram_state.AI_vehicles.path_nodes[section_no_index].sections[node1] = new_section;
  3280.         }
  3281.     }
  3282. }
  3283.  
  3284. // IDA: void __cdecl TrackElasticateyPath()
  3285. void TrackElasticateyPath(void) {
  3286.     LOG_TRACE("()");
  3287.  
  3288.     if (gAlready_elasticating && gNext_elastication < gTime_stamp_for_this_munging) {
  3289.         gNext_elastication = gTime_stamp_for_this_munging + 2000;
  3290.         BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p, &gSelf->t.t.translate.t);
  3291.         RebuildOppoPathModel();
  3292.         if (gNext_write_during_elastication < gTime_stamp_for_this_munging) {
  3293.             gNext_write_during_elastication = gTime_stamp_for_this_munging + 10000;
  3294.             WriteOutOppoPaths();
  3295.         }
  3296.     }
  3297. }
  3298.  
  3299. // IDA: void __usercall RecalcNearestPathSectionSpeed(int pMax_not_min@<EAX>, int pAdjustment@<EDX>)
  3300. void RecalcNearestPathSectionSpeed(int pMax_not_min, int pAdjustment) {
  3301.     tS16 section_no;
  3302.     br_vector3 direction_v;
  3303.     br_vector3 intersect;
  3304.     br_vector3 wank;
  3305.     br_scalar distance;
  3306.     br_scalar dist_to_start;
  3307.     br_scalar dist_to_finish;
  3308.     char str[128];
  3309.     int new_speed;
  3310.     int nearest_end;
  3311.     LOG_TRACE("(%d, %d)", pMax_not_min, pAdjustment);
  3312.  
  3313.     if (gOppo_paths_shown) {
  3314.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  3315.         if (!gAlready_elasticating && distance > 10.f) {
  3316.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any paths close enough");
  3317.         } else {
  3318.             BrVector3Sub(&wank, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p);
  3319.             dist_to_start = BrVector3Length(&wank);
  3320.             BrVector3Sub(&wank, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p);
  3321.             dist_to_finish = BrVector3Length(&wank);
  3322.             nearest_end = dist_to_finish < dist_to_start ? 1 : 0;
  3323.             if (pMax_not_min) {
  3324.                 new_speed = gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end];
  3325.             } else {
  3326.                 new_speed = gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end];
  3327.             }
  3328.             new_speed += 5 * pAdjustment;
  3329.             if (5 * pAdjustment < 0 && new_speed > 100) {
  3330.                 new_speed = 100;
  3331.             } else if (5 * pAdjustment > 0 && new_speed > 100) {
  3332.                 new_speed = 255;
  3333.             }
  3334.             if (new_speed < 0) {
  3335.                 new_speed = 0;
  3336.             } else if (new_speed > 255) {
  3337.                 new_speed = 255;
  3338.             }
  3339.             if (pMax_not_min) {
  3340.                 gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end] = new_speed;
  3341.             } else {
  3342.                 gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end] = new_speed;
  3343.             }
  3344.             if (nearest_end != 0) {
  3345.                 sprintf(str, "Towards section finish - Min Speed %d mph - Max speed %d mph",
  3346.                     (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[nearest_end]),
  3347.                     (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[nearest_end]));
  3348.             } else {
  3349.                 sprintf(str, "Towards section start - Min Speed %d mph - Max speed %d mph",
  3350.                     (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0]),
  3351.                     (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0]));
  3352.             }
  3353.             ShowOppoPaths();
  3354.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  3355.         }
  3356.     }
  3357. }
  3358.  
  3359. // IDA: void __cdecl RecalcNearestPathSectionWidth(br_scalar pAdjustment)
  3360. void RecalcNearestPathSectionWidth(br_scalar pAdjustment) {
  3361.     tS16 section_no;
  3362.     br_vector3 direction_v;
  3363.     br_vector3 intersect;
  3364.     br_scalar distance;
  3365.     char str[128];
  3366.     LOG_TRACE("(%f)", pAdjustment);
  3367.  
  3368.     if (gOppo_paths_shown) {
  3369.         if (!gAlready_elasticating) {
  3370.             section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  3371.             if (distance > 10.f) {
  3372.                 NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any paths close enough");
  3373.                 return;
  3374.             }
  3375.         } else {
  3376.             section_no = gMobile_section;
  3377.         }
  3378.         gProgram_state.AI_vehicles.path_sections[section_no].width += (int)pAdjustment * pAdjustment + pAdjustment;
  3379.         if (gProgram_state.AI_vehicles.path_sections[section_no].width < .05f) {
  3380.             gProgram_state.AI_vehicles.path_sections[section_no].width = .05f;
  3381.         }
  3382.         ShowOppoPaths();
  3383.         sprintf(str, "Width %2.1f BRU", 2.f * gProgram_state.AI_vehicles.path_sections[section_no].width);
  3384.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  3385.     }
  3386. }
  3387.  
  3388. // IDA: void __usercall CalcNegativeXVector(br_vector3 *pNegative_x_vector@<EAX>, br_vector3 *pStart@<EDX>, br_vector3 *pFinish@<EBX>, br_scalar pLength)
  3389. void CalcNegativeXVector(br_vector3* pNegative_x_vector, br_vector3* pStart, br_vector3* pFinish, br_scalar pLength) {
  3390.     br_vector3 positive_y_vector;
  3391.     //br_vector3 path_vector; // Pierre-Marie Baty -- unused variable
  3392.     LOG_TRACE("(%p, %p, %p, %f)", pNegative_x_vector, pStart, pFinish, pLength);
  3393.  
  3394.     positive_y_vector.v[0] = pFinish->v[0] - pStart->v[0];
  3395.     positive_y_vector.v[1] = pFinish->v[1] - pStart->v[1];
  3396.     positive_y_vector.v[2] = pFinish->v[2] - pStart->v[2];
  3397.     pNegative_x_vector->v[0] = 1.0 * positive_y_vector.v[2] - positive_y_vector.v[1] * 0.0;
  3398.     pNegative_x_vector->v[1] = 0.0 * positive_y_vector.v[0] - positive_y_vector.v[2] * 0.0;
  3399.     pNegative_x_vector->v[2] = positive_y_vector.v[1] * 0.0 - 1.0 * positive_y_vector.v[0];
  3400.  
  3401.     BrVector3Normalise(pNegative_x_vector, pNegative_x_vector);
  3402.     BrVector3Scale(pNegative_x_vector, pNegative_x_vector, pLength);
  3403. }
  3404.  
  3405. // IDA: void __usercall MakeVertexAndOffsetIt(br_model *pModel@<EAX>, int pVertex_num@<EDX>, br_scalar pX, br_scalar pY, br_scalar pZ, br_vector3 *pOffset)
  3406. void MakeVertexAndOffsetIt(br_model* pModel, int pVertex_num, br_scalar pX, br_scalar pY, br_scalar pZ, br_vector3* pOffset) {
  3407.     LOG_TRACE("(%p, %d, %f, %f, %f, %p)", pModel, pVertex_num, pX, pY, pZ, pOffset);
  3408.  
  3409.     BrVector3Set(&pModel->vertices[pVertex_num].p, pX, pY, pZ);
  3410.     BrVector3Accumulate(&pModel->vertices[pVertex_num].p, pOffset);
  3411. }
  3412.  
  3413. // IDA: void __usercall MakeFaceAndTextureIt(br_model *pModel@<EAX>, int pFace_num@<EDX>, int pV0@<EBX>, int pV1@<ECX>, int pV2, br_material *pMaterial)
  3414. void MakeFaceAndTextureIt(br_model* pModel, int pFace_num, int pV0, int pV1, int pV2, br_material* pMaterial) {
  3415.     LOG_TRACE("(%p, %d, %d, %d, %d, %p)", pModel, pFace_num, pV0, pV1, pV2, pMaterial);
  3416.  
  3417.     pModel->faces[pFace_num].vertices[0] = pV0;
  3418.     pModel->faces[pFace_num].vertices[1] = pV1;
  3419.     pModel->faces[pFace_num].vertices[2] = pV2;
  3420.     pModel->faces[pFace_num].smoothing = -1;
  3421.     pModel->faces[pFace_num].material = pMaterial;
  3422. }
  3423.  
  3424. // IDA: void __usercall MakeSection(br_uint_16 pFirst_vertex@<EAX>, br_uint_16 pFirst_face@<EDX>, br_vector3 *pStart@<EBX>, br_vector3 *pFinish@<ECX>, br_scalar pWidth, br_material *pMaterial_centre_lt, br_material *pMaterial_centre_dk, br_material *pMaterial_edges_start_lt, br_material *pMaterial_edges_start_dk, br_material *pMaterial_edges_finish_lt, br_material *pMaterial_edges_finish_dk)
  3425. void MakeSection(br_uint_16 pFirst_vertex, br_uint_16 pFirst_face, br_vector3* pStart, br_vector3* pFinish, br_scalar pWidth, br_material* pMaterial_centre_lt, br_material* pMaterial_centre_dk, br_material* pMaterial_edges_start_lt, br_material* pMaterial_edges_start_dk, br_material* pMaterial_edges_finish_lt, br_material* pMaterial_edges_finish_dk) {
  3426.     int i;
  3427.     br_vector3 offset_v;
  3428.     br_vector3 centre_length_v;
  3429.     br_material* the_material_start_lt;
  3430.     br_material* the_material_start_dk;
  3431.     br_material* the_material_finish_lt;
  3432.     br_material* the_material_finish_dk;
  3433.     br_scalar height;
  3434.     LOG_TRACE("(%d, %d, %p, %p, %f, %p, %p, %p, %p, %p, %p)", pFirst_vertex, pFirst_face, pStart, pFinish, pWidth, pMaterial_centre_lt, pMaterial_centre_dk, pMaterial_edges_start_lt, pMaterial_edges_start_dk, pMaterial_edges_finish_lt, pMaterial_edges_finish_dk);
  3435.  
  3436.     CalcNegativeXVector(&offset_v, pStart, pFinish, pWidth);
  3437.     for (i = 0; i < 3; i++) {
  3438.         the_material_start_lt = pMaterial_edges_start_lt;
  3439.         the_material_start_dk = pMaterial_edges_start_dk;
  3440.         the_material_finish_lt = pMaterial_edges_finish_lt;
  3441.         the_material_finish_dk = pMaterial_edges_finish_dk;
  3442.         height = .15f;
  3443.         if (i == 1) {
  3444.             BrVector3Negate(&offset_v, &offset_v);
  3445.         } else if (i == 2) {
  3446.             height = .3f;
  3447.             BrVector3Set(&offset_v, 0.f, 0.f, 0.f);
  3448.             the_material_finish_lt = pMaterial_centre_lt;
  3449.             the_material_start_lt = pMaterial_centre_lt;
  3450.             the_material_finish_dk = pMaterial_centre_dk;
  3451.             the_material_start_dk = pMaterial_centre_dk;
  3452.         }
  3453.         centre_length_v.v[0] = pStart->v[0] + (pFinish->v[0] - pStart->v[0]) / 2.f;
  3454.         centre_length_v.v[1] = pStart->v[1] + (pFinish->v[1] - pStart->v[1]) / 2.f;
  3455.         centre_length_v.v[2] = pStart->v[2] + (pFinish->v[2] - pStart->v[2]) / 2.f;
  3456.  
  3457.         MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 0, pStart->v[0], pStart->v[1], pStart->v[2], &offset_v);
  3458.         MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 1, pStart->v[0], pStart->v[1] + height, pStart->v[2], &offset_v);
  3459.         MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 2, centre_length_v.v[0], centre_length_v.v[1] + height, centre_length_v.v[2], &offset_v);
  3460.         MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 3, centre_length_v.v[0], centre_length_v.v[1], centre_length_v.v[2], &offset_v);
  3461.         MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 4, pFinish->v[0], pFinish->v[1] + height, pFinish->v[2], &offset_v);
  3462.         MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6 * i + 5, pFinish->v[0], pFinish->v[1], pFinish->v[2], &offset_v);
  3463.  
  3464.         MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 0, pFirst_vertex + 6 * i + 0, pFirst_vertex + 6 * i + 1, pFirst_vertex + 6 * i + 2, the_material_start_lt);
  3465.         MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 1, pFirst_vertex + 6 * i + 0, pFirst_vertex + 6 * i + 2, pFirst_vertex + 6 * i + 3, the_material_start_dk);
  3466.         MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 2, pFirst_vertex + 6 * i + 3, pFirst_vertex + 6 * i + 2, pFirst_vertex + 6 * i + 4, the_material_finish_lt);
  3467.         MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4 * i + 3, pFirst_vertex + 6 * i + 3, pFirst_vertex + 6 * i + 4, pFirst_vertex + 6 * i + 5, the_material_finish_dk);
  3468.     }
  3469. }
  3470.  
  3471. // IDA: void __usercall MakeCube(br_uint_16 pFirst_vertex@<EAX>, br_uint_16 pFirst_face@<EDX>, br_vector3 *pPoint@<EBX>, br_material *pMaterial_1@<ECX>, br_material *pMaterial_2, br_material *pMaterial_3)
  3472. void MakeCube(br_uint_16 pFirst_vertex, br_uint_16 pFirst_face, br_vector3* pPoint, br_material* pMaterial_1, br_material* pMaterial_2, br_material* pMaterial_3) {
  3473.     br_vector3 offset_v;
  3474.     br_vector3 point;
  3475.     LOG_TRACE("(%d, %d, %p, %p, %p, %p)", pFirst_vertex, pFirst_face, pPoint, pMaterial_1, pMaterial_2, pMaterial_3);
  3476.  
  3477.     BrVector3Set(&point, pPoint->v[0], pPoint->v[1] + .15f, pPoint->v[2]);
  3478.  
  3479.     BrVector3Set(&offset_v, .1f, .1f, .1f);
  3480.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 0, point.v[0], point.v[1], point.v[2], &offset_v);
  3481.     BrVector3Set(&offset_v, .1f, -.1f, .1f);
  3482.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 1, point.v[0], point.v[1], point.v[2], &offset_v);
  3483.     BrVector3Set(&offset_v, -.1f, -.1f, .1f);
  3484.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 2, point.v[0], point.v[1], point.v[2], &offset_v);
  3485.     BrVector3Set(&offset_v, -.1f, .1f, .1f);
  3486.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 3, point.v[0], point.v[1], point.v[2], &offset_v);
  3487.     BrVector3Set(&offset_v, .1f, .1f, -.1f);
  3488.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 4, point.v[0], point.v[1], point.v[2], &offset_v);
  3489.     BrVector3Set(&offset_v, .1f, -.1f, -.1f);
  3490.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 5, point.v[0], point.v[1], point.v[2], &offset_v);
  3491.     BrVector3Set(&offset_v, -.1f, -.1f, -.1f);
  3492.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 6, point.v[0], point.v[1], point.v[2], &offset_v);
  3493.     BrVector3Set(&offset_v, -.1f, .1f, -.1f);
  3494.     MakeVertexAndOffsetIt(gOppo_path_model, pFirst_vertex + 7, point.v[0], point.v[1], point.v[2], &offset_v);
  3495.  
  3496.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 0, pFirst_vertex + 3, pFirst_vertex + 2, pFirst_vertex + 1, pMaterial_1);
  3497.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 1, pFirst_vertex + 0, pFirst_vertex + 3, pFirst_vertex + 1, pMaterial_1);
  3498.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 2, pFirst_vertex + 1, pFirst_vertex + 5, pFirst_vertex + 4, pMaterial_2);
  3499.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 3, pFirst_vertex + 1, pFirst_vertex + 4, pFirst_vertex + 0, pMaterial_2);
  3500.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 4, pFirst_vertex + 0, pFirst_vertex + 4, pFirst_vertex + 3, pMaterial_3);
  3501.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 5, pFirst_vertex + 3, pFirst_vertex + 4, pFirst_vertex + 7, pMaterial_3);
  3502.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 6, pFirst_vertex + 4, pFirst_vertex + 5, pFirst_vertex + 7, pMaterial_1);
  3503.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 7, pFirst_vertex + 5, pFirst_vertex + 6, pFirst_vertex + 7, pMaterial_1);
  3504.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 8, pFirst_vertex + 2, pFirst_vertex + 7, pFirst_vertex + 6, pMaterial_2);
  3505.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 9, pFirst_vertex + 2, pFirst_vertex + 3, pFirst_vertex + 7, pMaterial_2);
  3506.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 10, pFirst_vertex + 1, pFirst_vertex + 2, pFirst_vertex + 6, pMaterial_3);
  3507.     MakeFaceAndTextureIt(gOppo_path_model, pFirst_face + 11, pFirst_vertex + 1, pFirst_vertex + 6, pFirst_vertex + 5, pMaterial_3);
  3508. }
  3509.  
  3510. // IDA: void __usercall CalcNumberOfFacesAndVerticesForOppoPathModel(br_uint_16 *pFace_index_ptr@<EAX>, br_uint_16 *pVertex_index_ptr@<EDX>)
  3511. void CalcNumberOfFacesAndVerticesForOppoPathModel(br_uint_16* pFace_index_ptr, br_uint_16* pVertex_index_ptr) {
  3512.     LOG_TRACE("(%p, %p)", pFace_index_ptr, pVertex_index_ptr);
  3513.  
  3514.     *pFace_index_ptr = gProgram_state.AI_vehicles.number_of_path_sections * 12 + gProgram_state.AI_vehicles.number_of_cops * 12;
  3515.     *pVertex_index_ptr = gProgram_state.AI_vehicles.number_of_path_sections * 18 + gProgram_state.AI_vehicles.number_of_cops * 8;
  3516. }
  3517.  
  3518. // IDA: void __usercall ReallocModelFacesAndVertices(br_model *pModel@<EAX>, int pNum_faces@<EDX>, int pNum_vertices@<EBX>)
  3519. void ReallocModelFacesAndVertices(br_model* pModel, int pNum_faces, int pNum_vertices) {
  3520.     br_vertex* new_vertices;
  3521.     br_face* new_faces;
  3522.     int i;
  3523.     LOG_TRACE("(%p, %d, %d)", pModel, pNum_faces, pNum_vertices);
  3524.  
  3525.     new_vertices = BrResAllocate(pModel, pNum_vertices * sizeof(br_vertex), BR_MEMORY_VERTICES);
  3526.     memset(new_vertices, 0, pNum_vertices * sizeof(br_vertex));
  3527.     if (pModel->nvertices != 0) {
  3528.         for (i = 0; i < ((pNum_vertices <= pModel->nvertices) ? pNum_vertices : pModel->nvertices); i++) {
  3529.             CopyVertex__opponent(&new_vertices[i], &pModel->vertices[i]);
  3530.         }
  3531.         BrResRemove(pModel->vertices);
  3532.         BrResFree(pModel->vertices);
  3533.     }
  3534.     pModel->vertices = new_vertices;
  3535.     pModel->nvertices = pNum_vertices;
  3536.  
  3537.     new_faces = BrResAllocate(pModel, pNum_faces * sizeof(br_face), BR_MEMORY_FACES);
  3538.     memset(new_faces, 0, pNum_faces * sizeof(br_face));
  3539.     if (pModel->nfaces != 0) {
  3540.         for (i = 0; i < ((pNum_faces <= pModel->nfaces) ? pNum_faces : pModel->nfaces); i++) {
  3541.             CopyFace__opponent(&new_faces[i], &pModel->faces[i]);
  3542.         }
  3543.         BrResRemove(pModel->faces);
  3544.         BrResFree(pModel->faces);
  3545.     }
  3546.     pModel->faces = new_faces;
  3547.     pModel->nfaces = pNum_faces;
  3548. }
  3549.  
  3550. // IDA: br_material* __usercall CreateSimpleMaterial@<EAX>(int pColour_index@<EAX>)
  3551. br_material* CreateSimpleMaterial(int pColour_index) {
  3552.     br_material* return_me;
  3553.     LOG_TRACE("(%d)", pColour_index);
  3554.  
  3555.     return_me = BrMaterialAllocate(NULL);
  3556.     return_me->index_base = pColour_index;
  3557.     return_me->index_range = 1;
  3558.     return_me->flags = BR_MATF_TWO_SIDED;
  3559.     return_me->index_shade = NULL;
  3560.     return_me->colour_map = NULL;
  3561.     return_me->identifier = NULL;
  3562.     BrMaterialAdd(return_me);
  3563.     return return_me;
  3564. }
  3565.  
  3566. // IDA: void __cdecl AllocateMatsForOppoPathModel()
  3567. void AllocateMatsForOppoPathModel(void) {
  3568.     LOG_TRACE("()");
  3569.  
  3570.     gMat_dk_yel = CreateSimpleMaterial(50);
  3571.     gMat_md_yel = CreateSimpleMaterial(51);
  3572.     gMat_lt_yel = CreateSimpleMaterial(52);
  3573.     gMat_dk_red = CreateSimpleMaterial(3);
  3574.     gMat_lt_red = CreateSimpleMaterial(4);
  3575.     gMat_dk_grn = CreateSimpleMaterial(66);
  3576.     gMat_lt_grn = CreateSimpleMaterial(68);
  3577.     gMat_dk_blu = CreateSimpleMaterial(162);
  3578.     gMat_lt_blu = CreateSimpleMaterial(164);
  3579.     gMat_dk_turq = CreateSimpleMaterial(130);
  3580.     gMat_lt_turq = CreateSimpleMaterial(132);
  3581.     gMat_dk_gry = CreateSimpleMaterial(253);
  3582.     gMat_md_gry = CreateSimpleMaterial(254);
  3583.     gMat_lt_gry = CreateSimpleMaterial(255);
  3584.  
  3585.     gMats_allocated = 1;
  3586. }
  3587.  
  3588. // IDA: void __cdecl RebuildOppoPathModel()
  3589. void RebuildOppoPathModel(void) {
  3590.     static int nvertices_last_time = 0;
  3591.     static int nfaces_last_time = 0;
  3592.     int i;
  3593.     //int at_least_one; // Pierre-Marie Baty -- unused variable
  3594.     br_uint_16 nfaces;
  3595.     br_uint_16 nvertices;
  3596.     //br_uint_16 first_face; // Pierre-Marie Baty -- unused variable
  3597.     //br_uint_16 first_vertex; // Pierre-Marie Baty -- unused variable
  3598.     br_material* centre_mat_lt;
  3599.     br_material* centre_mat_dk;
  3600.     br_material* edge_mat_start_lt;
  3601.     br_material* edge_mat_start_dk;
  3602.     br_material* edge_mat_finish_lt;
  3603.     br_material* edge_mat_finish_dk;
  3604.     LOG_TRACE("()");
  3605.  
  3606.     if (gProgram_state.AI_vehicles.number_of_path_nodes < 2) {
  3607.         if (gOppo_path_model != NULL) {
  3608.             BrModelRemove(gOppo_path_model);
  3609.             BrModelFree(gOppo_path_model);
  3610.             gOppo_path_model = NULL;
  3611.         }
  3612.         if (gOppo_path_actor != NULL) {
  3613.             gOppo_path_actor->type = BR_ACTOR_NONE;
  3614.             gOppo_path_actor->render_style = BR_RSTYLE_NONE;
  3615.         }
  3616.     } else {
  3617.         if (!gMats_allocated) {
  3618.             AllocateMatsForOppoPathModel();
  3619.         }
  3620.         if (gOppo_path_actor == NULL) {
  3621.             gOppo_path_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
  3622.             BrActorAdd(gNon_track_actor, gOppo_path_actor);
  3623.         }
  3624.         if (gOppo_path_model == NULL) {
  3625.             gOppo_path_actor->model = BrModelAllocate("OppoPathModel", 3, 1);
  3626.             gOppo_path_model = gOppo_path_actor->model;
  3627.             gOppo_path_model->flags |= BR_MODF_DONT_WELD | BR_MODF_KEEP_ORIGINAL | BR_MODF_GENERATE_TAGS;
  3628.             BrModelAdd(gOppo_path_model);
  3629.         }
  3630.         gOppo_path_actor->model = gOppo_path_model;
  3631.         gOppo_path_actor->type = BR_ACTOR_MODEL;
  3632.         gOppo_path_actor->render_style = BR_RSTYLE_FACES;
  3633.         CalcNumberOfFacesAndVerticesForOppoPathModel(&nfaces, &nvertices);
  3634.         if (nvertices_last_time < nvertices || nfaces_last_time < nfaces) {
  3635.             ReallocModelFacesAndVertices(gOppo_path_model, nfaces, nvertices);
  3636.             nvertices_last_time = nvertices;
  3637.             nfaces_last_time = nfaces;
  3638.         } else {
  3639.             gOppo_path_model->nvertices = nvertices;
  3640.             gOppo_path_model->nfaces = nfaces;
  3641.         }
  3642.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
  3643.             centre_mat_lt = gMat_lt_grn;
  3644.             centre_mat_dk = gMat_dk_grn;
  3645.             edge_mat_start_lt = gMat_lt_grn;
  3646.             edge_mat_start_dk = gMat_dk_grn;
  3647.             edge_mat_finish_lt = gMat_lt_grn;
  3648.             edge_mat_finish_dk = gMat_dk_grn;
  3649.             if (gProgram_state.AI_vehicles.path_sections[i].type == 1) {
  3650.                 centre_mat_lt = gMat_lt_red;
  3651.                 centre_mat_dk = gMat_dk_red;
  3652.             } else if (gProgram_state.AI_vehicles.path_sections[i].type == 2) {
  3653.                 centre_mat_lt = gMat_lt_blu;
  3654.                 centre_mat_dk = gMat_dk_blu;
  3655.             }
  3656.             if (gProgram_state.AI_vehicles.path_sections[i].one_way) {
  3657.                 centre_mat_lt = gMat_lt_yel;
  3658.             }
  3659.             if ((gProgram_state.AI_vehicles.path_sections[i].min_speed[0] != 0) ||
  3660.                 (gProgram_state.AI_vehicles.path_sections[i].max_speed[0] != 255)) {
  3661.                 edge_mat_start_lt = gMat_lt_turq;
  3662.                 edge_mat_start_dk = gMat_dk_turq;
  3663.             }
  3664.             if ((gProgram_state.AI_vehicles.path_sections[i].min_speed[1] != 0) ||
  3665.                 (gProgram_state.AI_vehicles.path_sections[i].max_speed[1] != 255)) {
  3666.                 edge_mat_finish_lt = gMat_lt_turq;
  3667.                 edge_mat_finish_dk = gMat_dk_turq;
  3668.             }
  3669.             if (gAlready_elasticating && gMobile_section == i) {
  3670.                 BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p,
  3671.                     &gSelf->t.t.translate.t);
  3672.             }
  3673.             MakeSection(18 * i, 12 * i,
  3674.                 &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[0]].p,
  3675.                 &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[i].node_indices[1]].p,
  3676.                 gProgram_state.AI_vehicles.path_sections[i].width,
  3677.                 centre_mat_lt,centre_mat_dk,
  3678.                 edge_mat_start_lt,edge_mat_start_dk,
  3679.                 edge_mat_finish_lt,edge_mat_finish_dk);
  3680.         }
  3681.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  3682.             MakeCube(18 * gProgram_state.AI_vehicles.number_of_path_sections + 8 * i,
  3683.                 12 * gProgram_state.AI_vehicles.number_of_path_sections + 12 * i,
  3684.                 gProgram_state.AI_vehicles.cop_start_points + i,
  3685.                 gMat_lt_turq,
  3686.                 gMat_lt_turq,
  3687.                 gMat_dk_turq);
  3688.         }
  3689.         BrModelUpdate(gOppo_path_model, BR_MODU_ALL);
  3690.     }
  3691. }
  3692.  
  3693. // IDA: int __cdecl ConsistencyCheck()
  3694. int ConsistencyCheck(void) {
  3695.     tS16 node_no;
  3696.     tS16 section_no;
  3697.     tS16 start_node;
  3698.     tS16 finish_node;
  3699.     tS16 section_no_index;
  3700.     tS16 section_no_index1;
  3701.     int found_how_many;
  3702.     int failed;
  3703.     tU8* nodes_referenced_by_sections_array = NULL;
  3704.     tU8* sections_referenced_by_nodes_array = NULL;
  3705.     LOG_TRACE("()");
  3706.  
  3707.     failed = 0;
  3708.     if (gProgram_state.AI_vehicles.number_of_path_nodes != 0) {
  3709.         nodes_referenced_by_sections_array = BrMemAllocate(gProgram_state.AI_vehicles.number_of_path_nodes, kMem_nodes_array);
  3710.         memset(nodes_referenced_by_sections_array, 0, gProgram_state.AI_vehicles.number_of_path_nodes);
  3711.     }
  3712.     if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
  3713.         sections_referenced_by_nodes_array = BrMemAllocate(gProgram_state.AI_vehicles.number_of_path_sections, kMem_sections_array);
  3714.         memset(sections_referenced_by_nodes_array, 0, gProgram_state.AI_vehicles.number_of_path_sections);
  3715.     }
  3716.     for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.number_of_path_sections; section_no_index++) {
  3717.         start_node = gProgram_state.AI_vehicles.path_sections[section_no_index].node_indices[0];
  3718.         finish_node = gProgram_state.AI_vehicles.path_sections[section_no_index].node_indices[1];
  3719.         if (finish_node == start_node) {
  3720.             dr_dprintf("CONSISTENCY FAILURE: Section #%d has both ends attached to same node!", section_no_index);
  3721.             failed = 1;
  3722.         }
  3723.         if (start_node >= 0 && gProgram_state.AI_vehicles.number_of_path_nodes - 1 >= start_node) {
  3724.             nodes_referenced_by_sections_array[start_node] = 1;
  3725.             nodes_referenced_by_sections_array[finish_node] = 1;
  3726.             found_how_many = 0;
  3727.             for (section_no_index1 = 0; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[start_node].number_of_sections; section_no_index1++) {
  3728.                 if (gProgram_state.AI_vehicles.path_nodes[start_node].sections[section_no_index1] == section_no_index) {
  3729.                     found_how_many++;
  3730.                 }
  3731.             }
  3732.             if (found_how_many == 0) {
  3733.                 dr_dprintf(
  3734.                     "CONSISTENCY FAILURE: Section #%d references node #%d but not vice-versa",
  3735.                     section_no_index,
  3736.                     start_node);
  3737.                 failed = 1;
  3738.             }
  3739.         } else {
  3740.             dr_dprintf(
  3741.                 "CONSISTENCY FAILURE: Section #%d references invalid node (#%d) - should be in range 0..%d",
  3742.                 section_no_index,
  3743.                 start_node,
  3744.                 gProgram_state.AI_vehicles.number_of_path_nodes - 1);
  3745.             failed = 1;
  3746.         }
  3747.         if (finish_node >= 0 && gProgram_state.AI_vehicles.number_of_path_nodes - 1 >= finish_node) {
  3748.             found_how_many = 0;
  3749.             for (section_no_index1 = 0; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[finish_node].number_of_sections; section_no_index1++) {
  3750.                 if (gProgram_state.AI_vehicles.path_nodes[finish_node].sections[section_no_index1] == section_no_index) {
  3751.                     found_how_many++;
  3752.                 }
  3753.             }
  3754.             if (found_how_many == 0) {
  3755.                 dr_dprintf(
  3756.                     "CONSISTENCY FAILURE: Section #%d references node #%d but not vice-versa",
  3757.                     section_no_index,
  3758.                     finish_node);
  3759.                 failed = 1;
  3760.             }
  3761.         } else {
  3762.             dr_dprintf(
  3763.                 "CONSISTENCY FAILURE: Section #%d references invalid node (#%d) - should be in range 0..%d",
  3764.                 section_no_index,
  3765.                 finish_node,
  3766.                 gProgram_state.AI_vehicles.number_of_path_nodes - 1);
  3767.             failed = 1;
  3768.         }
  3769.     }
  3770.     for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
  3771.         for (section_no_index = 0; section_no_index < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index++) {
  3772.             section_no = gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index];
  3773.             if (section_no >= 0 && gProgram_state.AI_vehicles.number_of_path_sections - 1 >= section_no) {
  3774.                 sections_referenced_by_nodes_array[section_no] = 1;
  3775.                 if (gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] != node_no
  3776.                     && gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] != node_no) {
  3777.                     dr_dprintf(
  3778.                         "CONSISTENCY FAILURE: Node #%d references section #%d but not vice-versa",
  3779.                         node_no,
  3780.                         section_no);
  3781.                     failed = 1;
  3782.                 }
  3783.             } else {
  3784.                 dr_dprintf(
  3785.                     "CONSISTENCY FAILURE: Node #%d references invalid section (#%d) - should be in range 0..%d",
  3786.                     node_no,
  3787.                     section_no,
  3788.                     gProgram_state.AI_vehicles.number_of_path_sections - 1);
  3789.                 failed = 1;
  3790.             }
  3791.             found_how_many = 0;
  3792.             for (section_no_index1 = section_no; section_no_index1 < gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections; section_no_index1++) {
  3793.                 if (gProgram_state.AI_vehicles.path_nodes[node_no].sections[section_no_index1] == section_no) {
  3794.                     found_how_many++;
  3795.                 }
  3796.             }
  3797.             if (found_how_many > 1) {
  3798.                 dr_dprintf(
  3799.                     "CONSISTENCY FAILURE: Node #%d references section #%d multiple times",
  3800.                     node_no,
  3801.                     section_no);
  3802.                 failed = 1;
  3803.             }
  3804.         }
  3805.     }
  3806.     for (section_no = 0; section_no < gProgram_state.AI_vehicles.number_of_path_sections; section_no++) {
  3807.         if (!sections_referenced_by_nodes_array[section_no]) {
  3808.             dr_dprintf("CONSISTENCY FAILURE: Section #%d not referenced by any nodes", section_no);
  3809.             failed = 1;
  3810.         }
  3811.     }
  3812.     for (node_no = 0; node_no < gProgram_state.AI_vehicles.number_of_path_nodes; node_no++) {
  3813.         if (!nodes_referenced_by_sections_array[node_no]) {
  3814.             dr_dprintf("CONSISTENCY FAILURE: Node #%d not referenced by any sections", node_no);
  3815.             failed = 1;
  3816.         }
  3817.     }
  3818.     if (gProgram_state.AI_vehicles.number_of_path_nodes != 0) {
  3819.         BrMemFree(nodes_referenced_by_sections_array);
  3820.     }
  3821.     if (gProgram_state.AI_vehicles.number_of_path_sections != 0) {
  3822.         BrMemFree(sections_referenced_by_nodes_array);
  3823.     }
  3824.     if (failed) {
  3825.         dr_dprintf(
  3826.             "CONSISTENCY FAILURE INFORMATION: Allegedly %d sections and %d nodes",
  3827.             gProgram_state.AI_vehicles.number_of_path_sections,
  3828.             gProgram_state.AI_vehicles.number_of_path_nodes);
  3829.         dr_dprintf("^^^ CONSISTENCY FAILURE ^^^");
  3830.         PDEnterDebugger("OPPONENT PATH CONSISTENCY FAILURE - refer to DIAGNOST.TXT");
  3831.     }
  3832.     return !failed;
  3833. }
  3834.  
  3835. // IDA: void __cdecl ShowOppoPaths()
  3836. void ShowOppoPaths(void) {
  3837.     char str[256];
  3838.     LOG_TRACE("()");
  3839.  
  3840.     if (!gOppo_paths_shown) {
  3841.         if (gOppo_path_actor != NULL) {
  3842.             gOppo_path_actor->render_style = BR_RSTYLE_NONE;
  3843.         }
  3844.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -1, "Not displaying any paths");
  3845.     } else {
  3846.         RebuildOppoPathModel();
  3847.         sprintf(str, "Total %d nodes, %d sections",
  3848.             gProgram_state.AI_vehicles.number_of_path_nodes,
  3849.             gProgram_state.AI_vehicles.number_of_path_sections);
  3850.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -1, str);
  3851.     }
  3852.     if (ConsistencyCheck()) {
  3853.         WriteOutOppoPaths();
  3854.     }
  3855. }
  3856.  
  3857. #include <errno.h>
  3858. #include <string.h>
  3859.  
  3860. // IDA: void __cdecl WriteOutOppoPaths()
  3861. void WriteOutOppoPaths(void) {
  3862.     char the_path[256];
  3863.     char str[13];
  3864.     FILE* f;
  3865.     int i;
  3866.     LOG_TRACE("()");
  3867.  
  3868.     if (!gMade_path_filename) {
  3869.         for (i = 0; 1; i++) {
  3870. #ifdef DETHRACE_FIX_BUGS
  3871.             sprintf(str, "OPATH%03d.TXT", i);
  3872. #else
  3873.             sprintf(str, "OPATH%0.3d.TXT", i);
  3874. #endif
  3875.             PathCat(the_path, gApplication_path, str);
  3876. #ifdef DETHRACE_FIX_BUGS
  3877.             // OldDRfopen refuses to open unknown .TXT files
  3878.             f = fopen(the_path, "r");
  3879. #else
  3880.             f = DRfopen(the_path, "r+");
  3881. #endif
  3882.             if (f == NULL) {
  3883.                 break;
  3884.             }
  3885.             fclose(f);
  3886.         }
  3887.         strcpy(gOppo_path_filename, the_path);
  3888.         gMade_path_filename = 1;
  3889.     }
  3890. #ifdef DETHRACE_FIX_BUGS
  3891.     f = fopen(gOppo_path_filename, "w");
  3892. #else
  3893.     f = DRfopen(gOppo_path_filename, "wt");
  3894. #endif
  3895.     if (f == NULL) { printf("f is NULL, errno=%d, msg=\"%s\"\n", errno, strerror(errno));}
  3896.     fprintf(f, "%s\n", "START OF OPPONENT PATHS");
  3897.     fprintf(f, "\n%-3d                             // Number of path nodes\n",
  3898.         gProgram_state.AI_vehicles.number_of_path_nodes);
  3899.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_nodes; i++) {
  3900.         fprintf(f, "%9.3f,%9.3f,%9.3f   // Node #%d\n",
  3901.             gProgram_state.AI_vehicles.path_nodes[i].p.v[0],
  3902.             gProgram_state.AI_vehicles.path_nodes[i].p.v[1],
  3903.             gProgram_state.AI_vehicles.path_nodes[i].p.v[2],
  3904.             i);
  3905.     }
  3906.     fprintf(f, "\n%-3d                                           // Number of path sections\n",
  3907.         gProgram_state.AI_vehicles.number_of_path_sections);
  3908.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_path_sections; i++) {
  3909.         fprintf(f, "%4d,%4d,%4d,%4d,%4d,%4d,%7.1f,%5d   // Section #%d\n",
  3910.             gProgram_state.AI_vehicles.path_sections[i].node_indices[0],
  3911.             gProgram_state.AI_vehicles.path_sections[i].node_indices[1],
  3912.             gProgram_state.AI_vehicles.path_sections[i].min_speed[0],
  3913.             gProgram_state.AI_vehicles.path_sections[i].max_speed[0],
  3914.             gProgram_state.AI_vehicles.path_sections[i].min_speed[1],
  3915.             gProgram_state.AI_vehicles.path_sections[i].max_speed[1],
  3916.             gProgram_state.AI_vehicles.path_sections[i].width,
  3917.             gProgram_state.AI_vehicles.path_sections[i].one_way ? (gProgram_state.AI_vehicles.path_sections[i].type + 1000) : gProgram_state.AI_vehicles.path_sections[i].type,
  3918.             i);
  3919.     }
  3920.     fprintf(f, "\n%-2d                                                            // Number of cop start points\n",
  3921.         gProgram_state.AI_vehicles.number_of_cops);
  3922.     for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  3923.         fprintf(f, "%9.3f,%9.3f,%9.3f,%9.3f,%9.3f,%9.3f   // Cop start point #%d\n",
  3924.             gProgram_state.AI_vehicles.cop_start_points[i].v[0],
  3925.             gProgram_state.AI_vehicles.cop_start_points[i].v[1],
  3926.             gProgram_state.AI_vehicles.cop_start_points[i].v[2],
  3927.             0.f, 0.f, 0.f, i);
  3928.     }
  3929.     fprintf(f, "END OF OPPONENT PATHS");
  3930.     fclose(f);
  3931. }
  3932.  
  3933. // IDA: int __cdecl NewNodeOKHere()
  3934. int NewNodeOKHere(void) {
  3935.     br_vector3 last_node_to_this;
  3936.     LOG_TRACE("()");
  3937.  
  3938.     if (gAlready_elasticating) {
  3939.         BrVector3Sub(&last_node_to_this,
  3940.             &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p,
  3941.             &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0]].p);
  3942.         return BrVector3Length(&last_node_to_this) != 0.f;
  3943.     }
  3944.     return 1;
  3945. }
  3946.  
  3947. // IDA: void __cdecl ShowHideOppoPaths()
  3948. void ShowHideOppoPaths(void) {
  3949.     LOG_TRACE("()");
  3950.  
  3951.     if (!gAlready_elasticating) {
  3952.         gOppo_paths_shown = !gOppo_paths_shown;
  3953.         ShowOppoPaths();
  3954.     }
  3955. }
  3956.  
  3957. // IDA: void __cdecl DropElasticateyNode()
  3958. void DropElasticateyNode(void) {
  3959.     char str[256];
  3960.     tS16 old_node;
  3961.     tS16 new_node;
  3962.     tS16 section_no_index;
  3963.     br_scalar distance;
  3964.     int all_the_same_type;
  3965.     int one_wayness;
  3966.     //tPath_section_type_enum section_type; // Pierre-Marie Baty -- unused variable
  3967.     tPath_section_type_enum original_type;
  3968.     LOG_TRACE("()");
  3969.  
  3970.     all_the_same_type = 1;
  3971.     if (!NewNodeOKHere()) {
  3972.         return;
  3973.     }
  3974.     if (gAlready_elasticating) {
  3975.         old_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
  3976.         BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[old_node].p,
  3977.             &gProgram_state.current_car.car_master_actor->t.t.translate.t);
  3978.         original_type = gProgram_state.AI_vehicles.path_sections[gMobile_section].type;
  3979.         one_wayness = gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way;
  3980.         new_node = ReallocExtraPathNodes(1);
  3981.         gMobile_section = ReallocExtraPathSections(1);
  3982.     } else {
  3983.         if (!gOppo_paths_shown) {
  3984.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "You must show paths before adding to them (F5)");
  3985.             return;
  3986.         }
  3987.         if (gProgram_state.AI_vehicles.number_of_path_nodes == 0) {
  3988.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not implemented yet. Go away.");
  3989.             return;
  3990.         }
  3991.         old_node = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
  3992.         if (distance > 10.f) {
  3993.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
  3994.             return;
  3995.         }
  3996.         original_type = 0;
  3997.         if (gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections != 0) {
  3998.             for (section_no_index = 1; section_no_index < gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections; section_no_index++) {
  3999.                 if (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[section_no_index]].type !=
  4000.                         gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].type) {
  4001.                     all_the_same_type = 0;
  4002.                 }
  4003.             }
  4004.             if (all_the_same_type) {
  4005.                 original_type = gProgram_state.AI_vehicles.path_sections [gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].type;
  4006.             }
  4007.         }
  4008.         gAlready_elasticating = 1;
  4009.         new_node = ReallocExtraPathNodes(1);
  4010.         gMobile_section = ReallocExtraPathSections(1);
  4011.         one_wayness = 0;
  4012.     }
  4013.     gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0] = old_node;
  4014.     gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = new_node;
  4015.     gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0] = 0;
  4016.     gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1] = 0;
  4017.     gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0] = 255;
  4018.     gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1] = 255;
  4019.     gProgram_state.AI_vehicles.path_sections[gMobile_section].type = original_type;
  4020.     gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way = one_wayness;
  4021.     if (gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections == 0) {
  4022.         gProgram_state.AI_vehicles.path_sections[gMobile_section].width = 1.f;
  4023.     } else {
  4024.         gProgram_state.AI_vehicles.path_sections[gMobile_section].width =
  4025.             gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[old_node].sections[0]].width;
  4026.     }
  4027.     gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections = 0;
  4028.     gProgram_state.AI_vehicles.path_nodes[new_node].sections[gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections] = gMobile_section;
  4029.     gProgram_state.AI_vehicles.path_nodes[new_node].number_of_sections += 1;
  4030.     gProgram_state.AI_vehicles.path_nodes[old_node].sections[gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections] = gMobile_section;
  4031.     gProgram_state.AI_vehicles.path_nodes[old_node].number_of_sections += 1;
  4032.     ShowOppoPaths();
  4033.     sprintf(str, "New section #%d, new node #%d", gMobile_section, new_node);
  4034.     NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4035. }
  4036.  
  4037. // IDA: void __cdecl InsertAndElasticate()
  4038. void InsertAndElasticate(void) {
  4039.     tS16 inserted_node;
  4040.     tS16 elasticatey_node;
  4041.     tS16 section_no;
  4042.     tS16 new_section;
  4043.     br_vector3 direction_v;
  4044.     br_vector3 intersect;
  4045.     br_vector3 wank;
  4046.     br_scalar distance;
  4047.     int not_perp;
  4048.     int one_wayness;
  4049.     char str[256];
  4050.     tPath_section_type_enum section_type;
  4051.     LOG_TRACE("()");
  4052.  
  4053.     not_perp = 0;
  4054.     if (NewNodeOKHere()) {
  4055.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4056.         BrVector3Sub(&wank,
  4057.             &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p,
  4058.             &intersect);
  4059.         if (BrVector3Length(&wank) == 0.f) {
  4060.             not_perp = 1;
  4061.         }
  4062.         BrVector3Sub(&wank,
  4063.             &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p,
  4064.             &intersect);
  4065.         if (BrVector3Length(&wank) == 0.f) {
  4066.             not_perp = 1;
  4067.         }
  4068.         if (not_perp || distance > 10.f) {
  4069.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Get nearer to the section");
  4070.         } else {
  4071.             new_section = ReallocExtraPathSections(1);
  4072.             if (gAlready_elasticating) {
  4073.                 inserted_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
  4074.                 section_type = gProgram_state.AI_vehicles.path_sections[gMobile_section].type;
  4075.                 one_wayness = gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way;
  4076.                 elasticatey_node = ReallocExtraPathNodes(1);
  4077.                 gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections = 0;
  4078.                 gProgram_state.AI_vehicles.path_sections[new_section].width = gProgram_state.AI_vehicles.path_sections[gMobile_section].width;
  4079.             } else {
  4080.                 inserted_node = ReallocExtraPathNodes(2);
  4081.                 gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections = 0;
  4082.                 elasticatey_node = inserted_node + 1;
  4083.                 gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections = 0;
  4084.                 gProgram_state.AI_vehicles.path_sections[new_section].width = gProgram_state.AI_vehicles.path_sections[section_no].width;
  4085.                 section_type = gProgram_state.AI_vehicles.path_sections[section_no].type;
  4086.                 one_wayness = gProgram_state.AI_vehicles.path_sections[section_no].one_way;
  4087.             }
  4088.             InsertThisNodeInThisSectionHere(inserted_node, section_no, &gSelf->t.t.translate.t);
  4089.             gMobile_section = new_section;
  4090.             gProgram_state.AI_vehicles.path_sections[new_section].node_indices[0] = inserted_node;
  4091.             gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = elasticatey_node;
  4092.             gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0] = 0;
  4093.             gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1] = 0;
  4094.             gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0] = 255;
  4095.             gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1] = 255;
  4096.             gProgram_state.AI_vehicles.path_sections[gMobile_section].type = section_type;
  4097.             gProgram_state.AI_vehicles.path_sections[gMobile_section].one_way = one_wayness;
  4098.             gProgram_state.AI_vehicles.path_nodes[inserted_node].sections[gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections] = gMobile_section;
  4099.             gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections += 1;
  4100.             gProgram_state.AI_vehicles.path_nodes[elasticatey_node].sections[gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections] = gMobile_section;
  4101.             gProgram_state.AI_vehicles.path_nodes[elasticatey_node].number_of_sections += 1;
  4102.             gAlready_elasticating = 1;
  4103.             ShowOppoPaths();
  4104.             sprintf(str, "New section %d, new node #%d inserted into section #%d",
  4105.                 gMobile_section, inserted_node, section_no);
  4106.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4107.         }
  4108.     }
  4109. }
  4110.  
  4111. // IDA: void __cdecl InsertAndDontElasticate()
  4112. void InsertAndDontElasticate(void) {
  4113.     tS16 inserted_node;
  4114.     tS16 section_no;
  4115.     br_vector3 direction_v;
  4116.     br_vector3 intersect;
  4117.     br_vector3 wank;
  4118.     br_scalar distance;
  4119.     int not_perp;
  4120.     char str[256];
  4121.     LOG_TRACE("()");
  4122.  
  4123.     not_perp = 0;
  4124.     if (NewNodeOKHere()) {
  4125.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4126.         BrVector3Sub(&wank, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0]].p, &intersect);
  4127.         if (BrVector3Length(&wank) == 0.f) {
  4128.             not_perp = 1;
  4129.         }
  4130.         BrVector3Sub(&wank, &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1]].p, &intersect);
  4131.         if (BrVector3Length(&wank) == 0.f) {
  4132.             not_perp = 1;
  4133.         }
  4134.         if (not_perp || distance > 10.f) {
  4135.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Get nearer to the section");
  4136.         } else {
  4137.             if (gAlready_elasticating) {
  4138.                 gAlready_elasticating = 0;
  4139.                 inserted_node = gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1];
  4140.             } else {
  4141.                 inserted_node = ReallocExtraPathNodes(1);
  4142.                 gProgram_state.AI_vehicles.path_nodes[inserted_node].number_of_sections = 0;
  4143.             }
  4144.             InsertThisNodeInThisSectionHere(inserted_node, section_no, &gSelf->t.t.translate.t);
  4145.             ShowOppoPaths();
  4146.             sprintf(str, "New node #%d inserted into section #%d", inserted_node, section_no);
  4147.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4148.         }
  4149.     }
  4150. }
  4151.  
  4152. // IDA: void __cdecl DropDeadEndNode()
  4153. void DropDeadEndNode(void) {
  4154.     char str[256];
  4155.     LOG_TRACE("()");
  4156.  
  4157.     if (NewNodeOKHere() && gAlready_elasticating) {
  4158.         gAlready_elasticating = 0;
  4159.         BrVector3Copy(
  4160.             &gProgram_state.AI_vehicles.path_nodes[gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]].p,
  4161.             &gSelf->t.t.translate.t);
  4162.         ShowOppoPaths();
  4163.         sprintf(str, "New section #%d, finish node #%d",
  4164.             gMobile_section,
  4165.             gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
  4166.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 4000, -1, str);
  4167.     }
  4168. }
  4169.  
  4170. // IDA: void __cdecl DropNodeOnNodeAndStopElasticating()
  4171. void DropNodeOnNodeAndStopElasticating(void) {
  4172.     int node_no;
  4173.     char str[256];
  4174.     br_scalar distance;
  4175.     LOG_TRACE("()");
  4176.  
  4177.     if (gAlready_elasticating) {
  4178.         node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
  4179.         if (gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0] == node_no || distance > 10.f) {
  4180.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
  4181.         } else if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections >= COUNT_OF(gProgram_state.AI_vehicles.path_nodes[node_no].sections)) {
  4182.             sprintf(str, "Sorry, node #%d already has %d sections attached", node_no, (int)COUNT_OF(gProgram_state.AI_vehicles.path_nodes[node_no].sections));
  4183.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4184.         } else {
  4185.             gAlready_elasticating = 0;
  4186.             gProgram_state.AI_vehicles.number_of_path_nodes -= 1;
  4187.             gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1] = node_no;
  4188.             gProgram_state.AI_vehicles.path_nodes[node_no].sections[gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections] = gMobile_section;
  4189.             gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections += 1;
  4190.             ShowOppoPaths();
  4191.             sprintf(str, "New section #%d, attached to existing node #%d",
  4192.                 gMobile_section,
  4193.                 gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
  4194.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 4000, -1, str);
  4195.         }
  4196.     }
  4197. }
  4198.  
  4199. // IDA: void __cdecl WidenOppoPathSection()
  4200. void WidenOppoPathSection(void) {
  4201.     LOG_TRACE("()");
  4202.  
  4203.  
  4204.    if (gOppo_paths_shown) {
  4205.        RecalcNearestPathSectionWidth(.05f);
  4206.    }
  4207. }
  4208.  
  4209. // IDA: void __cdecl NarrowOppoPathSection()
  4210. void NarrowOppoPathSection(void) {
  4211.     LOG_TRACE("()");
  4212.  
  4213.     if (gOppo_paths_shown) {
  4214.         RecalcNearestPathSectionWidth(-.05f);
  4215.     }
  4216. }
  4217.  
  4218. // IDA: void __cdecl IncreaseSectionMinSpeed()
  4219. void IncreaseSectionMinSpeed(void) {
  4220.     LOG_TRACE("()");
  4221.  
  4222.     if (gOppo_paths_shown) {
  4223.         RecalcNearestPathSectionSpeed(0, 1);
  4224.     }
  4225. }
  4226.  
  4227. // IDA: void __cdecl DecreaseSectionMinSpeed()
  4228. void DecreaseSectionMinSpeed(void) {
  4229.     LOG_TRACE("()");
  4230.  
  4231.     if (gOppo_paths_shown) {
  4232.         RecalcNearestPathSectionSpeed(0, -1);
  4233.     }
  4234. }
  4235.  
  4236. // IDA: void __cdecl IncreaseSectionMaxSpeed()
  4237. void IncreaseSectionMaxSpeed(void) {
  4238.     LOG_TRACE("()");
  4239.  
  4240.    if (gOppo_paths_shown) {
  4241.        RecalcNearestPathSectionSpeed(1, 1);
  4242.    }
  4243. }
  4244.  
  4245. // IDA: void __cdecl DecreaseSectionMaxSpeed()
  4246. void DecreaseSectionMaxSpeed(void) {
  4247.     LOG_TRACE("()");
  4248.  
  4249.     if (gOppo_paths_shown) {
  4250.         RecalcNearestPathSectionSpeed(1, -1);
  4251.     }
  4252. }
  4253.  
  4254. // IDA: void __cdecl PullOppoPoint()
  4255. void PullOppoPoint(void) {
  4256.     tS16 node_no;
  4257.     br_scalar distance;
  4258.     LOG_TRACE("()");
  4259.  
  4260.     if (gOppo_paths_shown) {
  4261.         if (gAlready_elasticating) {
  4262.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're elasticating");
  4263.         } else {
  4264.             node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
  4265.             if (distance > 10.f) {
  4266.                 NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any paths close enough");
  4267.             } else {
  4268.                 BrVector3Copy(&gProgram_state.AI_vehicles.path_nodes[node_no].p, &gSelf->t.t.translate.t);
  4269.                 ShowOppoPaths();
  4270.                 NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Bing!");
  4271.             }
  4272.         }
  4273.     }
  4274. }
  4275.  
  4276. // IDA: void __cdecl ShowNodeInfo()
  4277. void ShowNodeInfo(void) {
  4278.     tS16 node_no;
  4279.     char str[256];
  4280.     br_scalar distance;
  4281.     LOG_TRACE("()");
  4282.  
  4283.     if (!gOppo_paths_shown) {
  4284.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4285.     } else if (gAlready_elasticating) {
  4286.         sprintf(str, "Next point will be #%d",
  4287.             gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
  4288.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4289.     } else {
  4290.         node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
  4291.         if (distance > 10.f) {
  4292.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
  4293.         } else {
  4294.             sprintf(str, "Nearest node #%d has %d attached sections",
  4295.                 node_no,
  4296.                 gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections);
  4297.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4298.         }
  4299.     }
  4300. }
  4301.  
  4302. // IDA: void __cdecl ShowSectionInfo1()
  4303. void ShowSectionInfo1(void) {
  4304.     tS16 section_no;
  4305.     char str[256];
  4306.     br_scalar distance;
  4307.     br_vector3 direction_v;
  4308.     br_vector3 intersect;
  4309.     LOG_TRACE("()");
  4310.  
  4311.     if (!gOppo_paths_shown) {
  4312.       NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4313.     } else if (gAlready_elasticating) {
  4314.         sprintf(str, "This section will be #%d attached to nodes #%d and #%d",
  4315.             gMobile_section,
  4316.             gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0],
  4317.             gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
  4318.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4319.     } else {
  4320.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4321.         if (distance > 10.f) {
  4322.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
  4323.         } else {
  4324.             sprintf(str, "Nearest section #%d, start node #%d, finish node #%d",
  4325.                 section_no,
  4326.                 gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[0],
  4327.                 gProgram_state.AI_vehicles.path_sections[gMobile_section].node_indices[1]);
  4328.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4329.         }
  4330.     }
  4331. }
  4332.  
  4333. // IDA: void __cdecl ShowSectionInfo2()
  4334. void ShowSectionInfo2(void) {
  4335.     tS16 section_no;
  4336.     char str[256];
  4337.     br_scalar distance;
  4338.     br_vector3 direction_v;
  4339.     br_vector3 intersect;
  4340.     LOG_TRACE("()");
  4341.  
  4342.     if (!gOppo_paths_shown) {
  4343.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4344.     } else if (gAlready_elasticating) {
  4345.         sprintf(str, "Towards start - min %d max %d, finish - min %d, max %d mph",
  4346.             (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[0]),
  4347.             (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[0]),
  4348.             (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].min_speed[1]),
  4349.             (int)(2.2f * gProgram_state.AI_vehicles.path_sections[gMobile_section].max_speed[1]));
  4350.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4351.     } else {
  4352.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4353.         if (distance > 10.f) {
  4354.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
  4355.         } else {
  4356.             sprintf(str, "Towards start - min %d max %d, finish - min %d, max %d mph",
  4357.                 (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0]),
  4358.                 (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0]),
  4359.                 (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1]),
  4360.                 (int)(2.2f * gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1]));
  4361.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4362.         }
  4363.     }
  4364. }
  4365.  
  4366. // IDA: void __cdecl DeleteOppoPathSection()
  4367. void DeleteOppoPathSection(void) {
  4368.     br_scalar distance;
  4369.     br_vector3 intersect;
  4370.     br_vector3 direction_v;
  4371.     tS16 section_no;
  4372.     LOG_TRACE("()");
  4373.  
  4374.     if (gOppo_paths_shown == 0) {
  4375.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4376.     } else if (gAlready_elasticating) {
  4377.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4378.     } else {
  4379.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4380.         if (distance > 10.f) {
  4381.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
  4382.         } else {
  4383.             DeleteSection(section_no);
  4384.             DeleteOrphanNodes();
  4385.             ShowOppoPaths();
  4386.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Pop!");
  4387.         }
  4388.     }
  4389. }
  4390.  
  4391. // IDA: void __cdecl DeleteOppoPathNodeAndSections()
  4392. void DeleteOppoPathNodeAndSections(void) {
  4393.     br_scalar distance;
  4394.     tS16 node_no;
  4395.     LOG_TRACE("()");
  4396.  
  4397.     if (!gOppo_paths_shown) {
  4398.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4399.     }
  4400.     else if (gAlready_elasticating) {
  4401.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4402.     } else {
  4403.         node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
  4404.         if (distance > 10.f) {
  4405.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
  4406.         } else {
  4407.             DeleteNode(node_no, 1);
  4408.             DeleteOrphanNodes();
  4409.             ShowOppoPaths();
  4410.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Blam!");
  4411.         }
  4412.     }
  4413. }
  4414.  
  4415. // IDA: void __cdecl DeleteOppoPathNodeAndJoin()
  4416. void DeleteOppoPathNodeAndJoin(void) {
  4417.     br_scalar distance;
  4418.     tS16 node_no;
  4419.     LOG_TRACE("()");
  4420.  
  4421.     if (!gOppo_paths_shown) {
  4422.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4423.     } else if (gAlready_elasticating) {
  4424.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4425.     } else {
  4426.         node_no = FindNearestPathNode(&gSelf->t.t.translate.t, &distance);
  4427.         if (distance > 10.f) {
  4428.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any nodes close enough");
  4429.         } else if (gProgram_state.AI_vehicles.path_nodes[node_no].number_of_sections != 2) {
  4430.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Node must have exactly 2 sections attached");
  4431.         } else if ((gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[0]].node_indices[0] == node_no
  4432.                     && gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[1]].node_indices[1] == node_no) ||
  4433.                 (gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[1]].node_indices[0] == node_no
  4434.                     && gProgram_state.AI_vehicles.path_sections[gProgram_state.AI_vehicles.path_nodes[node_no].sections[0]].node_indices[1] == node_no)) {
  4435.             ConsistencyCheck();
  4436.             DeleteNode(node_no,0);
  4437.             ConsistencyCheck();
  4438.             DeleteOrphanNodes();
  4439.             ConsistencyCheck();
  4440.             ShowOppoPaths();
  4441.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Blam!");
  4442.         }
  4443.         else {
  4444.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Sections must point in same direction");
  4445.         }
  4446.     }
  4447. }
  4448.  
  4449. // IDA: void __cdecl ReverseSectionDirection()
  4450. void ReverseSectionDirection(void) {
  4451.     tS16 temp;
  4452.     tU8 speed_temp;
  4453.     br_scalar distance;
  4454.     br_vector3 intersect;
  4455.     br_vector3 direction_v;
  4456.     tS16 section_no;
  4457.     LOG_TRACE("()");
  4458.  
  4459.     if (!gOppo_paths_shown) {
  4460.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4461.     } else if (gAlready_elasticating) {
  4462.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4463.     } else {
  4464.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t,  &direction_v, &intersect, &distance);
  4465.         if (distance > 10.f) {
  4466.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
  4467.         } else {
  4468.             temp = gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0];
  4469.             gProgram_state.AI_vehicles.path_sections[section_no].node_indices[0] =
  4470.                 gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1];
  4471.             gProgram_state.AI_vehicles.path_sections[section_no].node_indices[1] = temp;
  4472.  
  4473.             speed_temp = gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0];
  4474.             gProgram_state.AI_vehicles.path_sections[section_no].min_speed[0] =
  4475.                 gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1];
  4476.             gProgram_state.AI_vehicles.path_sections[section_no].min_speed[1] = speed_temp;
  4477.  
  4478.             speed_temp = gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0];
  4479.             gProgram_state.AI_vehicles.path_sections[section_no].max_speed[0] =
  4480.                 gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1];
  4481.             gProgram_state.AI_vehicles.path_sections[section_no].max_speed[1] = speed_temp;
  4482.  
  4483.             ShowOppoPaths();
  4484.         }
  4485.     }
  4486. }
  4487.  
  4488. // IDA: void __cdecl CycleSectionType()
  4489. void CycleSectionType(void) {
  4490.     br_scalar distance;
  4491.     br_vector3 intersect;
  4492.     br_vector3 direction_v;
  4493.     tS16 section_no;
  4494.     char str[256];
  4495.     LOG_TRACE("()");
  4496.  
  4497.     if (!gOppo_paths_shown) {
  4498.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4499.     } else if (gAlready_elasticating) {
  4500.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4501.     } else {
  4502.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4503.         if (distance > 10.f) {
  4504.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
  4505.         } else {
  4506.             gProgram_state.AI_vehicles.path_sections[section_no].type =
  4507.                 (gProgram_state.AI_vehicles.path_sections[section_no].type + 1) % 3;
  4508.             sprintf(str, "%s section",  gPath_section_type_names[gProgram_state.AI_vehicles.path_sections[section_no].type]);
  4509.             ShowOppoPaths();
  4510.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4511.         }
  4512.     }
  4513. }
  4514.  
  4515. // IDA: void __cdecl ToggleOneWayNess()
  4516. void ToggleOneWayNess(void) {
  4517.     br_scalar distance;
  4518.     br_vector3 intersect;
  4519.     br_vector3 direction_v;
  4520.     tS16 section_no;
  4521.     LOG_TRACE("()");
  4522.  
  4523.     if (!gOppo_paths_shown) {
  4524.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4525.     } else if (gAlready_elasticating) {
  4526.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4527.     } else {
  4528.         section_no = FindNearestPathSection(&gSelf->t.t.translate.t, &direction_v, &intersect, &distance);
  4529.         if (distance > 10.f) {
  4530.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Can't find any sections close enough");
  4531.         } else {
  4532.             if (gProgram_state.AI_vehicles.path_sections[section_no].one_way) {
  4533.                 gProgram_state.AI_vehicles.path_sections[section_no].one_way = 0;
  4534.             }
  4535.             else {
  4536.                 gProgram_state.AI_vehicles.path_sections[section_no].one_way = 1;
  4537.             }
  4538.             ShowOppoPaths();
  4539.             if (gProgram_state.AI_vehicles.path_sections[section_no].one_way) {
  4540.                 NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "ONE-WAY");
  4541.             } else {
  4542.                 NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "TWO-WAY");
  4543.             }
  4544.         }
  4545.     }
  4546. }
  4547.  
  4548. // IDA: void __cdecl CopStartPointInfo()
  4549. void CopStartPointInfo(void) {
  4550.     char str[256];
  4551.     int i;
  4552.     int closest;
  4553.     br_scalar closest_distance;
  4554.     br_scalar distance;
  4555.     br_vector3 car_to_point;
  4556.     LOG_TRACE("()");
  4557.  
  4558.     closest = -1;
  4559.     closest_distance = FLT_MAX;
  4560.     if (!gOppo_paths_shown) {
  4561.       NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4562.     } else {
  4563.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  4564.             BrVector3Sub(&car_to_point, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
  4565.             distance = BrVector3LengthSquared(&car_to_point);
  4566.             if (distance < closest_distance) {
  4567.                 closest = i;
  4568.                 closest_distance = distance;
  4569.             }
  4570.         }
  4571.         if (closest < 0 || closest_distance > 10.f) {
  4572.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "No cop start points close enough");
  4573.         } else {
  4574.             sprintf(str, "Nearest cop start point #%d", closest);
  4575.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4576.         }
  4577.     }
  4578. }
  4579.  
  4580. // IDA: void __cdecl DropCopStartPoint()
  4581. void DropCopStartPoint(void) {
  4582.     char str[256];
  4583.     LOG_TRACE("()");
  4584.  
  4585.     if (!gOppo_paths_shown) {
  4586.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4587.     } else if (gAlready_elasticating) {
  4588.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4589.     } else {
  4590.         if (gProgram_state.AI_vehicles.number_of_cops < COUNT_OF(gProgram_state.AI_vehicles.cop_start_points)) {
  4591.             BrVector3Copy(&gProgram_state.AI_vehicles.cop_start_points[gProgram_state.AI_vehicles.number_of_cops], &gSelf->t.t.translate.t);
  4592.             gProgram_state.AI_vehicles.number_of_cops += 1;
  4593.             ShowOppoPaths();
  4594.             sprintf(str, "New cop start point dropped (%d of %d)", gProgram_state.AI_vehicles.number_of_cops, (int)COUNT_OF(gProgram_state.AI_vehicles.cop_start_points));
  4595.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4596.         } else {
  4597.             sprintf(str, "Sorry, no more than %d cop start points", (int)COUNT_OF(gProgram_state.AI_vehicles.cop_start_points));
  4598.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4599.         }
  4600.     }
  4601. }
  4602.  
  4603. // IDA: void __cdecl DeleteCopStartPoint()
  4604. void DeleteCopStartPoint(void) {
  4605.     char str[256];
  4606.     int i;
  4607.     int closest;
  4608.     br_scalar closest_distance;
  4609.     br_scalar distance;
  4610.     br_vector3 car_to_point;
  4611.     LOG_TRACE("()");
  4612.  
  4613.     closest = -1;
  4614.     closest_distance = FLT_MAX;
  4615.     if (!gOppo_paths_shown) {
  4616.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Show paths first (F5)");
  4617.     } else if (gAlready_elasticating) {
  4618.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "Not while you're creating a new section");
  4619.     } else {
  4620.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  4621.             BrVector3Sub(&car_to_point, &gSelf->t.t.translate.t, &gProgram_state.AI_vehicles.cop_start_points[i]);
  4622.             distance = BrVector3Length(&car_to_point);
  4623.             if (distance < closest_distance) {
  4624.                 closest = i;
  4625.                 closest_distance = distance;
  4626.             }
  4627.         }
  4628.         if (closest < 0 || closest_distance > 10.f) {
  4629.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, "No cop start points close enough");
  4630.         } else {
  4631.             for (i = closest; i < gProgram_state.AI_vehicles.number_of_cops - 1; i++) {
  4632.                 BrVector3Copy(&gProgram_state.AI_vehicles.cop_start_points[i],
  4633.                     &gProgram_state.AI_vehicles.cop_start_points[i + 1]);
  4634.             }
  4635.             gProgram_state.AI_vehicles.number_of_cops -= 1;
  4636.             ShowOppoPaths();
  4637.             sprintf(str, "Deleted cop start point #%d", closest);
  4638.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 2000, -1, str);
  4639.         }
  4640.     }
  4641. }
  4642.  
  4643. // IDA: void __cdecl CycleCopStartPointType()
  4644. void CycleCopStartPointType(void) {
  4645.     LOG_TRACE("()");
  4646.  
  4647. }
  4648.