saliacoel commited on
Commit
a8b71cc
·
verified ·
1 Parent(s): a3b5e0c

Upload 2 files

Browse files
Files changed (2) hide show
  1. wildcard_2.py +708 -0
  2. wildcard_4.py +535 -0
wildcard_2.py ADDED
@@ -0,0 +1,708 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ # ------------------------------------------------------------
4
+ # WildcardCharacterPrompt
5
+ #
6
+ # Generates two character prompts from the same "base"
7
+ # description:
8
+ # - 1girl variant
9
+ # - 1boy variant
10
+ #
11
+ # Structure of each prompt (comma-separated tokens):
12
+ #
13
+ # Category 1 : "1girl" or "1boy" (count + base gender, NEVER anything else)
14
+ # Category 2 : camera POV (8-ish logical directions, LEFTSIDE / RIGHTSIDE capitalized)
15
+ # Category 3 : "woman" / "man" / "girl" / "boy" / "grandma" / "grandpa" (age descriptor)
16
+ # Category 4 : two tokens: social type + inner personality
17
+ # both applied to Category 3 word:
18
+ # "<social> <age-word>, <inner> <age-word>"
19
+ # Skin tone : "<descriptor> skincolor"
20
+ # Category 5 : eyes:
21
+ # "<color> eyes, <style> eyes, <look direction> eyes"
22
+ # (no eyewear)
23
+ # Category 6 : hair:
24
+ # "<color> hair, <length> hair, <style> hair"
25
+ # Category 7 : top garment:
26
+ # "<color> <top>, <material> <top>, <adjective> <top>"
27
+ # Category 8 : skirt:
28
+ # "<color> skirt, <material> skirt, <adjective> skirt"
29
+ # Category 9 : bottom garment (pants/shorts/etc.):
30
+ # "<color> <bottom>, <material> <bottom>, <adjective> <bottom>"
31
+ # Category 10: footwear:
32
+ # "<color> <footwear>, <adjective> <footwear>, <adjective> <footwear>"
33
+ # Category 11: headwear:
34
+ # "<color> <headwear> headwear"
35
+ #
36
+ # Precision > token cost > political correctness, tuned for literal image generators.
37
+ # ------------------------------------------------------------
38
+
39
+ class Wildcard_2:
40
+
41
+ @classmethod
42
+ def INPUT_TYPES(cls):
43
+ return {
44
+ "required": {
45
+ "seed": ("INT", {
46
+ "default": 0,
47
+ "min": -1,
48
+ "max": 2**31 - 1,
49
+ "step": 1
50
+ }),
51
+ },
52
+ }
53
+
54
+ RETURN_TYPES = ("STRING", "STRING")
55
+ RETURN_NAMES = ("female_prompt", "male_prompt")
56
+ FUNCTION = "generate"
57
+ CATEGORY = "prompt/wildcards"
58
+
59
+ # -----------------------------
60
+ # Core public entry point
61
+ # -----------------------------
62
+ def generate(self, seed: int):
63
+ # Seed handling:
64
+ # seed >= 0 -> deterministic
65
+ # seed < 0 -> non-deterministic (system randomness)
66
+ if seed >= 0:
67
+ rng = random.Random(seed)
68
+ else:
69
+ rng = random.Random()
70
+
71
+ # Generate base traits once so male & female versions match
72
+ base_traits = self._generate_base_traits(rng)
73
+
74
+ female_prompt = self._build_prompt(base_traits, gender="female")
75
+ male_prompt = self._build_prompt(base_traits, gender="male")
76
+
77
+ return (female_prompt, male_prompt)
78
+
79
+ # -----------------------------
80
+ # Base trait randomization
81
+ # -----------------------------
82
+ def _generate_base_traits(self, rng: random.Random) -> dict:
83
+ # Camera POV (LEFTSIDE / RIGHTSIDE in caps where used)
84
+ camera_povs = [
85
+ "straight-on",
86
+ "Frontview",
87
+ "Diagonal LEFTSIDE",
88
+ "Three Quarter View LEFTSIDE",
89
+ "Sideview LEFTSIDE",
90
+ "frontview POV",
91
+ "Sideview RIGHTSIDE",
92
+ "Three Quarter View RIGHTSIDE",
93
+ ]
94
+ camera_pov = rng.choice(camera_povs)
95
+
96
+ # Age bucket decides Category 3 word for each gender
97
+ age_buckets = ["young", "adult", "old"]
98
+ age_bucket = rng.choice(age_buckets)
99
+
100
+ # Social + inner personality traits (Category 4)
101
+ social_traits = [
102
+ "rich",
103
+ "high-class",
104
+ "middle-class",
105
+ "low-class",
106
+ "noble",
107
+ "royal",
108
+ "commoner",
109
+ "servant",
110
+ "outcast",
111
+ "scholar",
112
+ "warrior",
113
+ "mercenary",
114
+ "assassin",
115
+ "hunter",
116
+ "merchant",
117
+ "priestly",
118
+ "streetwise",
119
+ "criminal",
120
+ ]
121
+ inner_traits = [
122
+ "sadistic",
123
+ "kind",
124
+ "shy",
125
+ "cheerful",
126
+ "cold",
127
+ "calculating",
128
+ "lonely",
129
+ "obsessive",
130
+ "playful",
131
+ "stoic",
132
+ "anxious",
133
+ "confident",
134
+ "sarcastic",
135
+ "gentle",
136
+ ]
137
+ social_trait = rng.choice(social_traits)
138
+ inner_trait = rng.choice(inner_traits)
139
+
140
+ # Skin tones (avoid ambiguous 'colored' wording)
141
+ skin_tones = [
142
+ "very pale skincolor",
143
+ "fair skincolor",
144
+ "light tan skincolor",
145
+ "tan skincolor",
146
+ "olive skincolor",
147
+ "medium brown skincolor",
148
+ "dark brown skincolor",
149
+ "freckled skincolor",
150
+ ]
151
+ skin_tone = rng.choice(skin_tones)
152
+
153
+ # Eyes
154
+ eye_colors = [
155
+ "blue eyes",
156
+ "green eyes",
157
+ "grey eyes",
158
+ "brown eyes",
159
+ "hazel eyes",
160
+ "amber eyes",
161
+ "dark brown eyes",
162
+ "pale blue eyes",
163
+ "violet eyes",
164
+ ]
165
+ eye_styles = [
166
+ "sharp eyes",
167
+ "soft eyes",
168
+ "narrow eyes",
169
+ "wide eyes",
170
+ "sleepy eyes",
171
+ "intense eyes",
172
+ "cold eyes",
173
+ "warm eyes",
174
+ "round eyes",
175
+ ]
176
+ eye_directions = [
177
+ "",
178
+ ]
179
+ eye_color = rng.choice(eye_colors)
180
+ eye_style = rng.choice(eye_styles)
181
+ eye_direction = rng.choice(eye_directions)
182
+
183
+ # Hair (color, length, style)
184
+ hair_colors = [
185
+ "black hair",
186
+ "dark brown hair",
187
+ "brown hair",
188
+ "light brown hair",
189
+ "blonde hair",
190
+ "platinum blonde hair",
191
+ "ginger hair", # use 'ginger' instead of 'red hair'
192
+ "silver hair",
193
+ "white hair",
194
+ "blue hair",
195
+ "pink hair",
196
+ "purple hair",
197
+ ]
198
+ hair_lengths = [
199
+ "very short hair",
200
+ "short hair",
201
+ "medium length hair",
202
+ "long hair",
203
+ "very long hair",
204
+ "waist-length hair",
205
+ ]
206
+ hair_styles = [
207
+ "straight hair",
208
+ "wavy hair",
209
+ "curly hair",
210
+ "messy hair",
211
+ "braided hair",
212
+ "spiky hair",
213
+ "neatly combed hair",
214
+ ]
215
+ hair_color = rng.choice(hair_colors)
216
+ hair_length = rng.choice(hair_lengths)
217
+ hair_style = rng.choice(hair_styles)
218
+
219
+ # Generic color palette for clothing and accessories
220
+ clothing_colors = [
221
+ "black",
222
+ "white",
223
+ "grey",
224
+ "brown",
225
+ "beige",
226
+ "red",
227
+ "blue",
228
+ "navy",
229
+ "green",
230
+ "yellow",
231
+ "purple",
232
+ "pink",
233
+ ]
234
+
235
+ # Top garment
236
+ top_types = [
237
+ "shirt",
238
+ "blouse",
239
+ "jacket",
240
+ "coat",
241
+ "sweater",
242
+ "hoodie",
243
+ "turtleneck",
244
+ "robe",
245
+ "vest",
246
+ ]
247
+ top_materials = [
248
+ "cotton",
249
+ "linen",
250
+ "silk",
251
+ "wool",
252
+ "leather",
253
+ "denim",
254
+ "canvas",
255
+ ]
256
+ top_adjectives = [
257
+ "simple",
258
+ "elegant",
259
+ "torn",
260
+ "baggy",
261
+ "tight",
262
+ "oversized",
263
+ "patterned",
264
+ "striped",
265
+ "checkered",
266
+ "decorated",
267
+ "embroidered",
268
+ "formal",
269
+ "casual",
270
+ ]
271
+ top_type = rng.choice(top_types)
272
+ top_color = rng.choice(clothing_colors)
273
+ top_material = rng.choice(top_materials)
274
+ top_adjective = rng.choice(top_adjectives)
275
+
276
+ # Skirt
277
+ skirt_colors = clothing_colors
278
+ skirt_materials = [
279
+ "cotton",
280
+ "linen",
281
+ "wool",
282
+ "silk",
283
+ "leather",
284
+ "denim",
285
+ "fur",
286
+ ]
287
+ skirt_adjectives = [
288
+ "short",
289
+ "knee-length",
290
+ "long",
291
+ "pleated",
292
+ "frilled",
293
+ "flowing",
294
+ "tight",
295
+ "dotted",
296
+ "checkered",
297
+ "patterned",
298
+ ]
299
+ skirt_color = rng.choice(skirt_colors)
300
+ skirt_material = rng.choice(skirt_materials)
301
+ skirt_adjective = rng.choice(skirt_adjectives)
302
+
303
+ # Bottom garment (pants / shorts / jeans / leggings, etc.)
304
+ bottom_types = [
305
+ "pants",
306
+ "trousers",
307
+ "jeans",
308
+ "shorts",
309
+ "leggings",
310
+ ]
311
+ bottom_materials = [
312
+ "cotton",
313
+ "linen",
314
+ "wool",
315
+ "denim",
316
+ "leather",
317
+ "synthetic",
318
+ ]
319
+ bottom_adjectives = [
320
+ "fluffy",
321
+ "baggy",
322
+ "tight",
323
+ "ripped",
324
+ "formal",
325
+ "casual",
326
+ "loose",
327
+ "high-waist",
328
+ "low-waist",
329
+ ]
330
+ bottom_type = rng.choice(bottom_types)
331
+ bottom_color = rng.choice(clothing_colors)
332
+ bottom_material = rng.choice(bottom_materials)
333
+ bottom_adjective = rng.choice(bottom_adjectives)
334
+
335
+ # Footwear
336
+ footwear_types = [
337
+ "boots",
338
+ "shoes",
339
+ "sneakers",
340
+ "sandals",
341
+ "high heels",
342
+ "loafers",
343
+ ]
344
+ footwear_adjectives = [
345
+ "long", # works for boots (long boots)
346
+ "ankle-high",
347
+ "knee-high",
348
+ "lace-up",
349
+ "high-heeled",
350
+ "elegant",
351
+ "sturdy",
352
+ "worn",
353
+ "dirty",
354
+ "polished",
355
+ ]
356
+ footwear_type = rng.choice(footwear_types)
357
+ footwear_color = rng.choice(clothing_colors)
358
+ # ensure two different adjectives if possible
359
+ fw_adj1 = rng.choice(footwear_adjectives)
360
+ fw_adj2_options = [a for a in footwear_adjectives if a != fw_adj1]
361
+ fw_adj2 = rng.choice(fw_adj2_options) if fw_adj2_options else fw_adj1
362
+
363
+ # Headwear
364
+ headwear_types = [
365
+ "tiara",
366
+ "crown",
367
+ "hat",
368
+ "beret",
369
+ "beanie",
370
+ "headband",
371
+ "hood",
372
+ "helmet",
373
+ "cap",
374
+ "fedora",
375
+ "top hat",
376
+ "bowler hat",
377
+ "straw hat",
378
+ "sun hat",
379
+ "bucket hat",
380
+ "boater hat",
381
+ "panama hat",
382
+ "cloche hat",
383
+ "pillbox hat",
384
+ "fascinator",
385
+ "wide-brim hat",
386
+ "cowboy hat",
387
+ "sombrero",
388
+ "safari hat",
389
+ "pith helmet",
390
+ "trilby",
391
+ "newsboy cap",
392
+ "flat cap",
393
+ "ivy cap",
394
+ "driver cap",
395
+ "baseball cap",
396
+ "snapback cap",
397
+ "trucker cap",
398
+ "visor",
399
+ "sun visor",
400
+ "party cone hat",
401
+ "birthday hat",
402
+ "graduation cap",
403
+ "mortarboard",
404
+ "chef hat",
405
+ "toque",
406
+ "nurse cap",
407
+ "maid headband",
408
+ "sailor cap",
409
+ "military cap",
410
+ "officer cap",
411
+ "forage cap",
412
+ "garrison cap",
413
+ "tam o'shanter",
414
+ "deerstalker hat",
415
+ "ushanka",
416
+ "trapper hat",
417
+ "winter hat",
418
+ "knit cap",
419
+ "pom-pom beanie",
420
+ "ski cap",
421
+ "balaclava",
422
+ "hooded cloak",
423
+ "cowl hood",
424
+ "earmuffs",
425
+ "earflap hat",
426
+ "rain hat",
427
+ "oilskin hat",
428
+ "hard hat",
429
+ "construction helmet",
430
+ "miner helmet",
431
+ "bike helmet",
432
+ "motorcycle helmet",
433
+ "combat helmet",
434
+ "riot helmet",
435
+ "samurai helmet",
436
+ "horned helmet",
437
+ "viking helmet",
438
+ "roman helmet",
439
+ "spartan helmet",
440
+ "space helmet",
441
+ "astronaut helmet",
442
+ "pilot cap",
443
+ "flight helmet",
444
+ "aviator cap",
445
+ "swim cap",
446
+ "shower cap",
447
+ "sleeping cap",
448
+ "nightcap",
449
+ "veil",
450
+ "veiled hat",
451
+ "lace headscarf",
452
+ "headscarf",
453
+ "bandana",
454
+ "kerchief",
455
+ "turban",
456
+ "keffiyeh",
457
+ "wrapped headscarf",
458
+ "flower crown",
459
+ "floral wreath",
460
+ "leaf crown",
461
+ "laurel wreath",
462
+ "circlet",
463
+ "halo headband",
464
+ "sports headband",
465
+ "sweatband",
466
+ "visor cap",
467
+ "wizard hat",
468
+ "witch hat",
469
+ "magician hat",
470
+ "jester hat",
471
+ "animal-ear headband",
472
+ "horn headband",
473
+ "antenna headband",
474
+ "visor",
475
+ "sunhat",
476
+ "fedora",
477
+ "bowler hat",
478
+ "top hat",
479
+ "trilby",
480
+ "bucket hat",
481
+ "boater hat",
482
+ "newsboy cap",
483
+ "flat cap",
484
+ "snapback cap",
485
+ "baseball cap",
486
+ "cowboy hat",
487
+ "wide-brim hat",
488
+ "straw hat",
489
+ "panama hat",
490
+ "safari hat",
491
+ "rain hat",
492
+ "fur hat",
493
+ "ushanka hat",
494
+ "ski cap",
495
+ "earflap hat",
496
+ "turban",
497
+ "headscarf",
498
+ "bandana",
499
+ "kerchief",
500
+ "hooded cloak",
501
+ "wizard hat",
502
+ "witch hat",
503
+ "party hat",
504
+ "chef hat",
505
+ "nurse cap",
506
+ "military cap",
507
+ "officer cap",
508
+ "combat helmet",
509
+ "biker helmet",
510
+ "space helmet",
511
+ "pilot cap",
512
+ "aviator helmet",
513
+ "construction helmet",
514
+ "hard hat",
515
+ "diving helmet",
516
+ "knight helmet",
517
+ "samurai helmet",
518
+ "viking helmet",
519
+ "horned helmet",
520
+ "circlet",
521
+ "laurel wreath",
522
+ "flower crown",
523
+ "veil",
524
+ "bridal veil",
525
+ "fascinator",
526
+ "pillbox hat",
527
+ "cloche hat",
528
+ "mob cap",
529
+ "nightcap",
530
+ "sleeping cap",
531
+ "sailor cap",
532
+ "conductor cap",
533
+ "police cap",
534
+ "firefighter helmet",
535
+ "ranger hat",
536
+ "tactical helmet",
537
+ "bike helmet",
538
+ "sports visor",
539
+ "golf cap",
540
+ "hunting cap",
541
+ "trapper hat",
542
+ "tam o'shanter",
543
+ "deerstalker hat",
544
+ "santa hat",
545
+ "jester hat",
546
+ "carnival mask",
547
+ "masquerade mask",
548
+ "plague doctor mask",
549
+ "gas mask",
550
+ "earmuffs",
551
+ "cowl",
552
+ "balaclava",
553
+ "ski mask",
554
+ "hooded scarf",
555
+ "head wrap",
556
+ "shawl hood",
557
+ "animal hood",
558
+ "wolf hood",
559
+ "cat ear headband",
560
+ "bear ear headband",
561
+ "devil horn headband",
562
+ "bunny ear headband",
563
+ "antenna headband",
564
+ "spiked crown",
565
+ "metal halo",
566
+ "glowing halo",
567
+ "tiara crown",
568
+ "opera hat",
569
+ "feathered hat",
570
+ "fur hood",
571
+ "rain hood",
572
+ "straw bonnet",
573
+ "bonnet",
574
+ ]
575
+ headwear_color = rng.choice(clothing_colors)
576
+ headwear_type = rng.choice(headwear_types)
577
+
578
+ return {
579
+ "camera_pov": camera_pov,
580
+ "age_bucket": age_bucket,
581
+ "social_trait": social_trait,
582
+ "inner_trait": inner_trait,
583
+ "skin_tone": skin_tone,
584
+ "eye_color": eye_color,
585
+ "eye_style": eye_style,
586
+ "eye_direction": eye_direction,
587
+ "hair_color": hair_color,
588
+ "hair_length": hair_length,
589
+ "hair_style": hair_style,
590
+ "top_color": top_color,
591
+ "top_material": top_material,
592
+ "top_adjective": top_adjective,
593
+ "top_type": top_type,
594
+ "skirt_color": skirt_color,
595
+ "skirt_material": skirt_material,
596
+ "skirt_adjective": skirt_adjective,
597
+ "bottom_color": bottom_color,
598
+ "bottom_material": bottom_material,
599
+ "bottom_adjective": bottom_adjective,
600
+ "bottom_type": bottom_type,
601
+ "footwear_color": footwear_color,
602
+ "footwear_type": footwear_type,
603
+ "footwear_adj1": fw_adj1,
604
+ "footwear_adj2": fw_adj2,
605
+ "headwear_color": headwear_color,
606
+ "headwear_type": headwear_type,
607
+ }
608
+
609
+ # -----------------------------
610
+ # Prompt assembly
611
+ # -----------------------------
612
+ def _build_prompt(self, base: dict, gender: str) -> str:
613
+ # gender: "female" or "male"
614
+ assert gender in ("female", "male")
615
+
616
+ # Category 1: "1girl" or "1boy"
617
+ if gender == "female":
618
+ cat1 = "1girl"
619
+ else:
620
+ cat1 = "1boy"
621
+
622
+ # Category 3: age word ("woman"/"man"/"girl"/"boy"/"grandma"/"grandpa")
623
+ age_bucket = base["age_bucket"]
624
+ if gender == "female":
625
+ if age_bucket == "young":
626
+ age_word = "girl"
627
+ elif age_bucket == "adult":
628
+ age_word = "woman"
629
+ else:
630
+ age_word = "loli"
631
+ else:
632
+ if age_bucket == "young":
633
+ age_word = "boy"
634
+ elif age_bucket == "adult":
635
+ age_word = "man"
636
+ else:
637
+ age_word = "shota"
638
+
639
+ # Category 4: social + inner personality descriptors that use the age_word
640
+ social_token = f"{base['social_trait']} {age_word}"
641
+ inner_token = f"{base['inner_trait']} {age_word}"
642
+
643
+ # Category 5: eyes (color, style, direction)
644
+ eye_color = base["eye_color"] # already contains "eyes"
645
+ eye_style = base["eye_style"] # already contains "eyes"
646
+ eye_direction = base["eye_direction"] # already contains "eyes"
647
+
648
+ # Category 6: hair
649
+ hair_color = base["hair_color"]
650
+ hair_length = base["hair_length"]
651
+ hair_style = base["hair_style"]
652
+
653
+ # Category 7: top garment
654
+ top_color = f"{base['top_color']} {base['top_type']}"
655
+ top_material = f"{base['top_material']} {base['top_type']}"
656
+ top_adjective = f"{base['top_adjective']} {base['top_type']}"
657
+
658
+ # Category 8: skirt
659
+ skirt_color = f"{base['skirt_color']} skirt"
660
+ skirt_material = f"{base['skirt_material']} skirt"
661
+ skirt_adjective = f"{base['skirt_adjective']} skirt"
662
+
663
+ # Category 9: bottom garment (pants etc.)
664
+ bottom_color = f"{base['bottom_color']} {base['bottom_type']}"
665
+ bottom_material = f"{base['bottom_material']} {base['bottom_type']}"
666
+ bottom_adjective = f"{base['bottom_adjective']} {base['bottom_type']}"
667
+
668
+ # Category 10: footwear
669
+ footwear_color = f"{base['footwear_color']} {base['footwear_type']}"
670
+ footwear_adj1 = f"{base['footwear_adj1']} {base['footwear_type']}"
671
+ footwear_adj2 = f"{base['footwear_adj2']} {base['footwear_type']}"
672
+
673
+ # Category 11: headwear
674
+ headwear = f"{base['headwear_color']} {base['headwear_type']} headwear"
675
+
676
+ # Assemble all tokens in the logical order of your template
677
+ tokens = [
678
+ cat1, # Category 1
679
+ base["camera_pov"], # Category 2
680
+ age_word, # Category 3
681
+ social_token, # Category 4 (social)
682
+ inner_token, # Category 4 (inner)
683
+ base["skin_tone"], # skincolor descriptor
684
+ eye_color, eye_style, eye_direction, # Category 5
685
+ hair_color, hair_length, hair_style, # Category 6
686
+ top_color, top_material, top_adjective, # Category 7
687
+ skirt_color, skirt_material, skirt_adjective, # Category 8
688
+ bottom_color, bottom_material, bottom_adjective, # Category 9
689
+ footwear_color, footwear_adj1, footwear_adj2, # Category 10
690
+ headwear, # Category 11
691
+ ]
692
+
693
+ # Join tokens into a single prompt
694
+ prompt = ", ".join(tokens) + "."
695
+ return prompt
696
+
697
+
698
+ # ------------------------------------------------------------
699
+ # ComfyUI node registration
700
+ # ------------------------------------------------------------
701
+
702
+ NODE_CLASS_MAPPINGS = {
703
+ "Wildcard_2": Wildcard_2,
704
+ }
705
+
706
+ NODE_DISPLAY_NAME_MAPPINGS = {
707
+ "Wildcard_2": "Wildcard_2",
708
+ }
wildcard_4.py ADDED
@@ -0,0 +1,535 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ class Wildcard_4:
4
+ """
5
+ ComfyUI custom node:
6
+ Takes a seed and generates a pair of matching wildcard prompts:
7
+ - one female-focused ("1girl")
8
+ - one male-focused ("1boy")
9
+ """
10
+
11
+ @classmethod
12
+ def INPUT_TYPES(cls):
13
+ return {
14
+ "required": {
15
+ "seed": ("INT", {
16
+ "default": 0,
17
+ "min": 0,
18
+ "max": 0x7FFFFFFF,
19
+ "step": 1,
20
+ }),
21
+ },
22
+ }
23
+
24
+ RETURN_TYPES = ("STRING", "STRING",)
25
+ RETURN_NAMES = ("female_prompt", "male_prompt",)
26
+ FUNCTION = "generate"
27
+ CATEGORY = "wildcards/prompt"
28
+
29
+ # -------- core random building blocks --------
30
+
31
+ def _select_age_group(self, rng: random.Random):
32
+ # young / adult / elder - affects the noun in category 3
33
+ return rng.choice(["young", "adult", "kid"])
34
+
35
+ def _age_noun(self, variant: str, age_group: str) -> str:
36
+ # variant: "female" or "male"
37
+ if variant == "female":
38
+ if age_group == "young":
39
+ return "girl"
40
+ elif age_group == "kid":
41
+ return "loli"
42
+ else:
43
+ return "woman"
44
+ else:
45
+ if age_group == "young":
46
+ return "boy"
47
+ elif age_group == "kid":
48
+ return "shota"
49
+ else:
50
+ return "man"
51
+
52
+ def _camera_pov(self, rng: random.Random) -> str:
53
+ # Category 2: POV of camera – 8 directions, LEFTSIDE/RIGHTSIDE in caps
54
+ pov_options = [
55
+ "straight-on",
56
+ "diagonal LEFTSIDE",
57
+ "sideview LEFTSIDE",
58
+ "straight-on",
59
+ "sideview RIGHTSIDE",
60
+ "diagonal RIGHTSIDE",
61
+ "low-angle view from below",
62
+ ]
63
+ return rng.choice(pov_options)
64
+
65
+ def _skincolor(self, rng: random.Random) -> str:
66
+ # No "colored skincolor" nonsense; purely descriptive
67
+ options = [
68
+ "porcelain skincolor",
69
+ "pale skincolor",
70
+ "fair skincolor",
71
+ "light brown skincolor",
72
+ "sun-kissed tan skincolor",
73
+ "olive skincolor",
74
+ "warm brown skincolor",
75
+ "deep brown skincolor",
76
+ "ashen grey skincolor",
77
+ "golden bronze skincolor",
78
+ ]
79
+ return rng.choice(options)
80
+
81
+ def _eyes(self, rng: random.Random):
82
+ # Category 5: eyecolor, eye-style, look direction – always with "eyes"
83
+ eye_colors = [
84
+ "blue", "green", "hazel", "amber",
85
+ "grey", "violet", "golden", "turquoise",
86
+ ]
87
+ eye_styles = [
88
+ "sharp gaze",
89
+ "cold gaze",
90
+ "soft gaze",
91
+ "tired gaze",
92
+ "glowing gaze",
93
+ "mysterious gaze",
94
+ "intense gaze",
95
+ "dreamy gaze",
96
+ ]
97
+ eye_directions = [
98
+ "",
99
+ ]
100
+ color = rng.choice(eye_colors)
101
+ style = rng.choice(eye_styles)
102
+ direction = rng.choice(eye_directions)
103
+ return [
104
+ f"{color} eyes",
105
+ f"{style} eyes",
106
+ f"{direction} eyes",
107
+ ]
108
+
109
+ def _hair(self, rng: random.Random):
110
+ # Category 6: hair (color, length, hairstyle), note: no "red hair" – use "ginger"
111
+ hair_colors = [
112
+ "black",
113
+ "white",
114
+ "silver",
115
+ "ginger",
116
+ "copper",
117
+ "dark brown",
118
+ "chestnut",
119
+ "platinum blonde",
120
+ "strawberry blonde",
121
+ "midnight blue",
122
+ "lavender",
123
+ ]
124
+ hair_lengths = [
125
+ "very short",
126
+ "short",
127
+ "shoulder-length",
128
+ "long",
129
+ "very long",
130
+ "waist-length",
131
+ "ankle-length",
132
+ ]
133
+ hair_styles = [
134
+ "straight",
135
+ "wavy",
136
+ "curly",
137
+ "messy",
138
+ "braided",
139
+ "twin braids",
140
+ "high ponytail",
141
+ "low ponytail",
142
+ "intricate updo",
143
+ "windswept",
144
+ "spiky",
145
+ ]
146
+ color = rng.choice(hair_colors)
147
+ length = rng.choice(hair_lengths)
148
+ style = rng.choice(hair_styles)
149
+ return [
150
+ f"{color} hair",
151
+ f"{length} hair",
152
+ f"{style} hair",
153
+ ]
154
+
155
+ def _top_garment(self, rng: random.Random):
156
+ # Category 7: top garment – color, material, descriptive adjective; same noun
157
+ garment_nouns = [
158
+ # basic tops
159
+ "shirt",
160
+ "t-shirt",
161
+ "tank top",
162
+ "crop top",
163
+ "blouse",
164
+ "dress shirt",
165
+ "polo shirt",
166
+ "long-sleeve shirt",
167
+ # fitted / structured
168
+ "vest",
169
+ "waistcoat",
170
+ "bodice",
171
+ "corset",
172
+ # knitwear
173
+ "sweater",
174
+ "jumper",
175
+ "cardigan",
176
+ "pullover",
177
+ "sweatshirt",
178
+ "hoodie",
179
+ # loose / draped
180
+ "poncho",
181
+ "tunic",
182
+ "smock",
183
+ # robes & kimonos
184
+ "kimono",
185
+ "haori",
186
+ "robe",
187
+ "cloak",
188
+ "cape",
189
+ # jackets & coats
190
+ "jacket",
191
+ "denim jacket",
192
+ "leather jacket",
193
+ "bomber jacket",
194
+ "biker jacket",
195
+ "puffer jacket",
196
+ "parka",
197
+ "trench coat",
198
+ "overcoat",
199
+ "coat",
200
+ "pea coat",
201
+ # fantasy / historical
202
+ "doublet",
203
+ "tabard",
204
+ "jersey",
205
+ "sports jersey",
206
+ "armor vest",
207
+ "breastplate",
208
+ "armor top",
209
+ ]
210
+ colors = [
211
+ "red", "crimson", "blue", "navy", "emerald",
212
+ "black", "white", "grey", "brown", "tan",
213
+ "purple", "royal purple", "teal",
214
+ ]
215
+ materials = [
216
+ "linen", "silk", "velvet", "cotton",
217
+ "leather", "chainmail", "satin", "wool",
218
+ "dragon-scale", "enchanted silk",
219
+ ]
220
+ descriptors = [
221
+ "torn", "ornate", "simple", "elegant",
222
+ "battle-worn", "embroidered", "loose",
223
+ "tight-fitting", "ceremonial", "casual",
224
+ "fancy", "royal",
225
+ ]
226
+ noun = rng.choice(garment_nouns)
227
+ color = rng.choice(colors)
228
+ material = rng.choice(materials)
229
+ descriptor = rng.choice(descriptors)
230
+ return [
231
+ f"{color} {noun}",
232
+ f"{material} {noun}",
233
+ f"{descriptor} {noun}",
234
+ ]
235
+
236
+ def _skirt(self, rng: random.Random):
237
+ # Category 8: skirt – always skirt, color + length + descriptor
238
+ colors = [
239
+ "black", "brown", "white", "grey", "navy",
240
+ "deep red", "forest green", "midnight blue",
241
+ "burgundy", "ivory",
242
+ ]
243
+ lengths = [
244
+ "short", "knee-length", "mid-length",
245
+ "long", "floor-length",
246
+ ]
247
+ descriptors = [
248
+ "pleated", "ruffled", "fur", "layered",
249
+ "dotted", "striped", "checkered",
250
+ "embroidered", "shimmering", "tattered",
251
+ ]
252
+ color = rng.choice(colors)
253
+ length = rng.choice(lengths)
254
+ descriptor = rng.choice(descriptors)
255
+ return [
256
+ f"{color} skirt",
257
+ f"{length} skirt",
258
+ f"{descriptor} skirt",
259
+ ]
260
+
261
+ def _bottoms(self, rng: random.Random):
262
+ # Category 9: any bottom garment – but consistent noun
263
+ garment_nouns = [
264
+ # pants & trousers
265
+ "pants",
266
+ "trousers",
267
+ "jeans",
268
+ "skinny jeans",
269
+ "cargo pants",
270
+ "sweatpants",
271
+ "track pants",
272
+ "yoga pants",
273
+ "chinos",
274
+ "slacks",
275
+ "dress pants",
276
+ "khaki pants",
277
+ "work pants",
278
+ # tight legwear
279
+ "leggings",
280
+ "tights",
281
+ "stockings",
282
+ # shorts
283
+ "shorts",
284
+ "denim shorts",
285
+ "bike shorts",
286
+ "running shorts",
287
+ # cropped / alternative cuts
288
+ "capris",
289
+ "breeches",
290
+ "riding pants",
291
+ "hakama",
292
+ "culottes",
293
+ "joggers",
294
+ # fantasy / armor
295
+ "battle greaves",
296
+ ]
297
+ colors = [
298
+ "black", "dark brown", "grey", "charcoal",
299
+ "olive", "navy", "sand-colored",
300
+ ]
301
+ materials = [
302
+ "leather", "denim", "wool", "linen",
303
+ "padded", "scale-plated",
304
+ ]
305
+ descriptors = [
306
+ "tailored", "baggy", "fluffy", "fitted",
307
+ "torn", "patched", "armored",
308
+ ]
309
+ noun = rng.choice(garment_nouns)
310
+ color = rng.choice(colors)
311
+ material = rng.choice(materials)
312
+ descriptor = rng.choice(descriptors)
313
+ return [
314
+ f"{color} {noun}",
315
+ f"{material} {noun}",
316
+ f"{descriptor} {noun}",
317
+ ]
318
+
319
+ def _footwear(self, rng: random.Random):
320
+ # Category 10: any footwear – color + two styles, same base noun
321
+ shoe_nouns = [
322
+ "boots",
323
+ "shoes",
324
+ "sandals",
325
+ "riding boots",
326
+ "combat boots",
327
+ "high-heeled boots",
328
+ ]
329
+ colors = [
330
+ "black", "dark brown", "grey", "white",
331
+ "red", "crimson", "navy", "forest green",
332
+ ]
333
+ styles = [
334
+ "long", "knee-high", "ankle", "lace-up",
335
+ "fur-lined", "armored", "worn", "polished",
336
+ "buckled",
337
+ ]
338
+ noun = rng.choice(shoe_nouns)
339
+ color = rng.choice(colors)
340
+ style1, style2 = rng.sample(styles, 2)
341
+ return [
342
+ f"{color} {noun}",
343
+ f"{style1} {noun}",
344
+ f"{style2} {noun}",
345
+ ]
346
+
347
+ def _headwear(self, rng: random.Random):
348
+ # HEADWEAR: color + thing + literal word "headwear" at the end
349
+ headpieces = [
350
+ # caps & casual
351
+ "cap",
352
+ "baseball cap",
353
+ "beanie",
354
+ "knit cap",
355
+ "watch cap",
356
+ "newsboy cap",
357
+ "flat cap",
358
+ # hats
359
+ "bucket hat",
360
+ "sun hat",
361
+ "wide-brim hat",
362
+ "straw hat",
363
+ "boater hat",
364
+ "panama hat",
365
+ "fedora",
366
+ "trilby",
367
+ "top hat",
368
+ "bowler hat",
369
+ "cowboy hat",
370
+ "sombrero",
371
+ "fur hat",
372
+ "ushanka",
373
+ # sport / utility
374
+ "visor",
375
+ "headband",
376
+ "bandana",
377
+ "balaclava",
378
+ # hoods & helmets
379
+ "hood",
380
+ "cloak hood",
381
+ "helmet",
382
+ "combat helmet",
383
+ "riding helmet",
384
+ "steel helm",
385
+ "open-face helm",
386
+ "closed helm",
387
+ "coif",
388
+ # crowns etc
389
+ "circlet",
390
+ "tiara",
391
+ "crown",
392
+ "laurel wreath",
393
+ "flower crown",
394
+ # veils / scarves
395
+ "veil",
396
+ "headscarf",
397
+ "kerchief",
398
+ # fantasy
399
+ "witch hat",
400
+ "wizard hat",
401
+ "officer cap",
402
+ ]
403
+ colors = [
404
+ "black", "silver", "gold", "dark-grey",
405
+ "white", "red", "purple", "green",
406
+ ]
407
+ color = rng.choice(colors)
408
+ piece = rng.choice(headpieces)
409
+ return f"{color} {piece} headwear"
410
+
411
+ def _social_inner_traits(self, rng: random.Random):
412
+ # Category 4: outer/social & inner personality
413
+ social_traits = [
414
+ "rich",
415
+ "noble",
416
+ "royal",
417
+ "streetwise",
418
+ "mysterious",
419
+ "famous",
420
+ "feared",
421
+ "holy",
422
+ "cursed",
423
+ "arcane",
424
+ ]
425
+ inner_traits = [
426
+ "kind",
427
+ "melancholic",
428
+ "sadistic",
429
+ "playful",
430
+ "arrogant",
431
+ "stoic",
432
+ "cold-blooded",
433
+ "gentle",
434
+ "obsessive",
435
+ "scheming",
436
+ ]
437
+ social = rng.choice(social_traits)
438
+ inner = rng.choice(inner_traits)
439
+ if inner == social:
440
+ # ensure two distinct traits
441
+ inner = rng.choice([t for t in inner_traits if t != social])
442
+ return social, inner
443
+
444
+ def _build_shared_description(self, rng: random.Random):
445
+ """
446
+ Use one RNG to build *shared* visual traits.
447
+ Gender-specific bits are applied later so male/female
448
+ prompts stay in sync.
449
+ """
450
+ age_group = self._select_age_group(rng)
451
+ pov = self._camera_pov(rng)
452
+ social, inner = self._social_inner_traits(rng)
453
+ skin = self._skincolor(rng)
454
+ eyes = self._eyes(rng)
455
+ hair = self._hair(rng)
456
+ top = self._top_garment(rng)
457
+ skirt = self._skirt(rng)
458
+ bottoms = self._bottoms(rng)
459
+ footwear = self._footwear(rng)
460
+ headwear = self._headwear(rng)
461
+
462
+ return {
463
+ "age_group": age_group,
464
+ "pov": pov,
465
+ "social_trait": social,
466
+ "inner_trait": inner,
467
+ "skin": skin,
468
+ "eyes": eyes,
469
+ "hair": hair,
470
+ "top": top,
471
+ "skirt": skirt,
472
+ "bottoms": bottoms,
473
+ "footwear": footwear,
474
+ "headwear": headwear,
475
+ }
476
+
477
+ def _build_prompt_from_shared(self, shared: dict, variant: str) -> str:
478
+ assert variant in ("female", "male")
479
+ age_group = shared["age_group"]
480
+ pov = shared["pov"]
481
+ social_trait = shared["social_trait"]
482
+ inner_trait = shared["inner_trait"]
483
+
484
+ age_noun = self._age_noun(variant, age_group)
485
+
486
+ # Category 1: gender count (always 1girl / 1boy)
487
+ cat1 = "1girl" if variant == "female" else "1boy"
488
+
489
+ # Category 2: camera POV
490
+ cat2 = pov
491
+
492
+ # Category 3: age noun (woman / man / girl / boy / grandma / grandpa)
493
+ cat3 = age_noun
494
+
495
+ # Category 4: social + inner traits, both reuse age_noun as second word
496
+ cat4_1 = f"{social_trait} {age_noun}"
497
+ cat4_2 = f"{inner_trait} {age_noun}"
498
+
499
+ parts = []
500
+ parts.append(cat1)
501
+ parts.append(cat2)
502
+ parts.append(cat3)
503
+ parts.append(cat4_1)
504
+ parts.append(cat4_2)
505
+ parts.append(shared["skin"])
506
+ parts.extend(shared["eyes"])
507
+ parts.extend(shared["hair"])
508
+ parts.extend(shared["top"])
509
+ parts.extend(shared["skirt"])
510
+ parts.extend(shared["bottoms"])
511
+ parts.extend(shared["footwear"])
512
+ parts.append(shared["headwear"])
513
+
514
+ return ", ".join(parts)
515
+
516
+ # -------- main entrypoint --------
517
+
518
+ def generate(self, seed: int):
519
+ rng = random.Random(int(seed))
520
+
521
+ shared = self._build_shared_description(rng)
522
+ female_prompt = self._build_prompt_from_shared(shared, "female")
523
+ male_prompt = self._build_prompt_from_shared(shared, "male")
524
+
525
+ return (female_prompt, male_prompt,)
526
+
527
+
528
+ # ComfyUI registration
529
+ NODE_CLASS_MAPPINGS = {
530
+ "Wildcard_4": Wildcard_4,
531
+ }
532
+
533
+ NODE_DISPLAY_NAME_MAPPINGS = {
534
+ "Wildcard_4": "Wildcard_4",
535
+ }