Downloads containing TrueFur.html

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

File preview

<!doctype html>
<html>
<head>
<meta charset="windows-1252">
<title>Fur color designer for TrueFur.mut</title>
<style>
	main {
		max-width: 640px;
		margin: 0 auto;
	}
	#preview {
		margin-top: 0.5em;
		padding-top: calc(100% * 103/120);
		background-size: cover;
		image-rendering: pixelated;
	}
	button {
		width: 20%;
		display: inline-block;
	}
	ul {
		list-style: none;
		padding-left: 0;
	}
	#gradients > li {
		width: 50%;
		display: inline-block;
	}
	ul.gradient {
		width: 100%;
	}
	ul.gradient > li {
		display: inline-block;
		width: 11%;
		height: 0;
		padding-top: calc(11% - 6px);
		position: relative;
		box-sizing: border-box;
	}
	ul.gradient > li + li {
		border: 3px inset;
		position: relative;
	}
	li.enabled {
		border-style: outset !important;
	}
	ul.gradient > li + li:not(.enabled):after {
		content: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAMAAACn6Q83AAAAB3RJTUUH5wgeECEaKvilHgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAAAbUExURZPC6AsHB1dLP3drV5uLc7uvk9vPr//z0y8jHwIMCFsAAAABdFJOUwBA5thmAAAAS0lEQVR42jWOCwoAQQhC0+xz/xPvVKwgPAQtMzM82QqRpWWoFOnDyCAvRqm5KVreYxhc8vHg60flYiWZ9afcNVBBD3EmOCXeOdwTH2dbAXTdwzryAAAAAElFTkSuQmCC");
		position: absolute;
		bottom: -3px;
		right: -3px;
		mix-blend-mode: luminosity;
	}
	li.enabled input[type=color] {
		opacity: 0;
		visibility: visible;
		cursor: pointer;
	}
	select {
		position: absolute;
		top: -3px; bottom: 3px;
		width: 100%;
	}
	input[type=color] {
		position: absolute;
		width: 100%;
		top: 0; left: 0; bottom: 0; right: 0;
		box-sizing: border-box;
		visibility: hidden;
	}
	input[type=file] {
		display: none;
	}
	#buttons {
		position: sticky;
		top: 0;
	}
</style>
</head>
<body
><main
	><div id="buttons"
		><input type="file" id="hiddenload" accept=".asdat"
		><button    id="butload">Load</button
		><button class="butsave">Save Player 1</button
		><button class="butsave">Save Player 2</button
		><button class="butsave">Save Player 3</button
		><button class="butsave">Save Player 4</button
	></div
	><div id="preview"></div
	><ul id="gradients"
	></ul
></main>
<script>
const FILEVERSION = 0;

window.addEventListener('load', function() {
	const CanonGradients = {
		Green: [199,255,0, 147,223,0, 107,191,0, 71,163,0, 43,131,0, 19,103,0, 7,55,0, 0,11,0],
		Red: [255,0,0, 227,0,0, 199,0,0, 171,0,0, 143,0,0, 115,0,0, 63,0,0, 11,0,0],
		Blue: [187,227,255, 123,199,255, 59,171,255, 0,139,255, 0,107,203, 0,79,151, 0,47,79, 0,7,11],
		Orange: [255,255,0, 255,199,0, 255,147,0, 255,95,0, 203,55,0, 155,27,0, 83,7,0, 11,0,0],
		Pink: [251,139,183, 247,91,151, 243,43,123, 239,0,99, 191,0,75, 147,0,55, 99,0,35, 55,0,19],
		Yellow: [255,255,0, 240,235,0, 230,210,0, 219,195,0, 187,147,0, 155,107,0, 83,55,0, 11,7,0],
		Brown: [255,243,211, 219,207,175, 187,175,147, 155,139,115, 119,107,87, 87,75,63, 47,35,31, 11,7,7],
		Silver: [211,231,255, 171,195,219, 139,159,187, 107,127,155, 75,95,119, 51,63,87, 27,31,47, 7,7,11],
		Greenblue: [0,255,163, 0,227,127, 7,199,95, 7,171,67, 11,143,47, 11,119,31, 0,63,7, 0,11,0],
		Purple: [231,119,255, 231,71,239, 223,31,207, 207,0,163, 163,0,127, 119,0,91, 63,0,43, 11,0,7],
		AltRed: [255,0,0, 219,0,19, 183,0,35, 147,0,39, 115,0,43, 79,0,35, 43,0,23, 11,0,7],
		AltYellow: [255,255,0, 231,231,0, 219,219,0, 199,199,0, 155,155,0, 115,115,0, 75,75,0, 35,35,0],
		AltGreenblue: [0,255,211, 0,227,179, 0,199,151, 0,171,127, 0,135,103, 0,103,75, 0,55,39, 0,11,7],
		AltPurple: [255,99,255, 215,67,223, 179,43,195, 147,23,167, 111,7,139, 83,0,111, 47,0,63, 11,0,15],
		AltPurple2: [219,127,255, 203,63,255, 187,0,255, 163,0,211, 139,0,171, 107,0,127, 51,0,67, 7,0,11],
		JustinYellow: [255,255,4, 200,200,0, 155,156,0, 130,130,0, 116,116,0, 63,62,0, 47,35,0, 11,11,0],
		JustinBlue: [0,138,255, 0,107,204, 0,93,177, 0,79,151, 0,47,98, 0,39,71, 0,23,47, 7,6,12],
		JustinBlue2: [7,251,248, 6,203,248, 0,152,239, 0,101,231, 0,48,224, 4,0,224, 0,23,148, 0,43,72],
		JustinPink: [228,179,255, 219,126,255, 247,112,163, 241,51,131, 240,0,100, 199,0,0, 143,0,0, 63,0,0],
		JustinOrange: [255,199,4, 254,148,4, 254,95,4, 255,2,0, 239,0,100, 191,0,76, 146,0,56, 79,0,11],
		JustinBlack: [109,108,109, 63,63,62, 33,32,33, 16,16,16, 8,9,8, 7,7,7, 3,4,4, 2,3,3],
		JustinMagenta: [236,64,127, 216,30,127, 193,0,127, 166,0,104, 141,0,85, 111,0,63, 56,0,33, 9,3,5],
		MouseguyDeadscrap: [204,102,81, 178,63,55, 153,30,30, 127,12,22, 102,0,17, 76,0,19, 51,0,16, 25,0,10]
	};

	let recolor = new Uint8Array(3 * (10 * 8 + 1));
	recolor[0] = recolor[1] = recolor[2] = 255; //white for Lori's eyes
	
	let gradients = document.getElementById("gradients");
	let addText = "<li><ul class='gradient'><li><select>";
	addText += "<option>Fill</option>";
	addText += "<option>1-color Gradient</option>";
	addText += "<option>2-color Gradient</option>";
	addText += "<option>3-color Gradient</option>";
	addText += "<option>Fully Custom</option>";
	for (let key in CanonGradients)
		addText += "<option>" + key + "</option>";
	addText += "</select></li>";
	for (let c = 0; c < 8; ++c)
		addText += "<li><input type='color' /></li>";
	addText += "</ul></li>";
	for (let i = 0; i < 10; ++i)
		gradients.insertAdjacentHTML("beforeend", addText);
	let selects = document.getElementsByTagName("select");
	let rows = document.getElementsByClassName("gradient");
	for (let i = 0; i < 10; ++i) {
		let select = selects[i];
		let children = rows[i].childNodes;
		select.addEventListener("change", function(){
			let enableds;
			switch (this.selectedIndex) {
			case 0: //fill
				enableds = [true, false, false, false, false, false, false, false];
				break;
			case 1: //1-color gradient
				enableds = [false, false, true, false, false, false, false, false];
				break;
			case 2: //2-color gradient
				enableds = [false, false, true, false, false, true, false, false];
				break;
			case 3: //3-color gradient
				enableds = [true, false, true, false, false, true, false, false];
				break;
			case 4: //fully custom
				enableds = [true, true, true, true, true, true, true, true];
				break;
			default:
				enableds = [false, false, false, false, false, false, false, false];
				let channels = CanonGradients[this.value];
				for (let c = 0; c < 8; ++c) {
					const recolorOffset = (1 + c + i * 8) * 3;
					for (let cc = 0; cc < 3; ++cc)
						recolor[recolorOffset + cc] = channels[c * 3 + cc];
				}
				break;
			}
			for (let c = 0; c < 8; ++c)
				children[c + 1].className = enableds[c] ? "enabled" : "";
			generateGradient(i);
		});
		function generateGradient(gradientID) {
			let recolorOffset = (1 + gradientID * 8) * 3;
			const gradientType = selects[gradientID].selectedIndex;
			if (gradientType == 0) { //fill
				for (let c = 0; c < 24; c += 3)
					for (let cc = 0; cc < 3; ++cc)
						recolor[recolorOffset + c + cc] = recolor[recolorOffset + cc];
			} else if (gradientType < 4) { //partially defined gradient
				function multiplyColor(oldOffset, newOffset, factor) {
					oldOffset *= 3;
					newOffset *= 3;
					for (let cc = 0; cc < 3; ++cc) {
						let intensity = recolor[recolorOffset + oldOffset + cc];
						intensity = intensity * factor | 0;
						if (intensity) {
							if (intensity > 255)
								intensity = 255;
							else
								intensity |= 3;
						}
						recolor[recolorOffset + newOffset + cc] = intensity;
					}
				}
				if (gradientType == 3) { //3-color gradient, defines top color
					for (let cc = 0; cc < 3; ++cc)
						recolor[recolorOffset + 3 + cc] = (recolor[recolorOffset + 0 + cc] + recolor[recolorOffset + 6 + cc]) / 2 | 3; //color 1 is average of colors 0 and 2
				} else { //generate top color
					multiplyColor(2, 1, 1.2);
					multiplyColor(2, 0, 1.5);
				}
				if (gradientType == 1) { //1-color gradient, need to derive a lot of middle colors
					multiplyColor(2, 3, 0.8);
					multiplyColor(2, 4, 0.6);
					multiplyColor(2, 5, 0.4);
				} else { //colors 3 and 4 are averages of 2 and 5
					for (let cc = 0; cc < 3; ++cc) {
						const startColor = recolor[recolorOffset + 6 + cc];
						const step = (recolor[recolorOffset + 15 + cc] - startColor) / 3;
						recolor[recolorOffset + 9 + cc] = (startColor + step) | 3;
						recolor[recolorOffset + 12 + cc] = (startColor + step * 2) | 3;
					}
				}
				multiplyColor(5, 6, 0.525);
				multiplyColor(5, 7, 0.125);
			}
			for (let c = 0; c < 8; ++c) {
				const rgbstring = recolor[recolorOffset] + "," + recolor[recolorOffset + 1] + "," + recolor[recolorOffset + 2];
				children[c + 1].title = rgbstring;
				children[c + 1].style.background = children[c + 1].style.borderColor = "rgb(" + rgbstring + ")";
				children[c + 1].childNodes[0].value = "#" + recolor[recolorOffset].toString(16).padStart(2, '0') + recolor[recolorOffset + 1].toString(16).padStart(2, '0') + recolor[recolorOffset + 2].toString(16).padStart(2, '0');
				recolorOffset += 3;
			}
		}
		select.selectedIndex = i + 5;
		select.dispatchEvent(new Event("change"));
		for (let cell = 1; cell < 9; ++cell) {
			let li = children[cell];
			li.childNodes[0].addEventListener("input", function() {
				const r = parseInt(this.value.substr(1,2), 16);
				const g = parseInt(this.value.substr(3,2), 16);
				const b = parseInt(this.value.substr(5,2), 16);
				const recolorOffset = (1 + i * 8 + (cell - 1)) * 3;
				recolor[recolorOffset + 0] = r;
				recolor[recolorOffset + 1] = g;
				recolor[recolorOffset + 2] = b;
				generateGradient(i);
				draw();
			});
		}
	}
	
	const grayscale = new Image();
	grayscale.src = "data:image/png;base64,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";
	function draw() {
		const cnvs = document.createElement("canvas");
		const w = grayscale.width, h=grayscale.height;
		if (!h) {
			return setTimeout(draw, 500);
		}
		cnvs.width = w; cnvs.height = h;
		const ctx = cnvs.getContext("2d");
		ctx.drawImage(grayscale, 0,0);
		
		const imgdata = ctx.getImageData(0,0,w,h);
		const rgba = imgdata.data;
		
		for (let px=0,ct=w*h*4; px<ct; px+=4) {
			const brightness = rgba[px];
			if (brightness) //not color 0, which is transparent
				for (let ch = 0; ch < 3; ++ch) {
					const recolorOffset = (brightness - 15) * 3;
					rgba[px+ch] = recolor[recolorOffset + ch];
				}
		}
					
		ctx.putImageData(imgdata,0,0);
		document.getElementById("preview").style.backgroundImage = "url('" + cnvs.toDataURL() + "')";
	}
	draw();
	for (let i = 0; i < 10; ++i)
		selects[i].addEventListener("change", draw);
		
	let saves = document.getElementsByClassName("butsave");
	for (let i = 0; i < 4; ++i) {
		saves[i].addEventListener("click", function(){
			let output = new Uint8Array(1 + 10 + recolor.length);
			output[0] = 0; //version
			for (let i = 0; i < 10; ++i)
				output[i + 1] = selects[i].selectedIndex;
			output.set(recolor, 1 + 10);
			
			//https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server
			const element = document.createElement('a');
			element.setAttribute('href', window.URL.createObjectURL(new Blob([output])));
			element.setAttribute('download', "Fur" + (i + 1) + ".asdat");
			element.style.display = 'none';
			document.body.appendChild(element);
			element.click();
			document.body.removeChild(element);
		});
	}
	
	document.getElementById("butload").addEventListener("click", function() {
		document.getElementById('hiddenload').click();
	});
	document.getElementById('hiddenload').addEventListener('change', function(evt) {
		let f = evt.target.files[0];
		if (f) {
			if (f.name != "Fur1.asdat" && f.name != "Fur2.asdat" && f.name != "Fur3.asdat" && f.name != "Fur4.asdat") {
				alert("Invalid filename");
				return;
			}
			if (f.size != 1 + 10 + recolor.length) {
				alert("Invalid filesize");
				return;
			}
			
			const reader = new FileReader();
			reader.onload = function() {
				const result = new Uint8Array(reader.result);
				let idx = 0;
				if (result[idx++] > FILEVERSION) {
					alert("File saved in a later version of TrueFur.html, please update your local copy.");
					return;
				}
				for (let selectID = 0; selectID < 10; ++selectID)
					selects[selectID].selectedIndex = result[idx++];
				recolor = result.slice(idx);
				for (let selectID = 0; selectID < 10; ++selectID)
					selects[selectID].dispatchEvent(new Event("change"));
			};
			reader.readAsArrayBuffer(f);
		}
	}, false);
});
</script>
</body>