像素艺术房间:家具网格、上限与评分
在《宝可梦 绿宝石》里,一间装饰好的房间,就是摆在 16 像素格子网格上的十六件东西。反编译源码把玩家秘密基地里可摆放的装饰品上限定为 16 件,卧室为 12 件,存档里最多保留 20 个基地,包括自己的和其他玩家的。12 目录共有 8 个类别、120 件装饰品:玩偶 35、摆件 23、地垫 18、海报 10、坐垫 10、桌子 9、椅子 9、植物 6。其中 71 件只占一格,平均 2.43 格,另有 18 件是可以托住小件物品的台面。23 一个基地可行走的格子在 44 到 81 之间,平均 60.2,所以上限大约是地面的四分之一。摆放是每个被覆盖的格子都要回答的一个问题,而摆放函数里没有任何一处检查通路是否还在。24 本文后面的三款游戏各以不同的预算运行这套模型:按 Nookipedia 的说法,Animal Crossing: New Horizons 每个房间 150 件,并有一封周日来信给房间打分;Final Fantasy XIV 自 7.5 版本起,按住宅类型室内 150 到 600 件;Club Penguin 的冰屋 99 件,出自一个粉丝来源。5678 我的应用 Kiradex 里有房间,由美术锻炉用 23 种家具的词汇来布置,但玩家一件也挪不动。本文实测这些经典,把规则连同数字写清楚,再映射到 SwiftData 和 SwiftUI 上,最后给出 Kiradex 尚未动手的一套家具摆放系统的简报:每层 16 件的上限,一条《绿宝石》的摆放函数没有的通路规则,一份列出每一分来源的每周评分,以及一份只能靠游玩解锁的目录。910
TL;DR
- 十六是构图的预算,不是储存的上限。 《绿宝石》在基地里能摆 16 件装饰品,卧室 12 件,却能在八个按类别划分的栏位里拥有 150 件,所以玩家拥有的可以比摆得下的多。每件摆出的装饰品占两个字节,一个 ID 加一个位置(x 和 y 各占半字节),整个基地布局只需 32 字节。111122
- 规则住在地面里。 《绿宝石》有五种摆放权限(地面、可踩过的地面、靠后、墙面、台面上)。桌面的格子带有一种行为,允许小件装饰品立在上面,所以桌子一放下,玩偶就能放上去。能托小件的装饰品有 18 件,能托大件的有 14 件。卧室只接受玩偶和坐垫。1324
- 上限随平台上涨,其中一个同时也是帧预算。 按 Nookipedia 的说法,Animal Crossing 每个房间的物品上限在 Wild World 是 24,City Folk 是 64,New Leaf 是 48,New Horizons 是 150。Final Fantasy XIV 在 7.5 版本把室内上限提高了一半,达到 150 到 600,并在说明里补充:屏幕上的家具超过 400 件时,其他楼层的家具就不再绘制。57
- 评分有列明的来源、固定的日子,以及随房子一起上涨的门槛。 Nookipedia 上的快乐之家学院表格规定,一个房间的物品达到 6、10、15、20 件时各给 1,000 分,另有系列、套装、类别、颜色和风水加分,并随房子扩建把 S 级门槛从 15,000 提高到 90,000。该页面没有为这些 New Horizons 的数字注明出处。6
- 目录靠购买增长、靠游玩增长,或两者兼有。 在《绿宝石》里,120 件中有 90 件用钱出售,24 件从不出售(奖品、对战点数和赠品),另有两件要用一步步收集来的 6,000 ash 和 8,000 ash(ash 即火山灰)换取。Stardew 每天出售随机的货品;第一次扩建房屋后,罗宾还会以 200,000g 出售家具目录(Furniture Catalogue),它随后会以 0g、不限数量地出售目录里列出的家具,不过仍有一些家具只能从博物馆、节日和其他来源获得。Habbo 出售家具,其中一部分是刻意稀缺或随机的。14151617
- 访客只看不碰。 Animal Crossing 的访客可以使用别人家里的家具,但不能“modify the interior in any way”(以任何方式改动室内)。Habbo 的访客没有房主授权,就不能放下或移动家具。Club Penguin 允许每个设计每天点赞一次,其总数(Grand Total)“will never decrease”(永远不会减少),这出自一份粉丝转载的帮助页面。51819
- 这为 Kiradex 带来什么。 一份简报,而不是已上线的功能:锻炉 23 种家具中的 20 种按格子摆放;一条从门口通往楼梯、主人以及每个展示组前方至少一格的通路规则,靠实际走一遍来检查;每层 16 件,初始房间以 8 件上线;锻炉画出的六种墙纸和六种地板;一封印出每一项得分来源的周日来信(最高 23,600 分);一份靠游玩解锁的目录,从现有的等级和徽章出发,由同步的收藏、应用记录在设备上的任务印章,以及三个新增并同步的计数器(步数、房间拜访和来信的房屋等级)推导而来;等服务器能保存布局后再开放只读拜访;一个没有唯一约束的 SwiftData 模型,因为 CloudKit 同步无法强制唯一约束;以及 VoiceOver 无需触摸也能完成的拖放摆放。9202122
1. 《绿宝石》:网格上的十六件东西,从源码实测
这一类型最小的完整形态,是《宝可梦 红宝石/蓝宝石》和《绿宝石》的装饰系统,而它几乎全是数据。我从 pret 的《绿宝石》反编译提交 731ad5b(提交于 2026 年 10 月 1 日)读取了它,用的是两个只解析源码、从不运行游戏的脚本:一个负责目录、上限、台面和房间尺寸,另一个负责每件装饰品的来源。它们的输出都保存在脚本旁边。214 本节的每一个数字,都来自这两份输出,或来自脚注指明的源文件。
上限
三个常量设定了上限。DECOR_MAX_SECRET_BASE 是 16,DECOR_MAX_PLAYERS_HOUSE 是 12,SECRET_BASES_COUNT 是 20,即存档保留的基地数量:自己的,加上从其他玩家那里收到的。12 能摆多少的上限,与能拥有多少的上限是分开的。存档在按类别划分的栏位里保存 150 件拥有的装饰品:桌子 10、椅子 10、植物 10、摆件 30、地垫 30、海报 10、玩偶 40、坐垫 10。112 所以玩家拥有的可以比一个房间放得下的多(新游戏开始时每个栏位都是空的),而挑选放什么,本身就是游戏。23
一件摆出的装饰品占两个字节。基地用 decorations[16] 存放各自的 ID,用 decorationPositions[16] 每件存一个字节,高半字节是 x,低半字节是 y,所以基地被寻址为最多 16×16 格的网格,整个基地布局只需 32 字节。1112 依我的解读,正是这个尺寸让存档能容下二十个基地,自己的和其他玩家的。
目录
gDecorations[] 有 121 个条目。条目 0 DECOR_NONE 是一个复用小桌子数据的空槽,剩下 ID 为 1 到 120 的 120 件装饰品。32 按类别,它们是玩偶 35、摆件 23、地垫 18、海报 10、坐垫 10、桌子 9、椅子 9、植物 6。2 按以格为单位的形状,1×1 有 71 件,1×2 有 22 件,3×3 有 13 件,2×1 有 5 件,2×2 有 4 件,3×2 有 3 件,2×4 和 4×2 各一件。头文件还声明了 3×1 和 1×3,但没有装饰品使用它们。一件装饰品覆盖 1 到 9 格,平均 2.43 格。213 如果像我为建筑篇所做的早期笔记那样把空槽算作一件装饰品,那么单格的有 72 件、实心地面的有 19 件;不算的话则是 71 和 18。2
《绿宝石》的目录大多是单格;大件是桌子、3×3 的地垫和少数摆件。243
表中价格从最便宜的地垫 500 到最贵的摆件和玩偶 10,000 不等。有三件价格为 0:两面盾牌和一件玻璃摆件,三件都是奖品。23
五种权限,以及作为数据的台面
每件装饰品带有五种权限之一。头文件称它们为 “collision and placement permissions, in that order”(碰撞与摆放权限,按此顺序)。13 数量如下:
| 权限 | 含义 | 装饰品 |
|---|---|---|
| 实心地面 | 阻挡行走,立在地面上 | 18:全部 9 张桌子,9 件摆件 |
| 可通过地面 | 可踩过,立在地面上 | 37:全部 18 块地垫,全部 9 把椅子,10 件摆件 |
| 靠后地面 | 阻挡,顶行可以搭在后墙上 | 10:全部 6 盆植物,4 件摆件 |
| 墙面 | 挂在后墙上 | 10:全部 10 张海报 |
| Sprite | 立在台面上的人偶 | 45:全部 35 个玩偶,全部 10 个坐垫 |
由 measure_emerald_decor.py 对 src/data/decoration/header.h 统计。2
最巧妙的一点,是台面是格子的属性,而不是一段代码。每件装饰品由 metatile 绘成,每个 metatile 带有一个行为字节。有 18 件装饰品的格子带有 MB_HOLDS_SMALL_DECORATION 行为:9 张桌子中的 7 张、3 块砖、1 个轮胎和 7 块地垫。有 14 件带有 MB_HOLDS_LARGE_DECORATION:同样的 7 张桌子和 7 块地垫。2 1×2 的玩偶下方需要一个“大”格,其他玩偶或坐垫“小”格、“大”格都可以。4 所以一张桌子放下后,地面上就多出了对玩偶回答“可以”的新格子,同一个摆放函数两者都能处理。这就是 16 件东西能让房间显得满满当当的原因:120 件中的 45 件 sprite,立在 18 件台面之上。2
有些装饰品还会动。八块音符地垫踩上去会发声;跳跃地垫、旋转地垫和闪光地垫如其名所示;三个气球和一个泥球会破;还有一扇可破坏的门、一个滑梯和一件沙子摆件。每一种都是装饰品 metatile 上的一种行为(MB_SECRET_BASE_SOUND_MAT、_JUMP_MAT、_SPIN_MAT、_GLITTER_MAT、_BALLOON、_BREAKABLE_DOOR、MB_SLIDE_SOUTH、_SAND_ORNAMENT),读自秘密基地图块集的属性文件。225
摆放是每个格子都要回答的问题
src/decoration.c 中的 CanPlaceDecoration 会逐一走过手上那件装饰品将要覆盖的格子,并向每一格提一个问题。地面类和可通过地面类的装饰品要求每个被覆盖格子底下都是普通地面(MB_NORMAL);只有实心木板可以盖住洞。格子上不能站着任何物体。玩家打开菜单时所站的那一格,只有当装饰品放在那一格的图块属于普通图层类型时才能覆盖;如果它在那一格上的图块是其他图层类型,函数就拒绝在那里摆放。靠后地面类的装饰品要求除顶行外的每一格底下都是地面,顶行可以搭在北墙上。墙面类装饰品要求每一格都是北墙。sprite 下方需要一个可托举的格子。4 在卧室里,菜单会拒绝玩偶和坐垫以外的所有类别:当 isPlayerRoom 被设置时,DecorationItemsMenuAction_AttemptPlace 会显示 gText_CantPlaceInRoom。4
CanPlaceDecoration 里没有任何一处检查通路是否还在。这个函数给行走提供的唯一保护,就是关于所站格子的那条有条件的规则。依我对这个函数的解读,玩家可以用桌子把基地的电脑围死,游戏也会允许。4 我会在简报里再回到这一点,因为别人会来拜访的房间,需要这个函数没有做的那项检查。
衡量上限所用的房间
24 种基地布局是六种风格各四种尺寸。从每个 map.bin 连墙一起量,宽 7 到 17 格,深 7 到 17 格,面积从 99 格(11×9)到 196 格(14×14),狭长的几种是 7×16、10×17 和 17×8。226 从每种布局的碰撞位读出的可行走格子在 44 到 81 之间,平均 60.2。2 上限 16 相当于一个平均基地可行走地面的 0.27。2 卧室,即两位玩家房屋的二楼,是 9×8,可行走格子 54,上限 12。21
只看地面会高估拥挤程度,因为 16 件里有许多是可以踩过的地垫,或者立在桌上的玩偶。上限预算的是构图,而不是地面。
格子与角色的比例
玩家的行走图集是 144×32 像素,即九张 16×32 的帧,所以在 16 像素的 metatile 网格上,角色的帧宽一格、高两格。装饰时的姿势是一张 16×32 的帧。27 网格的格子就是帧的宽度,每件装饰品都以这个单位定尺寸。
目录如何增长
来源脚本在商店、奖品和赠品脚本里搜索了每一个装饰品常量。120 件中有 90 件在五家商店用钱出售。3 件是奖品兑换处的兑换品,15 件用对战点数兑换,10 件由脚本赠送:又是那 3 件奖品兑换品、通关一座机关屋得到的两顶帐篷、博物馆楼上的一件摆件、对战设施主人给的两面盾牌,以及居民送的两个玩偶。24 件只能赢取,从不用钱出售。14
还有两件要靠走路来买。一家玻璃工坊用 6,000 ash 换一把椅子、用 8,000 ash 换一张桌子,而 ash 要在随身带着袋子时走过覆盖火山灰的草丛,每走一步收集一个单位,上限 9,999(VAR_ASH_GATHER_COUNT)。15 我把这一对解读为经典里最纯粹的“靠游玩解锁”:不经手任何钱,只经手步数。袋子不显示数量。它的野外用法是不能使用的道具所用的函数,说明文字是关于收集和装存火山灰的固定文本。28 除了步数计数本身,读取这个数的只有工坊的脚本:玩家不够时,它从价格中扣掉已收集的火山灰,把差额换算成还要走多少步告诉玩家。15
有四件装饰品(三个传说玩偶和另一个玩偶)没有被搜索范围内的任何商店、奖品或赠品脚本引用。它们可能来自交换或联机功能;我没有追查。售出 90 件、只能赢取 24 件这两个数字,建立在对商店、奖品和赠品脚本的搜索之上,这四件仍未追查。14
《绿宝石》教给我们什么
一个装饰系统就是三张表和一个函数:每件装饰品一行目录(权限、形状、类别、价格、metatile),一份标明每个格子是什么(地面、北墙、洞)的布局,一份大于摆放上限的拥有清单,以及一个向每个被覆盖格子提一个问题的摆放检查。台面就是对更小的装饰品回答“可以”的格子。上限只计算摆出的装饰品,别的都不算。缺口在于行走:摆放函数检查格子,却从不检查通路。
2. Animal Crossing:150 件物品和一封周日来信
本节依据粉丝维基 Nookipedia,于 2026 年 10 月 5 日阅读并保存;我没有去找 Nintendo 官方的数字。维基没有注明出处的地方,我会如实说明。56
上限与房间
系列中的每一款游戏都对一个房间里的物品设有上限,而这个上限一直在增长。Nookipedia 给出的每个房间 “furniture and clothing items”(家具和服饰物品)上限是:Wild World 24,City Folk 64,New Leaf 48。关于 New Horizons:“A maximum of 150 items can be placed in each room of the house, including wall and ceiling-mounted furniture.”(房屋的每个房间最多可放置 150 件物品,包括壁挂和吊顶家具。)5
按同一页面的扩建表,New Horizons 的房间:帐篷 4×4;房屋 6×6;扩建主房间后为 8×8;后、左、右房间各 6×6;二楼和地下室 10×6。“Unlike previous games, only the main room can be expanded in size, as all other rooms have a fixed size.”(与前作不同,只有主房间可以扩大,其他房间大小都是固定的。)5 6×6 的房间放 150 件,相当于每块地面图块 4.2 件,这之所以可行,只是因为墙面、天花板和其他家具的顶部不占地面也能容纳物品(这是我根据两个数字做的计算)。529
家具有 “a size in tiles that it takes up when placed, ranging from 1.0×0.5”(摆放时所占的图块尺寸,从 1.0×0.5 起)到 3×3,而在 New Horizons 里它们 “can also now be pushed in half-tile increments”(现在还可以按半格推动)。29 New Horizons 把家具分为家居用品、放在地面或台面上的杂项物品、壁挂件,以及 2.0 版加入的吊顶装饰。29 在本文读过其摆放网格的游戏中(《绿宝石》的格子、Stardew 的图块和 New Horizons 的图块),只有 New Horizons 能以小于一整格的幅度移动家具。我没有读 FFXIV 的摆放网格;而 Habbo 被引用的更细步长,是一个第三方客户端的堆叠高度(见第 3 节)。
目录
截至 3.0.2 版,Nookipedia 统计 New Horizons 有 2,076 件家具,其中 1,074 件是更新加入的。一个系列是 “around 10 furniture items, as well as a matching wallpaper and flooring”(十件左右的家具,外加相配的墙纸和地板)。29 维基统计截至 1.9.0 版有 262 种墙纸,地板 215 种。3031 墙纸和地板按房间更换,2.0 版还加入了重点墙:“a second wall covering can be applied to single wall in the room”(可以在房间的某一面墙上再贴第二种墙面)。5
快乐之家学院
Kiradex 借用的是评分这一半。Nookipedia:学院 “sends an evaluation by mail most Sunday mornings, which cannot be disabled”(大多数周日早晨会寄来评价,且无法关闭)。等级为 B、A、S,S 级门槛随每次扩建提高:初始房屋 15,000,扩建主房间后 23,000,然后是 35,000、47,000、60,000、75,000,有了地下室后为 90,000。6
维基整理的 New Horizons 加分如下:
| 条件 | 分数 |
|---|---|
| 一个房间有 6、10、15 和 20 件家具 | 每个门槛 1,000 |
| 幸运物品 | 每件 777 |
| 壁挂家具 | 每件 400,最多三件 |
| 同一系列 4 件或以上 | 每件 1,000 |
| 一整套家具 | 每件 800 |
| 同一类别 3 件或以上 | 每件 500 |
| 房间物品 70% 或以上为同一颜色 | 每件 200 |
| 90% 或以上为同一颜色 | 每件 600 |
| 红、绿或黄色风水 | 500 |
| 蟑螂 | 每只减 2,500 |
| 地面上的非家具物品 | 每件减 1 |
| 地面上的垃圾 | 每件减 500 |
| 朝向墙壁的有方向物品 | 每件减 300 |
Nookipedia,“Happy Home Academy”,New Horizons 表格,2026 年 10 月 5 日保存;页面上未注明出处。6
这些数字是本节中最不确定的。加分表和扣分表都没有出处。下方的奖励表写着 “Includes data sourced from the Data Spreadsheet for Animal Crossing New Horizons”(包含来自 Animal Crossing New Horizons 数据表格的数据),由署名的贡献者整理;New Horizons 的单件得分一节写着 “This section is a stub”(本节内容尚不完整)。6 所以这套算法是粉丝的重建。文末的简报借用它的形态(列明的来源、数量门槛、套装与颜色加分、固定的日子),一个数字也不借。
对任何想做每周房间评分的应用来说,其中有两个选择很关键。门槛随房子一起上涨,所以房间变大并不会让最高等级更容易。而来信在固定的日子到达,大多数周日早晨,玩家无法关闭。6 缺席在那里并非没有代价:房屋页面说,玩家若 “for at least one week”(至少一周)不管房子,房子里就会闹蟑螂,而上表每只蟑螂扣 2,500 分。56 简报保留固定的日子,去掉惩罚。
拜访
按维基的说法,系列中的每一款游戏里,“a player may enter any other player’s house freely”(玩家可以自由进入其他玩家的房屋)。他们可以使用家具,但不能放下或拾起物品,“or modify the interior in any way”(也不能以任何方式改动室内)。5 到另一座岛上的梦境拜访(dream visit)以另一种方式达到同样的结果。在主持梦境的那个角色的维基页面上,New Leaf 一节说在梦里 “any changes will not be saved and items cannot be brought back to the real world”(任何改动都不会保存,物品也不能带回现实世界),New Horizons 一节说她的服务 “work the same as they do in New Leaf”(与 New Leaf 中相同)。上传梦境会让主人获得一张价值 5,000 铃钱的 “Dream Bell Exchange Ticket”(梦境铃钱兑换券),每次更新再得一张。32 依我的解读,这是本文中对儿童应用来说最安全的一种拜访:访客走在一份副本里,改不了任何会被保存的东西,而主人因分享而得到回报。
3. Habbo、Stardew、Final Fantasy XIV、Club Penguin,以及这套模型从何而来
Habbo:房间就是商品,而商品是要卖的
Habbo 自己的帮助中心把所有权和访客规则说得很直白。“You can own 200 rooms.”(你可以拥有 200 个房间。)33 地板和墙面一经铺上便是永久的:“Wallpaper and flooring are stuck down - so once you put them down, they can’t be picked back up. You can put new wallpaper or floor down if you wish to change the look of your room.”(墙纸和地板是粘死的,一旦铺上就不能再拾起。想换房间的样子,可以铺新的墙纸或地板。)33 访客碰不得:“You can’t drop or manipulate furni in other Habbo’s rooms unless you’re a member of a group, and the owner of that group has enabled you to participate in the building of that room.”(除非你是某个群组的成员,且该群组的所有者允许你参与搭建那个房间,否则你不能在其他 Habbo 的房间里放下或操作家具。)18
家具经济以购买为主导。“To buy furni you need credits, diamonds or duckets.”(购买家具需要金币、钻石或 duckets。)18 Builders Club 按额度出借家具,会员每满一个月额度增加 250,还包括在平面图编辑器中保存自定义房间布局,并承诺 “Furni limits never go down.”(家具额度永不下降。)34 家具定义页面描述了这样几类:活动家具会被扣住 “for 4-12 months in order to leave an ample window for traders to build their businesses around them”(4 到 12 个月,好给交易者留出充足的时间围绕它们做生意),“will never be re-released”(永不再发行)的稀有品,买来后敲开 “to reveal another, random rare furni”(会开出另一件随机稀有家具)的可敲开稀有品,以及 LTD,即 “a special type of rare, which comes in limited quantities”(一种限量发行的特殊稀有品)。17 交易需要交易通行证,而且 “There is no guide to what items of furni are worth”(没有哪份指南告诉你哪件家具值多少)。35 Habbo 甚至限制房间里的随机性:“Placing more than three chance elements will disable all randomiser functions in the room.”(放置超过三个随机元素,会让房间里所有随机功能失效。)36
Habbo 自己的页面没有给出网格、堆叠高度或每个房间的家具上限;在帮助中心搜索 “furni”、“stack” 和 “floor plan”,也没有返回任何给出这些数字的文章。37 人们引用的数字来自 Nitro,一个开源的第三方客户端。它的平面图编辑器常量允许每条轴最多 64 块图块和一套 27 级的高度方案,本系列的建筑篇也从同一个文件引用过;我的早期笔记从同一客户端补充了一个从 0 到 40、步长 0.01 的堆叠高度工具,这次没有为本文重读。3839 这三项都请当作那个客户端的,而不是 Sulake 的。
依我的解读,Habbo 展示了当目录变成生意时,家具摆放循环会变成什么样。它的访客规则值得照搬;它的经济(刻意的稀缺、随机的内容、交易价值)则应当拒绝。
Stardew Valley:一次买下的目录,之后家具免费
Stardew Valley Wiki 的家具(Furniture)页面,修订版 193159,在 26 个分区中列出 718 行物品,不同的名称有 615 个。这些是我对页面行数的统计;电影海报分区使用了另一种标记,所以跳过了,“不同的” 指的是把列在两个分区里的同一物品合并为一个。4016 墙纸页面(修订版 193801)显示 137 种墙纸(目录内 123,目录外 12,无法获得 2),地板页面(修订版 190285)显示 97 种地板,都是按页面上的图标统计的,而不是按游戏的数据文件。404142
目录同时沿两条路增长。罗宾和旅行货车(Traveling Cart)都会 “offer a random selection of furniture each day they are open”(在营业的每一天提供随机挑选的家具)。“After the first Farmhouse upgrade, Robin also offers the Furniture Catalogue for sale”(第一次扩建农舍后,罗宾还会出售家具目录),在页面的目录表里售价 200,000g;摆放之后,它会以 0g “in unlimited quantity”(不限数量地)出售它所列出的家具。家具页面中有 278 行把家具目录列为来源。1640 页面上的例外仍然是例外:“Some furniture can be obtained only by donating items to the Museum”(有些家具只能通过向博物馆捐赠物品获得),或者在节日、赌场和其他来源获得。16 所以,一个游玩里程碑加上一笔用游戏内货币进行的大额购买,就终结了目录所列物品的稀缺,也只终结这些。
摆放反馈是逐图块的:“Furniture will display a green square on tiles where it can be placed. The tile will turn red if the furniture cannot be placed.”(家具会在可以摆放的图块上显示绿色方块;如果不能摆放,图块会变红。)16 墙纸是一个面对应一个动作:墙纸 “cover the whole room they’re placed in”(会覆盖所在的整个房间),贴墙纸时 “the character must be facing up by its wall”(角色必须站在墙边面朝上方);它们 “single-use and do not stack”(一次性使用,且不能叠放)。41 扩建前的农舍内部是 10×7,使用 16 像素图块,这是建筑篇从维基引用的。39
Final Fantasy XIV:一个同时也是帧预算的上限
Square Enix 在 Lodestone 发布的 7.5 版本说明提高了所有住宅上限。室内家具方面,公寓和个人房间从 100 增至 150,小型住宅从 200 增至 300,中型住宅从 300 增至 450,大型住宅从 400 增至 600;室外家具从 20 增至 40、30 增至 60、40 增至 80。7 同一份说明用自己的话交代了原因:“Please note that up to 400 furnishings can be displayed on-screen at once. If more than 400 furnishings are on-screen simultaneously, those on a different floor to your character will stop being displayed, with small, more distant furnishings being hidden first.”(请注意,屏幕上同时最多可显示 400 件家具。如果屏幕上同时有超过 400 件家具,与角色不在同一楼层的家具将不再显示,较小、较远的家具会先被隐藏。)7 一个上限,同时是设计预算和渲染预算。
说明里还加入了 “Furnishings from the FFXIV Furnishing Design Contest”(来自 FFXIV 家具设计大赛的家具):这是一场社区大赛,获奖设计在这个版本里成了家具。7 这就是比赛的安全形态:玩家来创作,工作室来评审并上线。
地块尺寸不在本文范围内。Square Enix 的住宅指南在 2026 年 10 月 5 日返回 HTTP 404,我没有再尝试其他官方页面,所以 FFXIV 为本文提供的只有 Lodestone 说明里的物品上限,别无其他。7
Club Penguin:每天一个赞,以及永不减少的总数
这里关于 Club Penguin 的每一个事实都是单一来源。Club Penguin Wiki 引用的官方博客存档文章,在 2026 年 10 月 5 日两次从 Wayback Machine 返回 HTTP 429,所以我无法核实。43
That Penguin Game 转载了它声称是游戏帮助页面的内容;我没有核实其来历。它的 “Your Igloo” 页面写道:“You can Like any igloo design once every day!”(每个冰屋设计你每天都可以点赞一次!)每个设计 “earns its own individual Likes”(各自获得独立的赞),汇入总数(Grand Total),而且 “Likes are permanent, so your Grand Total Likes will never decrease, even if you delete an igloo design!”(赞是永久的,所以即使你删掉某个冰屋设计,Grand Total Likes 也永远不会减少!)Popular 标签按赞的总数给冰屋排名,冰屋可以对 “Everyone”(所有人)开放,或只对 “Friends”(好友)开放。19
Club Penguin Wiki 说 “igloos have a limit preventing more than 99 units of furniture being placed at once”(冰屋有一个上限,同时摆放的家具不能超过 99 件),大多数家具每种的拥有上限为 99 件,房主可以装饰自己的冰屋,“but not other player’s igloos”(但不能装饰其他玩家的冰屋)。8 装饰曾是会员福利:从 2005 年 11 月 1 日起,非会员玩家可以拥有冰屋,但不能摆家具;从 2012 年 7 月 26 日起,他们获得 6 件免费家具,即一件地面物品、四件普通物品和一件墙面物品。438
这个维基记载的冰屋大赛历史:第一届在游戏内报纸第 9 期公布,该期出版于 2005 年 12 月 15 日,一位获胜者获得 5,000 枚金币。之后的大赛在征集后一到四周公布获胜者和亚军,奖品总是金币,有时还有家具。从 2008 年万圣节大赛起,由角色评选获胜者,评语随冰屋一同刊出;从 2009 年起,冰屋上的一个按钮可以提交作品;最后一届大赛在 2012 年 12 月举行。举一个例子,2008 年的 Ye Olde Igloo Contest 给十位获胜者各 25,000 枚金币,二十位亚军各 15,000 枚。44
The Sims:小人们原本是来给房子打分的
网格加目录的模型有一个起源故事,而那是一个关于评分的故事。我的来源只有一篇访谈:Tristan Donovan 对 Will Wright 的访谈,由 Game Developer 于 2011 年 5 月 23 日刊发。我没有找到 Wright 在 1999 或 2000 年谈及建造模式的任何记录。45
关于灵感:“one of the original things that was a really inspiration for The Sims was this book, A Pattern Language, by Christopher Alexander.”(The Sims 最初真正的灵感来源之一,是 Christopher Alexander 的《A Pattern Language》这本书。)关于评分:“In some sense I wanted The Sims originally to be an architecture game where it was analyzing these patterns. So the people in The Sims originally were just there to score the architecture.”(某种意义上,我最初想把 The Sims 做成一款分析这些模式的建筑游戏。所以 The Sims 里的人最初只是在那里给建筑打分的。)关于名字:“my original name for it was Doll House. We did some test marketing and found out that the name didn’t go very well with males.”(我最初给它起的名字是 Doll House。我们做了一些市场测试,发现这个名字不太受男性欢迎。)关于物体:“Everything we put in the world is advertising”(我们放进世界的每样东西都在打广告),他把这套模型追溯到 SimAnt 里的信息素踪迹。45
The Sims Wiki 是粉丝维基、单一来源,它描述了上线后的界面。购买模式让玩家 “purchase items from an object catalog and place them on the current lot”(从物品目录中购买物品并放置在当前地块上);The Sims 和 The Sims 2 按物品功能给目录分类;手上物品的占地图块在可以放的地方显示绿色,不可以的地方显示红色。维基把这句红绿的描述标注给了 The Sims 2 和 The Sims 3,所以我不把它归于 2000 年的初代。46 评分作为一种需求留在了模拟市民身上:初代里叫 Room,二代里叫 Environment,它会 “analyzes the design and content of the room that the Sim is currently standing in”(分析模拟市民当前所在房间的设计和内容):房间狭小、光线不足、东西脏了或坏了,都会让它下降。47
依我的解读,本文的每一款游戏都是同一个界面,只是预算不同:一份目录,一张人一步大小的网格,一个会变绿或变红的占地,以及一个评委。
各家上限并排对比
这些上限从《绿宝石》的卧室到 FFXIV 的大型住宅相差五十倍,但它们统计的不是同一样东西:FFXIV 的是按住宅类型列出的室内摆放上限,Animal Crossing 的按房间,Club Penguin 的按冰屋,《绿宝石》的按基地或卧室,Kiradex 的按楼层。只有 FFXIV 和《绿宝石》的数字来自发行方自己的文字或代码。247581
4. 手艺,写成带数字的规则
以下是测量结果所支持的规则,每条都附上它所依据的范围。凡是数字出自我的计算或我的选择,该行都会注明。“格” 指 16 像素的行走格,它也是 Kiradex 的图块。227
| 要素 | 规则 | 来源 |
|---|---|---|
| 摆放上限 | 按房间限制一个房间所展示的数量,墙面物品也算在内。 | 《绿宝石》基地 16,卧室 12;New Horizons 每个房间 150,“including wall and ceiling-mounted furniture”(包括壁挂和吊顶家具);FFXIV 按住宅大小室内 150 到 600;Club Penguin 99(一个粉丝来源) |
| 拥有上限 | 让玩家拥有的比放得下的多,这样摆放就成了挑选。 | 《绿宝石》八个栏位共拥有 150 件,摆放 16 件;Club Penguin 每种拥有 99 件(粉丝来源) |
| 上限与地面之比 | 在没有墙面或天花板图层的 2D 房间里,保持在每三个地面格少于一件(这是我的门槛,选它是为了让《绿宝石》刚好落在其下);台面和可踩过的物件会把它拉宽。 | 《绿宝石》可行走格平均 60.2,对应 16 件,0.27;New Horizons 6×6 房间 150 件,每格 4.2,只有借助墙面、天花板和台面摆放才可能(我的计算) |
| 台面 | 让“托住一件小物件”成为格子的属性,这样一张桌子就为玩偶增加了地面。 | 《绿宝石》:18 件装饰品可托小件,14 件可托大件,45 件 sprite 装饰品需要这样的格子 |
| 网格格子 | 网格按角色的帧宽或格宽来定;只有在物件可以微调、且美术允许时,才做得更细。 | 《绿宝石》玩家帧为 16 像素格的 1×2;Stardew 16 像素图块;New Horizons 半格 |
| 摆放检查 | 向每个被覆盖的格子提一个问题(是地面、墙面还是台面;是否空着;是否不是到达格或主人格),并逐格显示答案。 | 逐格提问来自《绿宝石》的 CanPlaceDecoration,不过它只在图块图层类型非普通时才拒绝玩家的起始格;到达格和主人格是 Kiradex 的规则(7.1);Stardew 逐图块显示绿色或红色方块;The Sims 2 和 3 绿色或红色占地(粉丝维基) |
| 通路 | 每次摆放都检查行走路线是否还在(简报新增)。 | 《绿宝石》的 CanPlaceDecoration 不检查通路,也只在图块图层类型非普通时才让玩家所站的格子保持空着;这里读过的 Stardew 和 Sims 页面描述了逐图块反馈,对通路只字未提;简报加入行走检查 |
| 墙纸与地板 | 每个房间一种,一个动作就能更换,并且可以撤销。 | Stardew 从墙面覆盖 “the whole room”(整个房间);New Horizons 按房间,外加一面重点墙;Habbo 的是永久的,只能替换 |
| 目录规模 | 大到足以让风格成为一种选择。 | Stardew 墙纸 137、地板 97(我按维基图标的统计);New Horizons 262 和 215;New Horizons 截至 3.0.2 有 2,076 件家具 |
| 评分 | 列明来源,固定日子,随房子提高门槛,绝不惩罚缺席。 | 快乐之家学院:大多数周日;B、A、S;S 从 15,000 到 90,000;6、10、15、20 件各 1,000;系列、套装、类别、颜色(粉丝表格,无出处);维基的房屋页面没有指明是哪一款游戏,它说房子被冷落至少一周就会满是蟑螂,而 New Horizons 的 HHA 表每只蟑螂扣 2,500,这条规则拒绝这种惩罚 |
| 目录增长 | 对 9+ 的应用来说,靠游玩增长:一个赢来的展架,加上一份在里程碑时开放的目录。绝不靠随机或稀缺销售。 | 《绿宝石》120 件中 90 件出售,24 件从不出售,另两件要 6,000 ash 和 8,000 ash;Stardew 每日随机货品,第一次扩建后一份 200,000g 的目录,所列家具售价 0g,博物馆、节日等独有物品不在其中;Habbo 的稀有品、可敲开稀有品和 LTD 作为反例 |
| 拜访 | 访客只看不碰;每位访客每天一个标记;主人的总数永不下降。 | Animal Crossing 访客不能 “modify the interior in any way”(以任何方式改动室内);Habbo 访客没有授权不能放下或移动家具;Club Penguin 每个设计每天一个赞,Grand Total 永不减少(一个粉丝来源) |
| 存储 | 一件摆出的物品只需几个字节:种类、格子、朝向。 | 《绿宝石》:一个 ID 字节和一个位置字节,一个基地 32 字节 |
来源:第 1 到第 3 节及其脚注。256781916414033181746141511
其中三条值得各补一句。
上限是给眼睛的预算,有时也是给 GPU 的预算。 《绿宝石》的 16 大约是基地地面的四分之一。FFXIV 直说屏幕上超过 400 件家具时会停止绘制其中一部分。27 对手机应用来说,第二个理由是真实存在的,但依我的解读,决定这个数字的是第一个理由:正是上限把一个架子变成一幅构图,这也是 10 月 3 日那篇世界篇给出的选择十六的理由。10
通路规则是简报新增的。 《绿宝石》的 CanPlaceDecoration 最多守住一个格子,也就是打开菜单时所站的那一格,却从不问门口是否还能走到电脑跟前。4 我读过的 Stardew 和 Sims 页面描述的是逐图块着色的占地;它们没有说这两款游戏是否也检查通路,我也没有进一步查证,所以本文对它们不做任何一方面的断言。1646 别人会来拜访的房间需要回答这个问题,因为走不到展示品跟前的访客,什么也看不到。
对儿童应用的目录问题来说,靠游玩解锁是站得住脚的答案。 这是我的解读,不是法律建议:要花钱买、随机或稀缺的装饰循环,会惹上收藏类应用根本不需要的开箱和付费压力问题。《绿宝石》赢来的展架展示了一个完全不需要这些的房间循环,而 Stardew 的目录在一个游玩里程碑之后用游戏内货币一次买下,展示了如何终结它所列一切物品的稀缺。141617
5. Apple 的做法:一个会同步的模型,一次吸附的拖动,一张 VoiceOver 能用的网格
iOS 上的家具摆放界面需要三样东西:每件摆出的家具一条通过 iCloud 同步的记录,一种把家具拖到格子上的方式,以及一种不靠视觉、不靠触摸也能做同样事情的方式。下文全部来自 2026 年 10 月 5 日以 JSON 形式保存的 Apple 文档页面,标题、声明、摘要和平台由脚本提取。48 本文没有为此构建或运行任何东西,下面的成本也没有一项是测量过的。
记录:为 CloudKit 塑形的 SwiftData
@Model “Converts a Swift class into a stored model that’s managed by SwiftData”(把一个 Swift 类转换为由 SwiftData 管理的存储模型),自 iOS 17.0 起可用。49 SwiftData 可以用 #Unique 强制唯一性,它 “Specifies the key-paths that SwiftData uses to enforce the uniqueness of model instances”(指定 SwiftData 用来强制模型实例唯一性的键路径),自 iOS 18.0 起。50 对摆出的家具来说,这看上去正合用:每格一件。
CloudKit 同步排除了这条路。Apple 关于同步 SwiftData 的指南说,这个框架 “does include a small number of features that CloudKit doesn’t support natively, such as unique constraints and nonoptional relationships”(确实包含少数 CloudKit 原生不支持的功能,例如唯一约束和非可选关系),并且 “CloudKit requires all relationships to be optional.”(CloudKit 要求所有关系都是可选的。)22 Kiradex 的模型早已是这种形态。CardCopy 的注释写着 “every property has a default and nothing is unique, for CloudKit”(为了 CloudKit,每个属性都有默认值,没有任何东西是唯一的),PriceHistory 则是 “Shaped for CloudKit like CollectionEntry: defaults everywhere, nothing unique”(像 CollectionEntry 一样为 CloudKit 塑形:处处有默认值,没有任何东西是唯一的),当两台设备创建了同一份历史时,还有一个在每台设备上都挑出同一个胜者的折叠操作。5152
所以真正要紧的唯一性,也就是每格一件,必须在代码里强制:一个纯函数,每次读取时运行,把两台设备落在同一格上的家具在任何地方都裁定为同一个胜者,并把输的一方放回托盘。两台设备能一起破坏的不止一个格子。两次编辑各自在自己的手机上通过了所有规则,合在一起却可能失败,所以这个函数必须让房间经过整套摆放检查重建,而不只是做重叠测试。第 7 节的简报对此作了规定。
拖动:draggable、放置目标,以及手指下的格子
draggable(_:) 针对 Transferable 载荷,“Activates this view as the source of a drag and drop operation”(把此视图激活为拖放操作的来源),自 iOS 16.0 起。53 dropDestination(for:action:isTargeted:) 接收被放下的项目和一个 CGPoint,其页面把后者描述为 “the drop location in this view’s coordinate space”(在此视图坐标空间中的放置位置),同样自 iOS 16.0 起。54 把这个点映射到格子只是算术:减去地图原点,再除以图块以点为单位的尺寸。
有一项发现是我早期笔记里没有的。保存下来的 dropDestination(for:action:isTargeted:) 页面把 iOS 27.2 列为弃用它的版本,并附有消息 “Use dropDestination(for:isEnabled:action:) with an action that takes a DropSession parameter instead.”(请改用 dropDestination(for:isEnabled:action:),并使用接收 DropSession 参数的 action。)54 替代方案的 action 接收的是一个 DropSession,即 “A description of a drop that is in progress”(对一次进行中的放置的描述),而不是一个点。54 我没有保存 DropSession 的页面,所以它是否带有视图坐标空间中的位置尚未读到,简报把两种形式都列了出来。
要移动房间里已有的家具,DragGesture 是 “A dragging motion that invokes an action as the drag-event sequence changes”(随拖动事件序列变化而调用动作的拖动手势)(iOS 13.0),SpatialTapGesture 是 “A gesture that recognizes one or more taps and reports their location”(识别一次或多次点按并报告其位置的手势)(iOS 16.0):点按选中一件家具,拖动它逐格移动。5556
在 Kiradex 的相机下,放置点能否干净地映射到图块格子,尚未测试。房间由 RealityKit 以每纹素整数个设备像素绘制,接收放置的 SwiftUI 覆盖层叠在它上面;这道算术与相机适配已经在做的那道相同,但还没有人在上面放下过任何东西。10
不用触摸的网格:VoiceOver
一张家具网格并不适合逐个元素地轻扫,所以设计给 VoiceOver 配上了自己的动词:
accessibilityElement(children:)“Creates a new accessibility element, or modifies theAccessibilityChildBehaviorof the existing accessibility element”(创建一个新的辅助功能元素,或修改现有辅助功能元素的AccessibilityChildBehavior)(iOS 13.0),这样一件跨多格的家具就是一个元素,而不是六个。57accessibilityAction(named:_:)添加一个具名动作;“Actions allow assistive technologies, such as the VoiceOver, to interact with the view by invoking the action”(动作让 VoiceOver 等辅助技术可以通过调用动作与视图交互)(iOS 16.0)。左移、右移、上移、下移、转向和收起各是一个动作。58accessibilityAdjustableAction(_:)把上下轻扫作为AccessibilityAdjustmentDirection处理(iOS 13.0),适合循环切换墙纸或地板。59accessibilityRotor(_:entries:)创建 “an Accessibility Rotor with the specified user-visible label”(带有指定的、用户可见标签的辅助功能转子)(iOS 16.0),这样一个“家具”转子就能在房间的家具之间跳转。60AccessibilityNotification.Announcement是 “A notification that an app posts when it needs to convey an announcement to an assistive app”(应用需要向辅助应用传达播报时发出的通知)(iOS 17.0):每一次被拒绝的移动都会连同原因被朗读出来。61
然后 UI 测试可以用 XCUIApplication.performAccessibilityAudit(for:_:) 审查界面,它标注为 iOS 17.0;保存的页面没有摘要,并为这个模块路径列出 Xcode 16.3。62 它做的只有审查。在随 Xcode 27.0 安装的 XCUIAutomation 头文件中,这项审查 “Runs an accessibility audit on the current view”(对当前视图运行辅助功能审查)。同一批头文件自 iOS 27.0 起加入了 XCUIVoiceOverService,它 “Provides programmatic control of VoiceOver for UI testing”(为 UI 测试提供对 VoiceOver 的程序化控制):可以打开和关闭 VoiceOver,把焦点向前、向后、移入和移出容器,并返回 VoiceOver 对当前焦点元素朗读的内容。搜索该框架的所有头文件,找不到任何执行具名自定义动作、转动转子或读取播报的调用,所以 UI 测试无法像 VoiceOver 用户那样按下“左移”或“收起”。63 简报直接测试这些动作的处理函数,用 VoiceOver 服务检查朗读出的标签,把动作、转子和播报留给一位打开 VoiceOver 的真人来检查。
成本
未测量。一座布置好的房子,两层各最多 16 条小记录。如果摆出的家具并入引擎已经为锻炉家具构建的那一个物体网格,那么每次改动都意味着重建那个网格;这些都没有在手机上做过性能分析。39
6. 案例研究:Kiradex 如今的室内
Kiradex 是一款 iPhone 上的集卡应用,里面有一座小小的像素小镇 Kiradex World,每位收藏者有一栋两层的房子,小镇还有一座展览大厅。本节的一切都是 2026 年 10 月 5 日从应用的代码仓库读出的,没有运行或修改过:室内用脚本解析,文档和模型按文本阅读。964
自建筑篇以来的变化
10 月 3 日的建筑篇说,interiors.py 绘制的房间、楼上和大厅 “each 14 by 11, with a two-row wall band”(各为 14×11,带两行墙带)。39 写的时候确实如此。同一天,TestFlight 第 26 版带来了室内套件:“Rooms sixteen wide with a three-row wall band (cornice, face, wainscot)”(房间宽十六格,带三行墙带(檐口、墙面、护墙板)),家具采用 32 像素的节奏,靠墙的家具画进地面,独立摆放的家具则作为收藏者可以从其后方走过的物体。65 如今锻炉把 room 和 room-up 画成 16×12 格,把大厅画成 16×13,各带一条三行墙带:檐口、墙面和护墙板。964 第 26 版介于建筑篇描述的第 23 版和其晚间补记描述的第 35 版之间,所以两篇文字各自对自己那一版都是对的。3965
锻炉的家具
scripts/forge/interiors.py 在 PIECES 表中列出 23 种家具,每种都有以格为单位的宽和高,以及一个表示是否独立摆放的标志。9
| 类型 | 家具及占地(格,宽×高) |
|---|---|
| 靠墙,画进地面(9) | 书架 5×2;衣柜 2×2;台面、水槽和炉灶各 1×1;壁炉 2×2;上行楼梯和下行楼梯各 2×3;柱子 1×3 |
| 独立摆放,作为与收藏者排序前后的物体导出(14) | 床和玫瑰床各 2×3;床头柜 1×1;桌子 2×2;四个朝向的椅子各 1×1;沙发 3×2;长凳 2×1;植物 1×2;箱子、木板箱和木桶各 1×1 |
由 measure_kiradex_interiors.py 从 PIECES 解析;该文件从未被导入或运行。9
一件家具覆盖 1 到 10 格,平均 3.04 格,而《绿宝石》是 1 到 9 格、平均 2.43。92 不同的画稿比名称少:四把椅子是一张画稿的四个朝向,两张床是两个变体,两段楼梯是两个方向,台面、水槽和炉灶是同一个台面的三种类型。9
文件的文档字符串和表格不一致。文档字符串定下的约定是 “furniture on the 16-pixel grid in a 32-pixel rhythm, every piece at least two tiles on one axis”(家具位于 16 像素网格上,采用 32 像素节奏,每件家具至少在一条轴上占两块图块)。64 而在表里,23 种中有 11 种是 1×1:四把椅子、台面、水槽、炉灶、床头柜、箱子、木板箱和木桶。9 这个节奏对大件成立,对小件不成立。这是同一个文件 scripts/forge/interiors.py 里注释与代码之间的差异;简报保留小件,因为一个只有两格家具的房间就没有椅子了。
墙面有一套自己的词汇,用字母表示:画 p、钟 k、窗 N、带窗帘的窗 K、旗帜 n、壁灯 l,以及挂墙板的位置 W。地面有木地板 o、瓷砖 t、大理石 s,还有一块按相邻格子以九宫格方式绘制自己的地毯 r。墙面风格有两种,房屋的灰泥加木构和大厅的方石,通过 FACES 按室内选择。64
原样制作的房间
| 地图 | 尺寸 | 地面格 | 家具(可移动) | 家具下的地面 | 空地面 | 应用布置的位置 |
|---|---|---|---|---|---|---|
room |
16×12 | 125 | 14(13) | 25 | 100 | 书架 5 格,展示柜 2,书桌 2;到达、楼梯、主人 |
room-up |
16×12 | 125 | 12(11) | 27 | 98 | 墙板 3 格,书桌 2;在楼梯处到达 |
hall |
16×13 | 144 | 6(4) | 8 | 摆上 8 个展座后为 128 | 8 个展座 |
可移动的不含楼梯和柱子。出自 measure_kiradex_interiors.py。9
host 位置在文件里的注释是 “where the owner stands when you visit”(你来拜访时主人站的地方)。64 锻炉的 check() 已经会拒绝:落在错误字母上的书架、展示柜、书桌或展座位置,不在门或楼梯上的传送点,不可行走的到达格、楼梯格或主人格,以及同一格上的两个导出物体。它较窄的边界对简报很重要。它对墙板的位置没有字母检查,而且它的重叠循环只读取导出的物体,也就是独立摆放的家具,所以画进地面的靠墙家具从不在其中。它也不检查从门口能否走到每一个位置。64
在如今的房间里运行通路规则
我写了一个脚本,按 export() 的方式重建导出的字母,并从每层的到达格出发,在锻炉的可行走字母 otsrUD 上沿四个方向行走。20 在如今的布局中,楼下能走到楼梯和主人,楼上能走到楼梯。展示位置正前方的格子则是另一回事:
- 楼下书架前的五格:两格被木桶和木板箱压着,三格可以到达。
- 展示柜前的两格都能到达。
- 书桌前的两格:一格被拉到桌前的椅子压着,一格可以到达。
- 楼上的书桌也一样,两格中有一格。
- 楼上墙板前的三格被床和床头柜压着。一格也到不了。20
所以,如果规则要求每个位置自己的正前方格子都可到达,那么两层楼中锻炉布置的分组里有四组不合格(楼下的书架和书桌,楼上的书桌和墙板),被堵住的正前方格子共七个;如果规则要求每个布置分组至少有一个可到达的正前方格子,则只有一组不合格:楼上的墙板。简报采用分组形式,并在初始房间的重新布局中修好墙板。
脚本还尝试在 room 里加家具。多放一件独立家具,有 733 种摆法能通过格子规则;其中 44 种会把某个目标隔断,而且每一种都是靠直接盖住目标格本身。多放两件家具,各自离开所有目标且互不重叠,共有 229,891 种组合,其中 713 种不盖住目标却把它隔断:比如 (9,3) 的一张床和 (6,4) 的一个床头柜,就会把玻璃展示柜前的那块凹处封死。20 这 713 种,正是逐格检查漏掉、而行走能抓到的。它们也是两部手机能联手造出来的:每件家具单独放时,所有目标都能到达,所以各自在自己的设备上都能通过行走检查,只有两者都同步之后才会失败(7.7)。66 我没有把搜索扩展到两件以上的额外家具;到了十六件时,有多少布局会被逐格检查放行、却被行走拒绝,尚未测量。
收藏者与网格
收藏者的 sprite 格是 32×40 像素,人物上方留 7 像素、下方留 2 像素,所以人物约 31 像素高,这个格子宽 2 块图块、高 2.5 块,而《绿宝石》的帧是 1×2。WORLD.md 把它描述为 “a 30-pixel figure in a 32 × 40 cell”(32 × 40 格子里的一个 30 像素人物)。2767 人物本身的宽度没有测量。我把这个格子解读为锻炉在 16 像素网格上以 32 像素节奏绘制家具的原因:收藏者的格子有两块图块宽。
房间做不到的事
建筑篇那份坦白的清单对房子来说依然成立:“Rooms have no wallpaper or floor choice and no placeable furniture; the house is dressed from the collection and nothing else.”(房间没有墙纸或地板可选,也没有可摆放的家具;房子只靠收藏来布置,别无其他。)39 应用用收藏来布置那些有名字的位置(书架上放收藏册,展示柜里放卡牌,墙板上放缎带和徽章,大厅的展座上放卡牌),而所有家具都由锻炉摆放。6439
服务器目前还存不住一个房间。它的 rooms 模块写道:“Everything here is in memory; a room empties when its last collector leaves and nothing of them is kept.”(这里的一切都在内存中;最后一位收藏者离开后房间就清空,关于他们的任何东西都不会保留。)68 所以如今去另一位收藏者家里拜访,拜访的只是一张地图;要只读地拜访一位真实收藏者的布置,服务器得先能保存布局。
房间规则写在哪里
10 月 3 日的世界篇发布了房间规则:“a furnishing cap of sixteen”(家具上限十六);“visits are read-only, a like is once a day, and the room’s total never decreases”(拜访是只读的,点赞每天一次,房间的总数永不减少);以及 “A report arrives every Sunday grading the room, with its point sources listed”(每周日送来一份给房间打分的报告,列出得分来源)。10 这些规则不在应用的设计文档里。docs/WORLD.md 第 4 节列出了金币商店(“clothes, hats, backpacks, card-display frames and emotes”(服装、帽子、背包、卡牌展示框和表情动作))和按等级解锁、“that cannot be bought”(无法购买)的装扮,对家具、上限、点赞或周日报告只字未提。67 这些规则写在研究笔记 docs/research/pixel-art/06-collecting-and-hubs.md 的 5.3 节里,那里还要求来信是 “a pixel object that appears on the doormat”(出现在门垫上的一个像素物体),要求 “a stamp on their passport per room visited, with ranks at 30 / 100 / 500”(每拜访一个房间在护照上盖一个章,在 30 / 100 / 500 处升级),并在 5.7 节要求赞助者物品 “never a bigger furnishing cap”(绝不是更大的家具上限)。69 本文因为世界篇已经发布,所以把这些规则视为已定,而简报的第一项整理工作,就是把它们抄进 WORLD.md。世界篇的同一段还说 “Patron flair stays on the name-plate and the room skin”(赞助者的标识只出现在名牌和房间皮肤上)。10 简报不把房间皮肤视为已定:它把这件事重新交给 Blake 决定(7.5)。
每周的节奏已经存在
每周任务在 Quest.current 中按日历周轮换,大厅的主题就是这一周任务的标题,原型笔记还向测试者询问了 “Weekly reset on your calendar’s week”(按你的日历周每周重置)。7071 周日来信不需要新的时钟,不过出于 7.4 给出的理由,它要用自己的键,即那个周日的日期。
7. 简报:Kiradex 要做什么,以及必须通过的检查
这是一份简报,不是已上线工作的报告:下面的东西应用里一样都还没有。它是针对第 6 节测得的现状写成的差异:锻炉的 23 种家具,两层 16×12 的房子和一座 16×13 的大厅,布置好的位置,一个什么都不保存的服务器,一个通过 CloudKit 同步的 SwiftData 存储,每周任务的时钟,以及世界篇已经发布的房间规则。96810 每个要素都沿用上文测得的一种机制,而一切都用 Kiradex 自己的锻炉、调色板和措辞来画。没有任何要素会点出某种生物、任何游戏里的地点或某个系列的功能名称,房间里也没有任何东西是被投掷、捕捉、召唤或骑乘的,所以没有任何东西类似世界篇所描述的那些游戏方法专利的权利要求步骤;这是我的解读,不是法律建议。10 坐标采用锻炉的约定,从左上角的格子起,x 向右,y 向下。本节中的数字(初始的八件、评分表、房屋等级门槛、解锁进度)都是我的提议,不是事实。
7.1 按格摆放,用锻炉自己的词汇
房子里的一个布置(Arrange)模式。可摆放的家具是锻炉 23 种中的 20 种:除 stairs、stairs_down 和 pillar 以外的全部,这三种仍是布局的一部分。四把椅子合成一种带朝向的家具,所以这 20 件家具是 17 种地面家具。墙面上的画、钟、旗帜和壁灯成为四种可摆放的墙面物品,于是如今的锻炉一共提供 21 种;窗户和楼梯口仍属建筑。7.5 节会新增三件家具(一张展示桌、一扇屏风和一件压花墙面物品),总数达到 24。72 一件摆出的家具记为它在该层字母地图上左上角格子处的 (kind, x, y, facing),与 ROOM_FURNITURE 已经使用的形态相同。964
逐格的规则如下。前五条是把《绿宝石》的逐格提问换成 Kiradex 的字母重述一遍;第六条是简报新增的,是《绿宝石》的摆放函数不做的一项检查。4
- 独立家具要求每个被覆盖的格子都是地面(
o、t、s或r),且上面没有别的家具。 - 靠墙家具(书架、衣柜、台面、水槽、炉灶、壁炉)要求顶行落在墙带上,也就是锻炉现在放它们的位置:壁炉在 y = 1,横跨墙面和护墙板,其余在 y = 2,落在护墙板上。64
- 墙面物品需要第 1 行的一个墙面格(
A),且不能是窗户或楼梯口。 - 到达格、两个门格(锻炉把门画在底墙上,宽两格)、楼梯的可行走格以及主人格上都不能放家具。
- 书架随身带着它的五个书架位置,因为这些位置是相对于家具的,所以移动书架,收藏册也跟着动。玻璃展示柜
c和书桌d在本简报里仍是固定的字母。 - 通路规则。 摆放之后,从到达格出发在可行走格上沿四个方向行走,必须仍能到达楼梯格、主人格,以及每个布置分组(书架、展示柜、书桌、墙板)正前方至少一个格子。破坏行走的家具会被拒绝,拒绝时会说明它会把什么隔断。
两件橙色家具各自都能通过格子规则;合在一起,它们把展示柜围死了,只有规则 6 的行走能抓到这一点。2420
为什么用分组形式。在如今的布局里,位置前方的格子常常就是一件家具:书架前五格中有两格压着木桶和木板箱,每张书桌前都有一把椅子,墙板前的三格全被床和床头柜压着。20 锻炉已经说着这门语言的一部分:check() 会拒绝独立物体之间的重叠,以及落在错误字母上的书架、展示柜、书桌和展座位置。64 玩家得到的,是把这些规则扩展到每一个占地(包括靠墙家具)和墙板的格子,再加上行走检查。
验收。
- 一个单元测试针对 room.json 运行这六条规则:24 种家具(如今锻炉的 21 种加上 7.5 新增的三种)中的每一种,都在一个格子上被接受,并对每条适用的规则在一个格子上被拒绝。
- 通路规则在两层要上线的初始楼层(7.2)上都能通过,并有测试锁定楼上墙板有一个可到达的正前方格子。
- 一个属性测试在 room 和 room-up 中随机放置最多 16 件的合法序列,并在每一步之后断言:从到达格出发的行走能到达楼梯、主人以及每个布置分组的一个正前方格子。
- 一个回归测试在 room 的 (9,3) 放一张床、在 (6,4) 放一个床头柜,并断言第二件会被拒绝,拒绝信息点明展示柜。20
- 在 iPhone 18 Pro Max 模拟器上截取一张布置模式的图,显示手上家具的占地按格着色,一处合法,一处非法。
- 锻炉的 check() 在应用可能产生的每一种布局上都能通过:测试把摆好的布局导出为 ROOM_FURNITURE,以只读方式对它运行锻炉的检查。由于那项检查只读取导出的物体,也没有对墙板字母的检查,同一个测试还会用简报自己的校验器断言:任意两件摆出家具的占地(包括靠墙家具)都不共享格子,并且墙板的三个位置都落在原始地图里墙板的 W 格上。64
7.2 上限:每层十六件,初始房间八件
内容。 每张楼层地图摆放 16 件家具,room 和 room-up 各算各的,墙面物品也计入,就像 New Horizons 把墙面和吊顶物品算进它的 150 一样。5 收藏所布置的东西(书架上的收藏册、展示柜里的卡牌、墙板上的缎带和徽章)从不计入:上限预算的是家具,而收藏才是重点。拥有不受它限制;收藏者可以拥有的比放得下的多,正如《绿宝石》允许拥有 150 件、摆放 16 件。2
数字。 125 个地面格放 16 件,每格 0.13,是《绿宝石》0.27 的一半,这适合平均 3.04 格的家具(《绿宝石》是 2.43 格),也适合一位格子有两块图块宽的收藏者(我的计算)。9227 锻炉的初始 room 里已经有 13 件可移动家具,room-up 有 11 件,所以一位新收藏者到来时就已经 16 件占了 13 件,只剩三次选择。9 提议是:每个初始楼层以 8 件可移动家具上线,由锻炉挑选。楼下保留书架,因为它带着书架位置;楼上保留一张床,重新布局时至少清出墙板正前方的一个格子,而如今一个都没有。被移走的家具进入收藏者的托盘,而不是消失,于是每层 8 个空位从第一天起就归收藏者所有。20 初始家具以固定 ID 创建,这样两台冷启动的设备会创建相同的记录,而不是两套(7.7)。
与赞助无关。 研究笔记的那句话依然有效:赞助者物品 “never a bigger furnishing cap”(绝不是更大的家具上限)。69
验收。 一个单元测试摆放 16 件,并断言第 17 件被拒绝,拒绝信息点明上限;一个测试证明布置位置上的收藏册、卡牌、缎带和徽章不改变计数;一个测试证明每个赞助等级看到的上限都是 16;一个测试证明两个初始楼层加载时各有 8 件可移动家具,并通过通路规则。
7.3 墙纸与地板
内容。 每张楼层地图一种墙面风格和一种地板风格,在布置模式里各点一下就能更换,免费且可撤销:这是 Stardew 的一个面一个动作和 New Horizons 的按房间选择,而不是 Habbo 的永久性。41533 风格不计入 16 件。
已有的词汇。 两种墙面(plaster 和 blocks),三种地面(木地板 o、瓷砖 t、大理石 s),以及地毯。64 锻炉按规则从具名的调色板色阶来画,所以一种风格就是一个绘制程序加一条色阶。第一套是两种墙面各配三条色阶、三种地面各配两条色阶:六种墙面风格和六种地板风格,每一种都由锻炉绘制,没有一样是手绘的。更多的靠游玩获得(7.5)。
如何上线。 锻炉把每种风格作为图集格子导出,放在房间自己的格子旁边,应用替换墙面格和地面格的地面索引;地毯保留它的九宫格。
验收。 以每种墙面风格和每种地板风格各截一张 room 的图,共 12 张;一个调色板检查,证明每种风格的每个像素都是锻炉调色板的索引;一个测试证明更换风格不会改动已摆放的家具和布置位置。
7.4 周日来信:一份印出每项来源的每周评分
内容。 每周一次,在当地时间周日零点之后第一次打开应用时,一封信出现在门垫上(到达格上的一个物体),按房子当时的样子打分。每个周日一封,以当地时间那个周日的日期为键。任务板的周不能作为键:Quest.current 采用日历的 weekOfYear 区间,而这个区间从日历规定的一周起始日开始,所以周键和周日规则可能对不上。70 错过一周没有任何代价。来信印出每一行来源及其分数,然后是总分和房屋等级。名字用 Kiradex 自己的,暂定为策展人手记,绝不用它借来节奏的那款游戏的名字。
来源。 形态是 Nookipedia 所整理的快乐之家学院的形态(数量门槛、套装加分、颜色加分);数字是我的,而且收藏也在其中。6 上限按楼层地图计算,与 16 件一样。
| 来源 | 分数 | 上限 | 借鉴自 |
|---|---|---|---|
| 每件摆出的家具 | 100 | 每层 16 件 | 数量门槛 |
| 布置好的楼层:6、10 和 16 件 | 各 500 | 每层 1,500 | 6、10、15 和 20 件时各 1,000 |
| 套间:同一层有 4 件或以上来自同一锻炉套装 | 每件 200 | 每层 16 件 | 同一系列 4 件或以上 |
| 单一色阶:该层家具及其墙面和地面两个面中有 70% 属于同一色阶系 | 该层每件摆出的家具 100 | 每层 16 | 70% 同一颜色 |
| 单一色阶达 90%(取代 70% 那行) | 该层每件摆出的家具 300 | 每层 16 | 90% 同一颜色 |
| 书架:5 个收藏册位置全部用收藏填满 | 500 | 一次 | 《绿宝石》可托玩偶的桌子 |
| 展示柜:2 个位置各放一张卡 | 各 300 | 600 | |
| 墙板:3 个格子各放一枚徽章、印章或缎带 | 各 100 | 300 | |
| 扣分 | 无 | 通路规则已经拒绝被堵死的房间 |
提议。每一行都能从本地的房间和收藏算出。
这张表能给出的最高分是 23,600,按两层各 16 件、所有加分全部达成逐行算出;套间和色阶两行假定有一个大到能容下全部十六件的套装和色阶系,所以这是一个上界,不是谁真画出来的布局。一个 8 件的初始楼层,在套装、色阶或位置之前,得 1,300 分。21
在分数的顶端,构图(套装和色阶)比数量更值钱,这正是设上限的意义。2421
来信以三个房屋等级给房子评级,每个门槛都是这张表本身给一座参照房子打出的分数:4,500 是两层各 10 件、书架填满;7,600 是两层各 16 件、书架、展示柜和墙板都填满;14,000 是在那座房子上,每层再加一组 8 件的套间和 70% 单一色阶。初始房子,两层各 8 件,得 2,600 分,低于第一档。21 这三个数字是我的提议;来信上线前由 Blake 定下最终数字。与学院的不同,它们不会上涨,因为房子的形状从不改变(见下文“本简报不包括的内容”)。6 房屋等级不是缎带。应用已经保存的缎带来自小镇的收藏者们,存为 plaza.ribbons;墙板上展示的是这些缎带(第 6 节)。73 房屋等级印在来信上,并作为一个数字保存(7.5),从不上墙板。
评分从不读取的东西。 赞助等级、金币、购买、点赞、任何关于其他玩家的东西,或赞助者房间皮肤。每一行都能从本地的房间和收藏算出,所以收藏者可以确切地看到自己为什么得了这个分。如果赞助者房间皮肤上线,它画在该层的普通墙面风格之上,本身不是一种墙面风格:该层为评分保留那种普通风格,所以无论皮肤开还是关,色阶那几行计入的家具和面都相同(7.5)。
锻炉改动。 PIECES 增加一个 set 标签,按相同的材质绘制;首批套装就是把现有家具按其绘制材质分组。
验收。 一个纯函数 Score.week(room:collection:),每一行都有单元测试:楼层有 5、6、10 和 16 件;套间 3 件和 4 件;单一色阶 69%、70%、89% 和 90%;书架有 4 本和 5 本收藏册。一个测试证明最大值是 23,600。一个测试证明三座参照房子分别得 4,500、7,600 和 14,000 分,并获得第一、第二和第三档房屋等级,而得 2,600 分的初始房子一档也得不到。一个测试证明达到某个房屋等级不会给 plaza.ribbons 增加任何东西,也不会给墙板增加任何东西。一个测试证明每个周日的来信只生成一次(同一个周日日期出现两次只有一封,下一个周日另有一封),并且一周从周一开始的日历也会产生相同的键。一个测试证明只有赞助等级不同的两个档案得到完全相同的分数。一张 UI 截图,展示周日门垫上的来信,用一个设定日期的启动参数。
7.5 靠游玩解锁,绝不靠购买
内容。 收藏者拥有的目录由游玩推导出来。六个输入里有三个如今已经存在:由同步的收藏推导出的等级和徽章,以及只记录在设备上的任务印章。另外三个需要新的计数器,每一个都具名并同步:
- 等级。 WORLD.md 的曲线是
level = floor(sqrt(XP / 40)) + 1。67 从 2 级到 20 级每级解锁一种家具或风格,之后每两级一次,就像 WORLD.md 已经用等级锁住 “that cannot be bought”(无法购买)的装扮那样。67 如今已可用,由同步的收藏推导:应用从中算出经验值和等级,“so every device agrees without anything being stored”(这样无需存储任何东西,每台设备的结果都一致)。74 - 徽章。 每枚徽章解锁一件具名家具:Full Set 解锁展示桌,Vintage 解锁钟,Across the Sea 解锁屏风。第一件和最后一件是新的锻炉家具。67 如今已可用,按同样的计算由同步的收藏推导。74
- 每周任务。 每完成一个每周任务,增加一件基础家具:一把椅子、一盆植物、一个木板箱。70 如今已有记录,但只在设备上:印章存于
@AppStorage("quests.stamps"),那是 UserDefaults,不是同步存储,所以在任何家具依赖它们之前,先把它们移进PlayRecord(见下)。7375 - 步数。 一段长途行走:一件需要大量步数的家具,就像《绿宝石》的玻璃椅子要用一步步收集来的 6,000 ash 换。《绿宝石》的袋子不显示数量,工坊只在玩家回去时才说明还差多少;这里的提议是一个始终显示在家具托盘卡片上的计数器,显示已走步数与所需步数。2815 用窗台上植物做的一件压花是另一项单独的提议,它是一件墙面物品。它的解锁条件在致敬老宝可梦游戏,而不借用它们一文的简报里设定,而不由这一条的计数器决定:植物每 256 步长一个阶段,开花持续 1,024 步,然后掉下一朵花。新增:应用如今不记步数,所以需要
stepsWalked。73 - 拜访。 护照等级 30、100 和 500 个拜访过的房间,各解锁一种风格。69 新增:如今设备上和服务器上都没有记录拜访的东西,所以需要
roomsVisited,即主人的 ID。7368 - 来信。 收藏者第一次达到每个房屋等级时,解锁一件家具。新增:房屋等级只存在于本简报(7.4)中,所以需要
letterTier,即达到过的最高房屋等级。
模型差异。 任务印章和三个新计数器放在一个同步模型 PlayRecord 里,每台设备一条记录,形态与应用的其他模型一样为 CloudKit 塑形(7.7)。目录读取每台设备的记录:步数相加,印章和拜访过的主人取并集,房屋等级取最高。设备把自己那条记录的 uid 存在一个 @AppStorage 键下,与设备在广场里的 ID plaza.id 位于同一个 UserDefaults 命名空间。7375 每台设备只写自己的记录,所以两台设备永远不会编辑同一条记录,也就不需要合并规则。这只在指针留在一台设备上时成立,而恢复到第二部手机上的备份会把指针一起带过去(我的解读;没有测试过恢复)。所以记录带一个设备标记 writer,规则是:如果恢复后的设备发现指针所指的记录是由另一台设备写的,就新建一条记录。旧记录保留,目录仍会读取它。用什么值做一个备份不会带到新手机上的标记,本简报没有定下;由测试来锁定这条规则。改动后第一次启动时,设备把自己的 quests.stamps 列表复制进自己的记录,只复制一次。
拒绝的东西。 不把家具或风格做成应用内购买,不给家具标金币价格,不要任何形式的随机家具,不要限量家具,不允许交易家具:Habbo 的可敲开稀有品、稀有品和交易价值就是反例。1735 依我的解读:一个 9+ 应用,如果装饰循环靠购买或随机,就会惹上收藏类应用不需要的开箱和付费压力问题,而靠游玩解锁可以避开它们。研究笔记允许的唯一付费例外,是给赞助者的 “one room skin”(一款房间皮肤),世界篇也点明了它:“Patron flair stays on the name-plate and the room skin”(赞助者的标识只出现在名牌和房间皮肤上)。6910 同一句话也设了界限:赞助者标识 “never touches a pedestal slot or a judging bonus”(绝不碰展座名额或评判加成)。10 本简报不把皮肤当作已定之事,而是把它重新交给 Blake 决定。如果他保留皮肤,那么皮肤是一层视觉覆盖,不是一种墙面风格。楼层保留收藏者选的普通墙面风格,皮肤画在它上面,评分只读取普通风格,所以无论皮肤开还是关,7.4 的色阶几行都以相同的分母计入相同的家具和面。改为把墙面从色阶计数中剔除,并不中立,因为占比既算家具也算面。一层 16 件家具,其中 15 件和地板风格属于同一色阶,墙面在色阶之外,就是 18 中占 16,88.9%,落在 70% 那行,得 1,600 分,16 件摆出的家具每件 100,包括那件不在色阶里的;剔除墙面后就是 17 中占 16,94.1%,落在 90% 那行,得 4,800 分,16 件每件 300,等于给付费皮肤送了 3,200 分的评判加成。76 下面的测试会点名把皮肤单独划出来。
验收。 一个单元测试证明拥有的目录是 XP、徽章、任务印章、已走步数、拜访过的房间和房屋等级的纯函数,不随其他任何东西变化;一个测试证明两台设备的 PlayRecord 无论按哪种顺序读取,都给出相同的目录(步数相加,印章和主人取并集,房屋等级取最高);一个测试证明设备已有的 quests.stamps 列表只被复制进自己的记录一次,不会两次;一个测试证明,当设备的指针指向一条带有另一台设备 writer 的记录时,它会新建一条记录,旧记录保持不变,并且两条都继续计入;一个测试证明没有任何 StoreKit 产品标识符或金币商店物品对应到某种家具或某种风格,只有一个点名的例外:如果 Blake 保留赞助者房间皮肤,它的产品恰好对应一层覆盖、不对应任何墙面风格,而且应用它不会改变楼层用于评分的墙面风格;一个测试证明一个楼层在皮肤开和关时逐行得分相同、获得相同的房屋等级,并在每个门槛边界上运行:5、6、10 和 16 件,单一色阶 69%、70%、89% 和 90%,每种情况都分别让普通墙面在色阶系之内和之外,以及比每个房屋等级门槛低一分和恰好达到门槛的房子;一个目录托盘的快照测试,分别在 1 级(只有初始套装)和 20 级。
7.6 拜访:只读,每天一个赞,总数永不下降
内容。 访客借助小镇已经为自家收藏者的房子准备好的房间传送点,从门口走进主人的房子,39 看到主人摆出的家具、风格和布置好的位置。访客可以走动、说预设的话,但不能移动、拿走或添加任何东西:这是 Habbo 的规则,也是 New Horizons 的梦境靠不保留访客的任何改动所达到的结果。1832 门上的赞,每位访客对每位主人每个当地日只计一次,而主人的总数永不减少,即便重新布置也是如此:这是 Club Penguin 的规则,出自它那唯一的粉丝来源。19 拜访会给访客盖一个护照章,由访客的设备把它加进自己 PlayRecord 里的 roomsVisited(7.5)。69
服务器差异。 如今的服务器什么都不保存。68 拜访需要每位玩家两条小记录。第一条是房屋布局:两层最多 32 件摆出的家具,每件包括种类、x、y 和朝向,外加每层两个风格 ID。用带具名键的紧凑 JSON,每个槽位都填上提议中最长的种类名,风格 ID 取 16 个字符,共 1,974 字节,不到 2 KB;若只存裸的数字四元组,则是 385 字节(我的计算;《绿宝石》整个基地是 32 字节)。7211 第二条是一个点赞总数,连同当天的访客集合。两者都不包含玩家昵称以外的个人数据。服务器在保存布局前用同样的六条规则校验,所以被改过的客户端无法存入一个不合法的房间。7.5 的游玩计数器(任务印章、已走步数、拜访过的房间、房屋等级)不是服务器记录:它们通过收藏者自己的 iCloud 在 PlayRecord 中同步,服务器从不读取。
验收。 服务器测试:含有重叠、通路被堵或某层第 17 件家具的布局会被拒绝;访客在主人房间里的移动或摆放消息会被拒绝;同一访客同一天的两个赞只计一次,第二天的赞会计入;清空房间的主人仍保留总数。一次双模拟器行走检查:设备 A 布置,设备 B 拜访,并在相同的格子上看到相同的家具,两边各截一张图。
7.7 iOS 26 上的模型与界面
SwiftData。 两个模型,与应用的其他模型一样为 CloudKit 塑形:处处有默认值,没有任何唯一约束,以及一个在 init 中设为 UUID 的 uid,就像 Profile、PriceHistory 和 CardCopy 各自带有的那样。51775222
@Model final class PlacedPiece {
var uid: String = "" // set once, at creation; orders the repair
var roomMap: String = "room" // "room" or "room-up"
var kind: String = "" // a forge PIECES name
var x: Int = 0
var y: Int = 0
var facing: String = "down"
init() { uid = UUID().uuidString }
}
@Model final class PlayRecord { // one per device (7.5)
var uid: String = "" // the device's pointer to it is an @AppStorage key
var writer: String = "" // device marker: the one device that writes it
var questStamps: [String] = [] // moved from @AppStorage("quests.stamps")
var stepsWalked: Int = 0
var roomsVisited: [String] = [] // hosts' ids
var letterTier: Int = 0 // highest house tier reached, 0 to 3
init() { uid = UUID().uuidString }
}
墙面和地板风格作为字符串存在 Profile 上,紧挨着 lookData。77 没有 #Unique,也没有必需的关系,因为 CloudKit 两者都不支持。22 格子独占、上限和行走,都由代码里的一个函数强制。RoomLayout.repair(_:) 在每次读取时运行。它把一层已同步的家具按 uid 升序排列,从该层固定的建筑(字母地图、楼梯和柱子)开始重建这一层,按这个顺序逐件加入家具,前提是 7.1 的整套摆放检查针对已经接受的家具接受它:格子规则(因此没有重叠)、16 件的上限,以及通路规则。检查拒绝的家具回到托盘。每台设备以同样的方式排序同样的记录,所以每台设备重建出同一个房间;两件家具冲突时,uid 较小的一方获胜,这正是 PriceBook 在两台设备的价格历史中挑一份时已经在用的规则。52 顺序按 uid 而不是按摆放时间,因为时间来自每部手机自己的时钟。重叠并不是两部手机能制造的唯一冲突。在一部手机上放在 (9,3) 的床和在另一部手机上放在 (6,4) 的床头柜,各自在自己的设备上都通过整套检查,合在一起却把展示柜前的两个格子都隔断了;重建会保留 uid 较小的那件,把另一件退回。2066 由于拿走一件家具从不会切断通路,一个本已合法的楼层无论以什么顺序重建都会原样通过(这是我的推理;验收测试会锁定它)。7.2 的初始家具是两台设备在玩家没有任何操作时创建记录的唯一场合,所以每件都按所在楼层和槽位得到一个固定的 uid,从 starter.room.1 到 starter.room-up.8,而不是随机值:两台冷启动的设备会创建同样的十六个 uid,而不是十六对会被重建拆分到楼层和托盘里的记录。重建之前,repair 会把共享同一个 uid 的记录折叠为一条,就像 ModelContext.profile() 已经把同步留下的重复档案折叠到 uid 最小的那份一样。77 如果两份副本不一致,即一台设备在另一台首次启动之前就移动了它的初始家具,折叠会保留离开初始格子的那份副本;若两份都被移动过,则保留行号较小的那份,其次是列号较小的(我的提议)。如果收起一件家具会删除它的记录,那么在一部手机上收起的初始家具,会在第二部手机冷启动时回来;本简报没有解决这种情况。
摆放。 布置模式在房间的 RealityView 之上叠一个 SwiftUI 托盘。托盘里的物品是以其种类作为 Transferable 载荷的 draggable;房间视图是一个放置目标。53 在 iOS 26 上,dropDestination(for:action:isTargeted:) 把视图坐标中的放置点交给 action,这个点再按相机已知的整像素缩放映射到格子:cell = floor((point - mapOrigin) / tileSizeInPoints)。54 由于 Apple 的页面把这种形式列为在 iOS 27.2 中弃用,简报也会实现 DropSession 形式,如果会话带有位置,就从会话里读取;那个页面尚未读过。54 摆出的家具用 SpatialTapGesture 选中,用 DragGesture 移动,逐格吸附,其占地按格着色,就像 Stardew 给图块着色那样。565516
VoiceOver。 在布置模式里,每件摆出的家具是一个辅助功能元素,例如沙发,三乘二,第 9 列,第 8 行,带有具名动作左移、右移、上移、下移、转向(仅椅子)和收起。5758 一个名为“家具”的转子列出房间里的家具。60 托盘物品的放置动作把家具放在离到达格最近的合法格子上,并把焦点移到它上面。墙面和地板风格是可调节元素:上下轻扫即可循环切换。59 每一次拒绝都作为播报朗读出来,例如无法移动:沙发会挡住通往书架的路。61 7.1 的规则是一个函数,所以语音和触摸永远不会出现分歧。
验收。 一个单元测试证明 repair 无论以哪种顺序都以同样方式解决两台设备的重叠。双设备合并回归测试,以两条记录的两种输入顺序各运行一次:在锻炉如今制作的 room 上,设备 A 在 (9,3) 放一张床,设备 B 在 (6,4) 放一个床头柜;每次摆放单独都能通过整套检查;两者同步后,repair 对两种顺序返回同一个房间,保留 uid 较小的家具,把另一件连同点明展示柜的信息放进托盘,而从到达格出发的行走仍能到达展示柜前的两个格子;交换两个 uid 后,保留的是另一件。66 一个测试证明 repair 对一个 16 件的合法楼层不做改动,即使其记录被打乱成多种顺序。一个测试证明一层上 17 件已同步家具,除上限外彼此都合法,会被还原为 16 件,uid 最大的那件进托盘。一个测试证明两台冷启动、各自创建初始家具的设备,无论同步顺序如何,都会同步成每个初始格子一条记录、每层 8 件,托盘里空无一物;并且在另一台启动前于一台设备上移动过的初始家具,会留在被移动到的位置。一个 UI 测试,把一件托盘物品拖到一个格子上,并在该格子找到一个 PlacedPiece。VoiceOver 动作处理函数的单元测试,直接在布置模型上调用,而不经过 VoiceOver:放置会把一把椅子放在离到达格最近的合法格子上;左移、右移、上移和下移各移动一格,或说明原因拒绝;转向循环切换朝向;收起把它送回托盘;每一次拒绝都会生成界面将要发出的播报文本。在一台 iOS 27 模拟器上,一个 UI 测试通过 XCUIVoiceOverService 打开 VoiceOver,把焦点移到一件摆出的家具上,并断言其朗读标签,例如沙发,三乘二,第 9 列,第 8 行。63 用 performAccessibilityAudit 审查布置模式,不报告任何问题。62 在一台打开 VoiceOver 的设备上做一次手动检查,记下日期和构建版本,因为没有任何 XCUIAutomation 调用能执行自定义动作、转动转子或读取播报:从托盘放一把椅子,用动作把它向四个方向移动,转向,收起,用“家具”转子在家具之间跳转,并听到每次被拒绝的移动都被播报出来。在这一切之前,先做一次探针实验:在模拟器里以相机的真实缩放把一个载荷放到房间上,记录计算出的格子与手指下的格子,因为这个映射尚未测试。
7.8 整理工作
- 把房间规则连同本简报的数字写进 WORLD.md,让设计文档说出世界篇已经发布的内容。6710
- 让
interiors.py的文档字符串和表格二者择一地一致起来:要么文档字符串注明小件家具是例外,要么把小件家具重画成两格。简报保留它们。9
本简报不包括的内容
- 超出书架自身位置的堆叠:桌上不放家具,没有玩偶上桌那样的台面。台面这个想法,是上限和行走得到验证之后的下一步。
- 踩上去会动的家具:没有声音地垫,没有弹簧,没有滑梯。
- 半格摆放。网格保持为整个的 16 像素格子。
- 布置展览大厅。它的展座仍归每周展览所用。
- 家具摆放大赛。每周的大厅就是比赛,按它自己的规则评审。
- 平面图:房子的形状不变。
- 拜访共享广场之外的陌生人,以及主人自己总数之外的任何房间排名。
家具摆放中绝不做的事
- 任何部分都不用系列专有词汇:不用基地、装饰电脑、学院、唱片混音,也不用任何一件装饰品的名字。只用 Kiradex 自己的说法。本文点出这些游戏,是把它们作为已出版作品的事实,而不是作为功能。
- 不照着任何游戏的装饰品画家具:没有生物玩偶或坐垫,没有任何球形的东西,没有生物海报,没有以招式命名的地垫。锻炉的家具是家用家具,并且一直如此。
- 任何房间里都没有生物。植物就是植物。
- 没有付费、随机或限量的家具(7.5)。
- 房间里没有任何东西是被瞄准、投掷、捕捉、召唤、放出去战斗或骑乘的。
- 应用和锻炉里不含任何来自反编译的代码或数据。反编译源码只用于研究和测量;上面的规则都用 Kiradex 自己的话重新表述。
验收。 用一份由第 1 条中的系列专有词汇构成、再加上 “decoration”、“doll”、“Happy Home” 和 “Academy” 的词表,搜索字符串目录、锻炉的 PIECES 名称和服务器的表,结果为空。
核心要点
如果您负责画美术
- 按角色的帧宽或格宽来画家具。《绿宝石》的玩家帧宽一个 16 像素格,装饰品平均 2.43 格;Kiradex 的收藏者站在一个两块图块宽的格子里,家具平均 3.04 格。2729
- 目录以小件为主,配上几件大的锚点:《绿宝石》120 件装饰品中有 71 件是单格,桌子和 3×3 地垫就是让小件立在上面的锚点。224
- 用规则和色阶来画墙纸和地板,这样一种风格就是一个绘制程序加一套调色板,而不是一幅新画:两种墙面配三条色阶、三种地面配两条色阶,Kiradex 就各有六种。64
如果您负责做引擎
- 把一件摆出的家具存为种类、格子和朝向。《绿宝石》一件用两个字节存,一个基地用 32 字节。1112
- 先逐格检查摆放,再走一遍。《绿宝石》的逐格检查从不问门口是否还能走到任何东西;在 Kiradex 的一楼,229,891 对合法的额外家具组合中,有 713 对不盖住楼梯、主人或展示品,却把它们隔断。420
- 在 CloudKit 同步下,SwiftData 无法强制唯一约束,所以每格一件要做成一个纯粹的修复函数,在每台设备上挑出同一个胜者。让它以固定顺序、经过包括行走在内的整套摆放检查来重建房间:两次各自在自己手机上通过的编辑,合在一起可能把一个展示品隔断。225266
- 给 VoiceOver 动词,而不是一张要逐格轻扫的网格:具名的移动动作、一个家具转子、每次拒绝都有播报。586061
如果您负责设计游戏循环
- 限制摆放的,而不是拥有的:《绿宝石》摆 16 件、存 150 件。上限就是构图。2
- 在固定的日子评分并列出每一项来源,只在房子变大时提高门槛,绝不惩罚错过的一周:维基的房屋页面没有指明是哪一款游戏,它说房子被冷落至少一周就会满是蟑螂,而 New Horizons 的 HHA 表每只蟑螂扣 2,500 分。56
- 靠游玩扩充目录。《绿宝石》120 件装饰品中有 24 件从不用钱出售,另有两件只能用走路收集的火山灰换;Stardew 在第一次扩建后出售一份 200,000g 的目录,此后以 0g 出售它所列的家具;而 Habbo 稀缺又随机的家具,是儿童应用的反例。14151617
- 访客只看不碰,每天在一个永不下降的总数上留下一个赞。51819
常见问题
一个房间应该允许玩家摆放多少件物品?
多到足以构图,少到必须挑选。《宝可梦 绿宝石》把一个基地的摆放上限定为 16 件装饰品,卧室 12 件,约为一个平均基地 60.2 个可行走格的四分之一。按 Nookipedia 的说法,Animal Crossing: New Horizons 每个房间允许 150 件,墙面和吊顶物品也算在内;Final Fantasy XIV 自 7.5 版本起按住宅大小允许室内 150 到 600 件。对一个没有墙面或天花板图层的 2D 房间,我的门槛是上限低于每三个地面格一件,《绿宝石》的 0.27 略低于这一门槛;Kiradex 提议的 16 件是每个地面格 0.13。2579
《宝可梦 绿宝石》的装饰系统是如何运作的?
它就是数据。120 件装饰品各自带有一个类别、一个 1 到 9 格的形状、一个价格,以及五种权限之一(实心地面、可踩过的地面、靠后、墙面或 sprite)。CanPlaceDecoration 检查每个被覆盖的格子:普通地面、没有物体,并且在玩家所站的格子上没有图层类型非普通的图块;海报需要墙面格;玩偶和坐垫需要可托举的格子,大多数小格或大格皆可,1×2 的玩偶需要大格。18 件装饰品提供小的托举格:七张桌子、三块砖、一个轮胎和七块地垫。摆出的装饰品各以一个 ID 和一个位置字节存储。2411
快乐之家学院如何给房间打分?
按 Nookipedia 的表格(对 New Horizons 没有注明出处):大多数周日早晨寄来一份评价,等级为 B、A、S,S 级门槛随房子扩建从 15,000 升到 90,000,一个房间的物品达到 6、10、15 和 20 件时各得 1,000 分,系列、套装、类别、主色调和风水有逐件加分,蟑螂、垃圾和朝向墙壁的物品会扣分。逐件得分在维基上尚不完整。6
在 CloudKit 同步下,SwiftData 能强制每个网格格子只放一件物品吗?
用 #Unique 不行。Apple 关于同步 SwiftData 的指南把唯一约束列为 “that CloudKit doesn’t support natively”(CloudKit 原生不支持)的功能之一,而且 CloudKit 要求每个关系都是可选的。给每个属性一个默认值,不设任何唯一项,并用一个纯函数解决两台设备落在同一格上的家具:它在每次读取时运行,在每台设备上挑出同一个胜者。让这个函数以固定顺序、经过完整的摆放检查来重建房间,而不只是做重叠测试,因为两次各自合法的编辑,合在一起可能违反某条规则。225166
如何让一个拖放式家具网格对 VoiceOver 可用?
让每件摆出的家具成为一个辅助功能元素,标签里写明尺寸和所在格子;给它具名动作,可以朝每个方向移动一格、转向和收起;加一个列出房间家具的转子;并对每一次被拒绝的移动连同原因进行播报。Apple 的 accessibilityAction(named:_:)、accessibilityRotor(_:entries:) 和 AccessibilityNotification.Announcement 分别覆盖各个部分,performAccessibilityAudit 则在 UI 测试中检查界面。审查不会按下具名动作,而在 Xcode 27.0 中也没有任何 XCUIAutomation 调用能做到,所以请直接测试动作处理函数,并在打开 VoiceOver 的情况下手动检查动作、转子和播报。5860616263
儿童游戏应该出售家具吗?
依我的解读,对一个评级为 9+ 的应用来说,不应该:要花钱买、随机或稀缺的装饰循环,会惹上游戏不需要的开箱和付费压力问题。《绿宝石》120 件装饰品中有 24 件从不用钱出售(它们是奖品、对战点数兑换品和赠品),另有两件只能用走路收集的火山灰换;而第一次房屋扩建完成后,Stardew 会以 200,000g 出售一份家具目录,此后它以 0g 出售所列家具,不过仍有一些家具只能从博物馆、节日和其他来源获得。Habbo 以购买为主导、带有稀有品和随机可敲开稀有品的经济,展示的是另一端。14151617
为什么这些游戏里的访客只有只读权限?
依我的解读,因为房间是主人的表达。Animal Crossing 的访客不能 “modify the interior in any way”(以任何方式改动室内),Habbo 的访客没有房主授权不能放下或移动家具,Club Penguin 的房主可以装饰自己的冰屋,“but not other player’s igloos”(但不能装饰其他玩家的冰屋)。访客得到的,是一个会被计入的标记:Club Penguin 每个设计每天一个赞,累加在一个永不减少的 Grand Total 上。518819
本站相关文章:像素艺术建筑:iPhone 上的房屋、大厅与室内 建造了本简报要布置的室内,其室内一节描述的是第 26 版所取代的 14×11 房间;在 iPhone 上造一个像素艺术世界 是十六件的上限、只读拜访、每日点赞和周日报告最初发布的地方;像素艺术人物:iPhone 上的角色与捏人器 讲的是那位以自身宽度决定家具节奏的收藏者;像素艺术小镇布局:从真新镇到鹈鹕镇的实测 测量了房子所在的小镇;像素艺术的运动:iPhone 上的行走、相机与门 讲的是访客进门时的行走和门;像素艺术路线:iPhone 上的地图连接与街区 讲的是小镇之外的土地。
参考来源
-
pret,
pokeemerald/include/constants/global.h(SECRET_BASES_COUNT 20、DECOR_MAX_SECRET_BASE 16、DECOR_MAX_PLAYERS_HOUSE 12),提交731ad5b(2026 年 10 月 1 日),2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/include/constants/global.h ↩↩↩↩↩ -
作者测量,2026 年 10 月 5 日:本文作者证据文件夹中的
measure_emerald_decor.py,输出保存为measure_emerald_decor.out,在 pretpokeemerald提交731ad5b的浅克隆上运行。它从include/constants/global.h读取上限;从src/data/decoration/header.h读取 120 件装饰品(不含DECOR_NONE)的类别、形状、权限和价格;从include/global.h读取按类别划分的拥有数组;从data/tilesets/secondary/secret_base/metatile_attributes.bin读取每个装饰品 metatile 的行为字节;从data/layouts/layouts.json及其map.bin读取 24 种基地布局和卧室,按碰撞位统计可行走格。每件装饰品的格数取自形状名称;上限除以平均可行走格数,16 / 60.2 = 0.27。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/data/decoration/header.h(gDecorations[]:每件装饰品的名称、权限、形状、类别、价格和图块;条目 0 为DECOR_NONE),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/src/data/decoration/header.h ↩↩↩↩ -
pret,
pokeemerald/src/decoration.c(CanPlaceDecoration、DecorationItemsMenuAction_AttemptPlace、isPlayerRoom、gText_CantPlaceInRoom;sprite 装饰品的托举格检查),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/src/decoration.c 。在 sprite 的情况下,1×2 的装饰品需要MetatileBehavior_HoldsLargeDecoration,其他的小或大托举格皆可;IsntInitialPosition只在装饰品于玩家起始格上的图块图层类型不是METATILE_LAYER_TYPE_NORMAL时才拒绝该格。函数中没有任何检查通路的部分,这是作者对它的解读。 ↩↩↩↩↩↩↩↩↩↩ -
Nookipedia,“Player house”(每个房间的上限:Wild World 24,City Folk 64,New Leaf 48,New Horizons 150 “including wall and ceiling-mounted furniture”;New Horizons 带房间尺寸的扩建表;“only the main room can be expanded in size”;2.0 起的重点墙;访客不得 “modify the interior in any way”;玩家若 “for at least one week” 冷落房子,房子会闹蟑螂),2026 年 10 月 5 日抓取并保存为房间篇证据文件夹中的
nook-player-house.html和.txt,https://nookipedia.com/wiki/Player_house 。每格 4.2 件是作者的计算(150 / 36)。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Nookipedia,“Happy Home Academy”(周日评价;各次扩建的 B、A、S 等级;New Horizons 的加分与扣分;New Horizons 逐件得分处的 “This section is a stub”;奖励表的说明 “Includes data sourced from the Data Spreadsheet for Animal Crossing New Horizons”),2026 年 10 月 5 日抓取并保存为
nook-hha.html和.txt,https://nookipedia.com/wiki/Happy_Home_Academy 。页面上的加分表和扣分表没有注明出处。 ↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Square Enix,The Lodestone,Final Fantasy XIV 7.5 版本说明(2026 年 5 月 13 日更新;住宅部分室内和室外家具上限的调整前后;400 件家具显示的说明;“Furnishings from the FFXIV Furnishing Design Contest have been added”),2026 年 10 月 5 日抓取并保存为
ffxiv-patch-7-5.html和.txt,https://eu.finalfantasyxiv.com/lodestone/topics/detail/9beb8a7b5c46944cd80ed92b2b8e972395fbea80/ 。住宅玩法指南 https://na.finalfantasyxiv.com/lodestone/playguide/contentsguide/housing/ 当天返回 HTTP 404;本文未为地块尺寸提供出处。 ↩↩↩↩↩↩↩↩↩↩ -
Club Penguin Wiki(Fandom),“Furniture”(大多数家具每种 99 件的拥有上限;“igloos have a limit preventing more than 99 units of furniture being placed at once”;房主可以装饰自己的冰屋,“but not other player’s igloos”;2012 年 7 月 26 日给非会员的六件免费物品),通过该维基的 MediaWiki API 读取,2026 年 10 月 5 日保存为
cp-fandom-furniture.html和.txt,https://clubpenguin.fandom.com/wiki/Furniture 。单一来源。 ↩↩↩↩↩↩ -
作者测量,2026 年 10 月 5 日:房间篇证据文件夹中的
measure_kiradex_interiors.py,输出measure_kiradex_interiors.out,用 Python 的ast解析 Kiradex 仓库中的scripts/forge/interiors.py(从不导入或运行该文件):PIECES的 23 个条目及其宽、高和独立摆放标志;ROOM、ROOM_UP和HALL字母地图及其家具列表和位置;地面格按字母o t s r统计;可移动家具不含stairs、stairs_down和pillar。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Blake Crosley,“Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew”,blakecrosley.com,2026 年 10 月 3 日(房间规则:“a furnishing cap of sixteen”、只读拜访、每天一次点赞、永不减少的总数、“with its point sources listed” 的周日报告;同一段中的 “Patron flair stays on the name-plate and the room skin”;Palworld 诉讼中提到的专利;按每纹素设备像素数适配的相机),https://blakecrosley.com/blog/pixel-art-world-on-iphone ↩↩↩↩↩↩↩↩↩↩
-
pret,
pokeemerald/include/global.h(秘密基地记录中的u8 decorations[DECOR_MAX_SECRET_BASE]和u8 decorationPositions[DECOR_MAX_SECRET_BASE];存档中按类别划分的装饰品拥有数组),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/include/global.h ↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/secret_base.c(读取装饰品位置时,高半字节为 x,低半字节为 y),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/src/secret_base.c ↩↩↩ -
pret,
pokeemerald/include/decoration.h(enum DecorationPermission及注释 “The nomenclature here describes collision and placement permissions, in that order.”;enum DecorationShape,其中DECORSHAPE_3x1和DECORSHAPE_1x3标为未使用),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/include/decoration.h ↩↩↩ -
作者测量,2026 年 10 月 5 日:房间篇证据文件夹中的
measure_emerald_sources.py,输出measure_emerald_sources.out,在 pokeemerald731ad5b中搜索每一个DECOR_常量:data/maps/*/scripts.inc中的pokemartdecoration列表、奖品角的脚本、src/data/battle_frontier/battle_frontier_exchange_corner.h中对战设施的兑换表,以及data/下任何地方givedecoration和adddecoration的第一个参数:五家商店用钱出售 90,奖品兑换处 3,对战点数 15,脚本赠送 10,从不用钱出售 24,以上都不属于 6。六件中有两件是玻璃工坊那一对(注 15);另外四件,三个传说玩偶和另一个玩偶,没有追查。 ↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/data/maps/Route113_GlassWorkshop/scripts.inc(PRETTY_CHAIR_PRICE, 6000、PRETTY_DESK_PRICE, 8000,从VAR_ASH_GATHER_COUNT中支付;Route113_GlassWorkshop_EventScript_NotEnoughAsh和_NotEnoughAshForItem从价格中减去计数,并把差额显示为还要走的步数;在src、data和include中搜索,VAR_ASH_GATHER_COUNT除定义外只在那里和field_tasks.c中使用)以及pokeemerald/src/field_tasks.c(携带袋子时,走过火山灰草丛每一步得一份火山灰,上限 9999),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/data/maps/Route113_GlassWorkshop/scripts.inc 和 https://github.com/pret/pokeemerald/blob/master/src/field_tasks.c ↩↩↩↩↩↩↩ -
Stardew Valley Wiki,“Furniture”,修订版 193159(罗宾和旅行货车 “offer a random selection of furniture each day they are open”;“After the first Farmhouse upgrade, Robin also offers the Furniture Catalogue for sale”;0g “in unlimited quantity”;目录表中家具目录一行,在木匠的店(Carpenter’s Shop)售价 200,000g;“Some furniture can be obtained only by donating items to the Museum”、节日、赌场和其他来源;绿色和红色的摆放方块),2026 年 10 月 5 日抓取并保存为
sdv-furniture.html和.txt,https://stardewvalleywiki.com/Furniture ↩↩↩↩↩↩↩↩↩↩↩ -
Sulake,Habbo 帮助中心,“Furni Definitions”(为交易者扣住 “4-12 months” 的活动家具,“will never be re-released” 的稀有品,可敲开的稀有家具,“in limited quantities” 的 LTD),文章正文于 2026 年 10 月 5 日保存在房间篇证据文件夹的
habbo-articles.txt中,https://help.habbo.com/hc/en-us/articles/360011621699-Furni-Definitions ↩↩↩↩↩↩↩ -
Sulake,Habbo 帮助中心,“What is Furni?”(“To buy furni you need credits, diamonds or duckets”;“You can’t drop or manipulate furni in other Habbo’s rooms unless you’re a member of a group”),文章正文于 2026 年 10 月 5 日保存在
habbo-articles.txt中,https://help.habbo.com/hc/en-us/articles/360011512940-What-is-Furni ↩↩↩↩↩↩↩ -
That Penguin Game,“Your Igloo”(对 Club Penguin 帮助页面的转载;其来历未经核实:“You can Like any igloo design once every day!”;单个设计的赞和 Grand Total Likes;“Likes are permanent, so your Grand Total Likes will never decrease, even if you delete an igloo design!”;Everyone 和 Friends 设置),2026 年 10 月 5 日抓取并保存为
tpg-your-igloo.html和.txt,https://thatpenguingame.com/club-penguin-help/game-help/your-igloo/ 。单一来源。 ↩↩↩↩↩↩ -
作者测量,2026 年 10 月 5 日:
measure_path_rule.py,保存在文章草稿旁,输出measure_path_rule.out和path_rule.json。它用ast解析 Kiradex 仓库中的scripts/forge/interiors.py,按export()的方式重建导出的字母,从每层的到达格出发在otsrUD(锻炉check()的可行走集合)上沿四个方向行走,并报告楼梯、主人以及每个布置位置的正前方格子(越过书架和墙带后正南方向的第一个格子)。随后在room中尝试:在远离到达格、楼梯格和主人格的空地面上额外放一件独立家具的所有摆法(10 种画稿:椅子算一次,床算一次)(733 种摆法;44 种隔断某个目标,全部是靠盖住目标),以及额外放两件、都远离所有目标且互不重叠的所有组合(689 种单件摆法,229,891 对;713 对隔断某个目标)。 ↩↩↩↩↩↩↩↩↩↩ -
作者计算,2026 年 10 月 5 日:草稿文件夹
figures/中的score_max.py,输出score_max.out和score_max.json:简报的评分表在两层各 16 件、每行都达到其上限时的总和(3,200 + 3,000 + 6,400 + 9,600 + 500 + 600 + 300 = 23,600),以及一层 8 件、没有套装、色阶或位置各行时的得分(800 + 500 = 1,300)。同一脚本用表格自身的各行,为设定拟议房屋等级门槛的三座参照房子打分:两层各 10 件、书架填满,4,500;两层各 16 件、书架、展示柜和墙板都填满,7,600;那座房子再加每层一组 8 件的套间和 70% 单一色阶,14,000;以及初始房子,两层各 8 件,2,600。 ↩↩↩↩ -
Apple 开发者文档,“Syncing model data across a person’s devices”(SwiftData;“does include a small number of features that CloudKit doesn’t support natively, such as unique constraints and nonoptional relationships”;“CloudKit requires all relationships to be optional”),文档 JSON 于 2026 年 10 月 5 日保存为
apple-swiftdata-cloudkit-sync.html和.txt,https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices ↩↩↩↩↩↩ -
pret,
pokeemerald/src/new_game.c(NewGameInitData调用ClearDecorationInventories())和pokeemerald/src/decoration_inventory.c(ClearDecorationInventory把每个类别的每个栏位设为DECOR_NONE),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/src/new_game.c ↩ -
作者制图,2026 年 10 月 5 日:草稿文件夹
figures/中的make_figures.py,它读取 pokeemerald731ad5b中的src/data/decoration/header.h、注 1、5、8 和 7 所引用的上限、path_rule.json和score_max.json,用上面的测量结果断言每一个总数,并写出emerald-decor-sizes.svg、caps.svg、room-path-rule.svg和score-sources.svg。占地分类(1 格、2 格、4 到 6 格、8 或 9 格)按形状覆盖的格数分组。调色板用 dataviz 校验器检查过;不含任何游戏美术。 ↩↩↩↩↩ -
pret,
pokeemerald/include/constants/metatile_behaviors.h(MB_SECRET_BASE_*行为、MB_SLIDE_SOUTH、MB_HOLDS_SMALL_DECORATION、MB_HOLDS_LARGE_DECORATION),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/include/constants/metatile_behaviors.h ↩ -
pret,
pokeemerald/data/layouts/layouts.json(24 种LAYOUT_SECRET_BASE_*布局和两位玩家房屋的二楼,含宽和高)以及每种布局的map.bin,提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/data/layouts/layouts.json 。宽、高和面积范围是作者根据测量输出得出的。 ↩ -
作者测量,2026 年 10 月 5 日:房间篇证据文件夹中的
measure_character_cell.py,输出measure_character_cell.out,读取 pokeemerald 的graphics/object_events/pics/people/brendan/walking.png(144×32)和decorating.png(16×32)的 PNG 头,以及 Kiradex 仓库Kiradex/Resources/World/characters.json中的格子(32×40)、figureTop(7)和underFeet(2)。脚本测量的是格子和人物的高度(31 像素),不是所画人物的宽度。 ↩↩↩↩↩ -
pret,
pokeemerald/src/data/items.h(ITEM_SOOT_SACK,.fieldUseFunc = ItemUseOutOfBattle_CannotUse)和pokeemerald/src/data/text/item_descriptions.h(sSootSackDesc,一段固定的三行说明),提交731ad5b,2026 年 10 月 5 日访问,https://github.com/pret/pokeemerald/blob/master/src/data/items.h ↩↩ -
Nookipedia,“Furniture”(尺寸 “ranging from 1.0×0.5” 到 3×3;半格推动;New Horizons 的家具类型和 2.0 起的吊顶装饰;截至 3.0.2 有 2,076 件家具,其中 1,074 件为更新加入;一个系列是 “around 10 furniture items, as well as a matching wallpaper and flooring”),2026 年 10 月 5 日抓取并保存为
nook-furniture.html和.txt,https://nookipedia.com/wiki/Furniture ↩↩↩↩ -
Nookipedia,“Wallpaper”(截至 1.9.0 版,New Horizons 有 262 种墙纸),2026 年 10 月 5 日抓取并保存为
nook-wallpaper.html和.txt,https://nookipedia.com/wiki/Wallpaper ↩ -
Nookipedia,“Flooring”(New Horizons 中 215 种),2026 年 10 月 5 日抓取并保存为
nook-flooring.html和.txt,https://nookipedia.com/wiki/Flooring ↩ -
Nookipedia,“Luna”(New Leaf 一节:“any changes will not be saved and items cannot be brought back to the real world”;New Horizons 一节:她的服务 “work the same as they do in New Leaf”,每次上传得一张价值 5,000 铃钱的 Dream Bell Exchange Ticket),2026 年 10 月 5 日抓取并保存为
nook-luna.html和.txt,https://nookipedia.com/wiki/Luna ↩↩ -
Sulake,Habbo 帮助中心,“Rooms”(“You can own 200 rooms.”;“Wallpaper and flooring are stuck down”),2026 年 10 月 5 日抓取并保存为
habbo-rooms.html和.txt,https://help.habbo.com/hc/en-us/articles/360011512640-Rooms ↩↩↩↩ -
Sulake,Habbo 帮助中心,“What is Builders Club?”(家具额度每个会员月 “increased by 250”;在平面图编辑器中保存自定义房间布局)和 “What can I buy in Habbo?”(“Furni limits never go down.”),文章正文于 2026 年 10 月 5 日保存在
habbo-articles.txt中,https://help.habbo.com/hc/en-us/articles/360011621659-What-is-Builders-Club 和 https://help.habbo.com/hc/en-us/articles/360011620599-What-can-I-buy-in-Habbo ↩ -
Sulake,Habbo 帮助中心,“Player to player trading in Habbo”(需要交易通行证;“There is no guide to what items of furni are worth”),文章正文于 2026 年 10 月 5 日保存在
habbo-articles.txt中,https://help.habbo.com/hc/en-us/articles/4408726781842-Player-to-player-trading-in-Habbo ↩↩ -
Sulake,Habbo 帮助中心,“Guidelines on user created games in Habbo”(“Placing more than three chance elements will disable all randomiser functions in the room.”),文章正文于 2026 年 10 月 5 日保存在
habbo-articles.txt中,https://help.habbo.com/hc/en-us/articles/360011513060-Guidelines-on-user-created-games-in-Habbo ↩ -
Sulake,Habbo 帮助中心搜索 API,“furni”、“stack” 和 “floor plan” 的结果集,2026 年 10 月 5 日保存在房间篇证据文件夹中,为
habbo-search-furni、habbo-search-stack和habbo-search-floorplan(.html和.txt);没有一篇给出网格尺寸、堆叠高度或每个房间的家具上限。 ↩ -
billsonnn,
nitro-react/src/components/floorplan-editor/common/Constants.ts(MAX_NUM_TILE_PER_AXIS = 64,HEIGHT_SCHEME = 'x0123456789abcdefghijklmnopq',空的x之后有 27 级高度),一个第三方开源 Habbo 客户端,如 2026 年 10 月 3 日建筑篇所引,https://github.com/billsonnn/nitro-react/blob/main/src/components/floorplan-editor/common/Constants.ts 。堆叠高度工具的范围(0 到 40,步长 0.01)来自我的早期笔记,读自同一客户端,2026 年 10 月 5 日没有重读。 ↩ -
Blake Crosley,“Pixel-Art Structures: Houses, Halls and Interiors on iPhone”,blakecrosley.com,2026 年 10 月 3 日(室内 “each 14 by 11, with a two-row wall band”;第 22、23 版,以及晚间补记中的第 35 版;“Rooms have no wallpaper or floor choice and no placeable furniture; the house is dressed from the collection and nothing else”;
room:npc-…传送点;物体网格;Stardew 农舍内部 “10×7”,注 54;Nitro 常量,注 62),https://blakecrosley.com/blog/pixel-art-structures-on-iphone ↩↩↩↩↩↩↩↩ -
作者测量,2026 年 10 月 5 日:房间篇证据文件夹中的
measure_sdv_catalogue.py,输出measure_sdv_catalogue.out:在保存的家具页面(rev 193159)上按每行的名称链接逐分区统计家具行,跳过行使用另一种标记的电影海报分区,以及 References、History 和 Exploits 分区;墙纸和地板按保存的墙纸页面(rev 193801)和地板页面(rev 190285)上不同的图标统计;以家具目录为来源的行按其链接统计。这是维基行数和图标数,不是游戏数据文件里的数量。 ↩↩↩↩ -
Stardew Valley Wiki,“Wallpaper”,修订版 193801(“Wallpapers are single-use and do not stack. They cover the whole room they’re placed in”;“the character must be facing up by its wall”),2026 年 10 月 5 日抓取并保存为
sdv-wallpaper.html和.txt,https://stardewvalleywiki.com/Wallpaper ↩↩↩↩ -
Stardew Valley Wiki,“Flooring”,修订版 190285,2026 年 10 月 5 日抓取并保存为
sdv-flooring.html和.txt,https://stardewvalleywiki.com/Flooring ↩ -
Club Penguin Wiki(Fandom),“Igloo”(历史:2005 年 11 月 1 日,非会员玩家也有了冰屋,但他们 “were unable to decorate them with furniture”;2012 年 7 月 26 日,新的冰屋体验和 “a set of six items available without membership”),通过 MediaWiki API 读取,2026 年 10 月 5 日保存为
cp-fandom-igloo.html和.txt,https://clubpenguin.fandom.com/wiki/Igloo 。它所引用的官方博客存档文章在 2026 年 10 月 5 日两次从 Wayback Machine 返回 HTTP 429。单一来源。 ↩↩ -
Club Penguin Wiki(Fandom),“Igloo Contests”(第一届大赛刊于第 9 期,2005 年 12 月 15 日,一位获胜者得 5,000 枚金币;获胜者在 “After one to four weeks” 后公布;2008 年万圣节大赛起由角色评审;2009 年起的提交按钮;最后一届在 2012 年 12 月;2008 年 Ye Olde Igloo Contest 十位获胜者各 25,000 枚金币,二十位亚军各 15,000 枚),通过 MediaWiki API 读取,2026 年 10 月 5 日保存为
cp-fandom-contests.html和.txt,https://clubpenguin.fandom.com/wiki/Igloo_Contests 。单一来源。 ↩ -
Tristan Donovan,“The Replay Interviews: Will Wright”,Game Developer,2011 年 5 月 23 日,2026 年 10 月 5 日抓取并保存为
gd-replay-wright.html和.txt,https://www.gamedeveloper.com/business/the-replay-interviews-will-wright 。没有找到 Wright 在 1999 或 2000 年谈及建造模式的录音或录像。 ↩↩ -
The Sims Wiki(Fandom),“Buy mode”(“purchase items from an object catalog and place them on the current lot”;The Sims 和 The Sims 2 中按功能分类;页面上标注给 The Sims 2 和 The Sims 3 的红绿占地那句话),通过 MediaWiki API 读取,2026 年 10 月 5 日保存为
sims-fandom-buymode.html和.txt,https://sims.fandom.com/wiki/Buy_mode 。单一来源。 ↩↩↩ -
The Sims Wiki(Fandom),“Environment”(Room 和 Environment 需求,它 “analyzes the design and content of the room that the Sim is currently standing in”),通过 MediaWiki API 读取,2026 年 10 月 5 日保存为
sims-fandom-environment.html和.txt,https://sims.fandom.com/wiki/Environment 。单一来源。 ↩ -
作者提取,2026 年 10 月 5 日:房间篇证据文件夹中的
measure_apple_docs.py,输出measure_apple_docs.out:从每个保存的 Apple 文档 JSON 页面中提取标题、声明、摘要和平台。为dropDestination(for:action:isTargeted:)引用的弃用字段读自同一份保存的 JSON。 ↩ -
Apple 开发者文档,
Model()(SwiftData 宏;“Converts a Swift class into a stored model that’s managed by SwiftData”;iOS 17.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftdata-model.html和.txt,https://developer.apple.com/documentation/swiftdata/model() ↩ -
Apple 开发者文档,
Unique(_:)(SwiftData 宏;“Specifies the key-paths that SwiftData uses to enforce the uniqueness of model instances”;iOS 18.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftdata-unique.html和.txt,https://developer.apple.com/documentation/swiftdata/unique(_:) ↩ -
Kiradex 仓库,
Kiradex/Models/CardCopy.swift(文档注释:“every property has a default and nothing is unique, for CloudKit”),2026 年 10 月 5 日读取。 ↩↩↩ -
Kiradex 仓库,
Kiradex/Models/PriceHistory.swift(文档注释:“Shaped for CloudKit likeCollectionEntry: defaults everywhere, nothing unique”;PriceBook把两台设备的历史折叠 “into the one with the smallestuid”),2026 年 10 月 5 日读取。 ↩↩↩↩ -
Apple 开发者文档,
draggable(_:)(SwiftUI;“Activates this view as the source of a drag and drop operation”;一个Transferable载荷;iOS 16.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-draggable.html和.txt,https://developer.apple.com/documentation/swiftui/view/draggable(_:) ↩↩ -
Apple 开发者文档,
dropDestination(for:action:isTargeted:)(SwiftUI;一个([T], CGPoint) -> Bool的 action,第二个参数为 “the drop location in this view’s coordinate space”;iOS 16.0;iOS 的平台条目列出deprecatedAt27.2,并附消息 “UsedropDestination(for:isEnabled:action:)with anactionthat takes aDropSessionparameter instead.”;DropSession的引用说明 “A description of a drop that is in progress”),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-dropdestination.html和.txt,https://developer.apple.com/documentation/swiftui/view/dropdestination(for:action:istargeted:) ↩↩↩↩↩ -
Apple 开发者文档,
DragGesture(SwiftUI;“A dragging motion that invokes an action as the drag-event sequence changes”;iOS 13.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-draggesture.html和.txt,https://developer.apple.com/documentation/swiftui/draggesture ↩↩ -
Apple 开发者文档,
SpatialTapGesture(SwiftUI;“A gesture that recognizes one or more taps and reports their location”;iOS 16.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-spatialtap.html和.txt,https://developer.apple.com/documentation/swiftui/spatialtapgesture ↩↩ -
Apple 开发者文档,
accessibilityElement(children:)(SwiftUI;iOS 13.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-accessibilityelement.html和.txt,https://developer.apple.com/documentation/swiftui/view/accessibilityelement(children:) ↩↩ -
Apple 开发者文档,
accessibilityAction(named:_:)(SwiftUI;“Actions allow assistive technologies, such as the VoiceOver, to interact with the view by invoking the action”;iOS 16.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-accessibilityaction-named.html和.txt,https://developer.apple.com/documentation/swiftui/view/accessibilityaction(named:_:) ↩↩↩↩ -
Apple 开发者文档,
accessibilityAdjustableAction(_:)(SwiftUI;接收AccessibilityAdjustmentDirection的处理函数;iOS 13.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-adjustable.html和.txt,https://developer.apple.com/documentation/swiftui/view/accessibilityadjustableaction(_:) ↩↩ -
Apple 开发者文档,
accessibilityRotor(_:entries:)(SwiftUI;“Create an Accessibility Rotor with the specified user-visible label”;iOS 16.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-accessibilityrotor.html和.txt,https://developer.apple.com/documentation/swiftui/view/accessibilityrotor(_:entries:) ↩↩↩↩ -
Apple 开发者文档,
AccessibilityNotification.Announcement(Accessibility;“A notification that an app posts when it needs to convey an announcement to an assistive app”;iOS 17.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-accessibilitynotification.html和.txt,https://developer.apple.com/documentation/accessibility/accessibilitynotification/announcement ↩↩↩↩ -
Apple 开发者文档,
performAccessibilityAudit(for:_:)(XCUIAutomation,XCUIApplication;iOS 17.0;JSON 列出 Xcode 16.3,没有摘要),JSON 于 2026 年 10 月 5 日保存为apple-xctest-accessibilityaudit.html和.txt,https://developer.apple.com/documentation/xcuiautomation/xcuiapplication/performaccessibilityaudit(for:_:) ↩↩↩ -
Apple,Xcode 27.0(构建 27A266a)中的 XCUIAutomation 框架头文件,iPhone Simulator 平台,2026 年 10 月 5 日读取:
XCUIApplication.h(performAccessibilityAuditWithAuditTypes:issueHandler:error:,“Runs an accessibility audit on the current view”);XCUIVoiceOverService.h(“Provides programmatic control of VoiceOver for UI testing”;启用、停用、向前、向后、移入和移出,以及当前朗读内容;iOS 27.0);XCUIDevice.h(voiceOverService属性,iOS 27.0)。在该框架的所有头文件中搜索自定义动作、转子和播报,均未找到。 ↩↩↩ -
Kiradex 仓库,
scripts/forge/interiors.py(模块文档字符串,包括 “every piece at least two tiles on one axis”;ROOM、ROOM_UP和HALL字母地图、家具列表、位置和传送点;host的注释 “where the owner stands when you visit”;PIECES、FACES、EXPORT_LETTERS、export()和check(),后者的位置检查覆盖到达、楼梯、主人、书架、展示柜、书桌和展座位置,不覆盖墙板,其重叠循环读取data["objects"],即导出的独立家具),2026 年 10 月 5 日读取,从未运行。 ↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Kiradex 仓库,
docs/TestFlight.md,第 46 项(“Build 26: the interior kit. Rooms sixteen wide with a three-row wall band (cornice, face, wainscot)”),2026 年 10 月 5 日读取。该构建在文件中没有日期;它介于第 23 版和第 35 版之间,建筑篇把这两版都标为 2026 年 10 月 3 日。 ↩↩ -
作者计算,2026 年 10 月 5 日:草稿文件夹
figures/中的merge_regression.py,输出merge_regression.out。在锻炉原样制作的room上(13 件可移动家具,读自path_rule.json和用ast解析的 Kiradex 仓库scripts/forge/interiors.py),(9,3) 的一张床单独放、(6,4) 的一个床头柜单独放,都能通过格子规则、上限和行走检查;两者一起则隔断 (7,3) 和 (8,3),即展示柜前的两个格子。一个按uid给两者排序、并让每件都经过完整检查加入的修复,会保留uid较小的那件并退回另一件,两种输入顺序和两种uid分配下结果都相同。713 对造成隔断的组合中,每一对按单件来看都合法,这一点可由measure_path_rule.out推出:远离目标的单件摆放没有一种会隔断目标。 ↩↩↩↩↩ -
Kiradex 仓库,
docs/WORLD.md(第 4 节:金币商店的 “clothes, hats, backpacks, card-display frames and emotes”,level = floor(sqrt(XP / 40)) + 1,按等级解锁、“that cannot be bought” 的装扮,包括 Full Set、Vintage 和 Across the Sea 在内的徽章;收藏者是 “a 30-pixel figure in a 32 × 40 cell”),2026 年 10 月 5 日读取;用 sixteen、cap、decoration、furnishing、Sunday、once a day 和 read-only 搜索过,没有一处说明房间规则。 ↩↩↩↩↩↩ -
Kiradex 仓库,
server/app/rooms.py(模块文档字符串:“Everything here is in memory; a room empties when its last collector leaves and nothing of them is kept.”),2026 年 10 月 5 日读取。 ↩↩↩↩ -
Kiradex 仓库,
docs/research/pixel-art/06-collecting-and-hubs.md,5.3 节(“Rooms: a budget, a Sunday letter, and a like that never goes down”:16 件上限,周日来信作为 “a pixel object that appears on the doormat”,只读拜访,每位访客每天一个赞,“a stamp on their passport per room visited, with ranks at 30 / 100 / 500”)和 5.7 节(赞助者物品包括 “one room skin”,并且 “never a bigger furnishing cap”),2026 年 10 月 5 日读取。 ↩↩↩↩↩ -
Kiradex 仓库,
Kiradex/World/Quests.swift(Quest.current(on:calendar:),以日历的weekOfYear区间为键,该区间从日历自己的一周首日开始)和Kiradex/World/HallView.swift(注释 “The week’s theme is the week’s quest”,以及读作每周任务标题的主题),2026 年 10 月 5 日读取。 ↩↩↩ -
Kiradex 仓库,
docs/PROTOTYPES.md(每周任务原型给测试者的问题 “Weekly reset on your calendar’s week: say if it feels wrong.”),2026 年 10 月 5 日读取。 ↩ -
作者计算,2026 年 10 月 5 日:草稿文件夹
figures/中的layout_size.py,输出layout_size.out:种类数,如今的锻炉 21 种(17 种地面家具和 4 种墙面物品),7.5 节新增 3 种,共 24 种;存储的房屋布局(两层,每层 16 件,每件有种类、x、y 和朝向,每层两个风格 ID)用带具名键的紧凑 JSON 表示,每个种类都设为提议中最长的名称(pressed_flower,14 个字符),每个风格 ID 设为 16 个字符,共 1,974 字节;把每个种类补足到 16 个字符时为 2,038 字节;32 件家具只存裸的整数四元组时为 385 字节。 ↩↩ -
Kiradex 仓库,
Kiradex/World/PlazaStage.swift(@AppStorage("plaza.id"),“Who this device is to the plaza”;@AppStorage("quests.stamps"),以逗号连接的任务印章列表,QuestBoardView和MeView也会读取;@AppStorage("plaza.ribbons"),“Ribbons from the town’s collectors”),2026 年 10 月 5 日读取;在应用的 Swift 源码中搜索已存储的步数或已拜访房间的记录,没有找到,在应用和服务器中搜索计步器、步数类型或护照,也没有找到(唯一的steps是TileMap.swift和PlazaRig.swift中的行走路径)。 ↩↩↩↩↩ -
Kiradex 仓库,
Kiradex/Models/Progression.swift(Progression的文档注释:经验值、等级和徽章是 “worked out from the collection as it is, so every device agrees without anything being stored”),2026 年 10 月 5 日读取。 ↩↩ -
Apple 开发者文档,
AppStorage(SwiftUI;“A property wrapper type that reflects a value from UserDefaults”;iOS 14.0),JSON 于 2026 年 10 月 5 日保存为apple-swiftui-appstorage.html和.txt,https://developer.apple.com/documentation/swiftui/appstorage ↩↩ -
作者计算,2026 年 10 月 5 日:草稿文件夹
figures/中的skin_denominator.py,输出skin_denominator.out,依据简报的色阶各行:一层 16 件家具,其中 15 件和地板风格属于同一色阶系,墙面在外,为 18 中占 16(88.9%),在 70% 那行得 1,600 分;把墙面从占比中剔除,则为 17 中占 16(94.1%),在 90% 那行得 4,800 分,多出 3,200 分。 ↩ -
Kiradex 仓库,
Kiradex/Models/Profile.swift(同步档案上的var lookData: Data = Data();var uid: String = "",在init(handle:)中设为UUID().uuidString,PriceHistory和CardCopy也带有uid;ModelContext.profile()第 224 到 237 行按uid排序获取档案,保留第一份并删除其余,注释称之为折叠到最小的 uid),2026 年 10 月 5 日读取。 ↩↩↩