Skip to content

Instantly share code, notes, and snippets.

@cetteup
Last active August 20, 2025 17:37
Show Gist options
  • Select an option

  • Save cetteup/45e24e2bf64656c4056b63797fef3342 to your computer and use it in GitHub Desktop.

Select an option

Save cetteup/45e24e2bf64656c4056b63797fef3342 to your computer and use it in GitHub Desktop.
Battlefield 2 ModManager module to modify control points at runtime
"""
ControlPointManager module
======================
Modify control point settings without modifying map files.
===== Config =====
# Add a condition
# You can use any server setting/single-value-returning RCON command as a condition for enabling control point modifications
mm_controlpointmanager.addCondition "<setting (including sv. prefix) or other RCON command>" "<value>"
# Example
mm_controlpointmanager.addCondition "sv.noVehicles" "1"
====
# Add a directive for the team of a control point to be changed
# Control point identifiers need to match those from the map files (use all lower case)
# You can easily find the identifiers in the localization files
mm_controlpointmanager.addDirective "set-team <map name> <game mode> <control point identifier>" <team>
# Example
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_64p_ms_controltower" 2
"""
import bf2
import host
import mm_utils
__version__ = 1.0
__required_modules__ = {
'modmanager': 1.6
}
__supports_reload__ = True
__supported_games__ = {
'bf2': True,
'bf2142': False, # Not tested
'bfheroes': False, # Not tested
'bfp4f': False # Not tested
}
__description__ = 'ControlPointManager v%s' % __version__
configDefaults = {
'conditions': {},
'directives': {}
}
class ControlPointManager(object):
def __init__(self, modManager):
"""Create a new instance."""
self.mm = modManager
self.__state = 0
def init(self):
"""Provides default initialisation."""
self.__config = self.mm.getModuleConfig(configDefaults)
if self.__state == 0 and len(self.__config) > 0:
host.registerGameStatusHandler(self.onGameStatusChanged)
self.__state = 1
def onGameStatusChanged(self, status):
if status != bf2.GameStatus.Playing:
return
if not self.__areConditionsMet():
self.mm.debug(1, 'Server settings do not meet conditions for modifying control points, skipping')
return
mapName = bf2.serverSettings.getMapName()
gameMode = bf2.serverSettings.getGameMode()
self.mm.debug(1, 'Checking for control point team changes on %s %s' % (mapName, gameMode))
changed = 0
for cp in bf2.objectManager.getObjectsOfType('dice.hfe.world.ObjectTemplate.ControlPoint'):
cpName = cp.templateName.lower()
current = cp.cp_getParam('team')
# Use current as default to make logging and comparing cleaner/easier
target = self.__config['directives'].get('set-team %s %s %s' % (mapName, gameMode, cpName), current)
self.mm.debug(2, 'Control point %s team is %s (%s), should be %s (%s)' % (
cpName,
current,
self.__getTeamName(current),
target,
self.__getTeamName(target)
))
if target != current:
self.mm.info('Changing control point %s from team %s (%s) to %s (%s)' % (
cpName,
current,
self.__getTeamName(current),
target,
self.__getTeamName(target)
))
cp.cp_setParam('team', target)
cp.cp_setParam('flag', target)
if target == 0:
# Move control point to the "middle" when making it neutral
cp.flagPosition = 1
cp.cp_setParam('takeOverChangePerSecond', -100)
else:
# Move control point to the top when giving it to a team
cp.flagPosition = 0
cp.cp_setParam('takeOverChangePerSecond', 100)
changed += 1
self.mm.debug(1, 'Changed team for %d control points on %s %s' % (changed, mapName, gameMode))
def __areConditionsMet(self):
for command, want in self.__config['conditions'].items():
have = host.rcon_invoke(command).strip()
self.mm.debug(2, 'Control point modification condition %s has %s, wants %s' % (command, have, want))
# Always convert to string as mod manager will even parse quoted numbers to integers
# (e.g. mm_controlpointmanager.addCondition "sv.ranked" "1")
if str(want) != have:
return False
return True
def __getTeamName(self, team):
if team == 0:
return None
return bf2.gameLogic.getTeamName(team)
def shutdown(self):
"""Shutdown and stop processing."""
host.unregisterGameStatusHandler(self.onGameStatusChanged)
self.__state = 2
#
# ModManager load
#
def mm_load(modManager):
"""Creates and returns your object."""
return ControlPointManager(modManager)
# Example configuration (only covers this module)
# Modules
modmanager.loadModule "mm_controlpointmanager"
#
# ControlPointManager v1.0
#
mm_controlpointmanager.addCondition "sv.noVehicles" 1
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_32p_ms_welldeck" 1
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_32p_ms_hanger_bay_aft" 1
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_32p_ms_controltower" 0
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_32p_ms_controltowerexterior" 0
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_32p_ms_insurgentbase" 0
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_64p_ms_welldeck" 1
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_64p_ms_hanger_bay_aft" 1
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_64p_ms_controltower" 0
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_64p_ms_controltowerexterior" 0
mm_controlpointmanager.addDirective "set-team iron_gator gpm_cq cp_64p_ms_insurgentbase" 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment