Downloads containing Condemned.mut

Downloads
Name Author Game Mode Rating
JJ2+ Only: CondemnedFeatured Download Violet CLM Mutator 9 Download file

File preview

#pragma description "Variant on LRS. One or more players are placed randomly on the red team, meaning they are Condemned, and timers count down next to their names. If a player's timer runs out, they are removed from the game. A condemned (red) player may roast a normal (blue) player to swap both their teams. Best played with a low max health, so people die a lot."

bool AtLeastOneRoast = false;

void onRoast(jjPLAYER@ victim, jjPLAYER@ killer) {
	if (jjIsServer) {
		AtLeastOneRoast = true;
		if (victim.team == TEAM::BLUE && killer !is victim) {
			jjChat("/swap " + (victim.playerID + 1) + " red");
			jjChat("/swap " + (killer.playerID + 1) + " blue");
		}
	}
}

int RectangleFrameIDs;
void onLevelLoad() {
	uint8 customSetID = 0;
	while (jjAnimSets[ANIM::CUSTOM[customSetID]] != 0) { ++customSetID; }
	RectangleFrameIDs = jjAnimations[jjAnimSets[ANIM::CUSTOM[customSetID]].allocate(array<uint>={100})];
	//jjAlert("" + RectangleFrameIDs);
	jjPIXELMAP rectangle(1,10);
	for (uint y = 0; y < 10; ++y)
		rectangle[0,y] = 25 + (y + 1) / 4;
	auto rectangleFrameID = RectangleFrameIDs;
	for (uint i = 0; i < 100; ++i) {
		jjANIMFRAME@ frame = jjAnimFrames[rectangleFrameID++];
		frame.hotSpotY = 3;
		rectangle.resize(i + 1, 10).addBorders(0,0,1,0).save(frame);
		rectangle.addBorders(0,0,-1,0);
	}
}

array<int> Timers(32, 200);

bool onDrawGameModeHUD(jjPLAYER@, jjCANVAS@ canvas) { return true; }

void onLevelBegin() {
	if (jjIsServer) {
		if (jjGameCustom != GAME::FR)
			jjChat("/fr");
		jjChat("/joinersspectate on");
		jjChat("/spectating off");
		if (!jjPlayers[0].isIdle) {
			if (jjPlayers[0].isSpectating)
				jjChat("/forcespectate 1 off");
			if (jjPlayers[0].team != TEAM::BLUE)
				jjChat("/swap 1 blue");
		}
		jjPrint("****     " + jjLevelName + " (" + jjLevelFileName + ") Condemned.mut");
	} else {	
		jjSTREAM packet;
		for (int i = 0; i < jjLocalPlayerCount; ++i)
			packet.push(uint8(jjLocalPlayers[i].playerID));
		jjSendPacket(packet);
	}
}
void onReceive(jjSTREAM &in packet, int fromClientID) {
	if (jjIsServer) {
		if (!AtLeastOneRoast)
			while (!packet.isEmpty()) {
				uint8 playerID;
				packet.pop(playerID);
				const jjPLAYER@ player = jjPlayers[playerID];
				if (player.clientID == fromClientID) {
					if (player.isSpectating)
						jjChat("/forcespectate " + (playerID + 1) + " off");
					if (player.team != TEAM::BLUE)
						jjChat("/swap " + (playerID + 1) + " blue");
				} else
					return;
			}
	} else {
		while (!packet.isEmpty()) {
			uint8 playerID;
			packet.pop(playerID);
			uint8 time;
			packet.pop(time);
			Timers[playerID] = time;
		}
	}
}

array<const jjPLAYER@> GetTeamPlayers(TEAM::Color team) {
	array<const jjPLAYER@> result;
	for (uint i = 0; i < 32; ++i) {
		jjPLAYER@ possible = jjPlayers[i];
		if (possible.isInGame && possible.team == team)
			result.insertLast(possible);
	}
	return result;
}

void onMain() {
	if (jjIsServer) {
		if ((jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME) && jjGameTicks % 21 == 20) {
			array<const jjPLAYER@>@ red = GetTeamPlayers(TEAM::RED);
			for (uint i = 0; i < red.length; ++i) { //should be at most 1...
				const jjPLAYER@ player = red[i];
				if ((Timers[player.playerID] -= (jjGameState == GAME::STARTED ? 1 : 2)) <= 0) {
					jjChat("/forcespectate " + (player.playerID + 1) + " on");
					jjPrint("**** " + player.name + " eliminated.");
					jjAlert(player.name + " is out!", true, STRING::MEDIUM);
				}
			}
			
			jjSTREAM packet;
			for (uint8 i = 0; i < 32; ++i) {
				if (jjPlayers[i].isInGame) {
					packet.push(i);
					if (Timers[i] < 0)
						packet.push(uint8(0));
					else
						packet.push(uint8(Timers[i]));
				}
			}
			if (packet.getSize() <= 2) { //at most one player alive
				if (packet.getSize() == 2) { //exactly one player alive
					uint8 playerID;
					packet.pop(playerID);
					jjPrint("**** " + jjPlayers[playerID].name + " wins.");
					jjAlert(jjPlayers[playerID].name + " wins!", true, STRING::MEDIUM);
				}
				jjChat("/stop");
				for (uint i = 0; i < 32; ++i)
					Timers[i] = 200;
				AtLeastOneRoast = false;
				return;
			}
			jjSendPacket(packet);
			
			if (int(GetTeamPlayers(TEAM::RED).length) <= (int(GetTeamPlayers(TEAM::BLUE).length) - 1) / 4) {
				int minimumRoasts = 9999;
				array<const jjPLAYER@> contenders;
				for (uint i = 0; i < 32; ++i) {
					const jjPLAYER@ possible = jjPlayers[i];
					if (possible.isInGame && possible.team == TEAM::BLUE) {
						if (possible.roasts < minimumRoasts) {
							minimumRoasts = possible.roasts;
							contenders.resize(0);
						}
						if (possible.roasts == minimumRoasts) { //including the above case
							contenders.insertLast(possible);
						}
					}
				}
				if (contenders.length != 0) {
					const jjPLAYER@ loser = contenders[jjRandom() % contenders.length];
					if (AtLeastOneRoast)
						jjAlert(loser.name + " had the fewest roasts...", true, STRING::MEDIUM);
					jjChat("/swap " + (loser.playerID + 1) + " red");
				}
			}
		}
	}
	
	for (uint playerID = 0; playerID < 32; ++playerID) {
		jjPLAYER@ player = jjPlayers[playerID];
		player.iconSet(player.isInGame && Timers[playerID] >= 2 ? jjAnimFrames[RectangleFrameIDs + Timers[playerID] / 2 - 1] : null, player.teamRed ? 0 : 8);
	}
}

uint TimeTopLeft = 0;
void onPlayer(jjPLAYER@ player) {
	if (player.health > 0 && int(player.xPos) / 32 <= 0 && int(player.yPos) / 32 <= 0 && player.warpID == 0) {
		if (++TimeTopLeft % 280 == 250) {
			for (int x = jjLayerWidth[4]; --x >= 0;)
			for (int y = jjLayerHeight[4]; --y >= 0;) {
				const uint8 event = jjEventGet(x,y);
				if (event == AREA::JAZZSTART || event == AREA::MPSTART) {
					player.warpToTile(x,y);
					return;
				}
			}
		}
	} else TimeTopLeft = 0;
}

void onHelp(array<jjHELPPAGE@>& pages) {
	for (int i = pages.length - 1; i >= 0; --i) { //remove any existing pages with the gamemode tag
		if (jjRegexSearch(pages[i].tags, "\\bgamemode\\b", true))
			pages.removeAt(i);
	}
	pages.insertLast(jjHELPPAGE(
		{
			jjHELPPARAGRAPH(
				"||Con|demned",
				textSize: STRING::MEDIUM,
				textAlignment: STRING::CENTER
			),
			jjHELPPARAGRAPH(
				"One or more players are placed randomly on the \\2red\\6 team, meaning they are Condemned, and timers count down next to their names. If a player's timer runs out, they are removed from the game. A condemned (\\2red\\6) player may roast a normal (\\3blue\\6) player to swap both their teams."
			),
			jjHELPPARAGRAPH(
				"Best played with a low max health, so people die a lot.",
				image: jjAnimFrames[RectangleFrameIDs + 99],
				position: HELP::TOPCENTER
			)
		},
		"mutator gamemode condemned"
	));
	if (jjIsServer || jjLocalPlayers[0].isAdmin)
		pages.insertLast(jjHELPPAGE(
			{
				jjHELPPARAGRAPH("Host Condemned with \\4/pregame on\\6 or \\4/autostart off\\6, to give the game time to decide who should start on the red team. The game automatically stops once the winner has been determined, so you'll need to use \\4/c\\6 after that."),
				jjHELPPARAGRAPH("For keeping track of scores for JDC-like purposes, search the server's chatlog for lines beginning with four asterisks (\\4****\\6).")
			},
			"mutator gamemode condemned commands",
			"Commands"
		));
}