Level Editor/Scripting/Examples/Team Deathmatch

Every kill scores a point for the killer's team. The first team to 25 wins, and when the 10-minute clock runs out the team ahead wins, or the round is a draw.

The map

  • Spawn points for team 1 and team 2.
  • A World Boundary below the map.

No zones are needed.

server.gd

extends GamemodeServerScript

const KILLS_TO_WIN := 25
const ROUND_SECONDS := 600

var _clock: float = 0.0


func _declare() -> void:
	api.declare_team_counter(&"kills", 0, 0, KILLS_TO_WIN)
	api.declare_global_counter(&"time_left", ROUND_SECONDS, 0, ROUND_SECONDS)


func _player_died(entity_id: int, killer_id: int, default_seconds: float) -> void:
	api.respawn_in(entity_id, default_seconds)
	# -1 when nobody earned the kill: a suicide, a fall, the world boundary.
	if killer_id < 0:
		return
	var team: int = api.team_of(killer_id)
	if team == 0 or team == api.team_of(entity_id):
		return
	if api.add_team(team, &"kills", 1) >= KILLS_TO_WIN:
		api.declare_winner(team, "first to %d kills" % KILLS_TO_WIN)


func _tick(delta: float) -> void:
	_clock += delta
	while _clock >= 1.0:
		_clock -= 1.0
		if api.add_global(&"time_left", -1) == 0:
			_time_up()
			return


func _time_up() -> void:
	var one: int = api.get_team(1, &"kills")
	var two: int = api.get_team(2, &"kills")
	if one == two:
		api.declare_winner(0, "time ran out")
	else:
		api.declare_winner(1 if one > two else 2, "time ran out")

What it uses:

  • _player_died respawns everybody on their normal timer first, then scores. A kill on a teammate scores nothing. See Deaths and respawns.
  • The clock is a global counter, so the HUD can show it. Its minimum is 0, so it stops there by itself.
  • declare_winner with team 0 is a draw.

client.gd

extends GamemodeClientScript


func _start() -> void:
	api.show_widget(&"team_score", {
		"counter": "kills",
		"threshold": api.counter_maximum(api.SCOPE_TEAM, &"kills"),
		"warn_at": api.counter_maximum(api.SCOPE_TEAM, &"kills") - 5,
	})
	api.show_widget(&"round_timer", {
		"counter": "time_left",
		"warn_at": 60,
	})

bot.gd

The starter bot script is enough: it sends every bot roaming, and a roaming bot hunts whoever is nearest, which is the whole of this mode. To make the bots easier instead, set their skill when they join; a bot with no orders roams anyway:

extends GamemodeBotScript


func _player_joined(entity_id: int) -> void:
	if api.is_bot(entity_id):
		api.bot_set_skill(entity_id, 0.4)
TeamSpec Level Editor
Basics Getting started · Interface · Viewport and camera · Selecting and hiding · Keyboard shortcuts · Settings · Map Config · Troubleshooting
Menus File · Edit · View · Tools · Functional
Panels Scene tree · Inspector · Materials · Props · Snapping · UV editor · Displacement · Terrain
Tools Move, Rotate and Scale · Box · Sphere · Cylinder · Prism · Stairs · Spiral Stairs · Spiral Tower · Arch · Path Brush · Plane · Terrain · Clip · Duplicate · Ruler and Cordon · Edit Vertex and Edge · Edit Face · Loop Cut · Material Brush · Vertex Color Brush · Displacement · Prop Path
Entities Brush · Zone · Group · Mirror Node · Prop · Spawn Point · Supply Pickup · World Boundary · Lights · Water · Sounds · Anchor Platform · Payload Track · Boss Spawn · Map Marker · World Text · Decal · Door · Button · Logic entities · 3D Skybox · Nav Region
Workflows Your first map · Brushes and CSG · Materials and UVs · Lighting and lightmaps · Bots and navigation · Mirrored maps · Outputs and logic · Custom assets · Playtesting · Exporting and publishing · Importing Source maps
Scripting Overview · Server rules · Client HUD · Bots · Teams and spawning · Zones · Pickups · Payload tracks · Map entities · Sandbox limits · Testing · King of the Hill · Team Deathmatch · API reference · What a script may use