Downloads containing tp.mut

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Teleportation mutatorFeatured Download ak-47 Mutator 10 Download file

File preview

#pragma name "Teleportations (by ak-47)"


namespace COMMAND {
	const string PREFIX = "[~!@\\.;]";
	const string POS = "pos(pix)?";
	const string FLY = "fly ?(\\w+)?";
	const string TP = "tp ?(\\d\\d?)";
	const string TP_HERE = "tphere ?(\\d\\d?)";
	const string TP_XY = "tp ?(\\d*)[.,] ?(\\d*)";
	const string TP_SETTINGS = "tp(settings|confi?g?)";
	const string SAVE_SETTINGS = "tpsave(settings|confi?g?)? ?(.*)";
	const string LOAD_SETTINGS = "tpload(settings|confi?g?)? ?(.*)";
	const string TAKEOVER = "takeover ?(\\d\\d?) ?(lax)?";
	const string RELEASE = "release";
	const string WARP_EFFECT = "warp(effect)? ?(\\w+)?";
	const string MOUSE_CAMERA = "m(ouse)?cam(era)? ?(.*)";
	const string CONTROLS = "controls ?(\\w+)?";
	const string FROZEN = "frozen ?(\\w+)?";
	const string FREEZE = "freeze";
	const string UNFREEZE = "unfreeze";
}

namespace CONFIG {
	const uint MAX_LINES = 10;
	const string FILE_NAME = "tpconfig.asdat";
	const string LINE_PATTERN = "(\\w+)=(.*)";
	
	namespace OPTION {
		const string FLY = "fly";
		const string WARP_EFFECT = "useWarpEffect";
		const string CONTROLS = "unlockControls";
		const string MOUSE_CAMERA = "mouseCamera";
	}
}

namespace KEY {
	const int8 MOUSE_CAMERA = 0x02;  // VK_RBUTTON
	const int8 MOUSE_TRACK = 0x02;   // VK_RBUTTON
	const int8 TAKEOVER = 0x10;      // VK_SHIFT
}

const uint PRECISION_FRAME_THRESHOLD = 10;
const int8 NO_PLAYER_INFORMATION = -1;
const int FRAME_PIXELS = 32;

int8 takenOverPlayerID = 0;
bool laxTakeOver = false;
bool fly = false;
bool useWarpEffect = true;
bool frozen = false;
bool mouseCamera = true;


// Mouse and arrow camera, inspired by the "look around" feature 
// introduced in Universal Test Manager (v1.3) by Gigo

bool usingMouseCamera = false;
bool unlockControls = false;


void enableMouseCamera() { usingMouseCamera = true; }
void disableSpecialCamera() { usingMouseCamera = false; }


void performMouseCamera(jjPLAYER@ player) {
	// middleTextTimer = 0;
	float posX = float(jjMouseX - 0.5 * jjResolutionWidth) / float(0.5 * jjResolutionWidth);
	float posY = float(jjMouseY - 0.5 * jjResolutionHeight) / float(0.5 * jjResolutionHeight);
	
	if (posX < -1) posX = -1;
	if (posX > 1) posX = 1;
	if (posY < -1) posY = -1;
	if (posY > 1) posY = 1;
	
	player.cameraFreeze(
		player.xPos + 1000 * posX, 
		player.yPos + 1000 * posY, 
		true, false
	);
	
	if (!jjKey[KEY::MOUSE_CAMERA]) {
		for (int8 i = 0; i < jjLocalPlayerCount; i++) {
			jjLocalPlayers[i].cameraUnfreeze(true);
		}
		usingMouseCamera = false;
	}
}


void manageControls(jjPLAYER@ player) {
	if (!unlockControls) {
		player.keyLeft = false;
		player.keyRight = false;
		player.keyUp = false;
		player.keyDown = false;
		player.keySelect = false;
		player.keyRun = false;
		player.keyJump = false;
		player.keyFire = false;
	}
}


void onPlayer(jjPLAYER@ player) {
	player.freeze(frozen);	
	if (usingMouseCamera) {
		manageControls(player);
		performMouseCamera(player);
	}
}


enum PACKET_ID {UPDATE_POSITION, TAKEOVER}


void onLevelLoad() {
	loadSettings(CONFIG::FILE_NAME, false);
}


bool loadSettings(const string fileName = CONFIG::FILE_NAME, bool log = true)
{
	jjSTREAM settingsFile (fileName);
	array<string> lines;

	if (settingsFile.isEmpty()) {
		saveSettings(fileName);
	}
	
	while (!settingsFile.isEmpty()) 
	{
		string nextLine;
		if (lines.length() == CONFIG::MAX_LINES) 
		{
			if (log) jjAlert("|>> Error: " + fileName + " configuration file is corrupted. Try to reset it with !tpsave.");
			return false;
		}
		settingsFile.getLine(nextLine);
		lines.insertLast(nextLine);
	}
	
	uint currentLineID = 0;
	string currentLine = "";
	string currentKey = "(unknown)";
	string currentValue = "(unknown)";
	
	for (currentLineID; currentLineID < lines.length(); ++currentLineID)
	{
		string currentHeader = "|>> " + fileName + ", line " + formatUInt(currentLineID + 1) + ":";
		currentLine = lines[currentLineID];
		array<string> data;		
		
		if (jjRegexMatch(currentLine, CONFIG::LINE_PATTERN, data)) {
			currentKey = data[1];
			currentValue = data[2];
			currentHeader = "|>> " + fileName + ", line " + formatUInt(currentLineID + 1) + ", option " + currentKey + ":";
		}
		else {
			if (log) {
				jjAlert(currentHeader);
				jjAlert("|>> Error: configuration format is invalid! Try to reset with !tpsave.");
			}
			return false;
		}
		
		string boolCastErrorMsg = currentHeader + " expected either 'on' or 'off'";

		if (jjRegexMatch(currentKey, CONFIG::OPTION::FLY, true))
			parseBool(fly, currentValue, bool(fly), boolCastErrorMsg, log);

		else if (jjRegexMatch(currentKey, CONFIG::OPTION::WARP_EFFECT, true))
			parseBool(useWarpEffect, currentValue, bool(useWarpEffect), boolCastErrorMsg, log);

		else if (jjRegexMatch(currentKey, CONFIG::OPTION::CONTROLS, true))
			parseBool(unlockControls, currentValue, bool(unlockControls), boolCastErrorMsg, log);

		else if (jjRegexMatch(currentKey, CONFIG::OPTION::MOUSE_CAMERA, true))
			parseBool(mouseCamera, currentValue, bool(mouseCamera), boolCastErrorMsg, log);

		else {
			if (log) {
				jjAlert(currentHeader);
				jjAlert("|>> Error: unexpected option " + currentKey + ". Try to reset with !tpsave.");
			}
			return false;
		}
	}
	return true;
}

bool saveSettings(const string fileName = CONFIG::FILE_NAME)
{
	jjSTREAM settingsFile;
	settingsFile.write(CONFIG::OPTION::FLY + "=" + (fly ? "on" : "off") + "\n");
	settingsFile.write(CONFIG::OPTION::WARP_EFFECT + "=" + (useWarpEffect ? "on": "off") + "\n");
	settingsFile.write(CONFIG::OPTION::CONTROLS + "=" + (unlockControls ? "on": "off") + "\n");
	settingsFile.write(CONFIG::OPTION::CONTROLS + "=" + (unlockControls ? "on": "off") + "\n");
	return settingsFile.save(fileName);
}


void onReceive(jjSTREAM &in packet, int fromClientID)
{
    int8 packetID;
    packet.pop(packetID);

    switch (packetID)
    {
        case UPDATE_POSITION:
            onUpdatePositionPacket(packet, fromClientID);
            break;
        case TAKEOVER:
            onTakeoverPacket(packet, fromClientID);
            break;
    }
}


void onPlayerInput(jjPLAYER@ player)
{
	if (mouseCamera && jjKey[KEY::MOUSE_CAMERA])
		usingMouseCamera = true;
    if (jjIsServer) {
        jjPLAYER@ targetPlayer = jjKey[KEY::TAKEOVER] ? jjPlayers[takenOverPlayerID] : player;
        followLocalPlayerMouse(targetPlayer, player);
    }
    else if (laxTakeOver ? true : player.playerID != takenOverPlayerID) 
        followLocalPlayerMouse(player, player);
}

bool parseBool(
	bool &out boolRef, 
	const string argument, 
	bool defaultValue = true,
	const string errorAlertMessage = "|>> Error: Invalid parameter - use either 'on' or 'off'",
	bool log = true
)
{
    if (argument != "")
        if (jjRegexMatch(argument, "(y(es)?|on|1|true)", true))
			boolRef = true;
        else if (jjRegexMatch(argument, "(no?|off|0|false)", true))  
			boolRef = false;
        else 
        {
            if (log) jjAlert(errorAlertMessage);
            return false;
        }
    else boolRef = defaultValue;
    return true;
}

bool isCommand(const string commandString, const string commandPattern, bool caseSensitive = false)
{
	return jjRegexMatch(commandString, COMMAND::PREFIX + commandPattern, !caseSensitive);
}

bool isCommand(const string commandString, const string commandPattern, array<string> &out paramCollector, bool caseSensitive = false)
{
	return jjRegexMatch(commandString, COMMAND::PREFIX + commandPattern, paramCollector, !caseSensitive);
}


void printFly() {
    jjConsole("Smooth mouse flying: " + (fly ? "||ON" : "|OFF"), true);
}

void printWarpEffect() {
	jjConsole("Warp effect: " + (useWarpEffect ? "||ON" : "|OFF"), true);
}

void printUnlockControls() {
	jjConsole("Unlock controls in mouse camera mode: " + (unlockControls ? "||ON" : "|OFF"), true);
}

void printMouseCamera() {
	jjConsole("Auto mouse camera mode: " + (mouseCamera ? "||ON" : "|OFF"), true);
}

void printSettings() {
	printFly();
	printWarpEffect();
	printUnlockControls();
	printMouseCamera();
}


bool onLocalChat(string &in stringReceived, CHAT::Type chatType)
{
    jjPLAYER@ player = jjLocalPlayers[0];

	array<string> posArgument;
    if (isCommand(stringReceived, COMMAND::POS, posArgument)) 
    {
		string xPos, yPos;
		if (posArgument[1] == "") 
		{
			xPos = formatUInt(int(player.xPos / FRAME_PIXELS));
			yPos = formatUInt(int(player.yPos / FRAME_PIXELS));			
		} 
		else 
		{
			xPos = formatUInt(int(player.xPos));
			yPos = formatUInt(int(player.yPos));			
		}
        jjConsole("Your position is: ||" + xPos + ", " + yPos, true);
        return true;
    }

    array<string> flyArgument;
    if (isCommand(stringReceived, COMMAND::FLY, flyArgument)) 
    {
        const string argument = flyArgument[1];
        if (argument != "") 
			parseBool(fly, argument, bool(fly));
		printFly();
        return true;
    }

    array<string> useWarpEffectArgument;
    if (isCommand(stringReceived, COMMAND::WARP_EFFECT, useWarpEffectArgument)) 
    {
        const string argument = useWarpEffectArgument[2];
        if (argument != "") 
			parseBool(useWarpEffect, argument, bool(useWarpEffect));
		printWarpEffect();
		return true;
    }
	
	array<string> unlockControlsArgument;
	if (isCommand(stringReceived, COMMAND::CONTROLS, unlockControlsArgument))
	{
        const string argument = unlockControlsArgument[1];
        if (argument != "") 
			parseBool(unlockControls, argument, bool(unlockControls));
		printUnlockControls();
		return true;		
	}
    
	if (isCommand(stringReceived, COMMAND::TP_SETTINGS)) {
		jjAlert(">> Current teleportation settings:");
		printSettings();
		return true;
	}
	
	array<string> loadSettingsArgument;
	if (isCommand(stringReceived, COMMAND::LOAD_SETTINGS, loadSettingsArgument)) 
	{
		string settingsFileName = loadSettingsArgument[2];
		settingsFileName = (settingsFileName != "") ? settingsFileName : CONFIG::FILE_NAME;
		bool loaded = loadSettings(settingsFileName);
		if (loaded) {
			jjAlert(">> Loaded settings from " + settingsFileName);
			printSettings();
		}
		return true;
	}
    
	array<string> saveSettingsArgument;
	if (isCommand(stringReceived, COMMAND::SAVE_SETTINGS, saveSettingsArgument)) 
	{
		string settingsFileName = saveSettingsArgument[2];
		settingsFileName = (settingsFileName != "") ? settingsFileName : CONFIG::FILE_NAME;
		bool saved = saveSettings(settingsFileName);
		if (saved) {
			jjAlert(">> Saved settings to " + settingsFileName);
			printSettings();
		}
		return true;
	}

	array<string> mouseCameraArgument;
	if (isCommand(stringReceived, COMMAND::MOUSE_CAMERA, mouseCameraArgument)) 
	{
		string argument = mouseCameraArgument[3];
        if (argument != "") 
			parseBool(mouseCamera, argument, bool(mouseCamera));
		printMouseCamera();		
		return true;
	}
	
	array<string> frozenArgument;
	if (isCommand(stringReceived, COMMAND::FROZEN, frozenArgument))
	{
		string argument = frozenArgument[1];
		parseBool(frozen, argument, false);
		return true;
	}	

	if (isCommand(stringReceived, COMMAND::FREEZE))
	{
		frozen = true;
		return true;
	}	

	if (isCommand(stringReceived, COMMAND::UNFREEZE))
	{
		frozen = false;
		return true;
	}	
	
    if (!jjIsServer) 
        return false;
    
    array<string> takeoverArguments;
    if (isCommand(stringReceived, COMMAND::TAKEOVER, takeoverArguments)) 
    {
        int8 candidate = saneGetPlayerID(uint8(parseUInt(takeoverArguments[1]) - 1));
        bool lax = takeoverArguments[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 (isCommand(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];

    array<string> tpXYArguments;
    if (isCommand(stringReceived, COMMAND::TP_XY, tpXYArguments)) {
        int xPos = int(parseInt(tpXYArguments[1]) * FRAME_PIXELS);
        int yPos = int(parseInt(tpXYArguments[2]) * FRAME_PIXELS);
        setPlayerPosition(player, xPos, yPos);
        return;
    }

    jjPLAYER@ targetPlayer;
    uint8 playerID = player.playerID;

    array<string> tpPlayerArguments;
    if (isCommand(stringReceived, COMMAND::TP, tpPlayerArguments))
    {
        uint8 targetPlayerID = parseUInt(tpPlayerArguments[1]) - 1;
        @targetPlayer = @jjPlayers[targetPlayerID];
        if (canTeleport(playerID, targetPlayerID))
            setPlayerPosition(player, targetPlayer);
        return;
    }

    array<string> tpHerePlayerArguments;
    if (isCommand(stringReceived, COMMAND::TP_HERE, tpHerePlayerArguments))
    {
        uint8 targetPlayerID = parseUInt(tpHerePlayerArguments[1]) - 1;
        @targetPlayer = @jjPlayers[targetPlayerID];
        if (canTeleport(targetPlayerID, playerID))
            setPlayerPosition(targetPlayer, player);
        return;
    }

}

/*
Moves a local player from the current position to the desired one, 
determined by the arguments following the first.

Works only for local player.
*/
void setLocalPlayerPosition
(
    jjPLAYER@ player,
    int xPos,    
    int yPos,
    bool affectDirection = true,
    bool isTeleportationToPlayer = false
)
{
    if (affectDirection && player.xPos != xPos)
        player.direction = int((player.xPos - xPos) < 0 ? abs(player.direction) : -abs(player.direction));
    if ((!fly || isTeleportationToPlayer) && useWarpEffect)
        player.warpToTile(xPos / FRAME_PIXELS, yPos / FRAME_PIXELS);
    else {
        player.xPos = xPos;
        player.yPos = yPos;
    }
}

void setPlayerPosition
(
    jjPLAYER@ player,
    int xPos,
    int yPos,
    int8 toPlayerID = NO_PLAYER_INFORMATION,
    bool log = true
)
{
    setLocalPlayerPosition(player, xPos, yPos, true, toPlayerID != NO_PLAYER_INFORMATION);
    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 toPlayer.isInGame;
}


uint lastClickTime = 0;
void followLocalPlayerMouse(jjPLAYER@ targetPlayer, jjPLAYER@ mimickedPlayer)
{
    if 
    (
        KEY::MOUSE_TRACK != -1
        && mimickedPlayer.isLocal
        && targetPlayer.isInGame 
        && jjKey[KEY::MOUSE_TRACK]
        && (fly ? true : int(lastClickTime + PRECISION_FRAME_THRESHOLD) < jjGameTicks)
    )
    {
        lastClickTime = jjGameTicks;
        int x = int(jjMouseX + mimickedPlayer.cameraX - mimickedPlayer.subscreenX - jjBorderWidth);
        int y = int(jjMouseY + mimickedPlayer.cameraY - mimickedPlayer.subscreenY - jjBorderHeight);
        setPlayerPosition(targetPlayer, x, y, NO_PLAYER_INFORMATION, false);
    }
}


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);
    packet.push(fly);
    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);
        packet.pop(fly);
        setPlayerPosition(
            player,
            newXPos,
            newYPos,
            toPlayerID,
            log
        );
    }
}


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 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);
    }
}