Downloads containing Condemned.mut

Downloads
Name Author Game Mode Rating
JJ2+ Only: Condemned Violet CLM Mutator N/A 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");
		}
	}
}

jjTEXTAPPEARANCE Monospaced(STRING::NORMAL);
void onLevelLoad() {
	Monospaced.monospace = true;
	Monospaced.spacing = 8;
}

array<int> Timers(32, 200);

bool onDrawGameModeHUD(jjPLAYER@, jjCANVAS@ canvas) {
	const int x = 4;
	int y = 11;
	for (int i = 0; i < 32; ++i) {
		const jjPLAYER@ other = jjPlayers[i];
		if (other.isInGame) {
			canvas.drawString(x, y, (other.teamRed ? "||" : "|||") + formatInt(i + 1, "0", 2) + " " + other.name.substr(0,3), STRING::SMALL, Monospaced);
			canvas.drawRectangle(x + 58, y - 8, Timers[i] / 2, 10, !other.teamRed ? 34 : 25);
			if (other.isLocal) //draw an orange > to mark localness, so you can find your timer easily
				canvas.drawSprite(x - 8, y - 10, ANIM::FONT, 1, '>'[0] - 32, 1, SPRITE::SINGLEHUE, 40);
			y += 16;
		}
	}
	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");
				}
			}
		}
	}
}

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;
}