fixed # 修复作物资源位置不对的历史遗留BUG

add # 新增作物会生虫的逻辑
This commit is contained in:
2026-10-19 17:25:25 +08:00
parent 24ae6102c0
commit 15646525cb
80 changed files with 793505 additions and 226 deletions
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
{
"schema": 2,
"mode": "full_canvas_bottom_center",
"reference_soil_height": 79.5,
"growth_scale": 0.75,
"common_scale": 0.5,
"wither_down": 8,
"basis": "WebGLMainSceneLandController.SetCropTexture and mainscene RectTransform"
}
+369
View File
@@ -46,6 +46,13 @@
92
]
},
"background/frame/frame.png": {
"sha256": "93148c78e15295b623a857ee016768531854696ef28557bb28e044e32a5ce6ac",
"size": [
230,
230
]
},
"background/ripe.png": {
"sha256": "1c94af3e0b923defc85d200bd25538bf1ee9a2e744406266cdaf7103c1ea2cbd",
"size": [
@@ -53,6 +60,279 @@
92
]
},
"background/userInfo.png": {
"sha256": "09e906c85d12491c297489ee6bcc31cd7f9e0c1527808c9c64d1783e8ae3cff3",
"size": [
580,
275
]
},
"pest/pest1.png": {
"sha256": "1ad3f1bd4521ef462056b88070728853fc93de85457eac1903d8dd11dc175642",
"size": [
44,
60
]
},
"pest/pest11.png": {
"sha256": "ea87a9e6ede36b36d492d641abe9c881ce57aecf130ebb5e9928276588923a52",
"size": [
44,
60
]
},
"pest/pest13.png": {
"sha256": "063d58763c1c06bf5cf88e73b946dbe9d35dc52709057911247d29528bd80346",
"size": [
44,
60
]
},
"pest/pest15.png": {
"sha256": "2927464be009a916862457a9dd1cf272e2face4809c8489496b8ca63f1064036",
"size": [
44,
60
]
},
"pest/pest17.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest19.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest21.png": {
"sha256": "863e604de7a63a6bc82d240b72fc0d16964a3536557400fd7e9e4251179efc0b",
"size": [
44,
60
]
},
"pest/pest23.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest25.png": {
"sha256": "17752162847dd22b45b041072dde51d9761f24179682794c14db4fe3a21b7843",
"size": [
44,
60
]
},
"pest/pest27.png": {
"sha256": "5827f8338358373507351449a8e82063c1683c3a0c4caaadf6e679c20306f376",
"size": [
44,
60
]
},
"pest/pest29.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest3.png": {
"sha256": "a88d9a45472167ad34af98e4a3e6db0bc02c3b7f3c441aef44908a97c136e37f",
"size": [
44,
60
]
},
"pest/pest31.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest33.png": {
"sha256": "eca7d05f269a417553ae38da5289caea9a8f4ba5bbc592a9477bc5dfa6a6ec1d",
"size": [
44,
60
]
},
"pest/pest35.png": {
"sha256": "d26b7c64ed82266c23ac90b5a8e7fd5996b15a6a65a9d4893f0f4a01c1facb16",
"size": [
44,
60
]
},
"pest/pest37.png": {
"sha256": "bb92d5687eb73be0a8bf1d9ae8c2aac5e67dcab6e5184d19e212b5931705c6b5",
"size": [
44,
60
]
},
"pest/pest39.png": {
"sha256": "5827f8338358373507351449a8e82063c1683c3a0c4caaadf6e679c20306f376",
"size": [
44,
60
]
},
"pest/pest41.png": {
"sha256": "fa5b7979e0d905454ddd515751e53646614680e6d29f850bdea40957bc6fbeb1",
"size": [
44,
60
]
},
"pest/pest43.png": {
"sha256": "d7f1eea15102154c623d82cd148126f34e82ef40602d7cae78a6e3ee94970b93",
"size": [
44,
60
]
},
"pest/pest45.png": {
"sha256": "c84fbc9a11d7e4a0e7ddff301d4dd79f75cc1882c76776fa617d189eec8f5eb4",
"size": [
44,
60
]
},
"pest/pest47.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest49.png": {
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82",
"size": [
44,
60
]
},
"pest/pest5.png": {
"sha256": "5ce899cd3c42a7919dea0b43686f261a94e80af9d122337b3c01c01df920a9b0",
"size": [
44,
60
]
},
"pest/pest51.png": {
"sha256": "3eeb2ef89514543ff616a51db2ad4a54da6fa67d3e573f3cda2b63d376d91d04",
"size": [
44,
60
]
},
"pest/pest53.png": {
"sha256": "d823df2c2b1d9b721a2727e8e60f496aa7791942b20ff894569819b47dfe7ce4",
"size": [
44,
60
]
},
"pest/pest55.png": {
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7",
"size": [
44,
60
]
},
"pest/pest57.png": {
"sha256": "526eca9a538b3a6acd8f9ef9a358a8df98d2c6cf29667e647b6fb3df5110f5d6",
"size": [
44,
60
]
},
"pest/pest59.png": {
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7",
"size": [
44,
60
]
},
"pest/pest61.png": {
"sha256": "6603a10d50bd88bb1032fd22fe482b0c9ba433ba25967408d64e07b47a6c9b64",
"size": [
44,
60
]
},
"pest/pest63.png": {
"sha256": "226c96b60648787fd457d2f968c0eee7c610444f191e6c73fdd071fa6e95bf14",
"size": [
44,
60
]
},
"pest/pest65.png": {
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7",
"size": [
44,
60
]
},
"pest/pest67.png": {
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7",
"size": [
44,
60
]
},
"pest/pest69.png": {
"sha256": "cc1fb7b27adcdab8bbb3872ab28dab66aee832b02ed36e588f214e03d19cbc8b",
"size": [
44,
60
]
},
"pest/pest7.png": {
"sha256": "e600a52be8e2aeb9ba01ad3b595fe9badb05a1987c2ec98b8d8bd6fe6fd73a65",
"size": [
44,
60
]
},
"pest/pest71.png": {
"sha256": "5bd1ed7e9db82572119403fce50ff9c7276df33ad4288869aeac3fa5896652e6",
"size": [
44,
60
]
},
"pest/pest73.png": {
"sha256": "d0d22707ce214b6c78b74315daa33f93c97e5c20368a002a094cd12964437726",
"size": [
44,
60
]
},
"pest/pest75.png": {
"sha256": "51e1142e2f4dd489cf392b0b25d2d8c04c3801b6d149254e7b7df7c86155b5e2",
"size": [
44,
60
]
},
"pest/pest9.png": {
"sha256": "cefc8172fd7f0a6137dd5c67c43ba6b165ee37faf58893ff8f37dbff2cc08d22",
"size": [
44,
60
]
},
"plant/ATTomato/1.png": {
"sha256": "48de2a54c2868852f70e981a0ef8228ad0bb09499e6c9e84057aaf82f16f7270",
"size": [
@@ -4714,8 +4994,97 @@
180,
120
]
},
"webgl/seed.png": {
"sha256": "353ff338fd5ab3f7812e9d2d7f98df11d0de4e04af6de7f9d5aa0249d40b3609",
"size": [
69,
30
]
},
"webgl/wither.png": {
"sha256": "1c87f05e0b7bd8287926351e07746bc9e36371a860afaef0e4b00c51536dc472",
"size": [
209,
92
]
}
},
"pest": {
"display_size": [
22,
30
],
"frames": [
"pest/pest1.png",
"pest/pest3.png",
"pest/pest5.png",
"pest/pest7.png",
"pest/pest9.png",
"pest/pest11.png",
"pest/pest13.png",
"pest/pest15.png",
"pest/pest17.png",
"pest/pest19.png",
"pest/pest21.png",
"pest/pest23.png",
"pest/pest25.png",
"pest/pest27.png",
"pest/pest29.png",
"pest/pest31.png",
"pest/pest33.png",
"pest/pest35.png",
"pest/pest37.png",
"pest/pest39.png",
"pest/pest41.png",
"pest/pest43.png",
"pest/pest45.png",
"pest/pest47.png",
"pest/pest49.png",
"pest/pest51.png",
"pest/pest53.png",
"pest/pest55.png",
"pest/pest57.png",
"pest/pest59.png",
"pest/pest61.png",
"pest/pest63.png",
"pest/pest65.png",
"pest/pest67.png",
"pest/pest69.png",
"pest/pest71.png",
"pest/pest73.png",
"pest/pest75.png"
],
"loop": true,
"pivot": [
0.5,
0.5
],
"positions": [
[
-26,
13
],
[
-5,
42
],
[
24,
24
]
],
"sample_rate": 24
},
"placement": {
"basis": "WebGLMainSceneLandController.SetCropTexture and mainscene RectTransform",
"common_scale": 0.5,
"growth_scale": 0.75,
"mode": "full_canvas_bottom_center",
"reference_soil_height": 79.5,
"schema": 2,
"wither_down": 8
},
"plants": {
"ATTomato": {
"general": 1,
+29 -11
View File
@@ -3,10 +3,24 @@
import hashlib
from farm_core.domain import Identity, Snapshot, integer
from farm_core.pests import advance_plot
from .models import PlotView, Scene
def pest_frames(scope, plot, frames):
available = list(frames)
chosen = []
for index in range(plot.pest_count):
seed = (
f"{scope}:{plot.slot}:{plot.plant.plant_id}:"
f"{plot.harvest_count}:{index}"
)
number = int.from_bytes(hashlib.sha256(seed.encode()).digest(), "big")
chosen.append(available.pop(number % len(available)))
return tuple(chosen)
def scene_from_snapshot(identity, snapshot, now, metadata):
if not isinstance(identity, Identity) or not isinstance(snapshot, Snapshot):
raise ValueError("Identity and snapshot are required")
@@ -17,42 +31,46 @@ def scene_from_snapshot(identity, snapshot, now, metadata):
):
raise ValueError("Invalid plot slots")
views = []
scope = hashlib.sha256(identity.key.encode()).hexdigest()
for plot in sorted(snapshot.plots, key=lambda p: p.slot):
plot = advance_plot(plot, now)
if plot.plant is None:
views.append(PlotView(plot.slot, "empty"))
continue
if plot.withered:
views.append(PlotView(
plot.slot, "withered", "plant/basic/9.png", size=(150, 212),
plot.slot, "withered", "webgl/wither.png",
))
continue
integer(plot.mature_at)
frames = pest_frames(scope, plot, metadata["pest"]["frames"])
name = plot.plant.name
info = metadata["plants"].get(name)
if info is None:
views.append(PlotView(plot.slot, "growing", "missing"))
views.append(PlotView(
plot.slot, "growing", "missing", pest_count=plot.pest_count,
pest_frames=frames,
))
continue
phases = tuple(dict.fromkeys(int(p) for p in info["phase"].split(",")))
duration = (plot.plant.growth_seconds if plot.harvest_count == 0
else plot.plant.regrowth_seconds)
elapsed = max(0, now - (plot.mature_at - duration))
work = (elapsed * 100 if plot.growth_remaining is None else
duration * 100 - plot.growth_remaining)
ripe = now >= plot.mature_at
stage = len(phases) if ripe else sum(
elapsed * phases[-1] >= threshold * duration
work * phases[-1] >= threshold * duration * 100
for threshold in phases
)
sprite = f"plant/{name}/{stage}.png"
size = None
if stage == 0:
if info["general"]:
sprite = "plant/basic/0.png"
size = (max(1, 35 + info["offsetW"]),
max(1, 58 + info["offsetH"]))
sprite = "webgl/seed.png"
views.append(PlotView(
plot.slot, "ripe" if ripe else "growing", sprite, stage,
(info["offsetX"], info["offsetY"]), size,
pest_count=plot.pest_count,
pest_frames=frames,
))
return Scene(
hashlib.sha256(identity.key.encode()).hexdigest(),
snapshot.account, tuple(views),
scope, snapshot.account, tuple(views),
)
+2 -2
View File
@@ -52,8 +52,8 @@ class PlotView:
state: str # empty/growing/ripe/withered。
sprite: str = "" # 包内受控素材名,不来自用户路径。
stage: int = 0 # 本季展示阶段。
offset: tuple[int, int] = (0, 0) # 旧素材中心偏移。
size: tuple[int, int] | None = None # 萌芽/枯萎原始尺寸覆盖。
pest_count: int = 0 # 当前可见虫数;参与成图缓存键。
pest_frames: tuple[str, ...] = () # 按虫序稳定选择的包内帧,参与缓存键。
@dataclass(frozen=True)
+79 -33
View File
@@ -64,6 +64,7 @@ class FarmRenderer:
started = perf_counter()
canvas = self._base(scene, options)
self._plants(canvas, scene, options)
self._pests(canvas, scene, options)
self._panel(canvas, scene, options, avatar)
composed = perf_counter()
content = canvas.encode(codec, options.quality, options.method)
@@ -123,6 +124,8 @@ class FarmRenderer:
def _plants(self, canvas, scene, options):
s = lambda n: self._scale(n, options)
soil_w, soil_h = self.assets.metadata["soil_size"]
rules = self.assets.metadata["placement"]
unit = soil_h / rules["reference_soil_height"]
first_ripe = True
for plot in scene.plots:
if not plot.sprite:
@@ -137,14 +140,22 @@ class FarmRenderer:
font=font(self.font_path, max(10, s(48))),
)
else:
size = plot.size or metadata["size"]
factor = rules["common_scale"] if (
plot.stage == 0 or plot.state == "withered"
) else rules["growth_scale"]
size = [n * factor * unit for n in metadata["size"]]
plant = self.assets.image(
plot.sprite, tuple(max(1, s(n)) for n in size),
)
offset_x, offset_y = plot.offset
center_x = pos["x"] + soil_w / 2
bottom = pos["y"] + soil_h
if plot.stage == 0 or plot.state == "withered":
bottom = pos["y"] + soil_h / 2
if plot.state == "withered":
bottom += rules["wither_down"] * unit
xy = (
s(pos["x"] + soil_w / 2 + offset_x) - plant.width // 2,
s(pos["y"] + soil_h / 2 + offset_y) - plant.height // 2,
round(center_x * options.width / 3072 - plant.width / 2),
s(bottom) - plant.height,
)
canvas.paste(plant, xy)
if plot.state == "ripe" and first_ripe:
@@ -154,19 +165,36 @@ class FarmRenderer:
canvas.paste(icon, (s(pos["x"] + 70), s(pos["y"] - 80)))
first_ripe = False
def _pests(self, canvas, scene, options):
s = lambda n: self._scale(n, options)
soil_w, soil_h = self.assets.metadata["soil_size"]
unit = soil_h / self.assets.metadata["placement"][
"reference_soil_height"
]
config = self.assets.metadata["pest"]
size = tuple(max(1, s(n * unit)) for n in config["display_size"])
for plot in scene.plots:
if plot.state not in ("growing", "ripe"):
continue
pos = self.assets.metadata["positions"][plot.slot - 1]
for frame, (x, y) in zip(plot.pest_frames, config["positions"]):
image = self.assets.image(frame, size)
center_x = pos["x"] + soil_w / 2 + x * unit
center_y = pos["y"] + soil_h / 2 - y * unit
canvas.paste(image, (
round(center_x * options.width / 3072 - image.width / 2),
round(center_y * options.width / 3072 - image.height / 2),
))
def _avatar(self, content, size):
if content and len(content) <= MAX_AVATAR_BYTES:
try:
with Image.open(BytesIO(content)) as image:
if image.width * image.height > MAX_AVATAR_PIXELS:
raise ValueError("Avatar dimensions exceed limit")
image.thumbnail(size, Image.Resampling.LANCZOS)
avatar = Image.new("RGBA", size, "#cce3c1")
avatar.alpha_composite(image.convert("RGBA"), (
(size[0] - image.width) // 2,
(size[1] - image.height) // 2,
))
return avatar
return image.convert("RGBA").resize(
size, Image.Resampling.LANCZOS,
)
except (UnidentifiedImageError, OSError, ValueError,
Image.DecompressionBombError):
logger.warning("farm render avatar rejected; using default")
@@ -179,26 +207,44 @@ class FarmRenderer:
def _panel(self, canvas, scene, options, avatar):
s = lambda n: self._scale(n, options)
canvas.rectangle(
tuple(s(n) for n in (90, 75, 1160, 425)),
"#fff9e8", s(24),
)
canvas.paste(self._avatar(avatar, (s(170), s(170))), (s(125), s(125)))
size = max(12, s(55))
name = canvas.fit_text(
scene.account.name, self.font_path, size, s(790),
)
canvas.text((s(330), s(105)), name, self.font_path, size, "#493525")
account = scene.account
lines = (
f"等级 {account.level} 经验 {account.experience}",
f"农场币 {account.point} 点券 {account.vip_point}",
f"已解锁 {len(scene.plots)}/30 块地",
)
for index, line in enumerate(lines):
size = max(10, s(43))
text = canvas.fit_text(line, self.font_path, size, s(790))
canvas.text(
(s(330), s(195 + index * 65)), text,
self.font_path, size, "#674d34",
def paste_asset(name, position):
original = self.assets.metadata["images"][name]["size"]
image = self.assets.image(
name, tuple(max(1, s(n)) for n in original),
)
canvas.paste(image, tuple(s(n) for n in position))
paste_asset("background/userInfo.png", (100, 30))
canvas.paste(
self._avatar(avatar, (max(1, s(140)), max(1, s(150)))),
(s(125), s(85)),
)
paste_asset("background/frame/frame.png", (75, 44))
account = scene.account
level = account.level
current = account.experience - 200 * level * (level + 1) // 2
next_total = 200 * (level + 1) * (level + 2) // 2
progress = min(247, max(0, current * 247 // next_total))
if progress:
canvas.rectangle(
tuple(s(n) for n in (292, 164, 292 + progress, 189)),
(171, 194, 41),
)
lines = (
(account.name, (300, 92), 24, (77, 35, 4), 330),
(f"{current} / {next_total}", (365, 164), 20,
(102, 120, 19), 165),
(str(level), (570, 158), 32, (214, 111, 1), 60),
(str(account.point), (330, 211), 24, (253, 253, 253), 100),
(str(account.vip_point), (570, 211), 24, (253, 253, 253), 40),
)
for value, position, original_size, color, width in lines:
size = max(1, s(original_size))
text = canvas.fit_text(
value, self.font_path, size, max(1, s(width)),
)
canvas.text(
tuple(s(n) for n in position), text,
self.font_path, size, color,
)
+354
View File
@@ -0,0 +1,354 @@
{
"schema": 1,
"source_url": "https://game.qqnc.qq.com/happyfarm/unity/Assets/AssetBundles/WebGL/mainscene_053483f584f07436ac0569e2f4f7ed01",
"source_sha256": "3766696845401ddcc877b304db34c9c3ccf5f8e92cad1a47ddbf7defcedf6362",
"extractor": "UnityPy 1.25.4 Sprite.image; full sprite rect checked",
"images": {
"pest/pest23.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest53.png": {
"size": [
44,
60
],
"sha256": "d823df2c2b1d9b721a2727e8e60f496aa7791942b20ff894569819b47dfe7ce4"
},
"pest/pest63.png": {
"size": [
44,
60
],
"sha256": "226c96b60648787fd457d2f968c0eee7c610444f191e6c73fdd071fa6e95bf14"
},
"pest/pest37.png": {
"size": [
44,
60
],
"sha256": "bb92d5687eb73be0a8bf1d9ae8c2aac5e67dcab6e5184d19e212b5931705c6b5"
},
"pest/pest75.png": {
"size": [
44,
60
],
"sha256": "51e1142e2f4dd489cf392b0b25d2d8c04c3801b6d149254e7b7df7c86155b5e2"
},
"pest/pest15.png": {
"size": [
44,
60
],
"sha256": "2927464be009a916862457a9dd1cf272e2face4809c8489496b8ca63f1064036"
},
"pest/pest13.png": {
"size": [
44,
60
],
"sha256": "063d58763c1c06bf5cf88e73b946dbe9d35dc52709057911247d29528bd80346"
},
"pest/pest41.png": {
"size": [
44,
60
],
"sha256": "fa5b7979e0d905454ddd515751e53646614680e6d29f850bdea40957bc6fbeb1"
},
"pest/pest55.png": {
"size": [
44,
60
],
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7"
},
"pest/pest7.png": {
"size": [
44,
60
],
"sha256": "e600a52be8e2aeb9ba01ad3b595fe9badb05a1987c2ec98b8d8bd6fe6fd73a65"
},
"pest/pest35.png": {
"size": [
44,
60
],
"sha256": "d26b7c64ed82266c23ac90b5a8e7fd5996b15a6a65a9d4893f0f4a01c1facb16"
},
"pest/pest51.png": {
"size": [
44,
60
],
"sha256": "3eeb2ef89514543ff616a51db2ad4a54da6fa67d3e573f3cda2b63d376d91d04"
},
"pest/pest39.png": {
"size": [
44,
60
],
"sha256": "5827f8338358373507351449a8e82063c1683c3a0c4caaadf6e679c20306f376"
},
"pest/pest25.png": {
"size": [
44,
60
],
"sha256": "17752162847dd22b45b041072dde51d9761f24179682794c14db4fe3a21b7843"
},
"pest/pest57.png": {
"size": [
44,
60
],
"sha256": "526eca9a538b3a6acd8f9ef9a358a8df98d2c6cf29667e647b6fb3df5110f5d6"
},
"pest/pest1.png": {
"size": [
44,
60
],
"sha256": "1ad3f1bd4521ef462056b88070728853fc93de85457eac1903d8dd11dc175642"
},
"pest/pest69.png": {
"size": [
44,
60
],
"sha256": "cc1fb7b27adcdab8bbb3872ab28dab66aee832b02ed36e588f214e03d19cbc8b"
},
"pest/pest5.png": {
"size": [
44,
60
],
"sha256": "5ce899cd3c42a7919dea0b43686f261a94e80af9d122337b3c01c01df920a9b0"
},
"webgl/wither.png": {
"size": [
209,
92
],
"sha256": "1c87f05e0b7bd8287926351e07746bc9e36371a860afaef0e4b00c51536dc472"
},
"pest/pest9.png": {
"size": [
44,
60
],
"sha256": "cefc8172fd7f0a6137dd5c67c43ba6b165ee37faf58893ff8f37dbff2cc08d22"
},
"pest/pest33.png": {
"size": [
44,
60
],
"sha256": "eca7d05f269a417553ae38da5289caea9a8f4ba5bbc592a9477bc5dfa6a6ec1d"
},
"pest/pest3.png": {
"size": [
44,
60
],
"sha256": "a88d9a45472167ad34af98e4a3e6db0bc02c3b7f3c441aef44908a97c136e37f"
},
"pest/pest47.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest67.png": {
"size": [
44,
60
],
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7"
},
"pest/pest65.png": {
"size": [
44,
60
],
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7"
},
"pest/pest29.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest31.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest59.png": {
"size": [
44,
60
],
"sha256": "8a78d0b046991de083334cabba06291fecc7739be501beb2caf3fcc87f5454f7"
},
"pest/pest43.png": {
"size": [
44,
60
],
"sha256": "d7f1eea15102154c623d82cd148126f34e82ef40602d7cae78a6e3ee94970b93"
},
"pest/pest21.png": {
"size": [
44,
60
],
"sha256": "863e604de7a63a6bc82d240b72fc0d16964a3536557400fd7e9e4251179efc0b"
},
"pest/pest11.png": {
"size": [
44,
60
],
"sha256": "ea87a9e6ede36b36d492d641abe9c881ce57aecf130ebb5e9928276588923a52"
},
"pest/pest17.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest45.png": {
"size": [
44,
60
],
"sha256": "c84fbc9a11d7e4a0e7ddff301d4dd79f75cc1882c76776fa617d189eec8f5eb4"
},
"pest/pest73.png": {
"size": [
44,
60
],
"sha256": "d0d22707ce214b6c78b74315daa33f93c97e5c20368a002a094cd12964437726"
},
"webgl/seed.png": {
"size": [
69,
30
],
"sha256": "353ff338fd5ab3f7812e9d2d7f98df11d0de4e04af6de7f9d5aa0249d40b3609"
},
"pest/pest27.png": {
"size": [
44,
60
],
"sha256": "5827f8338358373507351449a8e82063c1683c3a0c4caaadf6e679c20306f376"
},
"pest/pest19.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest61.png": {
"size": [
44,
60
],
"sha256": "6603a10d50bd88bb1032fd22fe482b0c9ba433ba25967408d64e07b47a6c9b64"
},
"pest/pest49.png": {
"size": [
44,
60
],
"sha256": "fc3a21ed236cd2200704d2971c54dcf6f90fe92ada756da6acb70b78d2c9fd82"
},
"pest/pest71.png": {
"size": [
44,
60
],
"sha256": "5bd1ed7e9db82572119403fce50ff9c7276df33ad4288869aeac3fa5896652e6"
}
},
"pest": {
"frames": [
"pest/pest1.png",
"pest/pest3.png",
"pest/pest5.png",
"pest/pest7.png",
"pest/pest9.png",
"pest/pest11.png",
"pest/pest13.png",
"pest/pest15.png",
"pest/pest17.png",
"pest/pest19.png",
"pest/pest21.png",
"pest/pest23.png",
"pest/pest25.png",
"pest/pest27.png",
"pest/pest29.png",
"pest/pest31.png",
"pest/pest33.png",
"pest/pest35.png",
"pest/pest37.png",
"pest/pest39.png",
"pest/pest41.png",
"pest/pest43.png",
"pest/pest45.png",
"pest/pest47.png",
"pest/pest49.png",
"pest/pest51.png",
"pest/pest53.png",
"pest/pest55.png",
"pest/pest57.png",
"pest/pest59.png",
"pest/pest61.png",
"pest/pest63.png",
"pest/pest65.png",
"pest/pest67.png",
"pest/pest69.png",
"pest/pest71.png",
"pest/pest73.png",
"pest/pest75.png"
],
"sample_rate": 24,
"loop": true,
"display_size": [
22,
30
],
"pivot": [
0.5,
0.5
],
"positions": [
[
-26,
13
],
[
-5,
42
],
[
24,
24
]
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB