Source

loaders/NpcLoader.js

/**
* @class NpcDefinition
* @category Definitions
* @hideconstructor
*/
class NpcDefinition {
	/** 
	 * The ID of this NPC
	 * @type {number} 
	 */
	id;

	/**
	 * The models that compose the NPC
	 * @type {Array} 
	 */
	models = [];

	/**
	 * Name of the NPC
	 * @type {string} 
	 */
	name;

	/**
	 * Tile size
	 * @type {number} 
	 */
	size;

	/**
	 * Idle animation
	 * @type {number} 
	 */
	standingAnimation = -1;

	/**
	 * Walking animation
	 * @type {number} 
	 */
	walkingAnimation = -1;

	/** @type {number} */
	rotateLeftAnimation = -1;
	
	/** @type {number} */
	rotateRightAnimation = -1;
	
	/** @type {number} */
	rotate180Animation = -1;
	
	/** @type {number} */
	rotate90LeftAnimation = -1;
	
	/** @type {number} */
	rotate90RightAnimation = -1;
	
	/** @type {number} */
	category;
	
	/** @type {Array} */
	actions = [];
	
	/**
	 * Color values to find to be replaced for this NPC 
	 * @type {Array} 
	 */
	recolorToFind = [];
	
	/**
	 * What the color values will be replaced with 
	 * @type {Array} 
	 */
	recolorToReplace = [];
	
	/**
	 * Textures to find to be replaced for this NPC 
	 * @type {Array} 
	 */
	retextureToFind = [];
	
	/**
	 * What the texture will be replaced with 
	 * @type {Array} 
	 */
	retextureToReplace = [];
	
	/**
	 * The models that will compose this NPC's chathead 
	 * @type {Array} 
	 */
	chatheadModels = [];
	
	/**
	 * If this NPC will show on the minimap 
	 * @type {boolean} 
	 */
	isMinimapVisible = true;
	
	/**
	 * This NPC's combat level 
	 * @type {number} 
	 */
	combatLevel = -1;
	
	/** @type {number} */
	heightScale;
	
	/** @type {boolean} */
	hasRenderPriority;
	
	/**
	 * Number from 0 to 255. Overrides NPC model's ambient lighting 
	 * @type {Byte} 
	 */
	ambient;
	
	/**
	 * Number from 0 to 255. Overrides NPC model's contrast 
	 * @type {Byte} 
	 */
	contrast;
	
	/** @type {Array} */
	headIconArchiveIds = [];
	
	/** @type {Array} */
	headIconSpriteIndex = [];
	
	/** @type {number} */
	rotationSpeed = 32;
	
	/** @type {number} */
	varbitId = -1;
	
	/** @type {number} */
	varpIndex = -1;
	
	/** @type {Array} */
	configs = [];
	
	/** @type {boolean} */
	isInteractable = true;
	
	/** @type {boolean} */
	rotationFlag = true;
	
	/** @type {boolean} */
	isPet;
	
	/** @type {number} */
	runAnimation = -1;
	
	/** @type {number} */
	runRotate180Animation = -1;
	
	/** @type {number} */
	runRotateLeftAnimation = -1;
	
	/** @type {number} */
	runRotateRightAnimation = -1;
	
	/** @type {number} */
	crawlAnimation = -1;
	
	/** @type {number} */
	crawlRotate180Animation = -1;
	
	/** @type {number} */
	crawlRotateLeftAnimation = -1;
	
	/** @type {number} */
	crawlRotateRightAnimation = -1;
	
	/** @type {Array} */
	params = [];
}

export { NpcDefinition };
export default class NpcLoader {

	load(bytes, id) {
		let def = new NpcDefinition();
		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) {
		var length;
		var index;
		if (opcode == 1) {
			length = dataview.readUint8();
			def.models = [];

			for (index = 0; index < length; ++index) {
				def.models.push(dataview.readUint16());
			}
		} else if (opcode == 2) {
			var name = dataview.readString();
			def.name = name;
		}
		else if (opcode == 12) {
			def.size = dataview.readUint8();
		}
		else if (opcode == 13) {
			def.standingAnimation = dataview.readUint16();
		}
		else if (opcode == 14) {
			def.walkingAnimation = dataview.readUint16();
		}
		else if (opcode == 15) {
			def.rotateLeftAnimation = dataview.readUint16();
		}
		else if (opcode == 16) {
			def.rotateRightAnimation = dataview.readUint16();
		}
		else if (opcode == 17) {
			def.walkingAnimation = dataview.readUint16();
			def.rotate180Animation = dataview.readUint16();
			def.rotate90RightAnimation = dataview.readUint16();
			def.rotate90LeftAnimation = dataview.readUint16();
		}
		else if (opcode == 18) {
			def.category = dataview.readUint16();
		}
		else if (opcode >= 30 && opcode < 35) {
			if (def.actions == undefined)
				def.actions = [];

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

			if (def.actions[opcode - 30] == "Hidden") {
				def.actions[opcode - 30] = undefined;
			}
		}
		else if (opcode == 40) {
			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) {
			length = dataview.readUint8();
			def.retextureToFind = [];
			def.retextureToReplace = [];

			for (index = 0; index < length; ++index) {
				def.retextureToFind.push(dataview.readUint16());
				def.retextureToReplace.push(dataview.readUint16());
			}

		}
		else if (opcode == 60) {
			length = dataview.readUint8();
			def.chatheadModels = [];

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

		}
		else if (opcode == 93) {
			def.isMinimapVisible = false;
		}
		else if (opcode == 95) {
			def.combatLevel = dataview.readUint16();
		}
		else if (opcode == 97) {
			this.widthScale = dataview.readUint16();
		}
		else if (opcode == 98) {
			def.heightScale = dataview.readUint16();
		}
		else if (opcode == 99) {
			def.hasRenderPriority = true;
		}
		else if (opcode == 100) {
			def.ambient = dataview.readInt8();
		}
		else if (opcode == 101) {
			def.contrast = dataview.readInt8();
		}
		else if (opcode == 102) {
			//def.headIcon = dataview.readUint16(); //before rev210
			let bitfield = dataview.readUint8();
			let len = 0;
			for (let var5 = bitfield; var5 != 0; var5 >>= 1) {
				++len;
			}

			def.headIconArchiveIds = [];
			def.headIconSpriteIndex = [];

			for (let i = 0; i < len; i++) {
				if ((bitfield & 1 << i) == 0) {
					def.headIconArchiveIds.push(-1);
					def.headIconSpriteIndex.push(1);
				}
				else {
					def.headIconArchiveIds.push(dataview.readBigSmart2());
					def.headIconSpriteIndex.push(dataview.readUnsignedShortSmartMinusOne());
				}
			}
		}
		else if (opcode == 103) {
			def.rotationSpeed = dataview.readUint16();
		}
		else if (opcode == 106) {
			def.varbitId = dataview.readUint16();
			if (def.varbitId == 65535) {
				def.varbitId = -1;
			}

			def.varpIndex = dataview.readUint16();

			if (def.varpIndex == 65535) {
				def.varpIndex = -1;
			}

			length = dataview.readUint8();

			def.configs = [];

			for (index = 0; index <= length; ++index) {
				def.configs[index] = dataview.readUint16();

				if (def.configs[index] == '\uffff') {
					def.configs[index] = -1;
				}
			}

			def.configs[length + 1] = -1;

		}
		else if (opcode == 107) {
			def.isInteractable = false;
		}
		else if (opcode == 109) {
			def.rotationFlag = false;
		}
		else if (opcode == 111) {
			def.isPet = true;
		}
		else if (opcode == 114) {
			def.runAnimation = dataview.readUint16();
		}
		else if (opcode == 115) {
			def.runAnimation = dataview.readUint16();
			def.runRotate180Animation = dataview.readUint16();
			def.runRotateLeftAnimation = dataview.readUint16();
			def.runRotateRightAnimation = dataview.readUint16();
		}
		else if (opcode == 116) {
			def.crawlAnimation = dataview.readUint16();
		}
		else if (opcode == 117) {
			def.crawlAnimation = dataview.readUint16();
			def.crawlRotate180Animation = dataview.readUint16();
			def.crawlRotateLeftAnimation = dataview.readUint16();
			def.crawlRotateRightAnimation = dataview.readUint16();
		}
		else if (opcode == 118) {
			def.varbitId = dataview.readUint16();

			if (def.varbitId == 65535) {
				def.varbitId = -1;
			}

			def.varpIndex = dataview.readUint16();

			if (def.varpIndex == 65535) {
				def.varpIndex = -1;
			}

			var varVal = dataview.readUint16();

			if (varVal == 0xFFFF) {
				varVal = -1;
			}

			length = dataview.readUint8();
			def.configs = [];

			for (index = 0; index <= length; ++index) {
				var value = dataview.readUint16();

				if (def.configs[index] == '\uffff') {
					def.configs.push(-1);
				} else {
					def.configs.push(value);
				}
			}

			def.configs.push(varVal);
		}
		else if (opcode == 249) {
			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;
			}
		}
	}
}