LeafCat79 commited on
Commit
3d8179d
·
verified ·
1 Parent(s): 6b851d5

Fix prompt refresh and broader asset applying

Browse files
Files changed (1) hide show
  1. app.py +138 -7
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import base64
2
  import hashlib
3
  import io
 
4
  import random
5
  import re
6
  import tempfile
@@ -145,6 +146,21 @@ def write_gallery_image(content: bytes, role: str) -> str:
145
 
146
 
147
  def palette_for(text: str) -> list[tuple[int, int, int]]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  digest = hashlib.sha256(text.encode("utf-8")).digest()
149
  base = digest[0] % 360
150
 
@@ -282,6 +298,59 @@ def draw_background(spec: AssetSpec, rng: random.Random) -> bytes:
282
  image.save(out, format="PNG")
283
  return out.getvalue()
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  for y in range(spec.height):
286
  mix = y / max(1, spec.height - 1)
287
  color = tuple(int(colors[2][i] * (1 - mix) + colors[0][i] * mix) for i in range(3))
@@ -480,16 +549,40 @@ def replacement_names(spec: AssetSpec) -> set[str]:
480
  names.update({"player.png", "sprite_player.jpg", "hero.png"})
481
  if slug == "enemy":
482
  names.update({"enemy.png", "monster.png", "sprite_enemy.jpg"})
 
 
483
  return names
484
 
485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  def embed_assets(html_code: str, assets: dict[str, str], specs: list[AssetSpec]) -> str:
487
  output = html_code
488
  manifest_lines = ["<!-- Embedded game assets generated by Image Generator for HTML Games"]
489
  background_uri = None
 
 
490
 
491
  for spec in specs:
492
  data_uri = assets[spec.role]
 
 
 
493
  manifest_lines.append(f"{spec.role}: {spec.filename}")
494
  if is_background_spec(spec) and background_uri is None:
495
  background_uri = data_uri
@@ -500,15 +593,52 @@ def embed_assets(html_code: str, assets: dict[str, str], specs: list[AssetSpec])
500
 
501
  manifest_lines.append("-->")
502
  manifest = "\n".join(manifest_lines) + "\n"
503
- asset_map = ",\n".join(
504
- f" {slugify(spec.role)!r}: {assets[spec.role]!r}" for spec in specs
505
- )
506
  helper_script = f"""<script>
507
- window.GENERATED_GAME_ASSETS = {{
508
- {asset_map}
509
- }};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  window.addEventListener("DOMContentLoaded", function () {{
511
- var background = {background_uri!r};
 
 
 
 
512
  if (!background) return;
513
  document.querySelectorAll("canvas").forEach(function (canvas) {{
514
  canvas.style.backgroundImage = "url(" + background + ")";
@@ -516,6 +646,7 @@ def embed_assets(html_code: str, assets: dict[str, str], specs: list[AssetSpec])
516
  canvas.style.backgroundPosition = "center";
517
  }});
518
  }});
 
519
  </script>"""
520
 
521
  if "</head>" in output:
 
1
  import base64
2
  import hashlib
3
  import io
4
+ import json
5
  import random
6
  import re
7
  import tempfile
 
146
 
147
 
148
  def palette_for(text: str) -> list[tuple[int, int, int]]:
149
+ lowered = text.lower()
150
+ themed = [
151
+ (("lava", "fire", "volcano", "hell"), [(139, 28, 18), (239, 84, 34), (42, 18, 16), (255, 198, 71)]),
152
+ (("ice", "snow", "frost", "arctic"), [(75, 151, 191), (187, 235, 255), (24, 63, 96), (240, 252, 255)]),
153
+ (("desert", "sand", "dune", "western"), [(194, 139, 72), (236, 198, 112), (102, 66, 36), (255, 226, 151)]),
154
+ (("ocean", "sea", "water", "underwater"), [(25, 103, 155), (57, 191, 205), (8, 43, 85), (177, 239, 224)]),
155
+ (("forest", "garden", "grass", "jungle"), [(51, 132, 72), (126, 196, 81), (24, 72, 43), (242, 201, 84)]),
156
+ (("dungeon", "castle", "stone", "crypt"), [(78, 82, 96), (133, 140, 154), (34, 36, 48), (181, 154, 94)]),
157
+ (("city", "street", "cyber", "neon"), [(40, 52, 87), (39, 230, 226), (19, 21, 38), (239, 67, 154)]),
158
+ (("sci-fi", "scifi", "space", "arena", "metal", "shooter"), [(30, 216, 236), (235, 64, 96), (18, 22, 31), (245, 192, 51)]),
159
+ ]
160
+ for words, colors in themed:
161
+ if any(word in lowered for word in words):
162
+ return colors
163
+
164
  digest = hashlib.sha256(text.encode("utf-8")).digest()
165
  base = digest[0] % 360
166
 
 
298
  image.save(out, format="PNG")
299
  return out.getvalue()
300
 
301
+ if any(word in prompt for word in ("lava", "fire", "volcano")):
302
+ image = Image.new("RGBA", (spec.width, spec.height), (52, 23, 18, 255))
303
+ draw = ImageDraw.Draw(image, "RGBA")
304
+ for y in range(0, spec.height, 58):
305
+ draw.rectangle((0, y, spec.width, y + 30), fill=(70, 32, 25, 255))
306
+ for _ in range(18):
307
+ x = rng.randint(-80, spec.width)
308
+ y = rng.randint(0, spec.height)
309
+ draw.line((x, y, x + rng.randint(120, 320), y + rng.randint(-20, 20)), fill=(255, 111, 43, 220), width=rng.randint(5, 12))
310
+ for _ in range(45):
311
+ x = rng.randint(0, spec.width)
312
+ y = rng.randint(0, spec.height)
313
+ r = rng.randint(5, 20)
314
+ draw.ellipse((x - r, y - r, x + r, y + r), fill=(255, 192, 53, rng.randint(70, 140)))
315
+ out = io.BytesIO()
316
+ image.save(out, format="PNG")
317
+ return out.getvalue()
318
+
319
+ if any(word in prompt for word in ("dungeon", "castle", "stone", "crypt")):
320
+ image = Image.new("RGBA", (spec.width, spec.height), (48, 50, 62, 255))
321
+ draw = ImageDraw.Draw(image, "RGBA")
322
+ tile = 54
323
+ for y in range(0, spec.height, tile):
324
+ for x in range(0, spec.width, tile):
325
+ shade = rng.randint(48, 78)
326
+ draw.rectangle((x, y, x + tile - 2, y + tile - 2), fill=(shade, shade + 2, shade + 8, 255), outline=(27, 29, 38, 190))
327
+ for _ in range(22):
328
+ x = rng.randint(0, spec.width)
329
+ y = rng.randint(0, spec.height)
330
+ draw.ellipse((x - 8, y - 8, x + 8, y + 8), fill=(185, 146, 80, 110))
331
+ out = io.BytesIO()
332
+ image.save(out, format="PNG")
333
+ return out.getvalue()
334
+
335
+ if any(word in prompt for word in ("ocean", "sea", "water", "underwater")):
336
+ image = Image.new("RGBA", (spec.width, spec.height), (8, 54, 92, 255))
337
+ draw = ImageDraw.Draw(image, "RGBA")
338
+ for y in range(spec.height):
339
+ mix = y / max(1, spec.height - 1)
340
+ draw.line((0, y, spec.width, y), fill=(int(16 + 6 * mix), int(99 + 62 * mix), int(151 + 58 * mix), 255))
341
+ for _ in range(26):
342
+ x = rng.randint(-100, spec.width)
343
+ y = rng.randint(0, spec.height)
344
+ draw.arc((x, y, x + 180, y + 70), 0, 180, fill=(165, 232, 225, 90), width=3)
345
+ for _ in range(38):
346
+ x = rng.randint(0, spec.width)
347
+ y = rng.randint(0, spec.height)
348
+ r = rng.randint(2, 8)
349
+ draw.ellipse((x - r, y - r, x + r, y + r), outline=(205, 247, 245, 120), width=2)
350
+ out = io.BytesIO()
351
+ image.save(out, format="PNG")
352
+ return out.getvalue()
353
+
354
  for y in range(spec.height):
355
  mix = y / max(1, spec.height - 1)
356
  color = tuple(int(colors[2][i] * (1 - mix) + colors[0][i] * mix) for i in range(3))
 
549
  names.update({"player.png", "sprite_player.jpg", "hero.png"})
550
  if slug == "enemy":
551
  names.update({"enemy.png", "monster.png", "sprite_enemy.jpg"})
552
+ if slug == "bullet":
553
+ names.update({"bullet.png", "projectile.png", "laser.png", "shot.png", "sprite_bullet.jpg"})
554
  return names
555
 
556
 
557
+ def asset_aliases(spec: AssetSpec) -> list[str]:
558
+ slug = slugify(spec.role)
559
+ aliases = {slug, spec.role.strip().lower(), spec.filename.lower()}
560
+ prompt = spec.prompt.lower()
561
+ role_groups = {
562
+ "player": ("player", "hero", "character", "avatar", "ship", "shooter", "tank", "knight", "wizard"),
563
+ "enemy": ("enemy", "monster", "alien", "drone", "foe", "zombie", "boss", "hazard"),
564
+ "bullet": ("bullet", "projectile", "laser", "shot", "missile", "beam", "ammo"),
565
+ "coin": ("coin", "gem", "seed", "star", "pickup", "collectible", "key", "orb"),
566
+ "background": ("background", "backdrop", "bg", "map", "level", "arena", "scene", "world", "floor"),
567
+ }
568
+ for group, words in role_groups.items():
569
+ if group in slug or any(word in prompt for word in words):
570
+ aliases.update(words)
571
+ return sorted(alias for alias in aliases if alias)
572
+
573
+
574
  def embed_assets(html_code: str, assets: dict[str, str], specs: list[AssetSpec]) -> str:
575
  output = html_code
576
  manifest_lines = ["<!-- Embedded game assets generated by Image Generator for HTML Games"]
577
  background_uri = None
578
+ asset_map: dict[str, str] = {}
579
+ alias_map: dict[str, list[str]] = {}
580
 
581
  for spec in specs:
582
  data_uri = assets[spec.role]
583
+ slug = slugify(spec.role)
584
+ asset_map[slug] = data_uri
585
+ alias_map[slug] = asset_aliases(spec)
586
  manifest_lines.append(f"{spec.role}: {spec.filename}")
587
  if is_background_spec(spec) and background_uri is None:
588
  background_uri = data_uri
 
593
 
594
  manifest_lines.append("-->")
595
  manifest = "\n".join(manifest_lines) + "\n"
596
+ asset_json = json.dumps(asset_map)
597
+ alias_json = json.dumps(alias_map)
598
+ background_json = json.dumps(background_uri)
599
  helper_script = f"""<script>
600
+ (function () {{
601
+ var ASSETS = {asset_json};
602
+ var ALIASES = {alias_json};
603
+ window.GENERATED_GAME_ASSETS = ASSETS;
604
+
605
+ function basename(value) {{
606
+ return String(value || "").split("?")[0].split("#")[0].split("/").pop().toLowerCase();
607
+ }}
608
+
609
+ function pickAsset(value) {{
610
+ var text = String(value || "").toLowerCase();
611
+ if (!text || text.indexOf("data:image/") === 0) return value;
612
+ var file = basename(text);
613
+ for (var role in ASSETS) {{
614
+ var aliases = ALIASES[role] || [role];
615
+ for (var i = 0; i < aliases.length; i++) {{
616
+ var alias = String(aliases[i]).toLowerCase();
617
+ if (!alias) continue;
618
+ if (file === alias || file === alias + ".png" || file === "sprite_" + alias + ".png") return ASSETS[role];
619
+ if (file.indexOf(alias) !== -1 || text.indexOf("/" + alias) !== -1 || text.indexOf("_" + alias) !== -1) return ASSETS[role];
620
+ }}
621
+ }}
622
+ return value;
623
+ }}
624
+
625
+ var descriptor = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, "src");
626
+ if (descriptor && descriptor.set && !HTMLImageElement.prototype.__generatedAssetMapper) {{
627
+ Object.defineProperty(HTMLImageElement.prototype, "src", {{
628
+ get: function () {{ return descriptor.get.call(this); }},
629
+ set: function (value) {{ descriptor.set.call(this, pickAsset(value)); }},
630
+ configurable: true,
631
+ enumerable: descriptor.enumerable
632
+ }});
633
+ HTMLImageElement.prototype.__generatedAssetMapper = true;
634
+ }}
635
+
636
  window.addEventListener("DOMContentLoaded", function () {{
637
+ document.querySelectorAll("img").forEach(function (img) {{
638
+ var mapped = pickAsset(img.getAttribute("src") || img.src);
639
+ if (mapped !== (img.getAttribute("src") || img.src)) img.src = mapped;
640
+ }});
641
+ var background = {background_json};
642
  if (!background) return;
643
  document.querySelectorAll("canvas").forEach(function (canvas) {{
644
  canvas.style.backgroundImage = "url(" + background + ")";
 
646
  canvas.style.backgroundPosition = "center";
647
  }});
648
  }});
649
+ }})();
650
  </script>"""
651
 
652
  if "</head>" in output: