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
@@ -0,0 +1,381 @@
|
|
1
|
+
|
2
|
+
module Cryptopunks
|
3
|
+
|
4
|
+
|
5
|
+
class Tool
|
6
|
+
def run( args )
|
7
|
+
Toolii.run( args )
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class Opts
|
14
|
+
def merge_gli_options!( options = {} )
|
15
|
+
# puts " update options:"
|
16
|
+
# puts options.inspect
|
17
|
+
|
18
|
+
@file = options[:file] if options[:file]
|
19
|
+
@outdir = options[:dir] if options[:dir]
|
20
|
+
|
21
|
+
@zoom = options[:zoom] if options[:zoom]
|
22
|
+
@offset = options[:offset] if options[:offset]
|
23
|
+
|
24
|
+
@seed = options[:seed] if options[:seed]
|
25
|
+
|
26
|
+
@verbose = true if options[:verbose] == true
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def verbose=(boolean) # add: alias for debug ??
|
31
|
+
@verbose = boolean
|
32
|
+
end
|
33
|
+
|
34
|
+
def verbose?
|
35
|
+
return false if @verbose.nil? # default verbose/debug flag is false
|
36
|
+
@verbose == true
|
37
|
+
end
|
38
|
+
|
39
|
+
def file() @file || './punks.png'; end
|
40
|
+
def file?() @file; end ## note: let's you check if file is set (or "untouched")
|
41
|
+
|
42
|
+
def zoom() @zoom || 1; end
|
43
|
+
def zoom?() @zoom; end
|
44
|
+
|
45
|
+
def offset() @offset || 0; end
|
46
|
+
def offset?() @offset; end
|
47
|
+
|
48
|
+
def outdir() @outdir || '.'; end
|
49
|
+
def outdir?() @outdir; end
|
50
|
+
|
51
|
+
### use a standard (default) seed - why? why not?
|
52
|
+
def seed() @seed || 4142; end
|
53
|
+
def seed?() @seed; end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
end # class Opts
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
## note: use gli "dsl" inside a class / namespace
|
62
|
+
class Toolii
|
63
|
+
extend GLI::App
|
64
|
+
|
65
|
+
opts = Opts.new
|
66
|
+
|
67
|
+
|
68
|
+
program_desc 'punk (or cryptopunk) command line tool'
|
69
|
+
|
70
|
+
version Cryptopunks::VERSION
|
71
|
+
|
72
|
+
|
73
|
+
desc "Zoom factor x2, x4, x8, etc."
|
74
|
+
arg_name 'ZOOM'
|
75
|
+
default_value opts.zoom
|
76
|
+
flag [:z, :zoom], type: Integer
|
77
|
+
|
78
|
+
desc "Start counting at offset"
|
79
|
+
arg_name 'NUM'
|
80
|
+
default_value opts.offset
|
81
|
+
flag [:offset], type: Integer
|
82
|
+
|
83
|
+
|
84
|
+
desc "Seed for random number generation / shuffle"
|
85
|
+
arg_name 'NUM'
|
86
|
+
default_value opts.seed
|
87
|
+
flag [:seed], type: Integer
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
desc "Output directory"
|
92
|
+
arg_name 'DIR'
|
93
|
+
default_value opts.outdir
|
94
|
+
flag [:d, :dir,
|
95
|
+
:o, :out, :outdir], type: String
|
96
|
+
|
97
|
+
|
98
|
+
### todo/check: move option to -t/--tile command only - why? why not?
|
99
|
+
desc "True Official Genuine CryptoPunks™ all-in-one composite image"
|
100
|
+
arg_name 'FILE'
|
101
|
+
default_value opts.file
|
102
|
+
flag [:f, :file], type: String
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
### global option (required)
|
107
|
+
## todo: add check that path is valid?? possible?
|
108
|
+
desc '(Debug) Show debug messages'
|
109
|
+
switch [:verbose], negatable: false ## todo: use -w for short form? check ruby interpreter if in use too?
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
desc "Flip (vertically) all punk characters in all-in-one punk series composite (#{opts.file})"
|
114
|
+
command [:f, :flip] do |c|
|
115
|
+
c.action do |g,o,args|
|
116
|
+
puts "==> reading >#{opts.file}<..."
|
117
|
+
punks = ImageComposite.read( opts.file )
|
118
|
+
|
119
|
+
## note: for now always assume 24x24
|
120
|
+
cols = punks.width / 24
|
121
|
+
rows = punks.height / 24
|
122
|
+
tile_width = 24
|
123
|
+
tile_height = 24
|
124
|
+
|
125
|
+
phunks = ImageComposite.new( cols, rows,
|
126
|
+
width: tile_width,
|
127
|
+
height: tile_height )
|
128
|
+
|
129
|
+
punks.each do |punk|
|
130
|
+
phunks << punk.flip_vertically
|
131
|
+
end
|
132
|
+
|
133
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
134
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
135
|
+
|
136
|
+
## note: allways assume .png extension for now
|
137
|
+
basename = File.basename( opts.file, File.extname( opts.file ) )
|
138
|
+
path = "#{opts.outdir}/#{basename}-flipped.png"
|
139
|
+
puts "==> saving phunks flipped one-by-one by hand to >#{path}<..."
|
140
|
+
|
141
|
+
phunks.save( path )
|
142
|
+
puts 'Done.'
|
143
|
+
end # action
|
144
|
+
end # command flip
|
145
|
+
|
146
|
+
|
147
|
+
desc "Shuffle all punk characters (randomly) in all-in-one punk series composite (#{opts.file})"
|
148
|
+
command [:s, :shuffle] do |c|
|
149
|
+
c.action do |g,o,args|
|
150
|
+
puts "==> reading >#{opts.file}<..."
|
151
|
+
punks = ImageComposite.read( opts.file )
|
152
|
+
|
153
|
+
## note: for now always assume 24x24
|
154
|
+
cols = punks.width / 24
|
155
|
+
rows = punks.height / 24
|
156
|
+
tile_width = 24
|
157
|
+
tile_height = 24
|
158
|
+
|
159
|
+
phunks = ImageComposite.new( cols, rows,
|
160
|
+
width: tile_width,
|
161
|
+
height: tile_height )
|
162
|
+
|
163
|
+
tiles = cols * rows
|
164
|
+
indexes = (0..tiles-1).to_a
|
165
|
+
srand( opts.seed ) ## note: for new reset **global** random seed and use (builtin) Array#shuffle
|
166
|
+
puts " using random generation number seed >#{opts.seed}< for shuffle"
|
167
|
+
indexes = indexes.shuffle
|
168
|
+
|
169
|
+
###
|
170
|
+
# seed 4142 ends in [..., 7566, 828, 8987, 9777]
|
171
|
+
# 333 ends in [..., 6067, 9635, 973, 8172]
|
172
|
+
|
173
|
+
|
174
|
+
indexes.each_with_index do |old_index,new_index|
|
175
|
+
puts " ##{old_index} now ##{new_index}"
|
176
|
+
phunks << punks[old_index]
|
177
|
+
end
|
178
|
+
|
179
|
+
puts " all #{tiles} old index numbers (zero-based) for reference using seed #{opts.seed}:"
|
180
|
+
puts indexes.inspect
|
181
|
+
|
182
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
183
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
184
|
+
|
185
|
+
## note: allways assume .png extension for now
|
186
|
+
basename = File.basename( opts.file, File.extname( opts.file ) )
|
187
|
+
path = "#{opts.outdir}/#{basename}-#{opts.seed}.png"
|
188
|
+
puts "==> saving p(h)unks shuffled one-by-one by hand to >#{path}<..."
|
189
|
+
|
190
|
+
phunks.save( path )
|
191
|
+
puts 'Done.'
|
192
|
+
end # action
|
193
|
+
end # command shuffle
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
desc "Get punk characters via image tiles from all-in-one punk series composite (#{opts.file}) - for IDs use 0 to 9999"
|
202
|
+
command [:t, :tile] do |c|
|
203
|
+
c.action do |g,o,args|
|
204
|
+
|
205
|
+
# puts "opts:"
|
206
|
+
# puts opts.inspect
|
207
|
+
|
208
|
+
puts "==> reading >#{opts.file}<..."
|
209
|
+
punks = ImageComposite.read( opts.file )
|
210
|
+
|
211
|
+
|
212
|
+
puts " setting zoom to #{opts.zoom}x" if opts.zoom != 1
|
213
|
+
|
214
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
215
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
216
|
+
|
217
|
+
args.each_with_index do |arg,index|
|
218
|
+
punk_index = arg.to_i( 10 ) ## assume base 10 decimal
|
219
|
+
|
220
|
+
punk = punks[ punk_index ]
|
221
|
+
|
222
|
+
punk_name = "punk-" + "%04d" % (punk_index + opts.offset)
|
223
|
+
|
224
|
+
## if zoom - add x2,x4 or such
|
225
|
+
if opts.zoom != 1
|
226
|
+
punk = punk.zoom( opts.zoom )
|
227
|
+
punk_name << "@#{opts.zoom}x"
|
228
|
+
end
|
229
|
+
|
230
|
+
path = "#{opts.outdir}/#{punk_name}.png"
|
231
|
+
puts "==> (#{index+1}/#{args.size}) saving punk ##{punk_index+opts.offset} to >#{path}<..."
|
232
|
+
|
233
|
+
punk.save( path )
|
234
|
+
end
|
235
|
+
puts 'Done.'
|
236
|
+
end # action
|
237
|
+
end # command tile
|
238
|
+
|
239
|
+
|
240
|
+
|
241
|
+
desc 'Generate punk characters from text attributes (from scratch / zero) via builtin punk spritesheet'
|
242
|
+
command [:g, :gen, :generate] do |c|
|
243
|
+
c.action do |g,o,args|
|
244
|
+
|
245
|
+
puts "==> generating >#{args.join( ' + ' )}<..."
|
246
|
+
punk = Image.generate( *args )
|
247
|
+
|
248
|
+
puts " setting zoom to #{opts.zoom}x" if opts.zoom != 1
|
249
|
+
|
250
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
251
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
252
|
+
|
253
|
+
punk_index = 0 ## assume base 10 decimal
|
254
|
+
punk_name = "punk-" + "%04d" % (punk_index + opts.offset)
|
255
|
+
|
256
|
+
## if zoom - add x2,x4 or such
|
257
|
+
if opts.zoom != 1
|
258
|
+
punk = punk.zoom( opts.zoom )
|
259
|
+
punk_name << "@#{opts.zoom}x"
|
260
|
+
end
|
261
|
+
|
262
|
+
path = "#{opts.outdir}/#{punk_name}.png"
|
263
|
+
puts "==> saving punk ##{punk_index+opts.offset} to >#{path}<..."
|
264
|
+
|
265
|
+
punk.save( path )
|
266
|
+
puts 'Done.'
|
267
|
+
end # action
|
268
|
+
end # command generate
|
269
|
+
|
270
|
+
|
271
|
+
desc 'Query (builtin off-chain) punk contract for punk text attributes by IDs - use 0 to 9999'
|
272
|
+
command [:q, :query] do |c|
|
273
|
+
c.action do |g,o,args|
|
274
|
+
|
275
|
+
# puts "opts:"
|
276
|
+
# puts opts.inspect
|
277
|
+
|
278
|
+
args.each_with_index do |arg,index|
|
279
|
+
punk_index = arg.to_i( 10 ) ## assume base 10 decimal
|
280
|
+
|
281
|
+
puts "==> (#{index+1}/#{args.size}) punk ##{punk_index}..."
|
282
|
+
|
283
|
+
attribute_names = CryptopunksData.punk_attributes( punk_index )
|
284
|
+
## downcase name and change spaces to underscore
|
285
|
+
attribute_names = attribute_names.map do |name|
|
286
|
+
name.downcase.gsub( ' ', '_' )
|
287
|
+
end
|
288
|
+
|
289
|
+
print " "
|
290
|
+
print attribute_names.join( ' ' )
|
291
|
+
print "\n"
|
292
|
+
end
|
293
|
+
puts 'Done.'
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
|
299
|
+
desc 'List all punk archetype and attribute names from builtin punk spritesheet'
|
300
|
+
command [:l, :ls, :list] do |c|
|
301
|
+
c.action do |g,o,args|
|
302
|
+
|
303
|
+
generator = Cryptopunks.generator
|
304
|
+
|
305
|
+
puts "==> Archetypes"
|
306
|
+
generator.meta.each do |rec|
|
307
|
+
next unless rec.archetype?
|
308
|
+
|
309
|
+
print " "
|
310
|
+
print "%-30s" % "#{rec.name} / (#{rec.gender})"
|
311
|
+
print " - #{rec.type}"
|
312
|
+
print "\n"
|
313
|
+
end
|
314
|
+
|
315
|
+
puts ""
|
316
|
+
puts "==> Attributes"
|
317
|
+
generator.meta.each do |rec|
|
318
|
+
next unless rec.attribute?
|
319
|
+
|
320
|
+
print " "
|
321
|
+
print "%-30s" % "#{rec.name} / (#{rec.gender})"
|
322
|
+
print " - #{rec.type}"
|
323
|
+
print "\n"
|
324
|
+
end
|
325
|
+
|
326
|
+
puts ""
|
327
|
+
puts " See github.com/cryptopunksnotdead/punks.spritesheet for more."
|
328
|
+
puts ""
|
329
|
+
|
330
|
+
puts 'Done.'
|
331
|
+
end # action
|
332
|
+
end # command list
|
333
|
+
|
334
|
+
|
335
|
+
|
336
|
+
pre do |g,c,o,args|
|
337
|
+
opts.merge_gli_options!( g )
|
338
|
+
opts.merge_gli_options!( o )
|
339
|
+
|
340
|
+
if opts.verbose?
|
341
|
+
## LogUtils::Logger.root.level = :debug
|
342
|
+
end
|
343
|
+
|
344
|
+
## logger.debug "Executing #{c.name}"
|
345
|
+
true
|
346
|
+
end
|
347
|
+
|
348
|
+
post do |global,c,o,args|
|
349
|
+
## logger.debug "Executed #{c.name}"
|
350
|
+
true
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
on_error do |e|
|
355
|
+
|
356
|
+
if opts.verbose?
|
357
|
+
puts e.backtrace
|
358
|
+
end
|
359
|
+
|
360
|
+
if e.is_a?( SystemExit )
|
361
|
+
puts
|
362
|
+
puts "*** error: system exit with status code ( #{e.status} )"
|
363
|
+
exit( e.status ) ## try exit again to make sure error code gets passed along!!!
|
364
|
+
else
|
365
|
+
puts
|
366
|
+
puts "*** error: #{e.message}"
|
367
|
+
end
|
368
|
+
|
369
|
+
## note: was false # skip default error handling
|
370
|
+
|
371
|
+
## note: try true - false WILL SWALLOW exit codes and such
|
372
|
+
## - looks like it's still returning 0 (e.g. on unknown option or such)!!!!
|
373
|
+
true
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
### exit run(ARGV) ## note: use Toolii.run( ARGV ) outside of class
|
378
|
+
end # class Toolii
|
379
|
+
end # module Cryptopunks
|
380
|
+
|
381
|
+
|
data/lib/cryptopunks/version.rb
CHANGED
data/lib/cryptopunks.rb
CHANGED
@@ -7,6 +7,7 @@ require 'csvreader'
|
|
7
7
|
## extra stdlibs
|
8
8
|
require 'digest' ## move/add to pixelart upstream - why? why not?
|
9
9
|
require 'optparse'
|
10
|
+
require 'gli' ## used for command line tool
|
10
11
|
|
11
12
|
|
12
13
|
|
@@ -22,6 +23,15 @@ end
|
|
22
23
|
require 'cryptopunks/attributes'
|
23
24
|
require 'cryptopunks/structs'
|
24
25
|
require 'cryptopunks/composite'
|
26
|
+
## add old backwards compatible alias
|
27
|
+
|
28
|
+
module Cryptopunks
|
29
|
+
class Image
|
30
|
+
Composite = ImageComposite
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
25
35
|
require 'cryptopunks/dataset'
|
26
36
|
|
27
37
|
require 'cryptopunks/colors'
|
@@ -40,117 +50,137 @@ module Cryptopunks
|
|
40
50
|
end
|
41
51
|
|
42
52
|
class Image
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
class Tool
|
58
|
-
def run( args )
|
59
|
-
opts = { zoom: 1,
|
60
|
-
outdir: '.',
|
61
|
-
file: './punks.png',
|
62
|
-
offset: 0,
|
63
|
-
}
|
64
|
-
|
65
|
-
parser = OptionParser.new do |cmd|
|
66
|
-
cmd.banner = "Usage: punk (or cryptopunk) [options] IDs"
|
67
|
-
|
68
|
-
cmd.separator " Mint punk characters from composite (#{opts[:file]}) - for IDs use 0 to 9999"
|
69
|
-
cmd.separator ""
|
70
|
-
cmd.separator " Options:"
|
71
|
-
|
72
|
-
cmd.on("-z", "--zoom=ZOOM", "Zoom factor x2, x4, x8, etc. (default: #{opts[:zoom]})", Integer ) do |zoom|
|
73
|
-
opts[:zoom] = zoom
|
74
|
-
end
|
53
|
+
def self.generate( *values, style: nil )
|
54
|
+
|
55
|
+
##### add style option / hack - why? why not?
|
56
|
+
if style
|
57
|
+
values = if style.downcase.index( 'natural') && style.downcase.index( '2')
|
58
|
+
["#{values[0]} (N2)"] + values[1..-1]
|
59
|
+
elsif style.downcase[0] == 'n' ## starting with n - always assume natural(s)
|
60
|
+
## auto-add (N) for Natural to archetype
|
61
|
+
["#{values[0]} (N)"] + values[1..-1]
|
62
|
+
else
|
63
|
+
puts "!! ERROR - unknown punk style #{style}; sorry"
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
end
|
75
67
|
|
76
|
-
cmd.on("-d", "--dir=DIR", "Output directory (default: #{opts[:outdir]})", String ) do |outdir|
|
77
|
-
opts[:outdir] = outdir
|
78
|
-
end
|
79
68
|
|
80
|
-
|
81
|
-
|
82
|
-
|
69
|
+
###### hack for black&white
|
70
|
+
## auto-add b&w (black&white) to all attribute names e.g.
|
71
|
+
## Eyes => Eyes B&W
|
72
|
+
## Smile => Smile B&W
|
73
|
+
## ....
|
74
|
+
|
75
|
+
archetype_key = Generator.normalize_key( values[0] )
|
76
|
+
if archetype_key.end_with?( 'bw' ) || ## e.g. B&W
|
77
|
+
archetype_key.end_with?( '1bit') ## e.g. 1-Bit or 1Bit
|
78
|
+
|
79
|
+
values = [values[0]] + values[1..-1].map do |attribute|
|
80
|
+
attribute_key = Generator.normalize_key( attribute )
|
81
|
+
if ['wildhair'].include?( attribute_key ) ## pass through known b&w attributes by "default"
|
82
|
+
attribute
|
83
|
+
elsif attribute_key.index( "black" )
|
84
|
+
attribute ## pass through as-is
|
85
|
+
else
|
86
|
+
"#{attribute} B&W"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
pp values
|
91
|
+
end
|
83
92
|
|
84
|
-
cmd.on("--offset=NUM", "Start counting at offset (default: #{opts[:offset]})", Integer ) do |offset|
|
85
|
-
opts[:offset] = offset
|
86
|
-
end
|
87
93
|
|
88
|
-
|
89
|
-
|
90
|
-
|
94
|
+
# note: female mouth by default has "custom" colors (not black)
|
95
|
+
# for every 1/2/3/4 (human) skin tone and for zombie
|
96
|
+
# auto-"magically" add mapping
|
97
|
+
#
|
98
|
+
# todo/check/fix - add more "contraints"
|
99
|
+
# for mapping to only kick-in for "basic" versions
|
100
|
+
# and not "colored" e.g. golden and such - why? why not?
|
101
|
+
#
|
102
|
+
# move this mapping here to "post-lookup" processing
|
103
|
+
# to get/incl. more "meta" attribute info - why? why not?
|
104
|
+
if archetype_key.index( 'female1' ) ||
|
105
|
+
archetype_key.index( 'female2' ) ||
|
106
|
+
archetype_key.index( 'female3' ) ||
|
107
|
+
archetype_key.index( 'female4' ) ||
|
108
|
+
archetype_key.index( 'zombiefemale' )
|
109
|
+
|
110
|
+
values = [values[0]] + values[1..-1].map do |attribute|
|
111
|
+
attribute_key = Generator.normalize_key( attribute )
|
112
|
+
|
113
|
+
if attribute_key == 'smile' || attribute_key == 'frown'
|
114
|
+
attribute += if archetype_key.index( 'zombiefemale' ) then ' Zombie'
|
115
|
+
elsif archetype_key.index( 'female1' ) then ' 1'
|
116
|
+
elsif archetype_key.index( 'female2' ) then ' 2'
|
117
|
+
elsif archetype_key.index( 'female3' ) then ' 3'
|
118
|
+
elsif archetype_key.index( 'female4' ) then ' 4'
|
119
|
+
else
|
120
|
+
puts "!! ERROR - smile or frown (mouth expression) not supported for archetype:"
|
121
|
+
pp values
|
122
|
+
exit 1
|
123
|
+
end
|
124
|
+
end
|
125
|
+
attribute
|
91
126
|
end
|
92
127
|
end
|
93
128
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
puts "==> reading >#{opts[:file]}<..."
|
100
|
-
punks = Image::Composite.read( opts[:file] )
|
129
|
+
img = Cryptopunks.generator.generate( *values, style: style )
|
130
|
+
## note: unwrap inner image before passing on to c'tor (requires ChunkyPNG image for now)
|
131
|
+
new( img.image )
|
132
|
+
end # method Image.generate
|
101
133
|
|
134
|
+
end # class Image
|
102
135
|
|
103
|
-
puts " setting zoom to #{opts[:zoom]}x" if opts[:zoom] != 1
|
104
136
|
|
105
|
-
|
106
|
-
|
137
|
+
class Spritesheet
|
138
|
+
## note: for now class used for "namespace" only
|
139
|
+
def self.find_by( name:, gender: nil, size: nil ) ## return archetype/attribute image by name
|
140
|
+
# note: pass along name as q (query string)
|
141
|
+
Cryptopunks.generator.find( name,
|
142
|
+
gender: gender,
|
143
|
+
size: size )
|
144
|
+
end
|
145
|
+
end # class Spritesheet
|
146
|
+
## add convenience (alternate spelling) alias - why? why not?
|
147
|
+
SpriteSheet = Spritesheet
|
148
|
+
Sheet = Spritesheet
|
149
|
+
Sprite = Spritesheet
|
150
|
+
end # module Cryptopunks
|
107
151
|
|
108
|
-
args.each_with_index do |arg,index|
|
109
|
-
punk_index = arg.to_i
|
110
152
|
|
111
|
-
punk = punks[ punk_index ]
|
112
153
|
|
113
|
-
|
154
|
+
####
|
155
|
+
# add CryptoPunksData (off-chain) contract
|
156
|
+
require 'cryptopunks/contract/punksdata-assets'
|
157
|
+
require 'cryptopunks/contract/punksdata-meta'
|
158
|
+
require 'cryptopunks/contract/punksdata-contract'
|
159
|
+
CryptoPunksData = CryptopunksData
|
160
|
+
PunksData = CryptopunksData
|
114
161
|
|
115
|
-
## if zoom - add x2,x4 or such
|
116
|
-
if opts[:zoom] != 1
|
117
|
-
punk = punk.zoom( opts[:zoom] )
|
118
|
-
punk_name << "x#{opts[:zoom]}"
|
119
|
-
end
|
120
162
|
|
121
|
-
path = "#{opts[:outdir]}/#{punk_name}.png"
|
122
|
-
puts "==> (#{index+1}/#{args.size}) minting punk ##{punk_index+opts[:offset]}; writing to >#{path}<..."
|
123
163
|
|
124
|
-
punk.save( path )
|
125
|
-
end
|
126
164
|
|
127
|
-
puts "done"
|
128
|
-
end ## method run
|
129
|
-
end # class Tool
|
130
165
|
|
166
|
+
require 'cryptopunks/tool'
|
131
167
|
|
132
|
-
|
133
|
-
|
134
|
-
end
|
168
|
+
### add tool convenience helper
|
169
|
+
module Cryptopunks
|
170
|
+
def self.main( args=ARGV ) Tool.new.run( args ); end
|
135
171
|
end ## module Cryptopunks
|
136
172
|
|
137
173
|
|
138
174
|
|
139
175
|
|
140
|
-
### add more built-in (load on demand) design series / collections
|
141
|
-
DESIGNS_ORIGINAL = Cryptopunks::DesignSeries.new( "#{Cryptopunks.root}/config/original" )
|
142
|
-
DESIGNS_MORE = Cryptopunks::DesignSeries.new( "#{Cryptopunks.root}/config/more" )
|
143
|
-
|
144
|
-
## all designs in one collections
|
145
|
-
DESIGNS = {}.merge( DESIGNS_ORIGINAL.to_h,
|
146
|
-
DESIGNS_MORE.to_h )
|
147
|
-
|
148
|
-
|
149
|
-
|
150
176
|
|
151
177
|
### add some convenience shortcuts
|
152
178
|
CryptoPunks = Cryptopunks
|
153
179
|
Punks = Cryptopunks
|
180
|
+
## add singular too -why? why not?
|
181
|
+
Cryptopunk = Cryptopunks
|
182
|
+
CryptoPunk = Cryptopunks
|
183
|
+
Punk = Cryptopunks
|
154
184
|
|
155
185
|
|
156
186
|
|