Surface
Return to the fire.
Every run begins and ends at base camp. Restock at a shop whose inventory and prices scale with your level, pick up quests from the townsfolk, and bank your progress before stepping back into the dark.
Saves live in the cloud — sign in and your knight follows you across devices, with up to ten slots per account.
Depth I
No two descents match.
Dungeons are generated fresh each time you enter — rooms, corridors, loot, and ambushes rearranged on a 32-pixel grid.
Movement is tile-by-tile and deliberate. Every step is a decision, and the enemies take theirs right after yours.
Depth II
Every turn is a wager.
Combat trades initiative in strict turns: accuracy checks, damage rolls, and critical hits contested by luck — a d100 where your edge over the enemy is worth up to fifty points.
Enemies range from common to legendary, and the legendary ones did not get that title by being fair.
Depth III
What’s in the pack.
PROGRESSION
Experience, levels, and stat scaling that keep pace with the depths you reach.
EQUIPMENT & RARITY
Weapons, armor, and accessories across five rarity tiers.
CLOUD SAVES
Sign in once; game state is serialized to Postgres. Quick save, quick load, ten slots.
DYNAMIC SHOP
Store stock and prices scale with your character — no level-one daggers at level twenty.
NPC DIALOGUE
Townsfolk with things to say, jobs to offer, and rewards for the persistent.
MOBILE READY
Touch D-pad and action buttons — the whole crawl works with two thumbs.
For fellow developers
Under the hood.
KneoKnight is built in Godot 4.5 with GDScript: autoload singletons for game state, signal-based systems that stay decoupled, and rendering kept strictly separate from logic.
Development happens in the open, with 189 of 189 tests passing. The screenshots on this page are the honest pre-alpha build — placeholder art and all. The hero art up top is concept art for where it’s headed; the systems are real, tested, and in the repo.
CHARACTERMANAGER.GD
## Criticals are a contest of luck — straight from the source static func calculate_critical_hit( attacker_luck: int, defender_luck: int) -> bool: var roll = randi() % 100 + 1 # 1d100 var luck_diff = mini(attacker_luck - defender_luck, 50) return (roll + luck_diff) > 100