cryptopunks 1.2.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Manifest.txt +4 -20
- data/README.md +416 -34
- data/Rakefile +2 -1
- data/config/spritesheet.csv +762 -237
- data/config/spritesheet.png +0 -0
- data/lib/cryptopunks/composite.rb +3 -4
- data/lib/cryptopunks/contract/punksdata-assets.rb +338 -0
- data/lib/cryptopunks/contract/punksdata-contract.rb +55 -0
- data/lib/cryptopunks/contract/punksdata-meta.rb +2107 -0
- data/lib/cryptopunks/generator.rb +148 -43
- data/lib/cryptopunks/image.rb +4 -92
- data/lib/cryptopunks/tool.rb +381 -0
- data/lib/cryptopunks/version.rb +3 -3
- data/lib/cryptopunks.rb +113 -83
- metadata +21 -43
- data/config/more/alien-female.txt +0 -34
- data/config/more/ape-female.txt +0 -33
- data/config/more/demon-female.txt +0 -33
- data/config/more/demon-male.txt +0 -33
- data/config/more/mummy-female.txt +0 -33
- data/config/more/mummy-male.txt +0 -33
- data/config/more/orc-female.txt +0 -33
- data/config/more/orc-male.txt +0 -33
- data/config/more/robot-female.txt +0 -34
- data/config/more/robot-male.txt +0 -33
- data/config/more/skeleton-female.txt +0 -33
- data/config/more/skeleton-male.txt +0 -33
- data/config/more/vampire-female.txt +0 -33
- data/config/more/vampire-male.txt +0 -33
- data/config/more/zombie-female.txt +0 -33
- data/config/original/alien-male.txt +0 -33
- data/config/original/ape-male.txt +0 -33
- data/config/original/human-female.txt +0 -33
- data/config/original/human-male.txt +0 -34
- data/config/original/zombie-male.txt +0 -33
@@ -5,23 +5,36 @@ module Cryptopunks
|
|
5
5
|
### todo/fix:
|
6
6
|
## move into Punks::Metadata or such
|
7
7
|
class Sprite
|
8
|
-
attr_reader :id, :name, :type, :gender
|
8
|
+
attr_reader :id, :name, :type, :gender, :size, :more_names
|
9
9
|
|
10
10
|
|
11
11
|
def initialize( id:,
|
12
12
|
name:,
|
13
13
|
type:,
|
14
|
-
gender
|
15
|
-
|
16
|
-
|
17
|
-
@
|
18
|
-
@
|
14
|
+
gender:,
|
15
|
+
size:,
|
16
|
+
more_names: [] )
|
17
|
+
@id = id # zero-based index eg. 0,1,2,3, etc.
|
18
|
+
@name = name
|
19
|
+
@type = type
|
20
|
+
@gender = gender
|
21
|
+
@size = size
|
22
|
+
@more_names = more_names
|
19
23
|
end
|
20
24
|
|
21
25
|
## todo/check - find better names for type attribute/archetypes?
|
22
26
|
## use (alternate name/alias) base or face for archetypes? any others?
|
23
27
|
def attribute?() @type.downcase.start_with?( 'attribute' ); end
|
24
28
|
def archetype?() @type.downcase.start_with?( 'archetype' ); end
|
29
|
+
|
30
|
+
def small?() @size == 's'; end
|
31
|
+
def large?() @size == 'l'; end
|
32
|
+
def universal?() @size == 'u'; end
|
33
|
+
alias_method :unisize?, :universal? ## add unisize or allsizes or such - why? why not?
|
34
|
+
|
35
|
+
def male?() @gender == 'm'; end
|
36
|
+
def female?() @gender == 'f'; end
|
37
|
+
def unisex?() @gender == 'u'; end
|
25
38
|
end # class Metadata::Sprite
|
26
39
|
end # class Metadata
|
27
40
|
|
@@ -33,33 +46,60 @@ module Cryptopunks
|
|
33
46
|
######
|
34
47
|
# static helpers - (turn into "true" static self.class methods - why? why not?)
|
35
48
|
#
|
36
|
-
def normalize_key( str )
|
37
|
-
|
49
|
+
def self.normalize_key( str )
|
50
|
+
## add & e.g. B&W
|
51
|
+
str.downcase.gsub(/[ ()&°_-]/, '').strip
|
38
52
|
end
|
39
53
|
|
40
|
-
def normalize_gender( str )
|
54
|
+
def self.normalize_gender( str )
|
41
55
|
## e.g. Female => f
|
42
56
|
## F => f
|
43
|
-
## always return f
|
57
|
+
## always return f/m
|
58
|
+
str.downcase[0]
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.normalize_size( str )
|
62
|
+
## e.g. U or Unisize or Univeral => u
|
63
|
+
## S or Small => s
|
64
|
+
## L or Large => l
|
65
|
+
## always return u/l/s
|
44
66
|
str.downcase[0]
|
45
67
|
end
|
46
68
|
|
69
|
+
def self.normalize_name( str )
|
70
|
+
## normalize spaces in more names
|
71
|
+
str.strip.gsub( /[ ]{2,}/, ' ' )
|
72
|
+
end
|
73
|
+
|
74
|
+
def normalize_key( str ) self.class.normalize_key( str ); end
|
75
|
+
def normalize_gender( str ) self.class.normalize_gender( str ); end
|
76
|
+
def normalize_size( str ) self.class.normalize_size( str ); end
|
77
|
+
def normalize_name( str ) self.class.normalize_name( str ); end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
|
47
83
|
|
48
84
|
def build_attributes_by_name( recs )
|
49
85
|
h = {}
|
50
86
|
recs.each_with_index do |rec|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
87
|
+
names = [rec.name] + rec.more_names
|
88
|
+
|
89
|
+
names.each do |name|
|
90
|
+
key = normalize_key( name )
|
91
|
+
key << "_(#{rec.gender}+#{rec.size})" if rec.attribute?
|
92
|
+
|
93
|
+
if h[ key ]
|
94
|
+
puts "!!! ERROR - attribute name is not unique:"
|
95
|
+
pp rec
|
96
|
+
puts "duplicate:"
|
97
|
+
pp h[key]
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
h[ key ] = rec
|
60
101
|
end
|
61
|
-
|
62
|
-
end
|
102
|
+
end
|
63
103
|
## pp h
|
64
104
|
h
|
65
105
|
end
|
@@ -83,16 +123,22 @@ module Cryptopunks
|
|
83
123
|
|
84
124
|
## convert to "wrapped / immutable" kind-of struct
|
85
125
|
recs = recs.map do |rec|
|
86
|
-
id
|
87
|
-
name
|
88
|
-
gender
|
89
|
-
|
126
|
+
id = rec['id'].to_i( 10 )
|
127
|
+
name = normalize_name( rec['name'] )
|
128
|
+
gender = normalize_gender( rec['gender'] )
|
129
|
+
size = normalize_size( rec['size'] )
|
130
|
+
type = rec['type']
|
131
|
+
|
132
|
+
more_names = (rec['more_names'] || '').split( '|' )
|
133
|
+
more_names = more_names.map {|str| normalize_name( str ) }
|
90
134
|
|
91
135
|
Metadata::Sprite.new(
|
92
|
-
id:
|
93
|
-
name:
|
94
|
-
type:
|
95
|
-
gender:
|
136
|
+
id: id,
|
137
|
+
name: name,
|
138
|
+
type: type,
|
139
|
+
gender: gender,
|
140
|
+
size: size,
|
141
|
+
more_names: more_names )
|
96
142
|
end
|
97
143
|
recs
|
98
144
|
end # method build_recs
|
@@ -116,27 +162,80 @@ module Cryptopunks
|
|
116
162
|
alias_method :sheet, :spritesheet
|
117
163
|
|
118
164
|
|
165
|
+
def records() @recs; end
|
166
|
+
alias_method :meta, :records
|
119
167
|
|
120
168
|
|
121
169
|
|
122
|
-
|
170
|
+
|
171
|
+
def find_meta( q, gender: nil,
|
172
|
+
size: nil,
|
173
|
+
style: nil ) ## note: gender (m/f) required for attributes!!!
|
123
174
|
|
124
175
|
key = normalize_key( q ) ## normalize q(uery) string/symbol
|
125
|
-
key << "_(#{normalize_gender( gender )})" if gender
|
126
176
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
177
|
+
keys = [] ## note allow lookup by more than one keys
|
178
|
+
## e.g. if gender set try f/m first and than try unisex as fallback
|
179
|
+
if gender
|
180
|
+
gender = normalize_gender( gender )
|
181
|
+
## auto-fill size if not passed in
|
182
|
+
## for f(emale) => s(mall)
|
183
|
+
## m(ale) => l(arge)
|
184
|
+
size = if size.nil?
|
185
|
+
gender == 'f' ? 's' : 'l'
|
186
|
+
else
|
187
|
+
normalize_size( size )
|
188
|
+
end
|
189
|
+
|
190
|
+
###
|
191
|
+
# try (auto-add) style-specific version first (fallback to "regular" if not found)
|
192
|
+
if style
|
193
|
+
## for now only support natural series
|
194
|
+
style_key = if style.downcase.start_with?( 'natural' )
|
195
|
+
'natural'
|
196
|
+
else
|
197
|
+
puts "!! ERROR - unknown attribute style #{style}; sorry"
|
198
|
+
exit 1
|
199
|
+
end
|
200
|
+
|
201
|
+
keys << "#{key}#{style_key}_(#{gender}+#{size})"
|
202
|
+
## auto-add (u)niversal size as fallback
|
203
|
+
keys << "#{key}#{style_key}_(#{gender}+u)" if size == 's' || size == 'l'
|
204
|
+
## auto-add u(nisex) as fallback
|
205
|
+
keys << "#{key}#{style_key}_(u+#{size})" if gender == 'f' || gender == 'm'
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
keys << "#{key}_(#{gender}+#{size})"
|
210
|
+
## auto-add (u)niversal size as fallback
|
211
|
+
keys << "#{key}_(#{gender}+u)" if size == 's' || size == 'l'
|
212
|
+
## auto-add u(nisex) as fallback
|
213
|
+
keys << "#{key}_(u+#{size})" if gender == 'f' || gender == 'm'
|
131
214
|
else
|
132
|
-
|
215
|
+
keys << key
|
133
216
|
end
|
217
|
+
|
218
|
+
|
219
|
+
rec = nil
|
220
|
+
keys.each do |key|
|
221
|
+
rec = @attributes_by_name[ key ]
|
222
|
+
if rec
|
223
|
+
puts " lookup >#{key}< => #{rec.id}: #{rec.name} / #{rec.type} (#{rec.gender}+#{rec.size})"
|
224
|
+
# pp rec
|
225
|
+
break
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
if rec.nil?
|
230
|
+
puts "!! WARN - no lookup found for #{keys.size} key(s) >#{keys.inspect}<"
|
231
|
+
end
|
232
|
+
|
134
233
|
rec
|
135
234
|
end
|
136
235
|
|
137
236
|
|
138
|
-
def find( q, gender: nil ) ## gender (m/f) required for attributes!!!
|
139
|
-
rec = find_meta( q, gender: gender )
|
237
|
+
def find( q, gender: nil, size: nil, style: nil ) ## gender (m/f) required for attributes!!!
|
238
|
+
rec = find_meta( q, gender: gender, size: size, style: style )
|
140
239
|
|
141
240
|
## return image if record found
|
142
241
|
rec ? @sheet[ rec.id ] : nil
|
@@ -145,10 +244,11 @@ module Cryptopunks
|
|
145
244
|
|
146
245
|
|
147
246
|
|
148
|
-
def to_recs( *values )
|
247
|
+
def to_recs( *values, style: nil )
|
149
248
|
archetype_name = values[0]
|
150
249
|
|
151
250
|
### todo/fix: check for nil/not found!!!!
|
251
|
+
## todo/check/fix: assert meta record returned is archetype NOT attribute!!!!
|
152
252
|
archetype = find_meta( archetype_name )
|
153
253
|
if archetype.nil?
|
154
254
|
puts "!! ERROR - archetype >#{archetype}< not found; sorry"
|
@@ -160,11 +260,15 @@ module Cryptopunks
|
|
160
260
|
attribute_names = values[1..-1]
|
161
261
|
## note: attribute lookup requires gender from archetype!!!!
|
162
262
|
attribute_gender = archetype.gender
|
263
|
+
attribute_size = archetype.size
|
163
264
|
|
164
265
|
attribute_names.each do |attribute_name|
|
165
|
-
attribute = find_meta( attribute_name,
|
266
|
+
attribute = find_meta( attribute_name,
|
267
|
+
gender: attribute_gender,
|
268
|
+
size: attribute_size,
|
269
|
+
style: style )
|
166
270
|
if attribute.nil?
|
167
|
-
puts "!! ERROR - attribute >#{attribute_name}< for (#{attribute_gender}) not found; sorry"
|
271
|
+
puts "!! ERROR - attribute >#{attribute_name}< for (#{attribute_gender}+#{attribute_size}) not found; sorry"
|
168
272
|
exit 1
|
169
273
|
end
|
170
274
|
recs << attribute
|
@@ -176,12 +280,13 @@ module Cryptopunks
|
|
176
280
|
|
177
281
|
|
178
282
|
|
179
|
-
def generate_image( *values,
|
283
|
+
def generate_image( *values, style: nil,
|
284
|
+
background: nil )
|
180
285
|
|
181
286
|
ids = if values[0].is_a?( Integer ) ## assume integer number (indexes)
|
182
287
|
values
|
183
288
|
else ## assume strings (names)
|
184
|
-
to_recs( *values ).map { |rec| rec.id }
|
289
|
+
to_recs( *values, style: style ).map { |rec| rec.id }
|
185
290
|
end
|
186
291
|
|
187
292
|
|
data/lib/cryptopunks/image.rb
CHANGED
@@ -1,45 +1,6 @@
|
|
1
1
|
module Cryptopunks
|
2
2
|
|
3
3
|
|
4
|
-
class Design ## todo/fix - move to its own file!!!
|
5
|
-
|
6
|
-
end # class Design
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
##############
|
11
|
-
## todo/check:
|
12
|
-
## find a better way to (auto?) include more designs?
|
13
|
-
class DesignSeries ## find a better name for class (just use Series?) - why? why not?
|
14
|
-
def self.build( dir )
|
15
|
-
data = {}
|
16
|
-
paths = Dir.glob( "#{dir}/**.txt" )
|
17
|
-
paths.each do |path|
|
18
|
-
basename = File.basename( path, File.extname( path ) )
|
19
|
-
text = File.open( path, 'r:utf-8' ) { |f| f.read }
|
20
|
-
## todo/check: auto-parse "ahead of time" here
|
21
|
-
## or keep "raw" text - why? why not?
|
22
|
-
data[ basename ] = text
|
23
|
-
end
|
24
|
-
data
|
25
|
-
end
|
26
|
-
|
27
|
-
def initialize( dir )
|
28
|
-
@dir = dir # e.g. "#{Cryptopunks.root}/config/more"
|
29
|
-
end
|
30
|
-
|
31
|
-
def data
|
32
|
-
## note: lazy load / build on first demand only
|
33
|
-
@data ||= self.class.build( @dir )
|
34
|
-
end
|
35
|
-
|
36
|
-
def [](key) data[ key ]; end
|
37
|
-
def size() data.size; end
|
38
|
-
def keys() data.keys; end
|
39
|
-
def to_h() data; end ## todo/check: use to_hash() - why? why not?
|
40
|
-
end # class DesignSeries
|
41
|
-
|
42
|
-
|
43
4
|
|
44
5
|
class Image
|
45
6
|
|
@@ -49,65 +10,16 @@ def self.read( path ) ## convenience helper
|
|
49
10
|
end
|
50
11
|
|
51
12
|
|
13
|
+
|
14
|
+
### keep design & colors keyword args in c'tor here
|
15
|
+
## or use parse() like in pixelart - why? why not?
|
16
|
+
|
52
17
|
def initialize( initial=nil, design: nil,
|
53
18
|
colors: nil )
|
54
19
|
if initial
|
55
20
|
## pass image through as-is
|
56
21
|
img = initial
|
57
22
|
else
|
58
|
-
|
59
|
-
## todo/fix:
|
60
|
-
## move design code into design class!!!
|
61
|
-
## for now assume design is a string
|
62
|
-
## split into parts
|
63
|
-
## original/alien-male or original@alien-male
|
64
|
-
## more/alien-female or more@alien-female
|
65
|
-
## original/human-male+darker or original@human-male!darker ????
|
66
|
-
## human-male!darker ?????
|
67
|
-
## keep @ as separator too - why? why not?
|
68
|
-
parts = design.split( %r{[@/]} )
|
69
|
-
parts.unshift( '*' ) if parts.size == 1 ## assume "all-in-one" series (use * as name/id/placeholder)
|
70
|
-
|
71
|
-
series_key = parts[0]
|
72
|
-
design_composite = parts[1]
|
73
|
-
|
74
|
-
## todo/check - find a way for unambigious (color) variant key
|
75
|
-
## use unique char e.g. +*!# or such
|
76
|
-
more_parts = design_composite.split( %r{[!+]} )
|
77
|
-
design_key = more_parts[0]
|
78
|
-
variant_key = more_parts[1] ## color variant for now (for humans) e.g. lighter/light/dark/darker
|
79
|
-
|
80
|
-
series = if ['*','**','_','__'].include?( series_key )
|
81
|
-
DESIGNS ## use all-series-in-one collection
|
82
|
-
else
|
83
|
-
case series_key
|
84
|
-
when 'original' then DESIGNS_ORIGINAL
|
85
|
-
when 'more' then DESIGNS_MORE
|
86
|
-
else raise ArgumentError, "unknown design series >#{series_key}<; sorry"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
design = series[ design_key ]
|
91
|
-
raise ArgumentError, "unknow design >#{design_key}< in series >#{series_key}<; sorry" if design.nil?
|
92
|
-
|
93
|
-
if colors.nil? ## try to auto-fill in colors
|
94
|
-
## note: (auto-)remove _male,_female qualifier if exist
|
95
|
-
colors_key = design_key.sub( '-male', '' ).sub( '-female', '' )
|
96
|
-
colors = COLORS[ colors_key ]
|
97
|
-
|
98
|
-
## allow / support color scheme variants (e.g. lighter/light/dark/darker) etc.
|
99
|
-
if colors.is_a?(Hash)
|
100
|
-
if variant_key
|
101
|
-
colors = colors[ variant_key ]
|
102
|
-
raise ArgumentError, "no colors defined for variant >#{variant_key}< for design >#{design_key}< in series >#{series_key}<; sorry" if colors.nil?
|
103
|
-
else ## note: use (fallback to) first color scheme if no variant key present
|
104
|
-
colors = colors[ colors.keys[0] ]
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
raise ArgumentError, "no (default) colors defined for design >#{design_key}< in series >#{series_key}<; sorry" if colors.nil?
|
109
|
-
end
|
110
|
-
|
111
23
|
## note: unwrap inner image before passing on to super c'tor
|
112
24
|
img = Pixelart::Image.parse( design, colors: colors ).image
|
113
25
|
end
|