Lonely Vazdru
Pimp my Title
And 13 years after its creation, the holy Might and Magic thread reached 300 pages. John Van Caneghem looked down upon its creation's legacy and saw that it was good. And there was much rejoicing...
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?
Noice, thanks for the detailsAll 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?
The script acts exactly as if I was pressing the keys. In the current implementation, I can't change the focus while the script executes - this would require some magic with finding certain processes for the banal_click function, I don't know whether the pynput module allows that, and it probably wouldn't be portable (I ran this game on Wine under Linux).
And, by the way - deleting a team member overwrites the last save - so I recommend backing up your saves in this game.
I've never played MM1, but I've beaten MM2. What exactly is your question?Hello everyone, sorry for being a noob, I just signed up and I didn't find any presentation or generic topic so I'm writing directly here.
I was browsing the internet asking for information about Might and Magic 1 and 2 and they advised me to come here and ask.....and here I am! So what direction should I go than making posts about M&M 1&2?
- Merchant Pass and King Pass: should you simply explore the world until you find them?