Downloads containing TrueFur.mut

Downloads
Name Author Game Mode Rating
JJ2+ Only: True FurFeatured Download Violet CLM Mutator 9.5 Download file

File preview

#pragma name "True Fur"
#pragma description "Draw players with full 16-bit color palettes instead of the sprite colors of the current level palette"

class Fur {
	int MappingID = -1;
	jjSTREAM Packet;
	Fur(){}
}
array<Fur> Furs(32);
void onLevelBegin() {
	if (!jjIsServer)
		jjSendPacket(jjSTREAM()); //hey, I'm here, send me stuff
	
	for (int localPlayerID = 0; localPlayerID < jjLocalPlayerCount; ++localPlayerID) {
		const int playerID = jjLocalPlayers[localPlayerID].playerID;
		Fur@ fur = Furs[playerID];
		fur.Packet.push(uint8(playerID));
		
		jjSTREAM furFile("Fur" + (localPlayerID + 1) + ".asdat");
		if (furFile.getSize() == 254) { //correct length
			uint8 version;
			furFile.pop(version);
			if (version == 0) { //only supported version
				furFile.discard(10); //selectedIndex values for the html file
				
				const int mappingID = jjSpriteModeFirstFreeMapping();
				if (mappingID >= 0) { //mappings available
					fur.MappingID = mappingID;
					fur.Packet.write(furFile);
					
					mapPacket(mappingID, furFile);
				}
			}
		}
		
		if (!jjIsServer)
			jjSendPacket(fur.Packet); //even if the file wasn't loaded, a packet with only the player ID is still valid (and counted as meaning no custom fur for this player)
	}
}

void mapPacket(int mappingID, jjSTREAM@ packet) {
	jjPAL mapping = jjPalette; //SOME palette, shouldn't really matter
	uint index = 15;
	uint8 r, g, b;
	for (int i = 0; i < 243; i += 3, ++index) {
		packet.pop(r);
		packet.pop(g);
		packet.pop(b);
		mapping.color[index] = jjPALCOLOR(r,g,b);
	}
	
	jjSpriteModeSetMapping(mappingID, array<uint8>(), mapping);
}

void onReceive(jjSTREAM &in packet, int fromClientID) {
	if (packet.isEmpty()) { //initial ping
		if (jjIsServer)
			for (int otherPlayerID = 0; otherPlayerID < 32; ++otherPlayerID) {
				const jjPLAYER@ player = jjPlayers[otherPlayerID];
				if (player.isActive && player.clientID != fromClientID)
					jjSendPacket(Furs[otherPlayerID].Packet, fromClientID); //send the other players' fur to the new player
			}
		return;
	}
	
	uint8 playerID;
	packet.pop(playerID);
	if (!jjIsServer || jjPlayers[playerID].clientID == fromClientID) {
		Fur@ fur = Furs[playerID];
		fur.Packet.clear();
		fur.Packet.push(playerID);
		
		if (packet.getSize() == 243) {
			if (fur.MappingID < 0) //this player ID hasn't had a mapping before
				fur.MappingID = jjSpriteModeFirstFreeMapping();
			if (fur.MappingID >= 0) {
				fur.Packet.write(packet);
				mapPacket(fur.MappingID, packet);
			}
		}
		
		if (jjIsServer) {
			for (int otherPlayerID = jjLocalPlayerCount; otherPlayerID < 32; ++otherPlayerID) {
				const jjPLAYER@ player = jjPlayers[otherPlayerID];
				if (player.isActive && player.clientID != fromClientID)
					jjSendPacket(fur.Packet, player.clientID); //send the new player's fur (or lack of fur) to the other players
			}
		}
	}
}

void onPlayerDraw(jjPLAYERDRAW& draw) {
	if (jjColorDepth <= 8) //don't bother
		return;
	if (!draw.sprite || (draw.spriteMode != SPRITE::PLAYER && draw.spriteMode != SPRITE::TRANSLUCENTPLAYER))
		return;
	if (draw.player.isZombie)
		return;
	
	const Fur@ fur = Furs[draw.player.playerID];
	if (fur.Packet.getSize() > 1) {
		draw.spriteMode = draw.spriteMode == SPRITE::PLAYER ? SPRITE::MAPPING : SPRITE::TRANSLUCENTMAPPING;
		draw.spriteParam = fur.MappingID;
	}
}

bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
	if (jjColorDepth <= 8)
		return false;
	const Fur@ fur = Furs[player.playerID];
	if (fur.Packet.getSize() <= 1)
		return false;
	
	int faceAnimID;
	switch (player.charCurr) {
		case CHAR::BIRD2:
			faceAnimID = 0;
			break;
		case CHAR::BIRD:
			faceAnimID = 1;
			break;
		case CHAR::FROG:
			faceAnimID = 2;
			break;
		case CHAR::JAZZ:
			faceAnimID = 3;
			break;
		case CHAR::LORI:
			if (jjIsTSF) {
				faceAnimID = 4;
				break;
			}
		default:
			faceAnimID = jjIsTSF ? 5 : 4;
			break;
	}
	const jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::FACES] + faceAnimID];
	canvas.drawSpriteFromCurFrame(sprite: anim.firstFrame + ((jjGameTicks / 6) % anim.frameCount), xPixel: 0, yPixel: jjSubscreenHeight, mode: SPRITE::MAPPING, param: fur.MappingID);
	
	if (jjGameCustom == GAME::LRS || jjGameCustom == GAME::TLRS || jjGameCustom == GAME::XLRS)
		canvas.drawString(32, jjSubscreenHeight - (jjSubscreenWidth <= 400 ? 9 : 14), "x" + player.lrsLives, jjSubscreenWidth <= 400 ? STRING::SMALL : STRING::MEDIUM);
	
	return true;
}

void onMain() {
	if (jjColorDepth <= 8)
		return;
	if (jjGameTicks & 15 == 15) {
		for (int objectID = jjObjectCount; --objectID != 0;) {
			jjOBJ@ obj = jjObjects[objectID];
			if (obj.behavior == BEHAVIOR::CORPSE && obj.counterEnd == SPRITE::PLAYER) {
				const Fur@ fur = Furs[obj.doesHurt]; //sprite param = player ID
				if (fur.Packet.getSize() > 1) {
					obj.doesHurt = fur.MappingID;
					obj.counterEnd = uint8(SPRITE::MAPPING);
				}
			}
		}
	}
}

bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
	if (player.currWeapon != WEAPON::BLASTER)
		return false;
	if (jjObjectPresets[OBJECT::BLASTERBULLET].behavior != BEHAVIOR::BULLET || jjObjectPresets[OBJECT::BLASTERBULLETPU].behavior != BEHAVIOR::BULLET)
		return false;
	if (jjColorDepth <= 8)
		return false;
	if (player.isZombie)
		return false;
	const Fur@ fur = Furs[player.playerID];
	if (fur.Packet.getSize() <= 1)
		return false;
	
	if (!player.noFire && player.charCurr != CHAR::FROG && player.charCurr != CHAR::BIRD) {
		const bool powerup = player.powerup[WEAPON::CURRENT];
		const CHAR::Char charCurr = player.charCurr != CHAR::BIRD2 ? player.charCurr : player.charOrig;
		const ::jjANIMATION@ anim = jjAnimations[
			charCurr == CHAR::SPAZ ? (powerup ? (::jjAnimSets[ANIM::AMMO] + 19) : (::jjAnimSets[ANIM::PICKUPS] + 30)) :
			(::jjIsTSF && charCurr == CHAR::LORI) ? (powerup ? (::jjAnimSets[ANIM::PLUS_AMMO] + 6) : (::jjAnimSets[ANIM::PLUS_AMMO] + 5)) :
			(powerup ? (::jjAnimSets[ANIM::AMMO] + 18) : (::jjAnimSets[ANIM::PICKUPS] + 29))
		];
		int x, y;
		STRING::Size font;
		::string text;
		if (::jjSubscreenWidth > 400) {
			x = ::jjSubscreenWidth - 88;
			y = ::jjSubscreenHeight - 14;
			font = STRING::MEDIUM;
		} else {
			x = ::jjSubscreenWidth - 48;
			y = ::jjSubscreenHeight - 9;
			font = STRING::SMALL;
		}
		canvas.drawSpriteFromCurFrame(x, y, anim + (::jjGameTicks >> 2) % anim.frameCount, mode: SPRITE::MAPPING, param: fur.MappingID);
		const ::jjWEAPON@ weapon = ::jjWeapons[WEAPON::BLASTER];
		if (weapon.infinite || weapon.replacedByShield && player.shieldTime > 0) {
			text = "x^";
		} else {
			const int ammo = player.ammo[WEAPON::BLASTER];
			text = "x" + (ammo > 0 ? ammo : 0);
		}
		canvas.drawString(x + 8, y, text, font);
	}
	return true;
}