OXYGENHUB
◆ main openmsx / share / scripts / _mog-overlay.tcl
14.0 KiB · Tcl · last changed chore(): initial commit 1 month ago source blame history raw
1
namespace eval mog_overlay {
2
3
variable num_enemies 16
4
variable num_rocks 8
5
variable mog_overlay_active false
6
variable mog_editor_active false
7
variable item_cache
8
variable tomb_cache
9
variable ladder_cache
10
variable wall_cache
11
variable demon_cache
12
variable ep
13
variable mouse1_pressed
14
15
# Demon summon spells
16
variable spell [list \
17
	"" "YOMAR" "ELOHIM" "HAHAKLA" "BARECHET" "HEOTYMEO" \
18
	"LEPHA" "NAWABRA" "ASCHER" "XYWOLEH" "HAMALECH"]
19
# Demon Max Power
20
variable demon_power [list -1 -1 24 40 64 112 72 56 64 80 80 248]
21
# Item names
22
variable items [list \
23
	"" "Arrow" "Ceramic Arrow" "Rolling Fire" "Fire" "Mine" \
24
	"Magnifying Glass" "Holy Water" "Cape" "Magical Rod" "World Map" \
25
	"Cross" "Great Key" "Necklace" "Crown" "Helm" "Oar" "Boots" "Doll" \
26
	"Robe" "Bell" "Halo" "Candle" "Armor" "Carpet" "Helmet" "Lamp" "Vase" \
27
	"Pendant" "Earrings" "Bracelet" "Ring" "Bible" "Harp" "Triangle" \
28
	"Trumpet Shell" "Pitcher" "Sabre" "Dagger" "Feather" "Bronze Shield" \
29
	"Bread and Water" "Salt" "Silver Shield" "Gold Shield" \
30
	"Quiver of Arrows" "Coins" "Keys"]
31
32
variable enemy_names [list \
33
	"" "Gorilla" "Twinkle" "HorBlob" "Gate" "Fire Snake" "06" "Ring Worm" \
34
	"08" "09" "Knight" "Water Strider" "Sparky" "Fish" "Bat" "Pacman" \
35
	"Insect" "Hedgehog" "Rockman" "Cloud Demon" "Mudman" "Ill" \
36
	"Bird Dragon" "Egg Bird" "Worm" "Butterfly" "Snake's Fire" "Fireball" \
37
	"1C" "1D" "1E" "Goblin" "20" "Crawler" "Pea Shooter" "Trapwall" "Swine" \
38
	"Bones" "Living Helmet" "Owl" "Ectoplasm" "29" "Poltergeist" "Wizard" \
39
	"Shoe 1" "Frost Demon" "Bamboo Shoot" "Frog Plant" "Seahorse Demon" \
40
	"31" "32" "Armor's Dart" "Bally" "35" "36" "Great Butterfly" "VerBlob" \
41
	"39" "3A" "Moai Head Projectile" "Trickster Ghost" "Star" \
42
	"Flocking Bird" "3F" "Cyclop's Ghost" "41" "Maner" "Gero" "44" "45" \
43
	"46" "47" "48" "Huge Bat" "4A" "4B" "Fuzzball" "4D" "4E" "4F" "50" "51" \
44
	"Middle Bat" "Mini Bat" "Bone" "Small Bone" "Small Fireball" "57" \
45
	"Crab's Breath" "Seed" "5A" "5B" "Spiral Ball " "5D" "5E" "5F" "Maner's Arm" "61" \
46
	"Gero's Tongue" "63" "Shoe 2" "Breath" "66" "Protectors"]
47
48
set_help_text toggle_mog_overlay \
49
"Shows OSD widgets that help you play The Maze of Galious. Only useful if you
50
are actually running this game... If you are not, you may get a lot of weird
51
stuff on your screen :) The script shows the Great Demon summon spells, ladders
52
which will disappear, walls which will grow, hitpoints of enemies, hidden
53
items, etc.\nSee also toggle_mog_editor."
54
55
set_help_text toggle_mog_editor \
56
"Gives you mouse bindings to fool around in The Maze of Galious. Only useful if
57
you are actually running this game... If you are not, you may get a lot of
58
weird stuff on your screen :) With this editor, you can draw walls with one
59
mouse button and put Popolon on your position of choice with the other. Needs
60
the MoG overlay to work, see toggle_mog_overlay."
61
62
#Init Overlays
63
proc init {} {
64
	variable num_enemies
65
	variable num_rocks
66
	variable label_color
67
	variable label_text_color
68
	variable item_cache
69
	variable tomb_cache
70
	variable ladder_cache
71
	variable wall_cache
72
	variable demon_cache
73
	variable max_ep
74
75
	osd_widgets::msx_init mog
76
77
	for {set i 0} {$i < $num_enemies} {incr i} {
78
		osd_widgets::create_power_bar mog.powerbar$i 14 2 0xff0000ff 0x00000080 0xffffffff
79
	}
80
81
	osd_widgets::create_power_bar mog.item 16 16 0x00000000 0xff770020 0xffffffff
82
	set item_cache 0
83
84
	osd create rectangle mog.tomb -rely 999 -w 16 -h 16 -rgba 0x00000080
85
	osd create text mog.tomb.text -x 1 -y 1 -size 5 -rgb 0xffffff
86
	set tomb_cache 0
87
88
	osd create rectangle mog.ladder -rely 999 -relw 16 -rgba 0xffffff80
89
	set ladder_cache 0
90
91
	osd create rectangle mog.wall -rely 999 -relw 8 -relh 24 -rgba 0x00ffff80
92
	set wall_cache 0
93
94
	osd create rectangle mog.wall2 -rely 999 -relw 32 -relh 24 -rgba 0x00000080
95
	osd create text mog.wall2.text -x 1 -y 1 -size 4 -rgb 0xffffff
96
97
	for {set i 0} {$i < $num_rocks} {incr i} {
98
		osd create rectangle mog.rock$i  -rely 999 -w 24 -h 4 -rgba 0x0000ff80
99
		osd create text mog.rock$i.text -x 0 -y 0 -size 4 -rgb 0xffffff
100
	}
101
102
	osd_widgets::create_power_bar mog.demon 48 4 0x2bdd2bff 0x00000080 0xffffffff
103
	set demon_cache 0
104
105
	# Enemy Power
106
	for {set i 0} {$i < $num_enemies} {incr i} {set max_ep($i) 0}
107
}
108
109
# Update the overlays
110
proc update_overlay {} {
111
	variable mog_overlay_active
112
	variable num_enemies
113
	variable num_rocks
114
	variable label_color
115
	variable label_text_color
116
	variable item_cache
117
	variable tomb_cache
118
	variable ladder_cache
119
	variable wall_cache
120
	variable demon_cache
121
	variable spell
122
	variable demon_power
123
	variable items
124
	variable enemy_names
125
	variable max_ep
126
127
	if {!$mog_overlay_active} return
128
129
	#see if not in demo
130
	if {[peek 0xe002] != 64} {osd configure mog -y 999} else {osd_widgets::msx_update mog}
131
132
	for {set i 0; set addr 0xe800} {$i < $num_enemies} {incr i; incr addr 0x20} {
133
		set enemy_type [peek $addr]
134
		set enemy_hp [peek [expr {$addr + 0x10}]]
135
136
		# If enemy's power is 0 set max power to 0
137
		if {$enemy_hp == 0 && $max_ep($i) > 0} {set max_ep($i) 0}
138
139
		if {$enemy_type > 0 && $enemy_hp > 0 && $enemy_hp < 200} {
140
			#Set Max Power Recorded for Enemy
141
			if {$max_ep($i) < $enemy_hp} {set max_ep($i) $enemy_hp}
142
143
			set pos_x [expr { 8 + [peek [expr {$addr + 7}]]}]
144
			set pos_y [expr {-6 + [peek [expr {$addr + 5}]]}]
145
			set power [expr {1.00 * $enemy_hp / $max_ep($i)}]
146
			set text [format "%s (%d): %d/%d" [lindex $enemy_names $enemy_type] $i $enemy_hp $max_ep($i)]
147
			osd_widgets::update_power_bar mog.powerbar$i $pos_x $pos_y $power $text
148
		} else {
149
			osd_widgets::hide_power_bar mog.powerbar$i
150
		}
151
	}
152
153
	set new_item [peek 0xe740]
154
	if {$new_item != $item_cache} {
155
		set item_cache $new_item
156
		if {$item_cache} {
157
			set height [expr {($item_cache < 7) ? 8 : 16}]
158
			osd configure mog.item -relh $height
159
			osd configure mog.item.text -text [lindex $items $item_cache]
160
			osd configure mog.item -relx [peek 0xe743] -rely [peek 0xe742]
161
		} else {
162
			osd configure mog.item -rely 999
163
		}
164
	}
165
166
	set new_tomb [peek 0xe678]
167
	if {$new_tomb != $tomb_cache} {
168
		set tomb_cache $new_tomb
169
		if {($tomb_cache > 0) && ($tomb_cache <= 10)} {
170
			osd configure mog.tomb.text -text "[lindex $spell $tomb_cache]"
171
			osd configure mog.tomb -relx [peek 0xe67a] -rely [peek 0xe679]
172
		} else {
173
			osd configure mog.tomb -rely 999
174
		}
175
	}
176
177
	#todo: see what the disappear trigger is (up, down, either)
178
	set new_ladder [expr {[peek 0xec00] | [peek 0xec07]}]
179
	if {$new_ladder != $ladder_cache} {
180
		set ladder_cache $new_ladder
181
		if {$ladder_cache != 0} {
182
			osd configure mog.ladder -relx [peek 0xec02] -rely [peek 0xec01] \
183
			                         -relh [expr {8 * ([peek 0xec03] & 31)}]
184
		} else {
185
			osd configure mog.ladder -rely 999
186
		}
187
	}
188
189
	set new_wall [peek 0xeaa0]
190
	if {$new_wall != $wall_cache} {
191
		set wall_cache $new_wall
192
		if {$wall_cache != 0} {
193
			osd configure mog.wall -relx [peek 0xeaa3] -rely [peek 0xeaa2]
194
		} else {
195
			osd configure mog.wall -rely 999
196
		}
197
	}
198
199
	if {[peek 0xea20] != 0} {
200
		set text [format "Wall (%02d)" [peek 0xea23]]
201
		osd configure mog.wall2 -relx [peek 0xea22] -rely [peek 0xea21]
202
		osd configure mog.wall2.text -text $text
203
	} else {
204
		osd configure mog.wall2 -rely 999
205
	}
206
207
	for {set i 0; set addr 0xec30} {$i < $num_rocks} {incr i; incr addr 8} {
208
		if {[peek $addr] > 0} {
209
			set pos_x [expr { 8 + [peek [expr {$addr + 3}]]}]
210
			set pos_y [expr {-6 + [peek [expr {$addr + 2}]]}]
211
			set rock_hp [peek [expr {$addr + 6}]]
212
			set text [format "Rock %d (%02d)" $i $rock_hp]
213
			osd configure mog.rock$i -relx $pos_x -rely $pos_y
214
			osd configure mog.rock$i.text -text $text
215
		} else {
216
			osd configure mog.rock$i -rely 999
217
		}
218
	}
219
220
	if {([peek 0xe0d2] != 0) && ([peek 0xe710] != 0)} {
221
		osd configure mog.demon -relx 112 -rely 26
222
		# let's not print text, it destroys a replay
223
		# print_mog_text 1 1 "Now Fighting [lindex $spell [expr {[peek 0xe041] - 1}]]"
224
		set new_demon [peek 0xe710]
225
		if {$new_demon != $demon_cache} {
226
			set demon_cache $new_demon
227
			set demon_max [lindex $demon_power [peek 0xe041]]
228
			if {$demon_max > 0} {
229
				set power [expr {$demon_cache * ([peek 0xe076] + 1.0) / $demon_max}]
230
				osd configure mog.demon.bar -relw $power
231
			}
232
		}
233
	} else {
234
		osd configure mog.demon -rely 999
235
	}
236
237
	after frame [namespace code update_overlay]
238
}
239
240
# better don't use this if you want to keep your replay OK
241
proc print_mog_text {x y text} {
242
	set text [string tolower $text]
243
	for {set i 0} {$i < [string length $text]} {incr i} {
244
		scan [string range $text $i $i] "%c" ascii
245
		incr ascii -86
246
		if {$ascii >= -38 && $ascii <= -29} {incr ascii 39}
247
		if {$ascii < 0} {set ascii 0}
248
		poke [expr {0xed80 + $x + $y * 32 + $i}] $ascii
249
	}
250
}
251
252
proc toggle_mog_overlay {} {
253
	variable mog_overlay_active
254
	variable mog_editor_active
255
256
	set mog_overlay_active [expr {!$mog_overlay_active}]
257
258
	if {$mog_overlay_active} {
259
		init
260
		update_overlay
261
		message "MoG overlay activated" info
262
		return "MoG overlay activated!"
263
	} else {
264
		set retval ""
265
		if {$mog_editor_active} {
266
			set retval "[toggle_mog_editor]\n"
267
		}
268
		osd destroy mog
269
		message "MoG overlay deactivated" info
270
		return "${retval}MoG overlay deactivated."
271
	}
272
}
273
274
# MoG editor
275
# Separated from MoG overlay, because the overlay is useful for TASing and the
276
# editor will ruin your replay
277
278
proc draw_block {} {
279
	osd_widgets::msx_update "mog"
280
	lassign [osd info "mog" -mousecoord] x y
281
	poke [expr {0xed80 + int($x / 8) + int(($y - 32) / 8) * 32}] 240
282
	if {$mog_overlay::mouse1_pressed} {after frame mog_overlay::draw_block}
283
}
284
285
proc put_popolon {} {
286
	osd_widgets::msx_update "mog"
287
	lassign [osd info "mog" -mousecoord] x y
288
	poke 0xe507 [utils::clip 0 255 [expr {int($x)}]]
289
	poke 0xe505 [utils::clip 0 212 [expr {int($y)}]]
290
}
291
292
proc toggle_mog_editor {} {
293
	variable mog_editor_active
294
	variable mog_overlay_active
295
296
	if {!$mog_editor_active && !$mog_overlay_active} {
297
		error "You need to have the MoG overlay active to use this editor!"
298
	}
299
300
	set mog_editor_active [expr {!$mog_editor_active}]
301
302
	if {$mog_editor_active} {
303
		bind -layer mog_editor "mouse button1 down" {
304
			set mog_overlay::mouse1_pressed true
305
			mog_overlay::draw_block
306
		}
307
308
		bind -layer mog_editor "mouse button1 up" {
309
			set mog_overlay::mouse1_pressed false
310
		}
311
312
		bind -layer mog_editor "mouse button3 down" {
313
			mog_overlay::put_popolon
314
		}
315
		activate_input_layer mog_editor
316
		return "MoG editor activated!"
317
	} else {
318
		deactivate_input_layer mog_editor
319
		return "MoG editor deactivated."
320
	}
321
	return ""
322
}
323
324
# ----------------------------------------------------------------------
325
# password handling
326
#
327
# Password starts at column 7, row 9:
328
#
329
#   XXXX XXXX XXXX XXXX
330
#   XXXX XXXX XXXX XXXX
331
#   XXXX XXXX XXXX X
332
#
333
# Pattern encoding:
334
#   0      = space
335
#   1..10  = 0..9
336
#   11..36 = A..Z
337
# ----------------------------------------------------------------------
338
339
set_help_text mog_password \
340
"Extracts the currently shown password from The Maze of Galious. The password
341
is displayed in the same groups as on screen and saved without spaces to a
342
file, so it can be typed back into the game automatically with type_from_file
343
(with the -release option, otherwise the game won't accept the typing).
344
With no argument, the password is saved as mog_password.txt. Give a filename
345
as an argument to save it to a different file."
346
347
set_help_text mog_password_show \
348
"Extracts and displays the currently shown password from The Maze of Galious.
349
The password is displayed in the same groups as on screen, but is not saved to
350
a file."
351
352
variable mog_name_table 0x3800
353
variable mog_screen_width 32
354
variable mog_start_col 7
355
variable mog_start_row 9
356
357
# Column offsets of the password characters on each row.
358
variable mog_password_offsets {
359
	{0 1 2 3 5 6 7 8 10 11 12 13 15 16 17 18}
360
	{0 1 2 3 5 6 7 8 10 11 12 13 15 16 17 18}
361
	{0 1 2 3 5 6 7 8 10 11 12 13 15}
362
}
363
364
365
proc _mog_password_char {p} {
366
	if {$p == 0} {
367
		return " "
368
	} elseif {$p <= 10} {
369
		return [format %c [expr {48 + $p - 1}]]
370
	} elseif {$p <= 36} {
371
		return [format %c [expr {65 + $p - 11}]]
372
	} else {
373
		return "?"
374
	}
375
}
376
377
378
proc mog_get_password {} {
379
	variable mog_name_table
380
	variable mog_screen_width
381
	variable mog_start_col
382
	variable mog_start_row
383
	variable mog_password_offsets
384
385
	set password ""
386
	set row 0
387
388
	foreach row_offsets $mog_password_offsets {
389
		foreach offset $row_offsets {
390
			set col [expr {$mog_start_col + $offset}]
391
			set address [expr {
392
				$mog_name_table +
393
				(($mog_start_row + $row) * $mog_screen_width) +
394
				$col
395
			}]
396
397
			append password [_mog_password_char [debug read VRAM $address]]
398
		}
399
400
		incr row
401
	}
402
403
	return $password
404
}
405
406
407
proc _mog_format_password {password} {
408
	# The raw password consists of:
409
	#
410
	#   16 characters
411
	#   16 characters
412
	#   13 characters
413
	#
414
	# Insert spaces between the visual groups and newlines
415
	# between the rows.
416
417
	set formatted ""
418
	set pos 0
419
420
	foreach length {4 4 4 4 4 4 4 4 4 4 4 1} {
421
		append formatted [string range $password $pos [expr {$pos + $length - 1}]]
422
		incr pos $length
423
424
		if {$pos < [string length $password]} {
425
			if {$pos == 16 || $pos == 32} {
426
				append formatted "\n"
427
			} else {
428
				append formatted " "
429
			}
430
		}
431
	}
432
433
	return $formatted
434
}
435
436
# ----------------------------------------------------------------------
437
# mog_password
438
#
439
# Extracts the current password, displays it in screen format, and
440
# saves the raw password to a file.
441
#
442
# Usage:
443
#   mog_password
444
#   mog_password filename
445
#
446
# Default filename: mog_password.txt
447
#
448
# The file contains only the password characters, without the spaces
449
# used to visually separate the groups on screen.
450
# ----------------------------------------------------------------------
451
452
proc mog_password {{filename "mog_password.txt"}} {
453
	set password [mog_get_password]
454
455
	set f [open $filename w]
456
	puts -nonewline $f $password
457
	close $f
458
459
	puts "MoG password:"
460
	puts [_mog_format_password $password]
461
	puts "Saved to: $filename"
462
463
	return $password
464
}
465
466
467
# ----------------------------------------------------------------------
468
# mog_password_show
469
#
470
# Extracts and displays the formatted password without saving it.
471
# ----------------------------------------------------------------------
472
473
proc mog_password_show {} {
474
	set password [mog_get_password]
475
476
	puts "MoG password:"
477
	puts [_mog_format_password $password]
478
479
	return $password
480
}
481
482
namespace export toggle_mog_overlay
483
namespace export toggle_mog_editor
484
namespace export mog_password
485
namespace export mog_password_show
486
487
};# namespace mog_overlay
488
489
namespace import mog_overlay::*