Firstly an overview of the problem...
Sven Co-op has map "series", by which I mean one map is played (example: projectg1) and child maps after. When players complete this map, the next map in the series is loaded and played continuously until the series is over (example: projectg2, projectg3....projectg9,
end).
At the
end map, the next map in the mapcycle file should be played.
Maps have always had 3 ways to call their next map, that is if they are part of a series:
- An entity in the map calls the next map (best).
- The map config (maps/map.cfg) file sets a cvar nextmap for the server DLL to read.
- The whole series must be placed in order within the map cycle file.
The problem with 2 and 3 is that if the players don't complete the current map, the next one in the series is played anyway. Not all mappers are smart enough to see that method 1 is by far the best, neither is it practical for every Sven Co-op server admin to ripent every map to change them to method 1.
However, method 1 causes a problem with the AMXX nextmap plugin. Here is an example map cycle:
- osprey
- projectg1
- extension
- vger
- omg
- sc_cyberspy
- sc_another
- toonrun1
- wired
- shattered
- sc_egypt
When projectg1 is played, 8 maps of the cycle will be skipped because AMXX reads the map cycle differently to the Sven Co-op server DLL. The server DLL will notice that the next map in the cycle is not being played, therefore it holds the cycle position until the current series has finished (with both method 1 and 2). AMXX does not do this, it will skip 8 maps instead.
To fix both method 1, the nextmap plugin needs to take these rules into account:
- Only increment the cycle position if the current position is being played.
- If the nextmap cvar is defined (from a map), read it as the next one to play then delete the CVAR.
On top of that to fix method 2, the nextmap plugin should not increment the cycle position if the time hasn't run out. That will prevent the next parts being played due to players not completing the current part.
I don't care about method 3 being screwed, because there is only 1 map series (2 maps long) that I've seen using it.
Coding time
This is what I've come up with to fix method 1 (and partly method 2). I need some help to see/test if it works correctly, and makes logical sense.
The stuff in plugin_init to fix method 1 works completely fine.
Code:
new g_currentMap[32] // For Sven Co-op fix
new g_nextMap[32]
new g_nextMapCvar[32] // For nextmap CVAR
new g_mapCycle[32]
new g_pos
public plugin_init()
{
register_plugin("NextMap", AMXX_VERSION_STR, "AMXX Dev Team")
register_dictionary("nextmap.txt")
register_event("30", "changeMap", "a")
register_clcmd("say nextmap", "sayNextMap", 0, "- displays nextmap")
register_clcmd("say currentmap", "sayCurrentMap", 0, "- display current map")
register_clcmd("say ff", "sayFFStatus", 0, "- display friendly fire status")
register_cvar("amx_nextmap", "", FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY)
// Sven Co-op stuff: Begin
register_srvcmd("nextmap", "svenFix", FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY)
register_cvar("amx_nextmap_svenfix", "1") // Enable fix.
// Sven Co-op stuff: End
new szString[32], szString2[32], szString3[8]
get_localinfo("lastmapcycle", szString, 31)
parse(szString, szString2, 31, szString3, 7)
g_pos = str_to_num(szString3)
get_cvar_string("mapcyclefile", g_mapCycle, 31)
if (!equal(g_mapCycle, szString2))
g_pos = 0 // mapcyclefile has been changed - go from first
readMapCycle(g_mapCycle, g_nextMap, 31)
set_cvar_string("amx_nextmap", g_nextMap)
format(szString3, 31, "%s %d", g_mapCycle, g_pos) // save lastmapcycle settings
set_localinfo("lastmapcycle", szString3)
// Sven Co-op fix: Begin
new svenfix = get_cvar_num("amx_nextmap_svenfix")
if (svenfix > 0)
{
new lastMap[32]
g_pos = g_pos - 2
readMapCycle(g_mapCycle, lastMap, 31)
get_mapname(g_currentMap, 31)
log_message("[Nextmap] Current %s :: Should be %s at postion %d", g_currentMap, lastMap, g_pos)
if (!equali(g_currentMap, lastMap))
{
copy(g_nextMap, 31, lastMap)
set_cvar_string("amx_nextmap", g_nextMap)
format(szString3, 31, "%s %d", g_mapCycle, g_pos) // save lastmapcycle settings
set_localinfo("lastmapcycle", szString3)
}
else
{
g_pos++
}
}
// Sven Co-op fix: End
}
public svenFix()
{
new szNewMap[32]
read_argv(1, szNewMap, 32)
new szString3[8]
new svenfix = get_cvar_num("amx_nextmap_svenfix")
if (svenfix > 0 && !equali(szNewMap, ""))
{
copy(g_nextMapCvar, 31, szNewMap)
set_cvar_string("nextmap", "")
get_mapname(g_currentMap, 31)
if (is_map_valid(g_nextMapCvar))
{
if (!equal(g_nextMapCvar, g_currentMap))
{
copy(g_nextMap, 31, g_nextMapCvar)
set_cvar_string("amx_nextmap", g_nextMap)
format(szString3, 31, "%s %d", g_mapCycle, g_pos) // save lastmapcycle settings
set_localinfo("lastmapcycle", szString3)
}
}
else
{
log_message("[Nextmap] Invalid map in nextmap CVAR: %s", g_nextMapCvar)
}
}
log_message("[Nextmap CVAR debug] Next map in play: %s (%s)", g_nextMap, szString3)
}
__________________