Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
![]() |
Player-to-Player Collision... | froducish | Mutator | N/A | ![]() |
#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);
array<HANDLING::Player> backedWeaponPlayerHandlings(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.1";
}
};
CollisionDetector publicInstance;
CollisionDetector@ onGetPublicInterface() {
return publicInstance;
}
enum packetType {
PACKET_FORWARD,
PACKET_COLLIDE,
PACKET_ON,
PACKET_OFF
};
class BulletDetector : jjBEHAVIORINTERFACE {
jjBEHAVIOR base;
BulletDetector(jjBEHAVIOR base) {
this.base = base;
}
void onBehave(jjOBJ@ obj) {
obj.behave(base);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ victim, int force) {
obj.playerHandling = victim.isEnemy(jjPlayers[obj.creatorID]) ? HANDLING::ENEMYBULLET : HANDLING::PLAYERBULLET;
if (int(obj.creatorID) != victim.playerID) {
sendCollision(obj.creatorID, victim.playerID, ((obj.var[6] & (1 << 3)) >>> 3) == 1 ? COLLIDE::BULLETPU : COLLIDE::BULLET);
}
return true;
}
}
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 alreadyHandlesCollision on its behavior
// returns false. Even stranger that it returns true only if Gun8 is in fireball mode. Can't explain that. Below's 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 && !alreadyHandlesCollision(obj.behavior)) {
obj.scriptedCollisions = true;
obj.playerHandling = HANDLING::SPECIAL;
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);
}
bool alreadyHandlesCollision(jjBEHAVIOR behavior) {
jjBEHAVIORINTERFACE@ implementsInterface = cast<jjBEHAVIORINTERFACE>(behavior);
if (implementsInterface !is null) {
BulletDetector@ handlesCollision = cast<BulletDetector>(implementsInterface);
return (handlesCollision !is null);
}
return false;
}
void makeDetectable(OBJECT::Object weapon) {
jjBEHAVIOR behavior = jjObjectPresets[weapon].behavior;
if (!alreadyHandlesCollision(behavior)) {
backedWeaponBehaviors[weapon] = behavior;
backedWeaponPlayerHandlings[weapon] = jjObjectPresets[weapon].playerHandling;
jjObjectPresets[weapon].scriptedCollisions = true;
jjObjectPresets[weapon].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[weapon].behavior = BulletDetector(backedWeaponBehaviors[weapon]);
}
}
void makeUndetectable(OBJECT::Object weapon) {
jjBEHAVIOR behavior = jjObjectPresets[weapon].behavior;
if (alreadyHandlesCollision(behavior)) {
jjObjectPresets[weapon].scriptedCollisions = false;
jjObjectPresets[weapon].playerHandling = backedWeaponPlayerHandlings[weapon];
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);
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.