cryptopunks 1.2.1 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +7 -20
- data/README.md +309 -34
- data/Rakefile +1 -0
- data/config/spritesheet.csv +402 -0
- data/config/spritesheet.png +0 -0
- data/lib/cryptopunks/composite.rb +9 -9
- 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 +223 -0
- data/lib/cryptopunks/image.rb +5 -93
- data/lib/cryptopunks/tool.rb +275 -0
- data/lib/cryptopunks/version.rb +2 -2
- data/lib/cryptopunks.rb +50 -81
- metadata +23 -42
- 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,223 @@
|
|
1
|
+
|
2
|
+
module Cryptopunks
|
3
|
+
|
4
|
+
class Metadata
|
5
|
+
### todo/fix:
|
6
|
+
## move into Punks::Metadata or such
|
7
|
+
class Sprite
|
8
|
+
attr_reader :id, :name, :type, :gender, :more_names
|
9
|
+
|
10
|
+
|
11
|
+
def initialize( id:,
|
12
|
+
name:,
|
13
|
+
type:,
|
14
|
+
gender:,
|
15
|
+
more_names: [] )
|
16
|
+
@id = id # zero-based index eg. 0,1,2,3, etc.
|
17
|
+
@name = name
|
18
|
+
@type = type
|
19
|
+
@gender = gender
|
20
|
+
@more_names = more_names
|
21
|
+
end
|
22
|
+
|
23
|
+
## todo/check - find better names for type attribute/archetypes?
|
24
|
+
## use (alternate name/alias) base or face for archetypes? any others?
|
25
|
+
def attribute?() @type.downcase.start_with?( 'attribute' ); end
|
26
|
+
def archetype?() @type.downcase.start_with?( 'archetype' ); end
|
27
|
+
end # class Metadata::Sprite
|
28
|
+
end # class Metadata
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
class Generator
|
34
|
+
|
35
|
+
######
|
36
|
+
# static helpers - (turn into "true" static self.class methods - why? why not?)
|
37
|
+
#
|
38
|
+
def normalize_key( str )
|
39
|
+
str.downcase.gsub(/[ ()°_-]/, '').strip
|
40
|
+
end
|
41
|
+
|
42
|
+
def normalize_gender( str )
|
43
|
+
## e.g. Female => f
|
44
|
+
## F => f
|
45
|
+
## always return f or m
|
46
|
+
str.downcase[0]
|
47
|
+
end
|
48
|
+
|
49
|
+
def normalize_name( str )
|
50
|
+
## normalize spaces in more names
|
51
|
+
str.strip.gsub( /[ ]{2,}/, ' ' )
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
def build_attributes_by_name( recs )
|
57
|
+
h = {}
|
58
|
+
recs.each_with_index do |rec|
|
59
|
+
names = [rec.name] + rec.more_names
|
60
|
+
names.each do |name|
|
61
|
+
|
62
|
+
key = normalize_key( name )
|
63
|
+
key << "_(#{rec.gender})" if rec.attribute?
|
64
|
+
|
65
|
+
if h[ key ]
|
66
|
+
puts "!!! ERROR - attribute name is not unique:"
|
67
|
+
pp rec
|
68
|
+
puts "duplicate:"
|
69
|
+
pp h[key]
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
h[ key ] = rec
|
73
|
+
end
|
74
|
+
end
|
75
|
+
## pp h
|
76
|
+
h
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def build_recs( recs ) ## build and normalize (meta data) records
|
81
|
+
|
82
|
+
## sort by id
|
83
|
+
recs = recs.sort do |l,r|
|
84
|
+
l['id'].to_i( 10 ) <=> r['id'].to_i( 10 ) # use base10 (decimal)
|
85
|
+
end
|
86
|
+
|
87
|
+
## assert all recs are in order by id (0 to size)
|
88
|
+
recs.each_with_index do |rec, exp_id|
|
89
|
+
id = rec['id'].to_i(10)
|
90
|
+
if id != exp_id
|
91
|
+
puts "!! ERROR - meta data record ids out-of-order - expected id #{exp_id}; got #{id}"
|
92
|
+
exit 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
## convert to "wrapped / immutable" kind-of struct
|
97
|
+
recs = recs.map do |rec|
|
98
|
+
id = rec['id'].to_i( 10 )
|
99
|
+
name = normalize_name( rec['name'] )
|
100
|
+
gender = normalize_gender( rec['gender'] )
|
101
|
+
type = rec['type']
|
102
|
+
|
103
|
+
more_names = (rec['more_names'] || '').split( '|' )
|
104
|
+
more_names = more_names.map {|str| normalize_name( str ) }
|
105
|
+
|
106
|
+
Metadata::Sprite.new(
|
107
|
+
id: id,
|
108
|
+
name: name,
|
109
|
+
type: type,
|
110
|
+
gender: gender,
|
111
|
+
more_names: more_names )
|
112
|
+
end
|
113
|
+
recs
|
114
|
+
end # method build_recs
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
def initialize( image_path="./spritesheet.png",
|
120
|
+
meta_path="./spritesheet.csv" )
|
121
|
+
@sheet = Pixelart::ImageComposite.read( image_path )
|
122
|
+
recs = CsvHash.read( meta_path )
|
123
|
+
|
124
|
+
@recs = build_recs( recs )
|
125
|
+
|
126
|
+
## lookup by "case/space-insensitive" name / key
|
127
|
+
@attributes_by_name = build_attributes_by_name( @recs )
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def spritesheet() @sheet; end
|
132
|
+
alias_method :sheet, :spritesheet
|
133
|
+
|
134
|
+
|
135
|
+
def records() @recs; end
|
136
|
+
alias_method :meta, :records
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
def find_meta( q, gender: nil ) ## gender (m/f) required for attributes!!!
|
142
|
+
|
143
|
+
key = normalize_key( q ) ## normalize q(uery) string/symbol
|
144
|
+
key << "_(#{normalize_gender( gender )})" if gender
|
145
|
+
|
146
|
+
rec = @attributes_by_name[ key ]
|
147
|
+
if rec
|
148
|
+
puts " lookup >#{key}< => #{rec.id}: #{rec.name} / #{rec.type} (#{rec.gender})"
|
149
|
+
# pp rec
|
150
|
+
else
|
151
|
+
puts "!! WARN - no lookup found for key >#{key}<"
|
152
|
+
end
|
153
|
+
rec
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def find( q, gender: nil ) ## gender (m/f) required for attributes!!!
|
158
|
+
rec = find_meta( q, gender: gender )
|
159
|
+
|
160
|
+
## return image if record found
|
161
|
+
rec ? @sheet[ rec.id ] : nil
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
def to_recs( *values )
|
168
|
+
archetype_name = values[0]
|
169
|
+
|
170
|
+
### todo/fix: check for nil/not found!!!!
|
171
|
+
archetype = find_meta( archetype_name )
|
172
|
+
if archetype.nil?
|
173
|
+
puts "!! ERROR - archetype >#{archetype}< not found; sorry"
|
174
|
+
exit 1
|
175
|
+
end
|
176
|
+
|
177
|
+
recs = [archetype]
|
178
|
+
|
179
|
+
attribute_names = values[1..-1]
|
180
|
+
## note: attribute lookup requires gender from archetype!!!!
|
181
|
+
attribute_gender = archetype.gender
|
182
|
+
|
183
|
+
attribute_names.each do |attribute_name|
|
184
|
+
attribute = find_meta( attribute_name, gender: attribute_gender )
|
185
|
+
if attribute.nil?
|
186
|
+
puts "!! ERROR - attribute >#{attribute_name}< for (#{attribute_gender}) not found; sorry"
|
187
|
+
exit 1
|
188
|
+
end
|
189
|
+
recs << attribute
|
190
|
+
end
|
191
|
+
|
192
|
+
recs
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
def generate_image( *values, background: nil )
|
199
|
+
|
200
|
+
ids = if values[0].is_a?( Integer ) ## assume integer number (indexes)
|
201
|
+
values
|
202
|
+
else ## assume strings (names)
|
203
|
+
to_recs( *values ).map { |rec| rec.id }
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
punk = Pixelart::Image.new( 24, 24 )
|
208
|
+
|
209
|
+
if background ## for now assume background is (simply) color
|
210
|
+
punk.compose!( Pixelart::Image.new( 24, 24, background ) )
|
211
|
+
end
|
212
|
+
|
213
|
+
ids.each do |id|
|
214
|
+
punk.compose!( @sheet[ id ] )
|
215
|
+
end
|
216
|
+
|
217
|
+
punk
|
218
|
+
end
|
219
|
+
alias_method :generate, :generate_image
|
220
|
+
|
221
|
+
end # class Generator
|
222
|
+
|
223
|
+
end # module Cryptopunks
|
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
|
-
img =
|
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
|
@@ -0,0 +1,275 @@
|
|
1
|
+
module Cryptopunks
|
2
|
+
|
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
|
+
@verbose = true if options[:verbose] == true
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def verbose=(boolean) # add: alias for debug ??
|
29
|
+
@verbose = boolean
|
30
|
+
end
|
31
|
+
|
32
|
+
def verbose?
|
33
|
+
return false if @verbose.nil? # default verbose/debug flag is false
|
34
|
+
@verbose == true
|
35
|
+
end
|
36
|
+
|
37
|
+
def file() @file || './punks.png'; end
|
38
|
+
def file?() @file; end ## note: let's you check if file is set (or "untouched")
|
39
|
+
|
40
|
+
def zoom() @zoom || 1; end
|
41
|
+
def zoom?() @zoom; end
|
42
|
+
|
43
|
+
def offset() @offset || 0; end
|
44
|
+
def offset?() @offset; end
|
45
|
+
|
46
|
+
def outdir() @outdir || '.'; end
|
47
|
+
def outdir?() @outdir; end
|
48
|
+
end # class Opts
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
## note: use gli "dsl" inside a class / namespace
|
53
|
+
class Toolii
|
54
|
+
extend GLI::App
|
55
|
+
|
56
|
+
opts = Opts.new
|
57
|
+
|
58
|
+
|
59
|
+
program_desc 'punk (or cryptopunk) command line tool'
|
60
|
+
|
61
|
+
version Cryptopunks::VERSION
|
62
|
+
|
63
|
+
|
64
|
+
desc "Zoom factor x2, x4, x8, etc."
|
65
|
+
arg_name 'ZOOM'
|
66
|
+
default_value opts.zoom
|
67
|
+
flag [:z, :zoom], type: Integer
|
68
|
+
|
69
|
+
desc "Start counting at offset"
|
70
|
+
arg_name 'NUM'
|
71
|
+
default_value opts.offset
|
72
|
+
flag [:offset], type: Integer
|
73
|
+
|
74
|
+
desc "Output directory"
|
75
|
+
arg_name 'DIR'
|
76
|
+
default_value opts.outdir
|
77
|
+
flag [:d, :dir,
|
78
|
+
:o, :out, :outdir], type: String
|
79
|
+
|
80
|
+
### todo/check: move option to -t/--tile command only - why? why not?
|
81
|
+
desc "True Official Genuine CryptoPunks™ all-in-one composite image"
|
82
|
+
arg_name 'FILE'
|
83
|
+
default_value opts.file
|
84
|
+
flag [:f, :file], type: String
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
### global option (required)
|
89
|
+
## todo: add check that path is valid?? possible?
|
90
|
+
desc '(Debug) Show debug messages'
|
91
|
+
switch [:verbose], negatable: false ## todo: use -w for short form? check ruby interpreter if in use too?
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
desc "Get punk characters via image tiles from all-in-one punk series composite (#{opts.file}) - for IDs use 0 to 9999"
|
96
|
+
command [:t, :tile] do |c|
|
97
|
+
c.action do |g,o,args|
|
98
|
+
|
99
|
+
# puts "opts:"
|
100
|
+
# puts opts.inspect
|
101
|
+
|
102
|
+
puts "==> reading >#{opts.file}<..."
|
103
|
+
punks = ImageComposite.read( opts.file )
|
104
|
+
|
105
|
+
|
106
|
+
puts " setting zoom to #{opts.zoom}x" if opts.zoom != 1
|
107
|
+
|
108
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
109
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
110
|
+
|
111
|
+
args.each_with_index do |arg,index|
|
112
|
+
punk_index = arg.to_i( 10 ) ## assume base 10 decimal
|
113
|
+
|
114
|
+
punk = punks[ punk_index ]
|
115
|
+
|
116
|
+
punk_name = "punk-" + "%04d" % (punk_index + opts.offset)
|
117
|
+
|
118
|
+
## if zoom - add x2,x4 or such
|
119
|
+
if opts.zoom != 1
|
120
|
+
punk = punk.zoom( opts.zoom )
|
121
|
+
punk_name << "@#{opts.zoom}x"
|
122
|
+
end
|
123
|
+
|
124
|
+
path = "#{opts.outdir}/#{punk_name}.png"
|
125
|
+
puts "==> (#{index+1}/#{args.size}) saving punk ##{punk_index+opts.offset} to >#{path}<..."
|
126
|
+
|
127
|
+
punk.save( path )
|
128
|
+
end
|
129
|
+
puts 'Done.'
|
130
|
+
end # action
|
131
|
+
end # command tile
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
desc 'Generate punk characters from text attributes (from scratch / zero) via builtin punk spritesheet'
|
136
|
+
command [:g, :gen, :generate] do |c|
|
137
|
+
c.action do |g,o,args|
|
138
|
+
|
139
|
+
puts "==> generating >#{args.join( ' + ' )}<..."
|
140
|
+
punk = Image.generate( *args )
|
141
|
+
|
142
|
+
puts " setting zoom to #{opts.zoom}x" if opts.zoom != 1
|
143
|
+
|
144
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
145
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
146
|
+
|
147
|
+
punk_index = 0 ## assume base 10 decimal
|
148
|
+
punk_name = "punk-" + "%04d" % (punk_index + opts.offset)
|
149
|
+
|
150
|
+
## if zoom - add x2,x4 or such
|
151
|
+
if opts.zoom != 1
|
152
|
+
punk = punk.zoom( opts.zoom )
|
153
|
+
punk_name << "@#{opts.zoom}x"
|
154
|
+
end
|
155
|
+
|
156
|
+
path = "#{opts.outdir}/#{punk_name}.png"
|
157
|
+
puts "==> saving punk ##{punk_index+opts.offset} to >#{path}<..."
|
158
|
+
|
159
|
+
punk.save( path )
|
160
|
+
puts 'Done.'
|
161
|
+
end # action
|
162
|
+
end # command generate
|
163
|
+
|
164
|
+
|
165
|
+
desc 'Query (builtin off-chain) punk contract for punk text attributes by IDs - use 0 to 9999'
|
166
|
+
command [:q, :query] do |c|
|
167
|
+
c.action do |g,o,args|
|
168
|
+
|
169
|
+
# puts "opts:"
|
170
|
+
# puts opts.inspect
|
171
|
+
|
172
|
+
args.each_with_index do |arg,index|
|
173
|
+
punk_index = arg.to_i( 10 ) ## assume base 10 decimal
|
174
|
+
|
175
|
+
puts "==> (#{index+1}/#{args.size}) punk ##{punk_index}..."
|
176
|
+
|
177
|
+
attribute_names = CryptopunksData.punk_attributes( punk_index )
|
178
|
+
## downcase name and change spaces to underscore
|
179
|
+
attribute_names = attribute_names.map do |name|
|
180
|
+
name.downcase.gsub( ' ', '_' )
|
181
|
+
end
|
182
|
+
|
183
|
+
print " "
|
184
|
+
print attribute_names.join( ' ' )
|
185
|
+
print "\n"
|
186
|
+
end
|
187
|
+
puts 'Done.'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
desc 'List all punk archetype and attribute names from builtin punk spritesheet'
|
194
|
+
command [:l, :ls, :list] do |c|
|
195
|
+
c.action do |g,o,args|
|
196
|
+
|
197
|
+
generator = Cryptopunks.generator
|
198
|
+
|
199
|
+
puts "==> Archetypes"
|
200
|
+
generator.meta.each do |rec|
|
201
|
+
next unless rec.archetype?
|
202
|
+
|
203
|
+
print " "
|
204
|
+
print "%-30s" % "#{rec.name} / (#{rec.gender})"
|
205
|
+
print " - #{rec.type}"
|
206
|
+
print "\n"
|
207
|
+
end
|
208
|
+
|
209
|
+
puts ""
|
210
|
+
puts "==> Attributes"
|
211
|
+
generator.meta.each do |rec|
|
212
|
+
next unless rec.attribute?
|
213
|
+
|
214
|
+
print " "
|
215
|
+
print "%-30s" % "#{rec.name} / (#{rec.gender})"
|
216
|
+
print " - #{rec.type}"
|
217
|
+
print "\n"
|
218
|
+
end
|
219
|
+
|
220
|
+
puts ""
|
221
|
+
puts " See github.com/cryptopunksnotdead/punks.spritesheet for more."
|
222
|
+
puts ""
|
223
|
+
|
224
|
+
puts 'Done.'
|
225
|
+
end # action
|
226
|
+
end # command list
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
pre do |g,c,o,args|
|
231
|
+
opts.merge_gli_options!( g )
|
232
|
+
opts.merge_gli_options!( o )
|
233
|
+
|
234
|
+
if opts.verbose?
|
235
|
+
## LogUtils::Logger.root.level = :debug
|
236
|
+
end
|
237
|
+
|
238
|
+
## logger.debug "Executing #{c.name}"
|
239
|
+
true
|
240
|
+
end
|
241
|
+
|
242
|
+
post do |global,c,o,args|
|
243
|
+
## logger.debug "Executed #{c.name}"
|
244
|
+
true
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
on_error do |e|
|
249
|
+
|
250
|
+
if opts.verbose?
|
251
|
+
puts e.backtrace
|
252
|
+
end
|
253
|
+
|
254
|
+
if e.is_a?( SystemExit )
|
255
|
+
puts
|
256
|
+
puts "*** error: system exit with status code ( #{e.status} )"
|
257
|
+
exit( e.status ) ## try exit again to make sure error code gets passed along!!!
|
258
|
+
else
|
259
|
+
puts
|
260
|
+
puts "*** error: #{e.message}"
|
261
|
+
end
|
262
|
+
|
263
|
+
## note: was false # skip default error handling
|
264
|
+
|
265
|
+
## note: try true - false WILL SWALLOW exit codes and such
|
266
|
+
## - looks like it's still returning 0 (e.g. on unknown option or such)!!!!
|
267
|
+
true
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
### exit run(ARGV) ## note: use Toolii.run( ARGV ) outside of class
|
272
|
+
end # class Toolii
|
273
|
+
end # module Cryptopunks
|
274
|
+
|
275
|
+
|