Downloads containing player_collision_detector.mut

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Player-to-Player Collision... froducish Mutator N/A Download file

File preview

#pragma name "Player-to-Player Collision Detector"
#pragma require "player_collision_detector_interface.asc"
#include "player_collision_detector_interface.asc"

array<jjBEHAVIOR> backedWeaponBehaviors(OBJECT::TNT + 1);

bool active = false;
array<int> playerForces(32, 0);
array<COLLIDE::HOOK@> onCollides(32);

void onCollide(jjPLAYER@ victim, jjPLAYER@ collider, COLLIDE::Collide collision) {
	for (uint i = 0; i < onCollides.length; i++) {
		COLLIDE::HOOK@ hook = onCollides[i];
		if (hook !is null) {
			hook(victim, collider, collision);
		}
	}
}

class CollisionDetector : COLLIDE::DetectorI {
	void setHook(COLLIDE::HOOK@ hook = onCollide, uint moduleID = jjScriptModuleID) {
		if (moduleID >= onCollides.length()) {
			onCollides.resize(moduleID * 2);
		}
		@onCollides[moduleID] = hook;
	}
	
	void unsetHook(uint moduleID = jjScriptModuleID) {
		@onCollides[moduleID] = null;
	}
	
	void startDetecting() {
		affectAllWeapons(makeDetectable);
		sendOfTypeExcept(PACKET_ON, jjLocalPlayers[0].playerID);
		active = true;
	}
	
	void stopDetecting() {
		affectAllWeapons(makeUndetectable);
		sendOfTypeExcept(PACKET_OFF, jjLocalPlayers[0].playerID);
		active = false;
	}
	
	string getVersion() const {
		return "1.3";
	}
};

CollisionDetector publicInstance;
CollisionDetector@ onGetPublicInterface() {
	return publicInstance;
}

enum packetType { 
	PACKET_FORWARD,
	PACKET_COLLIDE,
	PACKET_ON,
	PACKET_OFF
};

class BulletDetector : jjBEHAVIORINTERFACE {
	private jjBEHAVIOR base;
	private bool done;
	
	BulletDetector(const jjBEHAVIOR &in base) {
		this.base = base;
		this.done = false;
	}
	
	void onBehave(jjOBJ@ obj) {
		obj.behave(base);
		jjPLAYER@ me = jjLocalPlayers[0];
		if (!done && obj.creatorType == CREATOR::PLAYER && int(obj.creatorID) != me.playerID) {
			float x = me.xPos - obj.xPos;
			float y = me.yPos - obj.yPos;
			uint curAnim = me.curAnim - jjAnimSets[me.setID].firstAnim;
			if (((x*x)+(y*y)) <= (20 * 20) * 2 && (!me.isEnemy(jjPlayers[obj.creatorID]) || curAnim == RABBIT::HURT || curAnim == RABBIT::DIE)) {
				done = true;
				sendCollision(obj.creatorID, me.playerID, ((obj.var[6] & (1 << 3)) >>> 3) == 1 ? COLLIDE::BULLETPU : COLLIDE::BULLET);
			}
		}
	}
	
	bool onIsRFBullet(jjOBJ@ obj) {
		return base == BEHAVIOR::RFBULLET;
	}
}

void onPlayerInput(jjPLAYER@ player) {
	if (active) {
		forwardPacket(player.playerID, player.getObjectHitForce());
	}
}

void onPlayer(jjPLAYER@ player) {
	if (active) {
		for (int i = 0; i < 32; i++) {
			jjPLAYER@ cand = jjPlayers[i];
			float x = cand.xPos - player.xPos;
			float y = cand.yPos - player.yPos;
			if (cand.isInGame && !cand.isLocal) {
				if (playerForces[i] != 0 && ((x*x)+(y*y)) <= (32 * 32)) {
					COLLIDE::Collide collision = playerForces[i] < 0 ? COLLIDE::SPECIALMOVE : COLLIDE::STOMPORRUSH;
					sendCollision(i, player.playerID, collision);
				} else if (((x*x)+(y*y)) <= (24 * 24)) {
					sendCollision(i, player.playerID, COLLIDE::BUMP);
				}
			}
		}
	}
}

void initDetector(int8 origin, int force) {
	playerForces[origin] = force;
	// Even though Gun8 should be already accounted for when startDetecting() is called, for some strange reason obj.behavior remains unchanged. Even stranger
	// that it takes effect only if Gun8 is in fireball mode. Can't explain that. Below is a quick hack needed to specifically account for it.
	if (!jjAllowsFireball) {
		for (int i = 1; i < jjObjectCount; i++) {
			jjOBJ @obj = jjObjects[i];
			if (obj.isActive && (obj.eventID == OBJECT::BULLET) && obj.creatorType == CREATOR::PLAYER && int(obj.creatorID) == origin && obj.points != 0xFFFF) {
				obj.points = 0xFFFF;
				obj.behavior = BulletDetector(obj.behavior);
			}
		}
	}
}

void onReceive(jjSTREAM &in packet, int clientID) {
	uint8 type;
	packet.pop(type);
	if (jjIsServer) {
		switch (type) {
			case PACKET_FORWARD: {
				int8 origin;
				packet.pop(origin);
				if (jjPlayers[origin].clientID == clientID) {
					int force;
					packet.pop(force);
					forwardPacket(origin, force);
					initDetector(origin, force); // This specifically deals with the non-server player vs server player (this) case.
				}
			}
			break;
			case PACKET_COLLIDE: {
				int8 victim;
				packet.pop(victim);
				if (jjPlayers[victim].clientID == clientID) {
					int8 origin;
					int collision;
					packet.pop(origin);
					packet.pop(collision);
					sendCollision(origin, victim, COLLIDE::Collide(collision));
				}
			}
			break;
			case PACKET_ON: {
				int8 playerID;
				packet.pop(playerID);
				if (jjPlayers[playerID].clientID == clientID) {
					affectAllWeapons(makeDetectable);
					active = true;
					sendOfTypeExcept(PACKET_ON, playerID);
				}
			}
			break;
			case PACKET_OFF: {
				int8 playerID;
				packet.pop(playerID);
				if (jjPlayers[playerID].clientID == clientID) {
					affectAllWeapons(makeUndetectable);
					active = false;
					sendOfTypeExcept(PACKET_OFF, playerID);
				}
			}
			break;
		}
	} else {
		switch (type) {
			case PACKET_FORWARD: {
				int8 origin;
				int force;
				packet.pop(origin);
				packet.pop(force);
				initDetector(origin, force);
			}
			break;
			case PACKET_COLLIDE: {
				int8 victim, origin;
				int collision;
				packet.pop(victim);
				packet.pop(origin);
				packet.pop(collision);
				onCollide(jjPlayers[victim], jjPlayers[origin], COLLIDE::Collide(collision));
			}
			break;
			case PACKET_ON: {
				affectAllWeapons(makeDetectable);
				active = true;
			}
			break;
			case PACKET_OFF: {
				affectAllWeapons(makeUndetectable);
				active = false;
			}
			break;
		}
	}
}

void forwardPacket(int8 playerID, int force) {
	jjSTREAM packet;
	packet.push(uint8(PACKET_FORWARD));
	packet.push(playerID);
	packet.push(force);
	jjSendPacket(packet, -jjPlayers[playerID].clientID);
}

void sendCollision(int8 attackID, int8 victimID, COLLIDE::Collide collision) {
	jjSTREAM packet;
	packet.push(uint8(PACKET_COLLIDE));
	packet.push(victimID);
	packet.push(attackID);
	packet.push(int(collision));
	jjSendPacket(packet, -jjPlayers[victimID].clientID);
	onCollide(jjPlayers[victimID], jjPlayers[attackID], collision);
}

void sendOfTypeExcept(packetType type, int8 playerID) {
	jjSTREAM packet;
	packet.push(uint8(type));
	packet.push(playerID);
	jjSendPacket(packet, -jjPlayers[playerID].clientID);
}

void makeDetectable(OBJECT::Object weapon) {
	jjBEHAVIOR behavior = jjObjectPresets[weapon].behavior;
	if (!active) {
		backedWeaponBehaviors[weapon] = behavior;
		jjObjectPresets[weapon].behavior = function(obj) {
			obj.behavior = BulletDetector(backedWeaponBehaviors[obj.eventID]);
		};
	}
}

void makeUndetectable(OBJECT::Object weapon) {
	jjBEHAVIOR behavior = jjObjectPresets[weapon].behavior;
	if (active) {
		jjObjectPresets[weapon].behavior = backedWeaponBehaviors[weapon];
	}
}

funcdef void VOIDFUNCOBJSCONST(OBJECT::Object);
void affectAllWeapons(VOIDFUNCOBJSCONST@ func) {
	for (int i = 0; i < OBJECT::SMOKERING; i++) {
		func(OBJECT::Object(i));
	}
	func(OBJECT::TNT);
}