Allow ending a road on an existing road by placing a flag - #175
Open
iGEL wants to merge 1 commit into
Open
Conversation
In the original game a road under construction could be connected to an existing road by placing a flag on it, which splits that road. freeserf.net marked every position of an existing road as unavailable (striped marker) so this was not possible. The game logic for it was already there: Game.BuildFlag splits an existing road via BuildFlagSplitPath and Viewport.HandleDoubleClick already places a flag and connects the road under construction to it. Only the road cursor rejected those positions. DetermineMapCursorTypeRoad now shows the height indicator for positions of an existing road where a flag can be built. The direction is intentionally not marked as valid: a single click must not connect the road there as that would leave the road on top of the existing one without a flag. Only a double click (or the left+right special click, like in the original game) places the flag and connects the road. The flag rule itself moved from Game.CanBuildFlag to Map.CanBuildFlag so it can be shared with the new Map.CanRoadEndOnExistingRoad without duplicating it. Game.CanBuildFlag now delegates to it. Note that a road may still only *end* on an existing road. Passing through one stays rejected as only the destination of a road may have a flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
In the original game a road under construction could be connected to an existing road by placing a flag on it, which splits that road. For positions that allowed a flag (not too close to an existing one, no tree) the game showed the height indicator, and a special click (left + right mouse button) placed the flag and connected the road.
freeserf.net marked every position of an existing road as unavailable (the striped marker), so this was not possible.
How it works
Most of it was already there — only the road cursor rejected those positions:
Game.BuildFlagalready splits an existing road viaBuildFlagSplitPathViewport.HandleDoubleClickalready places a flag and connects the road under construction to itInterface.BuildRoadSegmentalready had a branch for a destination that has paths but no flag (it was unreachable)Pathfinder.FindShortestPathalready routes to such a position (its A* runs backwards from the destination, so the destination is never validated as a step target)So the change is in
DetermineMapCursorTypeRoad, which now shows the height indicator for positions of an existing road where a flag can be built.The direction is intentionally not marked as valid. A single click must not connect the road there, because that would leave the road on top of the existing one without a flag. Only a double click — or the left + right special click, as in the original — places the flag and connects the road. This also keeps it to a single code path: the immediate
Clickevent fires as the first half of every double click, so making the direction clickable would mean two ways to complete the same road.A road may still only end on an existing road. Passing through one stays rejected, since only the destination of a road may have a flag (
Game.CanBuildRoad).Refactoring
The flag rule moved from
Game.CanBuildFlagtoMap.CanBuildFlag(position, ownerIndex)so the newMap.CanRoadEndOnExistingRoadcan share it instead of duplicating it.Game.CanBuildFlagnow delegates to it. This also makes the rule reachable without aPlayer, which is what allows it to be tested.Tests
11 tests in
Freeserf.Test/Freeserf.Core/RoadBuildingTests.cs, covering the new rule plus the rejections that must stay in place — pass-through, adjacent flag, occupied position, foreign and unowned land — and the two cases that belong toIsRoadSegmentValidinstead.They run on a hand-built map (
new Map(new MapGeometry(3), null)with a small flat-grassMapGenerator), so noGame, renderer or game data is needed.dotnet test FreeserfNet.sln -c LinuxReleasepasses: 20 tests, including the 9 existing ones.Not covered
CreatePlaceFlagUserActionand the place-road action, and that code is unchanged.Disclosure
I am not a .NET developer. All changes in this PR were made with Claude Code, reviewed by me and tested in the running game. Please review accordingly — I am happy to adjust anything, and if the approach is not what you want for this codebase, feel free to close it.
🤖 Generated with Claude Code