Transfer Power Ups When Owner Leaves Room

Options
Hey all,

It's my first post here! Anyway, I have a situation with power ups in my game. Basically my game is a multiplayer FPS with power ups/downs floating around the levels. I currently have only the master client spawn the power ups, because if all players spawn them, every power up spawn point would be filled with how many people are in the room. So having the master control the power ups works great, except if the master leaves the room, they take all of the power ups with them. I need to find a way to transfer all of the power ups to the next master before they are all destroyed.

My first idea was to change the ownerId of each power up to the next master but was told by someone that I can't/shouldn't do that. So my next idea is to create a 'ghost' player ahead of the first player who creates the room. This ghost player will control the power spawns which subsequently control the power ups.

Here is my code:
PowerSpawns.cs
[code2=csharp]using UnityEngine;
using System.Collections;

public class PowerSpawns : Photon.MonoBehaviour {

private string chosen; // The power up to be spawned (random)
private bool isEmpty; // Tells whether the spawn point is empty or not
private GameObject newPowerUp; // The new power up game object

// All PowerUp names
private string[] powers
= new string[10] {"moreSpeed", "lessSpeed", "moreJump", "noJump", "noMovement", "increaseDamage",
"decreaseDamage", "healthPack", "invincibility", "invisibility"};


// Use this for initialization
void Start () {
isEmpty = true;
}

// Each spawn point will need to constantly ask if it it empty
// And then only the master client can spawn a new power up
void Update() {
if (isEmpty && PhotonNetwork.isMasterClient) {
isEmpty = false;
StartCoroutine(spawnIt ());
}
}

// IEnumerator function to spawn a new power up after x (random) amount of seconds
private IEnumerator spawnIt() {
chosen = powers [Random.Range (0, 9)];

int x = Random.Range (10, 20);
yield return new WaitForSeconds (x);

newPowerUp = (GameObject)PhotonNetwork.Instantiate (chosen, transform.position, transform.rotation, 0);
}

// Public function allowing power ups to tell their spawn point is empty when applicable
[RPC]
public void setEmpty() {
isEmpty = true;
}

private void onMasterClientSwitched(PhotonPlayer newMaster) {
Debug.Log ("Master left the room. New master: " + newMaster);
}
}[/code2]

And PowerUps.cs
[code2=csharp]using UnityEngine;
using System.Collections;

public class PowerUp : Photon.MonoBehaviour {

private PlayerTD player; // Reference to player who triggered the power up
private PhotonView puPV; // Reference to power up's photon view
private PowerSpawns[] myParent; // Used to find the power up's spawn point
private float wait = 5f; // Power up's effect time

// Use this for initialization
void Start () {
myParent = FindObjectsOfType<PowerSpawns>();
puPV = GetComponent<PhotonView> ();

// Start life time coroutine
StartCoroutine (lifeTime());
}

void OnTriggerEnter(Collider other) {
if (other.tag == "Player") {
player = other.GetComponent<PlayerTD>();

// Find the power up function
if (transform.gameObject.name.Contains("moreSpeed")) {
StartCoroutine(moreSpeed());
} else if (transform.gameObject.name.Contains("lessSpeed")) {
StartCoroutine(lessSpeed());
} else if (transform.gameObject.name.Contains("moreJump")) {
StartCoroutine(moreJump());
} else if (transform.gameObject.name.Contains("noJump")) {
StartCoroutine(noJump());
} else if (transform.gameObject.name.Contains("noMovement")) {
StartCoroutine(noMovement());
} else if (transform.gameObject.name.Contains("increaseDamage")) {
StartCoroutine(increaseDamage());
} else if (transform.gameObject.name.Contains("decreaseDamage")) {
StartCoroutine(decreaseDamage());
} else if (transform.gameObject.name.Contains("healthPack")) {
StartCoroutine(healthPack());
} else if (transform.gameObject.name.Contains("invincibility")) {
StartCoroutine(invincibility());
} else if (transform.gameObject.name.Contains("invisibility")) {
StartCoroutine(invisibility());
} else {
Debug.Log (transform.gameObject.name + " is not a valid pick up name");
}

// Power up was picked up, start a new spawn
findSpawn ();

// Hide power up so no one else may use it
puPV.RPC("hidePower", PhotonTargets.All);
}
}

// Find the spawn point and set it as empty
private void findSpawn() {
// The only way a power up can find its spawn point is to find the closest point
// All other attempts to get a spawn point led to a NULL Reference
for(int i = 0; i < myParent.Length; ++i) {
if(Vector3.Distance(myParent.GetComponent<Transform>().position, transform.position) < 3f)
// Only tell the master that the spawn is empty
myParent.GetComponent<PhotonView>().RPC ("setEmpty", PhotonTargets.MasterClient);
}
}

// ! IMPORTANT ! Destroying power ups will stop coroutines
// Destroy the power up game object for everyone on the server
[RPC]
public void destroyPower() {
PhotonNetwork.Destroy (gameObject);
}

// Hide the power up game object from everyone on the server
[RPC]
public void hidePower() {
gameObject.GetComponent<BoxCollider> ().enabled = false;
gameObject.GetComponent<MeshRenderer> ().enabled = false;
}

// Kill the power up after time
private IEnumerator lifeTime() {
yield return new WaitForSeconds (60);

// Only master client can spawn a new power up
// If this was opened to all users, each spawn will contain a power up per user
if(PhotonNetwork.isMasterClient)
findSpawn ();

puPV.RPC ("destroyPower", PhotonTargets.All);
}

// Add boost to player's movement
private IEnumerator moreSpeed() {
Debug.Log ("More Speed");
float x = player.getMovementSpeed () * 0.5f;
player.addMovementSpeed(x);

yield return new WaitForSeconds (wait);

player.addMovementSpeed(-x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Subtract from player's movement
private IEnumerator lessSpeed() {
Debug.Log ("Less Speed");
float x = player.getMovementSpeed () * 0.5f;
player.addMovementSpeed(-x);

yield return new WaitForSeconds (wait);

player.addMovementSpeed(x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Add boost to player's jump
private IEnumerator moreJump() {
Debug.Log ("More Jump");
float x = player.getCurrentJumpSpeed () * 0.5f;
player.addJumpHeight(x);

yield return new WaitForSeconds (wait);

player.addJumpHeight(-x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Take away player's jump
private IEnumerator noJump() {
Debug.Log ("No Jump");
float x = player.getCurrentJumpSpeed();
player.addJumpHeight(-x);

yield return new WaitForSeconds (wait);

player.addJumpHeight(player.getJumpSpeed());
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Take away all player movement (run/jump)
private IEnumerator noMovement() {
Debug.Log ("No Movement");
float x = player.getCurrentMovementSpeed ();
float y = player.getCurrentJumpSpeed ();
player.addMovementSpeed (-x);
player.addJumpHeight(-y);

yield return new WaitForSeconds (wait);

player.addMovementSpeed (player.getMovementSpeed());
player.addJumpHeight (player.getJumpSpeed());
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Increase damage dealt from player
private IEnumerator increaseDamage() {
Debug.Log ("Increase Damage");
int x = player.getBulletDamage();
player.addBulletDamage(x);

yield return new WaitForSeconds (wait);

player.addBulletDamage(-x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Decrease damage dealt from player
private IEnumerator decreaseDamage() {
Debug.Log ("Decrease Damage");
int x = player.getBulletDamage();
player.addBulletDamage(-x);

yield return new WaitForSeconds (wait);

player.addBulletDamage(x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Add to player's maximum health
private IEnumerator healthPack() {
Debug.Log ("Health Pack");
int x = 25;
player.addMaxHealth (x);

yield return new WaitForSeconds (wait);

player.addMaxHealth (-x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Temporarily make the player invincible
private IEnumerator invincibility() {
Debug.Log ("Invincibility");
int x = player.getHealth ();
player.setHealthAbsolute (99999);

yield return new WaitForSeconds (wait);

player.setHealthAbsolute (x);
puPV.RPC("destroyPower", PhotonTargets.All);
}

// Temporarily make the player invisible
[RPC]
private IEnumerator invisibility() {
Debug.Log ("Invisibility");
player.GetComponent<MeshRenderer> ().enabled = false;

yield return new WaitForSeconds (wait);

player.GetComponent<MeshRenderer> ().enabled = true;
puPV.RPC("destroyPower", PhotonTargets.All);
}
}[/code2]

Thanks for reading!

P

Comments

  • Tobias
    Options
    There is a better way. Use:
    PhotonNetwork.InstantiateSceneObject(). Those stay in the room automatically and the next master client can take over control (once everyone knows that the previous master client left).

    When a master client leaves, it takes a moment before everyone will know that. In that brief time, the leaving master client won't fulfill it's duties and you have to be prepared for that. This might be a bit tricky in some cases.