rlm@176
|
1 (ns com.aurellem.gb.pokemon
|
rlm@190
|
2 (:use (com.aurellem.gb gb-driver util constants characters
|
rlm@190
|
3 moves types items status dv species
|
rlm@192
|
4 experience stats
|
rlm@190
|
5 ))
|
rlm@176
|
6 (:import [com.aurellem.gb.gb_driver SaveState]))
|
rlm@176
|
7
|
rlm@176
|
8 (def pokemon-names-start 0xD2B4)
|
rlm@176
|
9
|
rlm@179
|
10 (defn set-party-number
|
rlm@179
|
11 ([^SaveState state new-party-num]
|
rlm@179
|
12 (set-memory state 0xD162 new-party-num))
|
rlm@179
|
13 ([new-party-num]
|
rlm@179
|
14 (set-party-number @current-state new-party-num)))
|
rlm@176
|
15
|
rlm@176
|
16 (def party-number-address 0xD162)
|
rlm@176
|
17
|
rlm@176
|
18 (defn party-number
|
rlm@176
|
19 ([^SaveState state]
|
rlm@176
|
20 (aget (memory state) party-number-address))
|
rlm@176
|
21 ([] (party-number @current-state)))
|
rlm@176
|
22
|
rlm@176
|
23 (defn party-names
|
rlm@176
|
24 ([^SaveState state]
|
rlm@176
|
25 (let [raw-names
|
rlm@176
|
26 (subvec (vec (memory state))
|
rlm@176
|
27 pokemon-names-start
|
rlm@176
|
28 (+ pokemon-names-start
|
rlm@176
|
29 (* name-width 6)))]
|
rlm@176
|
30 (map
|
rlm@176
|
31 read-name
|
rlm@176
|
32 (take
|
rlm@176
|
33 (party-number state)
|
rlm@176
|
34 (partition name-width
|
rlm@176
|
35 raw-names)))))
|
rlm@176
|
36 ([] (party-names @current-state)))
|
rlm@176
|
37
|
rlm@191
|
38 (defn pokemon-nickname
|
rlm@191
|
39 ([^SaveState state poke-num]
|
rlm@191
|
40 (nth (party-names state) poke-num))
|
rlm@191
|
41 ([poke-num]
|
rlm@191
|
42 (pokemon-nickname @current-state poke-num)))
|
rlm@191
|
43
|
rlm@176
|
44 (defn rename-pokemon
|
rlm@176
|
45 ([^SaveState state n new-name]
|
rlm@176
|
46 (assert (<= 0 n (dec (party-number state))))
|
rlm@176
|
47 (assert (<= (count new-name) max-name-length))
|
rlm@176
|
48 (set-memory-range
|
rlm@176
|
49 state
|
rlm@176
|
50 (+ (* n name-width) pokemon-names-start)
|
rlm@176
|
51 (concat (str->character-codes new-name) [end-of-name-marker])))
|
rlm@176
|
52 ([n new-name]
|
rlm@176
|
53 (rename-pokemon @current-state n new-name)))
|
rlm@176
|
54
|
rlm@176
|
55 (def OT-start 0xD272)
|
rlm@176
|
56
|
rlm@176
|
57 (defn original-trainers
|
rlm@176
|
58 ([^SaveState state]
|
rlm@176
|
59 (let [raw-names
|
rlm@176
|
60 (subvec (vec (memory state))
|
rlm@176
|
61 OT-start
|
rlm@176
|
62 (+ OT-start
|
rlm@176
|
63 (* name-width 6)))]
|
rlm@176
|
64 (map read-name
|
rlm@176
|
65 (take (party-number state)
|
rlm@176
|
66 (partition name-width raw-names)))))
|
rlm@176
|
67 ([] (original-trainers @current-state)))
|
rlm@176
|
68
|
rlm@192
|
69 (defn read-OT-name
|
rlm@192
|
70 ([^SaveState state poke-num]
|
rlm@192
|
71 (nth (original-trainers state) poke-num))
|
rlm@192
|
72 ([poke-num] (read-OT @current-state poke-num)))
|
rlm@192
|
73
|
rlm@192
|
74 (defn set-OT-name
|
rlm@176
|
75 "Set the OT name for a pokemon.
|
rlm@176
|
76 Note that a pokemon is still considered 'yours' if
|
rlm@176
|
77 the OT ID is the same as your own."
|
rlm@176
|
78 ([^SaveState state poke-num new-name]
|
rlm@176
|
79 (assert (<= 0 poke-num (dec (party-number state))))
|
rlm@176
|
80 (assert (<= (count new-name) max-name-length))
|
rlm@176
|
81 (set-memory-range
|
rlm@176
|
82 state
|
rlm@176
|
83 (+ (* poke-num name-width) OT-start)
|
rlm@176
|
84 (concat (str->character-codes new-name) [end-of-name-marker])))
|
rlm@176
|
85 ([n new-name]
|
rlm@176
|
86 (set-original-trainer @current-state n new-name)))
|
rlm@176
|
87
|
rlm@197
|
88 (def OT-ID-addresses [0xD176 0xD1A2 0xD1CE 0xD1FA 0xD226 0xD252])
|
rlm@176
|
89
|
rlm@192
|
90 (defn read-OT-id
|
rlm@192
|
91 ([^SaveState state poke-num]
|
rlm@192
|
92 (let [mem (memory state)
|
rlm@192
|
93 start (OT-ID-addresses poke-num)]
|
rlm@192
|
94 (glue-bytes
|
rlm@192
|
95 (aget mem start)
|
rlm@192
|
96 (aget mem (inc start)))))
|
rlm@192
|
97 ([poke-num] (read-OT-id @current-state poke-num)))
|
rlm@192
|
98
|
rlm@192
|
99 (defn set-OT-id
|
rlm@192
|
100 ([^SaveState state poke-num new-OT-num]
|
rlm@192
|
101 (assert (<= 0 poke-num 5))
|
rlm@192
|
102 (assert (<= 0 new-OT-num 0xFFFF))
|
rlm@176
|
103 (set-memory-range
|
rlm@176
|
104 state
|
rlm@192
|
105 (OT-ID-addresses poke-num)
|
rlm@192
|
106 (disect-bytes-2 new-OT-num)))
|
rlm@192
|
107 ([poke-num new-OT-num]
|
rlm@192
|
108 (set-pokemon-id @current-state poke-num new-OT-num)))
|
rlm@176
|
109
|
rlm@176
|
110 (def unknown "[[[UNKNOWN]]]")
|
rlm@176
|
111
|
rlm@176
|
112 (def unknown "")
|
rlm@176
|
113
|
rlm@189
|
114 (def pokemon-1-record
|
rlm@176
|
115 {0xD16A "Color Map" ;; 0
|
rlm@176
|
116 0xD16B "Current-HP (h)" ;; 1
|
rlm@176
|
117 0xD16C "Current-HP (l)" ;; 2
|
rlm@182
|
118 0XD16D "Unused" ;; 3
|
rlm@178
|
119 0xD16E "Status" ;; 4
|
rlm@182
|
120 0xD16F "Type 1" ;; 5
|
rlm@182
|
121 0xD170 "Type 2" ;; 6
|
rlm@182
|
122 0xD171 "scratch/C.R." ;; 7
|
rlm@176
|
123 0xD172 "Move 1 ID" ;; 8
|
rlm@176
|
124 0xD173 "Move 2 ID" ;; 9
|
rlm@176
|
125 0xD174 "Move 3 ID" ;; 10
|
rlm@176
|
126 0xD175 "Move 4 ID" ;; 11
|
rlm@176
|
127 0xD176 "OT-ID (h)" ;; 12
|
rlm@176
|
128 0xD177 "OT-ID (l)" ;; 13
|
rlm@176
|
129 0xD178 "Exp. Points (h)" ;; 14
|
rlm@176
|
130 0xD179 "Exp. Points (m)" ;; 15
|
rlm@176
|
131 0xD17A "Exp. Points (l)" ;; 16
|
rlm@176
|
132 0xD17B "HP Exp. (h)" ;; 17
|
rlm@176
|
133 0xD17C "HP Exp. (l)" ;; 18
|
rlm@176
|
134 0xD17D "Attack Exp. (h)" ;; 19
|
rlm@176
|
135 0xD17E "Attack Exp. (l)" ;; 20
|
rlm@176
|
136 0xD17F "Defense Exp. (h)" ;; 21
|
rlm@176
|
137 0xD180 "Defense Exp. (l)" ;; 22
|
rlm@176
|
138 0xD181 "Speed Exp. (h)" ;; 23
|
rlm@176
|
139 0xD182 "Speed Exp. (l)" ;; 24
|
rlm@176
|
140 0xD183 "Special Exp. (h)" ;; 25
|
rlm@176
|
141 0xD184 "Special Exp. (l)" ;; 26
|
rlm@176
|
142 0xD185 "DV Atk/Def" ;; 27
|
rlm@176
|
143 0xD186 "DV Speed/Spc" ;; 28
|
rlm@176
|
144 0xD187 "PP Move 1" ;; 29
|
rlm@176
|
145 0xD188 "PP Move 2" ;; 30
|
rlm@176
|
146 0xD189 "PP Move 3" ;; 31
|
rlm@176
|
147 0xD18A "PP Move 4" ;; 32
|
rlm@176
|
148 0xD18B "Current Level" ;; 33
|
rlm@176
|
149 0xD18C "HP Total (h)" ;; 34
|
rlm@176
|
150 0xD18D "HP Total (l)" ;; 35
|
rlm@176
|
151 0xD18E "Attack (h)" ;; 36
|
rlm@176
|
152 0xD18F "Attack (l)" ;; 37
|
rlm@176
|
153 0xD190 "Defense (h)" ;; 38
|
rlm@176
|
154 0xD191 "Defense (l)" ;; 39
|
rlm@176
|
155 0xD192 "Speed (h)" ;; 40
|
rlm@176
|
156 0xD193 "Speed (l)" ;; 41
|
rlm@176
|
157 0xD194 "Special (h)" ;; 42
|
rlm@176
|
158 0xD195 "Special (l)" ;; 43
|
rlm@176
|
159 })
|
rlm@176
|
160
|
rlm@189
|
161 (defn pokemon-record
|
rlm@176
|
162 ([^SaveState state pokemon-num]
|
rlm@176
|
163 (assert (<= 0 pokemon-num 5))
|
rlm@176
|
164 (let [base (+ (* pokemon-num pokemon-record-width) 0xD16A)]
|
rlm@176
|
165 (subvec (vec (memory state)) base
|
rlm@176
|
166 (+ base pokemon-record-width))))
|
rlm@189
|
167 ([pokemon-num] (pokemon-record @current-state pokemon-num)))
|
rlm@176
|
168
|
rlm@189
|
169 (defn set-pokemon-record
|
rlm@176
|
170 ([^SaveState state pokemon-num new-data]
|
rlm@176
|
171 (assert (<= 0 pokemon-num 5))
|
rlm@176
|
172 (let [base (+ (* pokemon-num pokemon-record-width) 0xD16A)]
|
rlm@176
|
173 (set-memory-range state base new-data)))
|
rlm@176
|
174 ([pokemon-num new-data]
|
rlm@189
|
175 (set-pokemon-record @current-state pokemon-num new-data)))
|
rlm@176
|
176
|
rlm@189
|
177 (defn print-pokemon-record
|
rlm@176
|
178 ([^SaveState state pokemon-num]
|
rlm@176
|
179 (assert (<= 0 pokemon-num 5))
|
rlm@189
|
180 (let [poke-data (pokemon-record state pokemon-num)
|
rlm@189
|
181 backbone (sort (keys pokemon-1-record))]
|
rlm@176
|
182 (println "Pokemon " pokemon-num " -- "
|
rlm@176
|
183 (nth (party-names state)
|
rlm@176
|
184 pokemon-num) \newline)
|
rlm@176
|
185
|
rlm@176
|
186 (println " Desc. | Hex | Dec | Binary |")
|
rlm@176
|
187 (println "-------------------+------+-----+----------+")
|
rlm@176
|
188 (dorun
|
rlm@176
|
189 (map
|
rlm@176
|
190 (comp println
|
rlm@176
|
191 (fn [desc data]
|
rlm@176
|
192 (format "%-16s | 0x%02X | %3d | %s |"
|
rlm@176
|
193 desc data data
|
rlm@176
|
194 (let [s (Integer/toBinaryString data)]
|
rlm@176
|
195 (apply
|
rlm@176
|
196 str
|
rlm@176
|
197 (concat (repeat (- 8 (count s)) "0" )
|
rlm@176
|
198 s))))))
|
rlm@189
|
199 (map pokemon-1-record backbone)
|
rlm@176
|
200 poke-data))))
|
rlm@176
|
201 ([pokemon-num]
|
rlm@189
|
202 (print-pokemon-record @current-state pokemon-num)))
|
rlm@190
|
203
|
rlm@190
|
204 (def mint-berry-item-code-gsc 0x54)
|
rlm@190
|
205
|
rlm@191
|
206 (defn pokemon-info
|
rlm@191
|
207 ([^SaveState state poke-num]
|
rlm@191
|
208 (assert (<= 0 poke-num 5))
|
rlm@191
|
209 (let [dv-values (read-DV state poke-num)
|
rlm@191
|
210 type (read-type state poke-num)
|
rlm@191
|
211 species (read-species state poke-num)
|
rlm@191
|
212 species2 (read-species2 state poke-num)
|
rlm@191
|
213 moves (read-moves state poke-num)
|
rlm@191
|
214 moves-pp (mapv (partial read-pp state
|
rlm@191
|
215 poke-num)
|
rlm@191
|
216 (range (count moves)))
|
rlm@191
|
217 nickname (pokemon-nickname state poke-num)
|
rlm@191
|
218 status (read-status state poke-num)
|
rlm@192
|
219 stats (read-stats state poke-num)
|
rlm@191
|
220 experience (read-experience state poke-num)
|
rlm@192
|
221 OT-name (read-OT-name state poke-num)
|
rlm@193
|
222 ID (read-OT-id state poke-num)]
|
rlm@193
|
223 {;; persistent
|
rlm@193
|
224 :name nickname
|
rlm@191
|
225 :species species
|
rlm@191
|
226 :species2 species2
|
rlm@191
|
227 :type type
|
rlm@191
|
228 :dv dv-values
|
rlm@192
|
229 :original-trainer OT-name
|
rlm@192
|
230 :ID ID
|
rlm@196
|
231 :moves (mapv vector moves moves-pp)
|
rlm@190
|
232
|
rlm@193
|
233 ;; ephemerial
|
rlm@200
|
234 :status status
|
rlm@192
|
235 :stats stats
|
rlm@191
|
236 :experience experience
|
rlm@193
|
237 }))
|
rlm@191
|
238 ([poke-num]
|
rlm@191
|
239 (pokemon-info @current-state poke-num)))
|
rlm@195
|
240
|
rlm@202
|
241 (def status-message
|
rlm@202
|
242 {:sleep-6 "sleeping. It will wake in six turns."
|
rlm@202
|
243 :sleep-5 "sleeping. It will wake in five turns."
|
rlm@202
|
244 :sleep-4 "sleeping. It will wake in four turns."
|
rlm@202
|
245 :sleep-3 "sleeping. It will wake in three turns."
|
rlm@202
|
246 :sleep-2 "sleeping. It will wake in two turns."
|
rlm@202
|
247 :sleep-1 "sleeping. It will wake in one turn."
|
rlm@202
|
248 :poisoned "poisoned."
|
rlm@202
|
249 :frozen "frozen solid."
|
rlm@202
|
250 :burned "burned."
|
rlm@202
|
251 :paralyzed "paralyzed."})
|
rlm@202
|
252
|
rlm@202
|
253
|
rlm@195
|
254 (defn print-pokemon
|
rlm@195
|
255 ([^SaveState state poke-num]
|
rlm@195
|
256 (let [info (pokemon-info state poke-num)]
|
rlm@195
|
257 (printf
|
rlm@195
|
258 (str
|
rlm@195
|
259 "##################################"
|
rlm@195
|
260 "##################################\n"
|
rlm@195
|
261 "# "
|
rlm@195
|
262 " #\n"
|
rlm@195
|
263 "# %-44s"
|
rlm@195
|
264 "%-20s#\n"
|
rlm@195
|
265 "# "
|
rlm@195
|
266 " #\n"
|
rlm@195
|
267 "##################################"
|
rlm@195
|
268 "##################################\n\n")
|
rlm@195
|
269
|
rlm@195
|
270 (str
|
rlm@195
|
271 (:name info)
|
rlm@195
|
272 (str
|
rlm@202
|
273 " [" (.toUpperCase
|
rlm@202
|
274 (.substring (str (:species info)) 1)) "]")
|
rlm@195
|
275 (str " Lvl." (format "%-3d" (:level (:stats info)))))
|
rlm@195
|
276 (str (:original-trainer info) " / " (:ID info)))
|
rlm@195
|
277
|
rlm@195
|
278 (println
|
rlm@195
|
279 (str
|
rlm@195
|
280 (str "-----------------------------------"
|
rlm@195
|
281 "---------------------------------\n" )
|
rlm@195
|
282 (str "| Stats | HP | Attack "
|
rlm@195
|
283 "| Defense | Speed | Special |\n")
|
rlm@195
|
284 (str "+-----------+----------+----------"
|
rlm@195
|
285 "+----------+----------+----------+")))
|
rlm@195
|
286
|
rlm@195
|
287 (printf
|
rlm@202
|
288 (str "|%-11s| %5d | %5d "
|
rlm@202
|
289 "| %5d | %5d | %5d |\n")
|
rlm@195
|
290 "DV Values" (:hp (:dv info)) (:attack (:dv info))
|
rlm@195
|
291 (:defense (:dv info)) (:speed (:dv info))
|
rlm@195
|
292 (:special (:dv info)))
|
rlm@195
|
293
|
rlm@195
|
294 (let [c (:stats info)]
|
rlm@195
|
295 (printf
|
rlm@202
|
296 (str "|%-11s|%8s | %5d "
|
rlm@202
|
297 "| %5d | %5d | %5d |\n")
|
rlm@202
|
298 "Current" (str (:current-hp c) "/" (:hp c)) (:attack c)
|
rlm@195
|
299 (:defense c) (:speed c)
|
rlm@195
|
300 (:special c)))
|
rlm@195
|
301
|
rlm@195
|
302 (let [e (:experience info)]
|
rlm@195
|
303 (printf
|
rlm@202
|
304 (str "|%-11s| %5d | %5d "
|
rlm@202
|
305 "| %5d | %5d | %5d |\n")
|
rlm@195
|
306 "Experience" (:hp-exp e) (:attack-exp e)
|
rlm@195
|
307 (:defense-exp e) (:speed-exp e)
|
rlm@195
|
308 (:special-exp e)))
|
rlm@195
|
309 (println
|
rlm@195
|
310 (str "+-----------+----------+----------"
|
rlm@195
|
311 "+----------+----------+----------+"))
|
rlm@195
|
312
|
rlm@195
|
313 (print "\n")
|
rlm@195
|
314 (println "+------------------+----+--------+--------+")
|
rlm@195
|
315 (println "| Move | PP | Max PP | PP UPs |")
|
rlm@196
|
316 (println "+------------------+----+--------+--------+")
|
rlm@196
|
317
|
rlm@197
|
318 (dorun
|
rlm@197
|
319 (for [[name {:keys [pp-ups current-pp]}] (:moves info)]
|
rlm@197
|
320 (printf
|
rlm@197
|
321 "| %-17s| %2d | %02d | %02d |\n"
|
rlm@197
|
322 (.substring (str name) 1)
|
rlm@197
|
323 current-pp (max-pp name pp-ups) pp-ups)))
|
rlm@195
|
324
|
rlm@200
|
325 (println "+------------------+----+--------+--------+\n")
|
rlm@195
|
326
|
rlm@200
|
327 (println "Total Experience:" (:main-exp (:experience info)))
|
rlm@202
|
328 (if (not= :normal (:status info))
|
rlm@202
|
329 (println "\n* This pokemon is currently"
|
rlm@202
|
330 (status-message (:status info))))
|
rlm@200
|
331 (if (not= (:species info) (:species2 info))
|
rlm@202
|
332 (println "\n* This pokemon has a secondary species"
|
rlm@200
|
333 (str
|
rlm@200
|
334 "("
|
rlm@200
|
335 (.substring (str (:species2 info)) 1) ")\n")
|
rlm@202
|
336 " that does not match its primary species."))))
|
rlm@195
|
337 ([poke-num]
|
rlm@195
|
338 (print-pokemon @current-state poke-num)))
|
rlm@192
|
339
|
rlm@202
|
340 (defn print-team []
|
rlm@202
|
341 (dorun (map print-pokemon (range (party-number)))))
|
rlm@202
|
342
|
rlm@202
|
343
|
rlm@192
|
344 (defn give-status-all
|
rlm@192
|
345 ([^SaveState state status]
|
rlm@192
|
346 (reduce (fn [state num]
|
rlm@192
|
347 (give-status state num status))
|
rlm@192
|
348 state
|
rlm@192
|
349 (range (party-number state))))
|
rlm@192
|
350 ([status]
|
rlm@192
|
351 (give-status-all @current-state status)))
|
rlm@203
|
352
|
rlm@203
|
353
|
rlm@203
|
354 (def pokemon-base
|
rlm@203
|
355 {:dv {:attack 15 :hp 15 :defense 15
|
rlm@203
|
356 :special 15 :speed 15}
|
rlm@203
|
357 :species :ditto
|
rlm@203
|
358 :original-trainer "RLM"
|
rlm@203
|
359 :ID 5195
|
rlm@203
|
360 :status :normal
|
rlm@203
|
361 :experience
|
rlm@203
|
362 {:main-exp 500
|
rlm@203
|
363 :attack-exp 0xFF
|
rlm@203
|
364 :defense-exp 0xFF
|
rlm@203
|
365 :speed-exp 0xFF
|
rlm@203
|
366 :special-exp 0xFF
|
rlm@203
|
367 :hp-exp 0xFF}
|
rlm@203
|
368
|
rlm@203
|
369 :stats
|
rlm@203
|
370 ;; TODO recalculate these from a real ditto
|
rlm@203
|
371 {:level 7
|
rlm@203
|
372 :current-hp 50
|
rlm@203
|
373 :hp 50
|
rlm@203
|
374 :attack 50
|
rlm@203
|
375 :defense 50
|
rlm@203
|
376 :speed 50
|
rlm@203
|
377 :special 50}
|
rlm@203
|
378
|
rlm@203
|
379
|
rlm@203
|
380 :moves [[:transform {:pp-up 3 :pp 5}]]})
|
rlm@203
|
381
|
rlm@203
|
382 (defn expand-pokemon
|
rlm@203
|
383 "Given a map describing a pokemon, fill in any missing
|
rlm@203
|
384 values based on the ones already present."
|
rlm@203
|
385 [pokemon]
|
rlm@203
|
386 (-> (merge pokemon-base pokemon)
|
rlm@203
|
387 ;; if no nickname is supplied, default to the
|
rlm@203
|
388 ;; uppercase name of the species, as r/b/y do
|
rlm@203
|
389 ;; when a pokemon is captured.
|
rlm@203
|
390 ((fn [pokemon]
|
rlm@203
|
391 (if (nil? (:name pokemon))
|
rlm@203
|
392 (assoc pokemon :name (.toUpperCase
|
rlm@203
|
393 (.substring
|
rlm@203
|
394 (str (:species pokemon)) 1)))
|
rlm@203
|
395 pokemon)))
|
rlm@203
|
396 ;; species2 should almost always just be the
|
rlm@203
|
397 ;; same as species.
|
rlm@203
|
398 ((fn [pokemon]
|
rlm@203
|
399 (if (nil? (:species2 pokemon))
|
rlm@203
|
400 (assoc pokemon :species2 (:species pokemon)))))
|
rlm@203
|
401
|
rlm@203
|
402 ;; enable the date in :moves to be any combo of
|
rlm@203
|
403 ;; [:move-1 :move-2]
|
rlm@203
|
404 ;; [[:move-1 {:pp 20}] :move-2]
|
rlm@203
|
405 ;; [[:move-1 {:pp 20 :pp-up 3}] :move-2]
|
rlm@203
|
406 ;; default to full pp for the move, with no
|
rlm@203
|
407 ;; pp-ups.
|
rlm@203
|
408 ((fn [pokemon]
|
rlm@203
|
409 (let [moves (:moves pokemon)]
|
rlm@203
|
410 (assoc pokemon :moves
|
rlm@203
|
411 (for [move moves]
|
rlm@203
|
412 (cond
|
rlm@203
|
413 (keyword? move)
|
rlm@203
|
414 [move {:pp (max-pp move) :pp-up 0}]
|
rlm@203
|
415 (vector? move)
|
rlm@203
|
416 [(first move)
|
rlm@203
|
417 (merge {:pp (max-pp (first move))
|
rlm@203
|
418 :pp-up 0} (second move))]))))))
|
rlm@203
|
419
|
rlm@203
|
420
|
rlm@203
|
421
|
rlm@203
|
422
|
rlm@203
|
423 ))
|
rlm@203
|
424 |