Downloads containing TimeTrial.mut

Downloads
Name Author Game Mode Rating
JJ2+ Only: Time TrialFeatured Download PurpleJazz Mutator 10 Download file

File preview

  1. #pragma name "Time Trial"
  2.  
  3. // Scripted by PurpleJazz with help from Sir Ementaler
  4.  
  5. /*******************************************************************/
  6. //*MUTATOR DECLARATIONS*//
  7.  
  8. class checkpoint {
  9.         int xTile, yTile;
  10.         float xPos, yPos;
  11. }
  12.  
  13. array<checkpoint> Checkpoints;
  14.  
  15. array<bool> StartedRace(32, false);
  16. array<bool> TouchedCheckpoint(32, false);
  17.  
  18. array<uint64> Time(32, 0);
  19. array<uint64> BestTime(32, 252000);
  20.  
  21. string formatTime(uint64 time) {
  22.         return (time / (70 * 60)) + ':' + formatInt(time / 70 % 60, '0', 2) + ':' + formatInt(time * 100 / 7 % 1000, '0', 3);
  23. }
  24.  
  25. uint8 goalColor = 240;
  26. uint8 cpColor = 248;
  27.  
  28. int goalTextOffset = 24;
  29. int cpTextOffset = 52;
  30.  
  31. string goalText = "START";
  32. string cpText = "CHECKPOINT";
  33.  
  34. const float PI = 3.1415927f;
  35. int CTFArrowTimer = 0;
  36.  
  37. bool playSample = false;
  38. bool hasBases = false;
  39.  
  40. /*******************************************************************/
  41. //*RECORD SAVING DECLARATIONS*/- Based on SE's Test Manager code/
  42.  
  43. string lowercase(string &in text) {
  44.         for (int i = text.length() - 1; i >= 0; i--) {
  45.                 if (text[i] > 64 && text[i] < 91)
  46.                         text[i] ^= 32;
  47.         }
  48.         return text;
  49. }
  50.  
  51. class record {
  52.         string name;
  53.         int time;
  54.         bool opEquals(const record &in other) const {
  55.                 return lowercase(this.name) == lowercase(other.name);
  56.         }
  57.         int opCmp(const record &in other) const {
  58.                 return this.time - other.time;
  59.         }
  60. }
  61.  
  62. array<record> records;
  63. string filename;
  64.  
  65. bool load(const string &in name) {
  66.         jjSTREAM file(name);
  67.         uint16 count;
  68.         if (!file.pop(count))
  69.                 return false;
  70.         records.resize(count);
  71.         for (uint i = 0; i < count; i++) {
  72.                 record@ rec = records[i];
  73.                 if (!file.pop(rec.name) || !file.pop(rec.time)) {
  74.                         records.resize(0);
  75.                         return false;
  76.                 }
  77.         }
  78.         return true;
  79. }
  80.  
  81. void save(const string &in name) {
  82.         jjSTREAM file;
  83.         file.push(uint16(records.length()));
  84.         for (uint i = 0; i < records.length(); i++) {
  85.                 const record@ rec = records[i];
  86.                 file.push(rec.name);
  87.                 file.push(rec.time);
  88.         }
  89.         file.save(name);
  90. }
  91.  
  92. /*******************************************************************/
  93. //*MAIN SCRIPT FUNCTIONS*//
  94.  
  95. void onLevelLoad() {
  96.         jjObjectPresets[OBJECT::CTFBASE].behavior = BEHAVIOR::INACTIVE;
  97.         jjObjectPresets[OBJECT::RFBULLET].behavior = jjObjectPresets[OBJECT::RFBULLETPU].behavior = rf;
  98.        
  99.         for (int i = 1; i <= 9; i++) jjWeapons[i].infinite = true;
  100.        
  101.         jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::FLAG] + 3];
  102.         for (uint i = 0; i < anim.frameCount; i++) {
  103.                 jjANIMFRAME@ frame = jjAnimFrames[anim + i];
  104.                 jjPIXELMAP flag(frame);
  105.                 for (uint x = 0; x < flag.width; x++) {
  106.                         for (uint y = 0; y < flag.height; y++) {
  107.                                 if (flag[x, y] == 22)
  108.                                         flag[x, y] = 86;
  109.                                 else if (flag[x, y] == 23)
  110.                                         flag[x,y] = 87;
  111.                         }
  112.                 }
  113.                 flag.save(frame);
  114.         }
  115.        
  116.         for (int x = 0; x < jjLayerWidth[4]; x++) {
  117.                 for (int y = 0; y < jjLayerHeight[4]; y++) {
  118.                         if (jjEventGet(x, y) == OBJECT::CTFBASE) {
  119.                                 hasBases = true;
  120.                                 checkpoint newCheckpoint;
  121.                                 newCheckpoint.xTile = x;
  122.                                 newCheckpoint.yTile = y;
  123.                                 newCheckpoint.xPos = x * 32 + 16;
  124.                                 newCheckpoint.yPos = y * 32 + 16;
  125.                                 Checkpoints.insertLast(newCheckpoint);
  126.                         }
  127.                         if (jjEventGet(x,y) == OBJECT::RFAMMO3 || jjEventGet(x,y) == OBJECT::RFAMMO15) {
  128.                                 jjWeapons[WEAPON::RF].allowed = true;
  129.                         }
  130.                         if (jjEventGet(x,y) == OBJECT::RFPOWERUP) {
  131.                                 jjWeapons[WEAPON::RF].allowedPowerup = true;
  132.                         }
  133.                 }
  134.         }
  135.        
  136.         if (jjIsServer) { //thanks to SE for this code
  137.                 uint32 sum1 = 0, sum2 = 0;
  138.                 for (int i = 0; i < jjLayerHeight[4]; i++) {
  139.                         for (int j = 0; j < jjLayerWidth[4]; j++) {
  140.                                 sum1 += jjTileGet(4, j, i);
  141.                                 sum2 += sum1;
  142.                         }
  143.                         sum1 %= 0xFFFF;
  144.                         sum2 %= 0xFFFF;
  145.                 }
  146.                 load(filename = "TimeTrial-" + formatInt(sum2 << 16 | sum1, '0H', 8) + ".asdat");
  147.                
  148.                 record rec;
  149.                 rec.name = jjLocalPlayers[0].name;
  150.                 int id = records.find(rec);
  151.                 if (id >= 0) BestTime[jjLocalPlayers[0].playerID] = records[id].time;
  152.         }
  153.         else jjSendPacket(jjSTREAM());
  154. }
  155.  
  156. [RFBULLET] void rf(jjOBJ@ obj) { //this is so your RF explosions won't affect other players
  157.         if (obj.creatorType == CREATOR::PLAYER && !jjPlayers[obj.creatorID].isLocal) {
  158.                 obj.objType = HANDLING::EXPLOSION;
  159.                 if (obj.state == STATE::EXPLODE) jjDrawSprite(obj.xPos, obj.yPos, ANIM::AMMO, 3, obj.curFrame, obj.direction, SPRITE::TRANSLUCENT);
  160.         }
  161.         obj.behave(BEHAVIOR::RFBULLET, obj.state == STATE::EXPLODE && !jjPlayers[obj.creatorID].isLocal? false:true);
  162. }
  163.  
  164. void onLevelBegin() {  
  165.         if (jjIsServer) {
  166.                 if (jjGameMode != GAME::CTF || jjGameCustom != GAME::TB) jjChat("/tb");
  167.                
  168.                 if (!jjEnabledTeams[TEAM::BLUE]) jjChat("/teams blue on");
  169.                
  170.                 array<TEAM::Color> TeamsToDisable = {TEAM::RED, TEAM::YELLOW, TEAM::GREEN};
  171.                 array<string> TeamColors = {"red", "yellow", "green"};
  172.                
  173.                 for (int i = 0; i < 3; i++) {
  174.                         if (jjEnabledTeams[TeamsToDisable[i]]) jjChat("/teams " + TeamColors[i] + " off");
  175.                 }
  176.         }
  177.        
  178.         if (jjIsServer || jjIsAdmin) {
  179.                 if (jjGameState == GAME::STOPPED) jjAlert("|The game must be started in order to play this mode!");
  180.         }
  181.        
  182.         jjAlert("||Type !help for a list of commands.");
  183. }
  184.  
  185. bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ canvas) {
  186.         canvas.drawString(
  187.         jjSubscreenWidth-796,
  188.         jjSubscreenHeight-520,
  189.         "||||" + formatTime(Time[play.playerID]),
  190.         STRING::MEDIUM,
  191.         STRING::NORMAL
  192.         );
  193.        
  194.         if (BestTime[play.playerID] < 252000)
  195.         canvas.drawString(
  196.         jjSubscreenWidth-796,
  197.         jjSubscreenHeight-496,
  198.         "|PB: " + formatTime(BestTime[play.playerID]),
  199.         STRING::SMALL,
  200.         STRING::NORMAL
  201.         );
  202.         return false;
  203. }
  204.  
  205. void onMain() {
  206.         for (uint i = 0; i < Checkpoints.length(); i++) {
  207.                 checkpoint@ cp = Checkpoints[i];
  208.                 jjDrawSprite(cp.xPos + (i == 1? 16:-16), cp.yPos + 16, ANIM::FLAG, 3, jjGameTicks / 6 % 8, i == 1? -1:1, SPRITE::PALSHIFT, i == 1? cpColor : goalColor, 4, 4);
  209.         }
  210.        
  211.         if (hasBases) {
  212.                 jjDrawString(Checkpoints[0].xPos - goalTextOffset, Checkpoints[0].yPos - 42, goalText, STRING::SMALL, STRING::NORMAL);
  213.                 jjDrawString(Checkpoints[1].xPos - cpTextOffset, Checkpoints[1].yPos - 42, cpText, STRING::SMALL, STRING::NORMAL);
  214.         }
  215.        
  216.         if (CTFArrowTimer > jjGameTicks + 280) CTFArrowTimer = jjGameTicks;
  217.                 if (CTFArrowTimer < jjGameTicks && hasBases) {
  218.                         if (CTFArrowTimer + 64 >= jjGameTicks) {
  219.                                 int angle_A = int(atan2(Checkpoints[0].yPos - jjLocalPlayers[0].yPos, Checkpoints[0].xPos - jjLocalPlayers[0].xPos) * (512 / PI));
  220.                                 int angle_B = int(atan2(Checkpoints[1].yPos - jjLocalPlayers[0].yPos, Checkpoints[1].xPos - jjLocalPlayers[0].xPos) * (512 / PI));
  221.                                 const float scale = 64.f / (112.f - jjSin((jjGameTicks - CTFArrowTimer) << 3) * 64.f);
  222.                                 if (!StartedRace[jjLocalPlayers[0].playerID] || TouchedCheckpoint[jjLocalPlayers[0].playerID]) jjDrawRotatedSprite(jjLocalPlayers[0].xPos + 32 * jjCos(angle_A), jjLocalPlayers[0].yPos + 32 * jjSin(angle_A), ANIM::FLAG, 0, 0, 970 - angle_A, scale, scale, SPRITE::PALSHIFT, goalColor, 1);
  223.                                 else if (StartedRace[jjLocalPlayers[0].playerID] || !TouchedCheckpoint[jjLocalPlayers[0].playerID]) jjDrawRotatedSprite(jjLocalPlayers[0].xPos + 32 * jjCos(angle_B), jjLocalPlayers[0].yPos + 32 * jjSin(angle_B), ANIM::FLAG, 0, 0, 970 - angle_B, scale, scale, SPRITE::PALSHIFT, cpColor, 1);
  224.                         } else {
  225.                         CTFArrowTimer = jjGameTicks + 210;
  226.                 }
  227.         }
  228.        
  229.         if (jjIsServer) {
  230.                 for (int i = 0; i < 32; i++) {
  231.                         jjPLAYER@ play = jjPlayers[i];
  232.                         if (play.isActive) {
  233.                                 for (uint j = 0; j < Checkpoints.length(); j++) {
  234.                                         checkpoint@ cp = Checkpoints[j];
  235.                                         if (int(play.xPos) / 32 == cp.xTile && int(play.yPos) / 32 == cp.yTile && jjGameState == GAME::STARTED) {
  236.                                                 if (j == 0) {
  237.                                                         if (!TouchedCheckpoint[play.playerID]) {
  238.                                                                 Time[play.playerID] = 0;
  239.                                                                 StartedRace[play.playerID] = true;
  240.                                                                 stream(i);
  241.                                                         }
  242.                                                         else {
  243.                                                                 if (Time[play.playerID] < BestTime[play.playerID] && TouchedCheckpoint[play.playerID]) {
  244.                                                                         BestTime[play.playerID] = Time[play.playerID];
  245.                                                                        
  246.                                                                         record rec;
  247.                                                                         rec.name = play.name;
  248.                                                                         rec.time = BestTime[play.playerID];
  249.                                                                         int id = records.find(rec);
  250.                                                                         if (id < 0) records.insertLast(rec);
  251.                                                                         else records[id] = rec;
  252.                                                                         records.sortAsc();
  253.                                                                         save(filename);
  254.                                                                        
  255.                                                                         jjAlert("|||||||" + play.name + " ||||set a new personal best of||||| " + formatTime(BestTime[play.playerID]), true);
  256.                                                                        
  257.                                                                         if (play.isLocal) {
  258.                                                                                 switch (play.charCurr) {
  259.                                                                                         case CHAR::JAZZ: jjSamplePriority(SOUND::JAZZSOUNDS_JUMMY); break;
  260.                                                                                         case CHAR::SPAZ: jjSamplePriority(SOUND::SPAZSOUNDS_HAPPY); break;
  261.                                                                                         case CHAR::LORI: jjSamplePriority(SOUND::LORISOUNDS_WEHOO); break;
  262.                                                                                 }
  263.                                                                         }
  264.                                                                 }
  265.                                                                
  266.                                                                 string newrecord = Time[play.playerID] <= BestTime[play.playerID]? "New record! " : "";
  267.                                                                 if (play.isLocal) {
  268.                                                                         jjSamplePriority(SOUND::COMMON_BELL_FIRE);
  269.                                                                         jjAlert("|||" + newrecord + "You finished in |||||" + formatTime(Time[play.playerID]), false, STRING::MEDIUM);
  270.                                                                 }
  271.                                                                
  272.                                                                 StartedRace[play.playerID] = false;
  273.                                                                 TouchedCheckpoint[play.playerID] = false;
  274.                                                                 stream(i);
  275.                                                         }
  276.                                                 }
  277.                                                 if (j == 1 && StartedRace[play.playerID] && !TouchedCheckpoint[play.playerID]) {
  278.                                                         TouchedCheckpoint[play.playerID] = true;
  279.                                                         stream(i);
  280.                                                 }
  281.                                         }
  282.                                         if (StartedRace[play.playerID] && j != 0 && jjGameState == GAME::STARTED) Time[play.playerID]++;
  283.                                        
  284.                                         else if (jjGameState == GAME::STOPPED) {
  285.                                                 Time[play.playerID] = 0;
  286.                                                 StartedRace[play.playerID] = false;
  287.                                                 TouchedCheckpoint[play.playerID] = false;
  288.                                         }
  289.                                        
  290.                                         if (play.invincibility < 0) {
  291.                                                 StartedRace[play.playerID] = false;
  292.                                                 TouchedCheckpoint[play.playerID] = false;
  293.                                                 Time[play.playerID] = 0;
  294.                                                 stream(i);
  295.                                         }
  296.                                 }
  297.                         }
  298.                 }
  299.                 if (TouchedCheckpoint[jjLocalPlayers[0].playerID]) {
  300.                         goalColor = 8;
  301.                         cpColor = 0;
  302.                         goalTextOffset = 16;
  303.                         cpTextOffset = 32;
  304.                         goalText = "GOAL";
  305.                         cpText = "RETURN";             
  306.                 }
  307.                 else {
  308.                         goalColor = 240;
  309.                         cpColor = 248;
  310.                         goalText = "START";
  311.                         cpText = "CHECKPOINT";
  312.                         cpTextOffset = 52;
  313.                         goalTextOffset = 24;
  314.                 }
  315.         }
  316.         else {
  317.                 if (StartedRace[jjLocalPlayers[0].playerID] && jjGameState == GAME::STARTED) Time[jjLocalPlayers[0].playerID]++;
  318.                
  319.                 else if (jjGameState == GAME::STOPPED) {
  320.                         Time[jjLocalPlayers[0].playerID] = 0;
  321.                         StartedRace[jjLocalPlayers[0].playerID] = false;
  322.                         TouchedCheckpoint[jjLocalPlayers[0].playerID] = false;
  323.                 }
  324.                
  325.                 if (jjLocalPlayers[0].invincibility < 0) {
  326.                         Time[jjLocalPlayers[0].playerID] = 0;
  327.                         StartedRace[jjLocalPlayers[0].playerID] = false;
  328.                         TouchedCheckpoint[jjLocalPlayers[0].playerID] = false;
  329.                 }
  330.                
  331.                 if (playSample) {
  332.                         jjSamplePriority(SOUND::COMMON_BELL_FIRE);
  333.                         if (Time[jjLocalPlayers[0].playerID] < BestTime[jjLocalPlayers[0].playerID]) {
  334.                                 switch (jjLocalPlayers[0].charCurr) {
  335.                                         case CHAR::JAZZ: jjSamplePriority(SOUND::JAZZSOUNDS_JUMMY); break;
  336.                                         case CHAR::SPAZ: jjSamplePriority(SOUND::SPAZSOUNDS_HAPPY); break;
  337.                                         case CHAR::LORI: jjSamplePriority(SOUND::LORISOUNDS_WEHOO); break;
  338.                                 }
  339.                         }
  340.                         playSample = false;
  341.                 }
  342.                
  343.                 if (TouchedCheckpoint[jjLocalPlayers[0].playerID]) {
  344.                         goalColor = 8;
  345.                         cpColor = 0;
  346.                         goalTextOffset = 16;
  347.                         cpTextOffset = 32;
  348.                         goalText = "GOAL";
  349.                         cpText = "RETURN";             
  350.                 }
  351.                 else {
  352.                         goalColor = 240;
  353.                         cpColor = 248;
  354.                         goalText = "START";
  355.                         cpText = "CHECKPOINT";
  356.                         cpTextOffset = 52;
  357.                         goalTextOffset = 24;
  358.                 }
  359.         }
  360. }
  361.  
  362. /*******************************************************************/
  363. //*CHAT COMMANDS*//
  364.  
  365. void onChat(int clientID, string &in text, CHAT::Type) {
  366.         array<string> results;
  367.         if (jjRegexMatch(text, """!(.*\S)\s*""", results)) {
  368.                 text = results[1];
  369.                 if (jjIsServer) {
  370.                         if (jjRegexMatch(text, """pb(\s+(.+))?""", results, true) || jjRegexMatch(text, """rank(\s+(.+))?""", results, true)) {
  371.                                 string name = results[2];
  372.                                 record rec;
  373.                                 rec.name = name;
  374.                                 int id = records.find(rec);
  375.                                
  376.                                 if (name.isEmpty()) {
  377.                                         jjAlert("|Please provide a valid player name.", true);
  378.                                 }
  379.                                 else {
  380.                                         if (id >= 0) jjAlert("|||||||" + records[id].name + " ||||has a personal best of |||||" + formatTime(records[id].time) + " ||- ranked " + (id + 1) + " of " + records.length(), true);
  381.                                         else jjAlert("|That player does not have a best time for this level.", true);
  382.                                 }
  383.                         }
  384.                         else if (jjRegexMatch(text, """top""", true)) {
  385.                                 if (records.length() >= 5) {
  386.                                         for (uint i = 0; i < 5; i++) {
  387.                                                 const record@ rec = records[i];
  388.                                                 jjAlert("||" + (i + 1) + ". |||||" + rec.name + "| - " + formatTime(rec.time), true);
  389.                                         }
  390.                                 }
  391.                                 else if (records.length() < 5 && !records.isEmpty()) {
  392.                                         for (uint i = 0; i < records.length(); i++) {
  393.                                                 const record@ rec = records[i];
  394.                                                 jjAlert("||" + (i + 1) + ". |||||" + rec.name + "| - " + formatTime(rec.time), true);
  395.                                         }
  396.                                 }
  397.                                 else if (records.isEmpty()) {
  398.                                         jjAlert("|No one has set any records for this level yet!", true);
  399.                                 }
  400.                         }
  401.                         else if (jjRegexMatch(text, """whois\s*([0-9]\d*)""", results, true)) {
  402.                                 uint id = parseInt(results[1]) - 1;
  403.                                 if (id < records.length()) {
  404.                                         const record@ rec = records[id];
  405.                                         jjAlert("|||||||" + rec.name + " ||||is ranked||||||| " + (id + 1) + " of " + records.length() +  " |with a best time of |||||" + formatTime(rec.time), true);
  406.                                 }
  407.                                 else jjAlert("|No player has been found with that ranking.", true);
  408.                         }
  409.                         else if (jjRegexMatch(text, """avg""", true) || jjRegexMatch(text, """average""", true)) {
  410.                             uint sum = 0;
  411.                                 for (uint i = 0; i < records.length(); i++) {
  412.                                         sum += records[i].time;
  413.                                 }
  414.                                 if (!records.isEmpty()) jjAlert("|||The average time recorded in this level is |||||" + formatTime(sum / records.length()), true);
  415.                                 else jjAlert("|No average can be calculated because no times have been recorded!", true);
  416.                         }
  417.                         else if (jjRegexMatch(text, """delete(\s+(.+))?""", results, true) && (clientID == jjPlayers[0].clientID || jjPlayers[clientID].isAdmin)) {
  418.                                 string name = results[2];
  419.                                 record rec;
  420.                                 rec.name = name;
  421.                                 int id = records.find(rec);
  422.                                
  423.                                 if (name.isEmpty()) {
  424.                                         jjAlert("|Please provide a valid player name.", true);
  425.                                 }
  426.                                 else if (id >= 0) {
  427.                                         jjAlert("|||" + records[id].name + "'s record of |||||" + formatTime(records[id].time) + " |||has been ||||||DELETED", true);
  428.                                         records.removeAt(id);
  429.                                         records.sortAsc();
  430.                                         save(filename);
  431.                                        
  432.                                         for (int i = 0; i < 32; i++) {
  433.                                                 if (jjPlayers[i].name == name) {
  434.                                                         BestTime[i] = 252000;
  435.                                                         stream(i);
  436.                                                 }
  437.                                         }
  438.                                 }
  439.                                 else jjAlert("|That player does not have a best time.", true);
  440.                         }
  441.                 }
  442.         }
  443. }
  444.  
  445. bool onLocalChat(string &in text, CHAT::Type type) {
  446.         if (type == CHAT::NORMAL && text == "!help") {
  447.                 jjAlert("|||!getrf ||- gives you RF ammo if obtainable; PU if a monitor is present");
  448.                 jjAlert("|||!top ||- shows the 5 fastest times and who recorded them");
  449.                 jjAlert("|||!rank <player name> ||- shows the best time and the rank of specific player");
  450.                 jjAlert("|||!whois <rank> ||- shows the best time of a player with that rank");
  451.                 jjAlert("|||!avg ||- calculates the average time taken by all player runs");
  452.                 if (jjIsServer || jjIsAdmin) {
  453.                         jjAlert("|||!delete <player name> ||- erases the record of a specified player");
  454.                 }
  455.                
  456.                 return true;
  457.         }
  458.        
  459.         if (type == CHAT::NORMAL && text == "!getrf" || text == "!getRF") {
  460.                 if (jjWeapons[WEAPON::RF].allowed) {
  461.                         jjLocalPlayers[0].ammo[WEAPON::RF] = 1;
  462.                         jjLocalPlayers[0].currWeapon = WEAPON::RF;
  463.                         if (!jjWeapons[WEAPON::RF].allowedPowerup) jjAlert(">> |||Received RF");
  464.                 }
  465.                 if (jjWeapons[WEAPON::RF].allowedPowerup) {
  466.                         jjLocalPlayers[0].powerup[WEAPON::RF] = true;
  467.                         jjLocalPlayers[0].ammo[WEAPON::RF] = 1;
  468.                         jjLocalPlayers[0].currWeapon = WEAPON::RF;
  469.                         jjAlert(">> |||Received RF");
  470.                 }
  471.                
  472.                 else if (!jjWeapons[WEAPON::RF].allowed) jjAlert("|>> No RF ammo could be found in this level");
  473.                 return true;
  474.         }
  475.        
  476.         return false;
  477. }
  478.  
  479. /*******************************************************************/
  480. //*SERVER-CLIENT NETWORKING*//
  481.  
  482. void stream(int id) {
  483.         if (jjPlayers[id].clientID != 0) {
  484.                 jjSTREAM packet;
  485.                 packet.push(!StartedRace[id] && Time[id] > 0? true:false);
  486.                 packet.push(Time[id]);
  487.                 packet.push(BestTime[id]);
  488.                 packet.push(StartedRace[id]);
  489.                 packet.push(TouchedCheckpoint[id]);
  490.                 jjSendPacket(packet, jjPlayers[id].clientID);
  491.         }
  492. }
  493.  
  494. void onReceive(jjSTREAM &in packet, int clientID) {
  495.         if (!jjIsServer) {
  496.                 bool x;
  497.                 uint64 time;
  498.                 uint64 best_time;
  499.                 bool started_race;
  500.                 bool touched_checkpoint;
  501.                
  502.                 packet.pop(x);
  503.                 packet.pop(time);
  504.                 packet.pop(best_time);
  505.                 packet.pop(started_race);
  506.                 packet.pop(touched_checkpoint);
  507.                        
  508.                 Time[jjLocalPlayers[0].playerID] = time;
  509.                 BestTime[jjLocalPlayers[0].playerID] = best_time;
  510.                 StartedRace[jjLocalPlayers[0].playerID] = started_race;
  511.                 TouchedCheckpoint[jjLocalPlayers[0].playerID] = touched_checkpoint;
  512.                
  513.                 string newrecord = Time[jjLocalPlayers[0].playerID] <= BestTime[jjLocalPlayers[0].playerID]? "New record! " : "";
  514.                 if (x) {
  515.                         jjAlert("|||" + newrecord + "You finished in |||||" + formatTime(Time[jjLocalPlayers[0].playerID]), false, STRING::MEDIUM);
  516.                         playSample = true;
  517.                 }
  518.         }
  519.         else {
  520.                 record rec;
  521.                 rec.name = jjPlayers[clientID].name;
  522.                 int id = records.find(rec);
  523.                 if (id >= 0) BestTime[clientID] = records[id].time;
  524.                 stream(clientID);
  525.         }
  526. }