Bought E1 on steam a while ago, just recently started playing.
Got quickly annoyed with it, so decided to hack the savegames to cheat. This is usually a bad sign: once I start hacking a game, it's running low on my playability meter, at which point it's not long left on my HDD. The exception is easily-script-moddable games, like Bethsoft stuff and Furcadia.
So I quickly noticed that there was scripty stuff in the savegames, and maps. I had figured most of the obvious parts of the char file, listed out the values for a lot of the list-values, etc. But this morning I thought "this format's so open, I'm
sure someone else must have documented this stuff - I'm reinventing the wheel here". However, when I Googled it, I never expected to find something THIS impressive!
What I did last night is just a tiny subset of what you've found already. I spotted a couple tiny bits where my notes differ from or add to what's on your scripting page for E1, though, since I ran "strings" on the exe file itself. Make sure to include unicode, since the commandlist in there is in 2-byte unicode. Most scripts seem to be ascii though.
So, those differences, I describe below.
activate_qt N - I'm assuming the param is 1 for the first in the list, and so on down?
effect (name) N - Here's a list of effect names I've seen in various contexts. Where I've seen them used with this command, the values are given in braces. No others are tested to work:
- Air Shielded
- Blessed
- Cat's Eyes (2)
- Chameleon
- Charmed
- Elemental Armor
- Enchanted Weapon
- Enkindled Weapon
- Entangled
- Gravedigger's Flame
- Greater Protection (3)
- Haste (I=2, II=4 III=6)
- Invisible (I=2, II=4 III=6)
- Keensight (3)
- Leatherskin (I=2, II=4 III=6)
- Mana Fortified (3)
- Nimbleness (3)
- Off-Balance
- Ogre Strength (3)
- Paralyzed
- Poisoned
- Predator Sight (3)
- Reveal Map
- Scared
- Stoneskin (2)
- Stunned
all_quests - not tested. Debugging command?
areacheck - Your writeup says it's not used in book 1, but I see it used in two places in scripts in the exe. This one I assume is a script that accompanies the endgame text:
Code:
add_gold 83510 ; screen_fade_out ; map_port 52 ; move_player 7018 ; screen_fade_in ; areacheck
updatezones - This command is often included with the "areacheck" command, whatever that does. I'm assuming they both rationalise the map in some way when you teleport, such as this one from a conversation script:
Code:
screen_fade_out ; map_port (Darkford) ; updatezones ; areacheck ; screen_fade_in
map_port - Contrary to your notes, the only place I've seen a
any string parameter that isn't in (braces) is where the string is at the end of a script, so it's terminated by the \r\n string terminator, and hence (I assume) doesn't need the braces to delimit the string. I've seen map_port with both numeric map number params like "map_port 52", and with params in parentheses, like the above "map_port (Darkford)".
book N / learn_book NI came up with this list, which I suspect is not exhaustive. Note that the parameters for book numbers and the learn_book numbers are NOT the same, though they are both called "booknum" in your writeup. Learn_book numbers are not the skill number either - they're the second number in the list below. Somewhere in the savegame, there'll be a set of fields (maybe a bitfield?) that stores which books have been "learnt", so you can't learn them twice. Not found this, but I suspect the sfx bitfield. 15 books, so 15 or 16 bits or bytes or ints, somewhere.
Book list:
1 The holy Tasslar
2 Of Men And Giants
3 Sealed Note
4 The Alchemist's Cookbook I
5 The Alchemist's Cookbook III (yes, out of order)
6 The Alchemist's Cookbook II
9 Torn Page
10 Journal Entry
13 Secrets of Transference Circuits
14 2nd Sealed note
15 37 The Legendary Swordsman
16 35 Keyholes and Tumblers
17 36 Bones to Splinters
18 32 the Greatest Ranger
19 41 Embrace the Night
20 38 Elements of Magick
21 43 Magicka Divine
22 39 Mapping Your World
23 42 Hidden Dangers and Treasures
24 40 Buying and Selling
25 34 Light Armor Field Guide
26 44 Heavy Armor Field Guide
27 33 It's a Trap!
28 31 The Art of Brewing
29 3rd Sealed note
30 Bloodstained Note
33 30 The Lumberjack
34 Imbuing Your Equipment
35 A History of Thaermore
36 Letter to Penelope
37 Mercenary's Note
38 The Goblins of Thaermore
39 The Orakur Are Watching
40 The Crimson Sky
41 Benny and the Dragon
42 Incriminating Letter
43 A World of Wonder
44 The Jewel of Thaermore
46 Sam's Guide to Demon Oil
48 Portal Instructions
cond_not_quest N - This is from 24.map: "cond_not_quest 25 (-1) ; trigger_talk 77" - I'm thinking that this shows two interesting things. First, like strings, negative numbers need to be quoted/delimited with braces, at least if they aren't the last parameter (which may mean that more commands can have negative numbers than previously thought?). Secondly, some kind of inactive quests (unstarted? killed? both?) seem to have a value of -1, so this handy code tests for that.
npc_disp_change 59 2 - change NPC 59's disposition to 2. I think 2 is attack, but I've only seen it on Lilith's (59) and Grimulk's (87) conversation options, when they go hostile. I've also seen 0 for npc 72, which also seems to be attack? Your text only lists 0 (hostile) and 1 (friendly), so I wonder what 2 is.
poison 200 - poison with this power level. I suspect 200 means "incurable".
npc_die 58 - kill the numbered NPC.
rename_item (SRC NAME) (DEST NAME) - rename an item (or, change all of an item to another type?). Since this is in conversation scripts, it probably works globally, renaming the item type. But it might just work on the PC's inventory. It's only used with unique, quest items, though, with a check to see that the player has them, so can't tell.
screen_fade_in - go from faded to black, to show play screen again. Used when teleporting, etc. No params.
screen_fade_out - fade play screen to black.
special_event 1 - trigger some special event? This may not be a command, as it's not included in the command list. But it looks like it is. May be a script ID or soemthing
strip_items - Remove all the player's items? Does this include script items? If so, are they placed in some chest? Takes no params, so it's not specifying a container. Only happens in conversation, so maybe the talking NPC gets the items? Used in one place:
Code:
screen_fade_out ; move_player 5753 ; strip_items ; message (You wake laying upon cold dirt. The smell of death is all around you...) ; screen_fade_in
The following commands most likely are only usable from conversation scripts.cleric_dehex - cleric remove curse action, from conversation options.
cleric_heal - cleric heal action, from conversation options.
init_trade (NAME) - trade with NPC NAME
rent_room 15 4690 - rent room from innkeeper dialog. I'm guessing price and room location? Values seen:
Porter: 15 4690
Eeru: rent_room 30 8959 (if you are polite)
Eeru: rent_room 50 8959 (if you aren't)
teach_skill (Garrett) 7 - Garrett teaches skill 7. Skill list seems to be the same as everywhere else:
01 Alchemy
02 Divination
03 Elemental
04 Light Armor
05 Heavy Armor
06 Shields
07 Cartography
08 Dodge
09 Hide in shadows
10 Lore
11 Meditation
12 Mercantile
13 Move silently
14 Pick Locks
15 Skullduggery
16 Spot hidden
17 Survival
18 Unarmed Combat
19 Bludgeoning weapons
20 Bow weapons
21 Cleaving weapons
22 Short bladed weapons
23 Swords
24 Thrown weapons
The following commands aren't used anywhere that I can find, but are included in the exe's list of commands. No indication of what params they support. The names are suggestive, but obviously they all need testing.cond_detected - run following code if the player has been detected by foes?
convert - convert an item to another type, maybe?
curse - maybe works like "disease"?
delay - ?
full_restore - restore full mana? Or mana and health?
remove_barrier - what's a barrier?
spell - maybe works like "disease"?
trap - maybe works like "disease"?
There are two messages that suggest there may be cheat/debugging codes: "You've just learned every spell and became a Master of all Skills!" and "Reset this map?" - since these are right by "Press any key to continue" and "You decide to stand passively for a moment", I suspect they're input related, so probably cheat codes. There may also be some way to turn on "developer mode", since elsewhere there's the message "Developer Mode LOCKED OBJECT UNLOCKED!" - however, just as with the unused commands, there's no saying whether any of that was ever compiled in or made bug-free.
If any of that was useful, I can maybe poke around in the E2 string table, too, if that'd be helpful.
Other notes:
Code:
# More Unknowns
for i in range(17):
self.unknown.iblock1.append(self.df.readint())
for i in range(5):
self.unknown.ssiblocks1.append(self.df.readstr())
self.unknown.ssiblocks2.append(self.df.readstr())
self.unknown.ssiblocki.append(self.df.readint())
self.unknown.extstr1 = self.df.readstr()
self.unknown.extstr2 = self.df.readstr()
To me, this feels more like:
Code:
# More Unknowns
for i in range(16):
self.unknown.iblock1.append(self.df.readint())
for i in range(6):
self.unknown.ssiblocki.append(self.df.readint())
self.unknown.ssiblocks1.append(self.df.readstr())
self.unknown.ssiblocks2.append(self.df.readstr())
16 is a rounder number, and (number, string, string), seems more sensible to me.
So that gets rid of two unknowns for you: extstr1 and extstr2.
also:
Code:
for i in range(4):
self.fxblock.append(self.df.readint())
# An unknown, seems to be a multiple of 256
self.unknown.anotherint = self.df.readint()
# Character profile pic (multiple of 256, for some reason)
self.picid = self.df.readint()
# Disease flag
self.disease = self.df.readint()
# More Unknowns. Apparently there's one 2-byte integer in here, too.
self.unknown.shortval = self.df.readshort()
self.unknown.emptystr = self.df.readstr()
feels more like:
Code:
# 5 byte bitfield.
self.unknown.fxbits = self.df.readint()
# Always 3F?
self.unknown.fx3f = self.df.readbyte()
# 4 unknown ints
for i in range(4):
self.fxblock.append(self.df.readbyte())
# An unknown.
self.unknown.anotherint = self.df.readint()
# Character profile pic.
self.picid = self.df.readint()
# Disease flag
self.disease = self.df.readint()
# More Unknowns. Apparently there's a byte in here, too.
self.unknown.byteval = self.df.readbyte()
self.unknown.emptystr = self.df.readstr()
That is, rather than:
08 52 B8 1E
3F 0F 00 00
00 5F 00 00
00 5A 00 00
00 01 00 00 - int multiple of 256
00 00 00 00 - int profile pic multiple of 256
00 00 00 00 - int disease - is this x256 - see constantsb1.py
00 00 - short
0D 0A - string
To me, it looks like:
08 52 B8 1E - sfx/lighting bitfield?
3F - Always 3F?
0F 00 00 00 - int
5F 00 00 00 - int
5A 00 00 00 - int
01 00 00 00 - int
00 00 00 00 - int profile pic
00 00 00 00 - int disease - need to edit constantsb1.py, see below.
00 - byte
0D 0A - string
That then changes constantsb1.py's code from:
diseasetable = {
0x0200: 'Dungeon Fever',
0x0400: 'Rusty Knuckles',
0x0800: 'Eye Fungus',
0x1000: 'Blister Pox',
0x2000: 'Insanity Fever',
0x4000: 'Fleshrot',
0x8000: 'Cursed'
}
to:
diseasetable = {
0x02: 'Dungeon Fever',
0x04: 'Rusty Knuckles',
0x08: 'Eye Fungus',
0x10: 'Blister Pox',
0x20: 'Insanity Fever',
0x40: 'Fleshrot',
0x80: 'Cursed'
}
No idea whether any of these help you decode the structure any, though.