Subversion Repositories Games.Carmageddon

Rev

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

  1. #include "netgame.h"
  2. #include "brender.h"
  3. #include "brucetrk.h"
  4. #include "car.h"
  5. #include "controls.h"
  6. #include "crush.h"
  7. #include "displays.h"
  8. #include "errors.h"
  9. #include "globvars.h"
  10. #include "globvrpb.h"
  11. #include "grafdata.h"
  12. #include "graphics.h"
  13. #include "harness/trace.h"
  14. #include "loading.h"
  15. #include "network.h"
  16. #include "newgame.h"
  17. #include "opponent.h"
  18. #include "pd/sys.h"
  19. #include "pedestrn.h"
  20. #include "powerup.h"
  21. #include "pratcam.h"
  22. #include "racestrt.h"
  23. #include "spark.h"
  24. #include "structur.h"
  25. #include "utility.h"
  26. #include <limits.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29.  
  30. int gPowerup_cost[4] = { 1500, 2500, 4000, 6000 };
  31. int gGame_scores[6] = { 1, 2, 3, 4, 6, 10 };
  32. int gPed_target;
  33. int gNot_shown_race_type_headup;
  34. tU32 gLast_it_change;
  35. tU32 gTime_for_punishment;
  36. tNet_game_player_info* gLast_lepper;
  37. int gInitialised_grid;
  38. int gIt_or_fox;
  39.  
  40. #define PACK_POWERUPS(car) (car->power_up_levels[0] & 0xff) + ((car->power_up_levels[2] & 0xff) << 6) + ((car->power_up_levels[1] & 0xff) << 3);
  41.  
  42. // IDA: void __usercall SendCarData(tU32 pNext_frame_time@<EAX>)
  43. void SendCarData(tU32 pNext_frame_time) {
  44.     tNet_contents* contents;
  45.     tCar_spec* car;
  46.     tCollision_info* ncar;
  47.     int i;
  48.     int j;
  49.     static tU32 last_time;
  50.     tU32 time;
  51.     int damaged_wheels;
  52.     LOG_TRACE("(%d)", pNext_frame_time);
  53.  
  54.     time = GetRaceTime();
  55.     if (gNet_mode == eNet_mode_none || (time > last_time && last_time + 80 > time)) {
  56.         return;
  57.     }
  58.     last_time = time;
  59.     contents = NetGetBroadcastContents(NETMSGID_TIMESYNC, 0);
  60.     contents->data.time_sync.race_start_time = gRace_start;
  61.  
  62.     if (gNet_mode == eNet_mode_host) {
  63.         for (i = 0; i < gNumber_of_net_players; i++) {
  64.             car = gNet_players[i].car;
  65.             if (car->disabled) {
  66.                 continue;
  67.             }
  68.             damaged_wheels = car->damage_units[eDamage_lf_wheel].damage_level > 30 || car->damage_units[eDamage_rf_wheel].damage_level > 30 || car->damage_units[eDamage_lr_wheel].damage_level > 30 || car->damage_units[eDamage_rr_wheel].damage_level > 30;
  69.             contents = NetGetBroadcastContents(NETMSGID_MECHANICS, damaged_wheels);
  70.             GetReducedMatrix(&contents->data.mech.mat, &car->car_master_actor->t.t.mat);
  71.             contents->data.mech.ID = gNet_players[i].ID;
  72.             contents->data.mech.time = pNext_frame_time;
  73.             BrVector3Copy(&contents->data.mech.omega, &car->omega);
  74.             BrVector3Copy(&contents->data.mech.v, &car->v);
  75.             contents->data.mech.curvature = (car->curvature / car->maxcurve * 32767.0f);
  76.             contents->data.mech.keys = car->keys;
  77.             contents->data.mech.keys.joystick_acc = (tU8)(car->joystick.acc >> 9);
  78.             contents->data.mech.keys.joystick_dec = (tU8)(car->joystick.dec >> 9);
  79.             contents->data.mech.revs = car->revs;
  80.             for (j = 0; j < COUNT_OF(contents->data.mech.d); j++) {
  81.                 contents->data.mech.d[j] = (int)(car->oldd[j] / car->susp_height[j >> 1] * 255.0f);
  82.             }
  83.             for (j = 0; j < COUNT_OF(contents->data.mech.damage); j++) {
  84.                 contents->data.mech.damage[j] = car->damage_units[j].damage_level;
  85.             }
  86.             contents->data.mech.front = car->bounds[1].min.v[2];
  87.             contents->data.mech.back = car->bounds[1].max.v[2];
  88.             contents->data.mech.powerups = PACK_POWERUPS(car);
  89.             contents->data.mech.repair_time = car->repair_time;
  90.             contents->data.mech.cc_coll_time = car->last_car_car_collision;
  91.             if (damaged_wheels) {
  92.                 for (j = 0; j < COUNT_OF(contents->data.mech.wheel_dam_offset); j++) {
  93.                     contents->data.mech.wheel_dam_offset[j] = car->wheel_dam_offset[j];
  94.                 }
  95.             }
  96.             if (car->time_to_recover != 0) {
  97.                 if (car->time_to_recover - 500 < pNext_frame_time) {
  98.                     contents = NetGetBroadcastContents(NETMSGID_RECOVER, 0);
  99.                     contents->data.recover.ID = gNet_players[i].ID;
  100.                     contents->data.recover.time_to_recover = car->time_to_recover;
  101.                 }
  102.             }
  103.         }
  104.         for (i = 0; i < gNum_active_non_cars; i++) {
  105.             contents = NetGetBroadcastContents(NETMSGID_NONCAR_INFO, 0);
  106.             ncar = (tCollision_info*)gActive_non_car_list[i];
  107.             GetReducedMatrix(&contents->data.mech.mat, &ncar->car_master_actor->t.t.mat);
  108.             contents->data.non_car.ID = ncar->car_ID;
  109.             contents->data.non_car.time = pNext_frame_time;
  110.             BrVector3Copy(&contents->data.non_car.omega, &ncar->omega);
  111.             BrVector3Copy(&contents->data.non_car.v, &ncar->v);
  112.             contents->data.non_car.flags = ncar->car_master_actor->identifier[3] == 2 * ncar->doing_nothing_flag + '!';
  113.         }
  114.         for (i = 0; i < gProgram_state.AI_vehicles.number_of_cops; i++) {
  115.             if (!gProgram_state.AI_vehicles.cops[i].finished_for_this_race) {
  116.                 contents = NetGetBroadcastContents(NETMSGID_COPINFO, 0);
  117.                 car = gProgram_state.AI_vehicles.cops[i].car_spec;
  118.                 GetReducedMatrix(&contents->data.mech.mat, &car->car_master_actor->t.t.mat);
  119.                 contents->data.cop_info.ID = car->car_ID;
  120.                 contents->data.cop_info.time = pNext_frame_time;
  121.                 BrVector3Copy(&contents->data.cop_info.omega, &car->omega);
  122.                 BrVector3Copy(&contents->data.cop_info.v, &car->v);
  123.                 for (j = 0; j < COUNT_OF(contents->data.cop_info.damage); ++j) {
  124.                     contents->data.cop_info.damage[j] = car->damage_units[j].damage_level;
  125.                 }
  126.                 for (j = 0; j < COUNT_OF(contents->data.cop_info.d); j++) {
  127.                     contents->data.cop_info.d[j] = car->oldd[j];
  128.                 }
  129.             }
  130.         }
  131.     } else if (gNet_mode == eNet_mode_client) {
  132.         car = &gProgram_state.current_car;
  133.         if (car->disabled) {
  134.             return;
  135.         }
  136.         damaged_wheels = car->damage_units[eDamage_lf_wheel].damage_level > 30 || car->damage_units[eDamage_rf_wheel].damage_level > 30 || car->damage_units[eDamage_lr_wheel].damage_level > 30 || car->damage_units[eDamage_rr_wheel].damage_level > 30;
  137.         contents = NetGetToHostContents(NETMSGID_MECHANICS, damaged_wheels);
  138.         GetReducedMatrix(&contents->data.mech.mat, &gProgram_state.current_car.car_master_actor->t.t.mat);
  139.         contents->data.mech.ID = gNet_players[gThis_net_player_index].ID;
  140.         contents->data.mech.time = pNext_frame_time;
  141.         BrVector3Copy(&contents->data.mech.omega, &car->omega);
  142.         BrVector3Copy(&contents->data.mech.v, &car->v);
  143.  
  144.         contents->data.mech.curvature = (car->curvature / car->maxcurve * 32767.0f);
  145.         contents->data.mech.keys = car->keys;
  146.         contents->data.mech.keys.joystick_acc = (tU8)(car->joystick.acc >> 9);
  147.         contents->data.mech.keys.joystick_dec = (tU8)(car->joystick.dec >> 9);
  148.         contents->data.mech.revs = car->revs;
  149.         contents->data.mech.cc_coll_time = car->last_car_car_collision;
  150.         for (j = 0; j < COUNT_OF(contents->data.mech.d); j++) {
  151.             contents->data.mech.d[j] = (int)(car->oldd[j] / car->susp_height[j >> 1] * 255.f);
  152.         }
  153.         for (j = 0; j < COUNT_OF(contents->data.mech.damage); j++) {
  154.             contents->data.mech.damage[j] = car->damage_units[j].damage_level;
  155.         }
  156.         contents->data.mech.front = car->bounds[1].min.v[2];
  157.         contents->data.mech.back = car->bounds[1].max.v[2];
  158.         contents->data.mech.powerups = PACK_POWERUPS(car);
  159.         contents->data.mech.repair_time = car->repair_time;
  160.         if (damaged_wheels) {
  161.             for (j = 0; j < COUNT_OF(contents->data.mech.wheel_dam_offset); j++) {
  162.                 contents->data.mech.wheel_dam_offset[j] = car->wheel_dam_offset[j];
  163.             }
  164.         }
  165.         if (car->time_to_recover > 0 && car->time_to_recover - 500 < pNext_frame_time) {
  166.             contents = NetGetToHostContents(NETMSGID_RECOVER, 0);
  167.             contents->data.recover.ID = gNet_players[gThis_net_player_index].ID;
  168.             contents->data.recover.time_to_recover = gProgram_state.current_car.time_to_recover;
  169.         }
  170.     }
  171. }
  172.  
  173. // IDA: void __usercall ReceivedRecover(tNet_contents *pContents@<EAX>)
  174. void ReceivedRecover(tNet_contents* pContents) {
  175.     int i;
  176.     LOG_TRACE("(%p)", pContents);
  177.  
  178.     if (gNet_players[gThis_net_player_index].ID != pContents->data.player_list.number_of_players) {
  179.         for (i = 0; i < gNumber_of_net_players; i++) {
  180.             if (gNet_players[i].ID == pContents->data.player_list.number_of_players) {
  181.                 gNet_players[i].car->time_to_recover = pContents->data.mech.time;
  182.             }
  183.         }
  184.     }
  185. }
  186.  
  187. // IDA: void __usercall CopyMechanics(tCar_spec *pCar@<EAX>, tNet_contents *pContents@<EDX>)
  188. void CopyMechanics(tCar_spec* pCar, tNet_contents* pContents) {
  189.     int j;
  190.     LOG_TRACE("(%p, %p)", pCar, pContents);
  191.  
  192.     memcpy(&pCar->message, pContents, pContents->header.contents_size);
  193.     // if it is not a full mechanics message...
  194.     if (pContents->header.contents_size != sizeof(tNet_message_mechanics_info)) {
  195.         for (j = 0; j < COUNT_OF(pCar->message.wheel_dam_offset); j++) {
  196.             pCar->message.wheel_dam_offset[j] = 0.0f;
  197.         }
  198.     }
  199. }
  200.  
  201. // IDA: void __usercall ReceivedMechanics(tNet_contents *pContents@<EAX>)
  202. void ReceivedMechanics(tNet_contents* pContents) {
  203.     int i;
  204.     tCar_spec* car;
  205.     LOG_TRACE("(%p)", pContents);
  206.  
  207.     car = NULL;
  208.     for (i = 0; i < gNumber_of_net_players; i++) {
  209.         if (gNet_players[i].ID == pContents->data.mech.ID) {
  210.             car = gNet_players[i].car;
  211.             break;
  212.         }
  213.     }
  214.     if (car == NULL || car->message.time >= pContents->data.mech.time) {
  215.         return;
  216.     }
  217.     if (car->disabled) {
  218.         EnableCar(car);
  219.         GetExpandedMatrix(&car->car_master_actor->t.t.mat, &pContents->data.mech.mat);
  220.         BrMatrix34Copy(&car->oldmat, &car->car_master_actor->t.t.mat);
  221.         BrVector3InvScale(&car->car_master_actor->t.t.translate.t, &car->car_master_actor->t.t.translate.t, WORLD_SCALE);
  222.         // car->car_master_actor->t.t.mat.m[3][0] = car->car_master_actor->t.t.mat.m[3][0] * 0.14492753;
  223.         // car->car_master_actor->t.t.mat.m[3][1] = car->car_master_actor->t.t.mat.m[3][1] * 0.14492753;
  224.         // car->car_master_actor->t.t.mat.m[3][2] = car->car_master_actor->t.t.mat.m[3][2] * 0.14492753;
  225.         car->box_face_ref = gFace_num__car - 2;
  226.         car->message.time = pContents->data.mech.time;
  227.         car->message.type = NETMSGID_NONE;
  228.         BrVector3Copy(&car->v, &pContents->data.mech.v);
  229.         BrVector3Copy(&car->omega, &pContents->data.mech.omega);
  230.  
  231.         for (i = 0; i < COUNT_OF(car->message.d); i++) {
  232.             car->message.d[i] = pContents->data.mech.d[i];
  233.         }
  234.         for (i = 0; i < COUNT_OF(car->message.damage); i++) {
  235.             car->message.damage[i] = pContents->data.mech.damage[i];
  236.         }
  237.         DisableCar(car);
  238.         return;
  239.     }
  240.     if (gNet_mode == eNet_mode_host) {
  241.         if (gThis_net_player_index == i) {
  242.             return;
  243.         }
  244.         if (car->last_car_car_collision <= pContents->data.mech.cc_coll_time) {
  245.             CopyMechanics(car, pContents);
  246.         }
  247.         car->power_up_levels[0] = pContents->data.mech.powerups & 7;
  248.         car->power_up_levels[1] = (pContents->data.mech.powerups >> 3) & 7;
  249.         car->power_up_levels[2] = (pContents->data.mech.powerups >> 6) & 7;
  250.         car->keys = car->message.keys;
  251.         if (car->message.keys.joystick_acc < 0) {
  252.             car->joystick.acc = -1;
  253.         } else {
  254.             car->joystick.acc = car->message.keys.joystick_acc << 9;
  255.         }
  256.         if (car->message.keys.joystick_dec < 0) {
  257.             car->joystick.dec = -1;
  258.         } else {
  259.             car->joystick.dec = car->message.keys.joystick_dec << 9;
  260.         }
  261.     } else if (gNet_mode == eNet_mode_client) {
  262.         if (gThis_net_player_index == i) {
  263.             if (car->last_car_car_collision < pContents->data.mech.cc_coll_time) {
  264.                 CopyMechanics(car, pContents);
  265.             }
  266.         } else {
  267.             CopyMechanics(car, pContents);
  268.             car->power_up_levels[0] = pContents->data.mech.powerups & 7;
  269.             car->power_up_levels[1] = (pContents->data.mech.powerups >> 3) & 7;
  270.             car->power_up_levels[2] = (pContents->data.mech.powerups >> 6) & 7;
  271.             car->keys = car->message.keys;
  272.             if (car->message.keys.joystick_acc < 0) {
  273.                 car->joystick.acc = -1;
  274.             } else {
  275.                 car->joystick.acc = car->message.keys.joystick_acc << 9;
  276.             }
  277.             if (car->message.keys.joystick_dec < 0) {
  278.                 car->joystick.dec = -1;
  279.             } else {
  280.                 car->joystick.dec = car->message.keys.joystick_dec << 9;
  281.             }
  282.             if (!car->active) {
  283.                 GetExpandedMatrix(&car->car_master_actor->t.t.mat, &pContents->data.mech.mat);
  284.                 BrMatrix34Copy(&car->oldmat, &car->car_master_actor->t.t.mat);
  285.                 BrMatrix34ApplyP(&car->pos, &car->cmpos, &car->car_master_actor->t.t.mat);
  286.                 BrVector3InvScale(&car->car_master_actor->t.t.translate.t, &car->car_master_actor->t.t.translate.t, WORLD_SCALE);
  287.                 BrVector3InvScale(&car->pos, &car->pos, WORLD_SCALE);
  288.                 car->box_face_ref = gFace_num__car - 2;
  289.             }
  290.         }
  291.     }
  292. }
  293.  
  294. // IDA: void __usercall ReceivedCopInfo(tNet_contents *pContents@<EAX>)
  295. void ReceivedCopInfo(tNet_contents* pContents) {
  296.     tCar_spec* c;
  297.     int i;
  298.     LOG_TRACE("(%p)", pContents);
  299.  
  300.     if (gNet_mode != eNet_mode_client) {
  301.         return;
  302.     }
  303.     if (pContents->data.cop_info.ID & 0xffffff00) {
  304.         c = GetCarSpec(pContents->data.cop_info.ID >> 8, pContents->data.cop_info.ID & 0xff);
  305.     } else {
  306.         c = &gProgram_state.current_car;
  307.     }
  308.     if (c == NULL || c->message.time > pContents->data.cop_info.time) {
  309.         return;
  310.     }
  311.     c->message.time = pContents->data.cop_info.time;
  312.     if (c->active) {
  313.         c->message.type = NETMSGID_MECHANICS;
  314.         c->message.mat = pContents->data.cop_info.mat;
  315.         BrVector3Copy(&c->message.v, &pContents->data.cop_info.v);
  316.         BrVector3Copy(&c->message.omega, &pContents->data.cop_info.omega);
  317.         c->message.curvature = (tS16)pContents->data.cop_info.curvature;
  318.         for (i = 0; i < COUNT_OF(c->message.d); i++) {
  319.             c->message.d[i] = pContents->data.cop_info.d[i];
  320.         }
  321.         for (i = 0; i < COUNT_OF(c->message.damage); i++) {
  322.             c->message.damage[i] = pContents->data.cop_info.damage[i];
  323.         }
  324.     } else {
  325.         GetExpandedMatrix(&c->car_master_actor->t.t.mat, &pContents->data.cop_info.mat);
  326.         BrVector3InvScale(&c->car_master_actor->t.t.translate.t, &c->car_master_actor->t.t.translate.t, WORLD_SCALE);
  327.         for (i = 0; i < COUNT_OF(c->damage_units); i++) {
  328.             c->damage_units[i].damage_level = pContents->data.cop_info.damage[i];
  329.         }
  330.     }
  331. }
  332.  
  333. // IDA: void __cdecl SendAllNonCarPositions()
  334. void SendAllNonCarPositions(void) {
  335.     int i;
  336.     br_actor** list;
  337.     tNon_car_spec* non_car;
  338.     tNet_contents* contents;
  339.     LOG_TRACE("()");
  340.  
  341.     list = gProgram_state.track_spec.non_car_list;
  342.     for (i = 0; i < gProgram_state.track_spec.ampersand_digits; ++i) {
  343.         if (list[i]->type_data != NULL) {
  344.             non_car = (tNon_car_spec*)list[i]->type_data;
  345.             if (non_car->collision_info.driver == eDriver_non_car_unused_slot || non_car->collision_info.car_ID != i) {
  346.                 contents = NetGetBroadcastContents(NETMSGID_NONCARPOSITION, 0);
  347.                 BrMatrix34Copy(&contents->data.non_car_position.mat, &list[i]->t.t.mat);
  348.                 contents->data.non_car_position.ID = i;
  349.                 contents->data.non_car_position.flags = list[i]->identifier[3] == '!';
  350.             }
  351.         }
  352.     }
  353.     NetSendMessageStacks();
  354. }
  355.  
  356. // IDA: void __usercall ReceivedNonCarPosition(tNet_contents *pContents@<EAX>)
  357. void ReceivedNonCarPosition(tNet_contents* pContents) {
  358.     br_actor* actor;
  359.     LOG_TRACE("(%p)", pContents);
  360.  
  361.     actor = gProgram_state.track_spec.non_car_list[pContents->data.non_car_position.ID];
  362.     if (actor != NULL && gNet_mode != eNet_mode_none) {
  363.         BrMatrix34Copy(&actor->t.t.mat, &pContents->data.non_car_position.mat);
  364.         if (pContents->data.non_car_position.flags) {
  365.             actor->identifier[3] = '!';
  366.         }
  367.     }
  368. }
  369.  
  370. // IDA: void __usercall ReceivedNonCar(tNet_contents *pContents@<EAX>)
  371. void ReceivedNonCar(tNet_contents* pContents) {
  372.     br_actor* actor;
  373.     br_vector3 tv;
  374.     tU8 cx;
  375.     tU8 cz;
  376.     tTrack_spec* track_spec;
  377.     tNon_car_spec* ncar;
  378.     tCollision_info* c;
  379.     LOG_TRACE("(%p)", pContents);
  380.  
  381.     track_spec = &gProgram_state.track_spec;
  382.     if (pContents->data.non_car.ID >= track_spec->ampersand_digits) {
  383.         return;
  384.     }
  385.     actor = track_spec->non_car_list[pContents->data.non_car.ID];
  386.     if (actor == NULL) {
  387.         return;
  388.     }
  389.     ncar = (tNon_car_spec*)actor->type_data;
  390.     if (ncar != NULL && (ncar->collision_info.driver != eDriver_non_car || ncar->collision_info.car_ID != pContents->data.non_car.ID)) {
  391.         ncar = NULL;
  392.     }
  393.     if ((pContents->data.non_car.flags & 1) != 0) {
  394.         actor->identifier[3] = '!';
  395.     } else {
  396.         actor->identifier[3] = 'x';
  397.     }
  398.     if (ncar == NULL && actor->identifier[1] >= '0' && actor->identifier[1] <= '9') {
  399.         BrVector3Sub(&tv, &gProgram_state.current_car.car_master_actor->t.t.translate.t, &actor->t.t.translate.t);
  400.         if (BrVector3LengthSquared(&tv) < 900.0f) {
  401.             DoPullActorFromWorld(actor);
  402.             ncar = (tNon_car_spec*)actor->type_data;
  403.         }
  404.     }
  405.     if (ncar != NULL) {
  406.         c = &ncar->collision_info;
  407.         if ((pContents->data.non_car.flags & 2) != 0) {
  408.             GetExpandedMatrix(&c->car_master_actor->t.t.mat, &pContents->data.non_car.mat);
  409.             BrVector3Copy(&c->v, &pContents->data.non_car.v);
  410.             BrVector3Copy(&c->omega, &pContents->data.non_car.omega);
  411.             c->doing_nothing_flag = 1;
  412.         } else {
  413.             BrVector3Copy(&c->message.v, &pContents->data.non_car.v);
  414.             BrVector3Copy(&c->message.omega, &pContents->data.non_car.omega);
  415.             memcpy(&c->message.mat, &pContents->data.non_car.mat, sizeof(c->message.mat));
  416.             c->message.time = pContents->data.non_car.time;
  417.             c->message.type = NETMSGID_NONCAR_INFO;
  418.             c->doing_nothing_flag = 0;
  419.         }
  420.     } else {
  421.         GetExpandedMatrix(&actor->t.t.mat, &pContents->data.mech.mat);
  422.         BrVector3InvScale(&actor->t.t.translate.t, &actor->t.t.translate.t, WORLD_SCALE);
  423.         XZToColumnXZ(&cx, &cz, actor->t.t.translate.t.v[0], actor->t.t.translate.t.v[2], track_spec);
  424.         if (track_spec->columns[cz][cx] != actor->parent) {
  425.             if (track_spec->columns[cz][cx] != NULL) {
  426.                 BrActorRemove(actor);
  427.                 BrActorAdd(track_spec->columns[cz][cx], actor);
  428.             }
  429.         }
  430.     }
  431. }
  432.  
  433. // IDA: void __usercall SignalToStartRace2(int pIndex@<EAX>)
  434. void SignalToStartRace2(int pIndex) {
  435.     tNet_message* the_message;
  436.     int i;
  437.     int j;
  438.     LOG_TRACE("(%d)", pIndex);
  439.  
  440.     if (gCurrent_race.number_of_racers > 6) {
  441.         FadePaletteUp();
  442.         dr_dprintf("AAAARRRGGGHHH!!!! More than 6 racers!!!!");
  443.         PDFatalError("AAAARRRGGGHHH!!!! More than 6 racers!!!!");
  444.     }
  445.     gNeed_to_send_start_race = 0;
  446.     gStart_race_sent = 1;
  447.     the_message = NetBuildMessage(NETMSGID_STARTRACE, 0);
  448.     the_message->contents.data.start_race.racing = gProgram_state.racing;
  449.     if (pIndex >= 0) {
  450.         gNet_players[pIndex].last_waste_message = 0;
  451.         gNet_players[pIndex].wasteage_attributed = 0;
  452.         the_message->contents.data.start_race.car_count = -1;
  453.         the_message->contents.data.start_race.car_list[0].index = pIndex;
  454.         BrMatrix34Copy(&the_message->contents.data.start_race.car_list[0].mat,
  455.             &gCurrent_race.opponent_list[gNet_players[pIndex].opponent_list_index].car_spec->car_master_actor->t.t.mat);
  456.     } else {
  457.         the_message->contents.data.start_race.car_count = gCurrent_race.number_of_racers;
  458.         for (i = 0; i < gCurrent_race.number_of_racers; i++) {
  459.             BrMatrix34Copy(&the_message->contents.data.start_race.car_list[i].mat,
  460.                 &gCurrent_race.opponent_list[i].car_spec->car_master_actor->t.t.mat);
  461.             for (j = 0; j < gNumber_of_net_players; j++) {
  462.                 if (gCurrent_race.opponent_list[i].car_spec == gNet_players[j].car) {
  463.                     the_message->contents.data.start_race.car_list[i].index = j;
  464.                     break;
  465.                 }
  466.             }
  467.             if (gCurrent_net_game->options.random_car_choice && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)) {
  468.                 if (gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index < 0) {
  469.                     gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index = PickARandomCar();
  470.                     gCar_details[gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index].ownership = eCar_owner_someone;
  471.                 }
  472.                 the_message->contents.data.start_race.car_list[i].next_car_index = gNet_players[the_message->contents.data.start_race.car_list[i].index].next_car_index;
  473.             }
  474.         }
  475.         if (gPending_race < 0) {
  476.             gPending_race = PickNetRace(gProgram_state.current_race_index,
  477.                 gCurrent_net_game->options.race_sequence_type);
  478.         }
  479.         the_message->contents.data.start_race.next_race = gPending_race;
  480.     }
  481.     NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
  482.     if (gProgram_state.racing) {
  483.         SendCurrentPowerups();
  484.     }
  485. }
  486.  
  487. // IDA: void __cdecl SignalToStartRace()
  488. void SignalToStartRace(void) {
  489.     LOG_TRACE("()");
  490.  
  491.     gCurrent_net_game->no_races_yet = 0;
  492.     SignalToStartRace2(-1);
  493. }
  494.  
  495. // IDA: void __cdecl SetUpNetCarPositions()
  496. void SetUpNetCarPositions(void) {
  497.     int i;
  498.     //int j; // Pierre-Marie Baty -- unused variable
  499.     int k;
  500.     int grid_index;
  501.     int racer_count;
  502.     LOG_TRACE("()");
  503.  
  504.     DisableNetService();
  505.     if (!gInitialised_grid) {
  506.         for (i = 0; i < gNumber_of_net_players; i++) {
  507.             gNet_players[i].grid_position_set = 0;
  508.             gNet_players[i].last_waste_message = 0;
  509.             gNet_players[i].wasteage_attributed = 0;
  510.         }
  511.     }
  512.     for (i = 0; i < gNumber_of_net_players; i++) {
  513.         gCurrent_race.opponent_list[i].index = -1;
  514.         gCurrent_race.opponent_list[i].ranking = IRandomBetween(0, 99);
  515.         gCurrent_race.opponent_list[i].car_spec = gNet_players[i].car;
  516.         gCurrent_race.opponent_list[i].net_player_index = i;
  517.         gNet_players[i].opponent_list_index = i;
  518.     }
  519.     if (!gInitialised_grid && gCurrent_net_game->options.grid_start) {
  520.         qsort(gCurrent_race.opponent_list, gNumber_of_net_players, sizeof(tOpp_spec), SortGridFunction);
  521.     }
  522.     gCurrent_race.number_of_racers = 0;
  523.     for (i = 0; i < gNumber_of_net_players; i++) {
  524.         gNet_players[gCurrent_race.opponent_list[i].net_player_index].opponent_list_index = i;
  525.     }
  526.     for (i = 0; i < gNumber_of_net_players; i++) {
  527.         if ((gCurrent_race.opponent_list[i].car_spec->driver == eDriver_oppo && !gInitialised_grid)
  528.             || (gCurrent_race.opponent_list[i].car_spec->driver >= eDriver_net_human && !gNet_players[gCurrent_race.opponent_list[i].net_player_index].grid_position_set)) {
  529.             grid_index = -1;
  530.             racer_count = 0;
  531.             while (racer_count < 6 && grid_index < 0) {
  532.                 grid_index = racer_count;
  533.                 for (k = 0; k < gNumber_of_net_players; k++) {
  534.                     if (k != i
  535.                         && gNet_players[gCurrent_race.opponent_list[k].net_player_index].grid_position_set
  536.                         && gNet_players[gCurrent_race.opponent_list[k].net_player_index].grid_index == racer_count) {
  537.                         grid_index = -1;
  538.                         break;
  539.                     }
  540.                 }
  541.                 racer_count++;
  542.             }
  543.             if (grid_index < 0) {
  544.                 FatalError(kFatalError_NetworkCodeSelfCheck);
  545.             }
  546.             SetInitialPosition(&gCurrent_race, i, grid_index);
  547.             gNet_players[gCurrent_race.opponent_list[i].net_player_index].grid_index = grid_index;
  548.             if (gInitialised_grid) {
  549.                 InitialiseCar2(gCurrent_race.opponent_list[i].car_spec, 0);
  550.             } else {
  551.                 gCurrent_race.number_of_racers = i + 1;
  552.             }
  553.             gNet_players[gCurrent_race.opponent_list[i].net_player_index].grid_position_set = 1;
  554.         }
  555.     }
  556.     gCurrent_race.number_of_racers = gNumber_of_net_players;
  557.     gInitialised_grid = 1;
  558.     ReenableNetService();
  559. }
  560.  
  561. // IDA: void __usercall ReinitialiseCar(tCar_spec *pCar@<EAX>)
  562. void ReinitialiseCar(tCar_spec* pCar) {
  563.     int i;
  564.     LOG_TRACE("(%p)", pCar);
  565.  
  566.     StopCarSmokingInstantly(pCar);
  567.     LoseAllLocalPowerups(pCar);
  568.     InitialiseCar(pCar);
  569.     TotallyRepairACar(pCar);
  570.     if (pCar->driver == eDriver_local_human) {
  571.         gLast_it_change = PDGetTotalTime() + 2000;
  572.     }
  573. }
  574.  
  575. // IDA: void __usercall RepositionPlayer(int pIndex@<EAX>)
  576. void RepositionPlayer(int pIndex) {
  577.     tNet_message* the_message;
  578.     tCar_spec* car;
  579.     LOG_TRACE("(%d)", pIndex);
  580.  
  581.     car = gNet_players[pIndex].car;
  582.     gNet_players[pIndex].wasted = 0;
  583.     gNet_players[pIndex].reposition_time = 0;
  584.     if (gCurrent_net_game->type == eNet_game_type_carnage
  585.         || gCurrent_net_game->type == eNet_game_type_checkpoint
  586.         || gCurrent_net_game->type == eNet_game_type_sudden_death) {
  587.         BrMatrix34Copy(&car->car_master_actor->t.t.mat, &gNet_players[pIndex].initial_position);
  588.     } else {
  589.         SetInitialPosition(&gCurrent_race, gNet_players[pIndex].opponent_list_index, gNet_players[pIndex].grid_index);
  590.     }
  591.     ReinitialiseCar(car);
  592.     SetFlipUpCar(car);
  593.     car->last_car_car_collision = GetRaceTime() + 100;
  594.     SignalToStartRace2(pIndex);
  595.     if (car->driver == eDriver_local_human) {
  596.         CancelPendingCunningStunt();
  597.         gProgram_state.credits_earned = gInitial_net_credits[gCurrent_net_game->options.starting_money_index];
  598.         gProgram_state.credits_lost = 0;
  599.     }
  600. }
  601.  
  602. // IDA: void __usercall DisableCar(tCar_spec *pCar@<EAX>)
  603. void DisableCar(tCar_spec* pCar) {
  604.     LOG_TRACE("(%p)", pCar);
  605.  
  606.     if (pCar->driver_name[0] != '\0') {
  607.         if (!pCar->disabled) {
  608.             pCar->disabled = 1;
  609.             ForceRebuildActiveCarList();
  610.         }
  611.         if (pCar->car_master_actor->t.t.mat.m[3][0] < 500.0f) {
  612.             pCar->car_master_actor->t.t.mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0] + 1000.0f;
  613.             pCar->car_master_actor->t.t.mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1] + 1000.0f;
  614.             pCar->car_master_actor->t.t.mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2] + 1000.0f;
  615.             pCar->old_frame_mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0];
  616.             pCar->old_frame_mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1];
  617.             pCar->old_frame_mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2];
  618.         }
  619.     }
  620. }
  621.  
  622. // IDA: void __usercall EnableCar(tCar_spec *pCar@<EAX>)
  623. void EnableCar(tCar_spec* pCar) {
  624.     LOG_TRACE("(%p)", pCar);
  625.  
  626.     if (pCar->driver_name[0] != '\0') {
  627.         if (pCar->disabled) {
  628.             pCar->disabled = 0;
  629.             ForceRebuildActiveCarList();
  630.         }
  631.         if (pCar->car_master_actor->t.t.mat.m[3][0] > 500.0f) {
  632.             pCar->car_master_actor->t.t.mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0] - 1000.0f;
  633.             pCar->car_master_actor->t.t.mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1] - 1000.0f;
  634.             pCar->car_master_actor->t.t.mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2] - 1000.0f;
  635.             pCar->old_frame_mat.m[3][0] = pCar->car_master_actor->t.t.mat.m[3][0];
  636.             pCar->old_frame_mat.m[3][1] = pCar->car_master_actor->t.t.mat.m[3][1];
  637.             pCar->old_frame_mat.m[3][2] = pCar->car_master_actor->t.t.mat.m[3][2];
  638.         }
  639.     }
  640. }
  641.  
  642. // IDA: void __usercall DoNetworkHeadups(int pCredits@<EAX>)
  643. void DoNetworkHeadups(int pCredits) {
  644.     char s[256];
  645.     char s2[256];
  646.     static tU32 last_flash;
  647.     static int flash_state;
  648.     LOG_TRACE("(%d)", pCredits);
  649.  
  650.     if (gNot_shown_race_type_headup) {
  651.         gNot_shown_race_type_headup = 0;
  652.         NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_NetworkGameTypeNames_START + gCurrent_net_game->type));
  653.     }
  654.     if (gTime_for_punishment && gTime_for_punishment <= (tU32) PDGetTotalTime()) { // Pierre-Marie Baty -- added type cast
  655.         gTime_for_punishment = 0;
  656.         switch (gCurrent_net_game->type) {
  657.         case eNet_game_type_carnage:
  658.             NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_THAT_HALVED_YOUR_KILL_COUNT));
  659.             break;
  660.         case eNet_game_type_checkpoint:
  661.             NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_THAT_LOST_YOU_A_CHECKPOINT));
  662.             break;
  663.         case eNet_game_type_sudden_death:
  664.             NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_BACK_TO_THE_START));
  665.             break;
  666.         case eNet_game_type_foxy:
  667.             NewTextHeadupSlot(4, 0, 2000, -4, GetMiscString(kMiscString_THAT_HALVED_YOUR_TIME));
  668.             break;
  669.         default:
  670.             break;
  671.         }
  672.     }
  673.     if (gNet_mode == eNet_mode_none || gNet_recovery_cost[gCurrent_net_game->type] <= gProgram_state.credits_earned - gProgram_state.credits_lost || Flash(200, &last_flash, &flash_state)) {
  674.         sprintf(s, "\xf8%d\xfa %s", pCredits, GetMiscString(kMiscString_CREDITS));
  675.         ChangeHeadupText(gNet_cash_headup, s);
  676.     } else {
  677.         ChangeHeadupText(gNet_cash_headup, "");
  678.     }
  679.     switch (gCurrent_net_game->type) {
  680.     case eNet_game_type_carnage:
  681.         sprintf(s, "%s \xf8%d\xfa", GetMiscString(kMiscString_TARGET_180), gPed_target);
  682.         break;
  683.     case eNet_game_type_car_crusher:
  684.         sprintf(s, "%s \xf8%d\xfa", GetMiscString(kMiscString_TARGET_181), gCurrent_net_game->options.race_end_target);
  685.         break;
  686.     case eNet_game_type_foxy:
  687.         TimerString(gCurrent_net_game->options.race_end_target, s2, 1, 1);
  688.         sprintf(s, "%s \xf8%s\xfa", GetMiscString(kMiscString_TARGET_182), s2);
  689.         break;
  690.     case eNet_game_type_tag:
  691.         TimerString(gCurrent_net_game->options.race_end_target, s2, 1, 1);
  692.         sprintf(s, "%s \xf8%s\xfa", GetMiscString(kMiscString_LIMIT), s2);
  693.         break;
  694.     default:
  695.         s[0] = '\0';
  696.         break;
  697.     }
  698.     ChangeHeadupText(gNet_ped_headup, s);
  699. }
  700.  
  701. #define HEADUP1 ((tHeadup_pair*)pFirst_one)
  702. #define HEADUP2 ((tHeadup_pair*)pSecond_one)
  703.  
  704. // IDA: int __usercall SortNetHeadAscending@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
  705. int SortNetHeadAscending(void* pFirst_one, void* pSecond_one) {
  706.     LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
  707.  
  708.     if (HEADUP1->out_of_game) {
  709.         if (HEADUP2->out_of_game) {
  710.             return HEADUP1->out_of_game - HEADUP2->out_of_game;
  711.         } else {
  712.             return INT_MAX;
  713.         }
  714.     } else if (HEADUP2->out_of_game) {
  715.         return -INT_MAX;
  716.     } else if (HEADUP2->score == HEADUP1->score) {
  717.         return gNet_players[HEADUP1->player_index].last_score_index
  718.             - gNet_players[HEADUP2->player_index].last_score_index;
  719.     } else {
  720.         return HEADUP1->score - HEADUP2->score;
  721.     }
  722. }
  723.  
  724. // IDA: int __usercall SortNetHeadDescending@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
  725. int SortNetHeadDescending(void* pFirst_one, void* pSecond_one) {
  726.     LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
  727.  
  728.     if (HEADUP1->out_of_game) {
  729.         if (HEADUP2->out_of_game) {
  730.             return HEADUP1->out_of_game - HEADUP2->out_of_game;
  731.         } else {
  732.             return INT_MAX;
  733.         }
  734.     } else if (HEADUP2->out_of_game) {
  735.         return -INT_MAX;
  736.     } else if (HEADUP2->score == HEADUP1->score) {
  737.         return gNet_players[HEADUP1->player_index].last_score_index
  738.             - gNet_players[HEADUP2->player_index].last_score_index;
  739.     } else {
  740.         return HEADUP2->score - HEADUP1->score;
  741.     }
  742. }
  743.  
  744. #undef HEADUP2
  745. #undef HEADUP1
  746.  
  747. // IDA: void __usercall ClipName(char *pName@<EAX>, tDR_font *pFont@<EDX>, int pMax_width@<EBX>)
  748. void ClipName(char* pName, tDR_font* pFont, int pMax_width) {
  749.     LOG_TRACE("(\"%s\", %p, %d)", pName, pFont, pMax_width);
  750.  
  751.     while (DRTextWidth(pFont, pName) > pMax_width) {
  752.         pName[strlen(pName) - 1] = 0;
  753.     }
  754. }
  755.  
  756. // IDA: void __usercall DoNetScores2(int pOnly_sort_scores@<EAX>)
  757. void DoNetScores2(int pOnly_sort_scores) {
  758.     int i;
  759.     int j;
  760.     int score;
  761.     int flags;
  762.     int index;
  763.     int right_edge;
  764.     int x;
  765.     int len;
  766.     int ascending_order;
  767.     char s[256];
  768.     static tU32 last_flash;
  769.     static int flash_state;
  770.     tHeadup_pair headup_pairs[6];
  771.     LOG_TRACE("(%d)", pOnly_sort_scores);
  772.  
  773.     ascending_order = gCurrent_net_game->type == eNet_game_type_checkpoint || gCurrent_net_game->type == eNet_game_type_tag;
  774.     for (i = 0; i < gNumber_of_net_players; i++) {
  775.         if (gNet_players[i].player_status < ePlayer_status_racing) {
  776.             headup_pairs[i].player_index = -1;
  777.             if (ascending_order) {
  778.                 headup_pairs[i].score = 1000001;
  779.             } else {
  780.                 headup_pairs[i].score = -1000001;
  781.             }
  782.             headup_pairs[i].out_of_game = 1000;
  783.         } else {
  784.             headup_pairs[i].player_index = i;
  785.             headup_pairs[i].score = gNet_players[i].score;
  786.             if (abs(gNet_players[i].score) != 1000000 && (gNet_players[i].score >= 0 || gCurrent_net_game->type == eNet_game_type_car_crusher)) {
  787.                 headup_pairs[i].out_of_game = 0;
  788.             } else {
  789.                 headup_pairs[i].out_of_game = gNet_players[i].last_score_index + 1;
  790.             }
  791.         }
  792.     }
  793.     for (i = gNumber_of_net_players; i < COUNT_OF(headup_pairs); i++) {
  794.         headup_pairs[i].player_index = -1;
  795.         if (ascending_order) {
  796.             headup_pairs[i].score = 1000001;
  797.         } else {
  798.             headup_pairs[i].score = -1000001;
  799.         }
  800.         headup_pairs[i].out_of_game = 1000;
  801.     }
  802.     qsort(headup_pairs, COUNT_OF(headup_pairs), sizeof(tHeadup_pair), (int (*)(const void*, const void*))(ascending_order ? SortNetHeadAscending : SortNetHeadDescending));
  803.  
  804.     right_edge = gCurrent_graf_data->net_head_box_x + gCurrent_graf_data->net_head_box_width + 5 * gCurrent_graf_data->net_head_box_pitch;
  805.     for (i = 0; i < COUNT_OF(headup_pairs); i++) {
  806.         index = headup_pairs[i].player_index;
  807.         if (index >= 0) {
  808.             gNet_players[index].last_score_index = i;
  809.         }
  810.         if (pOnly_sort_scores) {
  811.             continue;
  812.         }
  813.         x = gCurrent_graf_data->net_head_box_x + i * gCurrent_graf_data->net_head_box_pitch;
  814.         if (gCurrent_graf_data->net_head_box_bot > gProgram_state.current_render_top) {
  815.             DimRectangle(gBack_screen, x, gCurrent_graf_data->net_head_box_top, x + gCurrent_graf_data->net_head_box_width, gCurrent_graf_data->net_head_box_bot, 1);
  816.         }
  817.         if (index >= 0) {
  818.             Flash(200, &last_flash, &flash_state);
  819.             if (flash_state
  820.                 || (gCurrent_net_game->type != eNet_game_type_tag && gCurrent_net_game->type != eNet_game_type_foxy)
  821.                 || index != gIt_or_fox) {
  822.                 if (gNet_players[index].name_not_clipped) {
  823.                     ClipName(gNet_players[index].player_name, &gFonts[6], gCurrent_graf_data->net_head_box_width - gCurrent_graf_data->net_head_name_x_marg - 2);
  824.                     gNet_players[index].name_not_clipped = 0;
  825.                 }
  826.                 TransDRPixelmapText(gBack_screen, x + gCurrent_graf_data->net_head_name_x_marg, gCurrent_graf_data->net_head_name_y, &gFonts[6], gNet_players[index].player_name, right_edge);
  827.             }
  828.             if (abs(gNet_players[index].score) == 1000000) {
  829.                 if (flash_state) {
  830.                     strcpy(s, GetMiscString(kMiscString_WASTED_173));
  831.                 } else {
  832.                     s[0] = '\0';
  833.                 }
  834.             } else {
  835.                 switch (gCurrent_net_game->type) {
  836.                 case eNet_game_type_fight_to_death:
  837.                     sprintf(s, "%d%%", gNet_players[index].score);
  838.                     break;
  839.                 case eNet_game_type_car_crusher:
  840.                 case eNet_game_type_carnage:
  841.                     sprintf(s, "%d", gNet_players[index].score);
  842.                     break;
  843.                 case eNet_game_type_checkpoint:
  844.                     sprintf(s, "%d left", gNet_players[index].score >> 16);
  845.                     break;
  846.                 case eNet_game_type_sudden_death:
  847.                     if (gNet_players[index].score < 0) {
  848.                         if (flash_state) {
  849.                             sprintf(s, "%s", GetMiscString(kMiscString_OUT));
  850.                         } else {
  851.                             s[0] = '\0';
  852.                         }
  853.                     } else {
  854.                         score = gNet_players[index].score;
  855.                         sprintf(s, "%s -%d-", GetMiscString(kMiscString_IN), score);
  856.                     }
  857.                     break;
  858.                 case eNet_game_type_tag:
  859.                 case eNet_game_type_foxy:
  860.                     if (gNet_players[index].score >= 0) {
  861.                         if (index == gIt_or_fox && !flash_state) {
  862.                             s[0] = '\0';
  863.                         } else {
  864.                             TimerString(gNet_players[index].score, s, 0, 1);
  865.                         }
  866.                     } else {
  867.                         sprintf(s, "%s", GetMiscString(kMiscString_OUT));
  868.                     }
  869.                     break;
  870.                 default:
  871.                     break;
  872.                 }
  873.             }
  874.             len = DRTextWidth(&gFonts[6], s);
  875.             TransDRPixelmapText(gBack_screen, x + gCurrent_graf_data->net_head_score_x - len, gCurrent_graf_data->net_head_score_y, &gFonts[6], s, right_edge);
  876.             DRPixelmapRectangleMaskedCopy(gBack_screen, x + gCurrent_graf_data->net_head_num_x, gCurrent_graf_data->net_head_num_y, gDigits_pix, 0, i * gCurrent_graf_data->net_head_num_height, gDigits_pix->width, gCurrent_graf_data->net_head_num_height);
  877.             DRPixelmapRectangleMaskedCopy(gBack_screen, x + gCurrent_graf_data->net_head_icon_x, gCurrent_graf_data->net_head_icon_y, gIcons_pix, 0, gCurrent_graf_data->net_head_icon_height * gNet_players[index].car_index, gIcons_pix->width, gCurrent_graf_data->net_head_icon_height);
  878.             if (gNet_players[index].ID == gLocal_net_ID) {
  879.                 BrPixelmapLine(gBack_screen, x, gCurrent_graf_data->net_head_box_top, x, gCurrent_graf_data->net_head_box_bot - 1, 0);
  880.                 BrPixelmapLine(gBack_screen, x + gCurrent_graf_data->net_head_box_width - 1, gCurrent_graf_data->net_head_box_top, x + gCurrent_graf_data->net_head_box_width - 1, gCurrent_graf_data->net_head_box_bot - 1, 0);
  881.                 BrPixelmapLine(gBack_screen, x + 1, gCurrent_graf_data->net_head_box_bot, x + gCurrent_graf_data->net_head_box_width - 2, gCurrent_graf_data->net_head_box_bot, 0);
  882.                 BrPixelmapLine(gBack_screen, x - 1, gCurrent_graf_data->net_head_box_top, x - 1, gCurrent_graf_data->net_head_box_bot - 1, 255);
  883.                 BrPixelmapLine(gBack_screen, x + gCurrent_graf_data->net_head_box_width, gCurrent_graf_data->net_head_box_top, x + gCurrent_graf_data->net_head_box_width, gCurrent_graf_data->net_head_box_bot - 1, 255);
  884.                 BrPixelmapLine(gBack_screen, x + 1, gCurrent_graf_data->net_head_box_bot + 1, x + gCurrent_graf_data->net_head_box_width - 2, gCurrent_graf_data->net_head_box_bot + 1, 255);
  885.                 BrPixelmapPixelSet(gBack_screen, x, gCurrent_graf_data->net_head_box_bot, 255);
  886.                 BrPixelmapPixelSet(gBack_screen, x + gCurrent_graf_data->net_head_box_width - 1, gCurrent_graf_data->net_head_box_bot, 255);
  887.             }
  888.         }
  889.     }
  890. }
  891.  
  892. // IDA: void __cdecl DoNetScores()
  893. void DoNetScores(void) {
  894.     LOG_TRACE("()");
  895.  
  896.     DoNetScores2(0);
  897. }
  898.  
  899. // IDA: void __cdecl InitNetHeadups()
  900. void InitNetHeadups(void) {
  901.     LOG_TRACE("()");
  902.  
  903.     gIcons_pix = LoadPixelmap("CARICONS.PIX");
  904.     if (gIcons_pix != NULL) {
  905.         BrMapAdd(gIcons_pix);
  906.     }
  907.     gDigits_pix = LoadPixelmap("HDIGITS.PIX");
  908.     if (gDigits_pix != NULL) {
  909.         BrMapAdd(gDigits_pix);
  910.     }
  911.     /* The Windows version does not use gIcons_pix_low_res. */
  912.     if (gGraf_data_index != 0) {
  913.         SwitchToLoresMode();
  914.         gIcons_pix_low_res = LoadPixelmap("CARICONS.PIX");
  915.         SwitchToRealResolution();
  916.     } else {
  917.         gIcons_pix_low_res = gIcons_pix;
  918.     }
  919. }
  920.  
  921. // IDA: void __cdecl DisposeNetHeadups()
  922. void DisposeNetHeadups(void) {
  923.     LOG_TRACE("()");
  924.  
  925.     /* Windows version does not use gIcons_pix_low_res. */
  926.     if (gIcons_pix_low_res != NULL && gIcons_pix_low_res != gIcons_pix) {
  927.         BrPixelmapFree(gIcons_pix_low_res);
  928.     }
  929.  
  930.     if (gIcons_pix != NULL) {
  931.         BrMapRemove(gIcons_pix);
  932.         BrPixelmapFree(gIcons_pix);
  933.     }
  934.     if (gDigits_pix != NULL) {
  935.         BrMapRemove(gDigits_pix);
  936.         BrPixelmapFree(gDigits_pix);
  937.     }
  938. }
  939.  
  940. // IDA: void __cdecl EverybodysLost()
  941. void EverybodysLost(void) {
  942.     tNet_message* the_message;
  943.     int i;
  944.     LOG_TRACE("()");
  945.  
  946.     for (i = 0; i < gNumber_of_net_players; i++) {
  947.         gNet_players[i].played += 1;
  948.         the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
  949.         the_message->contents.data.race_over.reason = eRace_over_network_loss;
  950.         NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, gNet_players[i].ID, NULL);
  951.     }
  952. }
  953.  
  954. // IDA: void __usercall DeclareWinner(int pWinner_index@<EAX>)
  955. void DeclareWinner(int pWinner_index) {
  956.     tNet_message* the_message;
  957.     int i;
  958.     int j;
  959.     int best_score_index;
  960.     char s[256];
  961.     LOG_TRACE("(%d)", pWinner_index);
  962.  
  963.     DoNetScores2(1);
  964.     the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
  965.     the_message->contents.data.race_over.reason = eRace_over_network_victory;
  966.     NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, gNet_players[pWinner_index].ID, NULL);
  967.     gNet_players[pWinner_index].won++;
  968.     if (gCurrent_net_game->type == eNet_game_type_sudden_death
  969.         || gCurrent_net_game->type == eNet_game_type_tag
  970.         || gCurrent_net_game->type == eNet_game_type_fight_to_death) {
  971.         gNet_players[pWinner_index].games_score += gGame_scores[5];
  972.     }
  973.     sprintf(s, "%s %s", gNet_players[pWinner_index].player_name, GetMiscString(kMiscString_IS_THE_WINNER));
  974.     for (i = 0; i < gNumber_of_net_players; i++) {
  975.         if (gCurrent_net_game->type != eNet_game_type_sudden_death && gCurrent_net_game->type != eNet_game_type_tag && gCurrent_net_game->type != eNet_game_type_fight_to_death) {
  976.             best_score_index = gNet_players[i].last_score_index;
  977.             for (j = 0; j < gNumber_of_net_players; j++) {
  978.                 if (gNet_players[j].score == gNet_players[i].score && gNet_players[j].last_score_index < best_score_index) {
  979.                     best_score_index = gNet_players[j].last_score_index;
  980.                 }
  981.             }
  982.             gNet_players[i].games_score += gGame_scores[5 - best_score_index];
  983.         }
  984.         gNet_players[i].played++;
  985.         if (i != pWinner_index) {
  986.             the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
  987.             the_message->contents.data.race_over.reason = eRace_over_network_loss;
  988.             NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, gNet_players[i].ID, NULL);
  989.             NetSendHeadupToPlayer(s, gNet_players[i].ID);
  990.         }
  991.     }
  992.     gReceived_game_scores = 1;
  993.     the_message = NetBuildMessage(NETMSGID_GAMESCORES, 0);
  994.     for (i = 0; i < gNumber_of_net_players; i++) {
  995.         the_message->contents.data.game_scores.scores[i].played = gNet_players[i].played;
  996.         the_message->contents.data.game_scores.scores[i].won = gNet_players[i].won;
  997.         the_message->contents.data.game_scores.scores[i].score = gNet_players[i].games_score;
  998.     }
  999.     NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
  1000. }
  1001.  
  1002. // IDA: void __usercall PlayerIsIt(tNet_game_player_info *pPlayer@<EAX>)
  1003. void PlayerIsIt(tNet_game_player_info* pPlayer) {
  1004.     int i;
  1005.     char s[256];
  1006.     LOG_TRACE("(%p)", pPlayer);
  1007.  
  1008.     if (pPlayer - gNet_players == gIt_or_fox) {
  1009.         return;
  1010.     }
  1011.     for (i = 0; i < gNumber_of_net_players; i++) {
  1012.         StopCarBeingIt(gNet_players[i].car);
  1013.     }
  1014.     if (gCurrent_net_game->type == eNet_game_type_foxy) {
  1015.         pPlayer->car->power_up_levels[1] = 0;
  1016.     } else if (gCurrent_net_game->type == eNet_game_type_tag && gIt_or_fox >= 0) {
  1017.         gNet_players[gIt_or_fox].car->power_up_levels[1] = 0;
  1018.     }
  1019.     MakeCarIt(pPlayer->car);
  1020.     gIt_or_fox = pPlayer - gNet_players;
  1021.     sprintf(s, "%s", GetMiscString(gCurrent_net_game->type == eNet_game_type_tag ? kMiscString_HA_HA_YOU_ARE_IT : kMiscString_YOU_ARE_THE_FOX));
  1022.     NetSendHeadupToPlayer(s, pPlayer->ID);
  1023.     for (i = 0; i < gNumber_of_net_players; i++) {
  1024.         if (&gNet_players[i] != pPlayer) {
  1025.             sprintf(s, "%s %s", pPlayer->player_name, GetMiscString(gCurrent_net_game->type == eNet_game_type_tag ? 185 : 187));
  1026.             NetSendHeadupToPlayer(s, gNet_players[i].ID);
  1027.         }
  1028.     }
  1029. }
  1030.  
  1031. // IDA: int __usercall FarEnoughAway@<EAX>(tNet_game_player_info *pPlayer_1@<EAX>, tNet_game_player_info *pPlayer_2@<EDX>)
  1032. int FarEnoughAway(tNet_game_player_info* pPlayer_1, tNet_game_player_info* pPlayer_2) {
  1033.     br_vector3 difference;
  1034.     LOG_TRACE("(%p, %p)", pPlayer_1, pPlayer_2);
  1035.  
  1036.     BrVector3Sub(&difference, &pPlayer_1->car->pos, &pPlayer_2->car->pos);
  1037.     return BrVector3LengthSquared(&difference) >= 4.0f;
  1038. }
  1039.  
  1040. // IDA: void __usercall CarInContactWithItOrFox(tNet_game_player_info *pPlayer@<EAX>)
  1041. void CarInContactWithItOrFox(tNet_game_player_info* pPlayer) {
  1042.     LOG_TRACE("(%p)", pPlayer);
  1043.  
  1044.     if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
  1045.         if (PDGetTotalTime() - gLast_it_change > 500) {
  1046.             gLast_it_change = PDGetTotalTime();
  1047.             if (gIt_or_fox >= 0) {
  1048.                 gLast_lepper = &gNet_players[gIt_or_fox];
  1049.             }
  1050.             PlayerIsIt(pPlayer);
  1051.         }
  1052.     }
  1053. }
  1054.  
  1055. // IDA: void __usercall SelectRandomItOrFox(int pNot_this_one@<EAX>)
  1056. void SelectRandomItOrFox(int pNot_this_one) {
  1057.     int i;
  1058.     int new_choice;
  1059.     LOG_TRACE("(%d)", pNot_this_one);
  1060.  
  1061.     new_choice = 0;
  1062.     gLast_lepper = NULL;
  1063.     if (gCurrent_net_game->type == eNet_game_type_tag) {
  1064.         for (i = 0; i < gNumber_of_net_players; i++) {
  1065.             if (gNet_players[i].last_score_index == 0) {
  1066.                 new_choice = i;
  1067.                 break;
  1068.             }
  1069.         }
  1070.     } else {
  1071.         for (i = 0; i < gNumber_of_net_players; i++) {
  1072.             if (i != pNot_this_one && gNet_players[i].last_score_index == gNumber_of_net_players - 1) {
  1073.                 PlayerIsIt(&gNet_players[i]);
  1074.                 return;
  1075.             }
  1076.         }
  1077.         do {
  1078.             new_choice = IRandomBetween(0, gNumber_of_net_players - 1);
  1079.         } while (new_choice == pNot_this_one && !gNet_players[new_choice].car->knackered);
  1080.     }
  1081.     PlayerIsIt(&gNet_players[new_choice]);
  1082. }
  1083.  
  1084. // IDA: void __cdecl CalcPlayerScores()
  1085. void CalcPlayerScores(void) {
  1086.     int i;
  1087.     int j;
  1088.     int knock_out_bit;
  1089.     int e_dam;
  1090.     int t_dam;
  1091.     int d_dam;
  1092.     int w_dam;
  1093.     int cars_left;
  1094.     int car_left;
  1095.     int flags;
  1096.     int score;
  1097.     int highest;
  1098.     int next_highest;
  1099.     int lowest_score;
  1100.     int player_left;
  1101.     int new_choice;
  1102.     tCar_spec* car;
  1103.     tNet_message* message;
  1104.     tS32 time;
  1105.     char s[256];
  1106.     tNet_game_player_info* lowest_score_player;
  1107.     LOG_TRACE("()");
  1108.  
  1109.     time = GetTotalTime();
  1110.  
  1111.     if (gCurrent_net_game->type == eNet_game_type_carnage) {
  1112.         highest = 0;
  1113.         next_highest = 0;
  1114.         for (i = 0; i < gNumber_of_net_players; ++i) {
  1115.             if (gNet_players[i].score > highest) {
  1116.                 next_highest = highest;
  1117.                 highest = gNet_players[i].score;
  1118.             } else if (gNet_players[i].score > next_highest) {
  1119.                 next_highest = gNet_players[i].score;
  1120.             }
  1121.         }
  1122.         gPed_target = (gTotal_peds - (gProgram_state.peds_killed - highest - next_highest)) / 2 + 1;
  1123.         if (gCurrent_net_game->options.race_end_target < gPed_target) {
  1124.             gPed_target = gCurrent_net_game->options.race_end_target;
  1125.         }
  1126.     } else if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
  1127.         if (gIt_or_fox < 0) {
  1128.             SelectRandomItOrFox(-1);
  1129.         }
  1130.         if (gLast_lepper != NULL && gIt_or_fox >= 0 && FarEnoughAway(gLast_lepper, &gNet_players[gIt_or_fox])) {
  1131.             gLast_lepper = NULL;
  1132.         }
  1133.     }
  1134.     lowest_score = 9999;
  1135.     lowest_score_player = NULL;
  1136.     for (i = 0; i < gNumber_of_net_players; i++) {
  1137.         car = gNet_players[i].car;
  1138.         if (gNet_players[i].reposition_time != 0 && gNet_players[i].reposition_time <= time && (!gRace_finished || gRace_over_reason == -1)) {
  1139.             RepositionPlayer(i);
  1140.         }
  1141.         if (gNet_players[i].last_waste_message != 0
  1142.             && !gNet_players[i].wasteage_attributed
  1143.             && time - gNet_players[i].last_waste_message > 500) {
  1144.             sprintf(s, "%s %s", gNet_players[i].player_name, GetMiscString(kMiscString_COMMITTED_SUICIDE));
  1145.             gNet_players[i].last_waste_message = 0;
  1146.             gNet_players[i].wasteage_attributed = 0;
  1147.             if (gCurrent_net_game->type == eNet_game_type_car_crusher) {
  1148.                 gNet_players[i].score--;
  1149.             }
  1150.             NetSendHeadupToEverybody(s);
  1151.         }
  1152.         SetKnackeredFlag(car);
  1153.         if (car->knackered && !gNet_players[i].wasted) {
  1154.             gNet_players[i].wasted = 1;
  1155.             message = NetBuildMessage(NETMSGID_WASTED, 0);
  1156.             message->contents.data.wasted.victim = gNet_players[i].ID;
  1157.             message->contents.data.wasted.culprit = -1;
  1158.             NetGuaranteedSendMessageToEverybody(gCurrent_net_game, message, 0);
  1159.             switch (gCurrent_net_game->type) {
  1160.             case eNet_game_type_fight_to_death:
  1161.                 cars_left = 0;
  1162.                 for (j = 0; j < gNumber_of_net_players; j++) {
  1163.                     if (!gNet_players[j].wasted) {
  1164.                         cars_left++;
  1165.                         car_left = j;
  1166.                     }
  1167.                 }
  1168.                 gNet_players[i].games_score += gGame_scores[5 - cars_left];
  1169.                 if (cars_left == 1) {
  1170.                     DeclareWinner(car_left);
  1171.                 } else if (cars_left <= 0) {
  1172.                     EverybodysLost();
  1173.                 }
  1174.                 break;
  1175.             case eNet_game_type_car_crusher:
  1176.             case eNet_game_type_sudden_death:
  1177.                 gNet_players[i].reposition_time = GetTotalTime() + 5000;
  1178.                 break;
  1179.             case eNet_game_type_carnage:
  1180.                 gNet_players[i].reposition_time = GetTotalTime() + 5000;
  1181.                 gNet_players[i].score /= 2;
  1182.                 break;
  1183.             case eNet_game_type_checkpoint:
  1184.                 if (gNet_players[i].score >> 16 != gCurrent_race.check_point_count) {
  1185.                     knock_out_bit = IRandomBetween(0, gCurrent_race.check_point_count - 1);
  1186.                     while (((1 << knock_out_bit) & gNet_players[i].score) != 0) {
  1187.                         knock_out_bit++;
  1188.                         if (gCurrent_race.check_point_count <= knock_out_bit) {
  1189.                             knock_out_bit = 0;
  1190.                         }
  1191.                     }
  1192.                     gNet_players[i].score |= 1 << knock_out_bit;
  1193.                 }
  1194.                 gNet_players[i].reposition_time = GetTotalTime() + 5000;
  1195.                 break;
  1196.             case eNet_game_type_tag:
  1197.                 gNet_players[i].reposition_time = GetTotalTime() + 5000;
  1198.                 PlayerIsIt(&gNet_players[i]);
  1199.                 break;
  1200.             case eNet_game_type_foxy:
  1201.                 gNet_players[i].reposition_time = GetTotalTime() + 5000;
  1202.                 gNet_players[i].score /= 2;
  1203.                 if (gNumber_of_net_players > 1 && i == gIt_or_fox) {
  1204.                     SelectRandomItOrFox(i);
  1205.                 }
  1206.                 break;
  1207.             default:
  1208.                 break;
  1209.             }
  1210.         }
  1211.         switch (gCurrent_net_game->type) {
  1212.         case eNet_game_type_fight_to_death:
  1213.             if (car->knackered) {
  1214.                 if (gCurrent_net_game->type == eNet_game_type_checkpoint
  1215.                     || gCurrent_net_game->type == eNet_game_type_tag) {
  1216.                     gNet_players[i].score = 1000000;
  1217.                 } else {
  1218.                     gNet_players[i].score = -1000000;
  1219.                 }
  1220.             } else {
  1221.                 e_dam = car->damage_units[eDamage_engine].damage_level;
  1222.                 t_dam = car->damage_units[eDamage_transmission].damage_level;
  1223.                 d_dam = car->damage_units[eDamage_driver].damage_level;
  1224.                 w_dam = (car->damage_units[eDamage_lr_wheel].damage_level
  1225.                             + car->damage_units[eDamage_lf_wheel].damage_level
  1226.                             + car->damage_units[eDamage_rr_wheel].damage_level
  1227.                             + car->damage_units[eDamage_rf_wheel].damage_level)
  1228.                     / 4;
  1229.  
  1230.                 if (e_dam >= t_dam && e_dam >= d_dam && e_dam >= w_dam) {
  1231.                     gNet_players[i].score = 100 - e_dam;
  1232.                 } else if (t_dam >= d_dam && t_dam >= w_dam) {
  1233.                     gNet_players[i].score = 100 - t_dam;
  1234.                 } else if (w_dam >= d_dam) {
  1235.                     gNet_players[i].score = 100 - w_dam;
  1236.                 } else {
  1237.                     gNet_players[i].score = 100 - d_dam;
  1238.                 }
  1239.             }
  1240.             break;
  1241.         case eNet_game_type_carnage:
  1242.             if (gNet_players[i].score >= gPed_target && !gRace_finished) {
  1243.                 DeclareWinner(i);
  1244.             }
  1245.             break;
  1246.         case eNet_game_type_checkpoint:
  1247.             score = 0;
  1248.             gNet_players[i].score = gNet_players[i].score & 0xffff;
  1249.             flags = gNet_players[i].score;
  1250.             for (j = 0; j < gCurrent_race.check_point_count; j++) {
  1251.                 if ((flags & 1) != 0) {
  1252.                     score++;
  1253.                 }
  1254.                 flags >>= 1;
  1255.             }
  1256.             gNet_players[i].score |= score << 16;
  1257.             if (!score && !gRace_finished) {
  1258.                 DeclareWinner(i);
  1259.             }
  1260.             break;
  1261.         case eNet_game_type_sudden_death:
  1262.             if (gNet_players[i].score >= 0) {
  1263.                 if (gNet_players[i].score >= lowest_score) {
  1264.                     if (gNet_players[i].score == lowest_score) {
  1265.                         lowest_score_player = 0;
  1266.                     }
  1267.                 } else {
  1268.                     lowest_score = gNet_players[i].score;
  1269.                     lowest_score_player = &gNet_players[i];
  1270.                 }
  1271.             }
  1272.             break;
  1273.         case eNet_game_type_tag:
  1274.             if (i == gIt_or_fox && !gCountdown && gNet_players[i].score >= 0) {
  1275.                 gNet_players[i].score += gFrame_period;
  1276.                 if (gNet_players[i].score >= gCurrent_net_game->options.race_end_target) {
  1277.                     lowest_score_player = &gNet_players[i];
  1278.                 }
  1279.             }
  1280.             break;
  1281.         case eNet_game_type_foxy:
  1282.             if (i == gIt_or_fox && !gCountdown && !gRace_finished) {
  1283.                 gNet_players[i].score += gFrame_period;
  1284.                 if (gNet_players[i].score >= gCurrent_net_game->options.race_end_target) {
  1285.                     DeclareWinner(i);
  1286.                 }
  1287.             }
  1288.             break;
  1289.         default:
  1290.             continue;
  1291.         }
  1292.     }
  1293.     if ((gCurrent_net_game->type == eNet_game_type_sudden_death || gCurrent_net_game->type == eNet_game_type_tag)
  1294.         && lowest_score_player != NULL
  1295.         && lowest_score_player->score >= 0) {
  1296.         player_left = -1;
  1297.         cars_left = 0;
  1298.         for (i = 0; i < gNumber_of_net_players; ++i) {
  1299.             if (gNet_players[i].score >= 0 && &gNet_players[i] != lowest_score_player) {
  1300.                 ++cars_left;
  1301.                 if (player_left == -1) {
  1302.                     player_left = i;
  1303.                 } else {
  1304.                     player_left = -2;
  1305.                 }
  1306.             }
  1307.         }
  1308.         if (cars_left) {
  1309.             lowest_score_player->car->knackered = 1;
  1310.             lowest_score_player->wasted = 1;
  1311.             lowest_score_player->games_score += gGame_scores[5 - cars_left];
  1312.             lowest_score_player->score = -1;
  1313.             if (player_left == -1) {
  1314.                 EverybodysLost();
  1315.             } else if (player_left < 0) {
  1316.                 if (gCurrent_net_game->type == eNet_game_type_tag) {
  1317.                     SelectRandomItOrFox(i);
  1318.                 }
  1319.                 SendGameplay(lowest_score_player->ID, eNet_gameplay_suddenly_death, 0, 0, 0, 0);
  1320.                 sprintf(s, "%s %s", lowest_score_player->player_name, GetMiscString(kMiscString_IS_OUT));
  1321.                 NetSendHeadupToEverybody(s);
  1322.             } else {
  1323.                 DeclareWinner(player_left);
  1324.             }
  1325.         }
  1326.     }
  1327. }
  1328.  
  1329. // IDA: void __cdecl SendPlayerScores()
  1330. void SendPlayerScores(void) {
  1331.     tNet_contents* the_contents;
  1332.     int i;
  1333.     LOG_TRACE("()");
  1334.  
  1335.     the_contents = NetGetBroadcastContents(NETMSGID_SCORES, 0);
  1336.     if (gCurrent_net_game->type == eNet_game_type_carnage) {
  1337.         the_contents->data.scores.general_score = gPed_target;
  1338.     } else if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
  1339.         the_contents->data.scores.general_score = gNet_players[gIt_or_fox].ID;
  1340.     }
  1341.     for (i = 0; i < gNumber_of_net_players; i++) {
  1342.         the_contents->data.scores.scores[i] = gNet_players[i].score;
  1343.     }
  1344. }
  1345.  
  1346. // IDA: void __cdecl DoNetGameManagement()
  1347. void DoNetGameManagement(void) {
  1348.     LOG_TRACE("()");
  1349.  
  1350.     if (gNet_mode == eNet_mode_host) {
  1351.         CalcPlayerScores();
  1352.         SendPlayerScores();
  1353.     }
  1354. }
  1355.  
  1356. // IDA: void __usercall InitialisePlayerScore(tNet_game_player_info *pPlayer@<EAX>)
  1357. void InitialisePlayerScore(tNet_game_player_info* pPlayer) {
  1358.     LOG_TRACE("(%p)", pPlayer);
  1359.  
  1360.     PossibleService();
  1361.     switch (gCurrent_net_game->type) {
  1362.     case eNet_game_type_fight_to_death:
  1363.         pPlayer->score = 100;
  1364.         break;
  1365.     case eNet_game_type_car_crusher:
  1366.     case eNet_game_type_carnage:
  1367.         pPlayer->score = 0;
  1368.         break;
  1369.     case eNet_game_type_checkpoint:
  1370.         pPlayer->score = 0xffff;
  1371.         break;
  1372.     case eNet_game_type_sudden_death:
  1373.         pPlayer->score = 0;
  1374.         break;
  1375.     case eNet_game_type_tag:
  1376.         pPlayer->score = 0;
  1377.         break;
  1378.     case eNet_game_type_foxy:
  1379.         pPlayer->score = 0;
  1380.         break;
  1381.     default:
  1382.         TELL_ME_IF_WE_PASS_THIS_WAY();
  1383.     }
  1384.     pPlayer->credits = gInitial_net_credits[gCurrent_net_game->options.starting_money_index];
  1385.     pPlayer->wasted = 0;
  1386.     pPlayer->reposition_time = 0;
  1387. }
  1388.  
  1389. // IDA: void __cdecl InitPlayers()
  1390. void InitPlayers(void) {
  1391.     int i;
  1392.     LOG_TRACE("()");
  1393.  
  1394.     for (i = 0; i < gNumber_of_net_players; i++) {
  1395.         InitialisePlayerScore(&gNet_players[i]);
  1396.     }
  1397.     if (gNet_mode == eNet_mode_host) {
  1398.         gLast_it_change = 0;
  1399.         gLast_lepper = NULL;
  1400.     }
  1401.     gTime_for_punishment = 0;
  1402.     gNot_shown_race_type_headup = 1;
  1403.     gIt_or_fox = -1;
  1404. }
  1405.  
  1406. // IDA: void __usercall BuyPSPowerup(int pIndex@<EAX>)
  1407. void BuyPSPowerup(int pIndex) {
  1408.     char s[256];
  1409.     char s2[256];
  1410.     LOG_TRACE("(%d)", pIndex);
  1411.  
  1412.     if (gNet_mode == eNet_mode_none) {
  1413.         NewTextHeadupSlot(4, 0, 3000, -4, GetMiscString(kMiscString_ONLY_AVAILABLE_IN_NET_GAMES));
  1414.     } else if (gProgram_state.current_car.power_up_levels[pIndex] < 4) {
  1415.         if (gNet_mode == eNet_mode_none || gPowerup_cost[gProgram_state.current_car.power_up_levels[pIndex]] <= (gProgram_state.credits_earned - gProgram_state.credits_lost)) {
  1416.             SpendCredits(gPowerup_cost[gProgram_state.current_car.power_up_levels[pIndex]]);
  1417.             ImprovePSPowerup(&gProgram_state.current_car, pIndex);
  1418.         } else {
  1419.             strcpy(s, GetMiscString(kMiscString_CANNOT_AFFORD_IT));
  1420.             sprintf(s2, "%d", gPowerup_cost[gProgram_state.current_car.power_up_levels[pIndex]]);
  1421.             SubsStringJob(s, s2);
  1422.             NewTextHeadupSlot(4, 0, 3008, -4, s);
  1423.         }
  1424.     } else {
  1425.         NewTextHeadupSlot(4, 0, 3000, -4, GetMiscString(kMiscString_YOU_ARE_ALREADY_AT_MAX));
  1426.     }
  1427. }
  1428.  
  1429. // IDA: void __cdecl BuyArmour()
  1430. void BuyArmour(void) {
  1431.     LOG_TRACE("()");
  1432.  
  1433.     BuyPSPowerup(0);
  1434. }
  1435.  
  1436. // IDA: void __cdecl BuyPower()
  1437. void BuyPower(void) {
  1438.     LOG_TRACE("()");
  1439.  
  1440.     if (gNet_mode != eNet_mode_none && gCurrent_net_game->type == eNet_game_type_foxy && gThis_net_player_index == gIt_or_fox) {
  1441.         NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_THE_FOX_CANNOT_DO_THAT));
  1442.     } else if (gNet_mode != eNet_mode_none && gCurrent_net_game->type == eNet_game_type_tag && gThis_net_player_index != gIt_or_fox) {
  1443.         NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_ONLY_IT_CAN_DO_THAT));
  1444.     } else {
  1445.         BuyPSPowerup(1);
  1446.     }
  1447. }
  1448.  
  1449. // IDA: void __cdecl BuyOffense()
  1450. void BuyOffense(void) {
  1451.     LOG_TRACE("()");
  1452.  
  1453.     BuyPSPowerup(2);
  1454. }
  1455.  
  1456. // IDA: void __usercall UseGeneralScore(int pScore@<EAX>)
  1457. void UseGeneralScore(int pScore) {
  1458.     int i;
  1459.     LOG_TRACE("(%d)", pScore);
  1460.  
  1461.     if (gCurrent_net_game->type == eNet_game_type_carnage) {
  1462.         gPed_target = pScore;
  1463.     } else if ((gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) && gNet_players[gIt_or_fox].ID != pScore) {
  1464.         for (i = 0; i < gNumber_of_net_players; i++) {
  1465.             StopCarBeingIt(gNet_players[i].car);
  1466.         }
  1467.         for (i = 0; i < gNumber_of_net_players; i++) {
  1468.             if (gNet_players[i].ID == pScore) {
  1469.                 MakeCarIt(gNet_players[i].car);
  1470.                 gIt_or_fox = i;
  1471.             }
  1472.         }
  1473.     }
  1474. }
  1475.  
  1476. // IDA: void __usercall NetSendEnvironmentChanges(tNet_game_player_info *pPlayer@<EAX>)
  1477. void NetSendEnvironmentChanges(tNet_game_player_info* pPlayer) {
  1478.     LOG_TRACE("(%p)", pPlayer);
  1479.  
  1480.     SendAllPedestrianPositions(pPlayer->ID);
  1481.     SendAllNonCarPositions();
  1482. }
  1483.  
  1484. // IDA: void __cdecl UpdateEnvironments()
  1485. void UpdateEnvironments(void) {
  1486.     int i;
  1487.     LOG_TRACE("()");
  1488.  
  1489.     for (i = 1; i < gNumber_of_net_players; i++) {
  1490.         if (!gNet_players[i].race_stuff_initialised) {
  1491.             NetSendEnvironmentChanges(&gNet_players[i]);
  1492.             gNet_players[i].race_stuff_initialised = 1;
  1493.         }
  1494.         NetSendMessageStacks();
  1495.         SendGameplay(gNet_players[i].ID, eNet_gameplay_go_for_it, 0, 0, 0, 0);
  1496.     }
  1497. }
  1498.  
  1499. // IDA: void __usercall ReceivedGameplay(tNet_contents *pContents@<EAX>, tNet_message *pMessage@<EDX>, tU32 pReceive_time@<EBX>)
  1500. void ReceivedGameplay(tNet_contents* pContents, tNet_message* pMessage, tU32 pReceive_time) {
  1501.     int must_revert_reentrancy;
  1502.     int gPixel_buffer_size;
  1503.     char* gPixels_copy;
  1504.     char* gPalette_copy;
  1505.     static int pause_semaphore;
  1506.     LOG_TRACE("(%p, %p, %d)", pContents, pMessage, pReceive_time);
  1507.  
  1508.     switch (pContents->data.gameplay.mess) {
  1509.     case eNet_gameplay_host_paused:
  1510.         if (!pause_semaphore) {
  1511.             gPixel_buffer_size = gBack_screen->row_bytes * gBack_screen->height;
  1512.             gPixels_copy = BrMemAllocate(gPixel_buffer_size, kMem_quit_vfy_pixels);
  1513.             gPalette_copy = BrMemAllocate(1024, kMem_quit_vfy_pal);
  1514.             memcpy(gPixels_copy, gBack_screen->pixels, gPixel_buffer_size);
  1515.             memcpy(gPalette_copy, gCurrent_palette_pixels, 1024);
  1516.             pause_semaphore = 1;
  1517.             NetFullScreenMessage(kMiscString_PLEASE_WAIT_HOST_HAS_PAUSED, 1);
  1518.             must_revert_reentrancy = PermitNetServiceReentrancy();
  1519.             do {
  1520.                 NetService(0);
  1521.                 if (CheckQuit()) {
  1522.                     NetFullScreenMessage(kMiscString_PLEASE_WAIT_HOST_HAS_PAUSED, 1);
  1523.                 }
  1524.             } while (gWaiting_for_unpause
  1525.                 && gProgram_state.prog_status != eProg_idling
  1526.                 && (!gRace_finished || gRace_over_reason != eRace_over_abandoned));
  1527.             if (must_revert_reentrancy) {
  1528.                 HaltNetServiceReentrancy();
  1529.             }
  1530.             gWaiting_for_unpause = 1;
  1531.             FadePaletteDown();
  1532.             memcpy(gBack_screen->pixels, gPixels_copy, gPixel_buffer_size);
  1533.             memcpy(gCurrent_palette_pixels, gPalette_copy, 1024);
  1534.             BrMemFree(gPixels_copy);
  1535.             BrMemFree(gPalette_copy);
  1536.             PDScreenBufferSwap(0);
  1537.             FadePaletteUp();
  1538.             pause_semaphore = 0;
  1539.         }
  1540.         break;
  1541.     case eNet_gameplay_earn_credits:
  1542.         EarnCredits(pContents->data.gameplay.param_1);
  1543.         break;
  1544.     case eNet_gameplay_host_unpaused:
  1545.         gWaiting_for_unpause = 0;
  1546.         break;
  1547.     case eNet_gameplay_suicide:
  1548.         KnackerThisCar(NetCarFromPlayerID(pMessage->sender));
  1549.         break;
  1550.     case eNet_gameplay_go_for_it:
  1551.         gWait_for_it = 0;
  1552.         break;
  1553.     default:
  1554.         if (gCurrent_net_game->type == eNet_game_type_checkpoint || gCurrent_net_game->type == eNet_game_type_sudden_death || gCurrent_net_game->type == eNet_game_type_tag) {
  1555.             switch (pContents->data.gameplay.mess) {
  1556.             case eNet_gameplay_checkpoint:
  1557.                 Checkpoint(pContents->data.gameplay.param_1, 1);
  1558.                 break;
  1559.             case eNet_gameplay_wrong_checkpoint:
  1560.                 WrongCheckpoint(pContents->data.gameplay.param_1);
  1561.                 break;
  1562.             case eNet_gameplay_suddenly_death:
  1563.                 DoFancyHeadup(kFancyHeadupNetworkRaceOverNetworkLoss);
  1564.                 ChangeAmbientPratcam(36);
  1565.                 gRace_finished = 1;
  1566.                 break;
  1567.             default:
  1568.                 break;
  1569.             }
  1570.         }
  1571.         break;
  1572.     }
  1573. }
  1574.  
  1575. // IDA: void __usercall SendGameplay(tPlayer_ID pPlayer@<EAX>, tNet_gameplay_mess pMess@<EDX>, int pParam_1@<EBX>, int pParam_2@<ECX>, int pParam_3, int pParam_4)
  1576. void SendGameplay(tPlayer_ID pPlayer, tNet_gameplay_mess pMess, int pParam_1, int pParam_2, int pParam_3, int pParam_4) {
  1577.     tNet_message* the_message;
  1578.     LOG_TRACE("(%d, %d, %d, %d, %d, %d)", pPlayer, pMess, pParam_1, pParam_2, pParam_3, pParam_4);
  1579.  
  1580.     the_message = NetBuildMessage(NETMSGID_GAMEPLAY, 0);
  1581.     the_message->contents.data.gameplay.mess = pMess;
  1582.     the_message->contents.data.gameplay.param_1 = pParam_1;
  1583.     the_message->contents.data.gameplay.param_2 = pParam_2;
  1584.     the_message->contents.data.gameplay.param_3 = pParam_3;
  1585.     the_message->contents.data.gameplay.param_4 = pParam_4;
  1586.     NetGuaranteedSendMessageToPlayer(gCurrent_net_game, the_message, pPlayer, 0);
  1587. }
  1588.  
  1589. // IDA: void __usercall SendGameplayToAllPlayers(tNet_gameplay_mess pMess@<EAX>, int pParam_1@<EDX>, int pParam_2@<EBX>, int pParam_3@<ECX>, int pParam_4)
  1590. void SendGameplayToAllPlayers(tNet_gameplay_mess pMess, int pParam_1, int pParam_2, int pParam_3, int pParam_4) {
  1591.     tNet_message* the_message;
  1592.     LOG_TRACE("(%d, %d, %d, %d, %d)", pMess, pParam_1, pParam_2, pParam_3, pParam_4);
  1593.  
  1594.     the_message = NetBuildMessage(NETMSGID_GAMEPLAY, 0);
  1595.     the_message->contents.data.gameplay.mess = pMess;
  1596.     the_message->contents.data.gameplay.param_1 = pParam_1;
  1597.     the_message->contents.data.gameplay.param_2 = pParam_2;
  1598.     the_message->contents.data.gameplay.param_3 = pParam_3;
  1599.     the_message->contents.data.gameplay.param_4 = pParam_4;
  1600.     NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
  1601. }
  1602.  
  1603. // IDA: void __usercall SendGameplayToHost(tNet_gameplay_mess pMess@<EAX>, int pParam_1@<EDX>, int pParam_2@<EBX>, int pParam_3@<ECX>, int pParam_4)
  1604. void SendGameplayToHost(tNet_gameplay_mess pMess, int pParam_1, int pParam_2, int pParam_3, int pParam_4) {
  1605.     tNet_message* the_message;
  1606.     LOG_TRACE("(%d, %d, %d, %d, %d)", pMess, pParam_1, pParam_2, pParam_3, pParam_4);
  1607.  
  1608.     if (gNet_mode == eNet_mode_client) {
  1609.         the_message = NetBuildMessage(NETMSGID_GAMEPLAY, 0);
  1610.         the_message->contents.data.gameplay.mess = pMess;
  1611.         the_message->contents.data.gameplay.param_1 = pParam_1;
  1612.         the_message->contents.data.gameplay.param_2 = pParam_2;
  1613.         the_message->contents.data.gameplay.param_3 = pParam_3;
  1614.         the_message->contents.data.gameplay.param_4 = pParam_4;
  1615.         NetGuaranteedSendMessageToHost(gCurrent_net_game, the_message, NULL);
  1616.     }
  1617. }
  1618.  
  1619. // IDA: void __cdecl InitNetGameplayStuff()
  1620. void InitNetGameplayStuff(void) {
  1621.     LOG_TRACE("()");
  1622. }
  1623.  
  1624. // IDA: void __cdecl DefaultNetName()
  1625. void DefaultNetName(void) {
  1626.     NetObtainSystemUserName(gNet_player_name, 32);
  1627. }
  1628.  
  1629. // IDA: void __usercall NetSendPointCrush(tCar_spec *pCar@<EAX>, tU16 pCrush_point_index@<EDX>, br_vector3 *pEnergy_vector@<EBX>)
  1630. void NetSendPointCrush(tCar_spec* pCar, tU16 pCrush_point_index, br_vector3* pEnergy_vector) {
  1631.     tNet_contents* contents;
  1632.     LOG_TRACE("(%p, %d, %p)", pCar, pCrush_point_index, pEnergy_vector);
  1633.  
  1634.     contents = NetGetBroadcastContents(NETMSGID_CRUSHPOINT, 0);
  1635.     contents->data.crush.id = NetPlayerFromCar(pCar)->ID;
  1636.     contents->data.crush.vertex = pCrush_point_index;
  1637.     BrVector3Copy(&contents->data.crush.energy_vector, pEnergy_vector);
  1638. }
  1639.  
  1640. // IDA: void __usercall RecievedCrushPoint(tNet_contents *pContents@<EAX>)
  1641. void RecievedCrushPoint(tNet_contents* pContents) {
  1642.     tCar_spec* car;
  1643.     LOG_TRACE("(%p)", pContents);
  1644.  
  1645.     car = NetCarFromPlayerID(pContents->data.crush.id);
  1646.     if (car == NULL || gNet_mode == eNet_mode_host || car->active || gArrow_mode) {
  1647.         return;
  1648.     }
  1649.     if (car->car_model_actors[car->principal_car_actor].crush_data.number_of_crush_points == 0) {
  1650.         return;
  1651.     }
  1652.  
  1653.     CrushModelPoint(
  1654.         car,
  1655.         car->principal_car_actor,
  1656.         car->car_model_actors[car->principal_car_actor].actor->model,
  1657.         pContents->data.crush.vertex,
  1658.         &pContents->data.crush.energy_vector,
  1659.         BrVector3Length(&pContents->data.crush.energy_vector) + 0.06f,
  1660.         &car->car_model_actors[car->principal_car_actor].crush_data);
  1661.     SetModelForUpdate(car->car_model_actors[car->principal_car_actor].actor->model, car, 0);
  1662. }
  1663.  
  1664. // IDA: void __usercall GetReducedMatrix(tReduced_matrix *m1@<EAX>, br_matrix34 *m2@<EDX>)
  1665. void GetReducedMatrix(tReduced_matrix* m1, br_matrix34* m2) {
  1666.     LOG_TRACE("(%p, %p)", m1, m2);
  1667.  
  1668.     m1->row1.v[0] = m2->m[0][0];
  1669.     m1->row1.v[1] = m2->m[0][1];
  1670.     m1->row1.v[2] = m2->m[0][2];
  1671.     m1->row2.v[0] = m2->m[1][0];
  1672.     m1->row2.v[1] = m2->m[1][1];
  1673.     m1->row2.v[2] = m2->m[1][2];
  1674.     m1->translation.v[0] = m2->m[3][0];
  1675.     m1->translation.v[1] = m2->m[3][1];
  1676.     m1->translation.v[2] = m2->m[3][2];
  1677. }
  1678.  
  1679. // IDA: void __usercall GetExpandedMatrix(br_matrix34 *m1@<EAX>, tReduced_matrix *m2@<EDX>)
  1680. void GetExpandedMatrix(br_matrix34* m1, tReduced_matrix* m2) {
  1681.     LOG_TRACE("(%p, %p)", m1, m2);
  1682.  
  1683.     m1->m[0][0] = m2->row1.v[0];
  1684.     m1->m[0][1] = m2->row1.v[1];
  1685.     m1->m[0][2] = m2->row1.v[2];
  1686.  
  1687.     m1->m[1][0] = m2->row2.v[0];
  1688.     m1->m[1][1] = m2->row2.v[1];
  1689.     m1->m[1][2] = m2->row2.v[2];
  1690.  
  1691.     m1->m[3][0] = m2->translation.v[0];
  1692.     m1->m[3][1] = m2->translation.v[1];
  1693.     m1->m[3][2] = m2->translation.v[2];
  1694.  
  1695.     m1->m[2][0] = m2->row2.v[2] * m2->row1.v[1] - m2->row2.v[1] * m2->row1.v[2];
  1696.     m1->m[2][1] = m2->row1.v[2] * m2->row2.v[0] - m2->row2.v[2] * m2->row1.v[0];
  1697.     m1->m[2][2] = m2->row2.v[1] * m2->row1.v[0] - m2->row1.v[1] * m2->row2.v[0];
  1698. }
  1699.  
  1700. // IDA: void __usercall NetEarnCredits(tNet_game_player_info *pPlayer@<EAX>, tS32 pCredits@<EDX>)
  1701. void NetEarnCredits(tNet_game_player_info* pPlayer, tS32 pCredits) {
  1702.     LOG_TRACE("(%p, %d)", pPlayer, pCredits);
  1703.  
  1704.     // empty function
  1705. }
  1706.