#pragma name "Teleport (by ak-47)"
const string COMMAND_POS = "!pos";
const string COMMAND_TP = "!tp ?(\\d\\d?)";
const string COMMAND_TP_RELOC = "!tp ?(\\d*), ?(\\d*)";
const string COMMAND_TPA = "!tpa ?(\\d\\d?)";
const string COMMAND_TPA_ACCEPT = "!((tp ?y)|a) ?((\\d\\d?)";
const string COMMAND_TAKEOVER = "!takeover ?(\\d\\d?) ?(lax)?";
const string COMMAND_RELEASE = "!release";
const int MOUSE_TRACK_KEY = 2; // right button click
const uint MOUSE_PRECISION_FACTOR = 65536; // this is high, ok?
const int8 NO_PLAYER_INFORMATION = -1;
const int FRAME_PIXELS = 32;
const uint8 MAX_TPA_REQUESTS = 3;
const uint8 OVERRIDE_TAKEOVER_KEY = 0x10;
array<array<uint8>> requestMap (32);
array<bool> isRequestingTPA (32);
int8 takenOverPlayerID = 0;
bool laxTakeOver = false;
enum PACKET_ID {
UPDATE_POSITION,
TPA_REQUEST,
TAKEOVER
}
/*
Moves a player from the current position
to the desired one, determined by the arguments following the first.
*/
void setLocalPlayerPosition
(
jjPLAYER@ player,
int xPos,
int yPos,
bool affectDirection = true
)
{
if (affectDirection && player.xPos != xPos)
player.direction = ((player.xPos - xPos) < 0) ? abs(player.direction) : -abs(player.direction);
player.xPos = xPos;
player.yPos = yPos;
}
void sendUpdatePositionPacket
(
jjPLAYER@ player,
int newXPos,
int newYPos,
int8 toPlayerID = NO_PLAYER_INFORMATION,
bool log = true
)
{
jjSTREAM packet;
packet.push(uint8(UPDATE_POSITION));
packet.push(player.playerID);
packet.push(toPlayerID);
packet.push(newXPos);
packet.push(newYPos);
packet.push(log);
jjSendPacket(packet, player.clientID);
}
void onUpdatePositionPacket(jjSTREAM &in packet, int fromClientID)
{
uint8 playerID;
packet.pop(playerID);
jjPLAYER@ player = jjPlayers[playerID];
int8 toPlayerID;
packet.pop(toPlayerID);
if (player.isLocal)
{
int newXPos;
int newYPos;
bool log;
packet.pop(newXPos);
packet.pop(newYPos);
packet.pop(log);
setPlayerPosition(
player,
newXPos,
newYPos,
toPlayerID,
log
);
}
}
void sendTPARequestPacket
(
jjPLAYER@ player,
jjPLAYER@ toPlayer,
uint8 requestID
)
{
jjSTREAM packet;
packet.push(uint8(TPA_REQUEST));
packet.push(player.playerID);
packet.push(toPlayer.playerID);
packet.push(requestID);
jjSendPacket(packet, toPlayer.clientID);
}
void onTPARequestPacket(jjSTREAM &in packet, int fromClientID)
{
uint8 playerID;
packet.pop(playerID);
jjPLAYER@ player = jjPlayers[playerID];
int8 toPlayerID;
packet.pop(toPlayerID);
uint8 requestID;
packet.pop(requestID);
jjConsole(
"Request #" + formatUInt(requestID) + ": "
+ player.nameUnformatted
+ " wants to teleport to you. "
+ "Type \"!a "
+ formatUInt(requestID)
+ "\" to accept this request."
);
}
void onTakeoverPacket(jjSTREAM &in packet, int fromClientID)
{
// Why do we get those junks here? I don't know.
uint8 junk_1;
uint16 junk_2;
packet.pop(junk_1);
packet.pop(junk_2);
uint8 previousTakenOverPlayerID = takenOverPlayerID;
bool wasLax = laxTakeOver;
packet.pop(takenOverPlayerID);
packet.pop(laxTakeOver);
/* if (jjPlayers[takenOverPlayerID].isLocal && previousTakenOverPlayerID != takenOverPlayerID)
{
jjConsole(jjPlayers[0].nameUnformatted + " (the host) has gained control over your position. Follow the instructions carefully.");
return;
}
if (!jjPlayers[takenOverPlayerID].isLocal && !wasLax && jjPlayers[previousTakenOverPlayerID].isLocal)
jjConsole(jjPlayers[0].nameUnformatted + " (the host) has brought you back the privilege of freedom. Beware though.");
*/
}
void sendTakeoverPacket()
{
jjSTREAM packet;
packet.push(TAKEOVER);
packet.push(takenOverPlayerID);
packet.push(laxTakeOver);
jjSendPacket(packet);
}
void setTakenOver(uint8 value, bool lax = false)
{
takenOverPlayerID = value;
laxTakeOver = lax;
sendTakeoverPacket();
}
void onReceive(jjSTREAM &in packet, int fromClientID)
{
int8 packetID;
packet.pop(packetID);
switch (packetID)
{
case UPDATE_POSITION:
onUpdatePositionPacket(packet, fromClientID);
break;
case TPA_REQUEST:
onTPARequestPacket(packet, fromClientID);
break;
case TAKEOVER:
onTakeoverPacket(packet, fromClientID);
break;
}
}
void logPlayerTeleportation
(
jjPLAYER@ player,
int xPos,
int yPos,
int8 toPlayerID = NO_PLAYER_INFORMATION
)
{
string teleportationInformation;
if (toPlayerID != NO_PLAYER_INFORMATION)
{
jjPLAYER@ toPlayer = jjPlayers[toPlayerID];
teleportationInformation += toPlayer.nameUnformatted + " at ";
}
teleportationInformation += formatInt(int(xPos / FRAME_PIXELS));
teleportationInformation += ", ";
teleportationInformation += formatInt(int(yPos / FRAME_PIXELS));
if (jjIsServer)
{
jjConsole(
player.nameUnformatted
+ " is being teleported to "
+ teleportationInformation
);
}
else if (player.playerID == jjLocalPlayers[0].playerID)
{
jjConsole("You are being teleported to " + teleportationInformation);
}
}
void setPlayerPosition
(
jjPLAYER@ player,
int xPos,
int yPos,
int8 toPlayerID = NO_PLAYER_INFORMATION,
bool log = true
)
{
setLocalPlayerPosition(player, xPos, yPos);
if (log) logPlayerTeleportation(player, xPos, yPos, toPlayerID);
if (!player.isLocal && jjIsServer)
sendUpdatePositionPacket(player, xPos, yPos, toPlayerID, log);
}
void setPlayerPosition
(
jjPLAYER@ player,
jjPLAYER@ toPlayer,
bool log = true
)
{
setPlayerPosition(player, int(toPlayer.xPos), int(toPlayer.yPos), toPlayer.playerID, log);
}
int8 saneGetPlayerID(uint8 playerID, bool mustBeInGame = true, bool log = true)
{
if (playerID < 0 || playerID > 31)
{
if (log) jjConsole("|>> Player number in list must be in range 1-32", true);
return NO_PLAYER_INFORMATION;
};
if (mustBeInGame && !jjPlayers[playerID].isInGame)
{
if (log) jjConsole("|>> Player is not in the game", true);
return NO_PLAYER_INFORMATION;
}
return playerID;
}
bool canTeleport
(
uint8 playerID,
uint8 targetPlayerID,
bool log = true
)
{
if (targetPlayerID == playerID)
{
if (log) jjConsole("|>> Hold your horses, young lady", true);
return false;
}
jjPLAYER@ toPlayer = jjPlayers[saneGetPlayerID(targetPlayerID, true, log)];
return true;
}
void mimickPlayerMouse(jjPLAYER@ targetPlayer, jjPLAYER@ mimickedPlayer)
{
if
(
MOUSE_TRACK_KEY != -1
&& mimickedPlayer.isLocal
&& targetPlayer.isInGame
&& jjKey[MOUSE_TRACK_KEY]
)
{
int x = int(jjMouseX + mimickedPlayer.cameraX - (MOUSE_PRECISION_FACTOR * (mimickedPlayer.subscreenX - jjBorderWidth)));
int y = int(jjMouseY + mimickedPlayer.cameraY - (MOUSE_PRECISION_FACTOR * (mimickedPlayer.subscreenY - jjBorderHeight)));
setPlayerPosition(targetPlayer, x, y, NO_PLAYER_INFORMATION, false);
}
}
void onPlayerInput(jjPLAYER@ player)
{
if (!player.isLocal) return;
if (jjIsServer) {
jjPLAYER@ targetPlayer = jjKey[OVERRIDE_TAKEOVER_KEY] ? player : jjPlayers[takenOverPlayerID];
mimickPlayerMouse(targetPlayer, player);
}
else if (laxTakeOver ? true : player.playerID != takenOverPlayerID)
mimickPlayerMouse(player, player);
}
bool onLocalChat(string &in stringReceived, CHAT::Type chatType)
{
if (!jjIsServer) return false;
array<string> arguments;
if (jjRegexMatch(stringReceived, COMMAND_TAKEOVER, arguments)) {
int8 candidate = saneGetPlayerID(uint8(parseUInt(arguments[1]) - 1));
bool lax = arguments[2] != "";
if (candidate != NO_PLAYER_INFORMATION)
{
setTakenOver(uint8(candidate), lax);
jjConsole(jjPlayers[takenOverPlayerID].nameUnformatted + " has been taken over. You've become Darth Vader!");
}
return true;
}
if (stringReceived == COMMAND_RELEASE) {
if (takenOverPlayerID != 0)
jjConsole(jjPlayers[takenOverPlayerID].nameUnformatted + " has been released.");
setTakenOver(0);
return true;
}
return false;
}
void onChat(int clientID, string &in stringReceived, CHAT::Type chatType)
{
if (!jjIsServer) {return;}
jjPLAYER@ player = jjPlayersWithClientID(clientID)[0];
if (stringReceived == COMMAND_POS) {
string xPos = formatUInt(int(player.xPos / FRAME_PIXELS));
string yPos = formatUInt(int(player.yPos / FRAME_PIXELS));
jjConsole("Your position is: ||" + xPos + ", " + yPos, true);
return;
}
array<string> moveToPosition;
if (jjRegexMatch(stringReceived, COMMAND_TP_RELOC, moveToPosition)) {
int xPos = int(parseInt(moveToPosition[1]) * FRAME_PIXELS);
int yPos = int(parseInt(moveToPosition[2]) * FRAME_PIXELS);
setPlayerPosition(player, xPos, yPos);
return;
}
jjPLAYER@ toPlayer;
uint8 playerID = player.playerID;
array<string> moveToPlayer;
if (jjRegexMatch(stringReceived, COMMAND_TP, moveToPlayer))
{
uint8 targetPlayerID = parseUInt(moveToPlayer[1]) - 1;
@toPlayer = @jjPlayers[targetPlayerID];
if (canTeleport(playerID, targetPlayerID))
setPlayerPosition(player, toPlayer);
return;
}
array<string> askIfCanMoveToPlayer;
if (jjRegexMatch(stringReceived, COMMAND_TPA, askIfCanMoveToPlayer))
{
uint8 targetPlayerID = parseUInt(askIfCanMoveToPlayer[1]) - 1;
if (canTeleport(playerID, targetPlayerID)) {
@toPlayer = @jjPlayers[targetPlayerID];
bool isRequesting = isRequestingTPA[playerID];
bool isOppositeRequesting = isRequestingTPA[targetPlayerID];
if (isRequesting) {
jjConsole(
"|>> You already want to teleport to someone!"
+ "Type '!tpa cancel' to cancel that request.",
true
);
return;
}
if (isOppositeRequesting) {
jjConsole(
"|>> " + player.nameUnformatted
+ " already wants to teleport to somebody!"
);
return;
}
if (requestMap[targetPlayerID].length() == MAX_TPA_REQUESTS) {
jjConsole(
"|>> Too many players want to teleport to "
+ toPlayer.nameUnformatted
+ ". Please try again later!"
);
return;
}
requestMap[uint8(targetPlayerID)].insertLast(uint8(playerID));
sendTPARequestPacket(player, toPlayer, requestMap[uint8(targetPlayerID)].length() - 1);
isRequestingTPA[uint8(playerID)] = true;
}
return;
}
array<string> acceptTPARequest;
if (jjRegexMatch(stringReceived, COMMAND_TPA, acceptTPARequest))
{
uint8 acceptID = parseUInt(acceptTPARequest[2]);
array<uint8> requests = requestMap[toPlayer.playerID];
if (acceptID == 0 || acceptID-- >= requests.length())
{
jjConsole("|>> Request ID too big");
return;
}
int8 toPlayerID = requests[acceptID];
if (toPlayerID == NO_PLAYER_INFORMATION)
{
jjConsole("|>> This request is invalid or expired");
return;
}
if (canTeleport(playerID, toPlayerID))
{
setPlayerPosition(player, toPlayer);
isRequestingTPA[uint8(playerID)] = false;
// requestMap[uint8(toPlayerID)][acceptID] = NO_PLAYER_INFORMATION;
}
}
}