bat コウモリ
- Glyph
B- Lv
- 1
- Speed
- 22
- AC
- 8
- Attack
- 1d4
A fast bat that flits about erratically. Its bite is weak, but its movement is hard to predict, so your attacks often miss. Eating its corpse makes you confused.
The development guide for "HACK 8BIT EDITION", a roguelike that runs on an 8-bit console. It ports Hack 1.0.3 (1985) to the 6502 and adds the friendlier Modern Arrange (2020s). Here you will find the development log, the spec of the "Test Maze" built for testing, and encyclopedias of monsters, traps and items. Switch with the tabs above.
What the game is and how to play it.
HACK 8BIT EDITION is a port of the 1985 roguelike "Hack 1.0.3", rebuilt to run on an 8-bit console. It is developed by 125 Inc. This page is for first-time players: what the game is, how to control it, and some ways to enjoy it.
The game saves automatically after every move, so you can stop at any time and continue later.
Choose "NEW GAME" on the title screen to reach the preparation screen.
| Dungeon | What it is |
|---|---|
| Random | A different dungeon every time. The usual choice |
| Maze #1-#3 | The same dungeon every time, so you can compete for score on equal terms |
| Test Maze | A dungeon for development. Runs count as debug runs |
| This week (web version only) | A dungeon for this week only, changing every Monday at 9:00 (Japan time) |
| Button | What it does |
|---|---|
| D-pad | Walk. Walk into a monster to attack it |
| Ⓐ | Search around you (find hidden doors and traps). On stairs, go down or up |
| Ⓑ | Go back inside menus |
| START | Open the menu (Items, Floor, Elbereth and more) |
| SELECT | Look back at recent messages (the log) |
In the inventory, a short press of Ⓐ does the item's usual action at once (eat food, drink a potion). Hold Ⓐ to open a window where you choose another action, such as throw, drop or describe.
When playing the web version with a keyboard, use these keys:
| Key | Button |
|---|---|
| W A S D (or arrow keys) | D-pad |
| O | Ⓑ |
| P | Ⓐ |
| 9 | SELECT |
| 0 | START |
The top two lines show your status: current floor, hit points, hunger, level, strength and armor class (the smaller the number, the better your protection). The bottom two lines tell you what just happened. Red text is a warning. When in doubt, read the bottom lines first.
The first time you see a monster with a special property, the bottom line gives a short hint such as "You see ...! (steals gold and runs)". It helps you remember the monsters that are deadly if you do not know them.
When a run ends you get a score. It comes from your experience, the gold you gained and how deep you reached; if you escape to the surface, the value of your gems and the Amulet is added too.
We have no clear-rate statistics yet. Even in the original Hack, only a small handful of players ever made it back to the surface with the Amulet. The ranking marks runs that "escaped with the Amulet", so as records come in, it will show how hard it really is.
When you die, a new adventure is only a moment away. What you learn takes you a little deeper each time.
Coming soon. We will announce where you can play on this page as soon as it is ready.
The original rules and the Modern Arrange (2020s).
This page summarises the rules of HACK 8BIT EDITION (125 Inc.). The game itself (monsters, items, formulas) is carried over from the C source of the original Hack 1.0.3 as faithfully as possible. On top of that, the rules changed for easier play are called "Modern Arrange (2020s)". The current version always runs with Modern Arrange.
The main traps are the bear trap, arrow trap, dart trap, trap door, teleportation trap, pit and sleeping gas trap. Traps may be hidden; you find them by stepping on them or by searching.
| Room | Where it appears (original conditions) |
|---|---|
| Shop | Floors 2-19 |
| Vault | A hidden room connected to nothing. Linger and a guard comes to ask your name |
| Treasure zoo | Sometimes, on floor 7 and deeper |
| Beehive | Sometimes, on floor 10 and deeper. Holds royal jelly |
| Graveyard | Sometimes, on floor 12 and deeper |
| Swamp | Sometimes, on floor 19 and deeper |
On a floor where an earlier adventurer died, their grave, belongings and ghost may remain (a bones level).
Write Elbereth on the floor and, while you stand on it, most nearby monsters flee in fear. The letters smudge away as you fight and walk.
The score follows the original formula:
The ranking is ordered by score. The console keeps its top 10; the web version enters the online ranking automatically, and the 8-bit console version does so through the QR code on the result screen.
| Item | The original rules | Modern Arrange (2020s) |
|---|---|---|
| Action delays | Up to 16 turns to put on/take off armor, eat, engrave, etc. | 1 turn |
| Item names | Learned by use | Known from the start (curses and enchantment still need identify) |
| Praying | Available | Removed |
| Hunger | Every turn | 2/3 (no hunger every third turn) |
| Hidden doors | 1/7 | 1/3 |
| Mazes begin | From floor 26-28 | From floor 16-18 |
| Amulet floor | First maze at floor 30 or deeper | First maze at floor 18 or deeper |
| Frozen by a floating eye | 2/3 chance, up to 200 turns | 1/2 chance, 2-21 turns |
| First sight of a monster | No notice | A one-line hint for monsters with special properties |
| Curse marker | None | "(cursed)" on items known to be cursed |
| Inventory | Choose the action by key | Ⓐ does the usual action, hold Ⓐ to choose |
| Corridor sight | Adjacent cells only | Also see along corridors and doors you have walked (up to one corner) |
| Remembered map | Same look as visible areas | Areas not currently in view are drawn with a mesh |
| Records during play | Only at the end | Top 5 and your current standing each time you go downstairs |
| Death | The end | The end (unchanged) |
| Item | Original | Port |
|---|---|---|
| Monsters per floor | Unlimited | 24 |
| Items on the floor per floor | Unlimited | 64 |
| Traps per floor | Unlimited | 32 |
| Engravings per floor | Unlimited | 4 |
| Long worms | 31 | 1 (12 segments) |
| Monsters following you on stairs | Unlimited | 4 plus the dog |
| Bones levels | Any number | 1 |
The original stores each floor as it was; the port regenerates the floor and carries over the following.
| Item | Original | Port |
|---|---|---|
| Monsters you killed | Gone | Remembered by count and not regenerated |
| Items you dropped | All kept | Up to 16 kept |
| Dog left behind | Still there | Still there |
| Angry shopkeeper | Still angry | Still angry |
| Map you had seen | Kept | Reset |
| Doors you opened | Kept | Reset |
65 kinds. Numbers come from the original mons[] (Lv, speed — 12 equals the hero, AC — lower is tougher, attack — n dice of d sides). Red tags are the one-line hint shown the first time you see the monster.
BA fast bat that flits about erratically. Its bite is weak, but its movement is hard to predict, so your attacks often miss. Eating its corpse makes you confused.
GA small humanoid found on shallow levels. It may carry a weapon, but it is not very strong. Good early experience.
HA small goblin found on shallow levels. A weak foe that only punches with bare hands. Stay calm and fight them one at a time.
JWild dogs that come in packs. Each one is weak, but being surrounded hurts. The standard tactic is to lure them into a corridor and fight them in single file.
KA small humanoid found on shallow levels. It is weak, but its corpse is poisonous, so better not eat it.
LA little man who steals your gold and teleports away at once. Harmless if you carry no gold. Eating its corpse makes you prone to teleporting.
rA big rat common on shallow levels. A weak foe that only bites, but in numbers it wears down your hit points.
aA slime that splashes you with acid when you hit it. Killing it with missiles is safe. Its corpse never rots, and eating it cures stoning.
EAn eye that does not move. Hit it while you can see and its gaze paralyzes you for a long time. Kill it with missiles or a wand. Eating its corpse grants telepathy.
hA small demon whose bite can put you to sleep. You are defenseless while asleep. Its corpse is poisonous.
iA nimble little devil. Its wounds heal quickly, so a slow fight drags on. Finish it fast.
OA weapon-wielding humanoid, common from shallow levels to the mid game. Eating its corpse may make you feel sick.
yA light that drifts toward you and explodes, blinding you. Killing it nearby blinds you, so kill it from a distance. Eating its corpse makes you confused.
ZA sluggish undead, often found in packs in graveyards. A wand of undead turning works on it. It is slow, so escaping is easy.
AA fast giant ant whose poison sting drains your strength. A strong early foe, more dangerous than it looks. Its corpse is poisonous.
fA slowly drifting mist monster. Its attack is weak and it is little threat. It is slow too, so you can just ignore it.
NA sprite that steals an item and teleports away. Kill it and it drops what it stole. Eating its corpse makes you prone to teleporting.
pIt clings to the ceiling and drops on whoever passes below. Beware of the surprise attack. Once it has dropped, it moves slowly.
QA fast little devil. It attacks often and is not to be underestimated in the mid game. Eating its corpse may make you feel sick.
qA quivering slime. It is slow and has no special attacks. Stay calm and you can beat it down.
vA stationary mold. It sticks to you so you cannot get away. It is harmless if you keep your distance, so kill it from afar or go around it.
bA big beetle with a powerful bite. It is well armored, and a tough fight when met in the mid game. Its corpse is poisonous.
CA fast, strong half-human, half-horse. It easily catches you if you flee. A tough mid-game foe.
cA monster whose hissing breath starts turning you to stone. If stoning begins, eat an acid blob corpse. Eating its corpse turns you to stone instantly.
gA cube whose body fluid freezes you in place for a while when you hit it. Its touch can paralyze you too. Fight it with missiles or a wand.
jA big cat that attacks many times with claws and fangs. A strong mid-game foe that deals heavy damage in one turn.
kPoisonous bees that fly in swarms. Their stings are poisonous. Their hive rooms hold royal jelly. Eating a corpse may grant poison resistance.
SA poisonous snake that may hide under objects. Its bite is poisonous and lowers your strength. Its corpse is poisonous.
FA sphere that approaches and explodes, freezing you. The explosion deals heavy damage. Kill it from a distance. Eating its corpse grants cold resistance.
oA bear that grabs and crushes you. Once caught it is hard to escape, and the squeezing wears down your hit points.
RA monster whose touch rusts helmets and metal armor. If you wear precious armor, kill it with missiles. Eating its corpse makes you feel sick.
sA scorpion that stings with its poisonous tail. Its corpse is poisonous, but eating it may also grant poison resistance.
tIt teleports next to you and bites. Fleeing is useless; it catches up at once. Eating its corpse makes you prone to teleporting.
WA specter whose touch drains your levels. A wand of undead turning works on it. Eating its corpse raises your level, so eat it whenever you kill one.
wA giant worm dragging a long body. It is slow but has many hit points. Killing it may leave a worm tooth.
dA big wild dog. It is fast and its bite is strong. Eating a dog corpse makes you attract monsters.
lA very fast beast that attacks many times. Escaping it is hard. A strong foe of the deep levels.
MA monster that disguises itself as an object and lies in wait, sometimes among shop goods. Once it sticks to you it is hard to get away. Eating its corpse makes you mimic an object yourself.
TA strong troll whose wounds heal quickly. If it gets away it recovers, so finish it off in one go.
uA fast unicorn that backs away after each hit. Chasing it takes time. Killing it lowers your luck greatly (-5). Throwing it a gem improves your luck, and it leaves.
YA snowman resistant to cold. It is strong and attacks many times with its claws. Eating its corpse grants cold resistance.
IAn invisible monster. A ring of see invisible makes it easier to fight. Eating its corpse makes you invisible, or lets you see invisible if you already are.
UA huge monster whose gaze confuses you. It digs through rock. While confused you cannot move as you intend, so be careful. Eating its corpse makes you feel sick.
VIt bites and drains your levels. Its wounds heal quickly too. A wand of undead turning works on it. Its corpse is poisonous.
XA monster that passes through walls and rock. You cannot tell where it will come from, so stay alert even in corridors.
xAn insect that stings and wounds your legs. Wounded legs hamper your movement. Eating its corpse makes you feel sick.
zA very powerful giant beast. It hits hard and is one of the most fearsome foes of the deep levels. Eating its corpse makes you feel sick.
:It keeps changing into other monsters. Dangerous when it becomes a strong one. Wearing a ring of anti-shapechanger stops it from changing.
DA fire-breathing monster, among the strongest. A ring of fire resistance is reassuring. Its corpse is poisonous, but eating it grants fire resistance.
eA two-headed giant. It is strong and has many hit points. A tough foe of the deep levels.
'It lurks on the ceiling and swallows its prey whole to digest it. If swallowed, attack from inside to escape. Staying inside long is dangerous.
nShe heals your wounds if you wear no armor and wield no weapon. Eating her corpse fully restores your hit points, but it is cannibalism.
,It hides disguised as the floor and swallows its prey whole. If swallowed, attack from inside to escape. Staying inside long is dangerous.
PA giant worm. It bites and swallows you whole. If swallowed, attack from inside to escape. Eating its corpse makes you feel sick.
&A very strong demon. Between attacks it may summon fellow demons, so the longer the fight, the worse it gets.
mA very strong monster living in the maze. Fighting it head-on is dangerous. Do not hold back on wands and scrolls.
@A human who runs a shop. Pay and they sell you goods. Try to leave without paying and they attack in anger. Very strong.
dYour pet puppy. It follows you and fights at your side. As it kills enemies it grows into a dog.
dYour pet dog. Stronger than a little dog, it grows into a large dog. If it is next to you, it follows you up and down stairs.
dYour pet, a fully grown large dog. A powerful companion. If it is next to you, it follows you up and down stairs.
@A watchman who appears and asks your name if you linger in a vault. Drop the gold and follow, and he leads you to a corridor. Defy him and he gets angry.
(invisible)A ghost resting on a level with a previous adventurer's grave. It is hard to hit and moves slowly. The previous adventurer's belongings lie around the grave.
;A giant eel lurking in swamps. If it wraps around you and drags you underwater, you drown. Stay away from the water's edge.
1The wizard guarding the amulet of Yendor deep in the maze. He teleports, summons monsters with spells, and tries to steal the amulet.
dA fierce hound guarding the amulet with the wizard deep in the maze. Very strong and fast. Be fully prepared before taking it on.
7 kinds. Hidden traps are found with Ⓐ (search).
Stepping on it clamps your foot, leaving you stuck in place for a while. Dangerous to trigger with enemies nearby.
Stepping on it fires an arrow that damages you. An arrow that misses lands at your feet, so you can pick it up and use it.
Stepping on it fires a little dart. It may be poisoned, lowering your strength if it hits. Harmless if you resist poison.
Stepping on it drops you to the level below, sometimes even deeper. An adjacent pet comes along.
Stepping on it teleports you somewhere on the same level. With a ring of teleport control you can choose where. It is sometimes the entrance to a vault.
Falling in damages you, and climbing out takes several turns. Beware of being surrounded meanwhile.
Stepping on it releases sleeping gas, putting you to sleep for a while. You are defenseless while asleep.
Under Modern Arrange (2020s) every item name is known from the start (+n stays unknown until used, as in the original). Pictures are the class icon shown on the floor.
Weapons are wielded for melee or thrown as missiles. Arrows, sling bullets and crossbow bolts deal real damage when thrown with the matching bow, sling or crossbow wielded. Enchantment is raised with the scroll of enchant weapon and checked with the scroll of identify.
| arrow矢 | Arrows deal real damage when thrown with a bow wielded. They stack in bundles. Arrows that miss can be picked up and reused. |
| sling bullet石つぶて | Bullets thrown with a sling wielded. They stack in bundles. Bullets that miss can be picked up and reused. |
| crossbow boltクロスボウの矢 | Bolts thrown with a crossbow wielded. They stack in bundles. Bolts that miss can be picked up and reused. |
| dartダーツ | Small darts thrown by hand. They stack, making them handy missiles. Use them on foes you would rather not approach. |
| rock石 | A rock found everywhere. It can be thrown, but works better shot from a wielded sling. |
| boomerangブーメラン | A missile that flies in an arc and comes back. If you fail to catch it, it may hit you. |
| maceメイス | A club for bashing. An easy-to-use melee weapon. Its enchantment can be raised with the scroll of enchant weapon. |
| axe斧 | A melee weapon with a heavy blade. Decent damage. Its enchantment can be raised with the scroll of enchant weapon. |
| flailフレイル | A bashing weapon on a chain. Decent damage. Its enchantment can be raised with the scroll of enchant weapon. |
| long sword長剣 | A standard sword. A reliable melee weapon with high damage. Make it your main weapon once you find one. |
| two handed sword両手剣 | A great sword swung with both hands. Unmatched damage, but it cannot be used with a shield. For those who choose offense over defense. |
| dagger短剣 | A light dagger. Usable in melee, and useful thrown too. You can also carry several and throw them. |
| worm toothワームの牙 | A tooth dropped by a long worm. It works as a weapon as is, but reading the scroll of enchant weapon turns it into a crysknife. |
| crysknifeクリスナイフ | A sharp blade made by reading the scroll of enchant weapon on a worm tooth. A light melee weapon with high damage. |
| spear槍 | A long-shafted thrusting weapon. Decent damage. Its enchantment can be raised with the scroll of enchant weapon. |
| bow弓 | A bow for shooting arrows. Throw arrows with it wielded for real damage. Useless in melee. |
| slingスリング | A sling for shooting sling bullets and rocks. Throw them with it wielded for real damage. Useless in melee. |
| crossbowクロスボウ | A crossbow for shooting bolts. Throw bolts with it wielded for real damage. Useless in melee. |
Armor is worn and lowers your armor class (AC). The lower the number, the better the protection. You can wear one each of body armor, helmet, shield, gloves and cloak. In this 8-bit console version, putting armor on and taking it off takes no extra time. Metal armor may be rusted by a rust monster.
| helmet兜 | A helmet to protect your head. It can be worn with armor. A rust monster may rust it. |
| plate mailプレートメイル | The heavy armor with the best protection. Being metal, a rust monster may rust it. A prize worth wearing once found. |
| splint mailスプリントメイル | Heavy metal armor with strong protection, second only to plate mail. A rust monster may rust it. |
| banded mailバンデッドメイル | Heavy metal armor with strong protection, equal to splint mail. A rust monster may rust it. |
| chain mailチェインメイル | Metal armor of woven chain. A good balance of protection and ease of use. A rust monster may rust it. |
| scale mailスケイルメイル | Metal armor of overlapping scales. Medium protection. A rust monster may rust it. |
| ring mailリングメイル | Metal armor with sewn-on rings. Modest protection. A rust monster may rust it. |
| studded leather armor鋲打ちの革鎧 | Leather armor reinforced with studs. Modest protection, but it is not metal, so it never rusts. |
| leather armor革鎧 | Light leather armor. The lowest protection of any armor, but it never rusts. Far better than wearing nothing. |
| elven cloakエルフのマント | A light cloak worn over armor. Wear it with armor for a little extra protection. |
| shield盾 | A shield worn on the arm. Wear it with armor for extra protection. It cannot be used with a two handed sword. |
| pair of gloves手袋 | Armor worn on the hands. Wear them with armor for a little extra protection. They are not metal, so they never rust. |
Potions are drunk. Some have good effects such as healing or raising abilities; others are dangerous, such as paralysis, blindness or confusion. Under Modern Arrange (2020s) all names are known from the start, so you can pick the right one. Do not drink dangerous potions near enemies.
| restore strength力回復の薬 | Restores lost strength to its maximum. Drink it when poison has lowered your strength. |
| booze酒の薬 | Liquor. It tastes like liquid fire. Drinking it confuses you, and you may pass out. |
| invisibility透明の薬 | Drinking it makes you invisible. Monsters have trouble targeting you, making fights and escapes easier. |
| fruit juice果汁の薬 | Fruit juice. Drinking it satisfies a little hunger. It is harmless, so it tides you over when food runs out. |
| healing治療の薬 | Restores hit points and cures blindness. At full hit points it may raise your maximum hit points. |
| paralysis麻痺の薬 | Drinking it freezes your feet to the floor, leaving you unable to move for a while. Fatal if drunk near an enemy. |
| monster detection怪物感知の薬 | Reveals the locations of monsters on the level. Use it to check for dangerous foes. |
| object detection物体感知の薬 | Reveals the locations of objects on the level. Useful for finding treasure and tools. If there are none, you see nothing. |
| sickness病気の薬 | A potion that tastes like poison. Drinking it lowers strength and hit points. It is harmless if you resist poison, such as with a ring of poison resistance. |
| confusion混乱の薬 | Drinking it confuses you, so you cannot move as you intend for a while. Only drink it somewhere safe. |
| gain strength力増強の薬 | Raises strength. The stronger you are, the more often you hit and the more damage you deal. |
| speed速さの薬 | Makes you move faster, so you act more often than enemies. It also heals wounded legs. |
| blindness盲目の薬 | Blinds you for a while. One use: you will not be paralyzed when hitting a floating eye. |
| gain levelレベル上昇の薬 | Gives you experience and raises your level by one. Your maximum hit points rise too, making strong foes easier to fight. |
| extra healing特効治療の薬 | Restores more hit points than the potion of healing. It also cures blindness and more easily raises maximum hit points. |
| levitation浮遊の薬 | Makes you float in the air for a while. While floating you cannot pick things up or go down stairs. You will not trigger traps. |
Scrolls are read, and vanish once read. Reading one while confused may produce a different effect. Scrolls that enchant armor or weapons affect what you wear or wield. Under Modern Arrange (2020s) names are known from the start, but enchantment and curses on your items must be found with the scroll of identify.
| mail手紙の巻物 | A scroll of delivered mail. It seems blank inside, and reading it does nothing. Unlike other scrolls, it is useless. |
| enchant armor鎧強化の巻物 | Enchants the armor you wear to raise its protection. Reading it on over-enchanted armor may make the armor evaporate. |
| destroy armor鎧破壊の巻物 | Turns one piece of the armor you wear to dust. Do not read it while wearing precious armor. |
| confuse monster怪物混乱の巻物 | Reading it makes your hands glow, and the next monster you hit becomes confused. Confused monsters move at random. |
| scare monster怪物威嚇の巻物 | Reading it makes nearby monsters flee. Placed on the floor, monsters cannot step onto it. Picking it up again may turn it to dust. |
| blank paper白紙の巻物 | A scroll with nothing written on it. Reading it only tells you it seems to be blank. It has no effect. |
| remove curse解呪の巻物 | Removes curses from your belongings. Use it to take off cursed equipment that will not come off. |
| enchant weapon武器強化の巻物 | Enchants your wielded weapon. Reading it on a worm tooth makes a crysknife. Over-enchanting may make the weapon evaporate. |
| damage weapon武器損傷の巻物 | Lowers the enchantment of your wielded weapon. Read it with no weapon wielded and it does no harm. |
| create monster怪物召喚の巻物 | Reading it summons a monster. Usable for gaining experience, but the risk is high. |
| taming手なずけの巻物 | Tames adjacent monsters into pets. A trump card when surrounded by strong monsters. |
| genocide根絶の巻物 | Wipes out every monster of the kind you choose. Use it on strong or troublesome foes. Choose humans and you vanish too. |
| light光の巻物 | Lights up the room. Use it to look around a dark room. Read while confused, it darkens the room instead. |
| teleportation瞬間移動の巻物 | Reading it teleports you somewhere on the same level. An escape from danger. Read while confused, it may send you to another level. |
| gold detection金感知の巻物 | Reveals the locations of gold on the level. If there is none, you see nothing. |
| food detection食料感知の巻物 | Reveals the locations of food on the level. Use it when food is running low. |
| identify識別の巻物 | Identifies your belongings, revealing enchantment and curses. With luck it identifies several at once. |
| magic mapping地図の巻物 | Reveals the map of the level, including the stairs at once, saving you the trouble of exploring. |
| amnesia忘却の巻物 | Makes you forget the map of places you cannot currently see. Reading it gains you nothing. |
| fire炎の巻物 | Reading it raises a pillar of fire, engulfing you and your surroundings. Read while confused, it only burns your hands. |
| punishment罰の巻物 | As punishment, chains a heavy iron ball to your leg. It greatly restricts your movement, so do not read it. |
Wands are zapped in a chosen direction. Rays of magic missile, fire, cold, sleep and death bounce off walls, so zap in a direction where they will not come back at you. Charges are limited; once used up, nothing happens. Under Modern Arrange (2020s) names are known from the start.
| light光の杖 | Zapping it lights up the surroundings. Useful for looking around a dark room. Same effect as the scroll of light. |
| secret door detection隠し扉発見の杖 | Zapping it reveals nearby secret doors and secret corridors. Use it when stuck at a dead end. |
| create monster怪物召喚の杖 | Zapping it makes a monster appear. It only adds enemies, so it has few uses. Good for experience, but risky. |
| wishing願いの杖 | Zapping it grants a wish for any one item you like. Think carefully about what to wish for. |
| striking打撃の杖 | Strikes the target with an invisible force for damage. A handy means of attack. |
| slow monster減速の杖 | Slows the monster it hits. Use it on fast, strong foes to make the fight easier. |
| speed monster加速の杖 | Speeds up the monster it hits. Dangerous on enemies, but great on a pet. |
| undead turning死者退散の杖 | Repels the dead. Works on undead such as zombies, wraiths and vampires. |
| polymorph変化の杖 | Changes the monster it hits into another monster. It will not necessarily be weaker, so it is a gamble. |
| cancellation無効化の杖 | Removes the special powers of the monster it hits. Effective against foes with nasty abilities such as stealing or level drain. |
| teleportation瞬間移動の杖 | Sends the monster it hits somewhere on the same level. Gets rid of foes you cannot handle. |
| make invisible透明化の杖 | Makes the monster it hits invisible. It only adds invisible enemies, so using it on foes backfires. |
| digging穴掘りの杖 | Digs a hole through walls and rock in the chosen direction. Aim it down to dig through the floor to the level below. |
| magic missile魔法の矢の杖 | Fires a magic missile. Rays bounce off walls, so zap it in a direction where it will not hit you. |
| fire炎の杖 | Fires a ray of fire. High damage. Rays bounce off walls, so zap it in a direction where it will not hit you. |
| sleep眠りの杖 | Fires a ray that puts monsters to sleep. Sleeping foes can be attacked freely. Beware of a bounce putting you to sleep. |
| cold冷気の杖 | Fires a ray of cold. High damage. Rays bounce off walls, so zap it in a direction where it will not hit you. |
| death死の杖 | Fires a ray that instantly kills the monster it hits. Your last trump card. Fatal if it bounces back and hits you. |
Rings are worn, one on each hand. Their effect lasts while worn, but they make you hungrier. With both hands full, remove the uncursed one to swap. A cursed ring cannot be removed; use the scroll of remove curse.
| adornment装飾の指輪 | A decorative ring. Wearing it has no special effect. It only makes you hungrier, so take it off. |
| teleportation瞬間移動の指輪 | While worn, it teleports you at random now and then. Handy combined with a ring of teleport control. |
| regeneration再生の指輪 | Makes your wounds heal faster. Reliable on deep levels with constant fighting, but it makes you hungrier. |
| searching探索の指輪 | While worn, it automatically searches for secret doors and traps. Exploring becomes much easier. |
| see invisible透明視の指輪 | Lets you see invisible monsters. Use it against unseen foes such as stalkers. |
| stealth忍び足の指輪 | Silences your footsteps so sleeping monsters rarely wake. Lets you sneak past strong foes. |
| levitation浮遊の指輪 | Makes you float in the air. While floating you cannot pick things up or go down stairs. Take it off to return to normal. |
| poison resistance耐毒の指輪 | Protects against poison. Poison stings, darts, the potion of sickness and poisonous corpses lose their threat. Especially reliable on deep levels. |
| aggravate monster怪物刺激の指輪 | Draws nearby monsters to you and angers them. Wearing it does no good, so take it off. |
| hunger空腹の指輪 | While worn, it makes you get hungry very fast. Wearing it does no good, so take it off. |
| fire resistance耐火の指輪 | Grants resistance to fire. Protects against dragon fire, rays of fire and the scroll of fire. |
| cold resistance耐寒の指輪 | Grants resistance to cold. Protects against freezing sphere explosions and rays of cold. |
| anti-shapechanger変身防止の指輪 | Stops shapechangers such as chameleons from changing form. No more worries about them turning into something strong. |
| conflict争いの指輪 | Makes nearby monsters fight each other. Put it on when surrounded and your enemies start attacking one another. |
| gain strength力増強の指輪 | Raises strength by its enchantment. If the enchantment is negative, it lowers strength instead. |
| increase damage攻撃力増加の指輪 | Increases the damage of your attacks by its enchantment. If the enchantment is negative, it decreases damage instead. |
| protection守りの指輪 | Raises your defense by its enchantment. If the enchantment is negative, it lowers defense instead. |
| warning警告の指輪 | Warns you of nearby monsters and how dangerous they are. Prevents surprise attacks. |
| teleport control瞬間移動制御の指輪 | Lets you choose your destination when you teleport. Use it with a ring or scroll of teleportation. |
Tools are applied for their effects. The whistle, magic whistle, camera, pick-axe, ice box, can opener and others each have their own uses. The pick-axe is a reliable tool that digs through walls, but shops will not let it in.
| whistle笛 | Blowing it makes a shrill sound that wakes nearby monsters. It has few uses. Do not confuse it with the magic whistle. |
| magic whistle魔法の笛 | Blowing it makes a strange sound that calls your pet to your side. Handy for bringing back a stray dog. |
| expensive camera高いカメラ | A camera whose flash blinds monsters. Aim it at a foe, then flee or attack while it is blinded. |
| ice boxアイスボックス | A cold box that keeps corpses inside from rotting. Use it to save corpses you want to eat or that have useful effects. |
| pick-axeつるはし | Digs through walls and rock, and can dig a hole in the floor to go down a level. Shops will not let it in, so leave it outside. |
| can opener缶切り | A tool for opening tins. In this version tins are opened when you eat them, so it is almost never needed. |
Gems and glass. Bring gems to the surface and their value becomes your score. They can also be thrown as weapons. Under Modern Arrange (2020s) names are known from the start, so you can tell real gems from worthless glass.
| diamondダイヤモンド | The most valuable gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| rubyルビー | A valuable red gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| sapphireサファイア | A valuable blue gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| emeraldエメラルド | A valuable green gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| turquoiseトルコ石 | A green gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| aquamarineアクアマリン | A blue gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| tourmalineトルマリン | A green gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| topazトパーズ | A yellow gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| opalオパール | A yellow gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| garnetガーネット | A dark gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| amethystアメジスト | A violet gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| agate瑪瑙 | A green gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| onyxオニキス | A white gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| jasper碧玉 | A yellowish-brown gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| jade翡翠 | A green gem. Bring it to the surface and it scores points by its value. Under Modern Arrange (2020s) its name is known, so you cannot mistake it for glass. |
| worthless piece of blue glass青いガラス玉 | A worthless blue piece of glass. It scores no points even if brought to the surface. Under Modern Arrange (2020s) its name is known, so you can tell it from gems. |
| worthless piece of red glass赤いガラス玉 | A worthless red piece of glass. It scores no points even if brought to the surface. Under Modern Arrange (2020s) its name is known, so you can tell it from gems. |
| worthless piece of yellow glass黄色いガラス玉 | A worthless yellow piece of glass. It scores no points even if brought to the surface. Under Modern Arrange (2020s) its name is known, so you can tell it from gems. |
| worthless piece of green glass緑のガラス玉 | A worthless green piece of glass. It scores no points even if brought to the surface. Under Modern Arrange (2020s) its name is known, so you can tell it from gems. |
Food is eaten to satisfy hunger. Go too hungry and you weaken, then eventually faint. Food is sometimes rotten, and old or poisonous corpses are dangerous. On the other hand, some corpses grant special properties. Overeating when satiated makes you choke.
| food ration食料 | A nourishing portable meal. One lasts a long time. Beware: eating when satiated can make you choke to death. |
| tripe ration臓物 | Dog food. People who eat it may vomit, but vomiting cures food poisoning. Good for feeding pets. |
| pancakeパンケーキ | Ordinary food. An easy way to satisfy hunger. Old ones may be rotten. |
| fortune cookieおみくじクッキー | Break it open for a slip of paper with dungeon rumors and advice. It never rots, so it is always safe to eat. |
| carrotニンジン | Ordinary food. An easy way to satisfy hunger. Old ones may be rotten. |
| tin缶詰 | A tin with unknown contents. It is opened when you eat it. It satisfies hunger like ordinary food. |
| orangeオレンジ | An orange. Not very nourishing, but light and easy to carry. Good for a quick snack. |
| appleリンゴ | An apple. Not very nourishing, but light and easy to carry. Good for a quick snack. |
| pear洋ナシ | A pear. Not very nourishing, but light and easy to carry. Good for a quick snack. |
| melonメロン | A melon. Not very nourishing, but light and easy to carry. Good for a quick snack. |
| bananaバナナ | A banana. Not very nourishing, but light and easy to carry. Good for a quick snack. |
| candy barキャンディー | A sweet treat. Light and quick to satisfy hunger. Keep one as emergency food. |
| egg卵 | Ordinary food. An easy way to satisfy hunger. Old ones may be rotten. |
| clove of garlicニンニク | Ordinary food. An easy way to satisfy hunger. Old ones may be rotten. |
| lump of royal jellyローヤルゼリー | A nourishing lump found in killer bee hives. Eating it raises strength, restores hit points, and heals wounded legs. Eat it whenever you find one. |
The only amulet is the amulet of Yendor. It is guarded by the wizard in the maze on the bottom level of the dungeon; escaping to the surface with it is the goal of the adventure. Beware of cheap fakes.
| amulet of Yendorイェンダーの魔除け | The legendary amulet guarded by the wizard on the bottom level of the dungeon. Bring it back to the surface to win. Cheap fakes also exist. |
| enormous rock大岩 | A huge rock blocking a corridor. Walk into it to push it, but it will not move if the way beyond is blocked. |
| iron chain鉄の鎖 | The chain linking the heavy iron ball to your leg. Reading the scroll of punishment chains it to your leg along with the ball. |
| heavy iron ball重い鉄球 | A heavy ball chained to your leg by the scroll of punishment. You must drag it along, and stairs become a struggle. |
From Rogue to Hack and NetHack, and where this port fits.
Hack 1.0.3, the basis of HACK 8BIT EDITION, sits in the middle of a family of games that has lasted more than 40 years. This page follows that lineage in order. Dates and names are kept to what is widely known; where details are uncertain, they are described in general terms.
Around 1980, Michael Toy and Glenn Wichman, then students at the University of California, Santa Cruz, created "Rogue". Ken Arnold later joined the development, and the game came to be played on Unix terminals.
Rogue's defining features were:
Rogue was included in the BSD Unix distribution, and spread widely through university and research computers.
In 1982, Jay Fenlason, then a high-school student in Massachusetts, USA, created "Hack" together with his friends Kenny Woodland, Mike Thome and Jonathan Payne. Built on Rogue's gameplay, it greatly expanded the variety of monsters and items.
Andries Brouwer, at the mathematics and computer science institute CWI (Centrum Wiskunde & Informatica) in Amsterdam, the Netherlands, reworked Hack extensively and released it as Hack 1.0 in 1984. The source was distributed over Usenet, the network news of the day, and improved versions followed.
The 1985 version is Hack 1.0.3, and it is the version HACK 8BIT EDITION ports.
Hack 1.0.3 already has many elements that NetHack would later inherit:
The source carries the notice "Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, Amsterdam". Redistribution and modification are permitted on condition that the copyright notice is kept (a BSD-style license). This port exists thanks to that license.
In 1987, Mike Stephenson and others released "NetHack", bringing together the work of Hack 1.0.3 and its modified versions. The "Net" in the name comes from the developers being connected over the network and building it together.
NetHack has continued to be improved by its development team ever since, with monsters, items, classes and special levels growing greatly in number. It is still being updated today, making it one of the longest-running game projects in development.
Rogue's influence was not limited to the Hack family.
These games came to be called "roguelikes": games that share a generated dungeon, turn-based play and permanent death. In Japan, the style became widely known in the 1990s through games for home consoles, and today its influence can be seen across many genres.
Rogue (around 1980)
├─ Hack (1982)
│ └─ Hack 1.0 - 1.0.3 (1984-1985, CWI)
│ ├─ NetHack (1987 - present)
│ └─ HACK 8BIT EDITION (2026, 125 Inc.) <- this port
└─ Moria (early 1980s)
└─ Angband (around 1990) and its descendantsHACK 8BIT EDITION is based on Hack 1.0.3, not NetHack. The original C source was read as a specification, and its rules (random numbers, dungeon generation, combat, shops, traps and more) were rewritten in 6502 assembly. As a result, the monsters, items and formulas are almost the same as in the Hack of 1985.
On top of that, it adds changes for playing on an 8-bit console (graphics and sound, buttons and menus, Japanese and English, automatic saving) and "Modern Arrange (2020s)" rules for easier play. The "Game rules" page lists what was changed.
While NetHack has grown enormously over nearly 40 years, Hack 1.0.3 preserves the smaller, more compact form that came just before it. The aim of this port is to make this version, the midpoint between Rogue and NetHack, playable with today's screens and controls.
| Role | Name |
|---|---|
| Original HACK 1.0.3 | Jay Fenlason, Kenny Woodland, Mike Thome, Jonathan Payne, Andries Brouwer |
| Original source | Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, Amsterdam |
| Port | 125 Inc. (SUZUKI08 + AI) |
| Fonts | k8x12 and Misaki font (Num Kadoma) |
Every request from play sessions, in-page reports and chat instructions, fixed one by one with a version bump. Open a day to read it (newest first). Translated from the Japanese log.
Review round #0 marks (title name, class-select stairs, dungeon kinds as a vertical list, hold-A icon, direction hint glitch, nymph, stair table, seed page, etc.) and short clips per feature.
3.99 (title window: the logo is hidden while it is open, the window grows 3 rows upward with the current name centred as a heading over a separator, and the item is now "Change name"; the class-select stairs icon lost its floor rim; dungeon kinds are picked from a vertical sub-window (Up/Down, A OK, B Back); the inventory hint uses a two-cell hold-A icon + "More"; the direction hint no longer leaves half-glyphs after a second prompt, and the unanswered prompt no longer reappears in the small lines after the inventory; thieves float up about 4 tiles, blink out and "{M} vanishes."; the stairs and ripple on the floor-change screen are dimmer; the seed page left the START menu; inventory sorting moved from bank 13 to 32. Review page round #1 with 7 clips and replies.)
Review #1: result window a bit left, A opens a Title/QR/Rename sub-window (parent greyed, B back), show the whole flow on video; redesign the stairs; nymph blinks while rising over walls and hides under the top UI; colour the hero while turning to stone; longer ball-and-chain clip; put items needing answers on top.
4.00 (result window moved 4 columns left with only "A OK" at the bottom; A opens a sub-window Menu/Title/QR/Change name/B Back A OK while the window turns grey, B restores the field under the overhang and redraws the window instantly (rsmenu.s, mon2.s rs_body, rp.s rp_box/rp_atts/RP_FST); the name entry greys the title window from row 7; floor-change stairs redrawn without brick joints: a lit tread line, a shadow between steps and a solid slope; thieves blink all the way up and hide under the top UI; the hero turns dark grey while stoning and back when cured; clips v_dead (44 s), v_nymph, v_cock, v_ball (30 s); review page round #2 split into "needs an answer" and "decided".)
Review #2: title window 16 px taller; add Test Maze (Auto) and put the hint right under the list; hold-A icon wide with A centred; dog follows more and picks up less; result sub-window "Result QR" with a tighter hint; QR detail with a centred first line and a rule; thinner stairs; long worm, thieves and punishment in the Test Maze.
4.01 (title window top at row 5 with the name heading and a blank row before the items; dungeon kind 5 = Test Maze (Auto), for now the same maze with SD_AUTO = 1, and the sub-window height follows the item count; wide hold-A icon; the dog approaches 3/4 of the time in rooms (original 1/4) and picks things up a quarter as often; result sub-window shows Result QR with the hint right under the items; QR detail drops the second line and centres the first over a rule; stairs without the middle block row; Test Maze B4 long worm room, B5 thieves and punishment rooms. Review round #3.)
QR heading: drop "This run"; show it like the ranking (name, score, count, *), with a red dot when cheats were used.
4.02 (the QR heading drops the prefix and centres name, score, deepest floor and the escape marks; a run that used debug gets a red dot at the end, drawn as a sprite (tiles 48-49, palette 2 red: rank.s rk_dot).)
Avoid the boulder scene on B12. The puppy left behind on B1 feels odd: let the pet always go down and come back with you. Raise the return-trip monster spawn a bit from 1/70 (the way back feels like a chore).
3.91 (in the Test Maze the dog follows you on stairs wherever it is (follow.s dg_follow; the main game keeps the original adjacent-only rule). While carrying the real Amulet, monsters spawn at 1/35 instead of the original 1/70 (follow.s et_rnd from mon2.s et_post; every dungeon). tools/rec_dd.js: boulders are pushed only when the far side is free, and a boulder that would not move is remembered and avoided. When chasing the Wizard, paths without boulders come first, and pacing back and forth also counts as stuck (it used to stall for 5 minutes on B5).)
(Follow-up) Re-record.
No game change. tools/rec_dd.js: the dog also starts with 80 HP (healed below 20% as an aid); before, it had 2 HP and fell in the B9 bestiary. On B12 the Wizard is defeated without pushing boulders, and the dog stays with you from B12 to the surface. Aids: food 3, heal 7, teleport 2, level restore 5. Recording 16.6 min (build/20261105 HACK Test Maze autopilot.mp4), X edit 2:18 (about 7.8x, the final escape and result at 2x with sound).
When the dog goes feral, show it with the pet look in red and name it "feral Hachi". Red can no longer be picked as the dog colour.
3.92 (a dog left behind for more than 250 turns stays in slot 0 on your return but is no longer tame: it uses the pet dog sprite in red (enemy palette 2, sprite.s) and is named "feral {dog name}" (text.s ml_name, loc m_dog_wild). Before, it became an ordinary little dog enemy (lvsave2.s lv2_dog). Only a tame dog swaps places with you (main.s; a feral dog is attacked), follows on stairs and trapdoors (follow.s dg_follow; trap.s dog_adjacent removed), follows same-level teleport, is remembered as a dog left behind, and shows dog damage on the status line. Dog colours are white, brown, blue and yellow (red is skipped in petsel.s; a red picked in an older version shows as white). Test: WILDT=1 node tools/rec_dd.js)
Show the feral dog getting tamed again with food in a video: name Hachi, original colour brown.
3.93 (a brown dog appeared white on the stairs of the black floor-change screen: during the animation only the x side of VBUF is sent, so every frame now writes the Shiba colours to sprite palette 1 colours 2-3 (lvanim.s la_dogpal). tools/rec_dd.js FERAL=1: Maze #1, name Hachi and colour brown in the Dog window, then leave Hachi on B1, wait 283 turns on B2 and come back to a red "feral Hachi"; throw tripe (START, inventory, hold A, Throw, direction) and "You miss feral Hachi. Hachi devours the tripe." turns it back into brown Hachi following you. Recording 1:11 (build/20261105 HACK feral Hachi.mp4, the wait on B2 at 8x).)
The direction window for arrows and rays is annoying: as an exception, ask in the bottom UI, like "* Throw direction B Back".
3.94 (directions are asked with a line in the bottom window instead of a sub-window: "✚Throw ⒷBack", Zap (wands), Aim (camera) and Dig (pick-axe) (zap.s getdir: A = kind, loc m_dir_q0-3). ✚ is a new D-pad glyph (tools/loc.py). Ⓐ for down and SELECT for up still work without being shown. The map and bottom window keep drawing while waiting (dir_frame).)
Put the hint where "Ⓐ Look" is. Show "Which way to shoot ...?" as a message, logged as "... ? (left)". SELECT should pick self too.
3.95 (while asking for a direction, the top-right hint of the bottom window (where Ⓐ Look sits) widens to "✚Aim SESelf ⒷBack" (fastui.s ms_hid 8, ms_hw 14/17 columns, ms_hatt, and ms_hcol remaps the name table when the width changes). The message reads "Which way to shoot arrow (20)?" (throw, zap, aim the camera, dig; shoot when the ammo matches the wielded launcher), and the answer is appended to the same line as " (right)" (" (cancel)" when backed out), which is also logged as one line (nothing is logged while asking). SELECT picks self (Ⓐ unused, no up). The code lives in the new bank 32 (dirq.s, F32: the start of the ROM cleanup).)
Is (20) the count? Not needed.
3.96 (the item name in the direction prompt no longer shows the count: "Which way to shoot an arrow? (right)" (dirq.s: on_q = 1).)
Not SE, shouldn't Ⓐ be "self"? Does it clash with Ⓐ?
3.97 (while asking for a direction, Ⓐ now means self/down instead of SELECT (zap.s getdir). The hint reads "✚Aim ⒶSelf ⒷBack" (13/16 columns). No clash: the Ⓐ press that picked the item must be fully released before the question starts.)
Order the hint like the physical buttons (B left, A right): Aim, B Back, A Self.
3.98 (the direction hint now reads "✚Aim ⒷBack ⒶSelf" (loc ui_hint_dir), matching where the buttons sit on the pad.)
Videos take too long to watch: a public page listing screens and features where I can mark ○/× and comment (dev.html under Hack on base-08).
Review page https://base-08.web.app/app_nes_hack/dev.html (tools/review.py: 43 items with shots from special_shots and rec_dd at 256x240; marks and notes stay in the viewer's browser and "Copy all" pastes them into chat; raising an item's rev flags it for another look; --put publishes without a version bump; noindex).
Shouldn't the zoo have its own floor? It doesn't have to stay B10. Split them up so they look good.
3.85: one nest per floor in the Test Maze: B6 zoo, B7 beehive, B8 graveyard (now lit). Nest floors have only up stairs, the nest and down stairs, with no shop, so the 23-monster limit goes to the nest. Bestiary is now B9–B11 (shops: scroll, armor, food) and the amulet maze is B12. The floor count comes from the maps in ddmap.py (DD_NF). Design doc section 11 records the revision.
Show the room guide first thing when you enter. Not for shops. No brackets; show it as a normal message.
3.86: the room guide is now called before monsters move (start of pulse.s sn_new) instead of on the shop step after them, so it is the first message of the move that enters the room. The text drops the [brackets] and reads as a normal sentence (e.g. "The armory. Every kind of weapon.", "A zoo! Sleeping monsters and gold."), in Japanese too. Shops in the Test Maze no longer show it (ddmap.py writes 0 for shop rooms).
Keep the zoo groups awake as in the original. Make the nest rooms tall to reduce flicker (main dungeon generation later).
3.87: the Test Maze nests on B6–B8 are now tall 3×8 rooms. The console draws at most 8 sprites per line, which is 4 monsters; with 3 across, even the player fits in the same row. The row by the door stays empty as in the original, leaving 21 squares. Group companions stay awake as in the original.
Once the spec is fixed, an autopilot that clears every floor without skipping, and a video of it (strong enough starting stats to handle monsters, heals by itself; the seed is fixed).
No game change. tools/rec_dd.js: title → Start Exploring → Test Maze → visits every room on B1–B11 (one square inside the door; shops, swamp and bestiary from the doorway) and goes down; on B12 it wakes and fights the Wizard, chases the Wizard who vanished with the Amulet on the way back up, takes it back, and escapes from B1. Start: Lv14, 200 HP, St 18/100, poison/fire/cold resistance, regeneration. Nests are fought one at a time from the corridor outside the door. It never presses A on stairs while resting. Aids used: food 3, stoning 0, full heal 1, level restore 5, teleport 1 (into the sealed room on B12). Video 19.2 min (build/20261104 HACK 開発迷宮 自動踏破.mp4, plus a 4x version).
When climbing from B1 to the surface, show the stair-climbing animation, then the result screen. On the stairs, show only the top 1 and yourself in the ranking, make the window smaller and draw the stairs a little higher.
3.88 (escaping: after "Yes", the black stair screen shows the hero climbing up with a "Surface" card, then the screen is rebuilt with the map kept dark (only the top UI and windows), "You climb out to the surface! (A) Next", then the result. Stair ranking: the window is now rows 4-11 with only the top 1 and you (you and No. 2 if you are top); the stairs are drawn 2 rows higher.)
I want the result screen, and also samples of picking up, equipping and buying in a shop. Should only the pit trap catch you?
No game change. tools/rec_dd.js: on B1 picks up a weapon and wields it, then switches back to the two-handed sword, picks up and wears a shield, picks up a ring in the general store and pays with (A) (900 gold). On B4 steps into the visible pit on purpose and climbs out. The escape now shows the stair climb, then (A), then the result screen. When the Wizard cannot be reached, teleports next to it (aid). Aids: food 3, full heal 1, teleport 2, level restore 5. Recording 14.9 min (build/20261104 HACK Test Maze autopilot 2.mp4, plus a 4x version).
Fix the result window position (right right after it appears, then centre) and record again.
3.89 (the window was centred from the start; that was a misreading of a tiled image. Fixed a flicker found instead: after escaping, the rebuilt screen no longer draws the map (fastui.s el_body: dlevel 0). While the result window was painted row by row, the map under it showed for about 2 frames in the window colours (red).)
Put ripples behind the result screen too, like on the stairs.
3.90 (after escaping, the rebuilt screen lays ripple band tiles instead of the map (fastui.s el_rip; the camera is parked at the top-left edge and the dog etc. are removed), and only the ripple colour (palette 0 colour 1) is lit. From the "You climb out" wait through the result window, a ripple starts every 48 frames (ripple.s rip_esc: mon2.s you_died, result.s rs_menu). The hero silhouette is hidden. The result window text never borrows the band tiles $80-$95 (mwin.s mw_prep).)
Tidy up overall and check consistency.
3.69 (page: the load meter (5 taps on the version text), unreachable since the strip was removed, moved to DEV > dev tools > load meter. Removed unused lamp/circle CSS and verEl, updated old comments (SOUND, diagonal layout). Docs: ranking spec (debug contents, DEV decisions), FC design (REV_USED -> DB_REW), window docs and capture scripts updated for the removed revive. 21 reports from 10/13-10/17 were listed with short ids (only mv8uk5kc was missing; added at the top)).
Places that should be cleaned up, like control hiccups and sound interruptions.
3.70. Controls: 1) A/START/SELECT pressed while walking are buffered and applied when stopped (A before stairs was lost; fastui.s run_tick, PAD_BUF $7EB2). 2) The double-tap run forgets the earlier press after 12 frames (an 8-bit subtraction wrapped every 256 frames and caused spontaneous running); window 18 -> 12. 3) The direction prompt buffers presses during its 5-frame wait (short taps were lost: zap.s). 4) Web: release everything on window switch, going to background, report, DEV open/close (releaseAll; you kept walking with a button held). 5) Web: count presses per button and release only when all are released (a fast second press was released midway). 6) B on an empty name buzzes (mashing closed the window and restored the old name). Sound: 1-2) sound effect priority and a one-slot queue (within 5 frames of starting, or lower priority, waits and plays after; enemy sounds follow your attack sound; sound.s sfx_dec (bank 12), sfx_start). 3) The same sound isn't retriggered within 6 frames (held buzzing). 4) Web x2/x4 don't queue audio and drop old audio on speed change (muddiness, 0.3 s gap after restoring). Also fixed the mute-before-stop order. Verified: special_shots sfxq (attack -> monster sound follows).
Make lasers (wand rays) look nice like the arrows. Roughly the front 2/3 solid, back 1/3 dashed.
3.71 (fire, cold, sleep, death and lightning rays leave a trail: head + one behind solid, two behind dashed (right half of the 8x16 cleared; vertical can't split top/bottom so every other frame). Horizontal limited to 5 sprites per line. Thrown objects and digging have no trail. zap.s ray_tr_on / ray_push / ray_trail (bank 12); trail positions in internal RAM $07E9-$07EF (former BGM variables). Verified: special_shots rayt).
Two segments are enough for rays. The black outline makes them look cheap -> remove it.
3.72 (rays are 2 cells, head + one behind (dashed trail dropped). Color 1 (black outline) removed from the 3 ray images (vertical, horizontal, diagonal): tools/gfx.py _no_rim).
Remove the laser's corners.
3.73 (the horizontal ray image's four corners trimmed to round it: tools/gfx.py _no_corner. Vertical already had rounded ends; diagonal connects to its neighbors).
A fixed dungeon with everything (shops, monsters, traps, items) selectable as "Debug Maze". The Japanese title is "探索開始" (Start exploring). A theme per room, shown in the bottom UI on entry. 3 floors is just an example; it can be deeper (a theme park). First design it and write a document full of art and screenshots.
No fix (docs/20261103 HACK デバッグ迷宮 設計書.md: a 10-floor theme park, how to enter, floor plans, theme announcements, limits, how it's built, testing, schedule, decisions needed. Art via tools/dd_doc_img.py -> docs/img/20261103_dd, screenshots via special_shots dd and others).
Change the seed icon to down stairs, horizontally aligned with the characters / rename Seed to "Dungeon": Random, Fixed: Maze #1-#3, Fixed: Debug (in docs and ROM).
3.74 (top row of class selection becomes "Dungeon". Its head is a down-stairs sprite (same columns 3-4 as the class art, text at column 6 like class names: dd.s cl_stair, bank 31). Kinds: Random / Fixed: Maze #1-#3 / Fixed: Debug / (web) This week. Fixed #4, #5 and entry can't be chosen (battery values 5 and 6 become Random). Fixed: Debug is currently the old fixed #4 maze, always a debug record (DB_GODU bit 2), result shown as "Debug". Result and dungeon screens' head icon changed from seed to down stairs. "Seed" renamed "Dungeon" in the menu, headings and windows. Ranking page: this week's dungeon, fixed Maze #1-#3. Docs: ranking spec, Debug Maze design doc. Moving the name into this list is next).
A dedicated down-stairs icon without the floor pattern / seed number entry not needed / default Random; Start offers the existing save's dungeon.
3.75 (the dungeon row's down stairs get a dedicated image (white top step and 4 gray steps: tools/gfx.py STAIR_ICON). Class selection defaults to Random, or the save's dungeon if a save exists (SD_GM in battery RAM: seedsel.s sd_row0). Entry was already removed in V3.74. Verified: start on Maze #3, power cycle -> Start shows Fixed: Maze #3; no save gives Random).
Dungeon names: Random, Maze #1, Maze #2, Maze #3, Test Maze (Japanese 開発迷宮). In the ROM and the docs.
3.76 (kind names Maze #n and Test Maze (short English labels Maze #n and Test to fit the window). Result screen shows "Test Maze". The English window's value column now matches Japanese (Maze #3's number had hit the frame). Ranking page: Maze #1-#3, Test Maze. Docs: ranking spec; design doc renamed "20261103 HACK 開発迷宮 設計書.md" with its content updated).
It appears on the same screen as the top HUD's floor icon, so change it: use the descending-floor design without the floor pattern / also drop the SEED display while we're at it. Keep it simple.
3.77 (the dungeon row icon is the in-maze down stairs with the floor dots removed (frame and steps: tools/gfx.py _stair_icon). Seed numbers no longer shown: the dungeon window's "Number" row, the number in the right-hand description, result and dungeon screens show only the kind name (Maze #3, Test Maze, Random, This week), and the ranking page drops the seed from the week and maze descriptions).
Put the development history and the Test Maze spec on base-08 as a web page readable on PC and phone. Plus an encyclopedia of monsters, traps and armor. A long, detailed guide.
No fix (game). Development guide https://base-08.web.app/app_nes_hack/guide.html (tools/guide.py -> dist/guide.html, built and copied by deploy.py each time): overview (versions, days, counts), development log (log.md by day), Test Maze (design doc), 65 monsters (in-game art, stats, traits, descriptions), 7 traps, 148 items (overview and descriptions per category: tools/guide_text.json).
Dev guide: clearer tabs, an English version and the 125 Inc. logo. Keep updating this page regularly.
No game change. The guide now uses tabs (one section at a time, gold = current tab, swipe sideways on phones), a full-width header, and the logo in the header and footer. Added guide_en.html with translated encyclopedia texts, all 518 log entries and the Test Maze design doc. Switch languages at the top right.
Make the English guide use English screenshots too, and remove the links to the game page and the ranking.
No game change. Retook the Test Maze screenshots on the English game screens (docs/img/20261103_dd_en) and made the floor plans, legend, overview and room-message mock English (tools/dd_doc_img.py en). Removed the Play and Ranking buttons from the guide.
Drop the © from 125 Inc.; is there a readable pixel font for the web? Use the real title-screen logo; use character sprites as dividers; add a summary of the last 3 days with links at the top; add a tab with a more technical write-up of building for the 8-bit console; avoid the console's trademarked name.
3.78. Guide: body text now uses the game's own k8x12 font (converted from BDF to WOFF with tools/bdf2woff.py, shown at 24px = 2x), the real title-screen logo, hero/dog/monster sprite dividers, a last-3-days summary linking straight to each log entry, a new '8-bit console dev' tab (technical article in Japanese and English), and the footer reads '125 Inc.'. The trademarked console name was replaced with '8-bit console' in the guide, the play page's share text and image, and LICENSE-HACK.txt.
Is that font Noto? The α is how we will call the release, so drop it from the displayed name and the title.
3.79. The title screen now reads '8BIT EDITION' (loc t_edition1). Removed the α from the play page title and share text, the ranking page and the guide (title logo screenshot retaken). The ranking page also stops using the trademarked console name. Small text in the guide uses BIZ UDPGothic (not Noto); headings and body use k8x12.
Use BIZ UDPGothic for every font on the web page. Readability comes first.
No game change. The whole guide now uses BIZ UDPGothic (headings, tabs, body; k8x12 and DotGothic16 removed), at normal sizes (body 16px, headings 24/19px).
Stop saying 'Modern Arrange (2020s)': call it 'Modern Arrange' in the game and 'Modern Arrange (2020s)' on the web (logs, pages and future documents too). Add pages: How to play, Game rules (original vs Modern Arrange) and a History of Hack (Rogue, Hack, NetHack and how they relate). Show the game links as 'Coming soon'. Tidy up the whole page.
3.80. In-game help: the table of contents and two pages now say 'Modern Arrange', and the obsolete 'get back up once on death' line was removed. Guide: tabs reordered for players (Overview, How to play, Game rules, Monsters, Traps, Items, History of Hack, Dev log, 8-bit console dev, Test Maze), three new pages in Japanese and English, and a 'Where to play: Web version / itch.io / Ranking — Coming soon' box. 'Modern Arrange (2020s)' wording in the guide and docs became Modern Arrange (2020s).
Fix the Amulet depth in the in-game help to match the code. On base-08, make index.html the guide page and open the game at hack.html (the public release will use a store URL such as itch.io; base-08 is for the guide and testing).
3.81: the help page "Modern 1/2" now says the Amulet is on the first maze from floor 30 (original) / floor 18 (Modern Arrange). base-08: /app_nes_hack/ shows the dev guide (guide.html kept for old links); the game is at /app_nes_hack/hack.html (version check and og:url follow). The ranking page "Play" links go to hack.html and the © was dropped. The guide gained OGP tags.
When someone opens the entry page, show Japanese if the browser is set to Japanese and English otherwise.
No game change. The guide entry page (Japanese) moves to the English version (guide_en.html) when the browser language is not Japanese. Pressing the language switch at the top remembers the choice for next time.
Make the OGP image work in a square preview too, keep the text simple, and draw the controller in the current 8-bit console-style look.
No game change. The share image (ogp.png) was redrawn like the page: light gray frame, black face, a white-outlined D-pad on the left, SELECT/START and round red B/A on the right. The title logo and a play screen sit in the middle, so a square crop still shows both. The only text is the logo and 125 Inc.
Use an English screenshot in the OGP image, put 125 Inc. in as the logo image, and add the original source copyright.
No game change. The share image (ogp.png) now shows the play screen in English, the 125 Inc. logo image at bottom right, and the original credit "Based on Hack 1.0.3 ©1985 CWI ©1982 J.Fenlason" at bottom left.
Add GA4 (measurement ID G-JK85NBFDNN).
No game change. GA4 now runs on the base-08 guide (Japanese and English), the game page (hack.html) and the ranking page; deploy.py inserts the ID from web/ga_id.txt, and itch.io and local copies get nothing. The hop from the entry page to the English version is not counted, and a device that once opens a page with ?nostat is never counted (for the developer). The guide and ranking footers say the site uses Google Analytics (cookies) and link to Google's explanation.
Send in-game events (started, got the Amulet, cleared) to GA4.
3.82: on base-08 pages with GA, the web version reads the ROM RAM once a second and sends game_start (class, dungeon), amulet_get (floor, moves) and game_end (died / escaped / amulet, depth, score, moves, class, dungeon, debug). The end uses the same record as the ranking, one event per run; records or an Amulet already present when the page opens are not sent.
Build the Test Maze contents (decided: "Start Exploring", 10 floors ending at B10, 3000 gold, bestiary monsters asleep). Show the room theme in special rooms in normal games too.
3.83: Test Maze floors B1–B9 are now fixed layouts: entrance plaza (info desk, armory, armor, tools and food, training room), potions and scrolls (dark room, secret door), wands and rings (ray hall), traps (7 visible, 6 hidden, trap door), gadgets (boulders, swamp with a giant eel, Elbereth, hidden passage), nests (zoo, beehive, graveyard), and Bestiary 1–3 with 55 sleeping monsters. One shop of each of the 8 kinds on B1–B8. B10 is the Amulet labyrinth with the Wizard and hell hounds. Maps and contents come from tools/ddmap.py (gen/dd.inc), built by dd.s dd_load in bank 31. Entering a room shows its [name] and a hint in the message window (dd_room); normal mazes show it for zoos, beehives, graveyards and swamps. The Test Maze starts with 3000 gold. The title menu "NEW GAME" is now 探索開始 in Japanese.
Send more to GA4: which monsters you met, how deep you went, what you picked up — data that is fun to look at later.
3.84: more GA4 events on the web version: floor_reach (new deepest floor), level_up, monster_meet (a monster came next to you; once per kind), monster_kill (a monster vanished as your experience went up), item_get (a kind of item entered your pack for the first time), room_enter (shops, zoo, beehive, graveyard, swamp, vault). game_end now adds cause of death (monster, trap, starvation…), level, gold, kills, kinds met, kinds picked up and minutes played. Every event carries hk_debug (debug menu used). Names are in English (build.py fills them from the glossary and the object table).
Declare the iPhone home-screen icon and OGP properly (new base-08 rule).
No game change. The entry pages (guide JA/EN, tools/guide.py) now link apple-touch-icon and icon-512; the ranking page links icon-512. check_icons.py reports ok.
Go ahead (the remaining icon: icon-192).
No game change. icon-192.png (192×192) is now drawn by tools/make_ogp.py and declared in the head of the game, entry and ranking pages. check_icons.py shows no warnings (only the square-crop guide note).
Redo the OGP image / default language (8-bit console ROM English, web version follows the browser).
3.52 (titlefar.s: if the battery language isn't 19, use 23 (English). Page: only on first run with empty battery RAM, pick 19/23 from the browser language (base-08 and itch). Test tools (soak, test, special_shots, ogp_shot and 13 total) fixed to Japanese per their scripts (HACK_EN=1 for English). OGP: title shot taken at frame 420 when the logo has fully dropped, with logo and EDITION cut out by color (orange, yellow) (the title background had become a gray mesh and broke it); text "The 1985 roguelike / Hack 1.0.3's dungeon on the 8-bit console / ranking and weekly seed").
Online ranking inside the game (to-decide item 7: ship it).
3.53 (rank.s: entering the title ranking writes $5F to RK_NQ $741C -> if the page answers $A9 it's the web version (RK_WP). Arrows cycle tables: this cart, online weekly seed, Any, #1-#5 weekly. For online tables the ROM writes $80|table and the page (play_src.html) writes LootLocker's top 10 as 16-byte records into RK_NB $7300 and sets $40|table (only when ingame is 0 and still loading; in-game during the visibility wait). Loading / no connection / none yet texts; hints "<>Online (B)Back (A)Details" and "<>Switch (B)Back". 8-bit console hardware unchanged. Also fixed the no-record text erasing the right frame (column 31). special_shots.js rknet (stand-in for the page), ?rkmock=1 checked the browser tables).
Set up LootLocker (the Any board) / check last week's results.
3.54 (created hack_fc_free on LootLocker (ID 37702, HACK Any Seed, generic, descending, Game API writes, overwrite, metadata, no reset). After the 11/02 9:00 reset, read hack_fc_s1_week's archive (key 108199/37684/2026/november/json/..., last_modified) via /archive/read (the one TEST entry). The ranking page's "last week" sorts by newest last_modified).
Live end-to-end test (web banner, 8-bit console QR).
No fix (two entries from player A204005C "08" landed in hack_fc_free: banner 2217 pts (p web) and QR 52 pts (p fc, q holding the QR content), slots _1 and _2. Random seed, so other boards are empty. The web title "Ranking" > > "Online Any" showed the 2 live entries. Spec updated to V3.54 (7 done, item 7 of 8 decided)).
Spec to-decide items 4-6.
No fix (4: debug stays one board; 5: alpha anti-cheat stays light (remove obvious ones in the admin panel); 6: real hardware uses the week it was shot. All of spec section 8 is now decided).
Remove the online tables from the ROM (only the page's "Ranking") / on descending stairs, show this cart's top 5 and your current standing (new Modern Arrange (2020s) feature).
3.55 (rank.s: removed V3.53's online tables (RK_NB, RK_NQ) and the play_src.html exchange. rk_stair: on descending/falling (LV_KIND 1/3, during hb) lv_card shows a large window (rows 4-19, columns 2-29) with "B n F" and the top 5; if you're in the top 5 you're inserted, otherwise row 6 shows "-> rank name score deepest". Score uses sc_base like the top HUD trophy. Glyphs use a private backup RS_LO/HI ($6880+, 30 chars, tiles $3C+) without touching in-game gc. Stairs and falling floor moved down 3 rows (lvanim.s LA_DY, LA_DR, LA_FY, LA_F2). Flag handling unified in rk_mk. special_shots.js lvrk / lvrk2 checked outside, inside, no records, trap door and English. The original can't show the table mid-game).
When falling, show the hero falling from the top of the screen, passing behind the window, and landing below it.
3.56 (lvanim.s la_mask: when falling with the ranking window, black masks (tiles $76/$77 = color 1 black, behind BG) on OAM 0 and 8 inside the window (y 32-159); hero and dog on OAM 16/24. Opaque front sprites hide those behind, so nothing shows through mesh or text gaps. la_draw writes the mask tiles (in the map tile area during the sign). rank.s RS_MAX 30 -> 29 (text tiles $3C-$75)).
Don't show the table when falling (too much motion).
3.57 (rank.s rk_stair: only LV_KIND 1 (descending). Falling uses the old small sign. Removed V3.56's mask (lvanim.s la_mask) and set RS_MAX back to 30).
itch.io content and screenshots (move the rest forward too).
No fix (pushed V3.57 with tools/itch_push.py via butler (build #2277625). Added screenshots 3-6 for 6 total (3 room killing a bat, 4 menu, 5 QR, 6 stairs-descent table (English: special_shots.js lvrk with HACK_EN=1 -> build/itch_assets/shot6.png)). Page still Draft).
Polish the QR-linked ranking page overall / the web version submits automatically (just "Score uploaded").
3.58 (web/rank_src.html rebuilt: the game's blue window (white lines, navy mesh), HACK and > Play at top, received record shows name and big score, item boxes, destination tags -> after submitting the tags turn into ranks and it jumps to and highlights your row; tables for weekly seed / Any / fixed seeds (#1-#5) + period, debug via a bottom button, gold/silver/bronze for 1-3, FC/WEB/itch, weekly seed shows week n, seed and time left, "How to get ranked", ?mock=1&demo=1 for a demo. play_src.html: dropped the banner and "Submit"; sends as soon as a record exists and shows "Score uploaded" at top for 4 s (on failure retries twice more every 30 s)).
Update itch.io to 3.58 as well.
No fix (butler push via tools/itch_push.py. Page still Draft).
Add the new features to the itch.io English description.
No fix (FEATURES ranking changed to "your score is uploaded automatically when you die or escape", added "Every time you go down the stairs, a window shows your cart's top 5 and where you stand right now". Saved as Draft).
Launch day plan.
No fix (docs/20261102 HACK 公開日 段取り.md: up to the week before, the day before (delete test records, push the final build to base-08 and itch), launch day (the owner sets itch to Public), the week after launch, rollback).
Keep the test records in the ranking (on purpose, as a memento).
No fix (item 6 of the launch day plan changed to "don't delete").
Remove the revive feature.
3.59 (dropped reviving once at full HP on the up stairs after death (request of 2026-10-17, score halved): rv_try in mon2.s you_died, fastui.s rv_try / REV_USED ($7EB2 now free), the halving in score.s, text.csv m_revive. As in the original, death ends the game).
Invincibility, gold and XP stay in-game (alpha); rewind and scene loading only on the web version. The page's buttons are cluttered -> tidy up / merge Zoom and Fullscreen into one button with icons / the Seed menu item doesn't need an icon.
3.60 (controller buttons reduced to three icons: sound, screen, DEV (sound on/off; screen cycles Normal -> Zoom -> Fullscreen -> Normal, or Zoom -> Normal on devices without fullscreen; the strip inside Zoom uses the same single button). Diagonal input moved into DEV. DEV: rewind (turn, floor) / load scene > (floor, map, item, monster, trap, hero state) / dev tools > (log, emulator) / diagonal, ranking, report, name, ROM DL. Invincibility, gold +1000 and Lv +1 removed from the page (they're under the in-game menu's "Debug"). Loading a scene or reading a state sets DB_GODU bit 1, making that run a debug record ("Scene" in the debug window record; ranking goes to the debug table). Previously the page's invincibility/scenes left no record and could reach normal tables. Removed the seed icon before "Seed" in the main menu).
Put the keys inside the buttons to simplify (9 SELECT, 0 START, O B, P A), and make the 8-bit console button names bold.
3.61 (key shown thin at the top inside each button, name in bold (900). Removed the key labels outside and tightened button row spacing (portrait 22, landscape 46)).
SELECT/START are rounded rectangles, huh. Use 8-bit console colors / rounded and slightly rectangular.
3.62 (controller in 8-bit console colors: light gray frame, black face, black D-pad (gray edge), red A B (gray ring), gray SELECT START; sound/screen/DEV below also gray. Buttons are slightly wide rounded rectangles (A B 112x96, SELECT START 100x58; landscape 140x120, 126x72)).
The red buttons are round!
3.63 (A B back to circles (104, landscape 130). Only SELECT START are slightly wide rounded rectangles).
Outline the D-pad in white / thick white border around the screen (including the VER row) like a real 8-bit console photo / portrait is crowded -> adjust sizes / remove the circles right of VER / widen the pad.
3.64 (2px white edge on the D-pad. Thick light gray border around the TV (screen and VER row) (portrait 8, landscape 6). Portrait: D-pad 86, A B 92, SELECT START 88x50, row gap 44. Removed the lamp and 2 circles right of VER. Gray frame thinner (16 -> 8) and the face's side margins tightened to widen the black face. Key label color matches the button text).
VER and the rest (the strip under the TV) aren't needed. Keep it simple.
3.65 (the strip (VER, copyright, glyph credit) is no longer shown. The version appears on the game's title screen. The meter shown by pressing the strip 5 times is no longer reachable).
Add gaps between the frame, screen and buttons even if the buttons shrink a bit (landscape).
3.66 (landscape: 2.4% of width (min 16) between frame and buttons and between buttons and TV; 3.5% of height (min 10) above and below the TV. D-pad, buttons and TV get a bit smaller).
Move the bottom 3 buttons to the top row of the controller frame.
3.67 (portrait: sound, screen, DEV on the face's top row. D-pad and buttons centered in the remaining space below. Landscape stays under the TV).
Also show stat changes in color for rings of protection.
3.68 (pointing at a ring of protection in the inventory shows the top HUD shield as the value after putting it on (better cyan, worse red). For a worn ring, pointing at "Remove" shows the value after removal. Only when both kind and +n are known (otherwise hidden so nothing is revealed). With both hands full, includes the ring that would be swapped (the non-cursed one). invtop.s it_ring (bank 13 full, so bank 12). Verified: special_shots ringpv (AC 7 -> +2 gives 5 in cyan, an unknown +3 isn't shown)).
Effects of thrown potions (unimplemented in the special mechanics list).
3.33 (throw.s bank 30: pot_hit_mon (on hit, 1/5 chance -1 HP, 1/3 chance to take effect: healing types fully heal "looks completely healed", sickness halves, confusion/booze confuse, invisibility, speed; if adjacent, 4/5 chance you smell the vapors), pot_hit_you (thrown at the ceiling: hits your head for rnd(2), death cause "thrown potion"), pot_breathe (Str +1, HP +1, sickness -5, dizziness, can't see your right hand, paralysis rnd(5), speed, darkness). Also inhaled when dropped underfoot. 8 message lines added. Blindness has no effect since monsters lack a blind state).
Effects of the conflict, warning and protection-from-shape-changers rings; chameleon re-disguising.
3.33 (ring.s bank 30: conflict = visible monsters attack adjacent monsters 3/4 of the time (original fightm, mon2.s mvm_one). Warning = if the highest level of unseen awake monsters (distance^2 < 100) minus the hero's level is >= 0, "Your right ring glows black!" etc. in 6 colors (original warnings[], every 5 turns or when it rises; the original tracks only "approaching" monsters, but without previous positions awake monsters are used). Protection from shape changers = shape-changers revert to chameleons when put on (rescham). Chameleons re-disguise 1/6 of the time (start of dochug, using zap.s newcham)).
Shopkeepers follow you across floors.
On checking, it was already in follow.s (an angry adjacent shopkeeper follows to the next floor, bringing their gold). The "unimplemented" entry in the special mechanics list was outdated.
Thrown objects other than arrows shouldn't use the ray graphic either.
3.33 (14 floor-object images made into sprites (black/yellow/white + black outline; gfx.py fly_icon); 20 flying-object images (1280 bytes) loaded from bank 30 into tile 14. Rocks and bullets use the rock image. ray_dir also moved to bank 30 (FAR6 space)).
During alpha, the title is HACK 8BIT EDITION.
3.33 (loc t_edition1 -> 8BIT EDITION; also the web title and og:title).
Notify me when a report comes in.
tools/gas/report_notify.gs (GAS: checks RTDB hack_bug every 5 minutes and emails new reports with screenshots. Run setup once).
Design the ranking (8-bit console side): name, result screen menu, top 10 and recent 10, QR.
docs/20261101 HACK ランキング FC設計.md, mockups docs/mock/name_entry.png (hint START done, B delete, A OK; description row removed) and rank_flow.png (tools/name_mock.py, rank_mock.py).
Raise the HACK logo on the title to relieve the cramped bottom.
Planned for 3.34 (logo rows 4-5, version row 6, attributes 8-11 to the window palette. Widened the title window in mockup rank_flow.png to rows 9-26).
Default name is HACK-ER.
Design memo and mockup rank_flow.png changed to HACK-ER.
Overall ranking spec (periods, 5 entries per person, all-time scores, debug leaderboard, seed selection).
Added to the design memo (what LootLocker can do, list of 13 leaderboards, seed screen, localStorage can't be shared).
Create HACK's rankings on LootLocker.
Game HACK (id 108199), Guest Login ON, 13 leaderboards (IDs 37679-37691; the 6 weekly ones reset Monday 9:00 JST). Listed in design memo section F.
Name (for rankings): add "Name" and name entry to the title menu.
3.34 (name.s (bank 30): one name in battery RAM $7F80 (8 ASCII chars, default HACK-ER). Freed space by shrinking the guard's temporary path FC_* from 102 to 64 (vault.s; just stops adding when full). Title window raised 2 rows (rows 10+): Start, Continue, Name (current name on the right), Manual, Language; the language sub-window also raised 2 rows. Name entry window: 4 QWERTY rows + Delete and Done; selected key gets a white square (sprite OAM 224/228, tile 14); Delete/Done use the glove; B deletes one char (cancels if empty); START confirms (empty restores the old name); the march is hidden while open. Full runs captured in Japanese and English (special_shots.js name / lang)).
Name entry: write START as ST / instead of a square, put a pointer near the selected character.
3.35 (hint "ST Done (B)Delete (A)OK" (English ST:END). Keys are only 8 dots apart, so the 14-dot glove overlaps neighbors -> a small 8x8 hand (thumb up, pointing index finger) drawn in tile 14, placed 8 dots before the character (OAM 224, palette 2). Delete/Done keep the glove).
Name entry: use the usual glove for the key pointer too.
3.36 (dropped the small hand; keys use the same glove as Delete/Done (hd_put2, right end 2 dots before the character). It overlaps the key to the left).
Name entry: enter the name where the heading is / shorten the window vertically (not touching the bricks below).
3.37 (dropped the heading "Please enter a name"; that row (NW_R0 + 1) holds the name field. Keys from row 12, Delete/Done row 20, hint row 23, bottom frame row 25).
Opening and closing the menu makes the screen look like it shifts vertically.
3.37 (while P3_DIM, add 1 to the map y to cancel it out (main.s NMI). special_shots.js wins confirms the map and parent window line up exactly before opening and with a sub-window. jit: no overall shift on the title window and inventory).
In scenes with music, heavy load stops the music or garbles sound. Use a proper interrupt.
3.38 (nmi_mus at the end of NMI: switch $8000 to bank 10, mus_tick, then back to main's bank (r6_cur). IRQ disabled while switching; cli during music lets the scanline-21 IRQ through; overlapping NMIs are skipped. NMI stays enabled in PPUCTRL even while the screen is off; while ppuc bit 7 is 0 it's a "quiet NMI" doing only music and sound effects (no PPU access). screen_on sets PPUCTRL first. For fixed-bank space, screen_off, screen_on and part of startup moved to bank 30. special_shots.js bgm: 0 frames of music stall on title, name, manual and class selection (previously 12 in the manual). 4 soak runs (30000 frames) passed).
Moving the cursor repeatedly on class selection turns the left and right ends of top HUD line 2 black.
3.39 (s2_edge uses s2_mesh like the text background: during ui_top it's the window mesh BLANKW. special_shots.js clnt: class selection edges are $3C, in-game stays $FC).
Tidy up the debug UI (public alpha: rewind up to 15 turns, invincibility, gold, XP; keep a record of their use).
3.40 ("Debug" in the in-game menu -> sub-window debugwin.s (bank 30): rewind (1-15 turns with arrows; web version only, gray "Web only" on 8-bit console hardware), invincibility ON/OFF, gold +1000, level +1, record "Rew Gold Lv God". The record lives in battery RAM $7F95+ (DB_REW, DB_GODU, DB_GOLD, DB_LV) and goes into the save (SAVE_VER 4: older saves unreadable), cleared by Start. Rewind: the page writes the number of rewindable turns (same game, up to 15) to DB_RWA each frame; the ROM puts the requested count in DB_RWQ -> the page loads that state and writes the record back (exact turn count even after running several turns). Top HUD changes made while the window was open are redrawn after closing (dw_after). Mockup docs/mock/dbg_flow.png, special_shots.js dbgw).
At the top of class selection add "Seed": random, fixed #1-#5, seed entry.
3.41 (seedsel.s (STR): top row of the list (class and dog rows moved down 2: cl_row, CL_DROW 22). Pointing at it shows [Seed] kind, number and "Fixed #1-#5 can be ranked" on the right; A opens a sub-window like the dog's: arrows cycle 7 kinds; for entry, A on the number row -> left/right digit, up/down value. Kind stored in battery RAM $7FA0. Start sets gseed (fixed 1185, 2026, 1119, 0853, 7777); the kind is saved as SD_GM (SAVE_VER 5). List row text drawn in its own tiles (16; using the backup overwrote the description text). Seed glyph added. special_shots.js seed / cldesc (descriptions fully shown in both languages)).
Result screen: name, seed, To title / Change name.
3.42 (result.s (bank 30): blank row under the cushion heading shows "name seed#n" (random/entered show the 4-digit seed). Runs that used debug show the rank row as "Debug Rew3 Gold0 Lv0" (can't be ranked). Bottom row "To title Change name" chosen with arrows and the glove. Change name -> flag in battery RAM NM_GO -> name entry opens right away at the title (after running the march once to set pointer colors). special_shots.js res2 (DBG=1 with debug)).
Title "Ranking": top 10, single-entry result and a QR code (to submit on base-08).
3.43 (rank.s: 10 records x 16 bytes in battery RAM $7F20+ (guard's temporary path 64 -> 32; name/debug/seed variables moved to $7FC0+; recent 10 didn't fit, dropped). At game end inserted by score (replaces the old top 5; result screen shows "#n in the Best 10"). Title window adds Ranking (Start, Continue, Name, Ranking, Manual, Language) -> full-screen list (rank, name, score, deepest B, escape/amulet/D marks) -> A shows one entry (class, ending, deepest, turns, rewinds) with a 64-char QR (URL + name + hex of ID, record, version, checksum; format in design memo G). QR generated on the 6502 (qr.s: bank 7, alphanumeric, Reed-Solomon 15, mask 0, tables via tools/qrtab.py); the on-screen QR read fine with zxing-cpp. Change name on the result screen also renames that run's record).
Name and seed in the item menu; pressing shows a QR (to share the seed).
3.44 ("Seed" in the in-game menu (Items, At feet, Holy word, Seed, Debug, Title) -> full screen "name seed#n / seed number" and a QR (HTTPS://BASE-08.WEB.APP/S/ + 4-digit seed: scanning opens the web version with the same seed); B returns to the menu (redrawing the field like the inventory). special_shots.js sq read the QR. Name changes only on the title and result screens (the title's name entry window can't be used in-game)).
Drop the seed QR / receive ranking QRs on the web.
3.45 (in-game "Seed" shows only name and seed (QR and /S/ dropped). Spaces in names in the ranking QR become * (no spaces in URLs; turned back on the web). web/rank_src.html -> rank.html in the room (deploy.py); base-08's firebase.json rewrites /H/** to it. Reads the QR content (design memo G), verifies the checksum, shows class, deepest, ending, turns and seed; fixed #1-#5 go to weekly and overall, rewind/debug runs to the debug board via "Submit to ranking" (LootLocker: player ID + slot 1-5, no duplicate records, overwrite an empty or lowest slot, skip if lower). Notes that random/entered seeds aren't ranked. Leaderboard tables below (#1-#5 x weekly/overall/debug, submitting player highlighted). ?mock=1 for local testing. No LootLocker Game API key yet, so live shows "Coming soon").
Add the LootLocker key and take ranking submission live.
3.46 (rank_src.html gets API domain 955ddr3q.api.lootlocker.com and the Game API key. Guest session, leaderboard reads and submission verified live (test entry TEST 1520 in hack_fc_s1_week, cleared by the 11/02 9:00 reset). The Server API key is not put in the page).
Submit to the ranking directly from the web version.
3.47 (rank.s rk_web: at the end of a run, this run's 16-byte record is built at RK_WEB $6850 (free mklev RSLY space; survives returning to title) with flag $A7 and a checksum (sum of record 16 + player ID 4 ^ $5A). Top 10 insertion copies from there. The web page (base-08 only) reads it every 0.5 s; for rankable runs (fixed #1-#5, debug) a banner "name score board [Submit] x" appears -> LootLocker (name as of sending, metadata p:"web"). A record shown once isn't shown again (hk.rkseen). LootLocker code consolidated in web/hackrank.js, shared with the QR page (embedded by build.py / deploy.py). special_shots.js rkweb checks the ROM record, ?rkmock=1 the banner and submission).
Allow showing the QR on death / return to the surface / submissions from itch.io too.
3.48 (result screen bottom row "To title QR Change name" (arrows cycle the 3). QR -> full screen with this run's entry (class, ending, deepest, turns) and QR (even if not in the top 10); A Change name, B To title. Single-entry display now reads from RK_CUR $6862 (free RSLY space), shared with the ranking (rk_det). The web submit banner also appears on itch.io (window.SC_ITCH added by tools/itch_push.py; --dry builds build/itch_web) with metadata p "itch". special_shots.js rsqr read the QR with zxing).
Weekly random seed / last week's and all-time tables (leftovers 1-2).
3.49 (seed kind 7 = "This week" (web only): on the class screen the ROM writes $5E to $684A -> the page writes a seed derived from the week number to $684B and sets $A8 (free mklev RSLY space, written only on request). Kind count is 8 when answered. Start sets gseed, SD_GM 7, record seed 7 -> board hack_fc_wk. The week changes Monday 9:00 JST, week 1 = 11/02; seed from hackrank.js weekSeed (same on any page). Runs spanning a week change don't get a banner (gseed differs from this week's seed). Ranking page gains "Weekly seed" (this week, last week, all-time) and "last week" for fixed #n: reads LootLocker archives (/archive/list, /archive/read); all-time ranks each player's best per week and adds participants - rank + 1 (no GAS, hack_fc_wk_all unused). Archive format to be checked after the 11/02 reset. special_shots.js wseed; ?mock=1 verified the point calc (A 4, C 3, B 2)).
Check the English QR screens / add a ranking entry point to the page (leftovers 7 and 10).
3.50 (page DEV menu gets "Ranking" -> opens base-08.web.app/H/ in a new tab. English: captured result screen (REST IN PEACE, Title QR Rename), QR screen (This run..., Scan (A)Rename (B)Title), title (RANKING), list (RANKING TOP 10), entry (Scan to submit), seed screen (SEED); QRs read with zxing. special_shots.js: EN1=1 switches to English at the end of a run; rsqr marks revive (Modern Arrange (2020s)) as used, then dies and goes to the title ranking).
Random and entered seeds go to an "Any" board (decisions 1-3; keep DEV).
3.51 (hackrank.js boardsFor: everything except debug also goes to hack_fc_free (fixed: weekly, overall, Any; weekly seed: weekly, Any; random/entered: Any). If one fails the rest are still sent (the Any board isn't created on LootLocker yet: "x / Coming soon" until then). Ranking page gets an "Any" tab. Spec section 8 turned into a table of decided / to-decide items).
itch.io page (English screenshots; for now only game screens).
Created "HACK 8BIT EDITION" on itch.io (https://ttsyszk1973.itch.io/hack-8bit-edition, Draft, HTML, in development, $0 or donation (suggested $2), 768x720 frame, fullscreen button, Role Playing, 10 tags, AI disclosure (art, sound, text, code), comments on, English description) and pushed with butler via tools/itch_push.py (TARGET ttsyszk1973/hack-8bit-edition:html5). The itch build starts zoomed (game image only, no switch banner, CLICK TO PLAY on top: .itch in play_src.html). English screenshots from special_shots.js itch (English from the start) and rsqr (EN1) -> build/itch_assets (630x500 cover is the title screen).
Document somewhat special mechanics (bows, chains, segmented monsters, etc.) and their implementation status with screenshots.
docs/20261031 HACK 特殊仕様一覧.pptx / pdf (15 slides: overview, bows and arrows, long worms, punishment ball and chain, engulfing, wand rays, shops, wishes, special-attack monsters 1 and 2, wizard and amulet, mechanics without screenshots, summary). Screenshots via tools/special_shots.js (per scene, debug commands), document via tools/make_special_doc.py.
Flying arrows should use dedicated art instead of the ray graphic.
3.32 (arrows, crossbow bolts and darts use arrow art (vertical, horizontal, diagonal, flipped for 8 directions; art/sprites.json AR_*). 6 flying-object images (3 ray, 3 arrow) live in bank 30 and are written into sprite tile 14 (formerly the horizontal ray) when used (zap.s fly_load, F30). Vertical rays also fixed. All 8 directions captured and verified (special_shots.js fly8)).
When the inventory cursor is on armor, use a sprite so the shield icon's color doesn't change too.
3.32 (attributes untouched; the cyan number is a sprite (OAM 224-232, SP0 color 3 $31) overlaid on the text. Glyphs without mesh go into sprite tiles 56, 58, $7E (stline.s ac_spr / ac_oam, bank 30). Worse (red) is still text color 1. Hidden on close (oam_hide). Shield stays white/red; screenshots confirm the numbers overlap exactly (special_shots.js invc)).
What maintenance is needed now? -> Expand the ROM, ROM file layout, document naming.
3.29 (PRG 512KB: tools/build.py PRG_BANKS = 64, header 32. Fixed banks are 62-63; 30-61 free. dist/roms keeps only the latest and one prior (deploy.py deletes; not committed to git). Documents in docs renamed "YYYYMMDD HACK content", and the generating tools use those names).
Walking and stairs are heavy. Any room to lighten them?
3.24 (far_go: no switch if already in the same bank. sc_dec: subtract powers of 10 (avg 2,028 cycles, 3,012 cases match). rn2: low bits for powers of 2, only the low 8 bits when the divisor >= 32 (51,200 cases match; 5 floors 41 -> 30 frames). Same walk: per-turn slowdown 10.73 -> 10.32, max 42 -> 32; stairs 278 -> 275 frames).
Speed up in-game.
3.25 (cansee: cs_corr only when the hero is in a seen corridor/door (flag cs_en set in mv_upd). sw_move: stuck_chk not called unless engulfed/held. 30 F3 typat calls -> jsr typat (typat is fixed). wm_move only for long worms. Trap-door rock (tr_rock) moved to bank 3 for CODE1 space. Per turn 398,469 -> 385,480 cycles, slowdown 7.71 -> 7.29 (8 monsters: 488,286 -> 480,043)).
Major work: compute the turn during the walk animation / batch the top HUD redraws.
3.26 (on walking turns monsters are deferred: mon2.s end_turn sets TURN_PH and movemon becomes per-monster mvm_one. The game loop's final wait_nmi becomes fastui.s turn_wait (bank 3, where monster tables live), processing one monster at a time in idle time before the next NMI; any left when the walk ends are done in run_tick (no input taken mid-turn). The lunge animation also advances art and walk counters. vis_rings skipped when the lit room is fully shown and the box is unchanged. Score not rewritten if unchanged (SC_CV). game_start value clearing moved to gs_reset (bank 15) for fixed space; dart trap moved to bank 3 for CODE1 space. Verified game state identical to V3.24 for the same inputs with 0, 4, 8 monsters (build/rp/same.js). One step 16.7 -> 12.7 frames (floor where the dog fights every turn), surrounded by 8 monsters 42.3 -> 34.3, and the pause between pressing and walking is gone).
The development history from the start until now, in the 125 format.
docs/HACK_開発記録.pptx / pdf up to V3.26 (32 slides: cover, 12-chapter contents, 5 slides of version progress, new chapters 9-11 (windows and menus, Modern Arrange (2020s) and log, visibility/sound/speed), summary). tools/make_report.py, report_charts.py (version progress and daily charts over the whole period, REPO and request counting, PRG banks from nes.cfg), report_shots.js (shots using the current title flow).
When a controller is connected, hide the on-screen controller and make the screen the largest possible integer scale: Normal / Zoom / Fullscreen buttons.
3.27 (web/play_src.html: Zoom = only the game image on black at the largest integer multiple in device pixels (from the original 256x240); Fullscreen = the same in browser fullscreen. Under Normal, "Zoom" and "Fullscreen" buttons; while zoomed, Normal/Zoom/Fullscreen at top-right (only on mouse move); F key for fullscreen; zoom persists next time).
Is this really a boulder? Does it vanish right away?
3.28 (obj.s: fix_objbit scans everything with o_scan regardless of flags. On the reported state, 3 pushes move it one cell each and it stays visible).
Scanline / CRT filter via a shortcut.
3.28 (web/play_src.html: C key cycles none -> scanlines -> CRT (RGB stripes, corners, slight bleed). Works in Normal, Zoom and Fullscreen; choice is remembered).
A regression? When an item changes AC or attack, show it in the top HUD's current AC number. If the color can't change, use a sprite.
3.30 (the top HUD shield number itself shows the value after wearing/removing: stline.s ac_show / ac_now / ac_mark. Worse = red text (color 1, $40 in text.s st_service); better = that cell's attribute to palette 0 for cyan (shield also cyan); same = white. Restored on close (invui.s inv_close_noturn). The row 3 preview was dropped. Weapon +n stays on row 3 since the top HUD has no such number).
How many turns does the floating eye freeze you? Too long.
3.16 (fight2.s: always 2-21 turns. Manual's Modern Arrange (2020s) page 2/2: "Frozen by the eye: max 200 turns -> 21 turns").
Floating eye: a middle ground, 1/2 chance for 2-21 turns.
3.17 (fight2.s: freezing happens 1/2 of the time. Manual's 2026 page 2/2 changed to "2/3, max 200 turns -> 1/2, max 21 turns").
Isn't this wall weird?
3.18 (gfx.py: meshed walls (front and top) use a checkerboard (half). Confirmed on the reported state that the gaps between upper/lower walls and doors are visible).
After descending into the dungeon, moving right keeps the hero facing forward. Is the facing on arrival not set?
Under investigation (asking for the steps when it happened).
Web emulator sound: pitch too low, choppy, high notes muddy (bookofsecrets 07 chapter 9).
3.19 (APUSynth reshaped like music_nes: square C/2(p+1), triangle C/(p+1), 8 steps per sample, $4003/$4007 restart the waveform. play_src.html: while sound plays, an 8ms setInterval keeps the queue at about 4 frames (muted, fast-forward and pause still use the frame clock), AQ.under. Period 253 -> square 440Hz, triangle 220Hz; 15 s at 60.13fps with 0 underruns).
If you inhale gas, shouldn't the status say Gas instead of OK?
3.20 (fastui.s st_stat gains 3: while multi remains with multi_why 2-4, MU_WHY $77D9 gives "Gas" (trap.s tr_gas), "Paralyzed" (fight2.s floating eye) or "Can't move" (others). Sad face, red text. MU_WHY cleared when you can move again).
Bring in the shared nes_common music driver as a fork (no DPCM; so the title music and jingles can be elaborate).
Not released yet (src/driver.s = fork of nes_common 1.00 (5fabaa8): variables in internal RAM $0681-$0746 (former identified-items table, MUSRAM); driver, songs and tables in bank 10 (MUS) via F10 mus_tick. Songs: tools/music.py -> music/title.txt (FamiStudio text format) -> tools/fms/fmsconv.py -> gen/fms. Sound effects take music_own $09 while playing (square 1, noise). Compared 7300 frames with FamiStudio's NSF: square 2 and triangle match; all 228 noise mismatches are frames borrowed by the footstep effect (tools/bgm_check.js). 2 soak runs OK, 10 s on the page with 0 underruns).
Hand-port only the lighter parts from nes_common 1.01.
Not released yet (src/driver.s: ch_silent skips period calc for silent channels; zero pitch/fine/slide skipped. Envelope-head copy (+80 B work RAM) didn't fit the 215 B free, so skipped. Average 2,860, max 5,350. Still matches FamiStudio's NSF).
Release it.
3.21 (music driver fork (nes_common 1.00 + the lighter parts of 1.01); title music from music/title.txt).
Integrate a new opening theme and 16 sound effects.
Not released yet (music/title.txt replaced with "March of the Hero Entering the Labyrinth" (hack_title2.txt). tools/fms/fmsconv.py vibrato table from nes_common 1.03. tools/music.py now only converts. Sound effects: music/sfx.json -> tools/sfx.py -> gen/sfx.inc (each frame = register flags + values); sound.s borrows only the channels each effect uses (footsteps noise only). HIT..HURT pitch offset SFX_DV kept. tools/sfx_check.js: all 16 match; tools/bgm_check.js: music matches (only mismatch is noise borrowed by footsteps). 2 soak runs OK, 0 slowdown on the title).
Release it.
3.22 (new opening theme and 16 sound effects).
Stop the music when descending stairs. So the dog and hero don't overlap at the start and end of the descent, the hero disappears when done and the dog enters while moving on its first step.
3.23 (music.s: stops when LV_KIND is set (LV_KIND cleared at title start against battery-RAM garbage). lvanim.s: the dog follows 2 steps (16 dots) behind at the same pace, appearing after the hero starts walking. The hero disappears at the bottom. The animation is extended 2 steps until the dog finishes (la_hop limit LA_CAP)).
When the pet does something (picks up a corpse, etc.), say "(name) picked up ...!".
3.28 (dog.s -> uiwin.s dg_mpick / dg_mdrop: if visible, "Pochi picks up ..." / "drops ..." (m_dog_pick, m_dog_drop). jsr+rts in status2.s changed to jmp for bank 13 space).
When entering a room, light the whole room from the entry point.
3.31 (reveal.s rv_setup: a newly lit room always reveals the entire room (stage 3). The wave still centers on the entry point. Removed the stage calculation and RV_D / RV_LX / RV_LY ($066C-$066E). Standing in the doorway, the whole room appears in about 30 frames).
Darkening when leaving a room goes the wrong way. It should darken toward the hero's back.
3.31 (mv_upd -> rw_walk: processed in rings from the hero (Chebyshev distance). Cells becoming invisible go from the far ring (far side toward your back), cells becoming visible from the near ring. Box and 3x3 split into two passes (becoming visible / invisible); mv_cell handles only the matching direction. To make room, score.s moved from bank 12 to 30 (F30, far30 in CODE1; banks 30-61 at $8000). Verified darkening starts from the far side from either a left or right door).
The un-darkening of previously visited rooms is also reversed.
3.31 (same as above: cells becoming visible go nearest to the hero first. Verified that re-entering from the right door spreads left from around the hero).
Did I just warp? Is this correct? No broken wall so am I stuck? Can't continue anymore?
Not a bug: a teleport trap sent you into a vault (a 2x2 enclosed by walls) (original hack.trap.c vault_tele). Search and wait and a guard comes; answer him, drop the gold and follow, and he digs a corridor out (verified from the reported state).
Base the dog's side view on a Shiba Inu picture. When selecting an item in the inventory, show the changed value on the existing bar (if color is impossible, mesh for the increase).
2.99 (own_art.py DOG_RIGHT: pricked ears, muzzle, curled tail, white body with gray belly (same art for dog0/dog1 on title and class screen). fastui.s pv_set / pv_fix / pv_dirty: pointing at food in the inventory adds the current-to-after-eating dots as a black/white checker on the top HUD's 3-cell hunger bar (after the bar in status2.s st2_service; st2_service runs in the inventory loop); the preview bar on the lower row was dropped).
Tighten the menu's left by 8 dots (window width unchanged) / gray parent pointer at the second level / preview on the HP bar when pointing at a healing potion / returning from class selection goes to the Start/Continue main menu / dog sub-menu: choose name and color at the third level / Shiba front and back too.
3.00 (mwin.s items at column 3, pointer inside the window. Gray glove (gfx.py tiles 52-55, fastui.s hd_dim). pvbar.s: previews on hunger and HP bars (known healing potions 114/124 dithered up to max heal). titlefar.s TT_AO: returning keeps the window open. petsel.s in 3 levels: right window "[Dog] / Name Pochi / Color White" -> name (pages of 5 with arrows) or color (white, brown); gray pointer on the parent level. Color in DG_NM bit 4; class screen dog art uses it. In-game brown sets sprite palette 1 to 27/37 (shared with the pointer: back to white while a menu is open). own_art.py DOG_DOWN / DOG_UP as Shiba (pricked ears, gray cheeks and mouth, curled tail from behind). docs/dog_1027).
Tighten the title menu's left of the pointer by 8 dots like the others.
3.01 (titlefar.s: items and language options at column 11, language heading at column 9, pointer at 11*8-10. Window width unchanged. docs/dog_1027/tt_after.png).
The pointer slides up from below when a window opens -> start at the target / fix pointer shape and dog front / title character colors wrong at first / class screen background bottom is red / colors just below the top HUD are off in the weapon sub-menu / dog: open a window like the item sub-window "Dog / Name Hachi / Color White / <>Select (B)(A)Back".
3.02 (1: fastui.s hd_go: if not shown last frame, appear immediately without sliding. 2: title2.s: write immediately on the frame the lineup changes, rewrite after palette reload (main.s load_pal_xy). 3: role.s: rows 28+ palette 0 too. 4: wlay.s vb_attr: palette 2 navy to gray (restored in fastui.s vb_restore); mmenu.s it_at1: icon palette 1 -> 0 while a window is open. 5: petsel.s: window with frame, heading and separator; up/down picks the row, arrows change name (cycling 16) and color (white, brown) in place; A/B close. Pointer has a thumb and connects to the cuff (gfx.py); dog front has outward ears and a round face (own_art.py). docs/dog_1027).
Dog heading: Japanese "お供" (companion), English "Dog" as in the original / arrows aligned to the window's left / default white (top of list), later the previous choice (class too) / give the dog every palette usable in-game / while a sub-window is up, gray out the parent and hide the bottom hint (like items).
3.03 (petsel.s: heading "お供" (English "Dog": original little dog), hint "<>Select" left and "(B)(A)Back" right; 5 colors white, brown, blue (hero), red (monster), yellow (ray) (DG_NM bits 4-6; sprite.s dg_gpal, lvanim.s). While open, BG palette 0 goes gray with attribute 1 only inside the window; class art uses a grayed sprite palette 3 and the dog uses palette 2 in its color; the bottom hint is meshed out and restored on close. role.s: previous class saved in battery RAM $67FD ($A0 flag; warrior by default). mmenu.s mm_shint: paged sub-windows also put "<>" left and the rest right-aligned (genocide, naming). docs/dog_1027).
Generalize (a generic 8-bit console UI: maintainability, efficiency, reusable for future roguelikes) / language too as a sub-window under the same rules.
3.04 (new uiwin.s: frame and separator uw_box, mesh uw_mesh, text uw_at, right-aligned uw_atr, hint uw_hint (left text like arrows + right-aligned), parent dimming uw_dim (gray mesh and text on BG palette 0, attribute 1 only inside the sub-window), VBUF wait uw_room. Dog window (petsel.s) moved onto it; language becomes a sub-window over the grayed title window (titlefar.s tt_lwin / tt_lclose: gray pointer on the parent "Language"; on close attributes restored and the title window redrawn; unused tiles $4C-$D7 added to the title glyph pool). docs/dog_1027/lang_sheet.png).
Continue generalizing (starting with the item action window).
3.05 (mmenu.s mm_box / mm_boxs (with separator) are thin entry points to uiwin.s uw_box: class selection, manual, floor sign and action window share the same frame code. Action window heading, actions and hint placed via uw_atm / uw_hint. Removed fastui.s vb_sep. uiwin.s hints split into uw_hint (right-aligned) and uw_hintl (with left text). docs/dog_1027/m_sheet.png, card_sheet.png).
Continue generalizing (game screen windows).
3.06 (vb_attr moved onto uiwin.s uw_dim: uw_dim gained "outside blocks keep saved attributes" (UW_SA) and "color 1 also gray" (UW_G1). Removed wlay.s va_read. The field's top window (mwin.s) needs scrolling and tile-table lending, so it stays separate but follows the same rules. Parts and rules documented in docs/ui_kit.md (for new projects too)).
The current Shiba side view is great. Narrow the face a little on front and back / pointer should show the back of the hand facing forward with fingers hidden (fewer dots): 6 reference pictures.
3.07 (own_art.py DOG_DOWN / DOG_UP faces 2 dots narrower (front 12, back 10). Pointer candidates redone as back-of-hand shapes: G index and thumb only, K rounded corners, H gray cuff, I gray shadow below, J thick fingers (tools/art_cand.py -> docs/art_cand/cursor.png). To be added after a choice is made).
Dog front/back: legs look jagged, so remove the black line between hind and front legs / back-view tail higher to match the side view / head one step narrower.
3.08 (own_art.py DOG_DOWN / DOG_UP: left and right legs merged into one thick leg (with white socks), back-view curled tail just below the neck (same height as the side view), front face also 10 dots. docs/art_cand/dog_final.png).
Got ideas from GPT too: is the dog good like this / use the middle pointer design (finishing via AI collaboration).
3.09 (converted GPT's art (docs/art_cand/gpt_ref.webp) to 16x16: own_art.py dog in 3 directions (front face 12, white underside, legs in body color with white socks; back with V ears and a white swirl tail; side with round curled tail and white chest/belly). GPT's side view was 18 dots wide, so the torso was shortened by 2. Pointer (gfx.py) is the middle design: back of hand, thumb, round fingertips, other fingers not drawn. Gray pointer same shape. docs/art_cand/dog_final.png, new_cursor_shots.png).
Back and front as in the art I provided / I want a pixel editor: view the pointer, item, monster and hero art on the web, edit on the spot, save to apply, with looping 4-direction animation previews.
3.10 (back and front made exactly as provided. All art sources consolidated into art/sprites.json (ROM verified byte-identical to before). tools/pixed.py + tools/pixed/index.html: open and draw from a list of the hero's 6 classes, companion, pointer, 63 monsters + 11 shapes, floor items, traps, corpses (pen, fill, eyedropper, mirror, move, flip, undo); preview with other palettes; frames that go into the ROM (raised leg, sway); animation previews (rotate 4 directions, walk, pointer slide and tap); saving rebuilds the ROM and play page to play right away on the right. Launch: py -3.14 tools/pixed.py -> http://localhost:5210/).
Are attack effects in the editor too?
Still 3.10 (ROM unchanged): moved 4 items into art/sprites.json (ROM byte-identical); editor gets "Effects (attack)": the kill mark drawn mirrored with a preview expanding 1/2 -> 3/4 -> full and blinking; ray preview extends one cell at a time from in front of the hero. The lunge motion isn't art (just a position offset), so excluded. On narrow screens the animation and game go below).
Merge the editor's center and right panes, simplify the UI overall, make the palette picker big / the game preview on the right isn't needed (edits can't be seen immediately anyway).
Tool only (ROM unchanged): 2 columns (list, workspace). Workspace has one tool row on top (direction, pen/fill/eyedropper, mirror, undo/redo, others (move, flip, copy, paste, grid), palette set picker), below it the editing canvas with a large palette beside it (74 swatches with numbers and names) and the animation preview. Game preview removed.
Messages stopped appearing?
3.11 (uiwin.s sram_wipe called from main.s reset: zeroes the battery RAM work areas ($7000-$717F, $7300-$75BF, $7735-$77FF, $7A00-$7BFF, $7D00-$7DFF). Log, status and save kept. Booted with the reported battery RAM and confirmed combat messages appear).
Modern Arrange (2020s) change: show the dog and monsters if they are in a corridor you've already walked.
3.11 (follow.s cs_corr called from mon2.s cansee: under Modern Arrange (2020s), when the hero is in a corridor or doorway, cells reachable by following only previously seen corridors/doors are visible. Tracing goes diagonal-ahead, sideways and straight in both directions (up to one corner). In the reported map a leprechaun past an L-bend became visible with "... is here!").
Allow opening the menu and returning to the title even when unable to move. Actions that can't be done appear gray in the menu and play a can't-do sound when pressed.
3.11 (lvcard.s hl_wait -> mmenu.s mm_hlst: START opens the menu even while paralyzed, fainted or sick. Everything except Return to title is gray (mwin.s: glyphs thinned to a checkerboard; true gray can't be used because palette 1 color 1 is shared with the top HUD and rows don't line up with attribute blocks); pointer starts on Return to title; A on a gray row plays SFX_NG (buzz). Closing doesn't advance the turn).
Keep visible rooms/corridors as they are; show rooms/corridors that were explored but aren't currently visible with a mesh look (option C of samples A-E: remove 3 of every 4 dots).
3.12 (Modern Arrange (2020s): gfx.py MESH_CELLS puts mesh versions of floor, corridor, wall front/top, down and up stairs into free sets $04, $0C, $4C, $F0, $F4, $F8 (looks K61, 63, 15, 56, 57, 58). main.s ek_kind -> follow.s mk_kind: outside the view box (vx0-vy1) and the hero's 3x3, cells use the mesh look (doors and lettered floors as floor, cracked walls as walls; objects, traps and fountains unchanged). After vis_update, mv_upd redraws cells whose appearance changed between the old and new view. dog.s scan3 and mwin.s visible-set flags include K56+ (& $7F); menu flags start from the row right below the top HUD (mesh sets lent to text had broken)).
Rework the title layout: "Into the dungeon" in the usual menu window, hint "ST (B) (A)OK".
3.13 (titlefar.s: dropped blinking and the glyph backup (TT_PB); right after drawing the title, the TT_TOP window appears (columns 8-23, rows 12-17: t_dun, t_dunh). ST/B/A go to the existing title window (Start, Continue, Manual, Language). B in the title window returns to this window. Original credit, copyright and version stay below the window).
Title hint with no gap between buttons: "ST(A)OK", drop B from the display.
3.14 (loc t_dunh: ST and A adjacent. B still goes to the title window as before when pressed).
Remove unused original-rules code (if we want it back, look at the old version and port it).
3.15 (after tagging git last-with-original-rules (V3.14), the 17 G26 branches keep only the 2026 side: no extra turns for armor, eating, praying, engraving; Pray not in the menu; all names known; hunger 2/3; maze on floors 16-18; corridor visibility and mesh art. Removed the "... (k/n)" busy messages (m_busy_off/on/eat/pray/engr) and the wait in main.s ("Still can't move... (k/n)" for paralysis, fainting and sickness kept). 156 bytes of fixed-bank CODE freed).
Make the pointer a hand pointing with the index finger. Arrows (page, dog name, language) enlarge the pressed side's triangle by 1 dot and shrink the other. Tighten the floor sign vertically. Add hints to the title too (Start, Continue, Manual, <> Language (B)Back (A)OK).
2.92 (gfx.py: pointer sprites 62-63 become a pointing hand; class selection's background pointer (G_PR) too. loc.py: large left and right triangles. fastui.s lr_kick / lr_tick (FAR27): swap only the glyph of the arrow tile in the glyph backup (GC_*) or LR_TL (class screen dog name), restored after 8 frames (waits for free VBUF). Used on title, class selection, inventory pages, manual, genocide, naming. Title window: language row becomes hint "<>Language (B)Back (A)OK" (English "<>LANG (B)TOP (A)OK": title glyph pool is 18 chars and widening the window garbled the in-game top HUD). Sign: rows 10-13 (attribute boundaries prevent fewer than 4 rows)).
Sub-menu rules: keep a dim pointer on the parent's selected row; sub-windows go heading -> separator -> content -> (B)Back (A)OK. "To title" becomes "Return to title" (yes/no); stairs on floor 1 become "Exit to the surface" (yes/no). Apply these rules everywhere.
2.92 (mwin.s mw_tile: MW_SEPF ($07F8, set by mm_spass) draws a separator at top frame + 3 rows (the frame's top line graphic). mm_hdr: dropped the down-triangle; dim pointer on the parent row (mw_pcur: OAM+248, palette 1 brown). Content from row 6 (even rows), hint on odd rows. Applied to at-feet (heading is the item name if only one), Return to title, yes/no (exit to surface, fountain, shopkeeper, guard, icebox, naming: questions become headings), direction, teleport, genocide, naming, inventory action window (vb_sep, vb_phand). Genocide and naming pages also "<> n/m").
White pointer. When the cursor moves, it wiggles left and right a little at the destination. In sub-window pages, put the arrows to the left of "(B)Back" and drop "1/13" (the arrow enlarging shows the page changed).
2.93 (gfx.py: pointer is black outline with white inside (palette 3). Title uses palette 2 only for the pointer (people and dog 0-1: title2.s); manual's kd_spal color 3 white; class screen background pointer white too. main.s cn_x: when the pointer's y changes, wiggle for 12 frames (+3 +4 +3 +1 -1 -2 -2 -1 0 +1 +1 0, advanced by frame): mw_cur2, mm_spr, inv_cursor, kd_cur, title_cursor. Genocide and naming: hint "<> (B)Back (A)OK" (tx_cl_hint_lr), no n/m).
START menu pointer doesn't appear (pressing down shows a brown pointer at top-left) / after identify or wish in invincible Japanese mode the map is full of rock and the message window disappears.
2.94 (mw_pcur / mw_pcoff moved before mw_cur. main.s A0_SW (ZP): 1 while m8_emit / hb_chr switch banks; NMI skips copying the table then and does it next NMI).
Fix inconsistencies 1-6 from the window inventory (your judgment) with before/after screenshots.
2.95 (1: mw_cur2: sub-window pointer one char left (MW_SUB). 2: genocide GN_PER 4, naming VC_PER 8 (2 columns x 4), hint at row 15. 3: direction, identify and log use "(B)Back" / "(B)(A)Back"; wish hint "Up/Down choose L/R kind (A)OK". 4: inv_mode 2 dip / 3 put in get separate hints (kaisetsu.s ih_mlo). 5: at-feet names and naming heading up to 24 chars (window shifts left); English wish title "Wish: ". 6: ms_hid: "(A)Next" when killer >= $FA (escape); killer cleared in game_start. Before/after: docs/fix_1026/1-6.png).
Pointer as a glove with cuff (Plan C), with black-and-white dither for depth. Use the recommended motion (slide, finger tap on arrival, bob while waiting, poke on A).
2.96 (gfx.py: 16x10 glove (d = black/white checker), left half tiles 62-63, right half 50-51 (unused scroll art slots). fastui.s hd_put / hd_put2 / hd_dim / hd_press (FAR27): slides half the remaining distance each frame (about 4 frames), on arrival finger +1 +2 +2 +1, 1 dot forward for 8 of every 64 frames, A pokes 3 dots for 4 frames (menu, yes/no, at-feet, inventory, actions, title). Right half = old pointer slot, left half = OAM 252 (248). Dim parent pointer also 2 sprites (OAM 240/244 and 4/8). Glove's right end sits 2 dots before the text: main window items moved to column 4 and widened by 2 columns (mw_mwid Japanese 11, English 13; when opening a sub-window the main hint row is cleared only up to the sub's left). English "Return to title" is Quit. main.s cn_x removed).
Put the dither depth on the fingers (glove) rather than the sleeve; cuff gray. Pet base color white (same as pointer). Language and pet selection: drop the row-leading arrows and use the same style as other menus.
2.97 (gfx.py: dither on the glove's underside and curled fingers, cuff color 3 (gray). Sprite palette 1 is black/white/gray (dog and pointer): fastui.s hd_put uses palette 1, dim parent pointer palette 0. Title (tt_col 0 and dog), manual (kd_spal 1), class selection (cl_spal 1, dog palette 1) also white/gray. Title window: Start, Continue, Manual, Language; choosing Language turns the window into "Language / separator / Japanese, ENGLISH / (B)Back (A)OK" (tt_menu moved to STR: bank 14 full). Class selection: 7th row is the dog; pointing at it shows "[Dog name]" with pages of 6 on the right; A moves the pointer right, up/down choose, left/right page (arrow feedback), A confirms, B cancels; hint "<> (B)Back (A)OK" (petsel.s: STR)).
Review all gameplay UI and windows against the rules (audit: 6 high, 11 medium, 10 low).
2.98 (ident_one on the stack. kaisetsu.s contents call kd_cur every frame. wish.s does its own hd_put and hides other sprites. inv_close_noturn clears OAM 0/252/248. At-feet heading 19 chars, listed names 14. tc_put bottom is 6 rows. Title pointer column 12. Class selection: glove sprite (dim pointer on the dog row while choosing its name), background pointer glyph blank. fastui.s ml_cap: break at word boundaries (action, at-feet, naming headings). Vault guard: dialogue in the message window (m_vault_q), heading "Answer guard". At-feet window offers both stairs and holy word. Before/after: docs/audit_1026).
Show ongoing status effects in the top HUD: a face (smile for normal / troubled for problems) plus up to 4 Japanese chars. Cycle all every 1.5 s. 8 dots from the XP bar. Good states get a smile. Hunger only for Weak/Fainting. Stairs direction under the stairs icon; dot when not found or on the stairs.
2.89 (fastui.s st_stat: stoned, food poisoning, fainting (red troubled face, red text); weak, blind, confused, trapped, wounded, ball (red troubled face); levitation, invisible, fast (smile); normal. Column 17 blank, 18 face, 19-22 text (up to the trophy). loc.py smile and troubled faces. text.csv st_0-12 (4 English chars). status2.s: high byte $40 means color 1 (red); radar moved to column 0. s2_score from column 23).
2, 3, 4: full run bringing the amulet home / soak test before release / review all English.
2.90 (tools/endgame.js: pick up the amulet on floor 18, climb to floor 1 and out. Re-gives it if stolen. tools/deploy.py: two soak runs before release (stops on hang, runaway, black screen). English: 13 wording fixes; a/an before item names (inv.s o_art, loc.py ojvow; omitted after That/Your and at sentence start via {ON}); genocide list drops "the"; cldesc.s mesh continuous on rows 7-23; no bottom hint while asking for a direction).
Status face as a face on yellow fill (smile + "normal" OK in Japanese too); problems get a troubled face with red text.
2.90 (loc.py EXTRA8 adds color 2 (navy); faces are yellow (color 1) fill with navy eyes and mouth. Only the face's 16x16 (columns 18-19) uses palette 3 (fastui.s sc_attr $2BEC). Problems 0-8 have red text, normal shows "OK". Red text clears only the glyph's dots from plane 1, keeping the mesh (status2.s; VBUF-8 forced absolute with a:)).
Only the first room on floor 1 is dark.
Not a bug: original room lighting (rnd(level) < 10 and rn2(77)). Even on floor 1 each room is dark with 1/77 chance.
Don't make the player choose buttons in the message window: put it in sub-windows.
2.90 (rooms.s iw_open: an info window shaped like yes/no. Direction ("Which direction? / D-pad direction / SE up / (B)Cancel (A)Here") and floor ("Teleport to which floor? / D-pad change floor / (B)Random (A)OK"). "Return to title?" uses the yes/no window. Teleport location UI that doesn't hide the map is under discussion).
Put the teleport location in a sub-window too (Plan B: a small window in the corner opposite the cursor that flees when approached).
2.91 (tctrl.s tc_pick15: rooms.s iw_open kind 2 "Teleport where? / D-pad choose / (B)Random (A)OK". Corner in IW_SIDE (bit 0 left, bit 1 bottom: right edge to +29 / from column +2, 4 cells down). If the cursor (2x2, 8 dots) overlaps the window it reopens in the next free corner trying ^1, ^2, ^3 (tc_fit). The screen doesn't scroll while open; if the offset from centered exceeds 96 dots horizontally or 48 vertically it closes, recenters and reopens (tc_vis). Reopen takes about 0.5 s. No hint on the right of the message window during direction/floor/location).
Make the floor look even less blocky: weaker, more widely spaced dashes / make wall tops look a bit more continuous.
2.70 (tools/gfx.py: floor dashes and dots every 4 dots. Wall tops (wtop, wtop_cr) have one seam per cell, as dots every 4 dots).
If save data exists, put the cursor on Continue.
2.71 (titlefar.s: on opening the window t_sel = T_SV (save exists 1 = Continue, none 0 = Start)).
Button hints show up late when returning from the log.
2.72 (fastui.s log_tall close: treat the window as closed before writing back, and call ms_hint per row. Hints appear in about 4 frames).
Under the stairs icon, a found-stairs flag and radar for the current floor: * if not yet found, an arrow from your position once found, a dot when standing on the stairs.
2.73 (status2.s s2_radar: line 2 column 2 (under the floor number) shows */8-direction arrow/dot based on the down stairs' SEENBIT. Checked every frame (not rewritten if unchanged). Blank on the surface and floors without stairs. TX/TY not used).
Bugs found in the full check (4 testers), part 1.
2.74 (fastui.s s2_score and stline.s gold: restore sc_v. Added dog.s ob_oiy / ob_oit (STR); eat.s uses dg_nut, score.s and shop.s call it via F3. dog.s ac_dif computes the difference each time. use.s s_light: litroom(0) when confused. s_tame6 moved to bank 27 and refuses " &@12").
Bugs found in the full check, part 2 (visuals).
2.75 (mwin.s: when finding a set to lend, also check cells below the window in the main window's columns. mw_items split so mmenu.s mm_sub_off redraws items too; overlapped columns redrawn up to the right frame (mw_mwid). fastui.s vb_restore: restore BG 0 with the inventory palette (it_pal)).
Bugs found in the full check, part 3.
2.76 (loc/text.csv r_escam English changed to "escaped w/ amulet" (window is 18 chars). fastui.s s2_score: score 0 on the surface (dlevel 0)).
Fix all remaining full-check issues (part 1).
2.77 (fastui.s wr_on / wr_off (messages from original doweararm / doremarm), scr_cf / scr_sc0 (confusion from original hack.read.c), tele_go (only the adjacent dog before moving), rp_kby (English: "killed by" under the heading with the name on the next line; shorter English r_killed / r_trap / r_fire / r_level), q_done (queue a blank line with ml_fb), sh_food moved out of bank 9. mon2.s: sn_new before monsters move. mmenu.s: up to 4 items listed underfoot. shop.s shk_pick_pr: price before the message. wlay.s vb_attr: keep the top half of attribute row 1).
Fix all remaining full-check issues (part 2).
2.78 (fastui.s ms15: clear row 26 first in state 6. debug.s d_kill: shk_m = $FF; d_dnst: does nothing if there are no stairs; d_goto: u_utrap = 0. mwin.s yn_loop: announce when YN_LB. fastui.s rp_spill: black out the rows above and below with the same attributes as the cushion. Note: a debug floor rebuild being generated again later is because the floor is built after the floor heading animation, not a bug).
Fix the rest too (freeing CODE space).
2.79 (use.s chwepon moved to fastui.s (bank 27): about 100 bytes of CODE freed. s_ench_arm: name after enchanting. invui.s: NM_POST = 0 before an action. ring.s: protection ring calls ac_say via F27 ring_ac. fastui.s scr_rd (blind: "As you pronounce the formula..."), lit_pre (original litroom messages: suddenly dark / even darker / a bit brighter; none when blind)).
Refactor the log (slow to open: about 21-27 frames after SELECT).
2.80 (while building, log_on = 2: NMI sets the IRQ at scanline 118 to kind 2, below which nothing is drawn but navy (looks instant); while main waits, the IRQ flushes VBUF (plenty of time since nothing is drawn). VB_XTRA allows up to 150 bytes queued, NMI flushes only up to 90. Restoring the message window's right edge only touches the hint columns. litroom also moved to bank 27 for CODE space. Opening 27 -> 16 frames, navy 2 frames after pressing).
Make the log as fast as possible.
2.81 (dog.s scan3: flags for visible tile-kind sets computed once in bank 3 (dropped about 200 per-cell F3 disp_kind calls). fastui.s lt_build: backups looked up via a hash table (LT_HSH $68C0, 64 entries, borrowing mklev's rectangle area); on a hit no bank switch or VBUF wait. lt_room waits until it fits. NMI always uses VB_XTRA = 60 while building. Opening 16 -> 11 frames (2.79 was 27)).
Make the log as fast as possible (continued).
2.82 (fastui.s gc8: skip the search when LT_NS = $A5 (only when lt_build knows it isn't in the table; other values search since $6841 holds leftover mklev coordinates). Opening 11 -> 10 frames (2.79 was 27)).
Handle the leftovers.
2.83 (kaisetsu.s inv_help: i_help_id in inv_mode. loc/kaisetsu*.txt basics 4/4 updated for Modern Arrange (2020s). mon2.s mp_cur (unseen -> MON_IT 153 "something / it", English sentence-initial It via ms_cap / log_add). zap.s hurt_mon: zhit_msg on kills too. shop.s sh_say_n: jmp sh_say (the "that'll be..." on pickup is dropped since it duplicates the price in the name). fastui.s: continuation flag in LOG_L's high bit; a continuation at the very top of the log is blank. main.s disp_kind: a door below a door is decided by the one above; if that is also a door, vertical).
Scroll of genocide: choose from a list of seen monsters (the original has you type a letter).
2.84 (fastui.s gn_read: pick from not-yet-genocided seen kinds (MH_SEEN: pulse.s sn_new flags every kind seen), 1 column x 5 rows with pages. B cancels (scroll is used up). The chosen monster's letter class is flagged in GN_B ($6F04, 9 bytes, 0 in a new game) and that class is wiped from the current floor. use.s gn_chk -> mon.s makemon won't create genocided kinds. Confused or choosing @ kills you with "You wipe out all humans..." (death cause trap_27 scroll of genocide; Modern Arrange (2020s) revive). mp_cur moved to CODE. mwin.s mw_prep: skipping below the main window only for mw_main (doors broke in other windows). 12 unused messages removed (32KB text pool)).
Make the punishment scroll's ball and chain work as in the original.
2.85 (fastui.s bc_*: chain (96) and iron ball (95) placed underfoot as spe = 1 objects. BC_PUN ($6F0D). Before walking (try_move): if the chain doesn't reach, drag the ball (ball to chain spot, chain to current spot, 2 turns); if carrying the ball only the chain; too heavy means you can't drag. The chain can't be picked up. Changing floors: removed at the start of new_level and placed underfoot in place_dog (up: "with great effort"; down: "you fall down" 1-3 and with 2/3 chance 1-20 from the ball; falling 4/5 chance 1-25 to the head, death cause trap_28 iron ball). Thrown balls pull you along. Remove curse or losing the ball frees you. Already punished: "the ball gets heavier". moverock moved to STR for CODE space. 16 unused messages removed).
Where vertical walls end at doorways, use a wall-like pattern (so the side face is visible).
2.86 (main.s disp_kind: a wall with a door or LDOOR below also uses the front face (K_WFRONT, cracked K_WCRF)).
Work through the remaining tasks (soak test, memory map, ball weight, pulling out of traps, bones, genocide pages, CODE space).
2.87 (tools/soak.js rebuilt: enters the game with random input, re-enters on death, descends every 3000 frames when invincible, saves screen and state on hang/runaway/black screen -> found: lt_build for the inventory's previous line looped forever over an uncleared hash table (since V2.81). The table now exists only while building the log. docs/memmap_table.md rebuilt; memory and save list updated with 2026-10-24 addresses. use.s ow_base: punishment ball weighs 100 + 15*(spe - 1), re-reading gives spe + 1. A thrown ball pulls you out of pits and bear traps (bear trap injures legs, 2). bones.s: ball spe set to 0. Genocide heading shows "n/m". Manual pages moved to free banks 28-29 (about 5KB freed in the text pool; kaisetsu.s kd_sbv). timeouts moved to STR (about 240 bytes of CODE freed)).
The bottom-right button hint disappears while messages scroll / make the log a bit faster.
2.88 (fastui.s: nametable for the hint columns fixed (row 26 = even, 27 = odd: ms_hcol). ms_mrow, ms_blank and Misaki text don't write the hint columns (text wraps at 24 chars). Hints no longer swapped or redrawn on every scroll -> they stay visible. Opening the log sets hint columns to blank tiles (no glyph redraw), restored on close. About 11 frames to open is the limit of this design (same even queuing 170)).
Draw the top HUD one common way (no behavior differences per scene).
2.66 (invui.s inv_draw: top HUD via fastui.s lv_top15 (same NT_B rows 18+ as in-game, ui_top = 1). During the inventory, INV_UT sets ui_top = 1 on each screen_on (action window and returning too). Inventory palettes 0 = cyan/navy/white, 2 = red/navy/red, 1 and 3 game colors. invtop.s: stopped copying status; only use previews (shield, weapon, hunger bar) on rows 3-4 right below the top HUD. Now the top HUD is identical in game, menu, at-feet, log, class selection, floor sign and inventory).
Review the bottom UI, title and in-game menu code and share what can be shared.
2.67 (mwin.s yn_loop / subwin_close: yes/no and sub-window closing unified (rooms.s, mmenu.s, vcname.s). mmenu.s: nt_mesh -> nt_fill, one frame tile table (removed invui2.s's 160-byte copy, mm_tilesm), main window hint via a single mw_hintrow. invui.s nt2_put: unified 2-high writes of draw_mline / title_line (about 30 bytes freed in fixed). role.s: removed unused cl_hint. Click sound for the inventory action window and title up/down too. Fixed the star etc. mesh in the top HUD going gray in sub-windows (P3_DIM: navy in NMI, dark gray in IRQ kind 3)).
Remove the post-use "(B) Field (A) Items" prompt (whether to go back to the inventory); it's a special case.
2.68 (deleted postuse.s, about 1.7KB freed in bank 15. invui2.s iv_back only for naming; invui.s always returns to the field after an action. Removed PU_ON references, loc.py's line-break table and m_pu_hint).
Investigate the screen going black (only hero and dog visible) after quaffing a potion.
2.69 (sound.s sfx_play no longer clobbers A/X/Y (protecting other callers too). Restored `.segment "FAR15"` in loc.py. build/pot_all.js quaffs all 16 kinds and confirms the screen appears).
Base the D-pad, B/A, START, SELECT position and size on STREET KNUCKLE (add to the secret sauce) / gather assets and licenses into the credits and check duplicates.
2.33 (web/play_src.html: landscape uses SK's askFit (function row centered under the TV, TV + row vertically centered, D-pad and round buttons bottom-aligned with the TV bottom), DEV in that row, .phlT 3 columns. Portrait: face 410, .mid at bottom center, DEV in .mid too, controller top = max(h*0.5, TV bottom + 24), scale = min(k, ...). Confirmed same numbers as SK at 1280x800, 844x390, 390x844. Wrote the standard into bookofsecrets 06_8bit コンソール画面.md 3-0. Manual: the 2 asset/license pages became credits 1/3-3/3 (merged duplicate original game, source and font credits); 6 chapters in contents).
Switching language blanks the screen: just rewrite inside the menu.
2.34 (titlefar.s: after changing LANG_B, F14 tt_menu redraws only the window (screen not cleared, pointer position kept)).
Keep the top HUD shown as in-game during class selection and the floor-transition animation (reuse the routines).
2.35 (a "top window only" mode: ZP ui_top. NMI shows the top window (NT_B row 18, table $1000) and an IRQ at scanline 21 (BG off, NT_A y = 24, bg_tbl, BG on 2 lines later) even outside the game when ui_top is set. Line 2 glyphs/mesh use the same indices in $1000 during ui_top (status2.s; free since no messages). screen_off clears ui_top. text.s st_redraw_now (line 1 only). Class selection: game palettes, draw_frame, st_redraw_now, st2_reset, st_service/st2_tick each frame, palette 0 above the window. Floor sign: fastui.s lv_top15 (draw_frame, st_build, st_redraw_now, st2_reset, st2_tick); lv_dark doesn't darken palettes 1 and 3; palette 1 only inside the sign window (lv_attr15). Removed unused st_stair (FAR13 overflow). Fix: screen redraw (ms_redraw15) blanked the hint columns in row 26, hiding the top half of the A hint).
SELECT on the map: scroll back through past messages up/down in the message window (16 lines; SELECT / B / A to return). Add to the manual too.
2.36 (redraw ring cut 256 -> 128 entries ($7300-$757F, index and #$7F); the freed $75C0+ holds the log: 320-byte text ring, 16 line heads/lengths, control (to $7734; $7735-$777F kept free). Text packed (top = 0 is 1 byte, otherwise 11 + top/bottom). text.s ml_push calls F27 log_add (English line-initial "the" -> "The"). New bank 27 (FAR27, F27) holds fastui.s log_*: log_view scrolls the message window's 3 lines (rows 26-27 Misaki, 28-29 k8x12) with up/down, returns to latest on exit. Log cleared when building a maze (maze.s pushes reach it). kd_line8 width made a variable (kd_lineW), free mesh is mf_base + IVF_F. Manual "Controls" gets SELECT; menu description "Pray" -> "Holy word". Removed the unused identified-items screen).
The numbers in "Still can't move... (k/n)" are garbled.
2.37 (fastui.s mu_tick (bank 15) called msg_tx directly, reading the {K}/{KN} digit glyph table g_digit (bank 3) from the wrong bank -> F3 msg_tx).
SELECT extends the message window upward to show 13 lines at once (no scrolling). B / A close it.
2.38 (fastui.s (bank 27) log_tall: top HUD shows only the top window (lv_top15), rock above the window (map $10-$13), a line at row 14, newest 13 lines in Misaki on rows 15-27 (kd_lineW, glyphs from $14); closing redraws the map (inv_close_noturn). Removed (up/down) from the manual's SELECT. In-window scrolling log_view kept (unused)).
The log shows messages from the previous game / 10,000 points right after starting.
2.39 (main.s game_start calls F27 log_clr. The 10,000 points is the deepest-floor bonus (1000 x (deepest - 20)) when u_maxdl is 21+: it persists if you go deep via DEV floor jump. Working as designed).
Already 10,000 points on floor 1 (new game).
2.40 (score.s sc_kill (bank 12) read the XP tables mon_xpl / mon_xph (bank 3) from the wrong bank and added garbage (e.g. $8AF4 x 16) -> F3 sc_xp (small reader in the fixed bank). The 2.39 "deepest-floor bonus" diagnosis was wrong).
Keep the map visible above during the log. Same look as the message window (bottom line k8x12, upper lines Misaki, same colors); hint "(SE)(B)(A) Close" at the top right of the window.
2.41 (fastui.s log_tall rebuilt: a window below the map using the same mechanism as the field's top window (mwin.s) (cell-aligned palette 3, mesh $FC). Only tile kinds visible outside the window are kept; the rest are lent to text. main.s NMI split mode 3 (log_on: lower window from scanline 207, 8 dots from the Misaki above). 11-10 Misaki lines + previous 2 + current line. Closing doesn't blank the screen: mesh -> graphics -> attributes/cells restored. Lines overwritten after the ring wraps are not shown. SELECT also closes. Bank 1 overflowed, so trap.s utrap_move moved to fixed).
Button hint icons: START = ST, SELECT = SE.
2.42 (loc.py: rounded SE and ST as 2-char icons (k8x12 and Misaki, private use U+E030-E033); S-icon in text changed to SE (log, inventory, wand direction). ST glyph prepared only (no START hint currently)).
Rounder SE / ST / remove the line between the log window and message window / no grow animation, appear instantly.
2.43 (loc.py: SE/ST corners rounded 2 more steps. main.s irq @win: with log_on, don't turn BG off; jump first then switch tables (window mesh $FC and NT_B row 25 are mesh in either table: send mesh to $FC/$FD in $1000, row 25 = $FC only during the log). fastui.s log_tall: send all glyphs first (invisible because on-screen tile kinds aren't lent) -> one band per frame from the bottom (2 rows + 8 attribute bytes), about 6 frames. Closing unchanged).
The top HUD disappears once when moving to the floor after going down the stairs.
2.44 (new hb.s: when changing floors mid-game the screen isn't blanked; NMI enables only the top 21 scanlines, an IRQ at line 21 turns it off and VRAM is written after that (main.s wraps with hb_pb..hb_pe, vb_exec wraps itself; frames that miss simply stay dark). The top HUD nametable (NT_B rows 18-23, attributes $2BE0+) and glyphs ($1000-$17FF, $0AC0-$0E7F) are kept: the floor sign doesn't reload CHR, the new floor loads only the rest via hb_chr. Sign ripple band moved $C0+ -> $80-$95; ui_top = 2 during the sign. To free fixed-bank space, draw_frame, attr_dump and text_redraw_now moved to bank 15, clear_nt to 14. Down/up stairs, trap doors and floor jumps now have 0 blank frames and are about 8 frames shorter. The first floor of a new game is unchanged).
Double-tap a direction to run that way until hitting a wall or arriving next to an object or monster.
2.45 (fastui.s (bank 27) run_tick: pressing the same direction twice within 18 frames runs; any other button or direction stops. run_move calls try_move in that direction. Stops on: can't move (wall), new message (stairs, on an object, trap, etc.), HP dropped, an untamed monster adjacent, an object adjacent or underfoot. Two hooks in main.s game_loop. Stopped in el_body).
Messages about getting hit in red, landing hits in yellow (log in gray). Change miss messages to "You dodge the attack! / Your attack is dodged!" with dedicated sound effects.
2.46 (text.s ML_COL: kl_uhit and trap/ray hits = red, kl_hit and kills = yellow. Carried in byte 32 of the queued message; fastui.s ms15 colors only the current line (k8x12) in color 1, attribute red = palette 1 / yellow = palette 3. Previous line (Misaki) and log are gray. sound.s SFX_DODGE (swish) and SFX_EVADE (whiff/thud). Also fixed misses naming a different monster (kl_miss clobbered X). Running's try_move now called in bank 3 (a 2.45 bug), utrap_move moved to bank 3).
After equipping, return to the field without waiting for input.
2.47 (postuse.s pu_wait: wield, wear, take off, put on (V_WIELD..V_REMOVE, V_PUTON) return without showing the hint).
Don't show button hints in the bottom UI during the escape and grave result windows. Make the top and bottom UI gray.
2.48 (rp.s rp_begin: calls ms_hint 8 times while MW_ONF = 1 in mw_prep (hint "none" clears it). NT_B attribute rows 4-7 to palette 3, palette 3 = $00/$01/$10).
Narrower menu window (remove the score row); show the full score at the right of top line 2 with an icon. Escape-success heading in yellow.
2.49 (mwin.s: window height = 2*items + 6 rows, hint one blank row below items. fastui.s s2_score: trophy (new glyph loc.py G_CUP) + up to 8 digits right-aligned at top line 2, columns 16-29. rp.s -> fastui.s rp_ychk / rp_yel: heading drawn without the backup and redrawn in color 1. Palette 1 color 1 set to yellow (top/bottom UI use palette 3 gray)).
Tidy the top HUD as in the mockup (bars directly under the text).
2.50 (line 2: HP 4 cells at columns 4-7 (26 dots), hunger 9-11, XP 14-16. Line 1: star 14, sword 17, shield 21 (columns where digits don't land in the star cell). Attributes: star at bottom-right of $2BE3, HP/hunger yellow at $2BE9-$2BEB together. hb_take and s2_score from column 17. rp gray handling moved to bank 27 (bank 13 overflowed)).
Leave 8 dots between the menu pointer and items (like class selection).
2.51 (mwin.s: items moved from window column 2 to 3).
Also 8 dots between pointer and items in the item list; mark equipped items with a red * 8 dots after the name.
2.52 (invui.s: names from column 3; fastui.s inv_mtail appends " *", inv_mattr sets that cell's attribute to palette 2 (red) (blank before the *, so the name stays white). invsort.s is_red uses palette only).
SELECT log appears and disappears instantly with no grow/shrink. SE to the left of the hint. Always show the score after the title, with a yellow icon.
2.53 (fastui.s log_tall: the window is drawn first on off-screen map rows (world rows H..H+10) and shown at once by IRQ kind 6 (main.s: jump at scanline 118). Closing instantly restores the split and repairs offscreen. Mesh $3D (same index as message window row 25, so mesh in either table). 9 Misaki lines + 2 + current line. try_move moved to bank 3 (fixed-bank space). status2.s hb_take always shows the score (s2_score); trophy in color 1 with palette 3 only for its cell (sc_attr). text.csv: SE first).
Color the top HUD icons on the inventory screen too. Message window like in-game: 2 Misaki lines + 1 k8x12 line. Make the score icon yellow.
2.54 (fastui.s it_icon: heart/meat/star/sword/shield/coin use the same colored 8x8 as in-game (m8 table m8_cp) on tiles $E6-$F1, red/white = palette 3, yellow = palette 0 (invui.s yellow/navy/white). invsort.s: glyph backup up to $E6. iv_m8: rows 26-27 in Misaki. Trophy added to M8_CHARS (color 1 -> yellow); attributes rewritten after floor change (st2_reset). Fixed the wait after equipping: invui.s passed 0 instead of the action to iv_mark. Removed unused fastui.s mm_score).
Right-align gold and score.
2.55 (fastui.s s2_score: digits always end at column 29 (like gold); when the icon alone occupies the yellow cell, leave one space between icon and digits (same as the gold coin)).
Message window button hints in white. Log hint on the same line as text (overlapping chars erased), in white. Level-up message in yellow. Halve running speed.
2.56 (fastui.s ms_hatt: hint cells (bottom half of NT_B attributes $2BF6/$2BF7) palette 1, otherwise 3. Log: hint at the right of rows 0-1 (also erasing the preceding odd column), those cells palette 1 (lt_hpal), 11 Misaki lines. mon2.s / use.s: level-up message ML_COL = 2. Running: 16 frames per step, hero sprite moves 2 dots every other frame (main.s anim_all)).
Shop name messages ("Welcome to ...'s antique weapon shop!") and other special-place messages in yellow. Drop "The shopkeeper is here!" on entering a shop (keep it when chasing).
2.57 (shop.s: shop greetings (first and return) via sh_sayy (fixed: ML_COL = 2). rooms.s: zoo, swamp, morgue; vault.s: vault guard also yellow. pulse.s sn_new: a non-angry shopkeeper isn't announced).
When closing a window, the pointer sprite disappears late (hide it first; review all places with a pointer).
2.58 (invui.s cur_hide: clears OAM 0 and 63 then waits for vblank. Done first in the main menu (mm_close, mw_close), at-feet / yes-no windows (rooms.s), naming window (vcname.s), inventory action window (vb_out). mmenu.s: fixed rows 15-17 still hardcoded after narrowing the main window (hint mesh when opening at-feet, redraw on close)).
For equipment underfoot, show how much better it would be if worn/wielded: "A long sword is here. (+1)" (better in cyan, worse in red).
2.59 (fastui.s sh_here_msg / eq_delta: armor compares (10 - AC + known +n) with what's worn in the same slot; weapons compare known +n with the wielded one. msg_tx built without queueing (ML_HOLD), then (+-n) appended (separator ML_SEG and color ML_SEGC in bytes 31-32 of the queued message). Only those chars get color 1 on the current line; cyan sets the cell to palette 3 and main.s irq @win makes palette 3 color 1 cyan for the message window only (NMI restores yellow). Long English lines push (+-n) to the next line).
The mesh behind the title screen text is yellow.
2.60 (main.s NMI: restore palette 3 color 1 to yellow only in-game (PAL_CY could be 1 from battery-RAM garbage, turning the title mesh yellow). PAL_CY = 0 in el_body).
Show underfoot items as "axe +1 (coin)120 (+3)" (name, known +n, price, change when equipped; (+-0) if no change). In the SELECT log (+n) turns yellow. Remove "Description" from the at-feet window.
2.61 (fastui.s sh_here_msg: {O} (+n after), coin, price x qty, (+-n). Price from shop.s sh_dprice (from kind, location and floor without game RNG; same as the pickup price). Change only for hand weapons (mace..spear) and armor. Color split at "(" or the preceding space (left edge of the cell). main.s irq_k6: cyan in the log window too. mmenu.s: Description removed from the at-feet window).
Huh? The hero's facing no longer changes!!
2.62 (fastui.s ms15: don't send message glyphs while the hero sprite is pending (ms_hpend) -> sprite sent first).
Pack the trophy score so its right end lines up with gold's ones digit. Coin icons in the previous message line and log also gray (bottom line stays white). Return to the field after dropping too.
2.63 (fastui.s s2_score: icon column = start + 21 (digits to column 29). M8_MONO: previous line (ms15 state 1), log (lt_build) and lv_m8 draw colored Misaki symbols in one color. postuse.s: V_DROP doesn't wait either).
Show unpaid items with the amount instead of "(unpaid)".
2.64 (inv.s obj_name: unpaid via fastui.s nm_unpaid as "(coin price x qty)". on_from_inv puts the unit price (I_PL/I_PH) into ON_PR).
Messages from the previous playthrough remain in the inventory screen's message window.
2.65 (invui2.s iv_msg / fastui.s iv_m8: the bottom 3 lines come from the log (lv_get: empty in a new game), the same source as the in-game message window and SELECT log).
Support both Japanese and English (switch with left/right on the title).
2.17 (messages, monster/item/trap names and UI words placed at virtual addresses $0100+ at the same offsets for both languages: Japanese in banks 19-22, English 23-26 (loc.py pool, sized to the longer, never crossing 8KB). text.s sb_ptr reads addresses below $8000 from the LANG_B bank ($7EB4: 19/23). English: monsters take "the", sentence-initial "The" (ms_cap); traps and missiles take a/an; items like potion of X / X potion; 30-column wrap that doesn't split words (fastui.s ml_wrap). text.csv / glossary.csv get an English column (384 auto from the original source, the remaining 256+96 translated). Title: left/right switches language and redraws, row 16 shows "< JAPANESE / ENGLISH >", title glyphs shared via a backup (TL_GC). English glyphs overflowed the manual in bank 10, so glyph overflow went to free bank 5. Class screen: English avoids the top number tiles; dog name 5 chars. Manual still Japanese only).
English class descriptions and manual in Misaki 8x8 with tighter lines (Japanese unchanged). Add a language row to the title menu, left/right for the dog's name too, dog in the wizard's purple.
2.18 (the manual was blank: sb_ptr read everything below $8000 as virtual addresses, corrupting text in internal RAM like the manual's KD_BUF ($0780) -> $0000-$07FF read as-is, virtual addresses start at $0800 (loc.py). English manual kaisetsu_en.txt, 19 pages; English only in Misaki 8x8 with 8-dot lines (fastui.s kd_line8 / gc8g, cd_up8 / cd_ln8); Japanese stays k8x12 with 16-dot lines (kaisetsu.s kd_row, cldesc.s branch on LANG_B). Fixed gc8g's set selection advancing the index by one and garbling text. cd_mkbm moved to bank 15 (FAR14 overflow). Japanese warrior description overflowed by 2 chars (dog-name arrows), reworded. TL_GC / TL_T overlapped the ripple RPL_BUF ($07A0+) so the dog name used the backup and garbled English "Back" -> moved to $78EA / $78EB. Shorter English headings (old->new, Who's Who), inventory hint "(A)Hold", Misaki font added to licenses).
Remove the language row from the PUSH TO screen; lower the window by 1 row and remove "Based ... 125 Inc."; title character colors; English class screen drops the original-text line and starts body text under the heading; a blank line appears in the message window's previous text.
2.19 (language left/right only inside the window (titlefar.s). tt_menu: rows 20-25 restored to the ripple band, window from row 12 (aligned to 16x16 color blocks), pointer from row 13. Title sprites: 4 figures with 4 palettes (slot i = palette i, per-face colors tt_col: brown/blue/silver/purple like the class screen, dog purple, monsters red). {EMPTY} in text.csv English (loc.py: empty only in English) -> cl_e0-5; English body from row 10 (fastui.s cd_ln8). ml_push: whitespace-only lines are not queued (text.s)).
Put a left/right arrow chip (one char) before the dog name / add common English names (Japanese up to 4 chars) / compare Japanese and English side by side per scene to spot breakage.
2.20 (loc.py: a single-char left/right arrow glyph and an empty glyph (erases the old name on redraw). Monster face and arrows overlapped the inventory bar's left-end glyphs -> moved (also text.csv, kai_clean). 16 dog names (added Max, Buddy, Bella, Lucky, Rex, Daisy, Rocky, Coco; role.s / text.s and #15). build/bilingual.js shot 35 scenes in both languages: English wrap left a leading space on the next line (dropped in text.s ml_add); the space in English manual "(B) goes back" vanished (kai_clean now Japanese only); action window 18 chars in English, left column 8 (VB_C0: frame, heading, actions, pointer, vb_w); main menu 12 columns in English (mw_mwid: "(B)Back (A)OK" and Elbereth overflowed); at-feet text "On the up stairs" etc. reveal.s rv_setup / rv_blank moved to bank 15 (FAR14 overflow). Remaining: with inventory open, Misaki char 30 overlaps the hunger bar glyphs ($FA+)).
With the inventory open, the 30th character of the message disappears ("in the dus").
2.21 (inventory message (invui2.s iv_msg, redrawn in k8x12) was limited to 29 chars to avoid the frame -> 30 chars (columns 1-30, frame at 31). Also the reserved tiles $F2-$FD for colored text above the inventory (defense/weapon previews, hunger bar) overlapped Misaki $C0-$FB chars 25-30 of the previous message -> defense $BC+, weapon $FC+, hunger bar reuses those two (only one item pointed at: invtop.s it_bslot)).
Use the same left/right arrows for the language row as the dog name, and on paged screens' hints.
2.22 (unified with the single-char arrow (G_LR): title language "<> JAPANESE / ENGLISH", manual bottom-left "<> Page", inventory page "<> 1/2" (rooms.s iv_pagen; kaisetsu.s inv_help edge arrows dropped), identified list "<> 1/14" (ds_pg1)).
Move button hints from the top HUD to the right side of the top line of the 3-line message window (message takes priority if long). Short words in English.
2.23 (fastui.s ms_hint: overwrite one char per frame on the rightmost W columns (Japanese 4, English 6) of set MS_SLOT^1 in row 26 (Misaki line two back). Hint kind chosen like st_ahint (ms_hid: none during windows or post-use hint); redrawn when it changes or the line scrolls (ms15 state 5, ms_redraw15). MS_HW $78EC, MS_HP $78ED, MS_HK $78EE. Top st_ahint always blank. English search is "(A)Look". Measured message length: Japanese avg 12, max 29 (almost 0% of lines overlap a 4-col hint); English avg 31 (about 28% overlap 6 cols after wrapping)).
In English, word-wrap lines that collide with the hint onto 2 lines.
2.24 (English wraps within 24 chars: fastui.s ml_wrap breaks at the space before column 24; text.s ml_push splits 25-30 char lines in two via fastui.s ml_split. Trailing spaces not counted. Continuation lines flagged in byte 31 of MQ / MB_TEXT (ML_CONT $78EF); ms_cap doesn't capitalize "the" on continuation lines (had become "The dust")).
Remove the identified list / floor as "stairs 2" (leave the 2nd digit blank) / remove the stairs direction and monster face from top line 2 / score digits in the menu.
2.25 (removed $FE from the main menu and inventory special rows. stline.s: single-digit floors get a leading space; st_stair / st_mon not called (columns 16-26 blank); rewrote the manual's "the top face turns red". Score via fastui.s mm_score: Japanese n points / n-man points, English "npt / nkpt / nMpt" (6 chars max). Score is 24-bit (max 16,777,215)).
Leave one space between the floor's first digit and the heart.
2.26 (stline.s: heart at column 4, HP 5-7, meat at column 9 (center of the hunger bar columns 8-10, not touching HP)).
(Correction) Moving the inventory colored-text tiles to $BC+ / $FC+ in V2.21 was a mistake.
2.27 (invtop.s: back to IT_TAC $F2, IT_TWP $F6, IT_TBAR $FA (it_bslot in original order)).
Shopkeeper won't move from the door / unclear how to pay in shops (want to pay by bumping the shopkeeper) / similar blocking / title "Continue" and "<> Japanese / ENGLISH" / stairs with twice the steps, same stride.
2.28 (shop.s: the straight-line check when avoiding the hero on the door was inverted (original shk_move NOTONL: step to a non-aligned cell) -> steps aside next turn. shk_bump: bumping pays if you owe anything (shk_pay). vault.s: bumping a guard waiting for gold drops it (gd_dropgold); removed "(menu)" from the text. Title: t_cont "Continue", t_lang "<> Japanese / <> ENGLISH". lvanim.s: tile $F2 on top of steps (two 4-dot steps) to look like 12 steps; hop stays 8 dots).
Message window A/B hints in 8x12 / dog-name arrows in the pointer column / if you owe and the shopkeeper is adjacent, A pays; moving into the shopkeeper goes back to asking "Attack?".
2.29 (fastui.s ms_hint: drawn in k8x12 across the column sets of rows 26-27 ($C0+2*col and +1; swapped top/bottom in VBUF if row 26 is the odd set). Dog: TL_COL = column 2, text "<> name" (loc text.csv dp_). shop.s sh_apay (can pay and shopkeeper adjacent): main.s A and ms_hid ("(A)Pay"), also right after picking up. Guard waiting for gold: "(A)Drop". Bumping shopkeeper/guard asks "Attack?" again).
Title window: 8 dots of space after the pointer / ripple invisible on real hardware / PUSH TO DUNGEON looks like black text when disappearing.
2.30 (mwin.s tt_menu: items at column 12, language row "<> Japanese / <> ENGLISH". gfx.py PAL_TITLE color 1 (shared by all palettes: background dots, ripple, floor sign) set to gray $00. titlefar.s: PUSH TO DUNGEON tiles (rows 12-13, columns 8-23) read back after drawing and saved to TT_PB $7580 (borrowing VB_SATT); blinking alternates rip_erase (ground band) and writing back the saved copy).
Make the selection pointer the same red as the title / dog in the spelunker's colors.
2.31 (in-game and inventory pointer use palette 2 (monster red); manual's kd_spal palette 3 set to red. Title: monsters always palette 3 (red), people and dog palettes 0-2 by slot (title2.s TT_PA $75A0, TT_PK $75A4) -> pointer 3. Class screen lacks palettes, so the pointer is a background k8x12 arrow: list on even rows (CL_ROW0 8, CL_DROW 20), columns 2-3 attributes palette 3 (2 navy, 3 red), CL_CR $78F0. Dog: gfx.py SP1, cl_opal, tt_col, kd_spal brown $17/$27).
Manual contents: heading "Manual", first line "Contents", remove the controls line and leave it blank, no page count, 8 dots after the pointer, bottom-left "<> Page".
2.32 (page 1 of kaisetsu.txt / kaisetsu_en.txt, kaisetsu.s kd_draw: KD_P = 0 skips kd_pnum; loc.py: contents lines start with 2 full-width spaces; kd_hint "<> Page").
Cells already seen remain blank (dark).
2.06 (safety net: while nothing is pressed, redraw one of the 32 screen columns from the map every 8 frames (fastui.s heal_tick, same build_col / col_vb as scrolling; not during windows, death, ring redraw wait or scrolling). Verified 3 blanked cells come back).
Make the title, stairs and falling backgrounds match the map.
2.07 (gfx.py CROSS8 changed to diagonal dots (title background $FE, logo ground, class screen background); ripple.s rip_pat ground uses the same dots, the faint ring pattern is diagonal lines).
Make the top HUD Misaki 8x8 / match the pattern behind the title text.
2.08 (line 1 is NT_B row 19, line 2 row 20; NMI starts from the 2nd half of row 18: scanlines 7-14 and 15-21; the map always starts at scanline 24 (same whether still or moving; window positions unchanged). IRQ: 14 second-half table -> map, 21 to map, 23 BG. loc.py m8_lo/hi/dat (Misaki PNG in ref/font, 13 symbols hand-drawn in colors 1/3), fastui.s m8_emit / m8_sq (bars and face squeezed from 16 to 8 rows). Attributes: star at $2BE3 bottom-left, bars on the top half from $2BE8. gl_mesh behind title text also diagonal dots. Fixed m8_sq making VBUF-1,x a zero-page access).
Diagonal stairs background / steps 8 dots x 6 / stop-and-go movement / dog follows late from the edge / title also pauses each step.
2.09 (stairs: column 13+k, rows 19+k and 20+k stone, 21+k top-right triangle $F1. Hero moves one step per 20 frames (8 moving, 12 stopped), footstep and ripple when starting; the dog moves the same way 2 steps behind from the top edge; ends 10 frames after the hero reaches the bottom edge (LA_N 162). Falling ripples also every 20 frames. Title: one step per 64 frames (8 dots in 16), footstep and ripple on each step).
Line 2 of the top HUD sinks 1 dot upward.
2.10 (second half uses the map table from NMI onward (line 1 only uses the first half); IRQ back to 21 map, 23 BG, window, 236. BG at scanline 23 is always m_map).
Top HUD: thinner bars / more space from line 1 / gold in yellow / clearer A and B.
2.11 (m8_pick: mesh, frame, gap, 2 filled, gap, frame. The gold coin goes on an odd screen column (shifted by digit count with a blank inserted); st2_attr finds it via st_want and rewrites attribute row 4 with palette 3 only for the star and coin cells (S_AST = coin column). A and B are 8x8 white circles with cut-out letters).
Batch of reports: teleport trap, slow top HUD, pickup sound, identified list, R key, hunger bar, pointer, face, title walk, monster colors, music, landscape report screen.
2.12 (trap: "A teleport trap! You're whisked away!", sound after redraw, shake 12. st2_service 3 times per frame. Footsteps don't cut the playing sound; gold is just a clink. g26_skip (loc.py: 13 appearances with no original name). Identified list shows "n/m". inv_sort discards backups at $F2+. mw_close / mm_close clear the pointer first. Face: mo_face rows 3-11 with eyes on one row. Title: 32 frames per step, monsters red (tt_spal set 4 $16/$26, people blue/silver/purple). Music regenerated with seed 2026. Web: R to report; landscape puts the screen left and input right (2x text). 3 unused messages removed).
Hop with ripple / title menu Start / Continue / Manual (content uses Modern Arrange (2020s)) / 2026 chapter in the manual / remake the music.
2.13 (title: 4-dot hop over a 32-frame step (tt_jump, TT_JY $07B0), footstep and ripple on landing. Stairs: 4-dot hop over the 8-frame move (la_jump, LA_I), footstep and ripple on landing. Title window columns 10-21: Start, Continue, Manual; Start always sets G26 = 1. Manual contents get "Modern Arrange (2020s)", 2 pages (1-turn actions, names known, no praying). music.py: hand-written 32 bars in D minor (A A' B A', bass on root/5th/octave, snare on 2 and 4). Unused t_cont26 / t_new26 removed).
Modern Arrange (2020s) part 2: monster traits / curse marks / A for the usual action, hold for menu / hunger 2/3 / hidden doors 1/3 / amulet at floor 18 / revive once.
2.15 (pulse.s sn_hint: for 21 monster kinds with notable traits, first sighting shows "{M} is here! (trait)" (text in bank 3 mh_, MH_SEEN $7EB6). Known-curse flags CK_B $7EAB (7 bytes: ck_set when refused, ck_clr in add_inv, list shows (cursed)). Inventory A: release under 20 frames to do the first action immediately, hold for the window (fastui.s inv_hold), hint "B Back A Use A(hold) Menu". Hunger skips every 3rd turn. Hidden doors 1/3. Maze rn1(3,16), amulet at floor 18+. On first death, revive once at full HP on up stairs (rv_try, REV_USED $7EB2, score halved in score.s). Manual's 2026 pages rewritten. Unused ds_tick removed).
Score in the menu / full Misaki character set / previous line of the message window shown in Misaki as 2 lines.
2.16 (main menu row 13: "Score n" (sc_base, sc_dec). Bank 18 holds all 903 Misaki 8x8 characters (loc.py m8_bin; non-JIS characters hand-drawn or rows 2-9 of k8x12, 7.1KB); m8_emit temporarily sets R7 to 18 when a char isn't in the colored table (mmc_sel also 7, matched with IRQ). Message window: rows 26-27 in Misaki (tile sets $C0+2c and +1, MS_SLOT $7EB3), rows 28-29 the new message (always T_MB). msg_service has only a small entry in the fixed bank; the body is fastui.s ms15 (1 draw in Misaki -> 5 rows 26-27 -> 2 clear -> 3 k8x12 -> 4); screen redraw is ms_redraw15 (blank if two back)).
Comparison with the Misaki font, in the 125 version (cover and closing page, no page numbers).
docs/HACK_フォント比較.pptx / .pdf (17 slides: feature table, list of the game's 900 characters (current vs Misaki), 8 side-by-side screen comparisons, Misaki B, high-stroke kanji (top 40 by usage and strokes), summary). tools/make_font_report.py.
Buttons show names only, grouped under DEV / stairs ripple on every step, also while falling / stairs 1.5x slower.
2.02 (buttons: SELECT, START, B, A; above them 9, 0, O, P. DEV replaces REPO: turn/floor rewind, debug (hero, floor, spawn, log, emulator), report, name, ROM DL, in the same navy window with a pointing hand as the game menu. Selects are < value > (hold to repeat), seed entered by pressing. Stairs: 0.25 dot/frame, 216 frames, footstep and ripple every 12 frames, also every 12 frames while falling. Ripple slots 3 -> 8 (RPL_C / RPL_N overlap RPL_BUF). LA_E is IT_W+10 (+11 is LV_TT)).
During multi-turn actions show "Taking off ring mail... (1/6)".
2.03 (taking off / putting on: "...ing... (1/n)" at the start, "(k/n)" each turn, "removed / put on" at the end. Eating, praying, engraving show "(k/n)" each turn. Message codes {K}=BZ_N-multi, {KN}=BZ_N (text.s ml_k), BZ_K $78FE, BZ_N $78FF (0 at title); not shown if multi_why doesn't match. Next turn waits until the message finishes (the 4-entry queue overflowed and dropped lines). STR2 overflowed by 33 bytes, so 5 unused messages were removed).
Ripples at 4x the interval / remove INS/DEL / Modern Arrange (2020s) on the title.
2.04 (stairs and falling: one every 48 frames (every 4 LA_RP steps); title: one every 4 steps. Insert/Delete shortcuts removed. Title window: Continue ORIGINAL/2026 (by save version), Start ORIGINAL, Start 2026, Manual; no B Back / A OK hint (18 glyph slots); window columns 6-25. G26 at $7EBF (after SV_DATA): Modern Arrange (2020s) make armor on/off, eating, praying and engraving take 0 extra turns, and all item names are known from the start (OK all 1)).
Remove the item category descriptions / no praying in Modern Arrange (2020s) / show "Still can't move... (k/n)" while paralyzed.
2.05 (ds_tick no longer called (inventory, main items). Under G26 the main menu and inventory hide Pray. For multi_why 2-4, the first helpless turn sets BZ_K $83, BZ_N = multi+2, and turns with a free message line show "Still can't move... (k/n)" (no waiting: held input stays 1 turn / 2 frames)).
When descending, the characters overlap the 'Floor n' window (can they go under it? if not, lower the stairs)
1.66 (stairs lowered 16 dots: row 17 -> 19, step 0 top 136 -> 152)
A title-like ripple on the field too (when a turn is used, spreading from the hero over dark background only, overwriting the previous ring)
1.67 (src/field_rip.s: radius 1-6, 1 cell per 4 frames, denser closer in. Next ring waits until the redraw ring is free -> delay 31, walking speed unchanged)
When an attack lands on a monster, play the same sprite animation as a kill; on a miss, flash a small graphic plus a whiff sound
1.68 (klmark.s kl_hit, kl_miss: hit = kill marker (24 frames), miss = half-size graphic for 8 frames. Miss sound changed to a whiff (noise + thin rising tone). Marker moved to the front of the sprite order. Sound playback also moved into klmark.s to avoid growing the fixed bank)
Show a mark and sound effect when the hero takes damage and when a monster misses.
1.69 (klmark.s kl_uhit / kl_umiss: when hit, the same mark as a kill appears on the hero's cell plus a hit sound; when missed, a small mark plus a whoosh. There is only one mark, so if monsters keep attacking, the mark from your own attack is replaced midway).
Make the field ripple 8x8 like the title (one ring and one pattern is fine). It should spread and vanish while standing still.
1.70 (field_rip.s rebuilt: ring table for radius 1-8 tiles in field_rip_tab.inc, nametable tiles written straight to VBUF with horizontal runs merged, lightweight rock check. While a direction is held it only erases the old ring bit by bit and draws once you stop: 18 steps held, slowdown same as before; standing still, 8 dots in 3 frames. Only tiles $08/$09 are not lent to windows).
Make one ripple last until the arc has left the screen entirely (do not stop at radius 8).
1.71 (ring width per row from a table of squares comparing (2i+1)^2+(2j+1)^2 with (2R)^2. Ends when no part of the ring is on screen. Draws only on-screen, restores off-screen rows too. Far rings write a lot, about 10 frames per step).
After using an item: show the field with "(B) Field (A) Items" at the right end of the message. A = items, B / D-pad = field. Messages over 18 characters wrap after the name onto 2 lines.
1.72 (postuse.s: first use of bank 15, F15 called via far_go. Break points: particle (when not preceded by hiragana), then punctuation, then 18 characters. Tables built by loc.py. Top hint is blank while shown. Not shown on death, floor change or long actions. Ripple pauses while the message is up).
Ripples should move at an even speed and look big (16 dots at a time, dashed is fine). While walking, start the next when one vanishes; when stopped, finish with the current ring.
1.73 (16 dots per step every 8 frames at any radius, 16-dot checkered dash, ring in color 2 (gray). Walking mid-ring starts the next ring when it vanishes. Keeps going while walking with less work per frame: 4 s held = 17 steps, slowdown 87 (no ripple: 18 steps, 86)).
Drop the ripple (heavy and not pretty); instead lightly animate the dark background (so it doesn't look like damage). Plan A: footstep echo.
1.74 (pulse.s: each turn the dark rock cross pattern (tiles $10-$13) goes center -> gray arms -> all gray -> back in 7 steps, about 14 frames. CHR rewrite only (32 bytes per frame). While walking it resets to the bright step and continues. field_rip.s no longer called (kept in ROM)).
Use Plan B (twinkle). The whole background is noisy, so change only one half of a checkerboard.
1.75 (pulse.s: of the 4 dark rock tiles, only the top-left/bottom-right or top-right/bottom-left pair changes (an 8x8 checkerboard on screen). Center glows light gray, arms gray, then back over 8 frames. Pairs swap each turn. No new tiles).
Make the twinkle subtler.
1.76 (pulse.s: arms stay dark; only the center dot goes gray -> light gray -> gray -> back over 7 frames).
Twinkle only up to gray.
1.77 (pulse.s: the center dot just turns gray and back, 6 frames).
Add 8-direction arrows to the checkered twinkle; turns without movement (attack, search, etc.) show a circle.
1.78 (pulse.s: direction from the hero's position change over the turn; more than 1 cell or no move = circle. Marks are 9 gray (color 2) tiles overlaid on the cross (144 bytes). Shown 6 frames then restored).
While moving continuously, don't blink; keep showing the same direction (if the next step comes before it fades, leave it up).
1.79 (pulse.s: each turn resets the remaining time to 16 frames (held walking is about every 13 frames). Redraw only when the direction changes. Pair swap only at the start).
The arrows stand out too much; use the original pattern color and overlap so the heading is only vaguely visible. Also the arrow stays after returning to a floor even when standing still.
1.80 (pulse.s: marks are rearrangements of the cross dots (color 1, about the same dot count): up/down/left/right are ^ v < > with one dot removed, diagonals are corners, staying is +. When not in play the state is kept and restored on return).
Monsters are red as a rule. Aim to make every one unique, including shopkeepers and guards. Start with individual art for small humanoids and dragons/lizards.
1.81 (18 kinds in tools/mon_uniq.py: gnome, hobgoblin, kobold, homunculus, imp, quasit, leprechaun, tengu, nymph, nurse, shopkeeper, guard, cockatrice, chameleon, mimic, xorn, zruty, dragon. Placed in bank 16 (gen/spr16.bin); sprite.s reads picture numbers 32+ from bank 16 (SW_HI $20). Overview docs/art/char_chips.png via tools/char_sheet.py).
Chameleon: drop the tongue, use big eyes and a tail. When meeting monsters in a room, on entering, or at doors/corridors, show their names in the bottom UI. Make the common floor 1-5 monsters unique first.
1.82 (pulse.s sn_new: each turn, by kinds newly seen: "{M} is here!", "{M} and {M2} are here!", "Monsters besides {M} and {M2}!" (dogs and disguised mimics excluded; memory cleared on floor change). The post-use hint runs one frame first so this message is queued first. 14 more unique sprites (jackal, giant rat, bat, acid blob, orc, zombie, freezing sphere, little dog, yellow light, giant ant, fog cloud, piercer, quivering blob, violet fungus): 32 total).
For 3 or more kinds: "{M}, {M2} and more monsters!". Continue the unique art.
1.83 (remaining 31 kinds get individual art: everything except the floating eye (already unique). Banks 16 (32-73) and 17 (74+, SW_HI $80). mon_pic renumbered from each bank's start, x3).
Color the top HUD icons: heart red, meat (looked like a stomach, redrawn) red with white bone, star yellow.
1.84 (mwin.s gl_color: in the top number row glyphs, heart and star clear plane 1 for color 1; meat makes only the bone color 3. The star cell ($2BEB top-left) uses palette 3 (yellow) in status2.s. Adjacent numbers stay white in separate cells).
Side-facing monsters aren't distinctive: make them chunkier or add features.
1.85 (redrawn: centaur (bow, thick horse body), large dog (big head, tongue, collar), unicorn (white horn, mane, tail), rust monster (segmented shell, antennae, tail), dog (white chest, curled tail), little dog (big head)).
Gray dither on the stairs, sword and shield? If not, make just the sword hilt yellow and a yellow square inside the shield.
1.86 (using the same palette's red: sword below the guard, the square inside the shield. mwin.s gl_color unified into a per-glyph table (gl_msk: 1 = color 1)).
Give the heart a white highlight too (two colors wouldn't fit well).
1.87 (gl_msk: top-left of the heart (glyph rows 3-4, column 1) white).
Add a yellow accent to the stairs (yellow in the B shape).
1.88 (trial: stairs cell ($2BE8 top-left) palette 3, only the top and right corners of the steps yellow, the rest black (gl_bk)).
Stairs back to white after all, with red step corners (same red accent as sword and shield).
1.89 (stairs cell back to palette 1; top and right step corners red).
The warrior's (side view) sword looks bent. Make the stairs' ground and left vertical line a red dashed line.
1.90 (own_art.py HERO_DOWN/UP/LEFT: one straight blade, guard (3 wide), grip, pommel. gl_msk: stairs' left vertical line and bottom red every other dot).
The dark-area pattern (cross) is too strong; soften it (Plan A: two diagonal dots). Match the direction marks too.
1.91 (gfx.py stone: .a.. / .... / ...a / .... (also for the black part of corridors). pulse.s: idle art the same; marks are a single arrowhead in 8x8 (> ^ and corners, staying = diamond)).
Speed up everything around menus.
1.92 (body of enter_level_noloop moved to bank 15 (fastui.s; 123 bytes freed in fixed bank). After a window only the 4KB map table is reloaded; sprites untouched. During rebuild, skip attribute calc for palette-0 cells and trap search when no traps are found (rebuild 22 -> 18). Returning from full-screen windows to main keeps display off and flushes more VBUF, showing when done (60 -> 38). Window text gets +80 VBUF while sending glyphs only (fast path 300 cycles/char; NMI to last write max 1983/2273); nametable runs share one header (opening main 31 -> 25). gl_color moved to bank 15).
Speed up menus (continued).
1.93 (gc_tile search from the end at 11 cycles/entry (126k -> 62k). Direct X/Y-preserving versions of F3 vb_put, vb_hdr, vb_flush_if (vb_putx etc.); F3 vb_fits, isglyph, wait_nmi called directly. gl_color only for the top number row glyphs. Restoring under windows and window prep also skip trap search when none found (EL_TR = $80). Open main 31 -> 24, items 26 -> 21, action -> back 26 -> 21, items/identified -> back 55 -> 38, at feet 15 -> 12, close window 14 -> 12).
Make going back from the action window to items faster (don't redraw the whole inventory).
1.94 (at the start of vb_draw, nametable rows 4-23 and attributes are saved into the CQ area; B writes them back and redraws only the hint and description (fastui.s vb_save/vb_restore, invui.s inv_foot): 21 -> 13. The flag moved to $78E3+ (free space after OD/KD), cleared at title. ingame = 0 and display off until drawing finishes).
Corrupted graphics (part of a room floor drawn with rock tiles).
1.95 (already fixed by moving the address in V1.94 and clearing it at the title. The map data was correct).
Frozen for ages after being stared at by a floating eye.
1.95 (holding a button while paralyzed now takes 1 turn per frame with 2-frame moves: 150 turns 37.6 -> 15.8 s).
A floating eye flashed on screen for a moment.
1.95 (sprite.s SL_NEW: a slot for a new kind isn't drawn until its graphics are sent).
Graphics corrupted when the cursor was on a mace.
1.95 (reverted to searching from the front, 13 cycles per entry).
The inside of the grave window turned into a grid.
1.95 (for the grave (MW_NOSK), sets 1-5 are not lent out).
Stop rewriting the background every time you move.
1.95 (footstep marks removed. Monster names on sighting are kept).
Swap the A role with B; menu on START only.
1.95 (A = search / go down / go up, B = nothing, START = menu. Also updated the top hint, web button labels and the manual's controls page).
A damage-like effect at game start and on floor change.
Fixed in 1.94 (red flash 5 -> 0 frames on both start and floor change. Only the footstep sound remains).
A ghost looked like a shopkeeper (sprite).
1.96 (besides the V1.95 wait-before-drawing, a new kind's slot is sent whenever it fits even when busy: 3-16 frames until the graphic appears).
Continue optimizing.
1.97 (unused sprites cleared only up to last time's extent (fully every 16 frames). Visibility checked on turn advance and every 4 frames (BO_SB): bo_build 717k -> 502k cycles. rv_see moved to bank 13 with vis_rings; seen cells skip see_cell. Teleport control tctrl.s moved to bank 15 (540 bytes free in bank 11)).
Continue optimizing.
1.98 (483 bytes of title code moved to bank 15 (titlefar.s; 471 bytes free in fixed bank). Status line 1 updated on turn advance and every 8 frames: CPU 64.8% -> 57.7%. o_at / eg_scary skip searching when there's no floor object flag (OBJBIT) (o_here): per-turn slowdown 8.0 -> 5.8 frames).
Continue optimizing.
1.99 (called only when the turn or floor changes. UI_MODE set to 0 on the fixed side each time).
Continue optimizing.
2.00 (only up to the highest live monster slot BO_TOP: bo_build 502k -> 287k cycles (originally 717k). Traps only up to the highest used slot T_TOP (fixed on floor entry and in maketrap, 32 at title). rn2 division in 13 iterations (same results for all inputs). Object search filters by distance before mi_wants).
Continue optimizing.
2.01 (bank 1 mf_quick: not called for cells with no object flag, trap or engraving (4 checks): per-turn slowdown 5.71 -> 5.53. Monster item-search loop runs only up to the highest floor-object slot counted each turn).
Does the original also have items in unreachable places?
Not a bug.
Ask yes/no not in the bottom UI but in a decorated window over the field. No shows nothing; Yes shows the result in the bottom UI (e.g. 'You attacked the shopkeeper!')
1.44 (rooms.s yn_win: uses the menu window system (borrowing free tiles from visible cells on open), question, Yes, No (default No) and 'B Back A Confirm' at columns +8 to +23, rows +2 to +11. Used for shopkeeper, guard, pool, surface, ice box (3) and guard name. Button hints removed from question text. Width 16 to match mw_rows RUNB)
The surface question should be 'Go up to the surface?' / fit submenu frames tight to the outer edge, removing the black border (keep rounded corners)
1.45 (frame lines on the outermost pixels of the tile (row 0, column 0), inside all mesh; corners stay rounded; for all windows)
Sub windows should deliberately overlap instead of sitting adjacent (shows which is active)
1.46 (Underfoot and Return to title windows at columns +8 to +21, overlapping the main menu's right 2 columns. Main grayed at columns +0 to +7; on close only the non-overlapped part is restored and main columns +8/+9 redrawn. Death code moved to OVL to free bank 14)
Avoid 16x16 when overlapping (text shifted 8 dots vertically, 16-dot steps horizontally) / when only going back, show 'BA Back' / sub windows as 'chosen item, newline, contents, hints' / message window mesh dark gray (B)
1.47 (Underfoot and Return to title windows at rows +2 to +17, aligned to 16x16, text on even rows (8 dots below main): header -> blank -> content -> hints. Empty Underfoot also closes with A; hint and Discoveries show 'BA Back'. On close main columns +8/+9 redrawn with mw_tile. While a sub window is open, palette 3 mesh becomes dark gray $2D (previous message line too); the list behind the inventory action window is dark gray only inside the frame)
Don't change the top and bottom UI on the death -> grave transition (a leftover from the old spec?)
1.48 (grave text borrows tiles from invisible cells like menus (mw_prep; no main window so all cells checked with MW_NOSK); top/bottom keep the 2-row layout)
When a window has button hints, it takes priority: remove the top HUD hints / check how button displays relate to the UI
1.49 (top hints blank during the grave, yes/no windows and direction prompts (MW_ONF skips st_ahint while a window is open; st2_service during the grave wait). Added 'S Up' to the direction hints. Title window shows only 'A Confirm' since B does nothing)
A rat corpse doesn't fill the stomach? No light blue shows
1.49 (FC: any nutrition at all shows at least 1 dot of light blue in the preview)
Common window rules: width = longest line + 16 dots each side, hints right-aligned / sub windows as 'triangle chosen item, contents, hints'; choosing turns the arrow into a down triangle and grays the parent / the gray in the action window cuts off midway (16x16 limit?)
1.50 (action window and Underfoot/Return to title subs: width from character count (wlay.s vb_w, mwin.s mm_sbox), edges aligned to 16x16, header 'triangle name', content and hints every 16 dots. Outside the window grayed per 16x16 (vb_attr), gray triangle on the parent's chosen row. Inventory hints 'B Back A Confirm S Category'. Stairs animation lvanim.s moved to bank 7)
(found while working) The inventory page count iv_pagen was called from the wrong bank
1.50 (called via F4)
Menu frame tight (don't merge the gray background with the top HUD), corners with only 1 dot missing, sub corners the same / start 8 dots lower with 6 items, list right up to just above the UI
1.51 (frame rows 6-24: top line y = 48, bottom y = 199 (just above the bottom UI). 6 items per page (rows 8-18), Discoveries 7 rows; action window gray down to the frame's bottom line (bottom UI rows read and kept). All frame corners (inventory, windows, grave, title) miss 1 dot. Page count (1/2) now appears. Next-page arrow calculation x8 -> x6)
Apply the common window rules to the remaining screens (yes/no, title, grave, class selection)
1.52 (yes/no: question 16 dots from the left, width from char count (RUNB 16 -> 24 tiles, $77E0). Title: columns 10-21, 'B Back A Confirm' (B returns to PUSH TO DUNGEON; 4 more glyph sets), PUSH TO DUNGEON wider than the window is erased with crosses. Grave: width 24 -> 20 (longest line 15 chars + 16 dots x 2), hints 16 dots inside the right frame. Class screen on hold: the description window isn't wide enough)
Join the windows on the class screen
1.53 (one window (columns 0-31, rows 6-27): list from column 2, description columns 14-29 unchanged, hints bottom right (row 25), cursor x 16)
Title: a sonar effect with three 8x8-patterned rings of different densities spreading from the center. The march below steps slowly one step at a time with a footstep and ripple per step (continue up to the Continue/New Game window)
1.53 (ripple.s: background cross cells split into 22 band tiles by distance in 8-dot steps ($D8-$DF, $F0-$FD), only the band tiles' patterns are rewritten (up to 6 per frame). Rings are dense/medium/light, 8 dots per 3 frames. March: one step (8 dots) per 32 frames, SFX_STEP and a ripple on each landing)
Use the title's ripple effect for stairs and falling too
1.54 (floor card black background replaced with band tiles $C0-$D5 (avoiding stair stones $F0, $F1). Stairs ripple every 2 steps; falling ripples when the hero and dog land. Work RAM borrows the class screen cache CL_C1 ($0780))
Fill the black with the cross mesh for stairs and falling like the title, and make the ripple lighter
1.55 (card background also crosses (rows 28-29 too), rings use the light set (cross + 1-row dots, 2-row dots, filled crosses). Title unchanged)
Title menu with Continue, New Game, Guide and Credits (text and presentation for Guide/Credits later)
1.56 (after PUSH TO DUNGEON always a window: 4 items with a save, 3 without (no Continue). Window from row 10; with 4 items the 'Based on Hack' row also becomes ripple bands. Guide and Credits only play a sound for now. Glyph slots widened to sets $01-$07. title_cursor moved to bank 7 (CODE full). Test scripts press one extra time)
Floor revisits: remember only the kill count and reduce initial monsters / remember dropped items (always the ice box) / dogs left behind and shopkeeper anger
1.57 (lvsave2.s (bank 10 after glyphs, using the $7C00-$7CBF distance table): initial monster kills at 5 bits per floor x 28 floors = 18 B; on regeneration that many are removed from the last-created slots (not shopkeepers). Items dropped with Drop get OF_DROP, up to 16 total remembered on leaving and placed back on return (overwritten in order when full, never the ice box). Dog: floor, position and HP remembered and restored on return (becomes an ordinary monster after 250 turns). Shopkeepers: floors where angered or killed remembered)
Title Guide: history, the original, basics (sprites), port features (original -> port), assets and licenses, credits (port by 125 Inc. SUZUKI08 + AI). Left/right for pages, 'BA Back', keys work mid-draw, remember the viewed page, SELECT for contents
1.58 (loc/kaisetsu.txt, 17 pages -> encoded by tools/loc.py into KAI (bank 10). kaisetsu.s: one full-screen window (no top UI, so tiles go to text), buttons checked after each line, contents with up/down for chapters and A to jump, hero/dog/monster sprites on basics 3/4, last page viewed in SRAM $78D8. Guide characters excluded from the 1-byte code count (STR2 overflowed). 50 new glyphs)
Speed up drawing of guide pages (how?)
1.59 (upper and lower halves of a line each become one 28-cell write (kd_line), VBUF +35 on this screen, bottom hint glyphs kept. About 110 -> 48 frames per page)
Top right is garbled (showing 'Weapon?' when a dagger is highlighted in the inventory)
Under discussion
Isn't the death screen off-center?
1.60 (width 20 (columns 5-7) or 24 (column 4) chosen per camera position, within 8 dots of center)
No Credits menu (included in the Guide) / Guide contents shows 'arrows B Back A Confirm', other pages 'arrows B Contents A Next'; last page and contents loop
1.60 (title menu: Continue, New Game, Guide. Contents: up/down for chapters, A to jump, B to title. Pages: B to contents (cursor on the current chapter), A next. Left/right loop. Hints rewritten between contents and pages (both glyph sets sent first))
Sub menus will likely overflow horizontally (anticipating the English version), so handle it now
1.61 (sub windows (Underfoot, Return to title, yes/no) shift left by the amount they would cross the right edge (16-dot steps, max 8 columns: up to width 24 fits). MW_DX, MW_SUB; on close the overlapped main columns are redrawn with the shift)
Top-right glitch ('Weapon?' for unknown weapons) -> just '?' is fine
1.62 (when unknown, 'Weapon' removed and only '?' shown. Inventory bottom line inv_help moved from bank 13 to 10 to free space)
The alphabet in scroll names like 'scroll labeled ZELGO MER' feels off -> use Japanese-style looks (the English version keeps the original labels)
1.63 (25 scroll appearances: tattered scroll, pretty scroll, transparent scroll, old scroll... words that don't hint at effects, up to 8 chars)
Naming: no 'promising' etc.; choose from the real names in that class. If unidentified after use (quaff/read/zap), ask 'Name it? Yes/No' in a field sub window, then a list. Display as 'appearance (name?)'. Takes no turn (the original ddocall takes no time either).
1.64 (vcname.s, bank 8, far8: list in 2 columns x 5 rows, pages beyond 10, '(clear)' at the top if a name exists. ID bits 1-5 = index from the start of the class. The inventory 'Call' uses the same window (closes the inventory, on the field); gems can be named too. Added a prompt for wands not in the original. Not asked when the direction was cancelled)
(found while working) Zapping a wand doesn't ask a direction and says 'Nothing happens'
1.64 (remember the item index first)
Scene jump: monster houses (zoo, beehive, graveyard)
1.64 (web debug 'Floor': regenerates until one appears (4x speed), reveals the map and puts you on the room's door)
Menu text is misaligned
1.65 (zeroed every time a window opens (mw_prep). The naming 'cancelled' flag VC_SKIP overlapped the ripple work area, so moved to $07DA)
Put the bars inside rounded outlines with a black gap
1.08 (text.s emit_bar: row 3 top outline (ends pulled in 1 dot for rounded corners), 4 gap, 5-10 fill, 11 gap, 12 bottom outline; 1-dot outline and gap horizontally. Fill 5 dots at ends, 8 in the middle (HP 50, hunger and exp 18). The 8-dot bars on inventory and class screens use the same shape (G_BARL, G_BARR). Class screen ran out of glyphs and lost hints, so the glyph limit was raised)
Match the web button labels to the in-menu hints. No top HUD button hints while a menu is open.
1.09 (UI_MODE 3 = main menu/Underfoot: web shows A Select, B Back, START Close. Inventory SELECT shows the current order (category/newest/oldest). Row 2 hints (columns 23-29) blank while a menu is open (restored on close))
Can the first menu level have hints for both B and A and still be narrower?
1.10 (hints 'B Back A Confirm' (7 chars, same as the class screen); window 14 -> 10 columns (5 cells). Underfoot window to its right (+10 to +23). Web buttons Confirm/Back)
The screen blanks for a moment when opening the menu
1.11 (screen stays on and VBUF is fed per vblank: frame first, then glyphs (into hidden slots), then text. On close, attributes and cells under the window are restored and borrowed pattern slots restored from bank 0 (main.s mw_chr). Underfoot window too (B removes just that window). Cells hidden under the window aren't type-checked (open time 20 -> 6 frames). BGM (music.s) moved to bank 7 (after monster art) since bank 14 is full)
Stairs animation: start from the top step and go faster
1.12 (lvanim.s: down goes from the top step (x 104) to the bottom (152), up the reverse; speed 1 dot per 8 frames -> 0.375 dots/frame (t/2 - t/8, stops at 48))
Apply the menu design and rules to the title and class screens, and the clear/death screens
1.13 (title: Continue/New Game in a window (columns 9-22, rows 12-19) with 'A Confirm' bottom right; original, (c) and version at rows 20-24 (TITLE_ROWS); PUSH TO DUNGEON unchanged. Class screen: window (columns 7-24, rows 5-24), list up 2 rows, 'B Back A Confirm' bottom right (removed from top row 2, no stairs/face). Grave/escape (rp.s): framed panel (16 rows), white text, PUSH START -> 'A Next' bottom right, hero hidden when under the window. Bugs found in menu tests: special entries (Return to title, holy spell, Pray) didn't work because mw_off clobbered A (since 1.11); Discoveries broke from camera position and leftover sprites; Underfoot header overlapped the frame (name only, 11 chars))
Hero and monster graphics glitch
1.14 (BGM moved to section SPR7M (after monster art))
Title should first show only PUSH TO START, then Continue/New Game
1.14 (PUSH TO DUNGEON first even with a save; pressing opens a window (columns 8-23) with Continue, New Game and A Confirm without blanking the screen (mwin.s tt_menu, using slots unused on the title); B returns to PUSH)
Menu is slow to appear; sometimes the lines don't show
1.14 (start from 2 when column is 0, from 7 when misaligned at row 5; frame top, left and bottom auto-checked on floor 24; about 10 frames to open). Also: the holy spell letter loss is a request; VBUF is flushed in vblank whenever the screen is on
After 'Return to title', a separate 'Really return? Yes/No' with the cursor on No. No save-related message in the bottom UI.
1.15 (confirmation window right of the menu (mmenu.s mm_tq). Yes: silently save and go to title; No/B: close the window and return to the menu)
Can we avoid the 4-dot stop at map edges entirely? Keep it in 8-dot units with a fixed window position.
1.15 (follow_cam: camx 8-1016, camy 8-168 (the 8-dot edge is outer rock); when still, camx/8 and camy/8 are always odd, so windows are always at column 1, row 6)
Class screen: drop Random and the bottom text, window 1 row lower, order Fighter, Speleologist, Wizard, Tourist, Knight, Caveman, window narrowed to the text and moved left, class description on the right (original English name and traits)
1.16 (left window columns 1-14, rows 6-25; right window columns 15-30. 5 lines from the original hack.u_init.c HP, Str and items, using spare glyph slots (text in bank 3 STR since STR2 is full). Top 2 rows' glyphs not precached (IT_NOPRE))
BGM only through the title and class screen; it's too dark, speed it up a bit
1.16 (stops on entering the game (ingame); 80 -> about 95 BPM)
Death screen: button 'A To title', a blank line after 'Rest in peace', the walls turn red
1.16 (placement threshold measured at row 9; hint 'A To title', added a blank line)
Class description as [Name], Original: English name, blank, then the text
1.17 (left window columns 1-12, right 13-30 (16 chars per line). Text wraps at 16 chars (no punctuation at line start). Glyphs cached (each char once) in unused slots, and the description cache is discarded after drawing)
Title text shifted (8BIT CONSOLE EDITION, (c))
1.17 (TL_COL, IT_NOPRE and T_SV moved to internal RAM $0763, zeroed at title start)
Add and refresh walking and attack sounds
1.17 (footsteps: a small short noise each step; hit: whoosh + thud (noise sweep, low tone, heavy noise); miss: swish)
Original text in 'Original'; can the text extend to fill the window? Add something like 'this is how it plays'
1.18 (Original: English name. Body 5 lines (about 70-80 chars) with play style added. Name also cached and shares glyphs with the description; numbers and RAND cached first; all remaining slots used for the description (66 glyphs))
Description text is slow to appear; the dog's name shifts when selected
1.19 (clears batched, 13 chars common to all 6 cached up front, VBUF up to 135 bytes while sending glyphs (VB_XTRA; 150 overran vblank) -> about 30 -> 25 frames; no drift over 480 frames. Dog name aligned to the left window's column)
Descriptions: on cursor move show class name and Original, then when input stops and glyph transfer finishes show the description at once (the cursor feels sluggish)
1.20 (src/cldesc.s, bank 14 (11 full): the cursor moves immediately, only header and Original are written, the body is hidden with black attributes (palette 2 color 3). After 8 idle frames, body glyphs are sent up to 6 per frame in the background (input still read), then the body is written and attributes restored to show it at once. Cursor latency 5-7 -> 1-2 frames)
Drop the hint 'B (Search) can also find hidden doors.'
1.21 (removed the opening hint)
At the start item palettes were sometimes wrong (scroll with a red background)
Not fixed (awaiting repro)
Pressing a button while rapidly switching classes made item backgrounds orange (title palette)
1.22 (LV_TT = 0 at the top of lv_on)
Remove the black horizontal line on the controller
1.22 (landscape gold strip line .gbl no longer shown)
Garbled window text after returning to the title from the class screen
1.23 (cache cleared before drawing the window)
Make the initial Fighter description also appear after the transfer (so it's clear it's by design)
1.23 (on entry too, only header and Original first; body shown at once after cd_step finishes)
Opening the menu shows only text, without the black fill or frame
1.24 (mw_box sets 0 and mw_unbox 1 every time; pending cell/attribute rewrites flushed before drawing the window. Frame auto-checked 16 times right after arrival and on floor 24)
The frame still sometimes lags behind when opening the menu
1.25 (cache limited to 110 entries in-game; WM_X moved to unused mklev rectangle space at $6833. Frame auto-checked over walking, Items round trips and 60 Underfoot windows)
Add monsters to the title march to make it fancier (4 is fine)
1.26 (monster shapes 3 -> 9 (dwarf, orc, quadruped, flyer, blob, insect, snake, dragon, eye), varied colors, face RNG switched to an LFSR so the same face doesn't repeat)
On real hardware, dots in the black parts of menus (right of top row 2 and blank areas at the screen's right edge)
1.27 (each header counted as 4 extra bytes, limit 100 -> 90; vblank peak 1768 cycles on title, class, game and menus; 0 writes during rendering)
Sort out gaps and bugs in the menu spec table so nothing is logically broken, and implement
1.28 (Underfoot -> Describe fell into 'Really return?'; worn armor/rings could be dropped while keeping their effect -> Drop/Throw hidden, cursed wielded weapons refused; iv_stay out of range after Dip; holding against a blocked direction on the holy spell smudged it every frame -> only on moves, attacks and trap struggles; cornered monsters attacked on the holy spell; escaping by stairs while wrapped or engulfed; while wrapped, other directions didn't advance turns and spammed messages; mimics didn't stick; guard prompt moved to the shared yes/no; refusals (curse, two-hander, cancelled direction) don't take a turn; top row 2 turned into a red pattern when choosing a direction right after closing the inventory (since V1.20). Scroll of fire code moved to bank 6 to free CODE)
Instead of solid black backgrounds: top/bottom HUDs and windows in a navy-black checker mesh, dark field areas (unexplored, rock) as navy crosses without centers (prototype 3-2). Title and class screen too.
1.29-1.30 (color 2 = dark navy $01 (color 2 of window palettes 1 and 3), bar outlines gray -> white. Mesh added when sending glyphs (mwin.s gl_mesh); top row 2 is mesh + white bottom line, message window row 25 has a white top line. Window frames use 10 tiles (sides, corners, mesh) (mmenu.s mm_ftiles). Rock cells use the cross tile with palette 1. Inventory, Discoveries, grave, class and title windows use the mesh. Red inventory text via palette 3 attribute. Web $01 = 001E74. Left 8 dots of row 2 shown too)
Floor tile seams too strong: don't separate with black lines / ground hatch (unexplored crosses) darker gray / stair messages (floor card) in a framed window too
1.31-1.32 (floor: black grout removed, bottom/right faces as black dashes (same on item cells: gray/light gray on the right turned cream on item cells, so made black). Crosses use color 1 = dark gray $2D with attribute 0. Floor card is a window at rows 10-15 (white double line, navy mesh), text at row 12)
Music plays in the Items menu
1.33 (once the game has been entered (MU_ON = 2), no music until reset)
Class screen: hidden parts behind the description remain black; fill them with mesh
1.33 (hide color set to navy; the previous body is refilled with mesh one row per frame (cd_step). IRQ also stops outside the game)
Corridor backgrounds with the unseen pattern (only around the outer edge of the stone) / no white line under the top HUD, mesh in the row gap / no white line above the bottom HUD / single-line menu frames
1.34-1.35 (corridors: crosses only outside the outermost stone per row/column. Top row 2 uses only the upper half of the pattern table from $80 (mesh $FC, $FD); IRQ switches only that half without turning BG off (black gap between rows 1 and 2 gone). Frames are a single white line with rounded corners)
Equipped item color is inconsistent
1.35 (column 0 left untouched)
Submenus: put hints in the sub window, gray out the parent menu, and show what was chosen, e.g. 'Underfoot / Nothing'
1.35 (Underfoot and Return to title windows: header, content, hints bottom right ('B Back A Confirm' or 'B Back'). Main menu grayed via palette 3, hint row filled with mesh (tile by tile since it crosses nametable column 32; it used to break the frame's bottom right). Inventory action window has hints at the bottom, the list behind uses palette 0 (navy mesh, gray text))
When falling through a trapdoor, sprites should pass behind the panel
1.35 (hero and dog go behind BG while falling, passing behind the floor-card window)
Make side doors look the same as floor / horizontal cracks (hidden doors) show the top-face graphic until found
1.35 (vertical-wall door doorv = floor graphic; wall top/front treats hidden doors below as wall)
Class screen: let up/down work even while the description is drawing (feels heavy)
1.36 (header and body processed in stages each frame: mesh the row -> send glyphs -> write one row at a time; 6 rapid presses all register)
The pet name's 3rd character sometimes isn't erased
1.36 (name area of 4 columns x 2 rows cleared with mesh first)
Map crosses behind the title and class screen too / descending-stairs animation at the start of floor 1, footsteps during floor changes / in-between frames for the kill marker expanding / turn the field red on death
1.36 (background uses cross tile $FE, title palette color 1 dark gray, crosses around title text and logo too. New games use LV_KIND = 1. Footstep every 8 frames during the stairs animation. Kill marker: first 4 frames at 1/2, next 4 at 3/4 size. Death: palettes 0 and 2 shift to 3 shades of red)
Inventory action window: its top overlaps the list frame -> lower it 1 row and use the same 'name / actions / B Back A Confirm' as Underfoot. When hovering 'Remove', show the change in the top HUD in color.
1.37 (window top = the chosen item's row, columns 12-26, header is name +n. 'Remove': armor shows AC -> after removal (current + that armor), weapons show weapon +n -> - (red). Death code moved to bank 14 to free 13)
No arrows for changes in the top HUD; color only
1.38 (only the AC n / weapon +n or - values in color; current +n is used for comparison but not shown)
Just color in the top display is fine
1.39 (armor: row 1's AC number replaced with the after-wear/removal value in color; other items keep the original number. Weapon +n stays at the right end as weapon +n / - since the top HUD has no slot for it)
Inventory: always show the description of the highlighted item (keys work mid-draw). Remove the Describe action.
1.40 (7 items per page, type description (md_*) on rows 20-21. Each frame: clear with mesh -> up to 6 glyphs -> write. Stays white even with the action window open. Chars without a free glyph slot become mesh (gc_miss))
Make the stairs look like they sit on normal floor / let the floor show faintly through the black inside doors
1.40 (down stairs: 2 dots outside the frame use floor art; up stairs: outer ring floor art. Horizontal-wall doorway columns 5-12 checkered black and floor)
Make descriptions fit the OK pattern (28 chars, at most 16 distinct glyphs)
1.41 (weapon, armor and potion texts reworded (15-16 glyphs). The 21 bar glyphs of the top 2 rows are no longer precached, only when used, freeing cache for descriptions (all shown even on item-heavy pages). Spec table V140 'Descriptions' sheet)
Food gain color: the bar outline turns light blue too
1.42 (bar glyphs color only the fill pixels on rows 5-9 with color 1, outline stays white; no sprites used)
After the white hunger bar, the gain preview when eating looks wrong (white gets shorter with light blue on top)
1.43 (the boundary glyph is light blue only right of the current dots: pattern shifted by the current dot count)
Sound effect when searching breaks a wall
1.43 (SFX_KILL when finding a hidden door or passage)
Page count 1/2 on the left of the button-hint row
1.43 (n/m at column 2 of the hint row with 2+ pages)
Gray out the description while a sub window is open, and hide the part under it
1.43 (description rows also gray; if the window reaches row 20, only columns 1-11 are cleared and up to 10 chars shown)
Not 'Attack Watanabe?' but 'shopkeeper' whatever the name
1.43 (m_shk_ask 'Attack the shopkeeper?')
In shops don't pick up directly; show 'X is underfoot' and pick up from the Underfoot menu
1.43 (inside a shop (u_inshop) walking doesn't pick up: 'There is X underfoot.'. Scroll of taming code moved to bank 6 to free CODE)
The menu is broken
1.14
Fill under the floor-change stairs with blocks. No floor walking; the hero and dog move along the stairs (no dog if it isn't following).
0.96 (lvanim.s: area under the steps (columns 13-18, rows 19-22) filled with stone tiles. Down: center x 124 -> 142, up: 128 -> 110, 1 dot per 8 frames, dog 18 dots behind (only with dogkeep). Bank 11 overflowed, so lvanim.s moved to bank 14)
Solid blocks under the whole staircase look heavy; only the green part (right under the steps)
0.97 (lvanim.s: stone only at columns 13+2i, 14+2i and rows 19+2i, 20+2i for block i)
Choosing the dog's name with left/right leaves junk under the top HUD
0.97 (name area reserves 3 characters)
On phones, rotating from portrait to landscape shifts the button touch positions / they stop responding?
0.97 (re-layout at 60, 250, 600 and 1200 ms after rotation and release any held buttons)
(found in demo) A wizard with under 7 HP keeps fleeing without healing
0.97 (healing moved to the start of dochug)
Varied sound effects and a very lo-fi, long-phrase BGM that doesn't get boring
0.98 (tools/music.py: 80 BPM swung eighths, 4 sections of 7th/9th chords arranged A A' B A'' C B' A''' D over 64 bars (about 3:12), melody repeats with variations (seed 1985). src/music.s (bank 14): pulse 2 = melody (12.5%, decay, vibrato), triangle = bass, noise = hi-hat/snare (yields to SFX), driven every frame from wait_nmi. New SFX for quaff, zap, teleport, gold, menu click; hit, miss, kill and get-hit vary pitch slightly each time)
Corridors are too dark to see on a CRT; center them on gray dither
0.99 (corridor stone is a checkerboard dither of gray $00 and dark gray, top edge gray; hidden passage trace unchanged)
When room lighting spreads, show unrevealed parts as a faint version of the current tile instead of black
1.00 (look 13 = faint floor (sparse dark-gray edge and grain). While revealing, unseen floor of the current room is drawn faint (not marked as seen). Check in reveal.s rv_blank (hidden-passage trace check also moved out of the fixed bank to free space))
Implement the top 2 rows as Torneko-style plan B (no meter for floors up to 30, all meters the same length, stairs and monster face right-aligned)
1.01 (row 1: floor, current HP, meat, Lv, Str, AC, gold right-aligned (src/stline.s, bank 14). Row 2: HP, hunger and exp bars of 4 cells (28 dots) each (1-dot gaps separate the three), stairs direction, monster face and button hints right-aligned. Bar colors via row-2 attributes. Inventory and class screens use the same top layout (hunger preview over 4 cells). FC trade-off: max HP isn't shown as a number but as the bar ratio (to fit 30 cells))
Layout per the layout sheet (8x8 = 1 cell): only the HP meter large, HP number right-aligned to the max's width
1.02 (row 1: floor 1, heart 4, HP 5-7 right-aligned, meat 9, Lv 13, Str 17, AC 21, gold right-aligned. Row 2: HP meter 7 cells (screen 1-7, 52 dots), hunger 9-11 and exp 13-15 at 3 cells (20 dots), stairs 17-18, face 20, buttons 24-30. Bars use s2_bar with a selectable cell count. Gold always at the right end (overwrites AC when Str 18/** and AC are long). Sheet docs/HACK_画面レイアウト_8x8.xlsx updated)
Menu cleanup: B is Search, A always opens the menu, Search becomes Descend/Ascend only on stairs. No A/B choice in the bottom UI; act directly from B.
1.03 (field: A/START = menu, B = Search (repeats when held), on stairs B descends/ascends on press. Only B's hint at row 2's right end (Search, Descend, Ascend, Wait; after death A Next). Write, Pay and Drop gold are menu entries (Write near the bottom; Pay/Drop gold only in those situations, also in row 2). No 'Write? (B No A Yes)' prompt. Web button labels, opening hints and guard lines updated)
Put 'Write the holy spell on the floor with a finger' above 'Return to title' in the menu. The text is 'Holy spell Elbereth'; smudging replaces letters with boxes from the end, and stepping on it shows 'Holy spell E....' is written on the floor. The floor tile is the current pattern plus an E (unchanged when worn).
1.04 (menu: 'Write the holy spell with a finger' then 'Return to title' (Pay/Drop gold after). Always written in dust with a finger (no tool prompt). Reading: for Elbereth only, eg_s1-8 by remaining letters (1 even at 0); other text (vault hint) as before. Floor look 14 = engraved floor (main.s ek_kind -> engr.s eg_k), redrawn when written or erased)
Proceed with the menu prototype (docs/mock): A opens the main menu, Items opens the list, picking an item opens its action menu. Don't shift the field.
1.05 (src/mmenu.s, bank 14: main = Items, Underfoot, Write the holy spell, (Pay, Drop gold), Pray, Discoveries, Return to title (left window, columns 0-13). Items = item-only list (B to main, START to close); the action window opens vertically right of the chosen item (Cancel replaced by B; added Describe: one line per type). Underfoot = the item underfoot with Pick up/Describe, or nothing / stairs / holy spell. FC trade-off: the field behind windows isn't shown (window glyphs and map art compete for the same pattern table))
Show the field behind the windows (beat the 8-bit console's limits); window frames as white double lines
1.06 (src/mwin.s: when opening a menu, only the tile types visible outside the window stay in the pattern table; other slots (unused types etc.) are lent to the window frame and text (gc_tile jumps to free slots). Window written to the nametable at the current scroll, edges aligned to cells with palette 1, sprites under the window hidden; map redrawn on close. Main menu and Underfoot over the field; Items (list) stays as before due to many glyphs. White double-line frame (color 3, no palette change), inventory screen too. Describe code moved to bank 12 (14 full))
Rename Underfoot, 'inscribe the holy spell', 'Return to title' -> shorter; round the double-line corners
1.07 (3 wordings changed; the 4 corner tiles are rounded double lines (inventory screen too))
Stairs animation: no floors above/below, just the steps. Show the looks on the class screen (front view first).
0.83 (removed the landing and lower floor; class screen shows front views of the 6 classes (from bank 8 into sprite pattern slots 4-9), shared palettes: Fighter blue, Knight silver, Wizard purple, others brown; cursor left of the art)
The floor-change animation ignores the character's palette. Stairs with 4 steps per block, fading in and out during the descent, slowly.
0.84 (the black screen uses the class colors too (lv_dark swaps the title palette as well). Step tile $F1 (two 4-dot steps) gives 4 steps per block, 12 total; character moves 1 dot at a time over 150 frames, fading in over the first 16 and out over the last 16 (falling too))
Continue (remaining port items): 2. saving visited floors (simplified)
0.84 (src/lvsave.s: items present at generation are flagged (O_FL $20); on leaving, missing slots are stored as 64 bits (floors 1-32) and removed when the floor is regenerated, so picked-up items don't reappear. Message queue cut from 8 to 4 lines to free space. FC: monsters, seen map, doors and dropped items aren't restored)
Continue (remaining port items): 3. bones (simplified)
0.85 (src/bones.s: on the death floor (2+, skipped if !rn2(1+floor/2)) stores the hero's position, up to 6 items (80% cursed, real amulet becomes fake) and gold (carried + floor x rnd(30)). In a new game, reaching that floor 1/3 of the time spawns a sleeping ghost with the items and gold (in a free spot if the layout differs). Stored in the 32 bytes freed by limiting floor saves to floor 28 ($7260, marker $B5). FC: the items only last while you're on that floor)
There's nothing left to do but wait
0.86 (holding the button repeats one turn every 4 frames after 24 frames = fast-forward)
Show a kill marker like a yellow anger symbol when a monster dies?
0.86 (src/klmark.s: when the hero kills or a visible monster dies, a yellow burst appears on that cell for 24 frames; art in unused potion tiles 48-49 (symmetric, right half flipped); sprite palette 3 color 2 red -> yellow (wand ray edges also yellow). FC-only)
In portrait the game screen shrinks too much when the web keyboard appears
0.86 (while typing, the border and chin are dropped and the input is 2 lines so the screen stays bigger)
Status row 2 is always shown now, so don't assume the map below it is fully visible
0.86 (hero centered in the visible map: WARP_HERO + 8)
Button hints after death
0.86 (after death B is hidden and A shows 'Next')
After death, do the web buttons advance with both A and START in-game?
0.86 (after death A and START both say 'Next', B and SELECT show '-')
The web doesn't need D and B shortcuts
0.86 (removed letter shortcuts G (debug), B and H (report); Insert, Delete and F8 remain)
Enforce the physical layout (A right, B left) everywhere in-game / no arrow-type function hints
0.86 (all prompts put B left and A right (yes/no, guard, wand direction); class screen shows only 'A Confirm')
Auto-send per-turn processing time to the DB; consider basic speedups
0.86 (ROM: lag_n counts frames where main missed the NMI. Web: per-turn lag bucketed (0/1/2/<=5/<=10/<=30/more) with average, max (floor and turn) and level generation, sent to hack_tel turn at 30 s, 2 min, every 5 min and on close. tools/prof.js measured the CPU breakdown -> stopped converting gold to decimal every frame (floor 1: idle 25 -> 0, one search 8.5 -> 3.2 frames))
Continue (remaining port items): 4. teleport control
0.88 (src/tctrl.s: with a ring of teleport control, move a blinking hero with the d-pad and press A to choose the spot (rock, monster, boulder or trap spots say 'Sorry...' and go random); B = random. For level teleport, change the top-left floor number 0-40 with the d-pad and press A: 30+ is Hell (burnt to a crisp unless fire resistant, death cause 'went to hell'), 0 is the sky (escape if levitating, otherwise fall). FC: d-pad instead of typing a number)
(found while testing) Hint glyphs mix into the inventory action row; the last char of 'Read' goes missing
0.88 (rows 22-23 cleared; action row drawn leaving room for hint glyphs, prefetch dropped)
Title: VER up a row, everything from (c)1985 up one more row. Add more marchers as long as it doesn't flicker, like a ticker; new heroes random.
0.89 (version 24 -> 20, original 18 -> 16, (c) 20 -> 18. March moved to bank 11 (title2.s): 4 figures 72 dots apart; each new one from the left is a hero (random class) 60% of the time, otherwise a dog or monster. Art slots 2-5 can read bank 8 (SW_HI $40). Same 4 color sets as the class screen. Own RNG since rn2 doesn't run on the title)
Top HUD readability: buttons right-aligned in B, A order (SELECT, B, A). Search on A, Write on B. Meters 2/3 size (16 dots). Bar right after the heart, then 14/14. In menus right-aligned button hints on the bottom row. Remove the in-game save button; top of the menu is 'Return to title'.
0.89 (row 1: floor, HP bar nnn/nnn, hunger bar, stairs, face ... B (context action) A Search. Bars 3 -> 2 tiles (14 dots), yellow attribute only on the bar's 16 dots (heart and meat stay white). Field A/B swapped (web labels and text too). SELECT save removed; 'Return to title' at the top of the inventory (closes, then yes/no; auto-save means Continue works). Inventory hints right-aligned S, B, A)
ROM DL under DEBUG, NAME under REPO (same size). If NAME is SKZ08, enable a developer cloud save/load to the DB.
0.89 (both orientations: DEBUG, REPO / ROM DL, NAME in 2x2. NAME is the same as the report name (hk.name). With SKZ08, debug Records tab save/load 1-3 go to RTDB hack_dev/s1-3 (cloud save))
Continue (remaining port items): 5. tracks
0.89 (src/track.s: hero position recorded each turn, up to 50; the dog and humanoids (@ and lowercase) follow adjacent tracks when in a different room/corridor. Stored in unused internal RAM $0600)
NetHack-style room lighting: show monsters and items on entry, reveal the floor in 3 stages as you walk, in a wave from the entry point; small rooms all at once
0.89 (src/reveal.s: entering a lit room shows walls, doors, items and monsters immediately; floor revealed up to distance ceil(D*stage/3) from the entry, stage +1 per step; D <= 2 reveals all. The first room on arrival is fully shown)
(found while testing) Status row 2 glitches while 'Return to title?' is asked after closing the inventory
0.89 (st2_tick in yn_wait; locked so holding A after answering doesn't search)
Floor 10 with 12 monsters lags even when idle (half the frames) and each turn drops about 50 frames -> lighten per-frame work
0.89 (build_oam moved to bank 11 (src/oam.s); visibility calls bank 1 directly; sprites added in last frame's order so sorting is almost free (same-y order hero, slot, tail kept); monster sprite positions return early when still; row 2 rebuilt only when Str/AC/Lv/exp/gold change; HP/hunger bars copied from cache when unchanged; row 1 built in alternating halves. Floor 10 with 12 monsters: idle lag over 200 frames 100 -> 0, walking per turn avg 45 -> about 10, max 143 -> 30. Same visuals (face blink may be 1 frame late))
The dog's body looks thin from the side; make a round dog that looks chunky from the side too
0.89 (side-view dog (tools/own_art.py DOG_RIGHT, left is mirrored): body 2 -> 5 rows thick, rounded back and belly; legs, tail and face unchanged)
(summary) 0.89 was released separately by the main and speedup sessions
0.90 (merged both: right-aligned hints and 2-char bars on top of the speedup's alternating row-1 halves and bar cache)
Continue (remaining port items): 6. naming, 7. wizard disguise. You may rewrite the database rules.
0.91 (src/vcall.s: 'Call' inventory action for unidentified ! ? = /; choose from good/bad/promising/dubious, shown per type as 'appearance (name)'; stored in bits 1-3 of the ID table. FC: no names for monsters or single items since typing isn't possible. Wizard disguise (clonewiz) never triggers in the original (rnd(5) == 0), so nothing to port. Bank 11 overflowed, so tracks, room floors and naming moved to new bank 14 (far14). Added hack_dev (s1-3) to the sandbox DB rules -> developer cloud save works)
'HP bar 14/14' is right. Put button hints at the right end of row 2 and tidy the UI.
0.92 (hints built in row 1 copied to row 2 columns 23-29 (hb_take). Row 2: Str 0, AC 6, Lv 11, bar 14-15, gold 17-22 (no comma above 10,000). No hints in menus; armor preview 'AC -> n' at row 2's right end (also fixed Lv and gold vanishing))
Continue with any remaining port items
0.93 (src/disc.s: at the bottom of the inventory, 'Discoveries' (identified appearances and called items, 8 each) and 'Pray' (3 turns helpless as in the original; FC: message comes first). Potion action 'Dip': pick an item, 'Interesting...'; dipping arrows, crossbow bolts or darts into a potion of sickness uses it up for +1 (max 7))
Choose the dog's name from candidates: dog art and name under the class list, selected with left/right. Top/bottom HUD on the class screen too, floor shown as G, each class's starting stats in the top HUD. B back (to title), A confirm. Don't delete the save at this point.
0.94 (role.s: top 2 rows built like the inventory (it_copy split out of invtop.s) with the selected class's HP, Str and AC, floor as G, 'B Back A Confirm' at the right end; hints on the bottom 2 rows. Dog (art 10) and names (Pochi, Shiro, Kuro, Hachi, Koro, Taro, Maru, Chibi; SRAM $67FC). The pet is called by name in messages (text.s ml_name). Save deleted only after selection)
The screen flashes dark each time I switch class -> can you redraw only what changes? Show the Tourist's gold as RAND.
0.95 (role.s cl_diff: top 2 rows' codes cached (internal RAM $0780); on class change only differing cells are written with the screen on (waits for NMI if the write ring exceeds 52). RAND glyphs cached first)
Remake the OGP image
0.61 (tools/ogp_shot.js captures a room with dog, monsters and items, framed with the landscape gold panel and red border, ROM logo and 8BIT CONSOLE EDITION on the right)
Landscape: lower the DEBUG/diagonal row and make buttons 1.2x taller (hard to press / unresponsive on iPhone), d-pad and buttons a bit lower, bold labels. Expansion controllers supported?
0.61 (top row down 5% of height, height 66 -> 80, d-pad and buttons toward the bottom, bold text). Expansion controllers: hardware reads both D0 and D1 of $4016, so expansion-port pads work. Web supports USB/Bluetooth gamepads (standard mapping)
On floor change, show 'B n F' during the fade and fade in. Make the hero, monsters and dog hop 1 dot when walking (the lack of motion feels weak).
0.62 (src/lvcard.s: current screen darkens in 4 steps -> black with 'Floor n' for 40 frames (floor generated meanwhile) -> brightens in 4 steps. While walking, the middle 4 frames are y - 1; monsters and dog are y - 1 while 4-12 dots from the target cell. FC-only effect)
Make the web button labels fit the situation (e.g. 'Confirm' on the title)
0.63 (switches based on ROM UI_MODE (0 game, 1 inventory, 2 yes/no) plus ingame, on_stair and multi: title = Confirm/Select; game = Search and A same as top right (Write, Descend, Ascend, Pay, Drop), Wait while unable to move; inventory = Back/Select/Sort/Close; prompt = Yes/No)
Portrait should also have the red frame like landscape, with function buttons inside. DEBUG is INS and REPO is DEL (key shown above the button); SND and DIA toggles on the left, DEBUG and REPO on the right (a bit lower in landscape). Button text scalable, one line, consistent with in-game.
0.64 (portrait: gold panel and red frame, controller red removed, SND/DIA bottom left, DEBUG (INS) and REPO (DEL) right. Landscape: SND/DIA and rewind top left, DEBUG/REPO top right (with INS/DEL above, shifted down). Insert = debug, Delete = report (G, B, H, F8 still work). Labels one line and shrink to fit; B is 'Search', A matches the top-right hint)
Design (chat: portrait buttons misbehave, pressing the d-pad triggers buttons; in portrait put everything except d-pad/AB above them; the top is all red frame)
0.68 (nearest-button logic excludes the d-pad and function buttons and applies only right of the d-pad. Portrait puts SND, DIA, DEBUG and REPO above the d-pad/AB. Red frame only on the sides and bottom, TV below the top safe margin)
Continue (remaining port items): gems for unicorns, food for dogs, digging in shops
0.69 (src/gift.s: a unicorn catches thrown gems (known real +5, unknown +1, ignores known glass); feeding a dog (d) tames it; starting to dig in a shop gets a warning, and when a hole opens the nearby shopkeeper grabs your unworn items. Calm shopkeepers don't count as 'monster adjacent' while digging. During repeated actions messages aren't aged by 5 turns)
Menu: a space before +n; when the cursor rests, 2 rows on top showing for weapons/armor the stats if equipped (better light blue, worse red, same blue); for food show the gain in light blue on the bar
0.70 (src/invtop.s: 2 rows above the inventory (HP, hunger in 8 cells / AC -> if worn, weapon current +n -> if wielded, gold), redrawn over 3 frames when the selection changes; unknown +n shows 0/?. Names like 'Two-handed sword +1' (list only). 9 items per page from row 6 (aligned to attribute boundaries). Wish screen stays at 11)
Portrait: stop using the controller color for the whole background; black background, emulator screen, and a controller holding all the buttons
0.70 (portrait: black background; a red-framed gold controller holding rewind, SND, DIA, DEBUG, REPO, d-pad and buttons, right below the TV)
Continue (remaining port items): ice box
0.71 (gift.s ice_box: if it has contents, asks to take them all out; asks to put something in and stores one food item. Food doesn't age inside (stores the corpse age difference). FC: food only, up to 4, contents kept in SRAM across floors)
Continue (remaining port items): engraving with weapons and wands
0.72 (choose what to write Elbereth with: B finger = dust, wand of fire (charge - 1) = burn (permanent), dagger/two-handed sword/crysknife/long sword/axe = wield and engrave (+n drops; up to (spe+3)*2+1 letters; a +0 two-handed sword gives 'Elberet' which doesn't work, as in the original; -3 or below is too dull))
Switching weapons needn't take a turn. The 2 rows on top of the menu should be the same as the field.
0.73 (wield takes no time (verb_ret0). The inventory's top 2 rows copy the field status line (st_want, S2_LO/HI) to the same columns (bars from 9 8-dot glyphs). Previews on top: hunger bar after eating (gain in light blue), AC -> if worn, weapon 'current -> if wielded' where the stairs direction goes)
Keep the turn cost for wielding (revert). On floor 1 I got stuck because Write blocked Search.
0.74 (wield back to 1 turn. Search: in the emulator B still searched after 'Write -> No' (5 presses -> 5 turns). Waiting for a report of where it gets stuck)
During menus keep the in-game messages on the bottom 2 rows. Monster face on the top row: red if a monster is in the same room, blinking when the hero takes damage. Tidy the top row so 'B Search' fits too.
0.74 (inventory: previous and new message at the bottom (rows 25, 27), hints at row 23, 8 items per page; glyph cache starts at $16 (the hints went missing with 2 text rows). Status row 1 tightened: floor, HP, bar, hunger 15, bar 16-18, stairs direction 20-21, monster face 22 (generated tile; red = monster visible in the same room (nearby in corridors), blinks 48 frames on damage), B Search 23, A 27. Row 2: Lv 13, exp bar 16-18 (under hunger), gold 20)
Portrait: put rewind on the gold panel like landscape, same button design, controller full width with d-pad left and buttons right, as large as possible without overlap
0.74 (portrait gold panel: row 1 SND, DIA / DEBUG, REPO (INS, DEL above), row 2 rewind '0S' and '1F' (same rounded long buttons as landscape), d-pad 98x3, A/B 104, START/SELECT 88)
Title: move Continue / New Game up 2 rows, Based on and (c)1985 up 1 row
0.75 (Continue/New Game (PUSH TO DUNGEON) rows 16, 18 -> 12, 14; Based on and (c) rows 20, 22 -> 18, 20; cursor at row 12; attribute table (gfx.py TITLE_ROWS) matched)
For trapdoors, show the message first and the floor card before moving on. Stairs: a side-view staircase (2 steps per block x 3 = 6 steps) that the hero then dog walk down (reverse for up); trapdoors drop from above.
0.76 (lv_card waits until the message queue empties (up to 150 frames) + 24 frames before darkening. src/lvanim.s (bank 11): staircase drawn with stone tile $F0 on a black screen (3 blocks, landing, floor below); hero, then the dog 22 dots behind (if following), walk down/up 2 dots at a time. Falling accelerates toward the floor, dog 40 dots behind)
Garbled text? Tidy the top 2 HUD rows and button hints. No left/right arrows; show arrows on both sides only when there are pages. The flicker is tiring, so always show 2 rows top and bottom. Menu frame only in the area outside the HUDs, which stay where they are in-game.
0.76 (field: always 2 rows top and bottom (text.s ms_idle). Inventory: status rows 1 and 3, messages rows 26 and 28 (same height as the field), frame rows 5-24 (light blue lines), list 6-21, hints 22. Preview colors come from glyph pixel colors (palette 1: light blue, red, white; plane-0/plane-1-only glyphs in reserved tiles $F2-$FF), not attributes; cache limit $F2 (GC_LIM). Page arrows on left/right only when there is a previous/next page)
Continue (remaining port items): disguised monsters
0.77 (a disguised mimic shows a chest graphic when its cell is visible (silhouette 9, palette 3 item color, doesn't move), and its normal graphic once revealed. The monster face on the status line doesn't go red while disguised. FC: the original's ']' strange object is shown as a chest)
Continue (remaining port items): long worms
0.78 (src/worm.s, bank 11: the tail follows the head's path (grows when moving, +3 HP per wgrowtime, otherwise the tip drops; when it can't move the tip drops and -3), tail segments are part of the body (m_at returns them); hitting the tail with rnd(20) (+5 for long sword, two-handed sword, axe) >= 12 cuts one at the tip or, 1/3 of the time mid-tail, spawns a new worm from the cut part (otherwise it falls off) with half the head's HP; each tail segment adds a 1-damage hit when it bites. FC: only one worm may have a tail, up to 12 segments, max 3 biting segments, tail drawn as sprites (silhouette 10). Sprite code moved to bank 11 and dog biting to bank 13 (fixed bank full); work variables reorganized)
On iPhone the area outside the frame is red (top strip)
0.79 (theme-color #0e0d12 and explicit black background on html/body)
Round the corners of the landscape red strip; make ROM DL a button and fit it into both layouts
0.80 (landscape: black outside, rounded red frame and gold panel inside (::before). ROM DL as a pill button: landscape below DEBUG/REPO top right, portrait right of rewind; it isn't caught by nearest-button logic)
Continue (all remaining port items in order): 1. class selection with a look per class. Move the title's 8BIT CONSOLE up a row.
0.81 (src/role.s, bank 11: class screen after New Game (Tourist, Speleologist, Fighter, Knight, Caveman, Wizard, Random) with the original's HP, Str and starting items (type 0 = random of that class; the wand of wishing becomes a wand of death); tourist gold rnd(1000) and Str to carry it; speleologists are born fast and stealthy; tin opener 1/25, 1/10. Magic whistle for wizards or Lv10; self-teleport at Lv6 for wizards, otherwise Lv10. 6 hero sprites per class (12 frames from down/up/left) and colors. Title EDITION at row 8. Tests press START once more on the class screen)
Shopkeepers should have Japanese names
0.35 (80 surnames (Tanaka, Yamada, Morimoto...) picked by shop type x 11 + floor (the original uses the dlevel-th entry of a per-type place-name table). 'Welcome to Morimoto's used armor shop!'. Shop names shortened)
Continue (selection screen for the wand of wishing)
0.36 (no typing, so choose on screen: left/right category (random, weapon, armor, potion, scroll, wand, ring, food, gem, tool, amulet), up/down item (true names even if unidentified), enchantment -3 to +3 (negative = cursed), quantity (up to 3 for food/scrolls/potions/gems, 19 for arrows to rocks). Wands get creation charges, the amulet is a fake, the wand of wishing is -1 or 0 with 9/10 (as in the original). Random = the original's empty input. Also fixed g_plus being read from bank 9)
Let me enter a name via a Name button in the report (remembered in localStorage)
0.37 (Name button under the report opens an input, saved to hk.name and shown as 'Name: X'; sent as name (up to 30 chars); added name to the sandbox hack_bug rules; name in tools/bugs.py info.txt)
Vaults and guards
0.38 (unconnected 2x2 vaults (rnd(dlevel*100)+50 gold each), reached via a niche with a one-time teleport trap 1/3 of the time; matches Python (makerooms secret, 16-bit rn2w). A guard appears after 50 turns inside. FC: name prompt is 'A Give name / B Croesus'; dropping gold is A ('A Drop' = original d$). The guard digs a temporary path to the corridor, fills it behind and vanishes. Bumping a guard asks too. Not yet: the 'ad aerarium' engraving)
Next (special rooms)
0.39 (zoo from floor 7, beehive from 10, graveyard from 12, swamp from 19, in the shop's else branch as in the original; matches Python. Entry messages like 'Welcome to David's treasure zoo!', monsters wake (1/3 in zoos). Added ghosts and giant eels. Pools: entering drowns you (escape with a teleport ring at Lv 6; scrolls blank); the FC version asks 'Enter the water?'. Monsters drown in pools, eels move only in water and weaken on land, eels wrap and drag you in. Boulders fill pools. Glyphs exceeded 682 so the rest moved to bank 10)
Giant eels (;) are invisible when spawned / sleeping during mklev differs from the original
0.39
The string in gen/version.inc is still V0.02
0.40 (removed)
Next (monster special attacks, the next item in GPT's review)
0.41 (giant ant A (Str -1), cockatrice c (1/5 hiss -> petrification in 5 turns), dragon D (1/6 fire ray), freezing sphere F (explodes), jelly g / homunculus h (paralysis/sleep), killer bee k / snake S / scorpion s (poison), leprechaun L (steals gold and teleports), nymph N (steals items; FC: stolen items are lost), n (heals you if wearing nothing), owlbear o (grabs and crushes), rust monster R (rusts helmet/metal armor), t (teleports), v (engulfs), vampire V / wraith W (level drain), x (leg wound), yellow light y (blinds), demon & (1/13 summons). Also armor magic cancellation (a_can). Not yet: engulfing, wizard, long worm, leg wounds)
A slider to step back through in-game commands one at a time: memory snapshots per action, maybe 32 in localStorage?
0.42 (emulator state (~28KB) captured whenever moves or floor changes; 128 turns in memory, last 32 in localStorage hk.rew (~0.9MB, every 8 turns and on close). Slider and arrows; playing after rewinding discards the future (like undo). localStorage is usually ~5MB, so about 100 turns fit)
Next (item effects: corpses)
0.43 (kills leave a corpse (2/3) or an item (1/3) 4/5 of the time; minotaurs drop a wand of digging. Eating corpses: rotten (food poisoning kills in 10-19 turns, cured by vomiting tripe), poisonous (ADKSVabhks), sickening (ELNOPQRUuxz), L N t teleport, W level, n and @ cannibalism (aggravate), d aggravate, I invisibility, B y confusion, D fire / F Y cold / k s poison resistance, c petrifies, a cures petrification, M mimics a chest. Rotten food effects, tripe, royal jelly, lizard, fortune cookies (12 FC rumors). doeat moved from fixed bank to bank 11)
Next (monster inventories, engulfing, shopkeepers following downstairs)
0.45 (monster inventory: floor-item table entries with O_X = $80 + slot. L carries stolen gold, N stolen items, dropped on death; L/O/D seek nearby gold, O/D/u nearby gems (DIST < 25); L flees if richer than the hero. Engulfing by trapper, lurker and purple worm (1/4): moving inside hits the engulfer, digestion kills in 5/7/12 turns, a cockatrice outside petrifies both. Followers: adjacent tame monsters, certain kinds and angry shopkeepers (up to 4). FC: hero sprite hidden while engulfed, other monsters wait, wands/throws still go outside, the shopkeeper becomes an ordinary angry monster on the next floor. New bank 12)
Equipping should swap even if already wearing something. Eat/drink shouldn't close the menu (close with B). Shouldn't 'Eat' be the first option for food?
0.46 (armor: takes off armor in the same slot and puts on the new one (not if cursed; costs extra time). Rings: with both hands full, removes an uncursed one. After eat/quaff/wield/wear/remove/put on/drop, returns to the inventory with the last message at the bottom; returns to the map on death, damage, floor change or long actions (5 turns for food rations, armor changes). Throw, zap (direction) and read (map, teleport) go to the map)
Frame around the menu
0.47 (gray frame around the inventory screen; line tiles written into the map's rock tile only while open (restored by load_chr); cursor and bottom line shifted right one char to clear the frame)
Continue (endgame: mazes, wizard, amulet, escape)
0.47 (floors deeper than 26-28 are mazes (no down stairs; arrive at the up stairs when coming from below). The first maze at 30+ has the Amulet of Yendor walled inside a pool with a sleeping Wizard and a hellhound; other mazes hide a wand of wishing under a boulder. Wizard wakes 1/40 when you hold the amulet, teleports, casts spells (sleep, summon, wake, haste, invisibility, lightning ray), steals the amulet 1/20 and reappears on the next floor. Going up from floor 1 = escape screen (with the amulet, or deepest floor). Python copy matches mazes byte for byte. FC: floor-1 exit asks first, summons appear adjacent, goldseen is 0 in mazes. Debug 'Amulet floor')
What is Elbereth? And continue (Elbereth, applying tools)
0.48 (when A has nothing else to do, asks 'Write Elbereth on the floor?' and writes in the dust. Adjacent monsters flee and won't enter the square (except @ and the Wizard). Walking or monsters standing on it smudge letters to '?'; stepping on it shows the text. Scroll of scare monster works the same and turns to dust on second pickup. Vault niche says 'ad ae?ar um'. Apply: camera (wake, blind and scare), whistle (wake), magic whistle (from Lv 10, brings pets adjacent), pick-axe (sideways: dig rock and walls; down: pit then hole to the next floor; maze floors and shop walls are too hard), tin opener. FC: only Elbereth, always with a finger; ice box later; digging stops if a monster comes adjacent)
Capture state at the start of each floor this run so I can jump back (and see the deepest floor), next to rewind. Turn -1, -4... with left/right buttons.
0.49 (rewind in two rows: 'Floor' arrows jump to the start of each floor this run (floor, turn, k/n, deepest), up to 40 in localStorage hk.fl, cleared on new game; 'Turn' buttons -10 -1 +1 +10. Slider removed)
List the remaining items and proceed
0.49 (luck now actually matters; x stings wound your legs (lower carry capacity, cured by time, speed potion or royal jelly); eating a floating eye grants telepathy (sense monsters while blind). FC: no moon phases (no clock))
Score matters
0.50 (kills: exp x 4, tripe 4, identifying by use 10. At the end: gold (minus 10% on death) + 50 x deepest + deep-floor bonus; on escape also adjacent pets' HP, real gems, amulet (5000, real one doubles total). Grave/escape screens show 'Score n' and 'Rank n in top 5!' or 'Best n'. Top 5 in SRAM $7E50. FC: 5 records. Status-line display after the two-row discussion)
Reorganize numbers: while moving 'floor, HP n/n bar, hunger bar (no number)'; when stopped 'Lv bar (to next), Str, AC, gold n,nnn'. Sword for Str, meat for hunger, no 'hungry' text, HP and hunger bars white->yellow->red, bars aligned (exp under hunger), reserve HP digits for the max.
0.51 (two-row status: while stopped the top HUD grows 16 lines for row 2 (Str, AC, Lv with next-level bar right under the hunger bar, gold n,nnn). Row 1: floor, HP right-aligned 3 digits / max 3 digits, HP bar, hunger bar, A. Bar colors via attributes: HP yellow under 50%, red under 20%; hunger yellow when hungry, red when weak. Row-2 glyphs drawn into free map pattern slots ($AC-$E7), pattern table switched between two blank lines inside the HUD. IRQs: 7 stopped, 5 moving. Message window moved down 2 rows (NT_B 26-29). Grave attribute table to bank 9, row 2 in new bank 13)
(no comment: grave screen)
0.52
Game over text: score etc. under PUSH START; rethink the vertical layout
0.52 (cause of death row 12, floor 14, score 16, rank 18, PUSH START 22. If the score base exceeds 4 x exp + 2200, it is corrected to 4 x exp (on floor arrival and at the end). Note: 42064 remains in that device's top 5)
Resize the controller so nothing overlaps, bigger in landscape; rewind as one unlabeled row '1F 2F 3F T0 T-1 T-2'; debug label 'Debu'. When the hero or dog takes damage flash the screen red and shake slightly; shake slightly on attack. 'You died' in red and the hero falls on his back.
0.52 (rewind in one row (details in long-press title); portrait lowers the controller until it clears the TV and shrinks if needed; landscape fills the window with a wider TV column (760). On HP loss (hero or dog): red emphasis via PPUMASK 6 frames + 2-dot shake 8 frames; on a hit: shake 4. Added color emphasis to the emulator. On death the bottom line shows 'You died...' in red (palette 3) and the hero uses a lying frame (front view rotated 90 degrees))
Show text on a black panel over the game screen. Title walls same color as in-game. On the title, the hero, dog and monsters march left to right on the bottom row.
Wall color and march in 0.53 (title BG0 same gray as in-game ($10), logo and EDITION raised 2 rows for space above PUSH START, rows 26-27 have hero, dog, goblin, jackal and bat walking right (title2.s, swapping in-game art)). Black panel in 0.54 (grave and escape no longer separate screens: a 24x12 black panel drawn over the game screen one char at a time, aligned to multiples of 4 so attributes (palette 2) don't spill, placed opposite the hero; rp.s)
Title: PUSH TO DUNGEON, drop the (c)2026 before 125 INC., 8BIT CONSOLE EDITION on one line, remove the top wall
0.54 (done as asked; the bottom of the grave/escape panel stays PUSH START)
Dedicated layout for phone landscape: diagonal off by default, game screen at an integer scale vertically, d-pad left, buttons right. Debug overlays the game screen and takes the keys (others too). On phones the keyboard hides the screen when typing: make the input 2 lines with the game screen above it, not dimmed (landscape too).
0.55 (phone landscape (touch, landscape, height <= 600): d-pad, TV and buttons rearranged, TV at 240 x n device pixels. Diagonal default OFF (hk.diag='1' turns it on). Debug overlays .glass and only G/Esc work while open. Report puts a 2-line input right above the keyboard (visualViewport) with the TV in the remaining space; headings dropped when cramped)
On iOS the controller renders wrong (d-pad vanishes and 'null' appears), buttons under the camera cutout can't be pressed (avoid the safe area), make the landscape d-pad and buttons a size smaller
0.56 (elements found regardless of their current position, re-laid out on rotation events, landscape placed inside env(safe-area-inset-*), d-pad and buttons x .78)
The DB should fetch only differences; no need to save everything; FIFO of max 256 entries
tools/bugs.py: fetches the key list (shallow) and downloads only new entries; over 256, deletes the oldest already-downloaded ones from the DB only (firebase CLI, --project sandbox-a113b). docs/reports keeps everything
Landscape: buttons are small, stretch them vertically; make the d-pad and A/B as big as possible since the safe area narrowed them
0.57 (rewind in 2 rows (floor, turn) with G/H stacked, 54px tall and full column width; d-pad and buttons fill the rest below (x .78 dropped))
Let's do the dog! (the original's dog behavior)
0.58 (src/dog.s: hunger (hungrytime: eating adds 5 x nutrition; +500 confused and 1/3 max HP, +750 starves), eats food underfoot or at the target, carries items and drops them near the hero (apport: learns when given food right after dropping), target choice (food -> item to carry -> hero / room door), avoids traps and cursed items, comes to whistles, stays near if you carry tripe. FC: no hero track (gettrack); carried items are left behind on floor change)
Buttons don't work on phones. Stretch rewind, direction and debug buttons vertically with softer corners; A/B, START and SELECT as large as possible, 115% tall, rounded rectangles instead of ovals; Debug/Report labels smaller as DEBUG and REPO
0.58 (accepts input on .phlR too; landscape rewind 72px tall with 9px radius, G/H 9px radius, A/B 104x120, START/SELECT 88x101 rounded rectangles, G DEBUG and H REPO (.68em text))
Make phones comfortable: landscape exactly like the mockup (gold panel, red frame, DEBUG/DB and rewind (0S, 1F) top left, d-pad left, diagonal and SOUND top right, round buttons right). Drop integer scaling. DEBUG is G, DB (formerly report) is B.
0.59 (same landscape layout on phone and PC (phl): TV scaled up to 80% height / half width, parts scaled to the side columns. Turns '0S, -2S', arrows repeat on long press (no -10/+10 in landscape). Button labels DEBUG and DB, keys G and B (H and F8 still work))
Does time pass if I press buttons while a menu is open?
0.60 (after menus, the save prompt or yes/no, B does not count as Search until released (B_LOCK))
How do Search and Write relate? Does Write take priority?
Explained only
Row 2 of the status and the previous message stopped appearing when standing still?
Watching
For 'Save and quit? Yes/No', log the chosen answer in green
0.60 (the yes/no prompts (save, shopkeeper, guard, pool, surface, Elbereth) unified in F13 yn_wait, answer logged as '-> Yes / -> No'. Green not yet, since a text row is one color)
Inventory: mark equipment with a leading '*' and drop '(in hand)' / '(being worn)', which take space. Sort by category/new/old with SELECT.
0.60 (leading '*' or full-width space for alignment; red not yet; sorting next). On +n: for weapons it adds to hit and damage, for armor it lowers AC by n (sturdier); bigger is better. Names are up to about 14 chars including '+n '
Row 2 shows now! Put the same Japanese labels as in-game on the web A/B buttons, with 'B [O]' 'A [P]' above them. The right safe area on iPhone is too wide.
0.60 (button labels: Search/Back, Stairs/Select, Save, Items, with 'B [O]' etc. above. In landscape, the safe margin on the side without the notch is just the corner amount (10px) via screen.orientation.angle)
Gold as an icon; a stairs icon with a direction arrow once found
Next (status row 2)
Extra turns pass while being stared at by a floating eye?
0.60 (same as below)
Turns pass without input while you can't move -> show the situation and require a button press to advance
0.60 (while paralyzed, fainted or drunk, top right shows 'A Wait'; A/B/d-pad advance one turn, holding repeats every 24 frames (main.s hl_wait))
The same message stays forever -> treat it as previous after 5 turns. Keep reports and screenshots reviewable in the project, with fix times too.
0.60 (after 5 turns an empty line pushes it to the previous line (dog.s msg_age)). Reports are kept in docs/reports/<id>/ and this table (with the fixing version)
Rooms light up in a wave from the door the first time you enter
Next
Equip actions inside the menu, show the message window during menus, and show e.g. 'AC 7 (-1)' after wearing
0.60 (after wearing/removing shows 'AC n (+/-n)' (one line for swaps). In-menu window next)
Floating eye and bat look the same -> make it one-eyed
0.60 (silhouette 8: a single eyeball, floating eye only)
(continued) Sort by category/newest/oldest with SELECT, remember the chosen order, use icons for the arrows and SELECT
0.63 (src/invsort.s: items get an acquisition order (add_inv), SELECT cycles category -> newest -> oldest, order saved in SRAM. Bottom row shows 'S Sort' and 'Page' (new glyphs))
(continued) Gold as an icon, a down-stairs icon above the gold, '*' if not found, an arrow pointing from the hero once found
0.66 (gold on status row 2 shown as a coin icon. Row 1 columns 23-24: down stairs icon plus '*' / 8-direction arrow / a dot when standing on it; hidden on floors without down stairs such as mazes (status2.s st_stair))
(continued) Rooms light up in a wave from the door the first time
0.66 (visible cells drawn in rings outward from the hero (lvcard.s vis_rings), up to 3 cells per frame; visible items decided all at once. Columns revealed by camera movement appear at once, so the wave is short)
(continued) Make the equipped '*' red
0.67 (only on the inventory screen, BG2 color 3 is red and columns 2-3 of worn items use palette 2 (invsort.s is_red); space after '*', header moved to the name column)
It stopped moving?
0.07 (the queue now holds 256 entries in SRAM and trims itself when it overflows)
It stutters when I enter a room!
0.07 (same fix; big rooms now appear over several frames)
I got trapped!
Plan to add control instructions
In landscape, put the report button near the controller's top right, opened with R. The input box should cover the controller area and leave the game screen alone (same in portrait).
0.10 (button moved to the controller's top right, opened with R or F8, input box overlays the controller box)
Remove the raster effect / cracks on hidden-door walls (a hint not in the original) / sprite animation from attacker to target / remove the A-Z a-z letters? / restore the floor dent graphics / rework the bottom HUD (icons for HP and AC?)
0.12 (raster off via tools/warp.py; cracks on hidden-door walls as an FC-only hint; attack lunge for hero, monsters and dog; a-z letters removed ('Picked up X.'); inner-face graphics restored; status line uses icons (floor, HP, AC, Lv, with Lv and floor added on request))
Show opened doors with an open graphic. For horizontal walls the door can just disappear.
0.12 (doors in horizontal walls become an open doorway; doors in vertical walls become just a gap)
In landscape the report input is cramped and the buttons get squashed; use the whole right side. Use the k8x12L font.
0.11 (landscape uses the whole area right of the TV, portrait the whole area below it; font k8x12L)
Show what the buttons do on screen? (A and B change by situation)
0.13 (the right end of the status line shows only the buttons that work right now: 'B Search', plus 'A Descend/Ascend' on stairs; hidden when the status is long)
Use circled letters for the buttons / match the hardware's left-right order? (B is on the left on the 8-bit console)
0.14 (circled A/B glyphs; hints ordered B left, A right; keyboard swapped so O on the left is B and P on the right is A)
Make the floor icon a stepped staircase shape instead of a diagonal line (to match the top row)
0.15 (filled stepped shape; shield also made solid to match)
Garbled graphics when entering an unexplored room
0.17 (moved where Y is set so it is 0 even at the start)
Fighting a monster on a door... it can't be seen from beyond the door? / Show monsters you are fighting even in dark rooms
0.17 (same fix; adjacent monsters are visible even in the dark by design)
Always show B and A ('---' for A is fine) and keep their positions fixed / are the items on the left hard?
0.17 (status line uses fixed columns: 0 floor, 3 HP, 12 AC, 15 Lv, 18 hunger, 21 B Search, 26 A ---/Descend/Ascend)
Art direction: backgrounds mostly black/white/gray dither, floor items cream, monsters red-black for now, hero and dog white. Full list of needed tiles.
0.18 (palette change, per-cell attributes make only items cream, dither on floor and walls; list in docs/art/chip_list.md and chips.csv)
Numbers on top, messages at the bottom. Hero blue, dog light blue.
0.19 (lines 0-23 numbers (top 8 left empty for CRT overscan), 24-195 map, 196-235 two text lines without a frame; hero blue, dog light blue; Lv column moved to 16)
Continue with traps
0.20 (generation of 7 trap types and mimics (matches Python); stepping on, triggering, finding by searching, monsters can trigger them too; moved some tables to the string bank to make room)
Next steps: split the string bank, then wands
0.21 (string set 2 in bank 5, R6 = 5 only while reading $A000+; text space grew from 248 bytes to 2.7KB + 5.6KB)
Wands
0.22 (17 kinds can be zapped: non-directional, immediate, rays (bounce off walls), digging; monster speed up/slow, freeze, cancellation; wish selection not yet; code bank 6 at $8000 with far call far3)
Rings, and throwing/firing
0.23 (rings: put on/remove (hand chosen automatically), protection, strength, increase damage, levitation, regeneration, teleportation, searching, see invisible, stealth, aggravate monster, hunger, poison/fire/cold resistance; not yet: conflict, warning, teleport control, protection from shape changers. Throwing: one at a time, firing arrows with a bow, boomerang, ceiling and floor)
Stair graphics: down = sketch 10 (double-framed hole with 3 steps rising to the right), up = sketch B (boxy 3 steps with a shadow lower left). This is final.
0.24 (added down_final and up_final from tools/stairs_draw.py, matched to the current floor)
Swap in all the GPT-made art at once (with screenshots for the record)
0.25 (16 map tiles, 7 traps, floor items v2, hero v2 12 frames, dog 12 frames, 33 monsters x 3 frames; source art in ROM banks 7-8, only shown frames written to CHR-RAM (sprite.s); 22 kinds without art borrow a similar shape; before/after shots in docs/devlog/media)
Save: system save on hardware/emulators, localStorage in the browser. Choose Continue / New Game on the title.
0.26 (SELECT saves and quits (original dosave); Continue loads and deletes the save (dorecover); New Game deletes old saves; about 70 bytes of hero state + marker + checksum at battery RAM $7E60; the page stores the 8KB in localStorage hk.sram)
When a boulder won't move, 'The boulder doesn't move.' repeats while you keep pushing
0.27 (message shown only at the moment you push)
Hidden passages should also get a faint hint, like the wall cracks
0.27 (when adjacent, hidden passages show a faint trace (sparse passage graphic); stays once seen)
The top of the screen glitches when moving
0.27 (tile graphics use a fast path of 32 bytes x 2, max 2 per frame (only when VBUF is empty), VB_MAX 140 -> 100; vblank peak 2133 of about 2270 cycles)
It glitches even when not moving
0.27 (same as above)
Walking left makes the hero face right
0.27 (reverted to hand-drawn art and drew the added frames: hero/dog 4 directions x 3, monsters 3 frames, 7 traps, corpse)
After scrolling up, an extra line (orange dots) appears below the top row
0.27 (IRQ at line 21 turns BG off before switching, IRQ at line 23 turns it back on; lines 22-23 are black)
The GPT art isn't working; go back to the original art and have Claude fill in the extra frames
0.27 (tools/own_art.py: hero 12 frames, dog 12, monsters 8 shapes x 3, 7 traps, corpse; GPT data kept in art_out and gpt_art.py)
Is there auto-save? At least auto-save on stairs.
0.28 (auto-save on every turn and floor change (map and inventory are always current in battery RAM, so hero state is kept in sync); not deleted on Continue, deleted on death; SELECT 'Save and quit' stays)
Move the report button up a bit, label it 'Report', shortcut H
0.29 (button 12px higher, 'H Report', opened with H or F8 (R dropped))
Does it freeze after death? / Revert the dungeon art to Claude's too, stairs as agreed / graphics are garbled when moving in emulators
(1) 0.30 (counter moved to its own variable rip_t) (2) 0.27 (back to hand-drawn; stairs stay sketch 10 / B) (3) 0.27
Is it intended that the whole room you're in doesn't light up?
Answered
If a monster is right on the stairs, the dog warps? Place things first, then draw.
0.30 (on arrival, move any monster off the stairs, then place the dog, then build the screen)
Can't return to the title after death?
0.30 (verified in tests: grave -> A -> title -> A -> start)
Hero and monster sprites remain on the title and death screens
0.30 (clear OAM on grave and title, ingame = 0 on title, reset from grave goes to title)
The report button is too small / A should also confirm on the title and after death
0.30 (button 17px bold, 105x42; title and grave both advance with A or START)
Hero keeps facing right while moving left
0.31 (facing decided by the sign of mdx; left 2 / right 3 verified in tests)
Web debug mode: open with G (next to H Report). Hide the controller and use mouse/touch. Invincibility on/off, generate special floors such as shop floors.
0.32 (src/debug.s: the page writes dbg_* and it runs while waiting for input. Invincible (full HP in ROM, can't die), full heal, +1000 gold, +1 Lv, identify all, generate/regenerate a given floor (mixed into the seed), reveal map, go to down stairs, kill all monsters, create item (by type), create monster (by name), speed x1/x2/x4, pause, frame step, info. Shop floor usable once shops are ported)
Add the proposed debug features, with a nice UI
0.33 (tabs: Hero (confusion, blindness, levitation, fast, hunger, invisible) / Floor (reveal secrets, restart with a given seed) / Spawn (item, monster, trap underfoot) / Records (save/load states 1-3, to file, from file = reproduce a report's state.json) / Emu)
Can't tell when I'll die: show 14(14) as 14/14, add a framed 24-dot bar, white when full, shrinking proportionally and red below 20%. What's the max HP?
0.33 (bar after the heart (gray frame, 22 dots inside), then 14/14. HP is 8-bit, max 255 (the original has no cap; typically 80-150 at Lv14). Status line tightened, 'B Search' and 'A Descend' shortened)
Bottom text: normally show only the last line; when stopped, show the previous line in gray too
0.33 (text rows aligned to half attribute blocks (NT_B rows 24 and 26, status row 20); previous line uses palette 3: gray after 30 still frames, black while moving)
As more characters are added, messages get cut off (cut at 'Welcome,')
0.33 (tools/loc.py skips codes whose low 8 bits are 0)
Add the proposed debug features, with a nice UI
0.33 (tabs: Hero (confusion, blindness, levitation, fast, hunger, invisible) / Floor (reveal secrets, restart with a given seed) / Spawn (item, monster, trap underfoot) / Records (save/load states 1-3, to file, from file = reproduce a report's state.json) / Emu)
Can't tell when I'll die: show 14(14) as 14/14, add a framed 24-dot bar, white when full, shrinking proportionally and red below 20%. What's the max HP?
0.33 (bar after the heart (gray frame, 22 dots inside), then 14/14; status line tightened, 'B Search' and 'A Descend' shortened)
Is max HP 255 correct?
0.33 (hpmax_inc stops at 255; debug too)
Bottom text: normally show only the last line; when stopped, show the previous line in gray too
0.33 (text rows aligned to half attribute blocks (NT_B rows 24 and 26, status row 20); previous line uses palette 3: gray after 30 still frames, black while moving)
As more characters are added, messages get cut off (cut at 'Welcome,')
0.33 (tools/loc.py skips codes whose low 8 bits are 0)
Port the shops
0.34 (on floors 2-19 when rn2(dlevel) < 3, a lit room with no stairs and one door becomes a shop (matches Python). 8 kinds: rings, wands, weapons, deli, used books, liquor, used armor, antiques. Shopkeeper stands inside the door, wares, mimics. Greeting on entry, billing on pickup (original getprice; the FC version shows the price on pickup), A to pay, dropping returns or sells (half price, 1/3 if angry), used items are charged, leaving without paying is theft and the shopkeeper chases, on death he takes what you owe. Bumping a calm shopkeeper asks first (FC). Not yet: chasing across floors, digging with a pick, dog carrying wares)
Non-directional wands (except light, secret door detection, wishing) have one charge too many
0.34 (check the NODIR bit before adding 7)
Orange dotted line under the top row (in the chat screenshot)
0.38 (back to camy - 1; camy = 0 becomes 239)
There's a red dotted line at the top of the screen right now!
0.38 (camy - 1)
(no comment)
(none)
While moving, show the map on the line where the gray log line would be too!
0.40 (while moving, the message window IRQ moves from line 195 to 218 and the window starts at row 25 line 7 (new line at 220); map and sprites extend 24 lines lower; returns to 2 lines after 30 still frames; NMI picks from mode_tab each frame)
Add a switch to turn off diagonal input on the web version
0.40 ('Diagonal ON/OFF' left of SOUND, localStorage hk.diag. OFF: on-screen d-pad is 4-way; for keyboard/gamepad only the last pressed of vertical/horizontal counts)
Garbled text (action row under the inventory shows 'Throw > ru')
0.46 (INVL and WL moved to $7B80; action glyphs cached first; cache used up to $FE)
How Hack 1.0.3 was moved onto an 8-bit console (6502, MMC3).
HACK 8BIT EDITION / 125 Inc. Original: Hack 1.0.3 (1985, CWI / Jay Fenlason et al., BSD 3-clause)
HACK 8BIT EDITION is a port of the classic roguelike Hack 1.0.3 to a ROM that runs on real 8-bit console hardware. The original C source was treated as the specification, and its rules — random numbers, level generation, combat, shops, traps and so on — were rewritten in 6502 assembly (ca65). The terminal's character screen is replaced by 16×16-pixel tiles and sprites, and all text is shown in Japanese or English.
Development started with V0.01 on 8 October 2026 and reached V3.77 on 3 November, a span of 27 days. The version number goes up by 0.01 on every release, giving 377 steps. Bug reports and requests are recorded in docs/reports/log.md with timestamp, version, cause and fixing version; the log holds 520 entries (about 100 from the in-game report feature and 423 from chat requests). The first entry is from 03:08 on 8 October, against V0.06: "it stopped moving?"
| Item | Value |
|---|---|
| Period | 2026-10-08 – 2026-11-03 (27 days) |
| Versions | V0.01 → V3.77 (377 steps) |
| Log | 520 entries in log.md |
| Source | 80 6502 assembly files, ~59,600 lines; ~10,600 lines of Python tools |
| ROM | Mapper 4 (MMC3), 512KB PRG, 8KB CHR-RAM, 8KB battery-backed PRG-RAM |
The mapper is MMC3. The deciding factors were 8KB PRG bank switching, scanline IRQs for splitting the screen, and support for battery-backed PRG-RAM. PRG started at 256KB, but free space ran out on 30 October and it was doubled to 512KB via PRG_BANKS = 64 in tools/build.py.
$8000 is a window switched by R6; $A000 keeps bank 1 (DATA1, CODE1) permanently visible via R7; $C000-$FFFF maps the last two banks as fixed code (CODE, VECTORS). The linker configuration src/nes.cfg is generated by build.py.
| Bank | Contents |
|---|---|
| 0 | Tile graphics source (GFX) |
| 1 | Resident tables and code (DATA1, CODE1) |
| 2, 5, 10 | k8x12 glyphs (glyph 683 onward in other banks); bank 5 also holds string pool 2 |
| 3 | Strings and tables read only during play (STR) |
| 4 | Level generator (OVL) |
| 6, 9, 11–15 | Far code (wands, grave/shops, eating/OAM, monster inventory, status line, footprints, screen rebuild) |
| 7, 8, 16, 17 | Sprite graphics (monsters, hero/dog, per-monster artwork) |
| 10 | Help text, BGM driver and music |
| 18 | Full Misaki 8×8 font |
| 19–22 / 23–26 | Message text (Japanese / English) |
| 27 | Log window |
| 28, 29 | Help text (Japanese, English) |
| 30, 31 | Score, name, ranking / Test Maze |
| 32–61 | Free |
| 62, 63 | Fixed ($C000-$FFFF) |
The fixed region and CODE1, callable from anywhere, had only a few dozen to about 100 bytes free for most of development. New ported code goes into switchable banks (FAR6 to FAR31) and is reached through far-call macros such as F3 to F13. The convention passes A, X and Y through unchanged and returns A, X and the flags. Whenever a bank overflowed, routines were moved: gifts and the ice box from FAR13 to FAR11, sprite handling from FAR6 to FAR11, dog biting from CODE1 to FAR13, and so on. Half of the ROM was still free on 11 October, but by 29 October eight banks of bilingual text, the full Misaki font and per-monster sprites had nearly filled it.
CHR is 8KB of CHR-RAM rather than ROM. Glyphs and sprites live on the PRG side, and only what is on screen is written into CHR-RAM tiles when needed. This makes it possible to handle hundreds of kanji and individual monster artwork without being limited by pattern table size. Every write goes into VBUF ($0100) and is flushed by vb_exec in NMI during vertical blank.
The 2KB of internal RAM holds zero page, working variables, VBUF and the BGM driver variables (MUSRAM). The game state itself lives in battery-backed SRAM ($6000-$7FFF). The 1,760-byte map (80×22), 24 monsters, 64 floor objects and 52 inventory slots take about half of it. The rest holds the message queue, level records, bones, the glyph cache, and the player name and ranking. The $7EC0-$7FFF area filled up completely when the ranking was added in V3.43. To make room, the message queue was cut from 8 to 4 lines and saved levels from 32 to 28, and the cell-redraw queue is borrowed as scratch space while the inventory screen or level transitions are active.
The original hack.mklev.c was ported to src/mklev.s. It generates rooms, stairs, corridors, doors and niches in the same order and with the same random-number calls as the original. Random numbers are consumed exactly as in the original even where the corresponding feature is not yet implemented. Signed values (a room's hx of -1, corridor directions and so on) are kept in two's complement. The map is one byte per cell in LEVL[x*22+y]: the low 4 bits are the type (def.rm.h) and $40 is the lit flag.
A Python version, tools/mklev.py, serves as the reference. Both versions generate a level from the same seed; after running the ROM with node tools/test.js, tools/check_mklev.py compares LEVL in SRAM. The pass condition is that not a single byte differs. Trap and mimic generation and mazes (hack.mkmaze.c) were verified the same way. Because this separates assembly bugs from misreadings of the original, every change to generation logic passed this comparison before release.
Monster and object tables are not copied by hand: tools/mons.py generates them from hack.monst.c and tools/objs.py from the object definitions. Wherever behaviour differs from the original, the source carries an "FC version: ..." comment explaining why.
Each character of the original becomes one 16×16-pixel cell. A 16×11-cell window onto the 80×22 map is shown and scrolls smoothly in both axes to follow the hero. Nametables use horizontal mirroring: rows 0–29 of NT_A plus rows 0–13 of NT_B give 44 rows, enough for all 22 map rows. Horizontally the 32 columns are used as a ring, with the leftmost 8 pixels hidden so the seam of the rewrite never shows. Attributes (palettes) are also 16×16, so choosing a palette per cell falls out naturally.
Entering a large lit room overflowed the 64-entry cell-redraw queue and froze the game. This was the very first bug report; V0.07 enlarged the queue to 256 entries and moved it into SRAM.
V0.02 added a raster perspective effect that shrinks the far rows and stretches the near ones. tools/warp.py builds a table that says which map line to show on each screen line; build_irq computes the $2006/$2005 writes from the camera position every frame, and an IRQ applies them mid-frame. The top was scaled 1.55× and the area just above the hero was 1:1. It looked interesting but made testing hard, and after a report it was switched off in V0.12 with warp.ON = False, returning to a flat view. The mechanism remains in the code.
Sprites are 8×16. Their source artwork is in ROM (bank 7 for monsters, bank 8 for the hero and dog, banks 16 and 17 for per-monster art). src/sprite.s writes only the one frame being shown (64 bytes) into a fixed CHR-RAM slot: slot 0 is the hero, slot 1 the dog, and slots 2–9 cover up to eight monster types on screen.
The placement code (src/oam.s, bank 11) puts nearer (lower) objects first, because lower OAM indices are drawn on top. It insertion-sorts by descending y, starting from the previous frame's order, so the list is almost always nearly sorted and the sort is fast. Small effects such as a 1-pixel hop while walking and the attack lunge offset are applied here as well.
The screen is split into a status line at the top, the map, and a message window at the bottom. The top two rows use the Misaki 8×8 font; NMI displays lines 7–21 and an IRQ at line 21 switches to the map, which starts at line 24. The bottom window is rows 22–29 of NT_B, switched in by an IRQ at line 173 (IRQ_LINE); row 22 is left blank to hide the switch. The log window extends the bottom window upward from line 207.
The START main menu, inventory, yes/no prompts, shopkeeper dialogue and so on are windows overlaid on the map without blanking the screen. Window text is drawn into map tiles that are not in use and the original graphics are restored on close. Windows align to the 16×16 grid with text offset by 8 pixels, and a window's width is its longest line plus 16 pixels on each side. Sub-windows follow shared rules: the parent is greyed out, the parent's hints are hidden, ◀▶ sit at the window's left, and there is a heading with a divider. These rules were managed in a menu flow spreadsheet and an 8×8 screen layout sheet.
Body text uses the k8x12 font (8×12 pixels). tools/loc.py builds strings from loc/text.csv and loc/glossary.csv and extracts only the glyphs actually used. Each glyph is 12 bytes; 682 fit in bank 2 and the rest go into other banks. A glyph sits in a 16-pixel row and is drawn across two tiles, upper and lower.
Messages in the bottom window alternate between two tile sets. A new message is fully drawn into the hidden set before it is shown, so half-drawn characters never appear. The glyph cache holds up to 110 characters, and characters already on screen are not redrawn. The log window looks up the cache through a 64-entry hash table without crossing banks, which lets it open in 10–11 frames.
The top status line and the previous line of the message window use the Misaki 8×8 font. In V2.16 all 903 Misaki characters (7.1KB) were placed in bank 18; characters missing from JIS were hand-drawn or derived from rows of k8x12.
V2.17 made the game bilingual. Messages, monster/object/trap names and UI words are placed at the same virtual addresses from $0100 for both languages, with Japanese in banks 19–22 and English in 23–26. sb_ptr in text.s reads any address below $8000 from the bank named by LANG_B ($7EB4, value 19 or 23). Virtual addresses are sized to the longer language and laid out so that no string crosses an 8KB boundary; text in internal RAM ($0000-$07FF) is excluded from this remapping. Since V3.52 the console ROM defaults to English and the web version follows the browser language.
There are 16 sound effects. Their data was decided in MUSIC LAB (a sound-design page on base-08), stored in music/sfx.json, and converted by tools/sfx.py into gen/sfx.inc. Each frame is "mask of registers to write + values", and NMI writes one frame at a time to the APU. While an effect plays it borrows only the channels it uses from the BGM (footsteps use noise only). tools/sfx_check.js confirms that all 16 produce exactly the same register writes as the source data.
V3.70 moved playback to a priority scheme. Previously a new sound always cut off the previous one, so the player's own attack sound was lost to the enemy's counterattack. Now sfx_pri holds a priority per sound; a sound that has just started, or one of higher priority, is not interrupted, and a single pending slot is kept.
| Sound | Hit | Miss | Kill | Hurt | Stairs | Lv | Pick up | Drink | Wand | Teleport | Gold | Menu | Step | Dodge | Dodged | Buzz |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Priority | 5 | 3 | 7 | 9 | 6 | 8 | 4 | 5 | 5 | 7 | 4 | 2 | 0 | 3 | 3 | 2 |
BGM uses a fork of the music driver from the shared in-house library nes_common. DPCM is not used; the variables sit in free internal RAM (198 bytes), and the driver and songs live in bank 10. Songs are converted from FamiStudio's text export (music/title.txt), and tools/bgm_check.js checks that the output matches FamiStudio's NSF frame by frame. Only the optimisations from shared version 1.01 were merged by hand, cutting per-frame cost from 3,653 to 2,860 cycles. V3.19 fixed the web emulator's audio, which played square waves 3 octaves and the triangle 5 octaves too low, so pitch now matches hardware. Since 1 November the BGM advances in NMI, so music no longer stops when the main loop is busy.
The map, monsters, floor objects, traps, inventory and identification state already live in battery SRAM. src/save.s (the original's dosave/dorecover) copies the hero's state and other variables held in internal RAM into SV_DATA with a marker and an additive checksum. This runs every time a turn passes, so after power-off the game resumes from the last move. The save is erased on death or when "Start" is chosen. Whenever the layout of saved variables changes, SAVE_VER is incremented (currently 5). On the web the page stores the 8KB SRAM in localStorage (hk.sram) in place of the battery RAM.
The ranking runs on LootLocker. There are three ways to submit — the cartridge's QR code, the web version on base-08, and the web version on itch.io — and all follow the same rules in web/hackrank.js. Each device may hold up to five entries per board; an identical record is reported as already registered, and when all slots are full a new record replaces the lowest only if it scores higher. Boards are split into weekly and all-time per maze, the weekly seed, an any-seed board and a debug board; weekly boards are cleared every Monday at 9:00 JST. A run in which any debug feature was used goes only to the debug board.
The cartridge has no network connection, so the result screen shows a QR code. It encodes the 8-character name and 30 hex digits (4-byte device ID, 8-byte record, 1-byte version, 2-byte checksum); scanning it with a phone opens a registration page at base-08.web.app/H/<record>. If the same run is sent both by QR and from the web, the device ID and record match and it collapses into one entry. The device's top ten records are kept in battery RAM as 10 × 16 bytes and can be viewed from "Ranking" on the title screen.
The web version runs the ROM on an in-house JavaScript emulator, web/nes.js (about 430 lines). It is a minimal implementation: official 6502 opcodes, PPU rendering, CHR-RAM, joypad, DMC, and mappers 0 and 4 only. It can save and restore RAM, SRAM, VRAM, CHR-RAM, CPU, PPU and mapper state in one piece, which underpins both bug reports and rewind. build.py embeds the emulator and the ROM into a single HTML file.
One page serves desktop and phones in both portrait and landscape. The touch D-pad picks a direction from the angle to its centre and responds even on its transparent margin. The magnifier that iOS shows on rapid taps is suppressed by cancelling touchstart. Rewind keeps per-turn states: 128 turns in memory and the last 32 in localStorage.
The page's report feature sends a comment, a screenshot and the emulator state to Firebase. Importing with tools/bugs.py creates an image and state.json under docs/reports/<id>/, and node tools/bugrun.js <id> replays from that state. The workflow is to reproduce locally what happened on the reporter's machine, identify the cause, then record it in log.md. The web version's DEV menu offers debug features such as jumping to a level, spawning objects or monsters, and invincibility.
tools/deploy.py bumps the version by 0.01, builds, and before publishing runs two instances of tools/soak.js in parallel (one invincible and diving deep, one normal, 12,000 frames each). They press random inputs and, on a hang, runaway or stuck black screen, abort the release and save an image and the state. The hangs in V2.81–2.86 were found this way. After publishing, the script reads the version from the live page to confirm it.
tools/special_shots.js builds scenes by writing commands into debug RAM addresses (dbg_cmd and others) and takes documentation screenshots according to a script. For performance work, a per-label profiler (tools/prof.js) and a tool that checks whether the same inputs produce the same game state as the previous version were built first. The V3.24–3.26 rework cut one step on a level where the dog is fighting from 16.7 to 12.7 frames.
To avoid diving many levels just to check shops, nests, traps, monsters and items, a "Test Maze" (Japanese: 開発迷宮) is being designed: a 10-level theme-park dungeon in which every element is always in a fixed place, with each room's theme shown in the bottom window. Because locations are fixed, screenshot scripts and full test runs can capture the same scenes every time and compare them against the previous version. V3.74–3.77 made it selectable from the "Dungeon" row in class selection, and its records always go to the debug board. The maze contents are still to be built.
A rule set that keeps the feel of the original while reducing waiting and tedium is called the "Modern Arrange (2020s)" and has been the default since V2.02. The main changes are:
The guiding principle is to reduce things that kill unfairly if you do not already know them, and actions that cost turns without involving a decision. Level generation and combat formulas remain as in the original. In V3.15 the 17 branches for the original rules were removed, freeing 156 bytes in the fixed bank; the original code is preserved under the git tag last-with-original-rules.
The fixed bank and battery RAM are close to full, but PRG still has 30 free banks (240KB). Future additions will continue the same approach: move code into switchable banks, and use tools to confirm that results match the previous version.
A fixed dungeon for testing: a "theme park" where every element is always in the same place (in design).
2026-11-03 Version: design as of V3.77 (the way in, Chapter 2, is built; the maze contents are not yet). Diagrams are from tools/dd_doc_img.py; screenshots are from the current game (tools/special_shots.js).
Right now, to test shops, nests, traps, monsters and items, you dive many floors until you happen to meet them. It depends on luck and takes time.
Build one "theme park" maze where every element is always in a fixed place.
special_shots.js, end-to-end tests): since the locations are fixed, the same scenes can be captured every time. Tests that compare screenshots with the previous version also become possible.The top row of class selection (formerly "Seed") is renamed "Dungeon", with the following 5 kinds (the web version too, this week). The default is Random; if there is a save, the Dungeon of that run (V3.75).
| Kind | Contents | Ranking |
|---|---|---|
| Random | The seed at that moment | Any |
| Maze #1–#3 | The same maze every time | Maze #n / Any |
| Test Maze (English: Test Maze) | The maze in this design document (currently no contents yet; the former fixed #4 maze) | Debug |
| List | Window (Test Maze) | Description on the right | English |
|---|---|---|---|
Proposal: rename "Start" to "Start Exploring" (Decision needed 1). Since the Test Maze is chosen by Dungeon kind, no row is added to the title.
hack_fc_debug) (built in V3.74).DB_GODU (bit0 invincible, bit1 web scene). Next to the name on the result screen: "▼Test Maze".The layouts are drafts of room arrangement (positions on the 80×22-cell map). When building, draw the map at these positions.
| Zoo | Morgue | Shop (current screenshot) |
|---|---|---|
| Labyrinth | Wizard |
|---|---|
When you enter a room, its theme appears in the bottom window (Test Maze only).
loc/text.csv as dd_r_* (Japanese and English).| Thing | Limit | Defined by |
|---|---|---|
| Rooms | 15 | MAXNROFROOMS |
| Floor objects | 64 | MAXOBJ (shop goods included) |
| Monsters | 24 | MAXMON (shopkeeper, guard and dog included) |
| Traps | 32 | MAXTRAP |
| Shops | 1 | One shopkeeper per floor (shk_m) |
| Floor text | 4 | NENG |
| Kind | Count | Placement |
|---|---|---|
Weapons ) | 18 | B1 Armory |
Armor [ | 12 | B1 Armor Room |
Tools ( | 6 | B1 |
Food % (excluding corpses) | 17 | B1 |
Potions ! | 20 | B2 |
Scrolls ? | 25 | B2 |
Wands / | 21 | B3 |
Rings = | 21 | B3 |
Gems * | 19 | B4 |
Amulet " | 1 | B10 |
| Traps | 7 kinds × 2 (visible, hidden) | B4 |
| Monsters | 65 kinds | B1 Training Ground, B6 nests, B7–B9 Bestiary, role-specific places |
| Special rooms | 8 shops, zoo, beehive, morgue, swamp, vault, niche, labyrinth | B1–B10 |
Floor objects per floor are at most around 60 (B1: 53 for weapons, armor, tools and food; B2: 20 potions + 25 scrolls). Shop goods count as floor objects, so the shops on B1 and B2 are kept small (up to 10 goods) to fit within 64.
Draw the 80×22 maps as text in tools/dd/b01.txt–b10.txt (same symbols as the original game's screen).
----------- ------------ |.........| |..........| - | wall . floor # corridor + door |....<....+##########+...))))...| S hidden door = hidden passage < > stairs |.........| |..........| ^ trap (kind in the table below) 0-9 item/monster (table below) ----------- ------------ @room 1 dd_r_info ; room theme (in order from the top-left room) @room 2 dd_r_weapon @obj ) all ; all weapons (laid out inside the room) @mon 1 jackal awake ; a jackal at symbol 1 (awake) @trap ^ 3 seen ; trap @shop 5 general ; make room 5 a general store (shopkeeper and goods made the current shop way)
tools/ddmap.py reads them and builds src/gen/dd.bin (maps packed to a few hundred bytes per floor), placed in the free bank 31 (banks 31–61 are free).
mklev.s: in the Test Maze, for floors up to 10, call dd_load (bank 31) instead of random generation.dd_load: write the map, rooms, doors and stairs → place items, monsters and traps → for shops, zoos, vaults, etc., call the current generation code on that room (split the body of mk_shop, mk_zoo and mk_vault into forms that can be called with a given room).lvsave2.s).g.dd(floor, room) in special_shots.js: start in the Test Maze and jump to that room's entrance. Any scene can be captured in one line.deploy.py).| Step | Work | Check |
|---|---|---|
| 1 | Dungeon kind "Test Maze", record flag (DB_GODU bit2), result display | Done (V3.74–3.77) |
| 2 | Map → data (tools/ddmap.py → gen/dd.inc) and dd_load (rooms, doors, corridors, stairs) | Done (V3.83) |
| 3 | Place items, monsters, traps and floor writing | Done (V3.83) |
| 4 | Call shops, nests, swamp and labyrinth on rooms | Done (V3.83; vault and niche not yet) |
| 5 | Room theme guide (text in Japanese and English) | Done (V3.83; also zoos, beehives, graveyards and swamps in normal mazes) |
| 6 | The 10 floor maps | Done (V3.83: 4 rooms above and 4 below a central hall; B10 is the Amulet labyrinth) |
| 7 | Test scripts (special_shots.js ddw, ddroom; screenshot comparison and end-to-end tests not yet) | Partly |
| Item | Proposal | |
|---|---|---|
| 1 | Is it OK for "Start Exploring" to replace "Start" in the title menu? | Decided: yes (English stays NEW GAME) |
| 2 | Number of floors | Decided: 10 floors |
| 3 | Beyond B10 | Decided: dead end (no down stairs on B10) |
| 4 | Starting gold | Decided: 3000 |
| 5 | Should Bestiary monsters be asleep? | Decided: asleep |
| 6 | Test Maze ranking | The debug table (as in V3.74) |
| 7 | Should the room theme guide also show for special rooms (shops, zoos, etc.) in normal mazes? | Decided: yes (zoos, beehives, graveyards, swamps; not shops, which have the shopkeeper's greeting) |
Request: "Shouldn't the zoo have its own floor? It doesn't have to stay B10. Split them up so they look good." A floor holds at most 23 monsters, so three nests on B6 all looked sparse.
| Floor | Contents | Shop |
|---|---|---|
| B1–B5 | Unchanged | general, potion, ring, wand, weapon |
| B6 | Zoo (8×3: sleeping monsters and gold; group monsters also spill into the outer row) | — |
| B7 | Beehive (16 killer bees, royal jelly) | — |
| B8 | Graveyard (16 ghosts, wraiths and others; lit so it is easy to see) | — |
| B9–B11 | Bestiary 1–3 (formerly B7–B9) | scroll, armor, food |
| B12 | Amulet maze (formerly B10). Dead end | — |