瀏覽代碼

Resolves some Battleground issues (#4725)

* Queue data will now be kept available until a Battleground event is over.
* Players can now join an active Battleground.
* Adds a more detailed queue state tracking feature.
* When a battleground is being prepared and not enough players click accept, stop the battleground from beginning.
* When a player logs off or leaves a queue they will now properly be removed from the queue.
* Cleans up MinPlayers and MaxPlayers parsing to not accept values less than 1.
* Resolves players not being able to join an active battleground (up to MaxPlayers) unless someone quit early.
* Adds a team size priority check to avoid adding more players to a larger team.
* Fixes battlechat messages.
* Adds a battle_config to disable the ability for players on maps with MF_NOWARP to join Battlegrounds.
* Adds a new optional ActiveEvent label to the database.
* Fixes a typo in Flavius 2 during unbooking process.
* Cleans up the behavior of the Battle Therapists to use areapercentheal and areawarp.
* Fixes KVM score not properly set on the first Battleground.
* Fixes the global timer checks not ending the main timers on Flavius and Tierra Gorge.
* Added an extra bg_reserve on global timer checks in case someone tries to join the battleground just before it's ended.
* Fixed OnReadyCheck being called on global timer checks, causing the battlegrounds to start when there were no players on the map.
* Fixed a bug on Tierra Gorge where multiple barricade walls could be stacked.
* Fixes Croix also receiving 9 badges when Guillaume wins.
* Adjusts StartDelay default to 0.
* Add a database setting to give the ability to enable or disable joining as an individual, party, or guild.
* Add a database setting to restrict jobs from entering Battlegrounds.
* Better documents script commands bg_reserve and bg_unbook.
* Other fixes and cleanups.
Thanks to @roSBK and @Daegaladh, @admkakaroto, @Artuvazro, and @Atemo!
Co-authored-by: Daegaladh <Daegaladh@users.noreply.github.com>
Co-authored-by: Atemo <Atemo@users.noreply.github.com>
Aleos 5 年之前
父節點
當前提交
14c388b401

+ 4 - 0
conf/battle/battleground.conf

@@ -30,3 +30,7 @@ bg_flee_penalty: 20
 
 
 // Interval before updating the bg-member map mini-dots (milliseconds)
 // Interval before updating the bg-member map mini-dots (milliseconds)
 bg_update_interval: 1000
 bg_update_interval: 1000
+
+// Before a player is warped into a Battleground from the Battleground Queue,
+// check to see if the player's current map has MF_NOWARP.
+bgqueue_nowarp_mapflag: no

+ 2 - 1
conf/msg_conf/map_msg.conf

@@ -354,9 +354,10 @@
 334: Total Domination
 334: Total Domination
 
 
 // Battlegrounds Queue
 // Battlegrounds Queue
-337: You may not join a battleground queue when you're in a battleground map.
+337: You can't apply to a battleground queue from this map.
 338: You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds.
 338: You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds.
 339: You can't apply to a battleground queue for %d seconds due to recently leaving one.
 339: You can't apply to a battleground queue for %d seconds due to recently leaving one.
+340: Participants were unable to join. Delaying entry for more participants.
 
 
 // Templates for @who output
 // Templates for @who output
 343: Name: %s
 343: Name: %s

+ 91 - 54
db/battleground_db.yml

@@ -29,7 +29,12 @@
 #   MinLevel          Minimum level required to join the battleground. (Default: 1)
 #   MinLevel          Minimum level required to join the battleground. (Default: 1)
 #   MaxLevel          Maximum level to join the battleground. (Default: MAX_LEVEL value)
 #   MaxLevel          Maximum level to join the battleground. (Default: MAX_LEVEL value)
 #   Deserter          Amount of time in seconds a player is marked deserter. (Default: 600)
 #   Deserter          Amount of time in seconds a player is marked deserter. (Default: 600)
-#   StartDelay        Amount of time in seconds once a queue is filled before a start message is sent to players. (Default: 30)
+#   StartDelay        Amount of time in seconds once a queue is filled before players are warped. (Default: 0)
+#   Join:             Which application type is accepted. The entryqueuelist.lub can visually disable these options.
+#     Solo            Able to join a queue as an individual. (Default: true)
+#     Party           Able to join a queue as a party. (Default: true)
+#     Guild           Able to join a queue as a guild. (Default: true)
+#   JobRestrictions   List of jobs that are unable to join the battleground.
 #   Locations:        Battleground location settings.
 #   Locations:        Battleground location settings.
 #     - Map           The map on which the battleground will be played.
 #     - Map           The map on which the battleground will be played.
 #       StartEvent    NPC event triggered when the battleground starts.
 #       StartEvent    NPC event triggered when the battleground starts.
@@ -38,12 +43,14 @@
 #         RespawnY    Y coordinate for warping on death.
 #         RespawnY    Y coordinate for warping on death.
 #         DeathEvent  NPC event triggered when a player dies.
 #         DeathEvent  NPC event triggered when a player dies.
 #         QuitEvent   NPC event triggered when a player quits.
 #         QuitEvent   NPC event triggered when a player quits.
+#         ActiveEvent NPC event triggered when a player joints an active battleground.
 #         Variable    Name of BG ID variable used in the battleground script.
 #         Variable    Name of BG ID variable used in the battleground script.
 #       TeamB:        TeamB settings.
 #       TeamB:        TeamB settings.
 #         RespawnX    X coordinate for warping on death.
 #         RespawnX    X coordinate for warping on death.
 #         RespawnY    Y coordinate for warping on death.
 #         RespawnY    Y coordinate for warping on death.
 #         DeathEvent  NPC event triggered when a player dies.
 #         DeathEvent  NPC event triggered when a player dies.
 #         QuitEvent   NPC event triggered when a player quits.
 #         QuitEvent   NPC event triggered when a player quits.
+#         ActiveEvent NPC event triggered when a player joints an active battleground.
 #         Variable    Name of BG ID variable used in the battleground script.
 #         Variable    Name of BG ID variable used in the battleground script.
 ###########################################################################
 ###########################################################################
 
 
@@ -53,121 +60,151 @@ Header:
 
 
 Body:
 Body:
   - Id: 1
   - Id: 1
-    Name: "Tierra Gorge"
+    Name: Tierra Gorge
     MinPlayers: 6
     MinPlayers: 6
     MinLevel: 80
     MinLevel: 80
+    JobRestrictions:
+      Novice: true
+      SuperNovice: true
+      Novice_High: true
+      Baby: true
+      Super_Baby: true
+      Super_Novice_E: true
+      Super_Baby_E: true
     Locations:
     Locations:
-      - Map: "bat_a01"
-        StartEvent: "start#bat_a01::OnReadyCheck"
+      - Map: bat_a01
+        StartEvent: start#bat_a01::OnReadyCheck
         TeamA:
         TeamA:
           RespawnX: 50
           RespawnX: 50
           RespawnY: 374
           RespawnY: 374
-          QuitEvent: "start#bat_a01::OnGuillaumeQuit"
-          Variable: "$@TierraBG1_id1"
+          QuitEvent: start#bat_a01::OnGuillaumeQuit
+          ActiveEvent: start#bat_a01::OnGuillaumeActive
+          Variable: $@TierraBG1_id1
         TeamB:
         TeamB:
           RespawnX: 42
           RespawnX: 42
           RespawnY: 16
           RespawnY: 16
-          QuitEvent: "start#bat_a01::OnCroixQuit"
-          Variable: "$@TierraBG1_id2"
-      - Map: "bat_a02"
-        StartEvent: "start#bat_a02::OnReadyCheck"
+          QuitEvent: start#bat_a01::OnCroixQuit
+          ActiveEvent: start#bat_a01::OnCroixActive
+          Variable: $@TierraBG1_id2
+      - Map: bat_a02
+        StartEvent: start#bat_a02::OnReadyCheck
         TeamA:
         TeamA:
           RespawnX: 50
           RespawnX: 50
           RespawnY: 374
           RespawnY: 374
-          QuitEvent: "start#bat_a02::OnGuillaumeQuit"
-          Variable: "$@TierraBG2_id1"
+          QuitEvent: start#bat_a02::OnGuillaumeQuit
+          ActiveEvent: start#bat_a02::OnGuillaumeActive
+          Variable: $@TierraBG2_id1
         TeamB:
         TeamB:
           RespawnX: 42
           RespawnX: 42
           RespawnY: 16
           RespawnY: 16
-          QuitEvent: "start#bat_a02::OnCroixQuit"
-          Variable: "$@TierraBG2_id2"
+          QuitEvent: start#bat_a02::OnCroixQuit
+          ActiveEvent: start#bat_a02::OnCroixActive
+          Variable: $@TierraBG2_id2
   - Id: 2
   - Id: 2
-    Name: "Flavius"
+    Name: Flavius
     MinPlayers: 6
     MinPlayers: 6
     MinLevel: 80
     MinLevel: 80
+    JobRestrictions:
+      Novice: true
+      SuperNovice: true
+      Novice_High: true
+      Baby: true
+      Super_Baby: true
+      Super_Novice_E: true
+      Super_Baby_E: true
     Locations:
     Locations:
-      - Map: "bat_b01"
-        StartEvent: "start#bat_b01::OnReadyCheck"
+      - Map: bat_b01
+        StartEvent: start#bat_b01::OnReadyCheck
         TeamA:
         TeamA:
           RespawnX: 10
           RespawnX: 10
           RespawnY: 290
           RespawnY: 290
-          QuitEvent: "start#bat_b01::OnGuillaumeQuit"
-          Variable: "$@FlaviusBG1_id1"
+          QuitEvent: start#bat_b01::OnGuillaumeQuit
+          ActiveEvent: start#bat_b01::OnGuillaumeActive
+          Variable: $@FlaviusBG1_id1
         TeamB:
         TeamB:
           RespawnX: 390
           RespawnX: 390
           RespawnY: 10
           RespawnY: 10
-          QuitEvent: "start#bat_b01::OnCroixQuit"
-          Variable: "$@FlaviusBG1_id2"
-      - Map: "bat_b02"
-        StartEvent: "start#bat_b02::OnReadyCheck"
+          QuitEvent: start#bat_b01::OnCroixQuit
+          ActiveEvent: start#bat_b01::OnCroixActive
+          Variable: $@FlaviusBG1_id2
+      - Map: bat_b02
+        StartEvent: start#bat_b02::OnReadyCheck
         TeamA:
         TeamA:
           RespawnX: 10
           RespawnX: 10
           RespawnY: 290
           RespawnY: 290
-          QuitEvent: "start#bat_b02::OnGuillaumeQuit"
-          Variable: "$@FlaviusBG2_id1"
+          QuitEvent: start#bat_b02::OnGuillaumeQuit
+          ActiveEvent: start#bat_b02::OnGuillaumeActive
+          Variable: $@FlaviusBG2_id1
         TeamB:
         TeamB:
           RespawnX: 390
           RespawnX: 390
           RespawnY: 10
           RespawnY: 10
-          QuitEvent: "start#bat_b02::OnCroixQuit"
-          Variable: "$@FlaviusBG2_id2"
+          QuitEvent: start#bat_b02::OnCroixQuit
+          ActiveEvent: start#bat_b02::OnCroixActive
+          Variable: $@FlaviusBG2_id2
   - Id: 3
   - Id: 3
-    Name: "KVM (Level 80 and up)"
+    Name: KVM (Level 80 and up)
     MinPlayers: 5
     MinPlayers: 5
     MinLevel: 80
     MinLevel: 80
     Locations:
     Locations:
-      - Map: "bat_c01"
-        StartEvent: "KvM01_BG::OnStart"
+      - Map: bat_c01
+        StartEvent: KvM01_BG::OnStart
         TeamA:
         TeamA:
           RespawnX: 52
           RespawnX: 52
           RespawnY: 129
           RespawnY: 129
-          DeathEvent: "KvM01_BG::OnGuillaumeDie"
-          QuitEvent: "KvM01_BG::OnGuillaumeQuit"
-          Variable: "$@KvM01BG_id1"
+          DeathEvent: KvM01_BG::OnGuillaumeDie
+          QuitEvent: KvM01_BG::OnGuillaumeQuit
+          ActiveEvent: KvM01_BG::OnGuillaumeActive
+          Variable: $@KvM01BG_id1
         TeamB:
         TeamB:
           RespawnX: 147
           RespawnX: 147
           RespawnY: 55
           RespawnY: 55
-          DeathEvent: "KvM01_BG::OnCroixDie"
-          QuitEvent: "KvM01_BG::OnCroixQuit"
-          Variable: "$@KvM01BG_id2"
+          DeathEvent: KvM01_BG::OnCroixDie
+          QuitEvent: KvM01_BG::OnCroixQuit
+          ActiveEvent: KvM01_BG::OnCroixActive
+          Variable: $@KvM01BG_id2
   - Id: 4
   - Id: 4
-    Name: "KVM (Level 60~79)"
+    Name: KVM (Level 60~79)
     MinPlayers: 5
     MinPlayers: 5
     MinLevel: 60
     MinLevel: 60
     MaxLevel: 79
     MaxLevel: 79
     Locations:
     Locations:
-      - Map: "bat_c02"
-        StartEvent: "KvM02_BG::OnStart"
+      - Map: bat_c02
+        StartEvent: KvM02_BG::OnStart
         TeamA:
         TeamA:
           RespawnX: 52
           RespawnX: 52
           RespawnY: 129
           RespawnY: 129
-          DeathEvent: "KvM02_BG::OnGuillaumeDie"
-          QuitEvent: "KvM02_BG::OnGuillaumeQuit"
-          Variable: "$@KvM02BG_id1"
+          DeathEvent: KvM02_BG::OnGuillaumeDie
+          QuitEvent: KvM02_BG::OnGuillaumeQuit
+          ActiveEvent: KvM02_BG::OnGuillaumeActive
+          Variable: $@KvM02BG_id1
         TeamB:
         TeamB:
           RespawnX: 147
           RespawnX: 147
           RespawnY: 55
           RespawnY: 55
-          DeathEvent: "KvM02_BG::OnCroixDie"
-          QuitEvent: "KvM02_BG::OnCroixQuit"
-          Variable: "$@KvM02BG_id2"
+          DeathEvent: KvM02_BG::OnCroixDie
+          QuitEvent: KvM02_BG::OnCroixQuit
+          ActiveEvent: KvM02_BG::OnCroixActive
+          Variable: $@KvM02BG_id2
   - Id: 5
   - Id: 5
-    Name: "KVM (Level 59 and below"
+    Name: KVM (Level 59 and below
     MinPlayers: 5
     MinPlayers: 5
     MaxLevel: 59
     MaxLevel: 59
     Locations:
     Locations:
-      - Map: "bat_c03"
-        StartEvent: "KvM03_BG::OnStart"
+      - Map: bat_c03
+        StartEvent: KvM03_BG::OnStart
         TeamA:
         TeamA:
           RespawnX: 52
           RespawnX: 52
           RespawnY: 129
           RespawnY: 129
-          DeathEvent: "KvM03_BG::OnGuillaumeDie"
-          QuitEvent: "KvM03_BG::OnGuillaumeQuit"
-          Variable: "$@KvM03BG_id1"
+          DeathEvent: KvM03_BG::OnGuillaumeDie
+          QuitEvent: KvM03_BG::OnGuillaumeQuit
+          ActiveEvent: KvM03_BG::OnGuillaumeActive
+          Variable: $@KvM03BG_id1
         TeamB:
         TeamB:
           RespawnX: 147
           RespawnX: 147
           RespawnY: 55
           RespawnY: 55
-          DeathEvent: "KvM03_BG::OnCroixDie"
-          QuitEvent: "KvM03_BG::OnCroixQuit"
-          Variable: "$@KvM03BG_id2"
+          DeathEvent: KvM03_BG::OnCroixDie
+          QuitEvent: KvM03_BG::OnCroixQuit
+          ActiveEvent: KvM03_BG::OnCroixActive
+          Variable: $@KvM03BG_id2
 
 
 Footer:
 Footer:
   Imports:
   Imports:

+ 8 - 1
db/import-tmpl/battleground_db.yml

@@ -29,7 +29,12 @@
 #   MinLevel          Minimum level required to join the battleground. (Default: 1)
 #   MinLevel          Minimum level required to join the battleground. (Default: 1)
 #   MaxLevel          Maximum level to join the battleground. (Default: MAX_LEVEL value)
 #   MaxLevel          Maximum level to join the battleground. (Default: MAX_LEVEL value)
 #   Deserter          Amount of time in seconds a player is marked deserter. (Default: 600)
 #   Deserter          Amount of time in seconds a player is marked deserter. (Default: 600)
-#   StartDelay        Amount of time in seconds once a queue is filled before a start message is sent to players. (Default: 30)
+#   StartDelay        Amount of time in seconds once a queue is filled before players are warped. (Default: 0)
+#   Join:             Which application type is accepted. The entryqueuelist.lub can visually disable these options.
+#     Solo            Able to join a queue as an individual. (Default: true)
+#     Party           Able to join a queue as a party. (Default: true)
+#     Guild           Able to join a queue as a guild. (Default: true)
+#   JobRestrictions   List of jobs that are unable to join the battleground.
 #   Locations:        Battleground location settings.
 #   Locations:        Battleground location settings.
 #     - Map           The map on which the battleground will be played.
 #     - Map           The map on which the battleground will be played.
 #       StartEvent    NPC event triggered when the battleground starts.
 #       StartEvent    NPC event triggered when the battleground starts.
@@ -38,12 +43,14 @@
 #         RespawnY    Y coordinate for warping on death.
 #         RespawnY    Y coordinate for warping on death.
 #         DeathEvent  NPC event triggered when a player dies.
 #         DeathEvent  NPC event triggered when a player dies.
 #         QuitEvent   NPC event triggered when a player quits.
 #         QuitEvent   NPC event triggered when a player quits.
+#         ActiveEvent NPC event triggered when a player joints an active battleground.
 #         Variable    Name of BG ID variable used in the battleground script.
 #         Variable    Name of BG ID variable used in the battleground script.
 #       TeamB:        TeamB settings.
 #       TeamB:        TeamB settings.
 #         RespawnX    X coordinate for warping on death.
 #         RespawnX    X coordinate for warping on death.
 #         RespawnY    Y coordinate for warping on death.
 #         RespawnY    Y coordinate for warping on death.
 #         DeathEvent  NPC event triggered when a player dies.
 #         DeathEvent  NPC event triggered when a player dies.
 #         QuitEvent   NPC event triggered when a player quits.
 #         QuitEvent   NPC event triggered when a player quits.
+#         ActiveEvent NPC event triggered when a player joints an active battleground.
 #         Variable    Name of BG ID variable used in the battleground script.
 #         Variable    Name of BG ID variable used in the battleground script.
 ###########################################################################
 ###########################################################################
 
 

+ 7 - 7
doc/script_commands.txt

@@ -9393,19 +9393,19 @@ Example:
 
 
 ---------------------------------------
 ---------------------------------------
 
 
-*bg_reserve("<battleground_name>");
+*bg_reserve("<battleground_map_name>"{,<ended>});
 
 
-Reserves a slot for the given Battleground for the Battleground UI System.
+Reserves a Battleground map for the Battleground UI System. When a map is booked it prevents another similar
+queue from being created and will allow players to join an active Battlegrounds event.
 
 
-Note: 'bg_reserve' and 'bg_unbook' prevent the Battlegrounds queue from joining players in an active Battleground.
+If <ended> is true, then the Battleground is marked as over to prevent new players from joining. This state is meant
+for the period where players can get their Badges.
 
 
 ---------------------------------------
 ---------------------------------------
 
 
-*bg_unbook("<battleground_name>");
+*bg_unbook("<battleground_map_name>");
 
 
-Removes a spot for the given Battleground for the Battleground UI System.
-
-Note: 'bg_reserve' and 'bg_unbook' prevent the Battlegrounds queue from joining players in an active Battleground.
+Removes a Battleground map for the Battleground UI System. When a map is unbooked it allows a queue to be created.
 
 
 ---------------------------------------
 ---------------------------------------
 
 

+ 52 - 40
npc/battleground/flavius/flavius01.txt

@@ -58,25 +58,21 @@ OnStop:
 
 
 OnTimer1000:
 OnTimer1000:
 	stopnpctimer;
 	stopnpctimer;
-	if (!getbattleflag("feature.bgqueue"))
-		initnpctimer;
-	set .@chk_bat_a01,getmapusers("bat_b01");
-	if (.@chk_bat_a01 < 1) {
-		set $@FlaviusBG1, 0;
-		if( $@FlaviusBG1_id1 ) { bg_destroy $@FlaviusBG1_id1; set $@FlaviusBG1_id1, 0; }
-		if( $@FlaviusBG1_id2 ) { bg_destroy $@FlaviusBG1_id2; set $@FlaviusBG1_id2, 0; }
-		if (getbattleflag("feature.bgqueue")) {
+	if (bg_get_data($@FlaviusBG1_id1, 0) == 0 && bg_get_data($@FlaviusBG1_id2, 0) == 0) {
+		donpcevent "countdown#bat_b01::OnStop";
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_b01", true;
+		$@FlaviusBG1 = 0;
+		if( $@FlaviusBG1_id1 ) { bg_destroy $@FlaviusBG1_id1; $@FlaviusBG1_id1 = 0; }
+		if( $@FlaviusBG1_id2 ) { bg_destroy $@FlaviusBG1_id2; $@FlaviusBG1_id2 = 0; }
+		if (getbattleflag("feature.bgqueue"))
 			bg_unbook "bat_b01";
 			bg_unbook "bat_b01";
-			end;
-		} else
-			donpcevent "start#bat_b01::OnReadyCheck";
+		end;
 	}
 	}
-	if (getbattleflag("feature.bgqueue"))
-		initnpctimer;
+	initnpctimer;
 	end;
 	end;
 }
 }
 
 
-
 // Flavius Battleground Engine
 // Flavius Battleground Engine
 //============================================================
 //============================================================
 bat_b01,15,15,3	script	start#bat_b01	844,{
 bat_b01,15,15,3	script	start#bat_b01	844,{
@@ -139,13 +135,14 @@ OnReset:
 	donpcevent "guardian#bat_b01_b::OnEnable";
 	donpcevent "guardian#bat_b01_b::OnEnable";
 	donpcevent "cell#bat_b01_a::OnRed";
 	donpcevent "cell#bat_b01_a::OnRed";
 	donpcevent "cell#bat_b01_b::OnRed";
 	donpcevent "cell#bat_b01_b::OnRed";
-	donpcevent "time#bat_b01::OnEnable";
-	disablenpc "Guillaume Vintenar#b01_a";
-	disablenpc "Croix Vintenar#b01_b";
-	disablenpc "Vintenar#bat_b01_aover";
-	disablenpc "Vintenar#bat_b01_bover";
-	bg_warp $@FlaviusBG1_id1,"bat_b01",87,75;
-	bg_warp $@FlaviusBG1_id2,"bat_b01",311,224;
+	end;
+
+OnGuillaumeActive:
+	warp "bat_b01",87,75;
+	end;
+
+OnCroixActive:
+	warp "bat_b01",311,224;
 	end;
 	end;
 
 
 OnGuillaumeQuit:
 OnGuillaumeQuit:
@@ -181,6 +178,8 @@ OnMyMobDead:
 			enablenpc "Guillaume Vintenar#b01_a";
 			enablenpc "Guillaume Vintenar#b01_a";
 			enablenpc "Croix Vintenar#b01_b";
 			enablenpc "Croix Vintenar#b01_b";
 			donpcevent "time#bat_b01::OnStop";
 			donpcevent "time#bat_b01::OnStop";
+			if (getbattleflag("feature.bgqueue"))
+				bg_reserve "bat_b01", true;
 		}
 		}
 		else {
 		else {
 			set $@Croix_ScoreBG1,1;
 			set $@Croix_ScoreBG1,1;
@@ -215,6 +214,8 @@ OnMyMobDead:
 			enablenpc "Guillaume Vintenar#b01_a";
 			enablenpc "Guillaume Vintenar#b01_a";
 			enablenpc "Croix Vintenar#b01_b";
 			enablenpc "Croix Vintenar#b01_b";
 			donpcevent "time#bat_b01::OnStop";
 			donpcevent "time#bat_b01::OnStop";
+			if (getbattleflag("feature.bgqueue"))
+				bg_reserve "bat_b01", true;
 		}
 		}
 		else {
 		else {
 			set $@Guill_ScoreBG1,1;
 			set $@Guill_ScoreBG1,1;
@@ -314,12 +315,14 @@ bat_b01,10,294,3	script	Battle Therapist#b01_a	95,{
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_b01_rp1_a_warp";
+	// enablenpc "bat_b01_rp1_a_warp";
+	areapercentheal "bat_b01",0,280,20,300,100,100;
+	areawarp "bat_b01",0,280,20,300,"bat_b01",87,73;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_b01_rp1_a_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_b01_rp1_a_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	stopnpctimer;
 	stopnpctimer;
@@ -332,22 +335,25 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_b01_rp1_a_warp";
+	// disablenpc "bat_b01_rp1_a_warp";
 	disablenpc "Battle Therapist#b01_a";
 	disablenpc "Battle Therapist#b01_a";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 }
 }
 
 
+/*
+// replaced by areapercentheal and areawarp to prevent enqueue issue
 bat_b01,10,290,0	script	bat_b01_rp1_a_warp	45,10,10,{
 bat_b01,10,290,0	script	bat_b01_rp1_a_warp	45,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_b01_rp1_a_warp";
 	disablenpc "bat_b01_rp1_a_warp";
 	end;
 	end;
 
 
-OnTouch_:
+OnTouch:
 	percentheal 100,100;
 	percentheal 100,100;
 	warp "bat_b01",87,73;
 	warp "bat_b01",87,73;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_b01,389,14,3	script	Battle Therapist#b01_b	95,{
 bat_b01,389,14,3	script	Battle Therapist#b01_b	95,{
 	specialeffect2 EF_HEAL;
 	specialeffect2 EF_HEAL;
@@ -359,12 +365,14 @@ bat_b01,389,14,3	script	Battle Therapist#b01_b	95,{
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_b01_rp1_b_warp";
+	// enablenpc "bat_b01_rp1_b_warp";
+	areapercentheal "bat_b01",379,0,399,20,100,100;
+	areawarp "bat_b01",379,0,399,20,"bat_b01",312,225;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_b01_rp1_b_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_b01_rp1_b_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	stopnpctimer;
 	stopnpctimer;
@@ -377,15 +385,16 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_b01_rp1_b_warp";
+	// disablenpc "bat_b01_rp1_b_warp";
 	disablenpc "Battle Therapist#b01_b";
 	disablenpc "Battle Therapist#b01_b";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 }
 }
 
 
-bat_b01,389,10,0	script	bat_b01_rp1_b_warp	45,9,9,{
+/*
+bat_b01,389,10,0	script	bat_b01_rp1_b_warp	45,10,10,{
 OnInit:
 OnInit:
-	disablenpc "bat_b01_rp1_a_warp";
+	disablenpc "bat_b01_rp1_b_warp";
 	end;
 	end;
 
 
 OnTouch:
 OnTouch:
@@ -393,6 +402,7 @@ OnTouch:
 	warp "bat_b01",312,225;
 	warp "bat_b01",312,225;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_b01,87,76,0	script	A_CODE#bat_b01	-1,5,5,{
 bat_b01,87,76,0	script	A_CODE#bat_b01	-1,5,5,{
 OnTouch:
 OnTouch:
@@ -490,6 +500,8 @@ OnTimer1830000:
 	bg_warp $@FlaviusBG1_id2,"bat_b01",390,10;
 	bg_warp $@FlaviusBG1_id2,"bat_b01",390,10;
 	enablenpc "Vintenar#bat_b01_aover";
 	enablenpc "Vintenar#bat_b01_aover";
 	enablenpc "Vintenar#bat_b01_bover";
 	enablenpc "Vintenar#bat_b01_bover";
+	if (getbattleflag("feature.bgqueue"))
+		bg_reserve "bat_b01", true;
 	end;
 	end;
 
 
 OnTimer1900000:
 OnTimer1900000:
@@ -526,11 +538,11 @@ bat_b01,10,294,3	script	Vintenar#bat_b01_aover	419,{
 	set .@A_B_gap,$@Guill_ScoreBG1 - $@Croix_ScoreBG1;
 	set .@A_B_gap,$@Guill_ScoreBG1 - $@Croix_ScoreBG1;
 	if ($@FlaviusBG1_id1 == getcharid(4)) {
 	if ($@FlaviusBG1_id1 == getcharid(4)) {
 		if (.@A_B_gap > 0)
 		if (.@A_B_gap > 0)
-			callfunc "F_BG_Badge",1,"Guillaume","Flavius";
+			callfunc "F_BG_Badge",1,"Guillaume","Flavius"; //Guillaume wins
 		else if (.@A_B_gap == 0)
 		else if (.@A_B_gap == 0)
-			callfunc "F_BG_Badge",0,"Guillaume","Flavius";
+			callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Tie
 		else
 		else
-			callfunc "F_BG_Badge",0,"Guillaume","Flavius";
+			callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Croix wins
 	}
 	}
 	else {
 	else {
 		mes "[Axl Rose]";
 		mes "[Axl Rose]";
@@ -551,11 +563,11 @@ bat_b01,389,14,3	script	Vintenar#bat_b01_bover	415,{
 	set .@A_B_gap,$@Guill_ScoreBG1 - $@Croix_ScoreBG1;
 	set .@A_B_gap,$@Guill_ScoreBG1 - $@Croix_ScoreBG1;
 	if ($@FlaviusBG1_id2 == getcharid(4)) {
 	if ($@FlaviusBG1_id2 == getcharid(4)) {
 		if (.@A_B_gap > 0)
 		if (.@A_B_gap > 0)
-			callfunc "F_BG_Badge",1,"Croix","Flavius";
+			callfunc "F_BG_Badge",0,"Croix","Flavius"; //Guillaume wins
 		else if (.@A_B_gap == 0)
 		else if (.@A_B_gap == 0)
-			callfunc "F_BG_Badge",0,"Croix","Flavius";
+			callfunc "F_BG_Badge",0,"Croix","Flavius"; //Tie
 		else
 		else
-			callfunc "F_BG_Badge",1,"Croix","Flavius";
+			callfunc "F_BG_Badge",1,"Croix","Flavius"; //Croix wins
 	}
 	}
 	else {
 	else {
 		mes "[Swandery]";
 		mes "[Swandery]";

+ 56 - 44
npc/battleground/flavius/flavius02.txt

@@ -59,21 +59,18 @@ OnStop:
 
 
 OnTimer1000:
 OnTimer1000:
 	stopnpctimer;
 	stopnpctimer;
-	if (!getbattleflag("feature.bgqueue"))
-		initnpctimer;
-	set .@chk_bat_a01,getmapusers("bat_b02");
-	if (.@chk_bat_a01 < 1) {
-		set $@FlaviusBG2, 0;
-		if( $@FlaviusBG2_id1 ) { bg_destroy $@FlaviusBG2_id1; set $@FlaviusBG2_id1, 0; }
-		if( $@FlaviusBG2_id2 ) { bg_destroy $@FlaviusBG2_id2; set $@FlaviusBG2_id2, 0; }
-		if (getbattleflag("feature.bgqueue")) {
-			bg_unbook "bat_b01";
-			end;
-		} else
-			donpcevent "start#bat_b01::OnReadyCheck";
+	if (bg_get_data($@FlaviusBG2_id1, 0) == 0 && bg_get_data($@FlaviusBG2_id2, 0) == 0) {
+		donpcevent "countdown#bat_b02::OnStop";
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_b02", true;
+		$@FlaviusBG2 = 0;
+		if( $@FlaviusBG2_id1 ) { bg_destroy $@FlaviusBG2_id1; $@FlaviusBG2_id1 = 0; }
+		if( $@FlaviusBG2_id2 ) { bg_destroy $@FlaviusBG2_id2; $@FlaviusBG2_id2 = 0; }
+		if (getbattleflag("feature.bgqueue"))
+			bg_unbook "bat_b02";
+		end;
 	}
 	}
-	if (getbattleflag("feature.bgqueue"))
-		initnpctimer;
+	initnpctimer;
 	end;
 	end;
 }
 }
 
 
@@ -89,7 +86,7 @@ OnReadyCheck:
 	if( $@FlaviusBG2 )
 	if( $@FlaviusBG2 )
 		end;
 		end;
 	if (!getbattleflag("feature.bgqueue")) {
 	if (!getbattleflag("feature.bgqueue")) {
-			set .@Guillaume, getwaitingroomstate(0,"Lieutenant Huvas");
+		set .@Guillaume, getwaitingroomstate(0,"Lieutenant Huvas");
 		set .@Croix, getwaitingroomstate(0,"Lieutenant Yukon");
 		set .@Croix, getwaitingroomstate(0,"Lieutenant Yukon");
 		if( !.@Guillaume && !.@Croix ) {
 		if( !.@Guillaume && !.@Croix ) {
 			donpcevent "#bat_b02_timer::OnStop";
 			donpcevent "#bat_b02_timer::OnStop";
@@ -102,8 +99,8 @@ OnReadyCheck:
 	set $@FlaviusBG2_Victory, 0;
 	set $@FlaviusBG2_Victory, 0;
 	set $@Croix_ScoreBG2, 0;
 	set $@Croix_ScoreBG2, 0;
 	set $@Guill_ScoreBG2, 0;
 	set $@Guill_ScoreBG2, 0;
-
 	bg_updatescore "bat_b02",$@Guill_ScoreBG2,$@Croix_ScoreBG2;
 	bg_updatescore "bat_b02",$@Guill_ScoreBG2,$@Croix_ScoreBG2;
+
 	if (!getbattleflag("feature.bgqueue")) {
 	if (!getbattleflag("feature.bgqueue")) {
 		donpcevent "Lieutenant Huvas::OnEnterBG";
 		donpcevent "Lieutenant Huvas::OnEnterBG";
 		donpcevent "Lieutenant Yukon::OnEnterBG";
 		donpcevent "Lieutenant Yukon::OnEnterBG";
@@ -140,13 +137,14 @@ OnReset:
 	donpcevent "guardian#bat_b02_b::OnEnable";
 	donpcevent "guardian#bat_b02_b::OnEnable";
 	donpcevent "cell#bat_b02_a::OnRed";
 	donpcevent "cell#bat_b02_a::OnRed";
 	donpcevent "cell#bat_b02_b::OnRed";
 	donpcevent "cell#bat_b02_b::OnRed";
-	donpcevent "time#bat_b02::OnEnable";
-	disablenpc "Guillaume Vintenar#b02_a";
-	disablenpc "Croix Vintenar#b02_b";
-	disablenpc "Vintenar#bat_b02_aover";
-	disablenpc "Vintenar#bat_b02_bover";
-	bg_warp $@FlaviusBG2_id1,"bat_b02",87,75;
-	bg_warp $@FlaviusBG2_id2,"bat_b02",311,224;
+	end;
+
+OnGuillaumeActive:
+	warp "bat_b02",87,75;
+	end;
+
+OnCroixActive:
+	warp "bat_b02",311,224;
 	end;
 	end;
 
 
 OnGuillaumeQuit:
 OnGuillaumeQuit:
@@ -182,6 +180,8 @@ OnMyMobDead:
 			enablenpc "Guillaume Vintenar#b02_a";
 			enablenpc "Guillaume Vintenar#b02_a";
 			enablenpc "Croix Vintenar#b02_b";
 			enablenpc "Croix Vintenar#b02_b";
 			donpcevent "time#bat_b02::OnStop";
 			donpcevent "time#bat_b02::OnStop";
+			if (getbattleflag("feature.bgqueue"))
+				bg_reserve "bat_b02", true;
 		}
 		}
 		else {
 		else {
 			set $@Croix_ScoreBG2,1;
 			set $@Croix_ScoreBG2,1;
@@ -216,6 +216,8 @@ OnMyMobDead:
 			enablenpc "Guillaume Vintenar#b02_a";
 			enablenpc "Guillaume Vintenar#b02_a";
 			enablenpc "Croix Vintenar#b02_b";
 			enablenpc "Croix Vintenar#b02_b";
 			donpcevent "time#bat_b02::OnStop";
 			donpcevent "time#bat_b02::OnStop";
+			if (getbattleflag("feature.bgqueue"))
+				bg_reserve "bat_b02", true;
 		}
 		}
 		else {
 		else {
 			set $@Guill_ScoreBG2,1;
 			set $@Guill_ScoreBG2,1;
@@ -315,12 +317,14 @@ bat_b02,10,294,3	script	Battle Therapist#b02_a	95,{
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_b02_rp1_a_warp";
+	// enablenpc "bat_b02_rp1_a_warp";
+	areapercentheal "bat_b02",0,280,20,300,100,100;
+	areawarp "bat_b02",0,280,20,300,"bat_b02",87,73;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_b02_rp1_a_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_b02_rp1_a_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	stopnpctimer;
 	stopnpctimer;
@@ -333,22 +337,24 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_b02_rp1_a_warp";
+	// disablenpc "bat_b02_rp1_a_warp";
 	disablenpc "Battle Therapist#b02_a";
 	disablenpc "Battle Therapist#b02_a";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 }
 }
 
 
+/*
 bat_b02,10,290,0	script	bat_b02_rp1_a_warp	45,10,10,{
 bat_b02,10,290,0	script	bat_b02_rp1_a_warp	45,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_b02_rp1_a_warp";
 	disablenpc "bat_b02_rp1_a_warp";
 	end;
 	end;
 
 
-OnTouch_:
+OnTouch:
 	percentheal 100,100;
 	percentheal 100,100;
 	warp "bat_b02",87,73;
 	warp "bat_b02",87,73;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_b02,389,14,3	script	Battle Therapist#b02_b	95,{
 bat_b02,389,14,3	script	Battle Therapist#b02_b	95,{
 	specialeffect2 EF_HEAL;
 	specialeffect2 EF_HEAL;
@@ -360,12 +366,14 @@ bat_b02,389,14,3	script	Battle Therapist#b02_b	95,{
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_b02_rp1_b_warp";
+	areapercentheal "bat_b02",379,0,399,20,100,100;
+	areawarp "bat_b02",379,0,399,20,"bat_b02",312,225;
+	// enablenpc "bat_b02_rp1_b_warp";
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_b02_rp1_b_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_b02_rp1_b_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	stopnpctimer;
 	stopnpctimer;
@@ -378,15 +386,16 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_b02_rp1_b_warp";
+	// disablenpc "bat_b02_rp1_b_warp";
 	disablenpc "Battle Therapist#b02_b";
 	disablenpc "Battle Therapist#b02_b";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 }
 }
 
 
-bat_b02,389,10,0	script	bat_b02_rp1_b_warp	45,9,9,{
+/*
+bat_b02,389,10,0	script	bat_b02_rp1_b_warp	45,10,10,{
 OnInit:
 OnInit:
-	disablenpc "bat_b02_rp1_a_warp";
+	disablenpc "bat_b02_rp1_b_warp";
 	end;
 	end;
 
 
 OnTouch:
 OnTouch:
@@ -394,18 +403,19 @@ OnTouch:
 	warp "bat_b02",312,225;
 	warp "bat_b02",312,225;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_b02,87,76,0	script	A_CODE#bat_b02	-1,5,5,{
 bat_b02,87,76,0	script	A_CODE#bat_b02	-1,5,5,{
 OnTouch:
 OnTouch:
 	if (checkquest(2070) < 0)
 	if (checkquest(2070) < 0)
-		//setquest 2070;
+		setquest 2070;
 	end;
 	end;
 }
 }
 
 
 bat_b02,312,224,0	script	B_CODE#bat_b02	-1,5,5,{
 bat_b02,312,224,0	script	B_CODE#bat_b02	-1,5,5,{
 OnTouch:
 OnTouch:
 	if (checkquest(2070) < 0)
 	if (checkquest(2070) < 0)
-		//setquest 2070;
+		setquest 2070;
 	end;
 	end;
 }
 }
 
 
@@ -491,6 +501,8 @@ OnTimer1830000:
 	bg_warp $@FlaviusBG2_id2,"bat_b02",390,10;
 	bg_warp $@FlaviusBG2_id2,"bat_b02",390,10;
 	enablenpc "Vintenar#bat_b02_aover";
 	enablenpc "Vintenar#bat_b02_aover";
 	enablenpc "Vintenar#bat_b02_bover";
 	enablenpc "Vintenar#bat_b02_bover";
+	if (getbattleflag("feature.bgqueue"))
+		bg_reserve "bat_b02", true;
 	end;
 	end;
 
 
 OnTimer1900000:
 OnTimer1900000:
@@ -527,11 +539,11 @@ bat_b02,10,294,3	script	Vintenar#bat_b02_aover	419,{
 	set .@A_B_gap,$@Guill_ScoreBG2 - $@Croix_ScoreBG2;
 	set .@A_B_gap,$@Guill_ScoreBG2 - $@Croix_ScoreBG2;
 	if ($@FlaviusBG2_id1 == getcharid(4)) {
 	if ($@FlaviusBG2_id1 == getcharid(4)) {
 		if (.@A_B_gap > 0)
 		if (.@A_B_gap > 0)
-			callfunc "F_BG_Badge",1,"Guillaume","Flavius";
+			callfunc "F_BG_Badge",1,"Guillaume","Flavius"; //Guillaume wins
 		else if (.@A_B_gap == 0)
 		else if (.@A_B_gap == 0)
-			callfunc "F_BG_Badge",0,"Guillaume","Flavius";
+			callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Tie
 		else
 		else
-			callfunc "F_BG_Badge",0,"Guillaume","Flavius";
+			callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Croix wins
 	}
 	}
 	else {
 	else {
 		mes "[Axl Rose]";
 		mes "[Axl Rose]";
@@ -552,11 +564,11 @@ bat_b02,389,14,3	script	Vintenar#bat_b02_bover	415,{
 	set .@A_B_gap,$@Guill_ScoreBG2 - $@Croix_ScoreBG2;
 	set .@A_B_gap,$@Guill_ScoreBG2 - $@Croix_ScoreBG2;
 	if ($@FlaviusBG2_id2 == getcharid(4)) {
 	if ($@FlaviusBG2_id2 == getcharid(4)) {
 		if (.@A_B_gap > 0)
 		if (.@A_B_gap > 0)
-			callfunc "F_BG_Badge",1,"Croix","Flavius";
+			callfunc "F_BG_Badge",0,"Croix","Flavius"; //Guillaume wins
 		else if (.@A_B_gap == 0)
 		else if (.@A_B_gap == 0)
-			callfunc "F_BG_Badge",0,"Croix","Flavius";
+			callfunc "F_BG_Badge",0,"Croix","Flavius"; //Tie
 		else
 		else
-			callfunc "F_BG_Badge",1,"Croix","Flavius";
+			callfunc "F_BG_Badge",1,"Croix","Flavius"; //Croix wins
 	}
 	}
 	else {
 	else {
 		mes "[Swandery]";
 		mes "[Swandery]";

+ 11 - 1
npc/battleground/kvm/kvm01.txt

@@ -155,6 +155,14 @@ OnCroixDie:
 	}
 	}
 	end;
 	end;
 
 
+OnGuillaumeActive:
+	warp "bat_c01",61,120;
+	end;
+
+OnCroixActive:
+	warp "bat_c01",138,63;
+	end;
+
 OnStart:
 OnStart:
 	disablenpc "KVM Officer#KVM01A";
 	disablenpc "KVM Officer#KVM01A";
 	disablenpc "KVM Officer#KVM01B";
 	disablenpc "KVM Officer#KVM01B";
@@ -224,6 +232,7 @@ OnTimer61000:
 			end;
 			end;
 		}
 		}
 	}
 	}
+	bg_updatescore "bat_c01",.Guillaume_Count,.Croix_Count;
 	set $@KvM01BG, 2; // Playing
 	set $@KvM01BG, 2; // Playing
 	bg_warp $@KvM01BG_id1,"bat_c01",61,120;
 	bg_warp $@KvM01BG_id1,"bat_c01",61,120;
 	bg_warp $@KvM01BG_id2,"bat_c01",138,63;
 	bg_warp $@KvM01BG_id2,"bat_c01",138,63;
@@ -292,6 +301,8 @@ OnStop:
 	bg_warp $@KvM01BG_id1,"bat_c01",53,128;
 	bg_warp $@KvM01BG_id1,"bat_c01",53,128;
 	bg_warp $@KvM01BG_id2,"bat_c01",146,55;
 	bg_warp $@KvM01BG_id2,"bat_c01",146,55;
 	donpcevent "KvM01_BG_Out::OnBegin";
 	donpcevent "KvM01_BG_Out::OnBegin";
+	if (getbattleflag("feature.bgqueue"))
+		bg_reserve "bat_c01", true;
 	end;
 	end;
 }
 }
 
 
@@ -333,7 +344,6 @@ OnTimer60000:
 	disablenpc "KVM Officer#KVM01B";
 	disablenpc "KVM Officer#KVM01B";
 	mapwarp "bat_c01","bat_room",154,150;
 	mapwarp "bat_c01","bat_room",154,150;
 	maprespawnguildid "bat_c01",0,3; // Just in case someone else
 	maprespawnguildid "bat_c01",0,3; // Just in case someone else
-	bg_updatescore "bat_c01",5,5;
 	set $@KvM01BG, 0;
 	set $@KvM01BG, 0;
 
 
 OnGuillaumeJoin:
 OnGuillaumeJoin:

+ 11 - 1
npc/battleground/kvm/kvm02.txt

@@ -155,6 +155,14 @@ OnCroixDie:
 	}
 	}
 	end;
 	end;
 
 
+OnGuillaumeActive:
+	warp "bat_c02",62,119;
+	end;
+
+OnCroixActive:
+	warp "bat_c02",137,64;
+	end;
+
 OnStart:
 OnStart:
 	disablenpc "KVM Officer#KVM02A";
 	disablenpc "KVM Officer#KVM02A";
 	disablenpc "KVM Officer#KVM02B";
 	disablenpc "KVM Officer#KVM02B";
@@ -224,6 +232,7 @@ OnTimer61000:
 			end;
 			end;
 		}
 		}
 	}
 	}
+	bg_updatescore "bat_c02",.Guillaume_Count,.Croix_Count;
 	set $@KvM02BG, 2; // Playing
 	set $@KvM02BG, 2; // Playing
 	bg_warp $@KvM02BG_id1,"bat_c02",62,119;
 	bg_warp $@KvM02BG_id1,"bat_c02",62,119;
 	bg_warp $@KvM02BG_id2,"bat_c02",137,64;
 	bg_warp $@KvM02BG_id2,"bat_c02",137,64;
@@ -292,6 +301,8 @@ OnStop:
 	bg_warp $@KvM02BG_id1,"bat_c02",53,128;
 	bg_warp $@KvM02BG_id1,"bat_c02",53,128;
 	bg_warp $@KvM02BG_id2,"bat_c02",146,55;
 	bg_warp $@KvM02BG_id2,"bat_c02",146,55;
 	donpcevent "KvM02_BG_Out::OnBegin";
 	donpcevent "KvM02_BG_Out::OnBegin";
+	if (getbattleflag("feature.bgqueue"))
+		bg_reserve "bat_c02", true;
 	end;
 	end;
 }
 }
 
 
@@ -333,7 +344,6 @@ OnTimer60000:
 	disablenpc "KVM Officer#KVM02B";
 	disablenpc "KVM Officer#KVM02B";
 	mapwarp "bat_c02","bat_room",154,150;
 	mapwarp "bat_c02","bat_room",154,150;
 	maprespawnguildid "bat_c02",0,3; // Just in case someone else
 	maprespawnguildid "bat_c02",0,3; // Just in case someone else
-	bg_updatescore "bat_c02",5,5;
 	set $@KvM02BG, 0;
 	set $@KvM02BG, 0;
 
 
 OnGuillaumeJoin:
 OnGuillaumeJoin:

+ 11 - 1
npc/battleground/kvm/kvm03.txt

@@ -155,6 +155,14 @@ OnCroixDie:
 	}
 	}
 	end;
 	end;
 
 
+OnGuillaumeActive:
+	warp "bat_c03",62,119;
+	end;
+
+OnCroixActive:
+	warp "bat_c03",137,64;
+	end;
+
 OnStart:
 OnStart:
 	disablenpc "KVM Officer#KVM03A";
 	disablenpc "KVM Officer#KVM03A";
 	disablenpc "KVM Officer#KVM03B";
 	disablenpc "KVM Officer#KVM03B";
@@ -224,6 +232,7 @@ OnTimer61000:
 			end;
 			end;
 		}
 		}
 	}
 	}
+	bg_updatescore "bat_c03",.Guillaume_Count,.Croix_Count;
 	set $@KvM03BG, 2; // Playing
 	set $@KvM03BG, 2; // Playing
 	bg_warp $@KvM03BG_id1,"bat_c03",62,119;
 	bg_warp $@KvM03BG_id1,"bat_c03",62,119;
 	bg_warp $@KvM03BG_id2,"bat_c03",137,64;
 	bg_warp $@KvM03BG_id2,"bat_c03",137,64;
@@ -292,6 +301,8 @@ OnStop:
 	bg_warp $@KvM03BG_id1,"bat_c03",53,128;
 	bg_warp $@KvM03BG_id1,"bat_c03",53,128;
 	bg_warp $@KvM03BG_id2,"bat_c03",146,55;
 	bg_warp $@KvM03BG_id2,"bat_c03",146,55;
 	donpcevent "KvM03_BG_Out::OnBegin";
 	donpcevent "KvM03_BG_Out::OnBegin";
+	if (getbattleflag("feature.bgqueue"))
+		bg_reserve "bat_c03", true;
 	end;
 	end;
 }
 }
 
 
@@ -333,7 +344,6 @@ OnTimer60000:
 	disablenpc "KVM Officer#KVM03B";
 	disablenpc "KVM Officer#KVM03B";
 	mapwarp "bat_c03","bat_room",154,150;
 	mapwarp "bat_c03","bat_room",154,150;
 	maprespawnguildid "bat_c03",0,3; // Just in case someone else
 	maprespawnguildid "bat_c03",0,3; // Just in case someone else
-	bg_updatescore "bat_c03",5,5;
 	set $@KvM03BG, 0;
 	set $@KvM03BG, 0;
 
 
 OnGuillaumeJoin:
 OnGuillaumeJoin:

+ 57 - 32
npc/battleground/tierra/tierra01.txt

@@ -58,21 +58,18 @@ OnStop:
 
 
 OnTimer1000:
 OnTimer1000:
 	stopnpctimer;
 	stopnpctimer;
-	if (!getbattleflag("feature.bgqueue"))
-		initnpctimer;
-	set .@chk_bat_a01,getmapusers("bat_a01");
-	if (.@chk_bat_a01 < 1) {
-		set $@TierraBG1,0; set $@TierraBG1_Victory, 0;
-		if( $@TierraBG1_id1 ) { bg_destroy $@TierraBG1_id1; set $@TierraBG1_id1, 0; }
-		if( $@TierraBG1_id2 ) { bg_destroy $@TierraBG1_id2; set $@TierraBG1_id2, 0; }
-		if (getbattleflag("feature.bgqueue")) {
+	if (bg_get_data($@TierraBG1_id1, 0) == 0 && bg_get_data($@TierraBG1_id2, 0) == 0) {
+		donpcevent "countdown#bat_a01::OnStop";
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_a01", true;
+		$@TierraBG1 = 0; $@TierraBG1_Victory = 0;
+		if( $@TierraBG1_id1 ) { bg_destroy $@TierraBG1_id1; $@TierraBG1_id1 = 0; }
+		if( $@TierraBG1_id2 ) { bg_destroy $@TierraBG1_id2; $@TierraBG1_id2 = 0; }
+		if (getbattleflag("feature.bgqueue"))
 			bg_unbook "bat_a01";
 			bg_unbook "bat_a01";
-			end;
-		} else
-			donpcevent "start#bat_a01::OnReadyCheck";
+		end;
 	}
 	}
-	if (getbattleflag("feature.bgqueue"))
-		initnpctimer;
+	initnpctimer;
 	end;
 	end;
 }
 }
 
 
@@ -126,6 +123,14 @@ OnEnable:
 	disablenpc "Croix Vintenar#a01_b";
 	disablenpc "Croix Vintenar#a01_b";
 	end;
 	end;
 
 
+OnGuillaumeActive:
+	warp "bat_a01",352,342;
+	end;
+
+OnCroixActive:
+	warp "bat_a01",353,52;
+	end;
+
 OnGuillaumeQuit:
 OnGuillaumeQuit:
 OnCroixQuit:
 OnCroixQuit:
 	if (getbattleflag("feature.bgqueue"))
 	if (getbattleflag("feature.bgqueue"))
@@ -183,6 +188,8 @@ OnMyMobDead:
 		mapannounce "bat_a01", "Croix Vintenar Swandery: We destroyed Guillaume's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		mapannounce "bat_a01", "Croix Vintenar Swandery: We destroyed Guillaume's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		bg_warp $@TierraBG1_id1,"bat_a01",50,374;
 		bg_warp $@TierraBG1_id1,"bat_a01",50,374;
 		bg_warp $@TierraBG1_id2,"bat_a01",42,16;
 		bg_warp $@TierraBG1_id2,"bat_a01",42,16;
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_a01", true;
 	}
 	}
 	end;
 	end;
 }
 }
@@ -206,6 +213,8 @@ OnMyMobDead:
 		mapannounce "bat_a01", "Guillaume Vintenar Axl Rose : We destroyed Croix's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		mapannounce "bat_a01", "Guillaume Vintenar Axl Rose : We destroyed Croix's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		bg_warp $@TierraBG1_id1,"bat_a01",50,374;
 		bg_warp $@TierraBG1_id1,"bat_a01",50,374;
 		bg_warp $@TierraBG1_id2,"bat_a01",42,16;
 		bg_warp $@TierraBG1_id2,"bat_a01",42,16;
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_a01", true;
 	}
 	}
 	end;
 	end;
 }
 }
@@ -364,8 +373,10 @@ bat_a01,185,270,1	script	Guillaume Blacksmith#a01	851,{
 				mes "[Guillaume Blacksmith]";
 				mes "[Guillaume Blacksmith]";
 				mes "Wow! It's done.";
 				mes "Wow! It's done.";
 				mes "We are relieved.";
 				mes "We are relieved.";
-				delitem 7049,50; //Stone
-				donpcevent "barricade#bat_a01_a::OnEnable";
+				if (mobcount("bat_a01","barricade#bat_a01_a::OnMyMobDead") < 17) {
+					delitem 7049,50; //Stone
+					donpcevent "barricade#bat_a01_a::OnEnable";
+				}
 				close2;
 				close2;
 				disablenpc "Guillaume Blacksmith#a01";
 				disablenpc "Guillaume Blacksmith#a01";
 				end;
 				end;
@@ -448,8 +459,10 @@ bat_a01,170,121,5	script	Croix Blacksmith#bat_a01	851,{
 				mes "[Croix Blacksmith]";
 				mes "[Croix Blacksmith]";
 				mes "Wow! It's done.";
 				mes "Wow! It's done.";
 				mes "We are relieved.";
 				mes "We are relieved.";
-				delitem 7049,50; //Stone
-				donpcevent "barricade#bat_a01_b::OnEnable";
+				if (mobcount("bat_a01","barricade#bat_a01_b::OnMyMobDead") < 17) {
+					delitem 7049,50; //Stone
+					donpcevent "barricade#bat_a01_b::OnEnable";
+				}
 				close2;
 				close2;
 				disablenpc "Croix Blacksmith#bat_a01";
 				disablenpc "Croix Blacksmith#bat_a01";
 				end;
 				end;
@@ -500,35 +513,39 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_a01_rp1_a_warp";
+	// disablenpc "bat_a01_rp1_a_warp";
 	disablenpc "Battle Therapist#a01_a";
 	disablenpc "Battle Therapist#a01_a";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_a01_rp1_a_warp";
+	// enablenpc "bat_a01_rp1_a_warp";
+	areapercentheal "bat_a01",41,365,61,385,100,100;
+	areawarp "bat_a01",41,365,61,385,"bat_a01",352,342;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_a01_rp1_a_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_a01_rp1_a_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	donpcevent "Battle Therapist#a01_a::OnEnable";
 	donpcevent "Battle Therapist#a01_a::OnEnable";
 	end;
 	end;
 }
 }
 
 
+/*
 bat_a01,51,375,0	script	bat_a01_rp1_a_warp	-1,10,10,{
 bat_a01,51,375,0	script	bat_a01_rp1_a_warp	-1,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_a01_rp1_a_warp";
 	disablenpc "bat_a01_rp1_a_warp";
 	end;
 	end;
 
 
-OnTouch_:
+OnTouch:
 	percentheal 100,100;
 	percentheal 100,100;
 	warp "bat_a01",352,342;
 	warp "bat_a01",352,342;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_a01,45,19,3	script	Battle Therapist#a01_b	95,{
 bat_a01,45,19,3	script	Battle Therapist#a01_b	95,{
 	specialeffect2 EF_HEAL;
 	specialeffect2 EF_HEAL;
@@ -550,25 +567,28 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_a01_rp1_b_warp";
+	// disablenpc "bat_a01_rp1_b_warp";
 	disablenpc "Battle Therapist#a01_b";
 	disablenpc "Battle Therapist#a01_b";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_a01_rp1_b_warp";
+	areapercentheal "bat_a01",33,7,53,27,100,100;
+	areawarp "bat_a01",33,7,53,27,"bat_a01",353,52;
+	// enablenpc "bat_a01_rp1_b_warp";
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_a01_rp1_b_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_a01_rp1_b_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	donpcevent "Battle Therapist#a01_b::OnEnable";
 	donpcevent "Battle Therapist#a01_b::OnEnable";
 	end;
 	end;
 }
 }
 
 
+/*
 bat_a01,43,17,0	script	bat_a01_rp1_b_warp	-1,10,10,{
 bat_a01,43,17,0	script	bat_a01_rp1_b_warp	-1,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_a01_rp1_b_warp";
 	disablenpc "bat_a01_rp1_b_warp";
@@ -579,6 +599,7 @@ OnTouch:
 	warp "bat_a01",353,52;
 	warp "bat_a01",353,52;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_a01,60,216,3	script	Valley Ghost#bat_a01_n	950,{
 bat_a01,60,216,3	script	Valley Ghost#bat_a01_n	950,{
 	specialeffect2 EF_HEAL;
 	specialeffect2 EF_HEAL;
@@ -597,18 +618,21 @@ OnEnable:
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_a01_rp1_n_warp";
+	areapercentheal "bat_a01",45,203,65,223,100,100;
+	areawarp "bat_a01",45,203,65,223,"bat_a01",301,209;
+	// enablenpc "bat_a01_rp1_n_warp";
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_a01_rp1_n_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_a01_rp1_n_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	donpcevent "Valley Ghost#bat_a01_n::OnEnable";
 	donpcevent "Valley Ghost#bat_a01_n::OnEnable";
 	end;
 	end;
 }
 }
 
 
+/*
 bat_a01,55,213,0	script	bat_a01_rp1_n_warp	-1,10,10,{
 bat_a01,55,213,0	script	bat_a01_rp1_n_warp	-1,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_a01_rp1_n_warp";
 	disablenpc "bat_a01_rp1_n_warp";
@@ -619,6 +643,7 @@ OnTouch:
 	warp "bat_a01",301,209;
 	warp "bat_a01",301,209;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_a01,194,267,0	script	barri_warp_up#bat_a01_a	-1,7,0,{
 bat_a01,194,267,0	script	barri_warp_up#bat_a01_a	-1,7,0,{
 OnTouch:
 OnTouch:

+ 58 - 32
npc/battleground/tierra/tierra02.txt

@@ -57,21 +57,19 @@ OnStop:
 
 
 OnTimer1000:
 OnTimer1000:
 	stopnpctimer;
 	stopnpctimer;
-	if (!getbattleflag("feature.bgqueue"))
-		initnpctimer;
-	set .@chk_bat_a02,getmapusers("bat_a02");
-	if (.@chk_bat_a02 < 1) {
-		set $@TierraBG2,0; set $@TierraBG2_Victory, 0;
-		if( $@TierraBG2_id1 ) { bg_destroy $@TierraBG2_id1; set $@TierraBG2_id1, 0; }
-		if( $@TierraBG2_id2 ) { bg_destroy $@TierraBG2_id2; set $@TierraBG2_id2, 0; }
-		if (getbattleflag("feature.bgqueue")) {
+	if (!bg_get_data($@TierraBG2_id1, 0) && !bg_get_data($@TierraBG2_id2, 0)) {
+		donpcevent "countdown#bat_a02::OnStop";
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_a02", true;
+		mapwarp "bat_a02","bat_room",154,150;
+		$@TierraBG2 = 0; $@TierraBG2_Victory = 0;
+		if( $@TierraBG2_id1 ) { bg_destroy $@TierraBG2_id1; $@TierraBG2_id1 = 0; }
+		if( $@TierraBG2_id2 ) { bg_destroy $@TierraBG2_id2; $@TierraBG2_id2 = 0; }
+		if (getbattleflag("feature.bgqueue"))
 			bg_unbook "bat_a02";
 			bg_unbook "bat_a02";
-			end;
-		} else
-			donpcevent "start#bat_a02::OnReadyCheck";
+		end;
 	}
 	}
-	if (getbattleflag("feature.bgqueue"))
-		initnpctimer;
+	initnpctimer;
 	end;
 	end;
 }
 }
 
 
@@ -125,6 +123,14 @@ OnEnable:
 	disablenpc "Croix Vintenar#a02_b";
 	disablenpc "Croix Vintenar#a02_b";
 	end;
 	end;
 
 
+OnGuillaumeActive:
+	warp "bat_a02",352,342;
+	end;
+
+OnCroixActive:
+	warp "bat_a02",353,52;
+	end;
+
 OnGuillaumeQuit:
 OnGuillaumeQuit:
 OnCroixQuit:
 OnCroixQuit:
 	if (getbattleflag("feature.bgqueue"))
 	if (getbattleflag("feature.bgqueue"))
@@ -182,6 +188,8 @@ OnMyMobDead:
 		mapannounce "bat_a02", "Croix Vintenar Swandery: We destroyed Guillaume's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		mapannounce "bat_a02", "Croix Vintenar Swandery: We destroyed Guillaume's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		bg_warp $@TierraBG2_id1,"bat_a02",50,374;
 		bg_warp $@TierraBG2_id1,"bat_a02",50,374;
 		bg_warp $@TierraBG2_id2,"bat_a02",42,16;
 		bg_warp $@TierraBG2_id2,"bat_a02",42,16;
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_a02", true;
 	}
 	}
 	end;
 	end;
 }
 }
@@ -205,6 +213,8 @@ OnMyMobDead:
 		mapannounce "bat_a02", "Guillaume Vintenar Axl Rose : We destroyed Croix's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		mapannounce "bat_a02", "Guillaume Vintenar Axl Rose : We destroyed Croix's Food Storage. We won that! Wow!",bc_map,"0xFFCE00";
 		bg_warp $@TierraBG2_id1,"bat_a02",50,374;
 		bg_warp $@TierraBG2_id1,"bat_a02",50,374;
 		bg_warp $@TierraBG2_id2,"bat_a02",42,16;
 		bg_warp $@TierraBG2_id2,"bat_a02",42,16;
+		if (getbattleflag("feature.bgqueue"))
+			bg_reserve "bat_a02", true;
 	}
 	}
 	end;
 	end;
 }
 }
@@ -363,8 +373,10 @@ bat_a02,185,270,1	script	Guillaume Blacksmith#a02	851,{
 				mes "[Guillaume Blacksmith]";
 				mes "[Guillaume Blacksmith]";
 				mes "Wow! It's done.";
 				mes "Wow! It's done.";
 				mes "We are relieved.";
 				mes "We are relieved.";
-				delitem 7049,50; //Stone
-				donpcevent "barricade#bat_a02_a::OnEnable";
+				if (mobcount("bat_a02","barricade#bat_a02_a::OnMyMobDead") < 17) {
+					delitem 7049,50; //Stone
+					donpcevent "barricade#bat_a02_a::OnEnable";
+				}
 				close2;
 				close2;
 				disablenpc "Guillaume Blacksmith#a02";
 				disablenpc "Guillaume Blacksmith#a02";
 				end;
 				end;
@@ -447,8 +459,10 @@ bat_a02,170,121,5	script	Croix Blacksmith#bat_a02	851,{
 				mes "[Croix Blacksmith]";
 				mes "[Croix Blacksmith]";
 				mes "Wow! It's done.";
 				mes "Wow! It's done.";
 				mes "We are relieved.";
 				mes "We are relieved.";
-				delitem 7049,50; //Stone
-				donpcevent "barricade#bat_a02_b::OnEnable";
+				if (mobcount("bat_a02","barricade#bat_a02_b::OnMyMobDead") < 17) {
+					delitem 7049,50; //Stone
+					donpcevent "barricade#bat_a02_b::OnEnable";
+				}
 				close2;
 				close2;
 				disablenpc "Croix Blacksmith#bat_a02";
 				disablenpc "Croix Blacksmith#bat_a02";
 				end;
 				end;
@@ -499,35 +513,39 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_a02_rp1_a_warp";
+	// disablenpc "bat_a02_rp1_a_warp";
 	disablenpc "Battle Therapist#a02_a";
 	disablenpc "Battle Therapist#a02_a";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_a02_rp1_a_warp";
+	// enablenpc "bat_a02_rp1_a_warp";
+	areapercentheal "bat_a02",41,365,61,385,100,100;
+	areawarp "bat_a02",41,365,61,385,"bat_a02",352,342;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_a02_rp1_a_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_a02_rp1_a_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	donpcevent "Battle Therapist#a02_a::OnEnable";
 	donpcevent "Battle Therapist#a02_a::OnEnable";
 	end;
 	end;
 }
 }
 
 
+/*
 bat_a02,51,375,0	script	bat_a02_rp1_a_warp	-1,10,10,{
 bat_a02,51,375,0	script	bat_a02_rp1_a_warp	-1,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_a02_rp1_a_warp";
 	disablenpc "bat_a02_rp1_a_warp";
 	end;
 	end;
 
 
-OnTouch_:
+OnTouch:
 	percentheal 100,100;
 	percentheal 100,100;
 	warp "bat_a02",352,342;
 	warp "bat_a02",352,342;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_a02,45,19,3	script	Battle Therapist#a02_b	95,{
 bat_a02,45,19,3	script	Battle Therapist#a02_b	95,{
 	specialeffect2 EF_HEAL;
 	specialeffect2 EF_HEAL;
@@ -549,25 +567,28 @@ OnEnable:
 	end;
 	end;
 
 
 OnStop:
 OnStop:
-	disablenpc "bat_a02_rp1_b_warp";
+	// disablenpc "bat_a02_rp1_b_warp";
 	disablenpc "Battle Therapist#a02_b";
 	disablenpc "Battle Therapist#a02_b";
 	stopnpctimer;
 	stopnpctimer;
 	end;
 	end;
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_a02_rp1_b_warp";
+	// enablenpc "bat_a02_rp1_b_warp";
+	areapercentheal "bat_a02",33,7,53,27,100,100;
+	areawarp "bat_a02",33,7,53,27,"bat_a02",353,52;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_a02_rp1_b_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_a02_rp1_b_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	donpcevent "Battle Therapist#a02_b::OnEnable";
 	donpcevent "Battle Therapist#a02_b::OnEnable";
 	end;
 	end;
 }
 }
 
 
+/*
 bat_a02,43,17,0	script	bat_a02_rp1_b_warp	-1,10,10,{
 bat_a02,43,17,0	script	bat_a02_rp1_b_warp	-1,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_a02_rp1_b_warp";
 	disablenpc "bat_a02_rp1_b_warp";
@@ -578,6 +599,7 @@ OnTouch:
 	warp "bat_a02",353,52;
 	warp "bat_a02",353,52;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_a02,60,216,3	script	Valley Ghost#bat_a02_n	950,{
 bat_a02,60,216,3	script	Valley Ghost#bat_a02_n	950,{
 	specialeffect2 EF_HEAL;
 	specialeffect2 EF_HEAL;
@@ -596,18 +618,21 @@ OnEnable:
 
 
 OnTimer25000:
 OnTimer25000:
 	specialeffect EF_SANCTUARY;
 	specialeffect EF_SANCTUARY;
-	enablenpc "bat_a02_rp1_n_warp";
+	// enablenpc "bat_a02_rp1_n_warp";
+	areapercentheal "bat_a02",45,203,65,223,100,100;
+	areawarp "bat_a02",45,203,65,223,"bat_a02",301,209;
 	end;
 	end;
 
 
-OnTimer26000:
-	disablenpc "bat_a02_rp1_n_warp";
-	end;
+// OnTimer26000:
+	// disablenpc "bat_a02_rp1_n_warp";
+	// end;
 
 
 OnTimer26500:
 OnTimer26500:
 	donpcevent "Valley Ghost#bat_a02_n::OnEnable";
 	donpcevent "Valley Ghost#bat_a02_n::OnEnable";
 	end;
 	end;
 }
 }
 
 
+/*
 bat_a02,55,213,0	script	bat_a02_rp1_n_warp	-1,10,10,{
 bat_a02,55,213,0	script	bat_a02_rp1_n_warp	-1,10,10,{
 OnInit:
 OnInit:
 	disablenpc "bat_a02_rp1_n_warp";
 	disablenpc "bat_a02_rp1_n_warp";
@@ -618,6 +643,7 @@ OnTouch:
 	warp "bat_a02",301,209;
 	warp "bat_a02",301,209;
 	end;
 	end;
 }
 }
+*/
 
 
 bat_a02,194,267,0	script	barri_warp_up#bat_a02_a	-1,7,0,{
 bat_a02,194,267,0	script	barri_warp_up#bat_a02_a	-1,7,0,{
 OnTouch:
 OnTouch:

+ 1 - 0
src/map/battle.cpp

@@ -8951,6 +8951,7 @@ static const struct _battle_data {
 	{ "idletime_hom_option",                &battle_config.idletime_hom_option,             0x1F,   0x1,    0xFFF,          },
 	{ "idletime_hom_option",                &battle_config.idletime_hom_option,             0x1F,   0x1,    0xFFF,          },
 	{ "devotion_standup_fix",               &battle_config.devotion_standup_fix,            1,      0,      1,              },
 	{ "devotion_standup_fix",               &battle_config.devotion_standup_fix,            1,      0,      1,              },
 	{ "feature.bgqueue",                    &battle_config.feature_bgqueue,                 1,      0,      1,              },
 	{ "feature.bgqueue",                    &battle_config.feature_bgqueue,                 1,      0,      1,              },
+	{ "bgqueue_nowarp_mapflag",             &battle_config.bgqueue_nowarp_mapflag,          0,      0,      1,              },
 	{ "homunculus_exp_gain",                &battle_config.homunculus_exp_gain,             10,     0,      100,            },
 	{ "homunculus_exp_gain",                &battle_config.homunculus_exp_gain,             10,     0,      100,            },
 	{ "rental_item_novalue",                &battle_config.rental_item_novalue,             1,      0,      1,              },
 	{ "rental_item_novalue",                &battle_config.rental_item_novalue,             1,      0,      1,              },
 
 

+ 1 - 0
src/map/battle.hpp

@@ -675,6 +675,7 @@ struct Battle_Config
 	int idletime_hom_option;
 	int idletime_hom_option;
 	int devotion_standup_fix;
 	int devotion_standup_fix;
 	int feature_bgqueue;
 	int feature_bgqueue;
+	int bgqueue_nowarp_mapflag;
 	int homunculus_exp_gain;
 	int homunculus_exp_gain;
 	int rental_item_novalue;
 	int rental_item_novalue;
 
 

+ 463 - 126
src/map/battleground.cpp

@@ -32,6 +32,7 @@ using namespace rathena;
 BattlegroundDatabase battleground_db;
 BattlegroundDatabase battleground_db;
 std::unordered_map<int, std::shared_ptr<s_battleground_data>> bg_team_db;
 std::unordered_map<int, std::shared_ptr<s_battleground_data>> bg_team_db;
 std::vector<std::shared_ptr<s_battleground_queue>> bg_queues;
 std::vector<std::shared_ptr<s_battleground_queue>> bg_queues;
+int bg_queue_count = 1;
 
 
 const std::string BattlegroundDatabase::getDefaultLocation() {
 const std::string BattlegroundDatabase::getDefaultLocation() {
 	return std::string(db_path) + "/battleground_db.yml";
 	return std::string(db_path) + "/battleground_db.yml";
@@ -75,6 +76,11 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) {
 		if (!this->asInt32(node, "MinPlayers", min))
 		if (!this->asInt32(node, "MinPlayers", min))
 			return 0;
 			return 0;
 
 
+		if (min < 1) {
+			this->invalidWarning(node["MinPlayers"], "Minimum players %d cannot be less than 1, capping to 1.\n", min);
+			min = 1;
+		}
+
 		if (min * 2 > MAX_BG_MEMBERS) {
 		if (min * 2 > MAX_BG_MEMBERS) {
 			this->invalidWarning(node["MinPlayers"], "Minimum players %d exceeds MAX_BG_MEMBERS, capping to %d.\n", min, MAX_BG_MEMBERS / 2);
 			this->invalidWarning(node["MinPlayers"], "Minimum players %d exceeds MAX_BG_MEMBERS, capping to %d.\n", min, MAX_BG_MEMBERS / 2);
 			min = MAX_BG_MEMBERS / 2;
 			min = MAX_BG_MEMBERS / 2;
@@ -92,6 +98,11 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) {
 		if (!this->asInt32(node, "MaxPlayers", max))
 		if (!this->asInt32(node, "MaxPlayers", max))
 			return 0;
 			return 0;
 
 
+		if (max < 1) {
+			this->invalidWarning(node["MaxPlayers"], "Maximum players %d cannot be less than 1, capping to 1.\n", max);
+			max = 1;
+		}
+
 		if (max * 2 > MAX_BG_MEMBERS) {
 		if (max * 2 > MAX_BG_MEMBERS) {
 			this->invalidWarning(node["MaxPlayers"], "Maximum players %d exceeds MAX_BG_MEMBERS, capping to %d.\n", max, MAX_BG_MEMBERS / 2);
 			this->invalidWarning(node["MaxPlayers"], "Maximum players %d exceeds MAX_BG_MEMBERS, capping to %d.\n", max, MAX_BG_MEMBERS / 2);
 			max = MAX_BG_MEMBERS / 2;
 			max = MAX_BG_MEMBERS / 2;
@@ -158,7 +169,77 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) {
 		bg->start_delay = delay;
 		bg->start_delay = delay;
 	} else {
 	} else {
 		if (!exists)
 		if (!exists)
-			bg->start_delay = 30;
+			bg->start_delay = 0;
+	}
+
+	if (this->nodeExists(node, "Join")) {
+		const YAML::Node &joinNode = node["Join"];
+
+		if (this->nodeExists(joinNode, "Solo")) {
+			bool active;
+
+			if (!this->asBool(joinNode, "Solo", active))
+				return 0;
+
+			bg->solo = active;
+		} else {
+			if (!exists)
+				bg->solo = true;
+		}
+
+		if (this->nodeExists(joinNode, "Party")) {
+			bool active;
+
+			if (!this->asBool(joinNode, "Party", active))
+				return 0;
+
+			bg->party = active;
+		} else {
+			if (!exists)
+				bg->party = true;
+		}
+
+		if (this->nodeExists(joinNode, "Guild")) {
+			bool active;
+
+			if (!this->asBool(joinNode, "Guild", active))
+				return 0;
+
+			bg->guild = active;
+		} else {
+			if (!exists)
+				bg->guild = true;
+		}
+	} else {
+		if (!exists) {
+			bg->solo = true;
+			bg->party = true;
+			bg->guild = true;
+		}
+	}
+
+	if (this->nodeExists(node, "JobRestrictions")) {
+		const YAML::Node &jobsNode = node["JobRestrictions"];
+
+		for (const auto &jobit : jobsNode) {
+			std::string job_name = jobit.first.as<std::string>(), job_name_constant = "JOB_" + job_name;
+			int64 constant;
+
+			if (!script_get_constant(job_name_constant.c_str(), &constant)) {
+				this->invalidWarning(node["JobRestrictions"], "Job %s does not exist.\n", job_name.c_str());
+				continue;
+			}
+
+			bool active;
+
+			if (!this->asBool(jobsNode, job_name, active))
+				return 0;
+
+			if (active)
+				bg->job_restrictions.push_back(static_cast<int32>(constant));
+			else
+				util::vector_erase_if_exists(bg->job_restrictions, static_cast<int32>(constant));
+		}
 	}
 	}
 
 
 	if (this->nodeExists(node, "Locations")) {
 	if (this->nodeExists(node, "Locations")) {
@@ -174,9 +255,9 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) {
 				if (!this->asString(location, "Map", map_name))
 				if (!this->asString(location, "Map", map_name))
 					return 0;
 					return 0;
 
 
-				map_entry.mapid = map_mapname2mapid(map_name.c_str());
+				map_entry.mapindex = mapindex_name2id(map_name.c_str());
 
 
-				if (map_entry.mapid == -1) {
+				if (map_entry.mapindex == 0) {
 					this->invalidWarning(location["Map"], "Invalid battleground map name %s, skipping.\n", map_name.c_str());
 					this->invalidWarning(location["Map"], "Invalid battleground map name %s, skipping.\n", map_name.c_str());
 					return 0;
 					return 0;
 				}
 				}
@@ -245,6 +326,18 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) {
 						}
 						}
 					}
 					}
 
 
+					if (this->nodeExists(team[it], "ActiveEvent")) {
+						if (!this->asString(team[it], "ActiveEvent", team_ptr->active_event))
+							return 0;
+
+						team_ptr->active_event.resize(EVENT_NAME_LENGTH);
+
+						if (team_ptr->active_event.find("::On") == std::string::npos) {
+							this->invalidWarning(team["ActiveEvent"], "Battleground ActiveEvent label %s should begin with '::On', skipping.\n", team_ptr->active_event.c_str());
+							return 0;
+						}
+					}
+
 					if (this->nodeExists(team[it], "Variable")) {
 					if (this->nodeExists(team[it], "Variable")) {
 						if (!this->asString(team[it], "Variable", team_ptr->bg_id_var))
 						if (!this->asString(team[it], "Variable", team_ptr->bg_id_var))
 							return 0;
 							return 0;
@@ -284,6 +377,21 @@ std::shared_ptr<s_battleground_type> bg_search_name(const char *name)
 	return nullptr;
 	return nullptr;
 }
 }
 
 
+/**
+ * Search for a Battleground queue based on the given queue ID
+ * @param queue_id: Queue ID
+ * @return s_battleground_queue on success or nullptr on failure
+ */
+std::shared_ptr<s_battleground_queue> bg_search_queue(int queue_id)
+{
+	for (const auto &queue : bg_queues) {
+		if (queue_id == queue->queue_id)
+			return queue;
+	}
+
+	return nullptr;
+}
+
 /**
 /**
  * Search for an available player in Battleground
  * Search for an available player in Battleground
  * @param bg: Battleground data
  * @param bg: Battleground data
@@ -293,7 +401,12 @@ struct map_session_data* bg_getavailablesd(s_battleground_data *bg)
 {
 {
 	nullpo_retr(nullptr, bg);
 	nullpo_retr(nullptr, bg);
 
 
-	return (bg->members.size() != 0) ? bg->members[0].sd : nullptr;
+	for (const auto &member : bg->members) {
+		if (member.sd != nullptr)
+			return member.sd;
+	}
+
+	return nullptr;
 }
 }
 
 
 /**
 /**
@@ -514,7 +627,7 @@ int bg_create(uint16 mapindex, s_battleground_team* team)
 	bg->cemetery.y = team->warp_y;
 	bg->cemetery.y = team->warp_y;
 	bg->logout_event = team->quit_event.c_str();
 	bg->logout_event = team->quit_event.c_str();
 	bg->die_event = team->death_event.c_str();
 	bg->die_event = team->death_event.c_str();
-	bg->members.clear();
+	bg->active_event = team->active_event.c_str();
 
 
 	return bg->id;
 	return bg->id;
 }
 }
@@ -569,7 +682,7 @@ void bg_send_message(struct map_session_data *sd, const char *mes, int len)
 {
 {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	if (!sd->bg_id)
+	if (sd->bg_id == 0)
 		return;
 		return;
 	
 	
 	std::shared_ptr<s_battleground_data> bgteam = util::umap_find(bg_team_db, sd->bg_id);
 	std::shared_ptr<s_battleground_data> bgteam = util::umap_find(bg_team_db, sd->bg_id);
@@ -617,7 +730,7 @@ TIMER_FUNC(bg_send_xy_timer)
 }
 }
 
 
 /**
 /**
- * Mark a Battleground as ready to begin queuing
+ * Mark a Battleground as ready to begin queuing for a free map
  * @param tid: Timer ID
  * @param tid: Timer ID
  * @param tick: Timer
  * @param tick: Timer
  * @param id: ID
  * @param id: ID
@@ -625,14 +738,18 @@ TIMER_FUNC(bg_send_xy_timer)
  */
  */
 static TIMER_FUNC(bg_on_ready_loopback)
 static TIMER_FUNC(bg_on_ready_loopback)
 {
 {
-	s_battleground_queue *queue = (s_battleground_queue*)data;
+	int queue_id = (int)data;
+	std::shared_ptr<s_battleground_queue> queue = bg_search_queue(queue_id);
 
 
-	nullpo_retr(1, queue);
+	if (queue == nullptr) {
+		ShowError("bg_on_ready_loopback: Invalid battleground queue %d.\n", queue_id);
+		return 1;
+	}
 
 
 	std::shared_ptr<s_battleground_type> bg = battleground_db.find(queue->id);
 	std::shared_ptr<s_battleground_type> bg = battleground_db.find(queue->id);
 
 
 	if (bg) {
 	if (bg) {
-		bg_queue_on_ready(bg->name.c_str(), std::shared_ptr<s_battleground_queue>(queue));
+		bg_queue_on_ready(bg->name.c_str(), queue);
 		return 0;
 		return 0;
 	} else {
 	} else {
 		ShowError("bg_on_ready_loopback: Can't find battleground %d in the battlegrounds database.\n", queue->id);
 		ShowError("bg_on_ready_loopback: Can't find battleground %d in the battlegrounds database.\n", queue->id);
@@ -641,7 +758,7 @@ static TIMER_FUNC(bg_on_ready_loopback)
 }
 }
 
 
 /**
 /**
- * Reset Battleground queue data
+ * Reset Battleground queue data if players don't accept in time
  * @param tid: Timer ID
  * @param tid: Timer ID
  * @param tick: Timer
  * @param tick: Timer
  * @param id: ID
  * @param id: ID
@@ -649,31 +766,32 @@ static TIMER_FUNC(bg_on_ready_loopback)
  */
  */
 static TIMER_FUNC(bg_on_ready_expire)
 static TIMER_FUNC(bg_on_ready_expire)
 {
 {
-	s_battleground_queue *queue = (s_battleground_queue*)data;
-
-	nullpo_retr(1, queue);
+	int queue_id = (int)data;
+	std::shared_ptr<s_battleground_queue> queue = bg_search_queue(queue_id);
 
 
-	queue->in_ready_state = false;
-	queue->map->isReserved = false; // Remove reservation to free up for future queue
-	queue->map = nullptr;
-	queue->accepted_players = 0; // Reset the queue count
+	if (queue == nullptr) {
+		ShowError("bg_on_ready_expire: Invalid battleground queue %d.\n", queue_id);
+		return 1;
+	}
 
 
 	std::string bg_name = battleground_db.find(queue->id)->name;
 	std::string bg_name = battleground_db.find(queue->id)->name;
 
 
 	for (const auto &sd : queue->teama_members) {
 	for (const auto &sd : queue->teama_members) {
-		sd->bg_queue_accept_state = false;
 		clif_bg_queue_apply_result(BG_APPLY_QUEUE_FINISHED, bg_name.c_str(), sd);
 		clif_bg_queue_apply_result(BG_APPLY_QUEUE_FINISHED, bg_name.c_str(), sd);
+		clif_bg_queue_entry_init(sd);
 	}
 	}
 
 
 	for (const auto &sd : queue->teamb_members) {
 	for (const auto &sd : queue->teamb_members) {
-		sd->bg_queue_accept_state = false;
 		clif_bg_queue_apply_result(BG_APPLY_QUEUE_FINISHED, bg_name.c_str(), sd);
 		clif_bg_queue_apply_result(BG_APPLY_QUEUE_FINISHED, bg_name.c_str(), sd);
+		clif_bg_queue_entry_init(sd);
 	}
 	}
+
+	bg_queue_clear(queue, true);
 	return 0;
 	return 0;
 }
 }
 
 
 /**
 /**
- * Start a Battleground
+ * Start a Battleground when all players have accepted
  * @param tid: Timer ID
  * @param tid: Timer ID
  * @param tick: Timer
  * @param tick: Timer
  * @param id: ID
  * @param id: ID
@@ -681,9 +799,13 @@ static TIMER_FUNC(bg_on_ready_expire)
  */
  */
 static TIMER_FUNC(bg_on_ready_start)
 static TIMER_FUNC(bg_on_ready_start)
 {
 {
-	s_battleground_queue *queue = (s_battleground_queue*)data;
+	int queue_id = (int)data;
+	std::shared_ptr<s_battleground_queue> queue = bg_search_queue(queue_id);
 
 
-	nullpo_retr(1, queue);
+	if (queue == nullptr) {
+		ShowError("bg_on_ready_start: Invalid battleground queue %d.\n", queue_id);
+		return 1;
+	}
 
 
 	queue->tid_start = INVALID_TIMER;
 	queue->tid_start = INVALID_TIMER;
 	bg_queue_start_battleground(queue);
 	bg_queue_start_battleground(queue);
@@ -702,7 +824,7 @@ bool bg_player_is_in_bg_map(struct map_session_data *sd)
 
 
 	for (const auto &pair : battleground_db) {
 	for (const auto &pair : battleground_db) {
 		for (const auto &it : pair.second->maps) {
 		for (const auto &it : pair.second->maps) {
-			if (it.mapid == sd->bl.m)
+			if (it.mapindex == sd->mapindex)
 				return true;
 				return true;
 		}
 		}
 	}
 	}
@@ -724,7 +846,7 @@ static bool bg_queue_check_status(struct map_session_data* sd, const char *name)
 		if (sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]) { // Exclude any player who's recently left a battleground queue
 		if (sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]) { // Exclude any player who's recently left a battleground queue
 			char buf[CHAT_SIZE_MAX];
 			char buf[CHAT_SIZE_MAX];
 
 
-			sprintf(buf, msg_txt(sd, 339), (get_timer(sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]->timer)->tick - gettick()) / 1000); // You can't apply to a battleground queue for %d seconds due to recently leaving one.
+			sprintf(buf, msg_txt(sd, 339), static_cast<int32>((get_timer(sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]->timer)->tick - gettick()) / 1000)); // You can't apply to a battleground queue for %d seconds due to recently leaving one.
 			clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
 			clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
 			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], buf, false, SELF);
 			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], buf, false, SELF);
 			return false;
 			return false;
@@ -732,9 +854,9 @@ static bool bg_queue_check_status(struct map_session_data* sd, const char *name)
 
 
 		if (sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]) { // Exclude any player who's recently deserted a battleground
 		if (sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]) { // Exclude any player who's recently deserted a battleground
 			char buf[CHAT_SIZE_MAX];
 			char buf[CHAT_SIZE_MAX];
-			t_tick status_tick = get_timer(sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]->timer)->tick, tick = gettick();
+			int32 status_tick = static_cast<int32>(DIFF_TICK(get_timer(sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]->timer)->tick, gettick()) / 1000);
 
 
-			sprintf(buf, msg_txt(sd, 338), ((status_tick - tick) / 1000) / 60, ((status_tick - tick) / 1000) % 60); // You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds.
+			sprintf(buf, msg_txt(sd, 338), status_tick / 60, status_tick % 60); // You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds.
 			clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
 			clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
 			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], buf, false, SELF);
 			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], buf, false, SELF);
 			return false;
 			return false;
@@ -755,43 +877,64 @@ bool bg_queue_check_joinable(std::shared_ptr<s_battleground_type> bg, struct map
 {
 {
 	nullpo_retr(false, sd);
 	nullpo_retr(false, sd);
 
 
-	if (bg->min_lvl && sd->status.base_level < bg->min_lvl) { // Check min level if min_lvl isn't 0
+	for (const auto &job : bg->job_restrictions) { // Check class requirement
+		if (sd->class_ == job) {
+			clif_bg_queue_apply_result(BG_APPLY_PLAYER_CLASS, name, sd);
+			return false;
+		}
+	}
+
+	if (bg->min_lvl > 0 && sd->status.base_level < bg->min_lvl) { // Check minimum level requirement
 		clif_bg_queue_apply_result(BG_APPLY_PLAYER_LEVEL, name, sd);
 		clif_bg_queue_apply_result(BG_APPLY_PLAYER_LEVEL, name, sd);
 		return false;
 		return false;
 	}
 	}
 
 
-	if (bg->max_lvl && sd->status.base_level > bg->max_lvl) { // Check max level if max_lvl isn't 0
+	if (bg->max_lvl > 0 && sd->status.base_level > bg->max_lvl) { // Check maximum level requirement
 		clif_bg_queue_apply_result(BG_APPLY_PLAYER_LEVEL, name, sd);
 		clif_bg_queue_apply_result(BG_APPLY_PLAYER_LEVEL, name, sd);
 		return false;
 		return false;
 	}
 	}
 
 
-	if (!bg_queue_check_status(sd, name))
+	if (!bg_queue_check_status(sd, name)) // Check status blocks
 		return false;
 		return false;
 
 
 	if (bg_player_is_in_bg_map(sd)) { // Is the player currently in a battleground map? Reject them.
 	if (bg_player_is_in_bg_map(sd)) { // Is the player currently in a battleground map? Reject them.
 		clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
 		clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
-		clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You may not join a battleground queue when you're in a battleground map.
+		clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map.
+		return false;
+	}
+
+	if (battle_config.bgqueue_nowarp_mapflag > 0 && map_getmapflag(sd->bl.m, MF_NOWARP)) { // Check player's current position for mapflag check
+		clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd);
+		clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map.
 		return false;
 		return false;
 	}
 	}
 
 
-	return true; // Return true if all conditions are met.
+	return true;
 }
 }
 
 
 /**
 /**
- * Sub function for reserving a slot in the Battleground if it's joinable
+ * Mark a map as reserved for a Battleground
  * @param name: Battleground map name
  * @param name: Battleground map name
  * @param state: Whether to mark reserved or not
  * @param state: Whether to mark reserved or not
+ * @param ended: Whether the Battleground event is complete; players getting prize
  * @return True on success or false otherwise
  * @return True on success or false otherwise
  */
  */
-bool bg_queue_reservation(const char *name, bool state)
+bool bg_queue_reservation(const char *name, bool state, bool ended)
 {
 {
-	int16 mapid = map_mapname2mapid(name);
+	uint16 mapindex = mapindex_name2id(name);
 
 
-	for (const auto &pair : battleground_db) {
-		// Bound checking isn't needed since we iterate within battleground_db's bound.
-		for (auto &it : pair.second->maps) {
-			if (it.mapid == mapid) {
-				it.isReserved = state;
+	for (auto &pair : battleground_db) {
+		for (auto &map : pair.second->maps) {
+			if (map.mapindex == mapindex) {
+				map.isReserved = state;
+				for (auto &queue : bg_queues) {
+					if (queue->map == &map) {
+						if (ended) // The ended flag is applied from bg_reserve (bg_unbook clears it for the next queue)
+							queue->state = QUEUE_STATE_ENDED;
+						if (!state)
+							bg_queue_clear(queue, true);
+					}
+				}
 				return true;
 				return true;
 			}
 			}
 		}
 		}
@@ -800,6 +943,33 @@ bool bg_queue_reservation(const char *name, bool state)
 	return false;
 	return false;
 }
 }
 
 
+/**
+ * Join as an individual into a Battleground
+ * @param name: Battleground name
+ * @param sd: Player who requested to join the battlegrounds
+ */
+void bg_queue_join_solo(const char *name, struct map_session_data *sd)
+{
+	if (!sd) {
+		ShowError("bg_queue_join_solo: Tried to join non-existent player\n.");
+		return;
+	}
+
+	std::shared_ptr<s_battleground_type> bg = bg_search_name(name);
+
+	if (!bg) {
+		ShowWarning("bq_queue_join_solo: Could not find battleground \"%s\" requested by %s (AID: %d / CID: %d)\n", name, sd->status.name, sd->status.account_id, sd->status.char_id);
+		return;
+	}
+
+	if (!bg->solo) {
+		clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd);
+		return;
+	}
+
+	bg_queue_join_multi(name, sd, { sd }); // Join as solo
+}
+
 /**
 /**
  * Join a party onto the same side of a Battleground
  * Join a party onto the same side of a Battleground
  * @param name: Battleground name
  * @param name: Battleground name
@@ -807,6 +977,11 @@ bool bg_queue_reservation(const char *name, bool state)
  */
  */
 void bg_queue_join_party(const char *name, struct map_session_data *sd)
 void bg_queue_join_party(const char *name, struct map_session_data *sd)
 {
 {
+	if (!sd) {
+		ShowError("bg_queue_join_party: Tried to join non-existent player\n.");
+		return;
+	}
+
 	struct party_data *p = party_search(sd->status.party_id);
 	struct party_data *p = party_search(sd->status.party_id);
 
 
 	if (!p) {
 	if (!p) {
@@ -824,6 +999,11 @@ void bg_queue_join_party(const char *name, struct map_session_data *sd)
 	std::shared_ptr<s_battleground_type> bg = bg_search_name(name);
 	std::shared_ptr<s_battleground_type> bg = bg_search_name(name);
 
 
 	if (bg) {
 	if (bg) {
+		if (!bg->party) {
+			clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd);
+			return;
+		}
+
 		int p_online = 0;
 		int p_online = 0;
 
 
 		for (const auto &it : p->party.member) {
 		for (const auto &it : p->party.member) {
@@ -835,7 +1015,7 @@ void bg_queue_join_party(const char *name, struct map_session_data *sd)
 			clif_bg_queue_apply_result(BG_APPLY_PLAYER_COUNT, name, sd);
 			clif_bg_queue_apply_result(BG_APPLY_PLAYER_COUNT, name, sd);
 			return; // Too many party members online
 			return; // Too many party members online
 		}
 		}
-		
+
 		std::vector<struct map_session_data *> list;
 		std::vector<struct map_session_data *> list;
 
 
 		for (const auto &it : p->party.member) {
 		for (const auto &it : p->party.member) {
@@ -865,6 +1045,11 @@ void bg_queue_join_party(const char *name, struct map_session_data *sd)
  */
  */
 void bg_queue_join_guild(const char *name, struct map_session_data *sd)
 void bg_queue_join_guild(const char *name, struct map_session_data *sd)
 {
 {
+	if (!sd) {
+		ShowError("bg_queue_join_guild: Tried to join non-existent player\n.");
+		return;
+	}
+
 	if (!sd->guild) {
 	if (!sd->guild) {
 		clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd);
 		clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd);
 		return; // Someone has bypassed the client check for being in a guild
 		return; // Someone has bypassed the client check for being in a guild
@@ -878,6 +1063,11 @@ void bg_queue_join_guild(const char *name, struct map_session_data *sd)
 	std::shared_ptr<s_battleground_type> bg = bg_search_name(name);
 	std::shared_ptr<s_battleground_type> bg = bg_search_name(name);
 
 
 	if (bg) {
 	if (bg) {
+		if (!bg->guild) {
+			clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd);
+			return;
+		}
+
 		struct guild* g = sd->guild;
 		struct guild* g = sd->guild;
 
 
 		if (g->connect_member > bg->max_players) {
 		if (g->connect_member > bg->max_players) {
@@ -932,43 +1122,55 @@ void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vec
 	}
 	}
 
 
 	for (const auto &queue : bg_queues) {
 	for (const auto &queue : bg_queues) {
-		if (queue->id != bg->id)
-			continue;
-		if (queue->in_ready_state)
+		if (queue->id != bg->id || queue->state == QUEUE_STATE_SETUP_DELAY || queue->state == QUEUE_STATE_ENDED)
 			continue;
 			continue;
 
 
 		// Make sure there's enough space on one side to join as a party/guild in this queue
 		// Make sure there's enough space on one side to join as a party/guild in this queue
-		if (queue->teama_members.size() + list.size() > bg->required_players && queue->teamb_members.size() + list.size() > bg->required_players) {
+		if (queue->teama_members.size() + list.size() > bg->max_players && queue->teamb_members.size() + list.size() > bg->max_players) {
 			break;
 			break;
 		}
 		}
 
 
 		bool r = rnd() % 2 != 0;
 		bool r = rnd() % 2 != 0;
-		std::vector<map_session_data *>* team = r ? &queue->teamb_members : &queue->teama_members;
-
-		// If the designated team is full, put the player into the other team
-		if (team->size() + list.size() > bg->required_players) {
-			team = r ? &queue->teama_members : &queue->teamb_members;
+		std::vector<map_session_data *> *team = r ? &queue->teamb_members : &queue->teama_members;
+
+		if (queue->state == QUEUE_STATE_ACTIVE) {
+			// If one team has lesser members try to balance (on an active BG)
+			if (r && queue->teama_members.size() < queue->teamb_members.size())
+				team = &queue->teama_members;
+			else if (!r && queue->teamb_members.size() < queue->teama_members.size())
+				team = &queue->teamb_members;
+		} else {
+			// If the designated team is full, put the player into the other team
+			if (team->size() + list.size() > bg->required_players)
+				team = r ? &queue->teama_members : &queue->teamb_members;
 		}
 		}
 
 
-		while (!list.empty() && team->size() < bg->required_players) {
+		while (!list.empty() && team->size() < bg->max_players) {
 			struct map_session_data *sd2 = list.back();
 			struct map_session_data *sd2 = list.back();
 
 
 			list.pop_back();
 			list.pop_back();
 
 
-			if (!sd2 || sd2->bg_queue)
+			if (!sd2 || sd2->bg_queue_id > 0)
 				continue;
 				continue;
 
 
 			if (!bg_queue_check_joinable(bg, sd2, name))
 			if (!bg_queue_check_joinable(bg, sd2, name))
 				continue;
 				continue;
 
 
-			sd2->bg_queue = queue;
+			sd2->bg_queue_id = queue->queue_id;
 			team->push_back(sd2);
 			team->push_back(sd2);
 			clif_bg_queue_apply_result(BG_APPLY_ACCEPT, name, sd2);
 			clif_bg_queue_apply_result(BG_APPLY_ACCEPT, name, sd2);
 			clif_bg_queue_apply_notify(name, sd2);
 			clif_bg_queue_apply_notify(name, sd2);
 		}
 		}
 
 
-		// Enough players have joined
-		if (queue->teamb_members.size() == bg->required_players && queue->teama_members.size() == bg->required_players)
+		if (queue->state == QUEUE_STATE_ACTIVE) { // Battleground is already active
+			for (auto &pl_sd : *team) {
+				if (queue->map->mapindex == pl_sd->mapindex)
+					continue;
+
+				pc_set_bg_queue_timer(pl_sd);
+				clif_bg_queue_lobby_notify(name, pl_sd);
+			}
+		} else if (queue->state == QUEUE_STATE_SETUP && queue->teamb_members.size() >= bg->required_players && queue->teama_members.size() >= bg->required_players) // Enough players have joined
 			bg_queue_on_ready(name, queue);
 			bg_queue_on_ready(name, queue);
 
 
 		return;
 		return;
@@ -981,10 +1183,11 @@ void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vec
 /**
 /**
  * Clear Battleground queue for next one
  * Clear Battleground queue for next one
  * @param queue: Queue to clean up
  * @param queue: Queue to clean up
+ * @param ended: If a Battleground has ended through normal means (by script command bg_unbook)
  */
  */
-static void bg_queue_clear(s_battleground_queue *queue)
+void bg_queue_clear(std::shared_ptr<s_battleground_queue> queue, bool ended)
 {
 {
-	if (!queue)
+	if (queue == nullptr)
 		return;
 		return;
 
 
 	if (queue->tid_requeue != INVALID_TIMER) {
 	if (queue->tid_requeue != INVALID_TIMER) {
@@ -1002,49 +1205,47 @@ static void bg_queue_clear(s_battleground_queue *queue)
 		queue->tid_start = INVALID_TIMER;
 		queue->tid_start = INVALID_TIMER;
 	}
 	}
 
 
-	if (queue->map != nullptr) {
-		queue->map->isReserved = false; // Remove reservation to free up for future queue
-		queue->map = nullptr;
+	if (ended) {
+		if (queue->map != nullptr) {
+			queue->map->isReserved = false; // Remove reservation to free up for future queue
+			queue->map = nullptr;
+		}
+
+		for (const auto &sd : queue->teama_members)
+			sd->bg_queue_id = 0;
+
+		for (const auto &sd : queue->teamb_members)
+			sd->bg_queue_id = 0;
+
+		queue->teama_members.clear();
+		queue->teamb_members.clear();
+		queue->teama_members.shrink_to_fit();
+		queue->teamb_members.shrink_to_fit();
+		queue->accepted_players = 0;
+		queue->state = QUEUE_STATE_SETUP;
 	}
 	}
-	queue->in_ready_state = false;
-	queue->accepted_players = 0; // Reset the queue count
 }
 }
 
 
 /**
 /**
  * Sub function for leaving a Battleground queue
  * Sub function for leaving a Battleground queue
  * @param sd: Player leaving
  * @param sd: Player leaving
- * @param lista: List of players in queue data
- * @param listb: List of players in second queue data
+ * @param members: List of players in queue data
  * @return True on success or false otherwise
  * @return True on success or false otherwise
  */
  */
-static bool bg_queue_leave_sub(struct map_session_data *sd, std::vector<map_session_data *> lista, std::vector<map_session_data *> listb)
+static bool bg_queue_leave_sub(struct map_session_data *sd, std::vector<map_session_data *> &members)
 {
 {
 	if (!sd)
 	if (!sd)
 		return false;
 		return false;
 
 
-	auto list_it = lista.begin();
-
-	while (list_it != lista.end()) {
-		struct map_session_data *player = *list_it;
-
-		if (player == sd) {
-			if (sd->bg_queue->in_ready_state) {
-				sd->bg_queue->accepted_players = 0;
-				sd->bg_queue->in_ready_state = false;
-				sd->bg_queue_accept_state = false;
-			}
+	auto list_it = members.begin();
 
 
-			lista.erase(list_it);
-
-			if (lista.empty() && listb.empty()) { // If there are no players left in the queue, discard it
-				for (auto &queue : bg_queues) {
-					if (sd->bg_queue == queue)
-						bg_queue_clear(queue.get());
-				}
-			}
+	while (list_it != members.end()) {
+		if (*list_it == sd) {
+			members.erase(list_it);
 
 
 			sc_start(nullptr, &sd->bl, SC_ENTRY_QUEUE_APPLY_DELAY, 100, 1, 60000);
 			sc_start(nullptr, &sd->bl, SC_ENTRY_QUEUE_APPLY_DELAY, 100, 1, 60000);
-			sd->bg_queue = nullptr;
+			sd->bg_queue_id = 0;
+			pc_delete_bg_queue_timer(sd);
 			return true;
 			return true;
 		} else {
 		} else {
 			list_it++;
 			list_it++;
@@ -1061,14 +1262,26 @@ static bool bg_queue_leave_sub(struct map_session_data *sd, std::vector<map_sess
  */
  */
 bool bg_queue_leave(struct map_session_data *sd)
 bool bg_queue_leave(struct map_session_data *sd)
 {
 {
-	if (!sd || !sd->bg_queue)
+	if (!sd || sd->bg_queue_id == 0)
 		return false;
 		return false;
 
 
-	if (!bg_queue_leave_sub(sd, sd->bg_queue->teama_members, sd->bg_queue->teamb_members) && !bg_queue_leave_sub(sd, sd->bg_queue->teamb_members, sd->bg_queue->teama_members)) {
-		ShowError("bg_queue_leave: Couldn't find player %s in battlegrounds queue.\n", sd->status.name);
-		return false;
-	} else
-		return true;
+	pc_delete_bg_queue_timer(sd);
+
+	for (auto &queue : bg_queues) {
+		if (sd->bg_queue_id == queue->queue_id) {
+			if (!bg_queue_leave_sub(sd, queue->teama_members) && !bg_queue_leave_sub(sd, queue->teamb_members)) {
+				ShowError("bg_queue_leave: Couldn't find player %s in battlegrounds queue.\n", sd->status.name);
+				return false;
+			} else {
+				if ((queue->state == QUEUE_STATE_SETUP || queue->state == QUEUE_STATE_SETUP_DELAY) && queue->teama_members.empty() && queue->teamb_members.empty()) // If there are no players left in the queue (that hasn't started), discard it
+					bg_queue_clear(queue, true);
+
+				return true;
+			}
+		}
+	}
+
+	return false;
 }
 }
 
 
 /**
 /**
@@ -1086,29 +1299,27 @@ bool bg_queue_on_ready(const char *name, std::shared_ptr<s_battleground_queue> q
 		return false;
 		return false;
 	}
 	}
 
 
-	queue->accepted_players = 0; // Reset the counter just in case.
-
-	if (queue->teama_members.size() != queue->required_players || queue->teamb_members.size() != queue->required_players)
+	if (queue->teama_members.size() < queue->required_players || queue->teamb_members.size() < queue->required_players)
 		return false; // Return players to the queue and stop reapplying the timer
 		return false; // Return players to the queue and stop reapplying the timer
 
 
-	s_battleground_map *bgmap = nullptr;
+	bool map_reserved = false;
 
 
-	for (auto &it : bg->maps) {
-		if (!it.isReserved) {
-			it.isReserved = true;
-			bgmap = &it;
-			queue->map = &it;
+	for (auto &map : bg->maps) {
+		if (!map.isReserved) {
+			map.isReserved = true;
+			map_reserved = true;
+			queue->map = &map;
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
-	if (!bgmap) { // All the battleground maps are reserved. Set a timer to check for an open battleground every 10 seconds.
-		queue->tid_requeue = add_timer(gettick() + 10000, bg_on_ready_loopback, 0, (intptr_t)queue.get());
+	if (!map_reserved) { // All the battleground maps are reserved. Set a timer to check for an open battleground every 10 seconds.
+		queue->tid_requeue = add_timer(gettick() + 10000, bg_on_ready_loopback, 0, (intptr_t)queue->queue_id);
 		return false;
 		return false;
 	}
 	}
 
 
-	queue->in_ready_state = true;
-	queue->tid_expire = add_timer(gettick() + 20000, bg_on_ready_expire, 0, (intptr_t)queue.get());
+	queue->state = QUEUE_STATE_SETUP_DELAY;
+	queue->tid_expire = add_timer(gettick() + 20000, bg_on_ready_expire, 0, (intptr_t)queue->queue_id);
 
 
 	for (const auto &sd : queue->teama_members)
 	for (const auto &sd : queue->teama_members)
 		clif_bg_queue_lobby_notify(name, sd);
 		clif_bg_queue_lobby_notify(name, sd);
@@ -1119,25 +1330,150 @@ bool bg_queue_on_ready(const char *name, std::shared_ptr<s_battleground_queue> q
 	return true;
 	return true;
 }
 }
 
 
+/**
+ * Send a player into an active Battleground
+ * @param sd: Player to send in
+ * @param queue: Queue data
+ */
+void bg_join_active(map_session_data *sd, std::shared_ptr<s_battleground_queue> queue)
+{
+	if (sd == nullptr || queue == nullptr)
+		return;
+
+	// Check player's current position for mapflag check
+	if (battle_config.bgqueue_nowarp_mapflag > 0 && map_getmapflag(sd->bl.m, MF_NOWARP)) {
+		clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map.
+		bg_queue_leave(sd);
+		clif_bg_queue_entry_init(sd);
+		return;
+	}
+
+	int bg_id_team_1 = static_cast<int>(mapreg_readreg(add_str(queue->map->team1.bg_id_var.c_str())));
+	std::shared_ptr<s_battleground_data> bgteam_1 = util::umap_find(bg_team_db, bg_id_team_1);
+
+	for (auto &pl_sd : queue->teama_members) {
+		if (sd != pl_sd)
+			continue;
+
+		if (bgteam_1 == nullptr) {
+			bg_queue_leave(sd);
+			clif_bg_queue_apply_result(BG_APPLY_RECONNECT, battleground_db.find(queue->id)->name.c_str(), sd);
+			clif_bg_queue_entry_init(sd);
+			return;
+		}
+
+		clif_bg_queue_entry_init(pl_sd);
+		bg_team_join(bg_id_team_1, pl_sd, true);
+		npc_event(pl_sd, bgteam_1->active_event.c_str(), 0);
+		return;
+	}
+
+	int bg_id_team_2 = static_cast<int>(mapreg_readreg(add_str(queue->map->team2.bg_id_var.c_str())));
+	std::shared_ptr<s_battleground_data> bgteam_2 = util::umap_find(bg_team_db, bg_id_team_2);
+
+	for (auto &pl_sd : queue->teamb_members) {
+		if (sd != pl_sd)
+			continue;
+
+		if (bgteam_2 == nullptr) {
+			bg_queue_leave(sd);
+			clif_bg_queue_apply_result(BG_APPLY_RECONNECT, battleground_db.find(queue->id)->name.c_str(), sd);
+			clif_bg_queue_entry_init(sd);
+			return;
+		}
+
+		clif_bg_queue_entry_init(pl_sd);
+		bg_team_join(bg_id_team_2, pl_sd, true);
+		npc_event(pl_sd, bgteam_2->active_event.c_str(), 0);
+		return;
+	}
+
+	return;
+}
+
+/**
+ * Check to see if any players in the queue are on a map with MF_NOWARP and remove them from the queue
+ * @param queue: Queue data
+ * @return True if the player is on a map with MF_NOWARP or false otherwise
+ */
+bool bg_mapflag_check(std::shared_ptr<s_battleground_queue> queue) {
+	if (queue == nullptr || battle_config.bgqueue_nowarp_mapflag == 0)
+		return false;
+
+	bool found = false;
+
+	for (const auto &sd : queue->teama_members) {
+		if (map_getmapflag(sd->bl.m, MF_NOWARP)) {
+			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map.
+			bg_queue_leave(sd);
+			clif_bg_queue_entry_init(sd);
+			found = true;
+		}
+	}
+
+	for (const auto &sd : queue->teamb_members) {
+		if (map_getmapflag(sd->bl.m, MF_NOWARP)) {
+			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map.
+			bg_queue_leave(sd);
+			clif_bg_queue_entry_init(sd);
+			found = true;
+		}
+	}
+
+	if (found) {
+		queue->state = QUEUE_STATE_SETUP; // Set back to queueing state
+		queue->accepted_players = 0; // Reset acceptance count
+
+		// Free map to avoid creating a reservation delay
+		if (queue->map != nullptr) {
+			queue->map->isReserved = false;
+			queue->map = nullptr;
+		}
+
+		// Announce failure to remaining players
+		for (const auto &sd : queue->teama_members)
+			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 340), false, SELF); // Participants were unable to join. Delaying entry for more participants.
+
+		for (const auto &sd : queue->teamb_members)
+			clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 340), false, SELF); // Participants were unable to join. Delaying entry for more participants.
+	}
+
+	return found;
+}
+
 /**
 /**
  * Update the Battleground queue when the player accepts the invite
  * Update the Battleground queue when the player accepts the invite
  * @param queue: Battleground queue
  * @param queue: Battleground queue
  * @param sd: Player data
  * @param sd: Player data
  */
  */
-void bg_queue_on_accept_invite(std::shared_ptr<s_battleground_queue> queue, struct map_session_data *sd)
+void bg_queue_on_accept_invite(struct map_session_data *sd)
 {
 {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	sd->bg_queue_accept_state = true;
+	std::shared_ptr<s_battleground_queue> queue = bg_search_queue(sd->bg_queue_id);
+
+	if (queue == nullptr) {
+		ShowError("bg_queue_on_accept_invite: Couldn't find player %s in battlegrounds queue.\n", sd->status.name);
+		return;
+	}
+
 	queue->accepted_players++;
 	queue->accepted_players++;
-	clig_bg_queue_ack_lobby(true, map_mapid2mapname(queue->map->mapid), map_mapid2mapname(queue->map->mapid), sd);
+	clif_bg_queue_ack_lobby(true, mapindex_id2name(queue->map->mapindex), mapindex_id2name(queue->map->mapindex), sd);
+
+	if (queue->state == QUEUE_STATE_ACTIVE) // Battleground is already active
+		bg_join_active(sd, queue);
+	else if (queue->state == QUEUE_STATE_SETUP_DELAY) {
+		if (queue->accepted_players == queue->required_players * 2) {
+			if (queue->tid_expire != INVALID_TIMER) {
+				delete_timer(queue->tid_expire, bg_on_ready_expire);
+				queue->tid_expire = INVALID_TIMER;
+			}
 
 
-	if (queue->accepted_players == queue->required_players * 2) {
-		queue->tid_start = add_timer(gettick() + battleground_db.find(queue->id)->start_delay * 1000, bg_on_ready_start, 0, (intptr_t)queue.get());
+			// Check player's current position for mapflag check
+			if (battle_config.bgqueue_nowarp_mapflag > 0 && bg_mapflag_check(queue))
+				return;
 
 
-		if (queue->tid_expire != INVALID_TIMER) {
-			delete_timer(queue->tid_expire, bg_on_ready_expire);
-			queue->tid_expire = INVALID_TIMER;
+			queue->tid_start = add_timer(gettick() + battleground_db.find(queue->id)->start_delay * 1000, bg_on_ready_start, 0, (intptr_t)queue->queue_id);
 		}
 		}
 	}
 	}
 }
 }
@@ -1146,31 +1482,33 @@ void bg_queue_on_accept_invite(std::shared_ptr<s_battleground_queue> queue, stru
  * Begin the Battleground from the given queue
  * Begin the Battleground from the given queue
  * @param queue: Battleground queue
  * @param queue: Battleground queue
  */
  */
-void bg_queue_start_battleground(s_battleground_queue *queue)
+void bg_queue_start_battleground(std::shared_ptr<s_battleground_queue> queue)
 {
 {
+	if (queue == nullptr)
+		return;
+
 	std::shared_ptr<s_battleground_type> bg = battleground_db.find(queue->id);
 	std::shared_ptr<s_battleground_type> bg = battleground_db.find(queue->id);
 
 
 	if (!bg) {
 	if (!bg) {
-		queue->map->isReserved = false; // Remove reservation to free up for future queue
-		queue->map = nullptr;
+		bg_queue_clear(queue, true);
 		ShowError("bg_queue_start_battleground: Could not find battleground ID %d in battlegrounds database.\n", queue->id);
 		ShowError("bg_queue_start_battleground: Could not find battleground ID %d in battlegrounds database.\n", queue->id);
 		return;
 		return;
 	}
 	}
 
 
-	uint16 map_idx = map_id2index(queue->map->mapid);
+	// Check player's current position for mapflag check
+	if (battle_config.bgqueue_nowarp_mapflag > 0 && bg_mapflag_check(queue))
+		return;
+
+	uint16 map_idx = queue->map->mapindex;
 	int bg_team_1 = bg_create(map_idx, &queue->map->team1);
 	int bg_team_1 = bg_create(map_idx, &queue->map->team1);
 	int bg_team_2 = bg_create(map_idx, &queue->map->team2);
 	int bg_team_2 = bg_create(map_idx, &queue->map->team2);
 
 
 	for (const auto &sd : queue->teama_members) {
 	for (const auto &sd : queue->teama_members) {
-		sd->bg_queue = nullptr;
-		sd->bg_queue_accept_state = false;
 		clif_bg_queue_entry_init(sd);
 		clif_bg_queue_entry_init(sd);
 		bg_team_join(bg_team_1, sd, true);
 		bg_team_join(bg_team_1, sd, true);
 	}
 	}
 
 
 	for (const auto &sd : queue->teamb_members) {
 	for (const auto &sd : queue->teamb_members) {
-		sd->bg_queue = nullptr;
-		sd->bg_queue_accept_state = false;
 		clif_bg_queue_entry_init(sd);
 		clif_bg_queue_entry_init(sd);
 		bg_team_join(bg_team_2, sd, true);
 		bg_team_join(bg_team_2, sd, true);
 	}
 	}
@@ -1178,11 +1516,9 @@ void bg_queue_start_battleground(s_battleground_queue *queue)
 	mapreg_setreg(add_str(queue->map->team1.bg_id_var.c_str()), bg_team_1);
 	mapreg_setreg(add_str(queue->map->team1.bg_id_var.c_str()), bg_team_1);
 	mapreg_setreg(add_str(queue->map->team2.bg_id_var.c_str()), bg_team_2);
 	mapreg_setreg(add_str(queue->map->team2.bg_id_var.c_str()), bg_team_2);
 	npc_event_do(queue->map->bgcallscript.c_str());
 	npc_event_do(queue->map->bgcallscript.c_str());
-	queue->teama_members.clear();
-	queue->teamb_members.clear();
-	queue->teama_members.shrink_to_fit();
-	queue->teamb_members.shrink_to_fit();
-	bg_queue_clear(queue);
+	queue->state = QUEUE_STATE_ACTIVE;
+
+	bg_queue_clear(queue, false);
 }
 }
 
 
 /**
 /**
@@ -1195,13 +1531,14 @@ static void bg_queue_create(int bg_id, int req_players)
 {
 {
 	auto queue = std::make_shared<s_battleground_queue>();
 	auto queue = std::make_shared<s_battleground_queue>();
 
 
+	queue->queue_id = bg_queue_count++;
 	queue->id = bg_id;
 	queue->id = bg_id;
 	queue->required_players = req_players;
 	queue->required_players = req_players;
 	queue->accepted_players = 0;
 	queue->accepted_players = 0;
 	queue->tid_expire = INVALID_TIMER;
 	queue->tid_expire = INVALID_TIMER;
 	queue->tid_start = INVALID_TIMER;
 	queue->tid_start = INVALID_TIMER;
 	queue->tid_requeue = INVALID_TIMER;
 	queue->tid_requeue = INVALID_TIMER;
-	queue->in_ready_state = false;
+	queue->state = QUEUE_STATE_SETUP;
 
 
 	bg_queues.push_back(queue);
 	bg_queues.push_back(queue);
 }
 }

+ 26 - 7
src/map/battleground.hpp

@@ -27,32 +27,43 @@ struct s_battleground_data {
 	struct point cemetery; ///< Respawn point for players who die
 	struct point cemetery; ///< Respawn point for players who die
 	std::string logout_event; ///< NPC Event to call on log out events
 	std::string logout_event; ///< NPC Event to call on log out events
 	std::string die_event; ///< NPC Event to call on death events
 	std::string die_event; ///< NPC Event to call on death events
+	std::string active_event; ///< NPC Event to call on players joining an active battleground
 };
 };
 
 
 struct s_battleground_team {
 struct s_battleground_team {
 	int16 warp_x, warp_y; ///< Team respawn coordinates
 	int16 warp_x, warp_y; ///< Team respawn coordinates
 	std::string quit_event, ///< Team NPC Event to call on log out events
 	std::string quit_event, ///< Team NPC Event to call on log out events
 		death_event, ///< Team NPC Event to call on death events
 		death_event, ///< Team NPC Event to call on death events
+		active_event, ///< Team NPC Event to call on players joining an active battleground
 		bg_id_var; ///< Team NPC variable name
 		bg_id_var; ///< Team NPC variable name
 };
 };
 
 
 struct s_battleground_map {
 struct s_battleground_map {
 	int id; ///< Battleground ID
 	int id; ///< Battleground ID
-	int16 mapid; ///< ID of the map
+	uint16 mapindex; ///< Index of the map
 	s_battleground_team team1, team2; ///< Team data
 	s_battleground_team team1, team2; ///< Team data
 	std::string bgcallscript; ///< Script to be called when players join the battleground
 	std::string bgcallscript; ///< Script to be called when players join the battleground
 	bool isReserved; ///< Reserve BG maps that are used so that the system won't create multiple BG instances on the same map
 	bool isReserved; ///< Reserve BG maps that are used so that the system won't create multiple BG instances on the same map
 };
 };
 
 
+/// Enum for queue state tracking
+enum e_queue_state : uint16 {
+	QUEUE_STATE_SETUP = 0, ///< The initial setup of a queue (a required amount of players hasn't been met)
+	QUEUE_STATE_SETUP_DELAY, ///< The initial setup of a queue but a required amount of players have accepted and the delay timer is active
+	QUEUE_STATE_ACTIVE, ///< The queue is active script side and more players can join (players may or may not be on the field)
+	QUEUE_STATE_ENDED, ///< The queue is no longer joinable (players are getting prizes)
+};
+
 /// Battlegrounds client interface queue system [MasterOfMuppets]
 /// Battlegrounds client interface queue system [MasterOfMuppets]
 struct s_battleground_queue {
 struct s_battleground_queue {
+	int queue_id; ///< Battlegrounds Queue ID
 	int id; ///< Battlegrounds database ID
 	int id; ///< Battlegrounds database ID
 	std::vector<map_session_data *> teama_members; ///< List of members on team A
 	std::vector<map_session_data *> teama_members; ///< List of members on team A
 	std::vector<map_session_data *> teamb_members; ///< List of members on team B
 	std::vector<map_session_data *> teamb_members; ///< List of members on team B
 	int required_players; ///< Amount of players required on each side to start
 	int required_players; ///< Amount of players required on each side to start
 	int max_players; ///< Maximum amount of players on each side
 	int max_players; ///< Maximum amount of players on each side
 	int accepted_players; ///< Amount of players who accepted the offer to enter the battleground
 	int accepted_players; ///< Amount of players who accepted the offer to enter the battleground
-	bool in_ready_state; ///< Is this BG queue waiting for players to enter the BG?
+	e_queue_state state; ///< See @e_queue_state
 	int tid_expire; ///< Timer ID associated with the time out at the ready to enter window
 	int tid_expire; ///< Timer ID associated with the time out at the ready to enter window
 	int tid_start; ///< Timer ID associated with the start delay
 	int tid_start; ///< Timer ID associated with the start delay
 	int tid_requeue; ///< Timer ID associated with requeuing this group if all BG maps are reserved
 	int tid_requeue; ///< Timer ID associated with requeuing this group if all BG maps are reserved
@@ -69,6 +80,10 @@ struct s_battleground_type {
 	std::vector<s_battleground_map> maps; ///< List of battleground locations
 	std::vector<s_battleground_map> maps; ///< List of battleground locations
 	uint32 deserter_time; ///< Amount of time a player is marked deserter (seconds)
 	uint32 deserter_time; ///< Amount of time a player is marked deserter (seconds)
 	uint32 start_delay; ///< Amount of time before the start message is sent to players (seconds)
 	uint32 start_delay; ///< Amount of time before the start message is sent to players (seconds)
+	bool solo; ///< Ability to join a queue as an individual.
+	bool party; ///< Ability to join a queue as a party.
+	bool guild; ///< Ability to join a queue as a guild.
+	std::vector<int32> job_restrictions; ///< List of jobs that are unable to join.
 };
 };
 
 
 /// Enum of responses when applying for a Battleground
 /// Enum of responses when applying for a Battleground
@@ -109,15 +124,17 @@ public:
 
 
 extern BattlegroundDatabase battleground_db;
 extern BattlegroundDatabase battleground_db;
 extern std::unordered_map<int, std::shared_ptr<s_battleground_data>> bg_team_db;
 extern std::unordered_map<int, std::shared_ptr<s_battleground_data>> bg_team_db;
+extern std::vector<std::shared_ptr<s_battleground_queue>> bg_queues;
 
 
 std::shared_ptr<s_battleground_type> bg_search_name(const char *name);
 std::shared_ptr<s_battleground_type> bg_search_name(const char *name);
+std::shared_ptr<s_battleground_queue> bg_search_queue(int queue_id);
 void bg_send_dot_remove(struct map_session_data *sd);
 void bg_send_dot_remove(struct map_session_data *sd);
 int bg_team_get_id(struct block_list *bl);
 int bg_team_get_id(struct block_list *bl);
 struct map_session_data *bg_getavailablesd(s_battleground_data *bg);
 struct map_session_data *bg_getavailablesd(s_battleground_data *bg);
 
 
-bool bg_queue_reservation(const char *name, bool state);
-#define bg_queue_reserve(name) (bg_queue_reservation(name, true))
-#define bg_queue_unbook(name) (bg_queue_reservation(name, false))
+bool bg_queue_reservation(const char *name, bool state, bool ended);
+#define bg_queue_reserve(name, end) (bg_queue_reservation(name, true, end))
+#define bg_queue_unbook(name) (bg_queue_reservation(name, false, false))
 
 
 int bg_create(uint16 mapindex, s_battleground_team* team);
 int bg_create(uint16 mapindex, s_battleground_team* team);
 bool bg_team_join(int bg_id, struct map_session_data *sd, bool is_queue);
 bool bg_team_join(int bg_id, struct map_session_data *sd, bool is_queue);
@@ -126,13 +143,15 @@ int bg_team_leave(struct map_session_data *sd, bool quit, bool deserter);
 bool bg_team_warp(int bg_id, unsigned short mapindex, short x, short y);
 bool bg_team_warp(int bg_id, unsigned short mapindex, short x, short y);
 bool bg_player_is_in_bg_map(struct map_session_data *sd);
 bool bg_player_is_in_bg_map(struct map_session_data *sd);
 bool bg_queue_check_joinable(std::shared_ptr<s_battleground_type> bg, struct map_session_data *sd, const char *name);
 bool bg_queue_check_joinable(std::shared_ptr<s_battleground_type> bg, struct map_session_data *sd, const char *name);
+void bg_queue_join_solo(const char *name, struct map_session_data *sd);
 void bg_queue_join_party(const char *name, struct map_session_data *sd);
 void bg_queue_join_party(const char *name, struct map_session_data *sd);
 void bg_queue_join_guild(const char *name, struct map_session_data *sd);
 void bg_queue_join_guild(const char *name, struct map_session_data *sd);
 void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vector<map_session_data *> list);
 void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vector<map_session_data *> list);
+void bg_queue_clear(std::shared_ptr<s_battleground_queue> queue, bool ended);
 bool bg_queue_leave(struct map_session_data *sd);
 bool bg_queue_leave(struct map_session_data *sd);
 bool bg_queue_on_ready(const char *name, std::shared_ptr<s_battleground_queue> queue);
 bool bg_queue_on_ready(const char *name, std::shared_ptr<s_battleground_queue> queue);
-void bg_queue_on_accept_invite(std::shared_ptr<s_battleground_queue> queue, struct map_session_data *sd);
-void bg_queue_start_battleground(s_battleground_queue *queue);
+void bg_queue_on_accept_invite(struct map_session_data *sd);
+void bg_queue_start_battleground(std::shared_ptr<s_battleground_queue> queue);
 bool bg_member_respawn(struct map_session_data *sd);
 bool bg_member_respawn(struct map_session_data *sd);
 void bg_send_message(struct map_session_data *sd, const char *mes, int len);
 void bg_send_message(struct map_session_data *sd, const char *mes, int len);
 
 

+ 16 - 15
src/map/clif.cpp

@@ -609,13 +609,12 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 	case BG_SAMEMAP_WOS:
 	case BG_SAMEMAP_WOS:
 	case BG:
 	case BG:
 	case BG_WOS:
 	case BG_WOS:
-		if( sd && sd->bg_id && (bg = util::umap_find(bg_team_db, sd->bg_id)))
+		if( sd && sd->bg_id > 0 && (bg = util::umap_find(bg_team_db, sd->bg_id)))
 		{
 		{
-			for( i = 0; i < bg->members.size(); i++ )
-			{
-				if( (sd = bg->members[i].sd) == NULL || !(fd = sd->fd) )
+			for (const auto &member : bg->members) {
+				if((sd = member.sd) == nullptr || (fd = sd->fd) == 0)
 					continue;
 					continue;
-				if( sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) )
+				if(sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) )
 					continue;
 					continue;
 				if( type != BG && type != BG_WOS && sd->bl.m != bl->m )
 				if( type != BG && type != BG_WOS && sd->bl.m != bl->m )
 					continue;
 					continue;
@@ -17630,12 +17629,12 @@ void clif_parse_bg_queue_apply_request(int fd, struct map_session_data *sd)
 
 
 	safestrncpy(name, RFIFOCP(fd, 4), NAME_LENGTH);
 	safestrncpy(name, RFIFOCP(fd, 4), NAME_LENGTH);
 
 
-	if (sd->bg_queue) {
-		ShowWarning("clif_parse_bg_queue_apply_request: Received duplicate queue application: %d from player %s (AID:%d CID:%d).\n", type, sd->status.name, sd->status.account_id, sd->status.char_id);
+	if (sd->bg_queue_id > 0) {
+		//ShowWarning("clif_parse_bg_queue_apply_request: Received duplicate queue application: %d from player %s (AID:%d CID:%d).\n", type, sd->status.name, sd->status.account_id, sd->status.char_id);
 		clif_bg_queue_apply_result(BG_APPLY_DUPLICATE, name, sd); // Duplicate application warning
 		clif_bg_queue_apply_result(BG_APPLY_DUPLICATE, name, sd); // Duplicate application warning
 		return;
 		return;
 	} else if (type == 1) // Solo
 	} else if (type == 1) // Solo
-		bg_queue_join_multi(name, sd, { sd });
+		bg_queue_join_solo(name, sd);
 	else if (type == 2) // Party
 	else if (type == 2) // Party
 		bg_queue_join_party(name, sd);
 		bg_queue_join_party(name, sd);
 	else if (type == 4) // Guild
 	else if (type == 4) // Guild
@@ -17670,9 +17669,9 @@ void clif_bg_queue_apply_notify(const char *name, struct map_session_data *sd)
 {
 {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	std::shared_ptr<s_battleground_queue> queue = sd->bg_queue;
+	std::shared_ptr<s_battleground_queue> queue = bg_search_queue(sd->bg_queue_id);
 
 
-	if (!queue) {
+	if (queue == nullptr) {
 		ShowError("clif_bg_queue_apply_notify: Player is not in a battleground queue.\n");
 		ShowError("clif_bg_queue_apply_notify: Player is not in a battleground queue.\n");
 		return;
 		return;
 	}
 	}
@@ -17712,8 +17711,10 @@ void clif_parse_bg_queue_cancel_request(int fd, struct map_session_data *sd)
 
 
 	bool success;
 	bool success;
 
 
-	if (sd->bg_queue) {
-		if (sd->bg_queue->in_ready_state)
+	if (sd->bg_queue_id > 0) {
+		std::shared_ptr<s_battleground_queue> queue = bg_search_queue(sd->bg_queue_id);
+
+		if (queue && queue->state == QUEUE_STATE_SETUP_DELAY)
 			return; // Make the cancel button do nothing if the entry window is open. Otherwise it'll crash the game when you click on both the queue status and entry status window.
 			return; // Make the cancel button do nothing if the entry window is open. Otherwise it'll crash the game when you click on both the queue status and entry status window.
 		else
 		else
 			success = bg_queue_leave(sd);
 			success = bg_queue_leave(sd);
@@ -17751,11 +17752,11 @@ void clif_parse_bg_queue_lobby_reply(int fd, struct map_session_data *sd)
 {
 {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	if(sd->bg_queue) {
+	if(sd->bg_queue_id > 0) {
 		uint8 result = RFIFOB(fd, 2);
 		uint8 result = RFIFOB(fd, 2);
 
 
 		if(result == 1) { // Accept
 		if(result == 1) { // Accept
-			bg_queue_on_accept_invite(sd->bg_queue, sd);
+			bg_queue_on_accept_invite(sd);
 		} else if(result == 2) { // Decline
 		} else if(result == 2) { // Decline
 			bg_queue_leave(sd);
 			bg_queue_leave(sd);
 			clif_bg_queue_entry_init(sd);
 			clif_bg_queue_entry_init(sd);
@@ -17765,7 +17766,7 @@ void clif_parse_bg_queue_lobby_reply(int fd, struct map_session_data *sd)
 
 
 /// Plays a gong sound, signaling that someone has accepted the invite to enter a battleground.
 /// Plays a gong sound, signaling that someone has accepted the invite to enter a battleground.
 /// 0x8e1 <result>.B <battleground name>.24B <lobby name>.24B (ZC_REPLY_ACK_LOBBY_ADMISSION)
 /// 0x8e1 <result>.B <battleground name>.24B <lobby name>.24B (ZC_REPLY_ACK_LOBBY_ADMISSION)
-void clig_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd)
+void clif_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd)
 {
 {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 

+ 1 - 1
src/map/clif.hpp

@@ -833,7 +833,7 @@ void clif_bg_queue_apply_result(e_bg_queue_apply_ack result, const char *name, s
 void clif_bg_queue_apply_notify(const char *name, struct map_session_data *sd);
 void clif_bg_queue_apply_notify(const char *name, struct map_session_data *sd);
 void clif_bg_queue_entry_init(struct map_session_data *sd);
 void clif_bg_queue_entry_init(struct map_session_data *sd);
 void clif_bg_queue_lobby_notify(const char *name, struct map_session_data *sd);
 void clif_bg_queue_lobby_notify(const char *name, struct map_session_data *sd);
-void clig_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd);
+void clif_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd);
 
 
 // Instancing
 // Instancing
 void clif_instance_create(int instance_id, int num);
 void clif_instance_create(int instance_id, int num);

+ 1 - 1
src/map/map.cpp

@@ -2063,7 +2063,7 @@ int map_quit(struct map_session_data *sd) {
 	if (sd->bg_id)
 	if (sd->bg_id)
 		bg_team_leave(sd, true, true);
 		bg_team_leave(sd, true, true);
 
 
-	if (sd->bg_queue != nullptr)
+	if (sd->bg_queue_id > 0)
 		bg_queue_leave(sd);
 		bg_queue_leave(sd);
 
 
 	if( sd->status.clan_id )
 	if( sd->status.clan_id )

+ 52 - 3
src/map/pc.cpp

@@ -354,6 +354,54 @@ int pc_get_group_level(struct map_session_data *sd) {
 	return sd->group_level;
 	return sd->group_level;
 }
 }
 
 
+/**
+ * Remove a player from queue after timeout
+ * @param tid: Timer ID
+ * @param tick: Timer
+ * @param id: ID
+ * @return 0 on success or 1 otherwise
+ */
+static TIMER_FUNC(pc_on_expire_active)
+{
+	map_session_data *sd = (map_session_data *)data;
+
+	nullpo_retr(1, sd);
+
+	sd->tid_queue_active = INVALID_TIMER;
+
+	bg_queue_leave(sd);
+	clif_bg_queue_entry_init(sd);
+	return 0;
+}
+
+/**
+ * Function used to set timer externally
+ * @param sd: Player data
+ */
+void pc_set_bg_queue_timer(map_session_data *sd) {
+	nullpo_retv(sd);
+
+	if (sd->tid_queue_active != INVALID_TIMER) {
+		delete_timer(sd->tid_queue_active, pc_on_expire_active);
+		sd->tid_queue_active = INVALID_TIMER;
+	}
+
+	sd->tid_queue_active = add_timer(gettick() + 20000, pc_on_expire_active, 0, (intptr_t)sd);
+}
+
+/**
+ * Function used to delete timer externally
+ * @param sd: Player data
+ */
+void pc_delete_bg_queue_timer(map_session_data *sd) {
+	nullpo_retv(sd);
+
+	if (sd->tid_queue_active != INVALID_TIMER) {
+		delete_timer(sd->tid_queue_active, pc_on_expire_active);
+		sd->tid_queue_active = INVALID_TIMER;
+	}
+}
+
 static TIMER_FUNC(pc_invincible_timer){
 static TIMER_FUNC(pc_invincible_timer){
 	struct map_session_data *sd;
 	struct map_session_data *sd;
 
 
@@ -1471,6 +1519,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	sd->expiration_tid = INVALID_TIMER;
 	sd->expiration_tid = INVALID_TIMER;
 	sd->autotrade_tid = INVALID_TIMER;
 	sd->autotrade_tid = INVALID_TIMER;
 	sd->respawn_tid = INVALID_TIMER;
 	sd->respawn_tid = INVALID_TIMER;
+	sd->tid_queue_active = INVALID_TIMER;
 
 
 #ifdef SECURE_NPCTIMEOUT
 #ifdef SECURE_NPCTIMEOUT
 	// Initialize to defaults/expected
 	// Initialize to defaults/expected
@@ -1617,9 +1666,8 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	sd->bonus_script.head = NULL;
 	sd->bonus_script.head = NULL;
 	sd->bonus_script.count = 0;
 	sd->bonus_script.count = 0;
 
 
-	// Initialize BG queue pointer
-	sd->bg_queue = nullptr;
-	sd->bg_queue_accept_state = false;
+	// Initialize BG queue
+	sd->bg_queue_id = 0;
 
 
 #if PACKETVER >= 20150513
 #if PACKETVER >= 20150513
 	sd->hatEffectIDs = NULL;
 	sd->hatEffectIDs = NULL;
@@ -13417,6 +13465,7 @@ void do_init_pc(void) {
 	add_timer_func_list(pc_global_expiration_timer, "pc_global_expiration_timer");
 	add_timer_func_list(pc_global_expiration_timer, "pc_global_expiration_timer");
 	add_timer_func_list(pc_expiration_timer, "pc_expiration_timer");
 	add_timer_func_list(pc_expiration_timer, "pc_expiration_timer");
 	add_timer_func_list(pc_autotrade_timer, "pc_autotrade_timer");
 	add_timer_func_list(pc_autotrade_timer, "pc_autotrade_timer");
+	add_timer_func_list(pc_on_expire_active, "pc_on_expire_active");
 
 
 	add_timer(gettick() + autosave_interval, pc_autosave, 0, 0);
 	add_timer(gettick() + autosave_interval, pc_autosave, 0, 0);
 
 

+ 6 - 5
src/map/pc.hpp

@@ -634,10 +634,6 @@ struct map_session_data {
 		bool changed; // if true, should sync with charserver on next mailbox request
 		bool changed; // if true, should sync with charserver on next mailbox request
 	} mail;
 	} mail;
 
 
-	// Battlegrounds queue system [MasterOfMuppets]
-	std::shared_ptr<s_battleground_queue> bg_queue;
-	bool bg_queue_accept_state; // Set this to true when someone has accepted the invite to join BGs
-
 	//Quest log system
 	//Quest log system
 	int num_quests;          ///< Number of entries in quest_log
 	int num_quests;          ///< Number of entries in quest_log
 	int avail_quests;        ///< Number of Q_ACTIVE and Q_INACTIVE entries in quest log (index of the first Q_COMPLETE entry)
 	int avail_quests;        ///< Number of Q_ACTIVE and Q_INACTIVE entries in quest log (index of the first Q_COMPLETE entry)
@@ -668,7 +664,9 @@ struct map_session_data {
 	int debug_line;
 	int debug_line;
 	const char* debug_func;
 	const char* debug_func;
 
 
-	int bg_id;
+	// Battlegrounds queue system [MasterOfMuppets]
+	int bg_id, bg_queue_id;
+	int tid_queue_active; ///< Timer ID associated with players joining an active BG
 
 
 #ifdef SECURE_NPCTIMEOUT
 #ifdef SECURE_NPCTIMEOUT
 	/**
 	/**
@@ -1296,6 +1294,9 @@ struct sg_data {
 };
 };
 extern const struct sg_data sg_info[MAX_PC_FEELHATE];
 extern const struct sg_data sg_info[MAX_PC_FEELHATE];
 
 
+void pc_set_bg_queue_timer(map_session_data *sd);
+void pc_delete_bg_queue_timer(map_session_data *sd);
+
 void pc_setinvincibletimer(struct map_session_data* sd, int val);
 void pc_setinvincibletimer(struct map_session_data* sd, int val);
 void pc_delinvincibletimer(struct map_session_data* sd);
 void pc_delinvincibletimer(struct map_session_data* sd);
 
 

+ 6 - 5
src/map/script.cpp

@@ -20016,20 +20016,21 @@ BUILDIN_FUNC(bg_get_data)
 
 
 /**
 /**
  * Reserves a slot for the given Battleground.
  * Reserves a slot for the given Battleground.
- * bg_reserve(<"bg_name">);
+ * bg_reserve("<battleground_map_name>"{,<ended>});
  */
  */
 BUILDIN_FUNC(bg_reserve)
 BUILDIN_FUNC(bg_reserve)
 {
 {
 	const char *str = script_getstr(st, 2);
 	const char *str = script_getstr(st, 2);
+	bool ended = script_hasdata(st, 3) ? script_getnum(st, 3) != 0 : false;
 
 
-	if (!bg_queue_reserve(str))
+	if (!bg_queue_reserve(str, ended))
 		ShowWarning("buildin_bg_reserve: Could not reserve battleground with name %s\n", str);
 		ShowWarning("buildin_bg_reserve: Could not reserve battleground with name %s\n", str);
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
 }
 }
 
 
 /**
 /**
  * Removes a spot for the given Battleground.
  * Removes a spot for the given Battleground.
- * bg_unbook(<"bg_name">);
+ * bg_unbook("<battleground_map_name>");
  */
  */
 BUILDIN_FUNC(bg_unbook)
 BUILDIN_FUNC(bg_unbook)
 {
 {
@@ -20075,7 +20076,7 @@ BUILDIN_FUNC(bg_info)
 			size_t i;
 			size_t i;
 
 
 			for (i = 0; i < bg->maps.size(); i++)
 			for (i = 0; i < bg->maps.size(); i++)
-				setd_sub_str(st, nullptr, ".@bgmaps$", i, map_mapid2mapname(bg->maps[i].mapid), nullptr);
+				setd_sub_str(st, nullptr, ".@bgmaps$", i, mapindex_id2name(bg->maps[i].mapindex), nullptr);
 			setd_sub_num(st, nullptr, ".@bgmapscount", 0, i, nullptr);
 			setd_sub_num(st, nullptr, ".@bgmapscount", 0, i, nullptr);
 			script_pushint(st, i);
 			script_pushint(st, i);
 			break;
 			break;
@@ -25143,7 +25144,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(bg_updatescore,"sii"),
 	BUILDIN_DEF(bg_updatescore,"sii"),
 	BUILDIN_DEF(bg_join,"i????"),
 	BUILDIN_DEF(bg_join,"i????"),
 	BUILDIN_DEF(bg_create,"sii??"),
 	BUILDIN_DEF(bg_create,"sii??"),
-	BUILDIN_DEF(bg_reserve,"s"),
+	BUILDIN_DEF(bg_reserve,"s?"),
 	BUILDIN_DEF(bg_unbook,"s"),
 	BUILDIN_DEF(bg_unbook,"s"),
 	BUILDIN_DEF(bg_info,"si"),
 	BUILDIN_DEF(bg_info,"si"),