
The shape of a game: hero timing and pacing

How I taught the match engine that a Dota game has a shape: per-hero timing windows, real game length, and late-game carries that actually win the fights they were drafted to win.
For a while there was a match in my sim that I kept reopening because it felt like a lie. A team had drafted a farmed hard carry, the kind of hero whose whole reason to exist is to be scary at forty minutes, and it lost the forty-minute fight anyway. Not because of a mistake or a great play, but because my engine did not really believe in time. Every hero shared one flat power curve, so the late-game monster and the early-game bully arrived at the big fight as roughly the same thing. And the game had already force-ended before that fight could even matter. That bothered me more than any single number ever has, because it meant the sim was missing the one idea that makes Dota Dota: a game has a shape.
The thing that felt wrong
A real Dota game moves through phases. There is an early game where lanes and tempo decide who is ahead, a mid game where fights and objectives compound small leads, and a late game where the map belongs to whoever scaled. Heroes live at different points along that timeline. Some spike hard and early and then quietly fall off. Some are useless for twenty-five minutes and then win the game by themselves. That drift is not a footnote to Dota, it is most of the strategy.
My sim did not have any of that. It had one team-wide power level that ticked up over time, and a switch that turned a team's fight strength on or off. So a hard carry and a lane bully were, mechanically, the same hero wearing different names. Nobody scaled past anybody. Nobody fell off. The late game had no identity of its own.
On top of that, games force-ended at seventy minutes on a macro score, which sounds reasonable until you realize a lot of real games about scaling are decided after that. I was cutting off the exact stretch of the match my engine most needed to get right, and then wondering why the results felt flat.
First, let the game run
The first fix in 0.5.3 was almost laughably basic. I let games run up to a hundred and twenty minutes instead of ending at seventy. If I wanted late-game scaling to mean anything, the late game had to be allowed to happen. You cannot model a forty-five-minute carry timing in a match that is legally over at seventy on points.
I also cleaned up how games actually end. Before, after one team held its Ancient through a big fight, the other side could immediately throw itself at the base again, which is not how high ground works. Now the attackers have to wait and reset before trying to end again, the way a real team has to buy back, regroup, and find another opening. I also fixed a small but ugly bug where the GG line could credit a dead hero with the last hit on the Ancient, which is the kind of detail that quietly tells a careful player the whole thing is fake.
None of this was glamorous. It was mostly me admitting that the container the match lived in was the wrong size and shape before I earned the right to touch the interesting parts.
Timing belongs to the hero, not the team
In 0.5.4 I moved timing off the team and onto the hero. Instead of one on/off switch for a whole side, spike power in fights now comes from each hero's own peak window, the same timing you can see on the hero pages. A hero is strong in a fight when the clock is inside its window and softer when it is outside. That is the real thing. When you draft, you are not just picking five kits, you are picking five timelines, and the good drafts line those timelines up.
Two smaller pieces came with it, both trying to reward that lineup. A strong laner now earns a small early farm edge over its lane opponent through about the ten-minute mark, so winning your lane actually buys you a little something instead of just a nicer feeling. And when two or more cores share the same spike window, that team gets a modest deathball teamfight bonus, because that is exactly what a deathball is: several heroes all peaking at once and forcing fights while they do.
I want to be honest that these are approximations sitting on top of hand-tuned windows, not a physics engine. But the shape of the incentive is right, and that mattered more to me than any single coefficient.
Making the late game diverge
The window idea only paid off once it reached past laning, which is what 0.5.6 was about. Peak timing now matters in the mid and late game too, not just in the first ten minutes. And past roughly thirty minutes, late-game identity finally diverges. Hard-scaling heroes like Anti-Mage, Medusa, and Spectre keep getting stronger while early-game heroes fall off. That single change is the one I had been chasing since the match that started all of this, because now a farmed late carry actually wins the forty-minute fight it was drafted to win.
That is the transferable idea, if you take one thing from this. You do not beat a hard carry by out-fighting it at minute forty. You beat it before then, while its timeline hasn't arrived yet, by taking the map and closing the game inside your own window. If you let a scaling hero reach the late game even and farmed, you have already lost the fight you are about to take. The clock was the enemy the whole time.
I also added a smaller touch of realism: a team that just burned its ultimates fights weaker in the next committed fight until it recovers. Cooldowns are a resource, and a fight you take a beat too early, before your spells are back, is a different fight from the one you win in the highlight reel.
Knowing when a game is over
Letting games run to a hundred and twenty minutes created a new problem, which is that a decided game should not limp. In 0.6.0 a hopelessly behind team now concedes on a called GG instead of the leader bouncing off high-ground defense for another ten minutes of theater. Real players type GG and move on. A sim that makes the winner grind out a settled result is wasting your time and lying about the tension.
The trick was making the game run its full real length when the outcome is genuinely in doubt, and end promptly when it isn't, without doing either one at the wrong moment. I don't think I have that balance perfect yet. It is the kind of judgment that is easy to feel and hard to encode, and I will probably keep nudging it.
So that is the shape of a game, at least as much of it as I have managed to teach the engine so far. It is still an approximation, I still get things wrong, and some of these windows are one person's best guess rather than settled truth. But the sim finally believes in time now, and the late-game carry finally wins the fight it was born to win. A lot of this came from people reopening weird matches and telling me they felt off, the same way I kept reopening mine. Please keep sending those. They are how the shape gets sharper.