Grunker
RPG Codex Ghost
what is your question?I never tried 7 or 8, what’s with the undead? Lemme take a gander.
keighn only does stream-of-consciousness-posting in game threads, you're not meant to interact
what is your question?I never tried 7 or 8, what’s with the undead? Lemme take a gander.
Yes, I have played MM7 choosing the Light Path. It has so big defense, enemy could not hurt my party, and I killed everything using weapons. If I remember correctly.I think plenty of people do. Light Magic is actually pretty powerful when it comes to party buffs, where it lacks the direct damage output of Dark Magic.in 7 does no one choose Light?
it's much more than just QoL
I agree that's why I was taking another shitty hybrid instead of knight when did runs like that. Skipping on a proper caster too and use npcs instead is too much for me though, always autistically take +10 and +15 learning guys in mm7.It allows me more options and is generally weaker than most parties, which is good, because these games are easy enough as it is.
I agree that's why I was taking another shitty hybrid instead of knight when did runs like that. Skipping on a proper caster too and use npcs instead is too much for me though, always autistically take +10 and +15 learning guys in mm7.It allows me more options and is generally weaker than most parties, which is good, because these games are easy enough as it is.
Considering that there have already been 2 mobile games (IIRC) made by some Asian company, I highly doubt they'll change a "winning" formula.Wasn't there some word about a Chinese studio developing a Might & Magic project? Conventional wisdom says it will be a cheap mobile cash grab, and not a proper RPG. But nothing's been confirmed.
I like the script part. How did you write them?So, I've finished the third one.
Note that dead mercenaries are unable to collect the money you owe them.
My observations after playing it are:
1) I enjoyed puzzles in this game: especially the idea that there are in-game solutions to the puzzles, which allows to create much tougher and simultaneously more rewarding puzzles without forcing the player to solve each of them in order to complete the game; yet, when the player gets the solutions, the rewards (except for completing the game) are barely relevant. Also, brute-forcing puzzles was oftentimes feasible but still required some sort of general strategy or idea in order to solve them quickly - like evaluating at least the later part of the expression in the Arachnoid Caves or using the Gray code in the Greywind Castle.
Perhaps the most unique thing about them I noticed was using them as a means of presenting some stories; for example, in the dungeons below Castle Windshield, there are scattered parts of a dwarven rhyme:
The good King Zealot was quite a knave,
To his wife and her lover a box he gave,
His wife's young lover was an orc named Smello,
With hell-hound's breath and hair of yellow,
The queen was shocked by her pine box,
For the open end had golden locks,
Smello's box sent him reeling,
And wooden planks were his last feeling,
The countersign lies in the queen's box.
And the. While I admire the idea, I don't think it was a good riddle:Smellothe next to last verse seemed to imply that Smello was alive when the box was opened, which kind of doesn't make sense.
There were some other scuffed puzzles. Case in point - this riddle in the Cursed Cold Cavern:
With thechain.
Brave to say this in a world where wooden chainmail is the latest fashion trend. I expected the answer to be some sort of mineral that forms bubble-wrap-like structures.
I think the writers should care about precision in their wording when writing such enigmas - or, even better, get people experienced with writing technical documentation to create them.
2) I liked how almost all of the dunegons had some differentiating characteristics: Dragon Cavern featuring dragons and tons of gold; Blackwind dungeon with its invisible teleports; Cathedral of Carnage filled to the brim with spells to learn; or The Arachnoid Cavern with a puzzle forcing you to learn a lot about the structure of a dungeon - if you wanted to get those bonuses to stats while they were still relevant. Also, I don't think I ever saw before dungeons constantly referencing other dungeons; I think it added some tension to exploration - like constantly seeing remarks about the Maze From Hell being quite an unpleasant location.
3) Another pleasing idea in this game is that the player was rewarded for exploration with simplification of exploration (getting swimming or mountaineering; walk on water and other positional spells; portal activation words) - though I wouldn't expect anything else from a game in this series. In most open-world games I saw, the later I was in the game, the more boring traversing the world was; here, I got the Interspacial Transport Box allowing me to traverse the entire world almost instantly in the penultimate dungeon.
4) The game was possible to complete using only a keyboard, and all actions were effectively discrete; the obvious implication is that the game should (and was) quite fast; the less obvious is that it could be scripted - for example, I wrote some simple scripts that pressed key combinations, allowing me to buff automatically (spells + Blistering Heights statues + Well on D1 giving +100 to everything) or repair all the broken equipment after eradication without needing to lift a finger. I would love to see a game allowing me to write scripts using internal information about the state of the game - for example, in case of this game, what is my position+direction on the map, which parts of equipment are broken, or what is the status of my company - so that it would be possible to receive feedback from the game without resorting to processing screenshots.
I preferred this game to the sixth and seventh ones; I didn't feel like the game was punishing me for progress with subsequent tiresome encounters or, even worse, Castle Darkmoor.
This is an example of the actual script - the one I used for buffing:I like the script part. How did you write them?
from pynput.keyboard import Key, Controller
import time
SLEEP_BEFORE_START = 3
SLEEP_TIMES = {
'movement': 0.15,
'yes no': 0.2,
'regular spell': 0.6,
'altar change location': 8,
'teleport no location change' : 1.2,
'teleport change location': 6
}
SORCERER_POSITION = 5
CLERIC_POSITION = 4
SPELL_POSITION_SORCERER = {
'teleport' : 21,
'power shield' : 16,
}
SPELL_POSITION_CLERIC = {
'blessed': 14,
'holy bonus': 15,
'heroism': 17
}
keyboard = Controller()
def banal_click(to_press, time_sleep = SLEEP_TIMES['movement']):
keyboard.press(to_press)
keyboard.release(to_press)
time.sleep(time_sleep)
def move_forward(amount):
to_press = Key.up
for x in range(amount):
banal_click(to_press)
def spell(person=None, spell_number=-1, additional = (), post_wait = SLEEP_TIMES['regular spell']):
spell_button = 'c'
banal_click(spell_button)
if person:
to_press = Key.__getattr__(f'f{person}')
banal_click(to_press)
if spell_number >= 0:
banal_click('n')
for x in range(spell_number-10):
banal_click(Key.down)
if spell_number >= 10:
banal_click('0')
else:
banal_click(spell_number)
banal_click('s')
banal_click(spell_button)
for button in additional:
banal_click(button)
time.sleep(post_wait)
def turn_left():
banal_click(Key.left)
def turn_right():
banal_click(Key.right)
def yes():
banal_click('y', SLEEP_TIMES['yes no'])
def no():
banal_click('n', SLEEP_TIMES['yes no'])
def spell_buffing():
for spell_number in [SPELL_POSITION_CLERIC[_] for _ in ('blessed', 'holy bonus', 'heroism')]:
first_time = False
for key in [Key.f1, Key.f2]:
spell(person=CLERIC_POSITION, spell_number=-1 if first_time else spell_number, additional = (key,), post_wait=SLEEP_TIMES['regular spell']) #Spellcaster's Buffs (No protection from elements
first_time = True
time.sleep(1)
first_time = False
for key in [Key.__getattr__(f'f{_}') for _ in range(1, 7)]:
spell(person=SORCERER_POSITION, spell_number=-1 if first_time else SPELL_POSITION_SORCERER['power shield'], additional = (key,), post_wait=SLEEP_TIMES['regular spell']) #Spellcaster's Buffs (No protection from elements
first_time = True
def write(txt):
for _ in txt:
banal_click(_)
banal_click(Key.enter)
def to_the_well():
turn_right()
turn_right()
spell(SORCERER_POSITION, SPELL_POSITION_SORCERER['teleport'], ('4', Key.enter), SLEEP_TIMES['teleport no location change'])
turn_right()
spell(SORCERER_POSITION, additional = ('9', Key.enter), post_wait = SLEEP_TIMES['teleport change location'])
spell(SORCERER_POSITION, additional = ('5', Key.enter), post_wait = SLEEP_TIMES['teleport no location change'])
spell(SORCERER_POSITION, additional = ('9', Key.enter), post_wait = SLEEP_TIMES['teleport change location'])
spell(SORCERER_POSITION, additional = ('9', Key.enter), post_wait = SLEEP_TIMES['teleport no location change'])
turn_right()
yes()
yes()
yes()
def sequencer(*args):
for x in args:
if type(x) == int:
move_forward(x)
if x == 'l':
turn_left()
if x == 'r':
turn_right()
if type(x) == tuple and x[0] == 's':
spell(**x[1])
if type(x) == str and x[0] == 'y': #yes + amount of times to execute it
amount = 1
if len(x) > 0:
amount = int(x[1:])
for _ in range(amount):
yes()
if x == 'n':
no()
if x=='t': #turn around
turn_right()
turn_right()
def resistances_to_well():
sequencer('t', 1, 'l', 4, 'r', 1, 'y1')
time.sleep(1)
write('air')
time.sleep(SLEEP_TIMES['altar change location'])
to_the_well()
time.sleep(SLEEP_BEFORE_START)
# Start: face North, (14, 6) - Blistering Heights (location after Town Portal / Box use)
spell_buffing()
time.sleep(2)
sequencer(2, 'l', 3, 'y2', 4, 'n', 4, 'y2', 't', 4, 'n', 'l', 4, #Here - elemental resistances
'y2', 't', 4, 'n', 5, 'y2')
time.sleep(2)
resistances_to_well()
from pynput.keyboard import Key, Controller
import time
keyboard = Controller()
time.sleep(3)
keyboard.press(Key.up)
keyboard.release(Key.up)
time.sleep(0.1)
keyboard.press(Key.up)
keyboard.release(Key.up)
time.sleep(0.1)
from pynput.keyboard import Key, Controller
import time
TIME_SLEEP = 0.15
keyboard = Controller()
def banal_click(to_press, time_sleep = TIME_SLEEP):
keyboard.press(to_press)
keyboard.release(to_press)
time.sleep(time_sleep)
sizes = [-1, 17, 0, 17, 0, 0, 16] # This fixes 17 first items of the person under F1, 0 from person under F2 etc. -1 starts the list here, because F0 doesn't exist, and indexing in python starts from 0.
time.sleep(3)
for person_to_repair in range(1, 7):
banal_click(Key.__getattr__(f'f{person_to_repair}'))
for item in range(1, sizes[person_to_repair]+1):
if item == 10:
banal_click(Key.down)
post_item = item if item<10 else item-9
banal_click(f'{post_item}')
banal_click('f')
banal_click('y', 0.3)
Of course. Unpaid labor always comes in handy.Were you using those dead mercs as backpacks? It has been awhile since I messed with III.
Reminds me of the big giant binder me and my brother kept when we were playing mm2 together.The Might & Magic III discussion is very interesting. Thirty years ago, the Thanksgiving holiday fell on November 25, 1993 and I was in the middle of playing Isles of Terra. I found the old notebook I had used to record the adventure, and here is a page from that day.
I always loved how much random information M&M throws at the player during exploration, giving hints of what might be found in later areas.
Awesome! But how do you send the keys to the actual process once it gets the focus ? Or the script just runs in the background arbitrarily pressing keys?This is an example of the actual script - the one I used for buffing:I like the script part. How did you write them?Python:from pynput.keyboard import Key, Controller import time SLEEP_BEFORE_START = 3 SLEEP_TIMES = { 'movement': 0.15, 'yes no': 0.2, 'regular spell': 0.6, 'altar change location': 8, 'teleport no location change' : 1.2, 'teleport change location': 6 } SORCERER_POSITION = 5 CLERIC_POSITION = 4 SPELL_POSITION_SORCERER = { 'teleport' : 21, 'power shield' : 16, } SPELL_POSITION_CLERIC = { 'blessed': 14, 'holy bonus': 15, 'heroism': 17 } keyboard = Controller() def banal_click(to_press, time_sleep = SLEEP_TIMES['movement']): keyboard.press(to_press) keyboard.release(to_press) time.sleep(time_sleep) def move_forward(amount): to_press = Key.up for x in range(amount): banal_click(to_press) def spell(person=None, spell_number=-1, additional = (), post_wait = SLEEP_TIMES['regular spell']): spell_button = 'c' banal_click(spell_button) if person: to_press = Key.__getattr__(f'f{person}') banal_click(to_press) if spell_number >= 0: banal_click('n') for x in range(spell_number-10): banal_click(Key.down) if spell_number >= 10: banal_click('0') else: banal_click(spell_number) banal_click('s') banal_click(spell_button) for button in additional: banal_click(button) time.sleep(post_wait) def turn_left(): banal_click(Key.left) def turn_right(): banal_click(Key.right) def yes(): banal_click('y', SLEEP_TIMES['yes no']) def no(): banal_click('n', SLEEP_TIMES['yes no']) def spell_buffing(): for spell_number in [SPELL_POSITION_CLERIC[_] for _ in ('blessed', 'holy bonus', 'heroism')]: first_time = False for key in [Key.f1, Key.f2]: spell(person=CLERIC_POSITION, spell_number=-1 if first_time else spell_number, additional = (key,), post_wait=SLEEP_TIMES['regular spell']) #Spellcaster's Buffs (No protection from elements first_time = True time.sleep(1) first_time = False for key in [Key.__getattr__(f'f{_}') for _ in range(1, 7)]: spell(person=SORCERER_POSITION, spell_number=-1 if first_time else SPELL_POSITION_SORCERER['power shield'], additional = (key,), post_wait=SLEEP_TIMES['regular spell']) #Spellcaster's Buffs (No protection from elements first_time = True def write(txt): for _ in txt: banal_click(_) banal_click(Key.enter) def to_the_well(): turn_right() turn_right() spell(SORCERER_POSITION, SPELL_POSITION_SORCERER['teleport'], ('4', Key.enter), SLEEP_TIMES['teleport no location change']) turn_right() spell(SORCERER_POSITION, additional = ('9', Key.enter), post_wait = SLEEP_TIMES['teleport change location']) spell(SORCERER_POSITION, additional = ('5', Key.enter), post_wait = SLEEP_TIMES['teleport no location change']) spell(SORCERER_POSITION, additional = ('9', Key.enter), post_wait = SLEEP_TIMES['teleport change location']) spell(SORCERER_POSITION, additional = ('9', Key.enter), post_wait = SLEEP_TIMES['teleport no location change']) turn_right() yes() yes() yes() def sequencer(*args): for x in args: if type(x) == int: move_forward(x) if x == 'l': turn_left() if x == 'r': turn_right() if type(x) == tuple and x[0] == 's': spell(**x[1]) if type(x) == str and x[0] == 'y': #yes + amount of times to execute it amount = 1 if len(x) > 0: amount = int(x[1:]) for _ in range(amount): yes() if x == 'n': no() if x=='t': #turn around turn_right() turn_right() def resistances_to_well(): sequencer('t', 1, 'l', 4, 'r', 1, 'y1') time.sleep(1) write('air') time.sleep(SLEEP_TIMES['altar change location']) to_the_well() time.sleep(SLEEP_BEFORE_START) # Start: face North, (14, 6) - Blistering Heights (location after Town Portal / Box use) spell_buffing() time.sleep(2) sequencer(2, 'l', 3, 'y2', 4, 'n', 4, 'y2', 't', 4, 'n', 'l', 4, #Here - elemental resistances 'y2', 't', 4, 'n', 5, 'y2') time.sleep(2) resistances_to_well()
Note that I have to start at a certain place ((14, 6) - Blistering Heights) facing north. SLEEP_BEFORE_START determines the time I need to switch context to a game - the only thing this script does is pressing buttons and waiting. It's probably possible to send keys directly to the process, but I don't think it would be very portable. All the sleeping times were subjected to tinkering.
Now, obviously, this code is not very polished. I started by writing something likePython:from pynput.keyboard import Key, Controller import time keyboard = Controller() time.sleep(3) keyboard.press(Key.up) keyboard.release(Key.up) time.sleep(0.1) keyboard.press(Key.up) keyboard.release(Key.up) time.sleep(0.1)
And then gradually moved all the repeating parts into functions, and some repeating hardcoded variables into constants. There are some Python hacks - like using __getattr__ to get an attribute of the Key class from a string or dictionary unpacking (**some_list) to easily pass a dictionary as a list of arguments to a function - but you can avoid them (albeit the code will be even less concise).
The only visible and obvious problems are the fact that I need to start facing the exact direction and know the position of a spell in a list of each character; the lack of feedback gets more frustrating when it comes to repair:Python:from pynput.keyboard import Key, Controller import time TIME_SLEEP = 0.15 keyboard = Controller() def banal_click(to_press, time_sleep = TIME_SLEEP): keyboard.press(to_press) keyboard.release(to_press) time.sleep(time_sleep) sizes = [-1, 17, 0, 17, 0, 0, 16] # This fixes 17 first items of the person under F1, 0 from person under F2 etc. -1 starts the list here, because F0 doesn't exist, and indexing in python starts from 0. time.sleep(3) for person_to_repair in range(1, 7): banal_click(Key.__getattr__(f'f{person_to_repair}')) for item in range(1, sizes[person_to_repair]+1): if item == 10: banal_click(Key.down) post_item = item if item<10 else item-9 banal_click(f'{post_item}') banal_click('f') banal_click('y', 0.3)
Now: why did I need the hardcoded amount of items for each character? Because if I try to repair an item that doesn't exist, the script will get stuck (I'll get the message "Fix which item" that the script can't see, and I'll need to enter a digit).
This game would be easy to automate almost completely - because of the discrete nature of all actions - if one could get some response from the game that could be processed like a plain text.
All I do is execute the script (either from the command line or in any other way), switch the focus (I've got SLEEP_BEFORE_START seconds - set to 3 here) to the game (by clicking on its window), and... that's it.But how do you send the keys to the actual process once it gets the focus ? Or the script just runs in the background arbitrarily pressing keys?