var/datum/respawn_controls/respawn_controller // Datum to handle automatic respawns /* MVP should cover: - Tracking dead players and allowing them to respawn if possible - DONE - Triggered when player dies, player is put into waiting list - DONE - Player removed from waiting list when respawned in-game (eg. ghost drone, cloning, borging) - DONE - Granting the dead players a "Respawn" verb when they're brought back - DONE - Pop-up during RP-mode reminding of New-Life Rule - DONE - Configurable - Respawn timeout (in-code, ideally by define) - DONE - On/off (in-code) - DONE - Verification of mob-to-be respawned is in a valid state for respawning - DONE - Giving a disconnected/reconnected client their Respawn button back - DONE ============ Future ============ - Verification that player does not join as their old character (on pain of death / admin alert) - Most respawns done through this controller */ #define RESPAWNEE_STATE_WAITING 0 #define RESPAWNEE_STATE_ELIGIBLE 1 #define RESPAWNEE_STATE_ALIVE 2 /datum/respawn_controls var/respawn_time = DEFAULT_RESPAWN_TIME var/respawns_enabled = RESPAWNS_ENABLED var/list/respawnees var/rp_alert = 0 New() ..() respawnees = list() #ifdef RP_MODE rp_alert = 1 respawns_enabled = 1 #endif proc/process() if(!respawns_enabled) return // Check the ones due to respawn for(var/ckey in respawnees) var/datum/respawnee/R = respawnees[ckey] checkRespawnee(R) proc/checkRespawnee(var/datum/respawnee/R) switch(R.checkValid()) if(RESPAWNEE_STATE_WAITING) // This could happen if the client disconnects if(RESPAWNEE_STATE_ELIGIBLE) // They are eligible for respawn R.notifyAndGrantVerb() if(RESPAWNEE_STATE_ALIVE) // They were somehow revived unsubscribeRespawnee(R.ckey) proc/subscribeNewRespawnee(var/ckey) if(ckey && !respawnees.Find(ckey)) var/datum/respawnee/R = new R.initialize(ckey, src) respawnees[ckey] = R proc/unsubscribeRespawnee(var/ckey) if(!ckey) return var/datum/respawnee/R = respawnees[ckey] respawnees.Remove(ckey) if(R) R.disposing() R = null proc/doRespawn(var/ckey) if(!ckey) return var/datum/respawnee/R = respawnees[ckey] if(R) R.doRespawn() /datum/respawnee var/ckey var/client_processed var/died_time var/client/the_client var/due_for_respawn var/datum/respawn_controls/master disposing() if(the_client) the_client.verbs -= /client/proc/respawn_via_controller master = null ..() proc/initialize(var/ckey, var/datum/respawn_controls/master) src.ckey = ckey src.master = master src.died_time = world.time // Get a reference to the client - this way we would know if they have disconnected or not try the_client = getClientFromCkey(src.ckey) catch proc/checkValid() // Time check (short-circuit saves some steps) if(due_for_respawn || src.died_time + master.respawn_time <= world.time) due_for_respawn = 1 // Try to get a valid client reference if(!the_client) try client_processed = 0 the_client = getClientFromCkey(src.ckey) catch return RESPAWNEE_STATE_WAITING // Check that the client is currently dead if(isobserver(the_client.mob) || isdead(the_client.mob)) return RESPAWNEE_STATE_ELIGIBLE return RESPAWNEE_STATE_WAITING proc/notifyAndGrantVerb() if(!client_processed && checkValid()) // Send a message to the client SPAWN_DBG(0) alert(the_client.mob, "You are now eligible for respawn. Check the Commands tab.") boutput(the_client.mob, "