Downloads containing Multiflag.mut

Downloads
Name Author Game Mode Rating
JJ2+ Only: Multiflag Violet CLM Mutator N/A Download file

File preview

  1. #pragma name "Multiflag"
  2. #pragma description "Variant on Capture the Flag. Flags have no inherent team: bring one from the enemy base to your base to score a point, and it will stay at your base until recaptured. Roast an enemy flagholder to steal their flag, unless you're holding a flag already. Use the !flagsperteam command to add even more flags to the game."
  3. #include "ArtificialArrow.asc"
  4.  
  5. enum SpecialPlayerID { Free = -1, BlueBase = -2, RedBase = -3 }
  6. class Flag {
  7.         float xPos, yPos;
  8.         int playerID;
  9.         int direction;
  10.         int timeout;
  11.         Flag(){}
  12. }
  13. array<Flag> Flags(32);
  14. uint FlagCount = 2;
  15. const int FreeFlagTimeout = 30 * 70;
  16.  
  17. const bool LimitAllFlagPoints = true;
  18.  
  19. class Point {  
  20.         int xPos, yPos;
  21.         Point(){}
  22.         Point(int x, int y) { xPos = x * 32 + 15; yPos = y * 32 + 15; }
  23. }
  24. array<Point> BaseLocations(2);
  25. array<int> BaseDirections(2);
  26. array<jjOBJ@> BaseObjects(2, null);
  27.  
  28. array<int> NextCaptureTime(32, 5*70);
  29.  
  30. jjANIMSET@ CustomSet;
  31. void onLevelLoad() {
  32.         {
  33.                 //3, 4: blue
  34.                 //7, 8: red
  35.                 //ingame first, HUD second
  36.                 uint8 customSetID = 0;
  37.                 jjANIMSET@ flagSet = jjAnimSets[ANIM::FLAG];
  38.                 while (jjAnimSets[ANIM::CUSTOM[customSetID]] != 0) { ++customSetID; }
  39.                 @CustomSet = jjAnimSets[ANIM::CUSTOM[customSetID]];
  40.                 CustomSet.allocate(array<uint>={8,0,0,0,0});
  41.                 jjPIXELMAP emptySprite(1,1);
  42.                 for (uint i = 0; i < 8; ++i)
  43.                         emptySprite.save(jjAnimFrames[jjAnimations[CustomSet]+i]);
  44.                 jjAnimations[CustomSet+1] = jjAnimations[flagSet+3];
  45.                 jjAnimations[CustomSet+2] = jjAnimations[flagSet+7];
  46.                 jjAnimations[CustomSet+3] = jjAnimations[flagSet+4];
  47.                 jjAnimations[CustomSet+4] = jjAnimations[flagSet+8];
  48.                 jjAnimations[flagSet+3] = jjAnimations[flagSet+4] = jjAnimations[flagSet+7] = jjAnimations[flagSet+8] = jjAnimations[CustomSet];
  49.         }
  50.        
  51.         uint levelAlreadyHasCTFBases = 0;
  52.         for (int x = jjLayerWidth[4]; --x >= 0;)
  53.                 for (int y = jjLayerHeight[4]; --y >= 0;) {
  54.                         const auto eventID = jjEventGet(x,y);
  55.                         if (eventID == OBJECT::CTFBASE) {
  56.                                 const uint teamID = jjParameterGet(x,y,0,1);
  57.                                 BaseLocations[teamID] = Point(x,y);
  58.                                 BaseDirections[teamID] = (jjParameterGet(x,y,1,1) * 2) - 1;
  59.                                 levelAlreadyHasCTFBases |= (teamID + 1);
  60.                         }
  61.                 }
  62.                
  63.         if (levelAlreadyHasCTFBases != 3) { //0b11
  64.                 jjConsole("|WARNING: level does not have both CTF bases.");
  65.         }
  66.        
  67.         if (jjIsServer) {
  68.                 jjSTREAM load("multiflag.asdat");
  69.                 if (!load.isEmpty()) {
  70.                         load.pop(FlagCount);
  71.                 }
  72.         }
  73.                
  74.         for (uint i = 0; i < Flags.length; ++i) { //start
  75.                 const int teamID = int(i & 1);
  76.                 Flags[i].playerID = SpecialPlayerID::BlueBase - int(teamID);
  77.         }
  78. }
  79.  
  80. void onLevelBegin() {
  81.         if (jjIsServer) {
  82.                 if (jjGameCustom != GAME::FR)
  83.                         jjChat("/fr");
  84.         } else
  85.                 jjSendPacket(jjSTREAM());
  86.        
  87.         for (uint i = jjObjectCount; --i != 0;) {
  88.                 jjOBJ@ obj = jjObjects[i];
  89.                 if (obj.behavior == BEHAVIOR::FLAG)
  90.                         @BaseObjects[jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 1)] = @obj;
  91.         }
  92. }
  93.  
  94. void onPlayer(jjPLAYER@ player) {
  95.         array<int> flagLocations;
  96.         uint8 spriteParam = player.teamRed ? 32 : 24;
  97.         if (DoesPlayerHaveFlag(player) !is null) {
  98.                 flagLocations = array<int> = { BaseLocations[player.team].xPos, BaseLocations[player.team].yPos };
  99.                 spriteParam ^= (32+24);
  100.         } else {
  101.                 for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  102.                         const Flag@ flag = Flags[flagID];
  103.                         if (flag.playerID == SpecialPlayerID::Free || flag.playerID == (player.teamRed ? SpecialPlayerID::BlueBase : SpecialPlayerID::RedBase) || (flag.playerID >= 0 && jjPlayers[flag.playerID].teamRed != player.teamRed)) {
  104.                                 flagLocations.insertLast(int(flag.xPos));
  105.                                 flagLocations.insertLast(int(flag.yPos));
  106.                         }
  107.                 }
  108.         }
  109.         ArtificialArrow::DrawArrow(flagLocations, player, SPRITE::SINGLEHUE, spriteParam);
  110. }
  111.  
  112. void AssignFlagToBase(Flag@ flag, int teamID, bool score = true) {
  113.         flag.playerID = SpecialPlayerID::BlueBase - teamID;
  114.         const Point@ homeBase = BaseLocations[teamID];
  115.         if (score) {
  116.                 if (teamID == 1)
  117.                         jjChat("/redscore " + (jjTeamScore[TEAM::RED] + 1));
  118.                 else
  119.                         jjChat("/bluescore " + (jjTeamScore[TEAM::BLUE] + 1));
  120.         }
  121. }
  122.  
  123. void onRoast(jjPLAYER@ victim, jjPLAYER@ killer) {
  124.         if (jjIsServer) {
  125.                 NextCaptureTime[victim.playerID] = jjGameTicks + 7*70;
  126.                 Flag@ flag = DoesPlayerHaveFlag(victim);
  127.                 if (flag !is null) {
  128.                         if (victim !is killer) { //roast
  129.                                 if (DoesPlayerHaveFlag(killer) is null) {
  130.                                         flag.playerID = killer.playerID;
  131.                                         if (!killer.teamRed)
  132.                                                 jjAlert("||" + killer.nameUnformatted + " ||||stole a flag from |||" + victim.nameUnformatted, true, STRING::MEDIUM);
  133.                                         else
  134.                                                 jjAlert("|" + killer.nameUnformatted + " |||||stole a flag from ||||" + victim.nameUnformatted, true, STRING::MEDIUM);
  135.                                 } else {
  136.                                         flag.playerID = SpecialPlayerID::Free;
  137.                                         flag.timeout = jjGameTicks + FreeFlagTimeout;
  138.                                 }
  139.                         } else { //pit death
  140.                                 AssignFlagToBase(flag, victim.team ^ 1, false);
  141.                         }
  142.                         SendFlagData();
  143.                 }
  144.         }
  145. }
  146.  
  147. void onReceive(jjSTREAM &in packet, int clientID) {
  148.         if (packet.isEmpty()) { //ping
  149.                 if (jjIsServer)
  150.                         SendFlagData(clientID);
  151.                 else
  152.                         AutoScoreCountdown = 5*70;
  153.         } else { //flag data
  154.                 uint8 fc;
  155.                 packet.pop(fc);
  156.                 FlagCount = fc;
  157.                 for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  158.                         Flag@ flag = Flags[flagID];
  159.                         int8 pid;
  160.                         packet.pop(pid);
  161.                         flag.playerID = pid;
  162.                         if (pid == SpecialPlayerID::Free) {
  163.                                 int16 pos;
  164.                                 packet.pop(pos);
  165.                                 flag.xPos = pos;
  166.                                 packet.pop(pos);
  167.                                 flag.yPos = pos;
  168.                                 flag.timeout = jjGameTicks + FreeFlagTimeout; //approximate
  169.                         }
  170.                 }
  171.         }
  172. }
  173.  
  174. array<uint> FlagsPerTeam(2);
  175. const auto SmallAsterisk = jjAnimations[jjAnimSets[ANIM::FONT].firstAnim + 1].firstFrame + 10;
  176. uint AutoScoreCountdown = 0;
  177. bool someTeamHasAllFlags = false;
  178. array<int> LastTeamScore(2, 0);
  179. void onMain() {
  180.         someTeamHasAllFlags = false;
  181.         FlagsPerTeam[0] = FlagsPerTeam[1] = 0;
  182.         const bool gameActive = (jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME);
  183.        
  184.         array<int> baseFlagAngles(2, 0);
  185.         for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  186.                 Flag@ flag = Flags[flagID];
  187.                 if (flag.playerID >= 0) {
  188.                         const jjPLAYER@ holder = jjPlayers[flag.playerID];
  189.                         flag.xPos = holder.xPos;
  190.                         flag.yPos = holder.yPos;
  191.                         flag.direction = -holder.direction;
  192.                 } else if (flag.playerID != SpecialPlayerID::Free) {
  193.                         uint teamID = flag.playerID == SpecialPlayerID::BlueBase ? 0 : 1;
  194.                         flag.xPos = BaseLocations[teamID].xPos;
  195.                         flag.yPos = BaseLocations[teamID].yPos;
  196.                         flag.direction = BaseDirections[teamID];
  197.                 }
  198.                 if (jjIsServer && gameActive) {
  199.                         if (flag.playerID >= 0) {
  200.                                 const jjPLAYER@ holder = jjPlayers[flag.playerID];
  201.                                 if (holder.health <= 0 || !holder.isInGame) {
  202.                                         flag.playerID = (jjTeamScore[TEAM::BLUE] > jjTeamScore[TEAM::RED]) ? SpecialPlayerID::BlueBase : SpecialPlayerID::RedBase;
  203.                                         SendFlagData();
  204.                                 } else {
  205.                                         const auto teamID = holder.team;
  206.                                         const Point@ homeBase = BaseLocations[teamID];
  207.                                         if (abs(flag.xPos - homeBase.xPos) <= 32 && abs(flag.yPos - homeBase.yPos) <= 32) {
  208.                                                 const string pointScore = (teamID == 1 ? "|" : "||") + holder.nameUnformatted + " scored the point";
  209.                                                 AssignFlagToBase(flag, teamID);
  210.                                                 jjAlert(pointScore, true);
  211.                                                 SendFlagData();
  212.                                         }
  213.                                 }
  214.                         } else {
  215.                                 if (flag.playerID == SpecialPlayerID::Free && jjGameTicks >= flag.timeout) {
  216.                                         flag.playerID = (jjTeamScore[TEAM::BLUE] > jjTeamScore[TEAM::RED]) ? SpecialPlayerID::BlueBase : SpecialPlayerID::RedBase;
  217.                                         SendFlagData();
  218.                                         jjAlert("A flag timed out and returned to the " + (flag.playerID == SpecialPlayerID::BlueBase ? "blue" : "red") + " base", true);
  219.                                 } else for (uint playerID = 0; playerID < 32; ++playerID) {
  220.                                         const jjPLAYER@ player = jjPlayers[playerID];
  221.                                         if (player.isInGame && NextCaptureTime[playerID] < jjGameTicks && (flag.playerID == SpecialPlayerID::Free || ((flag.playerID == SpecialPlayerID::BlueBase) == (player.teamRed))) && DoesPlayerHaveFlag(player) is null) {
  222.                                                 if (abs(flag.xPos - player.xPos) <= 32 && abs(flag.yPos - player.yPos) <= 32) {
  223.                                                         flag.playerID = playerID;
  224.                                                         jjAlert((player.teamRed ? "|" : "||") + player.nameUnformatted + " captured a flag", true, STRING::MEDIUM);
  225.                                                         NextCaptureTime[playerID] = jjGameTicks + 3*70; //why not
  226.                                                         SendFlagData();
  227.                                                         break; //don't be captured twice at once
  228.                                                 }
  229.                                         }
  230.                                 }
  231.                         }
  232.                 }
  233.                 if (flag.playerID >= SpecialPlayerID::Free) {
  234.                         jjDrawSpriteFromCurFrame(flag.xPos, flag.yPos+6, jjAnimations[CustomSet.firstAnim + (flag.playerID == SpecialPlayerID::BlueBase ? 1 : 2)].firstFrame + ((jjGameTicks >> 3) & 7), flag.direction, SPRITE::SINGLEHUE, 64);
  235.                         if (flag.playerID == SpecialPlayerID::Free) {
  236.                                 jjDrawSpriteFromCurFrame(flag.xPos - 16, flag.yPos - 20, jjObjectPresets[OBJECT::STOPWATCH].curFrame + ((jjGameTicks >> 3) & 7), -1);
  237.                                 jjDrawString(flag.xPos, flag.yPos - 20, "" + ((flag.timeout - jjGameTicks) / 70 + 1), STRING::MEDIUM);
  238.                         }
  239.                 } else {
  240.                         const int teamID = flag.playerID == SpecialPlayerID::BlueBase ? 0 : 1;
  241.                         jjDrawRotatedSpriteFromCurFrame(flag.xPos, flag.yPos+21, jjAnimations[CustomSet.firstAnim + teamID + 1].firstFrame + (((jjGameTicks >> 3) + flagID) & 7), flag.direction * baseFlagAngles[teamID], flag.direction,1, SPRITE::NORMAL);
  242.                         baseFlagAngles[teamID] -= 32;
  243.                         FlagsPerTeam[teamID] += 1;
  244.                         if (FlagsPerTeam[teamID] == FlagCount) {
  245.                                 if ((!LimitAllFlagPoints) || (jjTeamScore[TEAM::Color(teamID)] <= jjTeamScore[TEAM::Color(teamID ^ 1)])) //don't let a team get too big of a lead
  246.                                         someTeamHasAllFlags = true;
  247.                         }
  248.                 }
  249.                 if (flag.playerID >= 0) {
  250.                         const jjPLAYER@ holder = jjPlayers[flag.playerID];
  251.                         jjDrawSpriteFromCurFrame(holder.xPos + jjGetStringWidth("0" + holder.name, STRING::SMALL, STRING::NORMAL)/2, holder.yPos - 38, SmallAsterisk, 1, SPRITE::PALSHIFT, 256-24, -99);
  252.                 }
  253.         }
  254.         if (someTeamHasAllFlags && gameActive) {
  255.                 if (AutoScoreCountdown != 0) {
  256.                         AutoScoreCountdown -= 1;
  257.                         if (jjIsServer && AutoScoreCountdown == 0) {
  258.                                 jjSamplePriority(SOUND::COMMON_BELL_FIRE2);
  259.                                 if (FlagsPerTeam[0] == 0)
  260.                                         jjChat("/redscore " + (jjTeamScore[TEAM::RED] + 1));
  261.                                 else
  262.                                         jjChat("/bluescore " + (jjTeamScore[TEAM::BLUE] + 1));
  263.                                 AutoScoreCountdown = 5*70;
  264.                                 SendTimePacket();
  265.                         }
  266.                 }
  267.                 if (jjIsServer) {
  268.                         if (AutoScoreCountdown == 0) {
  269.                                 AutoScoreCountdown = 5*70;
  270.                                 jjSamplePriority(SOUND::COMMON_BELL_FIRE2);
  271.                                 jjAlert((FlagsPerTeam[0] == 0 ? "|Red" : "||Blue") + " team has all flags", true, STRING::MEDIUM);
  272.                                 SendTimePacket();
  273.                         }
  274.                 }
  275.         } else
  276.                 AutoScoreCountdown = 0;
  277.         for (int teamID = 0; teamID < 2; ++teamID) {
  278.                 if (jjTeamScore[TEAM::Color(teamID)] != LastTeamScore[teamID]) {
  279.                         if (jjTeamScore[TEAM::Color(teamID)] > LastTeamScore[teamID] && BaseObjects[teamID] !is null) {
  280.                                 BaseObjects[teamID].var[4] = 1; //make eva jump
  281.                                 BaseObjects[teamID].counter = 0; //
  282.                         }
  283.                         LastTeamScore[teamID] = jjTeamScore[TEAM::Color(teamID)];
  284.                 }
  285.         }
  286. }
  287.  
  288. Flag@ DoesPlayerHaveFlag(const jjPLAYER@ player) {
  289.         const int playerID = player.playerID;
  290.         bool localPlayerHasFlag = false;
  291.         for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  292.                 Flag@ flag = Flags[flagID];
  293.                 if (flag.playerID == playerID)
  294.                         return flag;
  295.         }
  296.         return null;
  297. }
  298.  
  299. bool interceptChat = true;
  300. bool onLocalChat(string &in stringReceived, CHAT::Type chatType) {
  301.         if (interceptChat) {
  302.                 if (chatType == CHAT::WHISPER || chatType == CHAT::ME)
  303.                         return false;
  304.                 if (DoesPlayerHaveFlag(jjLocalPlayers[0]) is null)
  305.                         return false;
  306.                 if (stringReceived.length >= 1 && stringReceived[0] == '!'[0])
  307.                         return false;
  308.         }
  309.                
  310.         if (interceptChat) {
  311.                 interceptChat = false;
  312.                 jjChat("K|||| /*:||  " + stringReceived, chatType == CHAT::TEAMCHAT);
  313.                 interceptChat = true;
  314.                 return true;
  315.         } else {
  316.                 return false;
  317.         }
  318. }
  319.  
  320.  
  321. void onChat(int clientID, string &in stringReceived, CHAT::Type chatType) {
  322.         if (chatType == CHAT::TEAMCHAT && jjRegexMatch(stringReceived, "h\\??", true)) {
  323.                 if (DoesPlayerHaveFlag(jjLocalPlayers[0]) !is null)
  324.                         jjChat("" + jjLocalPlayers[0].health, true);
  325.         } else if (jjIsServer && (clientID == 0 || jjPlayersWithClientID(clientID)[0].isAdmin)) {
  326.                 array<string> results;
  327.                 if (jjRegexMatch(stringReceived, "!flagsperteam\\s+(\\d\\d?)", results, true)) {
  328.                         uint proposedFlagCount = parseUInt(results[1]);
  329.                         if (proposedFlagCount >= 1 && proposedFlagCount <= Flags.length/2) {
  330.                                 proposedFlagCount *= 2;
  331.                                 if (proposedFlagCount != FlagCount) {
  332.                                         for (uint lostFlag = FlagCount - 1; lostFlag >= proposedFlagCount; --lostFlag) {
  333.                                                 Flag@ flag = Flags[lostFlag];
  334.                                                 if (flag.playerID >= 0)
  335.                                                         jjConsole(jjPlayers[flag.playerID].name + "'s flag is gone.");
  336.                                         }
  337.                                         FlagCount = proposedFlagCount;
  338.                                         jjSTREAM save;
  339.                                         save.push(FlagCount);
  340.                                         save.save("multiflag.asdat");
  341.                                         SendFlagData();
  342.                                         jjConsole("Flags Per Team has been set to " + (proposedFlagCount/2), true);
  343.                                 } else {
  344.                                         jjConsole("Flags Per Team is already " + (proposedFlagCount/2), true);
  345.                                 }
  346.                         } else
  347.                                 jjConsole("|Must be between 1 and " + (Flags.length/2) + " flags per team.", true);
  348.                 }
  349.         }
  350. }
  351.  
  352. bool onDrawGameModeHUD(jjPLAYER@, jjCANVAS@ canvas) {
  353.         array<uint> evenHeldFlags = FlagsPerTeam;
  354.         const bool wide = jjResolutionWidth > 400;
  355.         const int x = wide ? 100 : 80;
  356.         int y = wide ? 22 : 17;
  357.         uint teamWithAllFlags = 99;
  358.        
  359.         if (someTeamHasAllFlags) {
  360.                 const bool redHasFlags = FlagsPerTeam[0] == 0;
  361.                 if ((!LimitAllFlagPoints) || (jjTeamScore[redHasFlags ? TEAM::RED : TEAM::BLUE] <= jjTeamScore[redHasFlags ? TEAM::BLUE : TEAM::RED])) {
  362.                         const int stringCenter = 0x8000 - (jjSubscreenWidth / 2) + 30;
  363.                         canvas.drawString(stringCenter, 60, (redHasFlags ? "||" : "|||") + ((AutoScoreCountdown + 69) / 70), STRING::LARGE);
  364.                         teamWithAllFlags = redHasFlags ? 1 : 0;
  365.                 }
  366.         }
  367.         if (((jjRenderFrame/17)&1) == 1) {
  368.                 for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  369.                         const Flag@ flag = Flags[flagID];
  370.                         if (flag.playerID >= 0)
  371.                                 evenHeldFlags[jjPlayers[flag.playerID].teamRed ? 1 : 0] += 1;
  372.                 }
  373.         }
  374.         for (uint teamID = 0; teamID < 2; ++teamID) {
  375.                 const auto firstFrame = jjAnimations[CustomSet + 3 + teamID].firstFrame;
  376.                 int xx = x, yy = y;
  377.                 if (teamWithAllFlags == teamID) {
  378.                         xx += jjRandom() & 3;
  379.                         yy += jjRandom() & 3;
  380.                 }
  381.                 for (uint flagID = 0; flagID < evenHeldFlags[teamID]; ++flagID)
  382.                         canvas.drawSpriteFromCurFrame(xx + flagID * 20, yy, firstFrame + (((jjGameTicks >> 3) + flagID) & 7), 1, flagID < FlagsPerTeam[teamID] ? SPRITE::NORMAL : SPRITE::SINGLEHUE, 64);
  383.                 /*
  384.                 for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  385.                         const Flag@ flag = Flags[flagID];
  386.                         if (flag.playerID == SpecialPlayerID::Free || flag.playerID == (player.teamRed ? SpecialPlayerID::BlueBase : SpecialPlayerID::RedBase) || (flag.playerID >= 0 && jjPlayers[flag.playerID].teamRed != player.teamRed)) {*/
  387.                 y += wide ? 24 : 12;
  388.         }
  389.         return false;
  390. }
  391.  
  392. void SendFlagData(int clientID = 0) {
  393.         //there's little enough that it's easier on my poor tender head to send everything at once every dang time
  394.         jjSTREAM packet;
  395.         packet.push(uint8(FlagCount));
  396.         for (uint flagID = 0; flagID < FlagCount; ++flagID) {
  397.                 const Flag@ flag = Flags[flagID];
  398.                 packet.push(int8(flag.playerID));
  399.                 if (flag.playerID == SpecialPlayerID::Free) {
  400.                         packet.push(int16(flag.xPos));
  401.                         packet.push(int16(flag.yPos));
  402.                 }
  403.         }
  404.         jjSendPacket(packet, clientID);
  405. }
  406. void SendTimePacket() {
  407.         jjSendPacket(jjSTREAM());
  408. }