Kick button, show player turn, can't join if 2-nd player alreay join
This commit is contained in:
parent
6398f44d18
commit
f94bf511a3
@ -1,6 +1,6 @@
|
|||||||
package actors
|
package actors
|
||||||
|
|
||||||
import actors.UserActor.{GetName, HostLeaveLobby, RefreshLobbyInfo, SetUserAsHost}
|
import actors.UserActor.{GetName, HostLeaveLobby, LobbyFatal, RefreshLobbyInfo, SecondPlayerLeaveLobby, SetUserAsHost, SetUserAsObs, SetUserAsSecondPlayer}
|
||||||
import akka.actor.{Actor, ActorLogging, ActorRef}
|
import akka.actor.{Actor, ActorLogging, ActorRef}
|
||||||
import akka.event.LoggingReceive
|
import akka.event.LoggingReceive
|
||||||
import com.typesafe.scalalogging.LazyLogging
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
@ -49,7 +49,14 @@ class LobbieActor(hostUser: LobbyUser) extends Actor with LazyLogging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostUser.actorRef.tell(
|
hostUser.actorRef.tell(
|
||||||
SetUserAsHost(LobbyInfo(hostUser.name, "", firstPlayerReady, secondPlayerReady, self.path.name, status.toString(), mapsLobby)), this.self)
|
SetUserAsHost(LobbyInfo(
|
||||||
|
UserInfo(hostUser.name,firstPlayerReady),
|
||||||
|
UserInfo("", secondPlayerReady),
|
||||||
|
self.path.name,
|
||||||
|
status.toString(),
|
||||||
|
playerTurn,
|
||||||
|
mapsLobby)),
|
||||||
|
this.self)
|
||||||
|
|
||||||
implicit val timeout: Timeout = 1.second
|
implicit val timeout: Timeout = 1.second
|
||||||
|
|
||||||
@ -98,21 +105,38 @@ class LobbieActor(hostUser: LobbyUser) extends Actor with LazyLogging {
|
|||||||
}
|
}
|
||||||
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
|
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
|
||||||
|
|
||||||
|
case KickSecondPlayer =>
|
||||||
|
if (sender() == host.actorRef) {
|
||||||
|
secondPlayer.foreach(player => player.actorRef ! LobbyFatal("You were kicked from lobby!"))
|
||||||
|
secondPlayerReady = false
|
||||||
|
secondPlayer = None
|
||||||
|
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
|
||||||
|
}
|
||||||
case JoinLobbyAsPlayer(sp) =>
|
case JoinLobbyAsPlayer(sp) =>
|
||||||
logger.info(s"User ${sender.path.name} join lobby ${self.path.name} as player")
|
logger.info(s"User ${sender.path.name} join lobby ${self.path.name} as player")
|
||||||
secondPlayer = Some(sp)
|
if (secondPlayer.isEmpty) {
|
||||||
users = users + sp.actorRef
|
secondPlayer = Some(sp)
|
||||||
sp.actorRef.tell(
|
users = users + sp.actorRef
|
||||||
SetUserAsHost(LobbyInfo(hostUser.name, sp.name, firstPlayerReady, secondPlayerReady, self.path.name, status.toString(), mapsLobby)), this.self)
|
sp.actorRef.tell(
|
||||||
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
|
SetUserAsSecondPlayer(getLobbyInfoResponse), this.self)
|
||||||
|
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
|
||||||
|
} else {
|
||||||
|
sp.actorRef ! LobbyFatal("Lobby already full")
|
||||||
|
}
|
||||||
|
|
||||||
case WatchLobby(_) =>
|
case WatchLobby(_) =>
|
||||||
// add the watcher to the list
|
// add the watcher to the list
|
||||||
users = users + sender
|
users = users + sender
|
||||||
|
sender ! SetUserAsObs(getLobbyInfoResponse)
|
||||||
case LeaveLobby =>
|
case LeaveLobby =>
|
||||||
users = users - sender
|
users = users - sender
|
||||||
if(secondPlayer.exists(sp => sp.actorRef == sender)) secondPlayer = None
|
if(secondPlayer.exists(sp => sp.actorRef == sender)) secondPlayer = None
|
||||||
if(host.actorRef == sender()){
|
if(host.actorRef == sender()){
|
||||||
users.foreach(_ ! HostLeaveLobby)
|
users.foreach(_ ! HostLeaveLobby)
|
||||||
|
context.stop(self)
|
||||||
|
}else if(secondPlayer.exists(_.actorRef == sender())){
|
||||||
|
users.foreach(_ ! SecondPlayerLeaveLobby)
|
||||||
|
context.stop(self)
|
||||||
}
|
}
|
||||||
if (users.isEmpty) {
|
if (users.isEmpty) {
|
||||||
logger.info(s"Stop lobby ${self.path.name}")
|
logger.info(s"Stop lobby ${self.path.name}")
|
||||||
@ -125,7 +149,13 @@ class LobbieActor(hostUser: LobbyUser) extends Actor with LazyLogging {
|
|||||||
|
|
||||||
private def getLobbyInfoResponse: LobbyInfo = {
|
private def getLobbyInfoResponse: LobbyInfo = {
|
||||||
val user2Name = secondPlayer.map(_.name).getOrElse("")
|
val user2Name = secondPlayer.map(_.name).getOrElse("")
|
||||||
LobbyInfo(host.name, user2Name, firstPlayerReady, secondPlayerReady, self.path.name, status.toString(), mapsLobby)
|
LobbyInfo(
|
||||||
|
UserInfo(host.name, firstPlayerReady),
|
||||||
|
UserInfo(user2Name, secondPlayerReady),
|
||||||
|
self.path.name,
|
||||||
|
status.toString(),
|
||||||
|
playerTurn,
|
||||||
|
mapsLobby)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +169,8 @@ case class WatchLobby(lobbyName: String)
|
|||||||
|
|
||||||
case class JoinLobbyAsPlayer(lobbyUser: LobbyUser)
|
case class JoinLobbyAsPlayer(lobbyUser: LobbyUser)
|
||||||
|
|
||||||
|
case object KickSecondPlayer
|
||||||
|
|
||||||
case object LeaveLobby
|
case object LeaveLobby
|
||||||
|
|
||||||
case object SetReady
|
case object SetReady
|
||||||
@ -147,12 +179,13 @@ case object SetNotReady
|
|||||||
|
|
||||||
case object InfoQuery
|
case object InfoQuery
|
||||||
|
|
||||||
case class LobbyInfo(user1Name: String,
|
case class UserInfo(name: String, isReady: Boolean)
|
||||||
user2Name: String,
|
|
||||||
user1Ready: Boolean,
|
case class LobbyInfo(user1Info: UserInfo,
|
||||||
user2Ready: Boolean,
|
user2Info: UserInfo,
|
||||||
lobbyActorName: String,
|
lobbyActorName: String,
|
||||||
status: String,
|
status: String,
|
||||||
|
playerTurn: BigDecimal,
|
||||||
maps: Set[DeciderMap])
|
maps: Set[DeciderMap])
|
||||||
|
|
||||||
class LobbyStatus
|
class LobbyStatus
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package actors
|
package actors
|
||||||
|
|
||||||
import actors.UserActor.{GetName, HostLeaveLobby, RefreshLobbyInfo, SetUserAsHost, SetUserAsSecondPlayer}
|
import actors.UserActor.{GetName, HostLeaveLobby, LobbyFatal, RefreshLobbyInfo, SecondPlayerLeaveLobby, SetUserAsHost, SetUserAsSecondPlayer}
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import akka.event.LoggingReceive
|
import akka.event.LoggingReceive
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
@ -29,6 +29,8 @@ class UserActor(out: ActorRef,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicit val userInfoWrites: OWrites[UserInfo] = Json.writes[UserInfo]
|
||||||
|
|
||||||
implicit val lobbyWrites: OWrites[LobbyInfo] = Json.writes[LobbyInfo]
|
implicit val lobbyWrites: OWrites[LobbyInfo] = Json.writes[LobbyInfo]
|
||||||
|
|
||||||
implicit val lobbyResponseWrites: Writes[List[LobbyInfo]] = new Writes[List[LobbyInfo]] {
|
implicit val lobbyResponseWrites: Writes[List[LobbyInfo]] = new Writes[List[LobbyInfo]] {
|
||||||
@ -67,12 +69,20 @@ class UserActor(out: ActorRef,
|
|||||||
|
|
||||||
case HostLeaveLobby =>
|
case HostLeaveLobby =>
|
||||||
logger.info(s"Host leave from lobby ${sender.path.name}")
|
logger.info(s"Host leave from lobby ${sender.path.name}")
|
||||||
out ! Json.obj("type" -> "hostLeaveLobby")
|
out ! Json.obj("type" -> "lobbyError", "error" -> "Host leave lobby")
|
||||||
|
|
||||||
|
case SecondPlayerLeaveLobby =>
|
||||||
|
logger.info(s"Second player lobby ${sender.path.name}")
|
||||||
|
out ! Json.obj("type" -> "lobbyError", "error" -> "Second player leave lobby")
|
||||||
|
|
||||||
case RefreshLobbyInfo(lobbyInfo) =>
|
case RefreshLobbyInfo(lobbyInfo) =>
|
||||||
logger.trace(s"Refresh lobby info: $lobbyInfo")
|
logger.trace(s"Refresh lobby info: $lobbyInfo")
|
||||||
out ! Json.obj("type" -> "refreshLobby", "lobby" -> lobbyInfo)
|
out ! Json.obj("type" -> "refreshLobby", "lobby" -> lobbyInfo)
|
||||||
|
|
||||||
|
case LobbyFatal(message) => // После этого сообщения выход из лобби
|
||||||
|
logger.warn(s"Lobby error: $message")
|
||||||
|
out ! Json.obj("type" -> "lobbyError", "error" -> message)
|
||||||
|
|
||||||
case json: JsValue =>
|
case json: JsValue =>
|
||||||
// Обрабатываем запросы с фронта
|
// Обрабатываем запросы с фронта
|
||||||
|
|
||||||
@ -104,6 +114,9 @@ class UserActor(out: ActorRef,
|
|||||||
case Some("setNotReady") =>
|
case Some("setNotReady") =>
|
||||||
lobbieActor.foreach(lobby => lobby ! SetNotReady)
|
lobbieActor.foreach(lobby => lobby ! SetNotReady)
|
||||||
|
|
||||||
|
case Some("kickSecondPlayer") =>
|
||||||
|
lobbieActor.foreach(lobby => lobby ! KickSecondPlayer)
|
||||||
|
|
||||||
case Some("joinDecider") =>
|
case Some("joinDecider") =>
|
||||||
val lobbyActorName = (json \ "lobbyActorName").as[String]
|
val lobbyActorName = (json \ "lobbyActorName").as[String]
|
||||||
logger.info(s"Player ${self.path.name} join lobby $lobbyActorName")
|
logger.info(s"Player ${self.path.name} join lobby $lobbyActorName")
|
||||||
@ -122,7 +135,6 @@ class UserActor(out: ActorRef,
|
|||||||
}
|
}
|
||||||
case Failure(ex) => logger.error("Received error", ex)
|
case Failure(ex) => logger.error("Received error", ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,10 +182,14 @@ object UserActor {
|
|||||||
|
|
||||||
case class SetUserAsSecondPlayer(lobbyInfo: LobbyInfo)
|
case class SetUserAsSecondPlayer(lobbyInfo: LobbyInfo)
|
||||||
|
|
||||||
|
case class SetUserAsObs(lobbyInfo: LobbyInfo)
|
||||||
|
|
||||||
|
case class LobbyFatal(message: String)
|
||||||
|
|
||||||
case object CreateLobby
|
case object CreateLobby
|
||||||
|
|
||||||
case object HostLeaveLobby
|
case object HostLeaveLobby
|
||||||
|
|
||||||
|
case object SecondPlayerLeaveLobby
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,16 +37,16 @@ window.onload = function() {
|
|||||||
|
|
||||||
var lobbiesAsTableRows = _.reduce(this.lobbies, function (memo, lobby) {
|
var lobbiesAsTableRows = _.reduce(this.lobbies, function (memo, lobby) {
|
||||||
|
|
||||||
var joinButton = '<button class="btn btn-primary join-as-player" onclick="joinDecider(\''+lobby.lobbyActorName+'\')">' +
|
var joinButton = (lobby.status === "NotStarted()") ? '<button class="btn btn-primary join-as-player" onclick="joinDecider(\''+lobby.lobbyActorName+'\')">' +
|
||||||
'<img src="assets/images/buttons/isAuto.png" style="height: 25px;"> Join lobby <img src="assets/images/buttons/isAuto.png" style="height: 25px;">' +
|
'<img src="assets/images/buttons/isAuto.png" style="height: 25px;"> Join lobby <img src="assets/images/buttons/isAuto.png" style="height: 25px;">' +
|
||||||
'</button>';
|
'</button>' : "";
|
||||||
var observerButton = '<button class="btn btn-success join-as-observer" lobbyId="'+lobby.lobbyActorName+'">' +
|
var observerButton = '<button class="btn btn-success join-as-observer" lobbyId="'+lobby.lobbyActorName+'">' +
|
||||||
'<img src="assets/images/buttons/Ulthwe.png" style="height: 25px;"> Observer <img src="assets/images/buttons/Ulthwe.png" style="height: 25px;">' +
|
'<img src="assets/images/buttons/Ulthwe.png" style="height: 25px;"> Observer <img src="assets/images/buttons/Ulthwe.png" style="height: 25px;">' +
|
||||||
'</button>';
|
'</button>';
|
||||||
|
|
||||||
return memo + '<tr>\n' +
|
return memo + '<tr>\n' +
|
||||||
' <td>'+ lobby.user1Name +'</td>\n' +
|
' <td>'+ lobby.user1Info.name +'</td>\n' +
|
||||||
' <td>'+ lobby.user2Name +'</td>\n' +
|
' <td>'+ lobby.user2Info.name +'</td>\n' +
|
||||||
' <td>'+ lobby.status +'</td>\n' +
|
' <td>'+ lobby.status +'</td>\n' +
|
||||||
' <td>' + joinButton + ' ' + observerButton +
|
' <td>' + joinButton + ' ' + observerButton +
|
||||||
' </td>' +
|
' </td>' +
|
||||||
@ -129,6 +129,9 @@ window.onload = function() {
|
|||||||
switchToLobby(message.lobby.maps);
|
switchToLobby(message.lobby.maps);
|
||||||
renderPlayersAndStats(message.lobby);
|
renderPlayersAndStats(message.lobby);
|
||||||
break;
|
break;
|
||||||
|
case "lobbyError":
|
||||||
|
console.log(message);
|
||||||
|
disconnectLobby(message.error);
|
||||||
case "lobbies":
|
case "lobbies":
|
||||||
lobbyList.lobbies = message.lobbies;
|
lobbyList.lobbies = message.lobbies;
|
||||||
lobbyList.render();
|
lobbyList.render();
|
||||||
@ -173,6 +176,12 @@ function switchToLobby(maps) {
|
|||||||
$("#decider").show();
|
$("#decider").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function disconnectLobby(error) {
|
||||||
|
$("#decider").hide();
|
||||||
|
$("#lobbies").show();
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
|
||||||
function renderMaps(maps) {
|
function renderMaps(maps) {
|
||||||
var resHtml = "";
|
var resHtml = "";
|
||||||
|
|
||||||
@ -203,51 +212,51 @@ function renderPlayersAndStats(lobby) {
|
|||||||
var readyImg = "<img src='/assets/images/buttons/isAuto.png' class='readyImg'/>";
|
var readyImg = "<img src='/assets/images/buttons/isAuto.png' class='readyImg'/>";
|
||||||
var notReadyImg = "<img src='/assets/images/buttons/isNotAuto.png' class='readyImg'/>";
|
var notReadyImg = "<img src='/assets/images/buttons/isNotAuto.png' class='readyImg'/>";
|
||||||
|
|
||||||
if(lobby.user1Name === userName){
|
if(lobby.user1Info.name === userName){
|
||||||
if(lobby.user1Ready){
|
if(lobby.user1Info.isReady){
|
||||||
player1ReadyBtn = "<button class='btn btn-default' onclick='setNotReady()'>"+readyImg+" Ready "+readyImg+"</button>"
|
player1ReadyBtn = "<button class='btn btn-default' onclick='setNotReady()'>"+readyImg+" Ready "+readyImg+"</button>"
|
||||||
}else{
|
}else{
|
||||||
player1ReadyBtn = "<button class='btn btn-default' onclick='setReady()'>"+notReadyImg+" Not ready "+notReadyImg+"</button>"
|
player1ReadyBtn = "<button class='btn btn-default' onclick='setReady()'>"+notReadyImg+" Not ready "+notReadyImg+"</button>"
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(lobby.user1Ready){
|
if(lobby.user1Info.isReady){
|
||||||
player1ReadyBtn = readyImg
|
player1ReadyBtn = readyImg
|
||||||
}else{
|
}else{
|
||||||
player1ReadyBtn = notReadyImg
|
player1ReadyBtn = notReadyImg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(lobby.user2Name === userName){
|
if(lobby.user2Info.name === userName){
|
||||||
if(lobby.user2Ready){
|
if(lobby.user2Info.isReady){
|
||||||
player2ReadyBtn = "<button class='btn btn-default' onclick='setNotReady()'>"+readyImg+" Ready "+readyImg+"</button>"
|
player2ReadyBtn = "<button class='btn btn-default' onclick='setNotReady()'>"+readyImg+" Ready "+readyImg+"</button>"
|
||||||
}else{
|
}else{
|
||||||
player2ReadyBtn = "<button class='btn btn-default' onclick='setReady()'>"+notReadyImg+" Not ready "+notReadyImg+"</button>"
|
player2ReadyBtn = "<button class='btn btn-default' onclick='setReady()'>"+notReadyImg+" Not ready "+notReadyImg+"</button>"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(lobby.user2Ready){
|
if(lobby.user2Info.isReady){
|
||||||
player2ReadyBtn = readyImg
|
player2ReadyBtn = readyImg
|
||||||
}else{
|
}else{
|
||||||
player2ReadyBtn = notReadyImg
|
player2ReadyBtn = notReadyImg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(lobby);
|
|
||||||
|
|
||||||
switch (lobby.status){
|
switch (lobby.status){
|
||||||
case "NotStarted()":
|
case "NotStarted()":
|
||||||
resHtml = "<div style='float: left'>" + lobby.user1Name + ": " + player1ReadyBtn +"</div>"
|
resHtml = "<div style='float: left'>" + lobby.user1Info.name + ": " + player1ReadyBtn +"</div>"
|
||||||
if(lobby.user2Name !== ""){
|
if(lobby.user2Info.name !== ""){
|
||||||
var kickBtn = ""
|
var kickBtn = ""
|
||||||
if(lobby.user1Name === userName){
|
if(lobby.user1Info.name === userName){
|
||||||
kickBtn = "<button class='btn btn-danger'>Kick</button>";
|
kickBtn = "<button class='btn btn-danger' onclick='kickSecondPlayer()'>Kick</button>";
|
||||||
}
|
}
|
||||||
resHtml += "<div style='float: right'>" + lobby.user2Name + ": " + player2ReadyBtn + "<br/>" + kickBtn + "</div>"
|
resHtml += "<div style='float: right'>" + lobby.user2Info.name + ": " + player2ReadyBtn + "<br/>" + kickBtn + "</div>"
|
||||||
}else{
|
}else{
|
||||||
resHtml += "<div style='float: right'>waiting 2-nd player...</div>"
|
resHtml += "<div style='float: right'>waiting 2-nd player...</div>"
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "Draft()":
|
case "Draft()":
|
||||||
resHtml = "<center><b>"+lobby.user1Name+ " vs " + lobby.user2Name +"</b></center>";
|
console.log(lobby.turn);
|
||||||
|
var playerTurn = (lobby.playerTurn === 1) ? lobby.user1Info.name : lobby.user2Info.name
|
||||||
|
resHtml = "<center><b>"+lobby.user1Info.name + " vs " + lobby.user2Info.name +"</b><br/>" + playerTurn +" turn</center>";
|
||||||
break;
|
break;
|
||||||
case "Finish()":
|
case "Finish()":
|
||||||
var lastMap = _.find(lobby.maps, function (map){
|
var lastMap = _.find(lobby.maps, function (map){
|
||||||
@ -288,6 +297,12 @@ function joinDecider(actorName){
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function kickSecondPlayer(){
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "kickSecondPlayer"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function convertMapName (techMapName) {
|
function convertMapName (techMapName) {
|
||||||
var mapName = techMapName.replace("2p_", "").replaceAll("_", " ") + " (2)";
|
var mapName = techMapName.replace("2p_", "").replaceAll("_", " ") + " (2)";
|
||||||
return mapName.charAt(0).toUpperCase() + mapName.slice(1);
|
return mapName.charAt(0).toUpperCase() + mapName.slice(1);
|
||||||
|
|||||||
@ -88,7 +88,7 @@ class HomeController @Inject()(cc: ControllerComponents) extends AbstractControl
|
|||||||
* Returns true if the value of the Origin header contains an acceptable value.
|
* Returns true if the value of the Origin header contains an acceptable value.
|
||||||
*/
|
*/
|
||||||
def originMatches(origin: String): Boolean = {
|
def originMatches(origin: String): Boolean = {
|
||||||
origin.contains("localhost:9000") || origin.contains("localhost:19001")
|
origin.contains("139.59.210.74") || origin.contains("localhost") || origin.contains("localhost:9000") || origin.contains("localhost:19001")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
# https://www.playframework.com/documentation/latest/SecurityHeaders
|
|
||||||
# Connect to localhost:9000 for content security policy on websockets
|
|
||||||
play.filters.headers {
|
|
||||||
contentSecurityPolicy = "connect-src 'self' ws://localhost:9000"
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://www.playframework.com/documentation/latest/AllowedHostsFilter
|
# https://www.playframework.com/documentation/latest/AllowedHostsFilter
|
||||||
# Allow requests to localhost:9000.
|
# Allow requests to localhost:9000.
|
||||||
play.filters.hosts {
|
play.filters.hosts {
|
||||||
allowed = ["localhost:9000", "localhost"]
|
allowed = ["localhost:9000", "localhost", "139.59.210.74"]
|
||||||
}
|
}
|
||||||
|
|
||||||
maps = ["2p_battle_marshes", "2p_fallen_city", "2p_fata_morgana_[Rem]", "2p_meeting_of_minds", "2p_outer_reaches", "2p_quests_triumph", "2p_shrine_of_excellion_[Rem]", "2p_titan_fall_[Rem]", "2p_tranquilitys_end_[rem]", "2p_blood_river_[Rem]", "2p_emerald_river", "2p_deadly_fun_archeology", "2p_fraziersdemise"]
|
maps = ["2p_battle_marshes", "2p_fallen_city", "2p_fata_morgana_[Rem]", "2p_meeting_of_minds", "2p_outer_reaches", "2p_quests_triumph", "2p_shrine_of_excellion_[Rem]", "2p_titan_fall_[Rem]", "2p_tranquilitys_end_[rem]", "2p_blood_river_[Rem]", "2p_emerald_river", "2p_deadly_fun_archeology", "2p_fraziersdemise"]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user