# Sport: ball matches and golf (sport.* kits) Team ball games and a golf round, as rules and data. Readable offline: `gripforge docs sport`, or the MCP tool `gripforge_docs` (topic `sport`). ## What it is for | Kit | What it adds | | --- | --- | | `sport.ball` | a team ball game on a walled or open field: a ball (or a puck) with its own bounce, roll and wall physics, kicked by athletes on foot or hit by cars, a goal per team, score, kickoffs, a match clock, overtime, bots in simple roles | | `sport.golf` | a golf round on a course written as data: holes, surfaces, a bag of clubs, wind, a two-step swing (aim, then stop the power gauge), one to four local players and bots taking turns, a scorecard | ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "sport.ball" } gripforge_gamekit_configure { project_id: "gkp_…", id: "sport.ball", config: { "mode": "foot", "teamSize": 3, "match": { "seconds": 180, "overtime": "golden_goal" } } } ``` Both kits require `input.actions`, `world.ground` and `game.engine`. Pair `sport.ball` with `world.arena` (same size, a gap of the goal's size in the end walls) or any flat ground; in `mode: "cars"` also install `vehicle.driveable`. `sport.golf` pairs with a camera kit that frames the ball (a top-down or third-person character kit, or a strategy camera). ## sport.ball The field runs along z, centred on the origin: team 0 defends the -z end, team 1 the +z end. Settings are grouped; the main ones and their defaults: | Group | Keys (default) | | --- | --- | | mode | `mode` `foot` (athletes who kick) or `cars` (everyone drives and hits the ball on impact) | | `field` | `width` 34 (x), `length` 52 (z, goal to goal), `walls` true, `goalWidth`, goal height and depth | | `ball` | `radius` 0.35, `mass` 1, `bounce` 0.55, `rolling` 2.5 (m/s squared), `drag` 0.05, `maxSpeed`, `restSpeed` 1, `netBounce` 0.1 | | `kick` | `reach` 1.6, `minImpulse`, `maxImpulse`, `chargeSeconds`, `lift` 0.18, `cooldown`, `maxHeight` 1.6 | | `impact` | `bodyRadius`, `bodyHeight` 1.9, `bounce` 0.6, `carBoost` 1.3, `carLift` 0.25 | | teams | `teams` (names and colours; default Blue and one more), `teamSize` 3 (1 to 5), `roles` (default keeper, chase, defend, chase, defend), `humans` 1 (0: a bot-versus-bot match to watch), `playerTeam` 0 | | `match` | `seconds` 180, `overtime` `golden_goal` / `timed` / `none`, `overtimeSeconds`, `scoreLimit` 0, `kickoffSeconds`, `goalPause`, `outOfBounds` `restart` or `kickoff` (without walls), `outPause` 1, `endGame` `true` | | `possession` / `hold` | `possession` `false`: on foot the ball sticks to the feet of the athlete controlling it until a kick or a tackle | | `ai` | `enabled` true, `speed` 6, `keeperRange` 9, `aim` 0.7, `shootRange`, `skill` 1, `pushCharge` 0.05 | Rules in short: on foot, hold the kick key to charge and release to kick where the character faces (within `kick.reach` of the ball, then `kick.cooldown`); a kick of charge c gives an impulse between `minImpulse` and `maxImpulse`, the ball leaves at impulse over mass, capped at `maxSpeed`. A goal is the whole ball over the line inside the mouth. Kickoff, then play, then goal, then kickoff; the team that conceded kicks off. At 0 the leader wins; a draw follows `match.overtime`. With `match.endGame` the end of the match ends the game: `game.won` or `game.lost` for the player's team. Service `sport.match`: `state()`, `ball()`, `ballPosition()`, `ballVelocity()`, `setBall(p, v?)`, `kick(athlete, charge, dir?)`, `kickoff(team?)`, `restart()`, `score()`, `clock()`, `overtime()`, `athletes()`, `goals()`, `holder()`, `spotOf(athlete)`. Verbs `sport_kickoff`, `sport_restart`, conditions `sport_score_at_least`, `sport_leading`. Events `sport.kickoff`, `sport.live`, `sport.kick`, `sport.touch`, `sport.goal`, `sport.out`, `sport.overtime`, `sport.ended`, `game.won`, `game.lost`. The whole state is saved. ## sport.golf | Group | Keys (default) | | --- | --- | | course | `course` (id of a `golf_courses` document; empty: the first one, else the built-in nine holes), `holes` 0 (only the first N holes; 0 is all) | | `players` | `humans` 1, `bots` 0, `names`, `colors`, `order` `farthest` or `rotation`, `maxStrokes` 12 (0: no limit) | | `layout` | `fairwayWidth` 30, `bounds` 45, `teeSpacing` 0.6 | | `ball` | `radius` 0.1, `gravity` 9.81, `drag` 0.01, `restSpeed` 0.5, `maxSpeed` 120, `slope` 1 | | `hole` | `cupRadius` 0.18, `captureSpeed` 2.5 (faster, the ball lips out) | | `shot` | `gaugeSeconds` 1.1, `accuracy` false (a third click stops a needle), `maxError` 8 degrees, `minPower` 0.05, `aimRate` 60, `autoClub` true, `clubs` (the bag; empty: every club) | | `wind` | `enabled` true, `minSpeed` 0, `maxSpeed` 8, `effect` 0.12, `perHole` true | | `flow` | `shotPause` 1, `holePause` 3, `endGame` true | | `ai` | `thinkSeconds` 1.2, `error` 3 degrees, `powerError` 0.08 | | `hidePlayer` | `true`: the character is hidden and parked at the ball so a follow camera frames the ball | A course is a document `{ id, name, holes: [...] }`; a hole has `id`, `par` (2 to 7), `tee` and `cup` (`[x, z]`), `green` (`{ radius, at? }`), `fairway` strips (`{ from, to, width }`), `bunkers` and `water` (circles or strips), `bounds` and a fixed `wind`. Surfaces are the registry `golf_surfaces` (`friction`, `bounce`, `grip`, `power`, `penalty`, `drop`, `lost`) and clubs the registry `golf_clubs` (`carry`, `loft`; a putter rolls the ball). Ball flight: gravity, drag and the wind; landing uses the surface bounce and grip; rolling uses its friction and the slope. A rest in water or out of bounds costs the surface's penalty strokes and drop. Service `sport.golf`: `state()`, `course()`, `hole()`, `players()`, `scorecard()`, `clubs()`, `club()`, `setClub(id)`, `aimAt(point)`, `aimYaw(yaw?)`, `swing(power, accuracy?)`, `cancel()`, `ball(player?)`, `distanceToCup(player?)`, `surfaceAt(x, z)`, `wind()`, `setWind(direction, speed)`, `nextHole()`, `restart()`. Verbs `golf_next_hole`, `golf_restart`, `golf_set_wind`, conditions `golf_hole_is`, `golf_leading`. Events `golf.round_started`, `golf.hole_started`, `golf.wind`, `golf.turn`, `golf.swing`, `golf.landed`, `golf.penalty`, `golf.holed`, `golf.hole_ended`, `golf.finished`. Variables `golf.hole`, `golf.par`, `golf.stroke`, `golf.player`, `golf.phase`, `golf.gauge`, `golf.club`, `golf.wind_speed`, `golf.distance`, `golf.to_par`, `golf.surface`. The course position, cards, balls, gauge and wind are saved. ## Hooks and modding | Hook | Payload | What a tap may do | | --- | --- | --- | | `sport.kick` | `{ entity, team, role, human, charge, power, lift }` | change `power` and `lift`, cancel | | `sport.goal` | `{ team, scorer, own, golden, points }` | change `points` (0 to 99), cancel (the goal is disallowed) | | `sport.kickoff` | `{ team, seconds, reason }` | change who kicks off and the countdown | | `sport.overtime` | `{ rule, seconds, overtime, score }` | change what comes next, cancel (the match ends drawn) | | `golf.wind` | `{ hole, index, direction, speed, fixed }` | change the wind, cancel | | `golf.turn` | `{ player, hole, stroke, club, distance, surface }` | change the player or the club | | `golf.swing` | `{ player, hole, stroke, club, power, accuracy, speed, loft, yaw }` | change speed, loft, yaw; cancel | | `golf.lie` | `{ player, hole, surface, penalty, drop, at }` | change the penalty or the drop, cancel | | `golf.hole_out` | `{ player, hole, par, strokes, pickedUp }` | change the strokes on the card | | `golf.finish` | `{ course, winner, player, won, totals }` | change the winner, cancel | ```json { "id": "long_shots", "hook": "sport.kick", "match": { "human": true }, "mul": { "power": 1.25 } } ``` Touches, possession, tackles, balls out of bounds and the choices of the bots are facts, not hooks: react to their events. Moddable for `sport.ball`: field, ball, kick, impact, teams, teamSize, roles, match, possession, hold, ai (not `mode`, `humans`, `playerTeam`). For `sport.golf`: holes, players, layout, ball, hole, shot, wind, flow, ai, hidePlayer, and the collections `golf_courses`, `golf_clubs`, `golf_surfaces`. ## Engines Both kits run as the same Rust logic on the web and in the native player, with the web interface fragment for the golf hud (hole, par, strokes, gauge, wind, club buttons for touch screens, card). Their engine targets are the web and the native player. ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | The ball cannot be kicked | the athlete is farther than `kick.reach`, the cooldown runs, the ball is higher than `kick.maxHeight`, or the phase is not live | | A goal does not count | the ball was not entirely over the line inside the mouth, or a `sport.goal` tap cancelled it | | The match never ends | `scoreLimit` is 0 and the clock is long, or `overtime` is `golden_goal` with a draw | | A golf shot goes short | the surface's `power` (the rough 0.8, a bunker 0.6) or a headwind; check `golf.surface` | | Putts never drop | the ball rolls faster than `captureSpeed` and lips out | | The camera ignores the ball | `hidePlayer` is off or the camera kit has no target to follow |