fixed # 修复作物资源位置不对的历史遗留BUG
add # 新增作物会生虫的逻辑
@@ -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"
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 10 KiB |