Source

loaders/ObjectLoader.js

/**
* @class ObjectDefinition
* @category Definitions
* @hideconstructor
*/
export class ObjectDefinition {
	/** 
	* The ID of this Object
	* @type {number} 
	*/
	id;

	/**
	 * Object types
	 * @type {Array} 
	 */
	objectTypes = [];

	/**
	 * The models that compose the NPC
	 * @type {Array} 
	 */
	objectModels = [];
	
	/**
	 * Name of the Object
	 * @type {string} 
	 */
	name;

	/**
	 * Tile size X
	 * @type {number} 
	 */
	sizeX = 1;

	/**
	 * Tile size Y
	 * @type {number} 
	 */
	sizeY = 1;

	/**
	 * Interact Type. Loader only ever sets it to 0 or 1. Defaults value is 2
	 * @type {number} 
	 */
	interactType = 2;

	/**
	 * Blocks projectiles such as arrows and spells
	 * @type {boolean} 
	 */
	blocksProjectile = true;

	/**
	 * I think this allows other objects to be placed on the same tile
	 * @type {number} 
	 */
	wallOrDoor = -1;

	/** @type {number} */
	contouredGround;

	/**
	 * Merge normals with objects nearby
	 * @type {boolean} 
	 */
	mergeNormals = false;

	/** @type {boolean} */
	aBool2111 = false;

	/**
	 * Default animation
	 * @type {number} 
	 */
	animationID = -1;

	/** @type {number} */
	decorDisplacement = 16;
	
	/**
	 * Number from 0 to 255. Overrides Object model's ambient lighting 
	 * @type {Byte} 
	 */
	ambient = 0;

	/**
	 * Number from 0 to 255. Overrides Object model's contrast 
	 * @type {Byte} 
	 */
	contrast = 0;

	/** @type {Array} */
	actions = [];

	/**
	 * Color values to find to be replaced for this Object 
	 * @type {Array} 
	 */
	recolorToFind = [];

	/**
	 * What the color values will be replaced with 
	 * @type {Array} 
	 */
	recolorToReplace;

	/**
	 * Textures to find to be replaced for this Object 
	 * @type {Array} 
	 */
	retextureToFind;

	/**
	 * What the texture will be replaced with 
	 * @type {Array} 
	 */
	textureToReplace;

	/** @type {number} */
	category;

	/** @type {boolean} */
	rotated = false;
	
	/** @type {boolean} */
	shadow = true;

	/**
	 * Model size X
	 * @type {number} 
	 */
	modelSizeX = 128;

	/**
	 * Model size height
	 * @type {number} 
	 */
	modelSizeHeight = 128;

	/**
	 * Model size Y
	 * @type {number} 
	 */
	modelSizeY = 128;

	/** @type {number} */
	mapSceneID = -1;

	/** @type {number} */
	blockingMask = 0;

	/** @type {number} */
	offsetX = 0;

	/** @type {number} */
	offsetHeight = 0;

	/** @type {number} */
	offsetY = 0;

	/** @type {boolean} */
	obstructsGround = false;
	
	/** @type {number} */
	hollow = false;
	
	/** @type {number} */
	supportsItems = -1;

	/** @type {number} */
	varbitID = -1;

	/** @type {number} */
	varpID = -1;
	
	/** @type {Array} */
	configChangeDest = [];
	
	/** @type {number} */
	ambientSoundId = -1;
	
	/** @type {number} */
	ambientSoundDistance = 0;
	
	/** @type {number} */
	ambientSoundChangeTicksMin = 0;
	
	/** @type {number} */
	ambientSoundChangeTicksMax = 0;
	
	/** @type {Array<number>} */
	ambientSoundIds = [];
	
	/** @type {number} */
	mapAreaId = -1;
	
	/** @type {boolean} */
	randomizeAnimStart;
	
	/** @type {Object} */
	params;

	constructor() {
	}
}
export default class ObjectLoader {

	load(bytes, id) {
		//console.log(id, bytes.length);
		let def = new ObjectDefinition();
		def.id = id;
		let dataview = new DataView(bytes.buffer);
		do {
			var opcode = dataview.readUint8();
			this.handleOpcode(def, opcode, dataview);
		} while (opcode != 0);

		return def;
	}

	handleOpcode(def, opcode, dataview) {
		//console.log(opcode);
		if (opcode == 0)
			return;

		if (opcode == 1) {
			var length = dataview.readUint8();
			if (length > 0) {
				def.objectTypes = [];
				def.objectModels = [];

				for (var index = 0; index < length; ++index) {
					def.objectModels.push(dataview.readUint16());
					def.objectTypes.push(dataview.readUint8());
				}

			}
		}
		else if (opcode == 2) {
			def.name = dataview.readString();
		}
		else if (opcode == 5) {
			var length = dataview.readUint8();
			if (length > 0) {
				def.objectTypes = null;
				def.objectModels = [];

				for (var index = 0; index < length; ++index) {
					def.objectModels.push(dataview.readUint16());
				}
			}
		}
		else if (opcode == 14) {
			def.sizeX = dataview.readUint8();
		}
		else if (opcode == 15) {
			def.sizeY = dataview.readUint8();
		}
		else if (opcode == 17) {
			def.interactType = 0;
			def.blocksProjectile = false;
		}
		else if (opcode == 18) {
			def.blocksProjectile = false;
		}
		else if (opcode == 19) {
			def.wallOrDoor = dataview.readUint8();
		}
		else if (opcode == 21) {
			def.contouredGround = 0;
		}
		else if (opcode == 22) {
			def.mergeNormals = true;
		}
		else if (opcode == 23) {
			def.aBool2111 = true;
		}
		else if (opcode == 24) {
			def.animationID = dataview.readUint16();
			if (def.animationID == 0xFFFF) {
				def.animationID = -1;
			}
		}
		else if (opcode == 27) {
			def.interactType = 1;
		}
		else if (opcode == 28) {
			def.decorDisplacement = dataview.readUint8();
		}
		else if (opcode == 29) {
			def.ambient = dataview.readInt8();
		}
		else if (opcode == 39) {
			def.contrast = dataview.readInt8() * 25;
		}
		//30-34, 40, 41 are similar to NPCLoader, maybe make parent class for similar opcode loaders
		else if (opcode >= 30 && opcode < 35) {
			if (def.actions == undefined)
				def.actions = [];

			var readString = dataview.readString();
			def.actions[opcode - 30] = readString;

			//might be better to leave it as hidden (?)
			if (def.actions[opcode - 30] == "Hidden") {
				def.actions[opcode - 30] = undefined;
			}

		}
		else if (opcode == 40) {
			var length = dataview.readUint8();
			def.recolorToFind = [];
			def.recolorToReplace = [];

			for (index = 0; index < length; ++index) {
				def.recolorToFind.push(dataview.readUint16());
				def.recolorToReplace.push(dataview.readUint16());
			}
		}
		else if (opcode == 41) {
			var length = dataview.readUint8();
			def.retextureToFind = [];
			def.textureToReplace = [];

			for (index = 0; index < length; ++index) {
				def.retextureToFind.push(dataview.readUint16());
				def.textureToReplace.push(dataview.readUint16());
			}
		}
		else if (opcode == 61) {
			def.category = dataview.readUint16();
		}
		else if (opcode == 62) {
			def.rotated = true;
		}
		else if (opcode == 64) { 
			def.shadow = false;
		}
		else if (opcode == 65) {
			def.modelSizeX = dataview.readUint16();
		}
		else if (opcode == 66) {
			def.modelSizeHeight = dataview.readUint16();
		}
		else if (opcode == 67) {
			def.modelSizeY = dataview.readUint16();
		}
		else if (opcode == 68) {
			def.mapSceneID = dataview.readUint16();
		}
		else if (opcode == 69) {
			def.blockingMask = dataview.readInt8();
		}
		else if (opcode == 70) {
			def.offsetX = dataview.readUint16();
		}
		else if (opcode == 71) {
			def.offsetHeight = dataview.readUint16();
		}
		else if (opcode == 72) {
			def.offsetY = dataview.readUint16();
		}
		else if (opcode == 73) {
			def.obstructsGround = true;
		}
		else if (opcode == 74) {
			def.hollow = true;
		}
		else if (opcode == 75) {
			def.supportsItems = dataview.readUint8();
		}
		else if (opcode == 77) {
			var varpID = dataview.readUint16();
			if (varpID == 0xFFFF) {
				varpID = -1;
			}
			def.varbitID = varpID;

			var configId = dataview.readUint16();
			if (configId == 0xFFFF) {
				configId = -1;
			}
			def.varpID = configId;

			var length = dataview.readUint8();
			def.configChangeDest = [];

			for (var index = 0; index <= length; ++index) {
				def.configChangeDest.push(dataview.readUint16());
				if (0xFFFF == def.configChangeDest[index]) {
					def.configChangeDest[index] = -1;
				}
			}
			def.configChangeDest.push(-1);
		}
		else if (opcode == 78) {
			def.ambientSoundId = dataview.readUint16();
			def.ambientSoundDistance = dataview.readUint8();
		}
		else if (opcode == 79) {
			def.ambientSoundChangeTicksMin = dataview.readUint16();
			def.ambientSoundChangeTicksMax = dataview.readUint16();
			def.ambientSoundDistance = dataview.readUint8();
			var length = dataview.readUint8();
			let ambientSoundIds = [];

			for (var index = 0; index < length; ++index) {
				ambientSoundIds.push(dataview.readUint16());
			}

			def.ambientSoundIds = ambientSoundIds;
		}
		else if (opcode == 81) {
			def.contouredGround = dataview.readUint8() * 256;
		}
		else if (opcode == 82) {
			def.mapAreaId = dataview.readUint16();
		}
		else if (opcode == 89) {
			def.randomizeAnimStart = true;
		}
		else if (opcode == 92) {
			var varpID = dataview.readUint16();
			if (varpID == 0xFFFF) {
				varpID = -1;
			}
			def.varbitID = varpID;

			var configId = dataview.readUint16();
			if (configId == 0xFFFF) {
				configId = -1;
			}
			def.varpID = configId;


			var varValue = dataview.readUint16();
			if (varValue == 0xFFFF) {
				varValue = -1;
			}

			var length = dataview.readUint8();
			def.configChangeDest = [];

			for (var index = 0; index <= length; ++index) {
				def.configChangeDest.push(dataview.readUint16());
				if (0xFFFF == def.configChangeDest[index]) {
					def.configChangeDest[index] = -1;
				}
			}
			def.configChangeDest.push(varValue);
		}
		else if (opcode == 249) {
			var length = dataview.readUint8();
			def.params = {};

			for (var i = 0; i < length; i++) {
				var isString = dataview.readUint8() == 1;

				var key = dataview.readInt24();
				var value;

				if (isString) {
					value = dataview.readString();
				}
				else {
					value = dataview.readInt32()
				}

				def.params[key] = value;
			}
		}
	}
}