Level Editor/Scripting/Examples/King of the Hill
One zone in the middle of the map. A team scores a point for every second it holds the hill with nobody from the other team on it, and the first team to 60 wins.
The map
- A brush over the hill with Is Area ticked and Trigger Name set to
Hill. Leave Zone Team at 0. - Spawn points for team 1 and team 2.
- A World Boundary below the map.
server.gd
extends GamemodeServerScript
const HILL := &"Hill"
const WIN_SCORE := 60
## Seconds held towards the next point.
var _held: float = 0.0
func _declare() -> void:
api.declare_team_counter(&"score", 0, 0, WIN_SCORE)
# Who holds the hill right now: 0 for nobody, or when it is contested.
api.declare_global_counter(&"holder", 0, 0, 2)
func _start() -> void:
if api.zones_labeled(HILL).is_empty():
api.log("no zone named Hill in this map")
func _player_died(entity_id: int, _killer_id: int, default_seconds: float) -> void:
api.respawn_in(entity_id, default_seconds)
func _zone_tick(zone: StringName, occupants: Array, delta: float) -> void:
if api.zone_label(zone) != HILL:
return
var holder: int = 0
for entity_id in occupants:
var team: int = api.team_of(entity_id)
if holder == 0:
holder = team
elif team != holder:
holder = 0
break
if holder != api.get_global(&"holder"):
api.set_global(&"holder", holder)
if holder != 0:
api.notify_all("%s took the hill" % api.team_name(holder))
if holder == 0:
_held = 0.0
return
_held += delta
while _held >= 1.0:
_held -= 1.0
if api.add_team(holder, &"score", 1) >= WIN_SCORE:
api.declare_winner(holder, "held the hill for %d seconds" % WIN_SCORE)
What it uses:
_zone_tickruns for the hill every tick, empty or not, so the timer resets the moment the hill is empty or contested. See Zones and area triggers.- The
holdercounter only changes when control changes, so the message goes out once per change rather than every tick. add_teamreturns the new score, which is all the win check needs. api.declare_winner ends the round; the game does the rest.
client.gd
extends GamemodeClientScript
func _start() -> void:
api.show_widget(&"team_score", {
"counter": "score",
"threshold": api.counter_maximum(api.SCOPE_TEAM, &"score"),
"warn_at": api.counter_maximum(api.SCOPE_TEAM, &"score") - 10,
})
The client script reads the winning score from the counter's declared maximum, so changing WIN_SCORE in the server script is the only edit a different target needs.
bot.gd
extends GamemodeBotScript const HILL := &"Hill" const REVIEW_SECONDS := 1.0 var _hill: StringName = &"" var _review: float = 0.0 func _start() -> void: var hills: Array = api.zones_labeled(HILL) if not hills.is_empty(): _hill = hills[0] func _player_joined(entity_id: int) -> void: if api.is_bot(entity_id): api.bot_set_skill(entity_id, 0.5) func _tick(delta: float) -> void: _review -= delta if _review > 0.0 or _hill == &"": return _review = REVIEW_SECONDS var at: Vector3 = api.zone_position(_hill) for entity_id in api.bots(): if api.is_alive(entity_id): api.bot_defend(entity_id, at, 8.0)
Every bot is told to hold the hill once a second. Orders do not expire, so giving the same one again is the normal way to keep it; see Bots.