Downloads containing survivor.mut

Downloads
Name Author Game Mode Rating
JJ2+ Only: Survivor.mutFeatured Download Violet CLM Mutator 9.7 Download file

File preview

void onLevelBegin() { //can't use commands while cycling
	if (jjIsServer || jjGameConnection == GAME::LOCAL) {
		if (jjGameMode != GAME::CTF) { //non-team-based
			if (jjGameCustom != GAME::LRS && jjGameCustom != GAME::XLRS)
				jjChat("/lrs");
		} else {
			if (jjGameCustom != GAME::TLRS)
				jjChat("/tlrs");
		}
	}
}

array<uint> StartWarpIDs();
array<int> ArenaBoundaries = {9999, 9999, 0, 0}; //left, top, right, bottom
void onLevelLoad() { //find start warp ID
	for (int yTile = 0; yTile < 26; ++yTile)
		for (int xTile = 0; xTile < 30; ++xTile) {
			const int eventID = jjEventGet(xTile, yTile);
			if (eventID == AREA::WARP && jjParameterGet(xTile, yTile, 8, 8) == 0) { //not a coin warp
				jjMASKMAP mask(jjTileGet(4, xTile, yTile));
				if (!mask[15, 2] && !mask[2, 30] && !mask[30, 30]) { //not a solid tile; these three pixels serve to weed out all the warps on masked tiles in all thirty survivor levels/tilesets
					const uint warpID = jjParameterGet(xTile, yTile, 0, 8);
					if (!(warpID == 0 && StartWarpIDs.length > 0)) //avoid issue with survivor08
						StartWarpIDs.insertLast(warpID); //several levels use multiple warp IDs instead of (only) multiple warp targets per ID
				}
			} else if (eventID == AREA::WATERLEVEL) //survivor03, survivor23, maybe others I'm forgetting
				jjSetWaterLevel(jjParameterGet(xTile, yTile, 0, 8) * 32, true);
		}
	if (jjEventGet(6, 7) == AREA::TRIGGERZONE) //survivor13
		jjTriggers[jjParameterGet(6, 7, 0, 5)] = (jjParameterGet(6, 7, 5, 1) == 1);
		
	jjCharacters[CHAR::JAZZ].airJump = jjCharacters[CHAR::SPAZ].airJump;
	jjCharacters[CHAR::JAZZ].doubleJumpCountMax = jjCharacters[CHAR::SPAZ].doubleJumpCountMax;
	jjCharacters[CHAR::JAZZ].doubleJumpXSpeed = jjCharacters[CHAR::SPAZ].doubleJumpXSpeed;
	jjCharacters[CHAR::JAZZ].doubleJumpYSpeed = jjCharacters[CHAR::SPAZ].doubleJumpYSpeed;
	if (jjIsTSF) {
		jjCharacters[CHAR::LORI].airJump = jjCharacters[CHAR::SPAZ].airJump;
		jjCharacters[CHAR::LORI].doubleJumpCountMax = jjCharacters[CHAR::SPAZ].doubleJumpCountMax;
		jjCharacters[CHAR::LORI].doubleJumpXSpeed = jjCharacters[CHAR::SPAZ].doubleJumpXSpeed;
		jjCharacters[CHAR::LORI].doubleJumpYSpeed = jjCharacters[CHAR::SPAZ].doubleJumpYSpeed;
	}
	
	for (int xTile = jjLayerWidth[4] - 1; xTile >= 0; --xTile)
		for (int yTile = jjLayerHeight[4] - 1; yTile >= 0; --yTile) {
			const int eventID = jjEventGet(xTile, yTile);
			if (eventID == OBJECT::DESTRUCTSCENERY) {
				if (xTile < ArenaBoundaries[0])
					ArenaBoundaries[0] = xTile;
				if (xTile > ArenaBoundaries[2])
					ArenaBoundaries[2] = xTile;
				if (yTile < ArenaBoundaries[1] && (ArenaBoundaries[1] == 9999 || ArenaBoundaries[1] - yTile < 40)) //avoid issue with survivor24
					ArenaBoundaries[1] = yTile;
				if (yTile > ArenaBoundaries[3])
					ArenaBoundaries[3] = yTile;
			}
		}
	for (int i = 0; i < 4; ++i) { //extend sides slightly
		if (i > 1)
			ArenaBoundaries[i] += 2;
		else
			ArenaBoundaries[i] -= 1;
		ArenaBoundaries[i] *= 32;
	}
	
	SpinningString.align = STRING::CENTER;
}

bool GameWasActive = true;
bool LocalPlayerWasBlinking = false;
jjPLAYER@ LocalPlayer = jjLocalPlayers[0];
void onMain() { //Go through and adjust certain objects to be destroyable (or not) depending on whether the game is in progress or paused/pregaming
	bool gameActive = jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME;
	if (gameActive && !GameWasActive) {
		for (int i = 0; i < 32; ++i)
			if (jjPlayers[i].isActive && jjPlayers[i].isConnecting) {
				gameActive = false; //don't start the game if someone's connecting
				break;
			}
	}
	if (GameWasActive != gameActive) {
		GameWasActive = gameActive;
		if (gameActive) {
			for (int i = jjObjectCount - 1; i >= 1; --i) {
				jjOBJ@ obj = jjObjects[i];
				if (obj.behavior == BEHAVIOR::DESTRUCTSCENERY)
					obj.bulletHandling = HANDLING::DETECTBULLET;
				else if (obj.behavior == BEHAVIOR::CRATE)
					obj.playerHandling = HANDLING::SPECIAL;
			}
		} else {
			for (int i = jjObjectCount - 1; i >= 1; --i) {
				jjOBJ@ obj = jjObjects[i];
				if (obj.behavior == BEHAVIOR::DESTRUCTSCENERY)
					obj.bulletHandling = HANDLING::IGNOREBULLET;
				else if (obj.behavior == BEHAVIOR::CRATE)
					obj.playerHandling = HANDLING::SPECIALDONE;
			}
		}
	}
	
	const bool localPlayerBlinking = LocalPlayer.blink != 0;
	if (LocalPlayerWasBlinking != localPlayerBlinking) {
		for (int i = jjObjectCount - 1; i >= 1; --i) {
			jjOBJ@ obj = jjObjects[i];
			if (obj.behavior == BEHAVIOR::SPRING || obj.behavior == BEHAVIOR::BUMP || obj.behavior == BEHAVIOR::PADDLE) //stuff that you shouldn't be able to use while blinking
				obj.playerHandling = (localPlayerBlinking) ? HANDLING::SPECIALDONE : HANDLING::SPECIAL;
		}
		LocalPlayerWasBlinking = localPlayerBlinking;
	}
}

array<int> playerState(4, 0); //0: starting. 1: warping. 2: in game.
void onPlayer(jjPLAYER@ play) {
	if (play.health > 0) {
		if (play.yPos < 20*32) { //in start area
			if (playerState[play.localPlayerID] == 0) {
				play.keyLeft = play.keyRight = play.keyJump = play.keyFire = play.keyRun = false;
				play.doubleJumpCount = 0; //so you'll be able to double jump again if you revive
				
				const int arenaLeft = ArenaBoundaries[0];
				const int arenaTop = ArenaBoundaries[1];
				const int arenaRight = ArenaBoundaries[2] - jjSubscreenWidth;
				const int arenaBottom = ArenaBoundaries[3] - jjSubscreenHeight;
				const int arenaHalfWidth = (arenaRight - arenaLeft) / 2;
				const int arenaHalfHeight = (arenaBottom - arenaTop) / 2;
				const float intensityX = jjSin((arenaHalfWidth > arenaHalfHeight) ? jjRenderFrame/2 : jjRenderFrame*3);
				const float intensityY = jjSin((arenaHalfWidth > arenaHalfHeight) ? jjRenderFrame*3 : jjRenderFrame/2);
				play.cameraFreeze(
					arenaLeft + arenaHalfWidth + intensityX * arenaHalfWidth,
					arenaTop + arenaHalfHeight + intensityY * arenaHalfHeight,
					false, true
				);
				
				if (play.warpID == 0 && play.blink == 0 && GameWasActive) { //ready to begin; has to be ==0, not <2, to work with survivor21
					play.warpToID(StartWarpIDs[jjRandom() % StartWarpIDs.length]);
					playerState[play.localPlayerID] = 1;
					jjTriggers[1] = true; //in a happy bit of consistency, all Survivor levels use trigger 1 to start the level
				}
			} else if (GameWasActive && playerState[play.localPlayerID] == 2) { //out
				playerState[play.localPlayerID] = 0;
				play.hurt(10, true); //works better than jjPLAYER::kill
			}
		} else { //in arena
			if (playerState[play.localPlayerID] == 1) { //finished warping
				playerState[play.localPlayerID] = 2; //in game
				play.cameraUnfreeze(false);
			}
			if (play.health != jjMaxHealth && !(jjGameCustom == GAME::XLRS || jjGameState == GAME::OVERTIME)) //invincible, but in a way that allows players to be hit
				play.health = jjMaxHealth;
		}
	}
}

jjTEXTAPPEARANCE SpinningString(STRING::SPIN);
bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ screen) {
	if (GameWasActive) { //reviving after death, preferably
		int intensity = abs(play.blink);
		if (intensity > 255) intensity = 255;
		if (playerState[play.localPlayerID] == 0)
			screen.drawString(jjSubscreenWidth/2, jjSubscreenHeight/2 - 30, "Get Ready", STRING::LARGE, SpinningString, 0, SPRITE::BLEND_NORMAL, intensity);
	}
	
	return !(jjGameCustom == GAME::XLRS || jjGameState == GAME::OVERTIME);
}