MMExtension v2.3 HelpBack
Hover your mouse over empty space on the left side of the page to see the table of contents.
Contents
- General Information
- Examples
- Evt Commands
- evt.EnterHouse
- evt.PlaySound
- evt.MoveToMap
- evt.OpenChest
- evt.FaceExpression
- evt.DamagePlayer
- evt.SetSnow
- evt.SetTexture
- evt.SetTextureOutdoors
- evt.ShowMovie
- evt.SetSprite
- evt.Cmp
- evt.SetDoorState
- evt.Add
- evt.Subtract
- evt.Set
- evt.SummonMonsters
- evt.CastSpell
- evt.SpeakNPC
- evt.SetFacetBit
- evt.SetFacetBitOutdoors
- evt.SetMonsterBit
- evt.Question
- evt.StatusText
- evt.SetMessage
- evt.SetLight
- evt.SimpleMessage
- evt.SummonObject
- evt.ForPlayer
- evt.SetNPCTopic
- evt.MoveNPC
- evt.GiveItem
- evt.ChangeEvent
- evt.CheckSkill
- evt.SetNPCGroupNews
- evt.SetMonsterGroup
- evt.SetNPCItem
- evt.SetNPCGreeting
- evt.CheckMonstersKilled
- evt.ChangeGroupToGroup
- evt.ChangeGroupAlly
- evt.CheckSeason
- evt.SetMonGroupBit
- evt.SetChestBit
- evt.FaceAnimation
- evt.SetMonsterItem
- evt.StopDoor
- evt.CheckItemsCount
- evt.RemoveItems
- evt.Jump
- evt.IsTotalBountyInRange
- evt.CanPlayerAct
- evt.RefundChestArtifacts
- Events
- Structs\After\ Spells.lua
- Core\ events.lua
- CalcSpellDamage
- WalkToMap
- DeathMap
- NewGameMap
- NewGameDefaultParty
- NewGameClearParty
- LoadedRosterTxt
- GameInitialized0
- GameInitialized1
- GameInitialized2
- CanSaveGame
- CanCastLloyd
- IsUnderwater
- FogRange
- PopulateQuestLog
- PopulateAutonotesList
- PopulateAwardsList
- MonsterInfoPictureChanged
- WindowMessage
- KeyDown
- KeyUp
- PostRender
- Action
- MenuAction
- ExitMapAction
- KeysFilter
- BeforeSaveGame
- BeforeNewGameAutosave
- AfterNewGameAutosave
- AfterSaveGame
- SkyBitmap
- LoadSavedMap
- PlayMapTrack
- ShowMovie
- PlaySound
- FaceAnimation
- CalcStatBonusByItems
- CalcStatBonusByMagic
- CalcStatBonusBySkills
- GetSkill
- GetAttackDelay
- CalcDamageToPlayer
- GetMerchantTotalSkill
- GetDisarmTrapTotalSkill
- GetDiplomacyTotalSkill
- GetPerceptionTotalSkill
- GetLearningTotalSkill
- DoBadThingToPlayer
- GetStatisticEffect
- UseMouseItem
- CanLearnSpell
- Regeneration
- ModifyItemDamage
- GenerateItem
- ItemGenerated
- MonsterKilled
- MonsterKillExp
- ItemAdditionalDamage
- CalcDamageToMonster
- PickCorpse
- CastTelepathy
- CanMonsterCastSpell
- MonsterChooseAction
- MonsterAttacked
- AfterMonsterAttacked
- PlayerAttacked
- AfterPlayerAttacked
- Core\ evt.lua
- Core\ main.lua
- Core\ npc.lua
- EnterAnyNPC
- EnterNPC
- ShowNPCTopics
- ShowHiredNPCTopics
- DrawNPCGreeting
- SpeakWithMonster
- CanExitNPC
- CanExitStreetNPC
- CanExitHiredNPC
- ExitNPC
- ExitAnyNPC
- ExitHouseScreen
- CanTeachSkillMastery
- CanTempleHealPlayer
- GetShopItemTreatment
- CanShopOperateOnItem
- ShopItemsGenerated
- GuildItemsGenerated
- HouseMovieFrame
- ArcomageSetup
- ArcomageText
- ArcomageWin
- NewBountyHunt
- BountyHuntDone
- SetMapNoNPC
- PopulateNPCDialog
- AfterPopulateNPCDialog
- PopulateHouseDialog
- PopulateArcomageDialog
- PopulateDisplayInventoryDialog
- AfterPopulateHouseDialog
- PopulateLearnSkillsDialog
- AfterPopulateLearnSkillsDialog
- BeforeDrawDialogs
- BeforeDrawDialog
- DrawDialog
- AfterDrawDialog
- AfterDrawDialogs
- AfterDrawNoDialogs
- NewDialog
- BeforeDestroyDialog
- DestroyDialog
- AfterDestroyDialog
- BeforeShowOODialog
- AfterShowOODialog
- CloseOODialog
- DrawProgressBar
- HideProgressBar
- Core\ timers.lua
- Modules\ Faces.lua
- Modules\ PaperDoll.lua
- General Functions
- Structs\After\ 00 Mem Functions.lua
- Structs\After\ Backup.lua
- Structs\After\ Dialogs.lua
- Structs\After\ Draw.lua
- Structs\After\ Functions.lua
- SplitSkill
- JoinSkill
- XYZ
- ClearConsoleEvents
- Message
- Question
- ReplaceNPCTopic
- SuppressSound
- ExitScreen
- ReloadHouse
- SwitchHouseMovie
- DrawSimpleMessage
- HouseMessage
- AddGoldExp
- TakeItemFromParty
- CheckMonstersKilled
- EnumAvailableSkills
- RGB
- StrLeft
- StrRight
- StrColor
- PcxCache
- IconCache
- SummonMonster
- SummonItem
- RebildIDList
- ChangeSprite
- CreateSprite
- MoveModel
- Structs\After\ LocalizationAndQuests.lua
- TakeQuestOperation
- Quests
- GenerateLocalization_BakFiles
- GameLocalizationIgnore
- GameLocalizationSchema
- QuestNPC
- vars.Quests
- vars.QuestAwards
- vars.QuestAutonotes
- LocalizeAll
- Localize
- GenerateQuestsLocalization
- GenerateLocalization
- GenerateGameLocalization
- ShowQuestEffect
- ShowAwardEffect
- ShowAutonoteEffect
- AutoQuest
- AutoAward
- AutoAutonote
- GetLocalName
- Autonote
- AddAutonote
- CheckAutonote
- FindAutonote
- UpdateNPCQuests
- QuestBranch
- QuestBranchScreen
- ExitQuestBranch
- GetQuestBranchStack
- Quest
- Greeting
- NPCTopic
- KillMonstersQuest
- QCheck
- Structs\After\ Spells.lua
- Structs\After\ Text Tables.lua
- Core\ Common.lua
- Core\ Debug.lua
- Core\ ErrorFunction.lua
- Core\ RSFunctions.lua
- Core\ RSMem.lua
- Core\ RSPersist.lua
- Core\ dump.lua
- Core\ events.lua
- Core\ evt.lua
- Core\ evtdeco.lua
- Core\ main.lua
- Core\ npc.lua
- Core\ timers.lua
- Modules\ Faces.lua
- Modules\ KeepLogs.lua
- Modules\ PaperDoll.lua
- Structures
- Game
- Party
- Map
- Mouse
- Screen
- structs.ActionItem
- structs.Arcomage
- structs.ArcomageAction
- structs.ArcomageActions
- structs.ArcomageCard
- structs.ArcomagePlayer
- structs.BSPNode
- structs.BaseBonus
- structs.BaseLight
- structs.BitmapsLod
- structs.BlvHeader
- structs.Button
- structs.CurrentTileBin
- structs.CustomLods
- structs.DChestItem
- structs.DecListItem
- structs.DialogLogic
- structs.Dlg
- structs.EventLine
- structs.Events2DItem
- structs.FaceAnimationInfo
- structs.FacetData
- structs.FloatVector
- structs.Fnt
- structs.FogChances
- structs.GameClassKinds
- structs.GameClasses
- structs.GameRaces
- structs.GeneralStoreItemKind
- structs.HistoryTxtItem
- structs.HouseMovie
- structs.IFTItem
- structs.Item
- structs.ItemsTxtItem
- structs.LanguageLod
- structs.LanguageLodFile
- structs.LloydBeaconSlot
- structs.Lod
- structs.LodBitmap
- structs.LodFile
- structs.LodPcx
- structs.LodRecord
- structs.LodSprite
- structs.LodSpriteD3D
- structs.LodSpriteLine
- structs.MapChest
- structs.MapDoor
- structs.MapExtra
- structs.MapFacet
- structs.MapLight
- structs.MapModel
- structs.MapMonster
- structs.MapNote
- structs.MapObject
- structs.MapOutline
- structs.MapOutlines
- structs.MapRoom
- structs.MapSprite
- structs.MapStatsItem
- structs.MapVertex
- structs.MissileSetup
- structs.ModelFacet
- structs.ModelVertex
- structs.MonListItem
- structs.MonsterAttackInfo
- structs.MonsterKind
- structs.MonsterSchedule
- structs.MonstersTxtItem
- structs.MoveToMap
- structs.NPC
- structs.NPCNewsItem
- structs.NPCProfTxtItem
- structs.OODialogManager
- structs.ObjListItem
- structs.ObjectRef
- structs.OdmHeader
- structs.OverlayItem
- structs.PFTItem
- structs.PatchOptions
- structs.Player
- structs.ProgressBar
- structs.SFT
- structs.SFTItem
- structs.ShopItemKind
- structs.SoundsItem
- structs.SpawnPoint
- structs.SpcItemsTxtItem
- structs.SpellBuff
- structs.SpellEffect
- structs.SpellInfo
- structs.SpellsTxtItem
- structs.SpritesLod
- structs.StartStat
- structs.StdItemsTxtItem
- structs.TFTItem
- structs.TileItem
- structs.TilesetDef
- structs.TownPortalTownInfo
- structs.TravelInfo
- structs.Weather
- Constants
- const.*
- const.AIState
- const.Actions
- const.ArcomageIf
- const.CharScreens
- const.ChestBits
- const.Class
- const.Condition
- const.Damage
- const.DlgID
- const.ExitMapAction
- const.FaceAnimation
- const.FacetBits
- const.HouseScreens
- const.HouseType
- const.InfoDialog
- const.ItemSlot
- const.ItemType
- const.Keys
- const.MonsterAction
- const.MonsterBits
- const.MonsterBonus
- const.MonsterBuff
- const.MonsterKind
- const.MonsterPref
- const.NPCProfession
- const.ObjectRefKind
- const.PartyBuff
- const.PlayerBuff
- const.Race
- const.Screens
- const.Season
- const.Skills
- const.Spells
- const.SpriteBits
- const.Stats
- evt.Players
- evt.VarNum
General Information
You will need MMArchive. mm8leveleditor and MM Map Viewer may also come in handy.Extract all *.txt files from appropriate LOD archive: icons.lod in MM6, events.lod in MM7, EnglishT.lod in MM8. You will need them.
Easiest thing to start with is editing text tables with TxtEdit. In addition to *.txt files that you've extracted from LOD archives MMExtension will create some tables in Data\Tables folder on first launch. You can change any values in them and add new lines to many of them.
Lua is a simple, yet powerful scripting language. You can read about it here, here and here. MMExtension exposes all standard Lua and LuaJIT libraries.
I used to use SciTE to edit Lua scripts before I switched to Sublime Text. Here are some of my settings for SciTE:
Open SciTEGlobal.properties. Find and set up these settings: tabsize=2 indent.size=2 braces.autoclose=0 Find user.shortcuts=\ delete line with Alt+X add line: Ctrl+Shift+Z|IDM_REDO|\ add line: Ctrl+R|IDM_REPLACE|\ |
All scripts are located in Scripts directory. Subfolders determine when scripts get loaded and unloaded:
Subfolder | Description |
Core | MMExtension core scripts. Don't put your scripts here. |
General | These scripts are loaded before the game starts and are never unloaded. |
Global | These scripts are loaded when a new game is started or a saved game is loaded and are unloaded when user exits to main menu or loads another game. This is a good place for most of your scripts that don't belong to specific maps. Quests scripts in particular. |
Localization | These scripts and text files are loaded on game start just before "General" scripts and can be reloaded with ReloadLocalization() function. They should be used for mods localization. |
Maps | These scripts correspond to maps. For example, scripts named oute3.lua and some_name.oute3.lua would be loaded when
oute3.odm map (New Sorpigal) is loaded and unloaded when it is left. Map scripts can have accompanying global scripts next to them: oute3.global.lua and some_name.oute3.global.lua respectively. These scripts behave as the ones in Global folder, but are loaded after them and share local autonotes with their respective map script. |
Modules | These scripts aren't loaded automatically. Instead they can be loaded with require function. |
Structs | These scripts are intended for structs definitions. These things are low-level, so I won't describe them yet. If you find some interesting address with ArtMoney or disassembler, contact me. |
You can create your own subfolders. Scripts in them won't be loaded automatically and
won't be unloaded either.
The word 'unload' means removing all events of a script. As a consequence of global and maps scripts being unloaded and then loaded again, you can change them and test changes by simply reloading a saved game. No need to exit Might and Magic for this. |
Debug console is a convenient way to quickly test things. Press Ctrl+F1 to open it. You can write any script here. For example, dump(Party[0].Stats) would output first player's stats.
Press Ctrl+Enter to execute a script.
Press Ctrl+E to repeat the last script.
Press Esc to cancel debug console.
Variables used by your scripts can be of 4 types:
Local variables, declared with the word local in Lua. They can be used in the place where they were declared (i.e. function, script or code block) and aren't stored is saved games.
Global variables. They can be accessed from anywhere, but aren't stored in saved games.
Variables in vars table. They can be accessed from anywhere and are stored in savegames.
Variables in mapvars table. They belong to current map. When a new map is entered the appropriate variables table is set as mapvars table. They are stored in savegames. When a map is refilled, new mapvars table is created, but the old one is accessible at this moment as Map.Refilled.
Hello world!
Let's first use the debug console. In the game press Ctrl+F1 and paste this script:Message("Hello world!")
To make your first game script create out01.lua file (or oute3.lua for MM6) in Scripts\Maps directory and write this text there:
MessageBox("Hello world!")
To show a native MM message you can use a script like this:
Game.NeedRedraw = true -- I remember having problems with minimap not getting drawn
Sleep(1) -- sleep for 1 tick to let it be drawn
Message("Hello world!")
Sleep(1) -- sleep for 1 tick to let it be drawn
Message("Hello world!")
Decompiled Scripts
Original Might and Magic scripts are stored in binary format in files with extension .evt. MMExtension can decompile these scripts. Get decompiled scripts here.Decompiled scripts come in the form of Lua scripts and also pseudo code that directly corresponds to binary format.
Just so you know, here are the differences between pseudo code commands and real MMExtension commands:
1) "evt." isn't written in them.
2) They use jumps instead of if-then and loops. They have their lines numbered.
3) They almost always supply parameter names in calls to Evt functions. You can do the same, but you can also use calls without parameter names.
4) Some commands are replaced by better analogs in MMExtension. For example, you cannot use evt.OnTimer command, instead you should use Timer function.
5) There are other differences, like declaration of events.
Here's an example (a Lua script):
Game.MapEvtLines:RemoveEvent(60) -- remove original event
evt.map[60] = function()
local i
if evt.Cmp("MapVar0", 4) then
evt.StatusText(5) -- "Nothing here"
else
evt.Add("MapVar0", 1)
evt.StatusText(4) -- "You found something!"
i = Game.Rand() % 6
if i == 1 then
evt.Add("Inventory", 1) -- "Longsword"
elseif i == 2 then
evt.Add("Inventory", 15) -- "Dagger"
elseif i == 3 then
evt.Add("Inventory", 58) -- "Club"
elseif i == 4 then
evt.Add("Inventory", 161) -- "Phirna Root"
elseif i == 5 then
evt.Add("Inventory", 309) -- "Inferno"
else
evt.Add("Inventory", 94) -- "Cloth Hat"
end
end
end
evt.map[60] = function()
local i
if evt.Cmp("MapVar0", 4) then
evt.StatusText(5) -- "Nothing here"
else
evt.Add("MapVar0", 1)
evt.StatusText(4) -- "You found something!"
i = Game.Rand() % 6
if i == 1 then
evt.Add("Inventory", 1) -- "Longsword"
elseif i == 2 then
evt.Add("Inventory", 15) -- "Dagger"
elseif i == 3 then
evt.Add("Inventory", 58) -- "Club"
elseif i == 4 then
evt.Add("Inventory", 161) -- "Phirna Root"
elseif i == 5 then
evt.Add("Inventory", 309) -- "Inferno"
else
evt.Add("Inventory", 94) -- "Cloth Hat"
end
end
end
Now it's time for a script doing the same thing (if you play the game from start with it), but written using more fitting MMExtension features:
local TXT = Localize{
NothingHere = "Nothing here",
FoundSomething = "You found something!",
}
local items = {1, 15, 58, 161, 309, 94}
Game.MapEvtLines:RemoveEvent(60) -- remove original event
evt.map[60] = function()
if mapvars.Shelf60 == 4 then
return Game.StatusText(TXT.NothingHere)
end
mapvars.Shelf60 = (mapvars.Shelf60 or 0) + 1
Game.StatusText(TXT.FoundSomething)
local i = math.random(1, #items)
evt.Add("Inventory", items[i]) -- alternatively: evt.GiveItem{Id = items[i]}
end
NothingHere = "Nothing here",
FoundSomething = "You found something!",
}
local items = {1, 15, 58, 161, 309, 94}
Game.MapEvtLines:RemoveEvent(60) -- remove original event
evt.map[60] = function()
if mapvars.Shelf60 == 4 then
return Game.StatusText(TXT.NothingHere)
end
mapvars.Shelf60 = (mapvars.Shelf60 or 0) + 1
Game.StatusText(TXT.FoundSomething)
local i = math.random(1, #items)
evt.Add("Inventory", items[i]) -- alternatively: evt.GiveItem{Id = items[i]}
end
You can decompile scripts by yourself if you ever need to. To do so, extract all *.evt and *.str files from the same LOD archive you used to extract *.txt files. Create "Decompile" sub-folder inside the game folder, put these files there and run this simple script:
local dir = "Decompile/"
for f in path.find(dir.."*.evt") do
evt.Decompile(f, 0, dir.."Scripts/"..path.setext(path.name(f), ".lua"))
evt.Decompile(f, 0, dir.."Scripts/txt/"..path.setext(path.name(f), ".txt"), true)
end
for f in path.find(dir.."*.evt") do
evt.Decompile(f, 0, dir.."Scripts/"..path.setext(path.name(f), ".lua"))
evt.Decompile(f, 0, dir.."Scripts/txt/"..path.setext(path.name(f), ".txt"), true)
end
Note: You can't turn a decompiled script back into EVT file, even if it's in text form. In case you do need to edit raw EVT files, you can use my 010Editor Templates.
Short functions syntax
MMExtension features one language extension – short syntax for functions declaration. Here's an example:sum = |x, y| x + y
-- translates into
sum = function(x, y)
return x + y
end
-- translates into
sum = function(x, y)
return x + y
end
check = |b| if b then
print(b)
end
-- translates into
check = function(b)
if b then
print(b)
end
end
print(b)
end
-- translates into
check = function(b)
if b then
print(b)
end
end
Timer(|| evt.DamagePlayer{Damage = 1}, const.Minute)
-- translates into
Timer(function()
return evt.DamagePlayer{Damage = 1}
end, const.Minute)
-- translates into
Timer(function()
return evt.DamagePlayer{Damage = 1}
end, const.Minute)
switch = (|x, y| y, x)
-- translates into
switch = (function(x, y)
return y, x
end)
-- translates into
switch = (function(x, y)
return y, x
end)
print2 = (|x, y|
print(x)
print(y)
)
-- translates into
print2 = (function(x, y)
print(x)
print(y)
end)
print(x)
print(y)
)
-- translates into
print2 = (function(x, y)
print(x)
print(y)
end)
print2 = |x, y| do
print(x)
print(y)
end
-- translates into
print2 = function(x, y)
do
print(x)
print(y)
end
end
print(x)
print(y)
end
-- translates into
print2 = function(x, y)
do
print(x)
print(y)
end
end
Quests
Original MM quests were spread across multiple files and manually programmed, which is inconvenient and error prone. My quests support has undergone a few iterations and I'm very happy with it now.Here are 6 examples. They are for MM8. At the end of most examples there is extra information about functions used. Most examples utilize short functions syntax.
Start with Quest Example Simple.lua. It contains a simple quest and two text topics, as well as a greeting.
--[[
This is the simplest quest example. Go to the Clan Leader's Hall in Dagger Wound to see it in action.
It utilises new NPCTopic function for more convenient creation of simple text-only topics.
It also uses new Quest function ability to automatically find free quest slot if the Slot parameter isn't specified.
Texts are set using SetTexts function to reduce indentation level.
--]]
QuestNPC = 32 -- Frederick Talimere
-- another way to make a greeting
Greeting{
"Hello, world!",
"Hi.",
}
-- a simple text topic
NPCTopic{
"Blah 1",
"Blah Blah Blah",
}
-- a simple quest: require item #1 (Longsword), give 1000 exp, 1000 gold and an artifact hat
Quest{
"SimpleExampleQuest",
QuestItem = 1, -- quest item index (Longsword)
Gold = 1000, -- reward: gold
Exp = 1000, -- reward: experience
RewardItem = 536, -- reward: Lucky Hat
}
.SetTexts{
Topic = "Quest",
Give = "I need a Longsword!",
Done = "That's the sword I was looking for! Thank you! I have this hat and some gold coins for you!",
Undone = "You don't have the sword yet?",
After = "Thank you for the sword!",
Quest = "Bring a Longsword (the most basic of swords) to Frederick Talimere on Dagger Wound Islands.",
Award = "Brought a sword to Frederick Talimere",
}
-- another simple text topic
NPCTopic{
"Blah 2",
"Second Blah Blah Blah!",
}
--[[
NPCTopic{topic, text} and Greeting{firstGreet, greet} functions just call Quest function with appropriate parameters.
You can specify any parameters you would normally pass to Quest function, like Slot, CanShow etc.
Note that if you pass a number as topic to NPCTopic function, it will set StdTopic to that number.
See Quest Example.lua for details on StdTopic and other parameters of Quest function.
The call of Greeting function in this script is equivalent to this:
Quest{
Slot = -1,
Texts = {
FirstGreet = "Hello, world!",
Greet = "Hi.",
}
}
The first call of NPCTopic function is equivalent to this:
Quest{
Texts = {
Topic = "Blah 1",
Ungive = "Blah Blah Blah",
}
}
]]
This is the simplest quest example. Go to the Clan Leader's Hall in Dagger Wound to see it in action.
It utilises new NPCTopic function for more convenient creation of simple text-only topics.
It also uses new Quest function ability to automatically find free quest slot if the Slot parameter isn't specified.
Texts are set using SetTexts function to reduce indentation level.
--]]
QuestNPC = 32 -- Frederick Talimere
-- another way to make a greeting
Greeting{
"Hello, world!",
"Hi.",
}
-- a simple text topic
NPCTopic{
"Blah 1",
"Blah Blah Blah",
}
-- a simple quest: require item #1 (Longsword), give 1000 exp, 1000 gold and an artifact hat
Quest{
"SimpleExampleQuest",
QuestItem = 1, -- quest item index (Longsword)
Gold = 1000, -- reward: gold
Exp = 1000, -- reward: experience
RewardItem = 536, -- reward: Lucky Hat
}
.SetTexts{
Topic = "Quest",
Give = "I need a Longsword!",
Done = "That's the sword I was looking for! Thank you! I have this hat and some gold coins for you!",
Undone = "You don't have the sword yet?",
After = "Thank you for the sword!",
Quest = "Bring a Longsword (the most basic of swords) to Frederick Talimere on Dagger Wound Islands.",
Award = "Brought a sword to Frederick Talimere",
}
-- another simple text topic
NPCTopic{
"Blah 2",
"Second Blah Blah Blah!",
}
--[[
NPCTopic{topic, text} and Greeting{firstGreet, greet} functions just call Quest function with appropriate parameters.
You can specify any parameters you would normally pass to Quest function, like Slot, CanShow etc.
Note that if you pass a number as topic to NPCTopic function, it will set StdTopic to that number.
See Quest Example.lua for details on StdTopic and other parameters of Quest function.
The call of Greeting function in this script is equivalent to this:
Quest{
Slot = -1,
Texts = {
FirstGreet = "Hello, world!",
Greet = "Hi.",
}
}
The first call of NPCTopic function is equivalent to this:
Quest{
Texts = {
Topic = "Blah 1",
Ungive = "Blah Blah Blah",
}
}
]]
local A, B, C, D, E, F = 0, 1, 2, 3, 4, 5
local Q = vars.Quests
--[[
This is an example of a simple quest, dialog topics that depend on quest state and a 'sell item' topic.
Mostly it consists of texts rather than code. Go to the Clan Leader's Hall in Dagger Wound to see it in action.
It also demonstrates short functions syntax addon (see help for more info).
--]]
QuestNPC = 1 -- The lizard in the tavern
-- a simple quest: require item #1 (Longsword), give 1000 exp, 1000 gold and an artifact hat
Quest{
"ExampleQuest", -- Same as: Name = "ExampleQuest",
Slot = A,
Texts = {
FirstGreet = "Hello, world!",
Greet = "Hi.",
Topic = "Quest!",
Give = "I need a Longsword!",
GreetGiven = "How's it going?",
-- TopicGiven can be set as well, but I keep it at "Quest!" here
Done = "That's the sword I was looking for! Thank you! I have this hat and some gold coins for you!",
Undone = "You don't have the sword yet?",
GreetDone = "Greetings to you, The Man Who Gave The Sword!",
TopicDone = "Thanks!",
After = "Thank you for the sword!",
Quest = "Bring a Longsword (the most basic of swords) to that lizard in the tavern of Dagger Wound Islands.",
Award = "Brought a sword to a tavern-dwelling lizard",
},
QuestItem = 1, -- quest item index (Longsword)
Gold = 1000, -- reward: gold
Exp = 1000, -- reward: experience
RewardItem = 536, -- reward: Lucky Hat
}
-- this will hide the standard topic in slot B
Quest{
Slot = B,
}
-- this will hide the standard topic in slot C
Quest{
Slot = C,
}
-- this topic is shown only when the quest is taken
Quest{
Slot = C,
CanShow = || Q.ExampleQuest == "Given", -- a check that the quest is taken (short function!)
-- Short function syntax language extension translates this into:
-- CanShow = function()
-- return Q.ExampleQuest == "Given"
-- end,
Texts = {
Topic = "Reverse Engineering",
Ungive = "If you bring me the sword, I can reverse-engineer it and make its copies. To tell you the truth, everything here is reverse-engineered.",
},
}
-- this topic is shown only when the quest is done in place of the topic above (sell swords)
Quest{
Slot = C,
CanShow = || Q.ExampleQuest == "Done", -- a check that the quest is finished (short function!)
Texts = {
Topic = "Buy Longsword for 50 gold",
Done = "Here's a sword I made for you!",
Undone = "No money - no Longsword, honey."
},
NeverGiven = true, -- skip "Given" state, perform Done/Undone check immediately
NeverDone = true, -- sell any number of swords. This makes the quest completable mutiple times
QuestGold = 50, -- pay: 50 gold
RewardItem = 1, -- reward: Longsword
}
--[[
Other Quest properties:
StdTopic -- use the standard topic with specified number. For example, a trainer topic or "Join" topic.
StdTopicGiven, StdTopicDone -- similarly, standard topics specific to Given and Done states.
FirstStdTopic -- standard topics specific to initial (ungiven) state.
Quest -- quest number in quests.txt, by default it's allocated automatically
BaseName -- for quests that require you to go from one NPC to another. Setting BaseName makes two quests share the same quest state defined by BaseName. See "Quest With 2 NPCs.lua".
GivenState -- by default it's "Given". This may be useful in a quest with many stages.
DoneState -- by default it's "Done". This may be useful in a quest with many stages.
If you don't pass Slot, any slot not occupied by an MMExt quest will be chosen. If you pass "same" as Slot value, the last used slot will be reused.
Localization:
To generate localization template for all scripts, press Ctrl+F1, write
GenerateLocalization()
and press Ctrl+Enter. It will generate the following items in Scripts\Localization folder:
Quests.txt -- for quests
Common.txt -- for strings passed to LocalizeAll function
Scripts.txt -- for strings passed to Localize function
These files should be edited with Txt Tables Editor from my site.
Alternatively, run GenerateLocalization(true) to generate *.lua localization files. Choose whichever format you prefer.
To generate localization for quests only you can use GenerateQuestsLocalization() instead of GenerateLocalization() command.
To generate localization excluding quests you can use GenerateLocalization(false, false) command.
Note that GenerateLocalization function assumes your scripts contain up to 1 use of LocalizeAll and up to 1 use of Localize function, both of which are at the beginning of the script.
To test localization changes without restarting the game you can create a script in Globals folder with this line:
ReloadLocalization()
Then you'll only need to reload a saved game to refresh localization.
For more complex quests you can set up the following functions:
(here 't' is the table that you passed to the Quest function, it also has some new fields set by Quest function)
Checks (return value is interpreted as true or false):
CanShow(t)
CheckGive(t)
CheckDone(t)
Called on corresponding events:
Give(t)
Ungive(t)
Done(t)
Undone(t)
For complete control - these will override default behavior:
GetGreeting(t, NotFirstVisit)
GetTopic(t)
Execute(t)
IsGiven(t)
GetGreeting and GetTopic functions return a string.
Note that you can also make custom quest states. Say, you have a quest called MyQuest. If you set vars.Quests.MyQuest = "MyState", Texts.TopicMyState (or StdTopicMyState) will be displayed. When you click it, Texts.MyState will be displayed and MyState function will be called.
]]
local Q = vars.Quests
--[[
This is an example of a simple quest, dialog topics that depend on quest state and a 'sell item' topic.
Mostly it consists of texts rather than code. Go to the Clan Leader's Hall in Dagger Wound to see it in action.
It also demonstrates short functions syntax addon (see help for more info).
--]]
QuestNPC = 1 -- The lizard in the tavern
-- a simple quest: require item #1 (Longsword), give 1000 exp, 1000 gold and an artifact hat
Quest{
"ExampleQuest", -- Same as: Name = "ExampleQuest",
Slot = A,
Texts = {
FirstGreet = "Hello, world!",
Greet = "Hi.",
Topic = "Quest!",
Give = "I need a Longsword!",
GreetGiven = "How's it going?",
-- TopicGiven can be set as well, but I keep it at "Quest!" here
Done = "That's the sword I was looking for! Thank you! I have this hat and some gold coins for you!",
Undone = "You don't have the sword yet?",
GreetDone = "Greetings to you, The Man Who Gave The Sword!",
TopicDone = "Thanks!",
After = "Thank you for the sword!",
Quest = "Bring a Longsword (the most basic of swords) to that lizard in the tavern of Dagger Wound Islands.",
Award = "Brought a sword to a tavern-dwelling lizard",
},
QuestItem = 1, -- quest item index (Longsword)
Gold = 1000, -- reward: gold
Exp = 1000, -- reward: experience
RewardItem = 536, -- reward: Lucky Hat
}
-- this will hide the standard topic in slot B
Quest{
Slot = B,
}
-- this will hide the standard topic in slot C
Quest{
Slot = C,
}
-- this topic is shown only when the quest is taken
Quest{
Slot = C,
CanShow = || Q.ExampleQuest == "Given", -- a check that the quest is taken (short function!)
-- Short function syntax language extension translates this into:
-- CanShow = function()
-- return Q.ExampleQuest == "Given"
-- end,
Texts = {
Topic = "Reverse Engineering",
Ungive = "If you bring me the sword, I can reverse-engineer it and make its copies. To tell you the truth, everything here is reverse-engineered.",
},
}
-- this topic is shown only when the quest is done in place of the topic above (sell swords)
Quest{
Slot = C,
CanShow = || Q.ExampleQuest == "Done", -- a check that the quest is finished (short function!)
Texts = {
Topic = "Buy Longsword for 50 gold",
Done = "Here's a sword I made for you!",
Undone = "No money - no Longsword, honey."
},
NeverGiven = true, -- skip "Given" state, perform Done/Undone check immediately
NeverDone = true, -- sell any number of swords. This makes the quest completable mutiple times
QuestGold = 50, -- pay: 50 gold
RewardItem = 1, -- reward: Longsword
}
--[[
Other Quest properties:
StdTopic -- use the standard topic with specified number. For example, a trainer topic or "Join" topic.
StdTopicGiven, StdTopicDone -- similarly, standard topics specific to Given and Done states.
FirstStdTopic -- standard topics specific to initial (ungiven) state.
Quest -- quest number in quests.txt, by default it's allocated automatically
BaseName -- for quests that require you to go from one NPC to another. Setting BaseName makes two quests share the same quest state defined by BaseName. See "Quest With 2 NPCs.lua".
GivenState -- by default it's "Given". This may be useful in a quest with many stages.
DoneState -- by default it's "Done". This may be useful in a quest with many stages.
If you don't pass Slot, any slot not occupied by an MMExt quest will be chosen. If you pass "same" as Slot value, the last used slot will be reused.
Localization:
To generate localization template for all scripts, press Ctrl+F1, write
GenerateLocalization()
and press Ctrl+Enter. It will generate the following items in Scripts\Localization folder:
Quests.txt -- for quests
Common.txt -- for strings passed to LocalizeAll function
Scripts.txt -- for strings passed to Localize function
These files should be edited with Txt Tables Editor from my site.
Alternatively, run GenerateLocalization(true) to generate *.lua localization files. Choose whichever format you prefer.
To generate localization for quests only you can use GenerateQuestsLocalization() instead of GenerateLocalization() command.
To generate localization excluding quests you can use GenerateLocalization(false, false) command.
Note that GenerateLocalization function assumes your scripts contain up to 1 use of LocalizeAll and up to 1 use of Localize function, both of which are at the beginning of the script.
To test localization changes without restarting the game you can create a script in Globals folder with this line:
ReloadLocalization()
Then you'll only need to reload a saved game to refresh localization.
For more complex quests you can set up the following functions:
(here 't' is the table that you passed to the Quest function, it also has some new fields set by Quest function)
Checks (return value is interpreted as true or false):
CanShow(t)
CheckGive(t)
CheckDone(t)
Called on corresponding events:
Give(t)
Ungive(t)
Done(t)
Undone(t)
For complete control - these will override default behavior:
GetGreeting(t, NotFirstVisit)
GetTopic(t)
Execute(t)
IsGiven(t)
GetGreeting and GetTopic functions return a string.
Note that you can also make custom quest states. Say, you have a quest called MyQuest. If you set vars.Quests.MyQuest = "MyState", Texts.TopicMyState (or StdTopicMyState) will be displayed. When you click it, Texts.MyState will be displayed and MyState function will be called.
]]
local A, B, C, D, E, F = 0, 1, 2, 3, 4, 5
local Q = vars.Quests
--[[
This is an example of a quest that requires you to go from one NPC to another.
Also demonstrates usage of StdTopic to make NPC join the party.
Like in Quest Example.lua, short function syntax addon is utilized.
--]]
QuestNPC = 1 -- The lizard in the tavern
-- quest start: take the quest in the tavern
Quest{
"PowerStone",
Slot = D,
GivenItem = 617, -- gives Power Stone upon giving the task
CheckDone = false, -- the quest can't be completed here
}
.SetTexts{
Topic = "Frederick Talimere",
TopicDone = false, -- don't show if it's done
Give = "Show this stone to Frederick Talimere. He is an expert in these.",
Undone = "Frederick is obsessed with these stones. What a fool...",
Quest = "Show a stone to Frederick Talimere in Dagger Wound Islands.",
}
QuestNPC = 32 -- Frederick Talimere
-- quest end: talk to Frederick to get the stone
Quest{
BaseName = "PowerStone",
Slot = D,
Exp = 1000, -- reward: experience
QuestItem = 617, -- need Power Stone
KeepQuestItem = true, -- don't take power stone away
}
.SetTexts{
-- no Topic - don't show the topic if the quest isn't taken
TopicGiven = "Power Stone",
TopicDone = "Power Stone",
Done = "Wow, this is a stone! I love stones!",
Undone = "One lizard told me you have a stone, and so do I. Why don't you show me yours and I'll show you mine?",
After = "I have about 30 of these stones. You know what, let's go looking for stones together! But I need to get a sword first. Because who knows, we might meet some enemies on our way. With a sword in my hands I'll look more persuasive and will calm down any enemy. Trust me, negotiations are my thing!",
Award = "Showed a stone to Frederick Talimere",
}
-- show Join topic when both example quests are completed
Quest{
Slot = D,
StdTopic = 602, -- Join topic of Frederick Talimere
CanShow = || Q.PowerStone == "Done" and Q.SimpleExampleQuest == "Done",
}
local Q = vars.Quests
--[[
This is an example of a quest that requires you to go from one NPC to another.
Also demonstrates usage of StdTopic to make NPC join the party.
Like in Quest Example.lua, short function syntax addon is utilized.
--]]
QuestNPC = 1 -- The lizard in the tavern
-- quest start: take the quest in the tavern
Quest{
"PowerStone",
Slot = D,
GivenItem = 617, -- gives Power Stone upon giving the task
CheckDone = false, -- the quest can't be completed here
}
.SetTexts{
Topic = "Frederick Talimere",
TopicDone = false, -- don't show if it's done
Give = "Show this stone to Frederick Talimere. He is an expert in these.",
Undone = "Frederick is obsessed with these stones. What a fool...",
Quest = "Show a stone to Frederick Talimere in Dagger Wound Islands.",
}
QuestNPC = 32 -- Frederick Talimere
-- quest end: talk to Frederick to get the stone
Quest{
BaseName = "PowerStone",
Slot = D,
Exp = 1000, -- reward: experience
QuestItem = 617, -- need Power Stone
KeepQuestItem = true, -- don't take power stone away
}
.SetTexts{
-- no Topic - don't show the topic if the quest isn't taken
TopicGiven = "Power Stone",
TopicDone = "Power Stone",
Done = "Wow, this is a stone! I love stones!",
Undone = "One lizard told me you have a stone, and so do I. Why don't you show me yours and I'll show you mine?",
After = "I have about 30 of these stones. You know what, let's go looking for stones together! But I need to get a sword first. Because who knows, we might meet some enemies on our way. With a sword in my hands I'll look more persuasive and will calm down any enemy. Trust me, negotiations are my thing!",
Award = "Showed a stone to Frederick Talimere",
}
-- show Join topic when both example quests are completed
Quest{
Slot = D,
StdTopic = 602, -- Join topic of Frederick Talimere
CanShow = || Q.PowerStone == "Done" and Q.SimpleExampleQuest == "Done",
}
local A, B, C, D, E, F = 0, 1, 2, 3, 4, 5
--[[
This is an example of an alchemic quest like those seen in MM8. It works smarter by removing weaker ingredients first.
It replaces default quest of Thistle that normally makes a Potion of Pure Speed.
Like the example with 2 NPCs, it uses BaseName property to make a cross-topic quest easily.
Then there is some stuff just for fun: I reinitiate a conversation 2 minutes after the quest is completed and show some text.
--]]
QuestNPC = 88 -- Thistle
Quest{ -- Get quest
"StreetAlchemy",
}
.SetTexts{
FirstTopic = "Street Alchemy", -- only show if it isn't given
Give = "Hey! Hey fellas! I'm chronicling a Street Alchemy special. You wanna see some alchemy?",
Quest = "Bring Thistle on the Dagger Wound Islands the basic ingredients for a Potion of Disappearance.",
}
NPCTopic{ -- Ingredients topic
"Ingredients",
"Ultimate Potions are made of a complex blending of the three basic ingredients: Swords, Boots and Armor. Potion of Disappearance requires 6 Longswords, 2 pairs of boots and 2 leather armors."
}
Quest{ -- Finish quest
BaseName = "StreetAlchemy",
QuestItem = {
{1,2,3,4,5, Count = 6}, -- all kinds of Longswords, 6 in total
{132,133,134,135,136, Count = 2}, -- all kinds of Boots, 2 in total
{84,85,86,87,88, Count = 2}, -- all kinds of Leather Armor, 2 in total
},
Exp = 1000,
RewardItem = 535, -- Ring of Fusion
Done = function(t)
evt.Add("Inventory", 220) -- add an empty bottle to make it appear as if it's the reward
-- now make him speak to us again after some time
local npc = QuestNPC -- QuestNPC is set to NPC being spoken to in a dialog
Sleep(const.Minute*3, nil, {0}) -- sleep for 3 minutes and only wake up when no dialog is active (hence, the 3rd parameter)
evt.SpeakNPC(npc)
end
}
.SetTexts{
TopicGiven = "Do you have the Ingredients?", -- only show if it's given
Done = "Hey look, the Potion of Disappearance has disappeared! Take this empty bottle, it's yours now.",
Undone = "You are missing all or some of the needed ingredients. Remember, to make a Potion of Disappearance I need 6 Longswords, 2 pairs of boots and 2 leather armors.",
}
Quest{ -- Instead of Ingredients topic
Slot = B,
CanShow = |t| Game.CurrentScreen ~= const.Screens.House, -- only show if NPC was met on the street
}
.SetTexts{
Greet = "I think there's something in your backpack...",
}
--[[
This is an example of an alchemic quest like those seen in MM8. It works smarter by removing weaker ingredients first.
It replaces default quest of Thistle that normally makes a Potion of Pure Speed.
Like the example with 2 NPCs, it uses BaseName property to make a cross-topic quest easily.
Then there is some stuff just for fun: I reinitiate a conversation 2 minutes after the quest is completed and show some text.
--]]
QuestNPC = 88 -- Thistle
Quest{ -- Get quest
"StreetAlchemy",
}
.SetTexts{
FirstTopic = "Street Alchemy", -- only show if it isn't given
Give = "Hey! Hey fellas! I'm chronicling a Street Alchemy special. You wanna see some alchemy?",
Quest = "Bring Thistle on the Dagger Wound Islands the basic ingredients for a Potion of Disappearance.",
}
NPCTopic{ -- Ingredients topic
"Ingredients",
"Ultimate Potions are made of a complex blending of the three basic ingredients: Swords, Boots and Armor. Potion of Disappearance requires 6 Longswords, 2 pairs of boots and 2 leather armors."
}
Quest{ -- Finish quest
BaseName = "StreetAlchemy",
QuestItem = {
{1,2,3,4,5, Count = 6}, -- all kinds of Longswords, 6 in total
{132,133,134,135,136, Count = 2}, -- all kinds of Boots, 2 in total
{84,85,86,87,88, Count = 2}, -- all kinds of Leather Armor, 2 in total
},
Exp = 1000,
RewardItem = 535, -- Ring of Fusion
Done = function(t)
evt.Add("Inventory", 220) -- add an empty bottle to make it appear as if it's the reward
-- now make him speak to us again after some time
local npc = QuestNPC -- QuestNPC is set to NPC being spoken to in a dialog
Sleep(const.Minute*3, nil, {0}) -- sleep for 3 minutes and only wake up when no dialog is active (hence, the 3rd parameter)
evt.SpeakNPC(npc)
end
}
.SetTexts{
TopicGiven = "Do you have the Ingredients?", -- only show if it's given
Done = "Hey look, the Potion of Disappearance has disappeared! Take this empty bottle, it's yours now.",
Undone = "You are missing all or some of the needed ingredients. Remember, to make a Potion of Disappearance I need 6 Longswords, 2 pairs of boots and 2 leather armors.",
}
Quest{ -- Instead of Ingredients topic
Slot = B,
CanShow = |t| Game.CurrentScreen ~= const.Screens.House, -- only show if NPC was met on the street
}
.SetTexts{
Greet = "I think there's something in your backpack...",
}
--[[
These are 2 example quests that require killing multiple kinds of monsters.
Go to the Clan Leader's Hall in Dagger Wound to see the quests in action.
--]]
QuestNPC = 182 -- Dirthic, an empty NPC in a house on an island
evt.MoveNPC(QuestNPC, 173) -- move him to Clan Leader's Hall
-- Quest 1: Kill all pirates
KillMonstersQuest{
-- quest name is optional
{Map = "out01.odm", Monster = {181, 182, 183}},
Exp = 2000,
Gold = 2000,
}
.SetTexts{
Greet = "Hi there!",
Topic = "Pirates!",
Give = "Kill all da Pirates on Dagger Wound Islands.",
Done = "It's great that you've killed them! 2000 gold for you.",
Undone = "Just kill 'em, ok?",
TopicDone = false, -- hide the topic when the quest is completed
Quest = "Kill all Pirates on Dagger Wound Islands. Return to Dirthic in Clan Leader's Hall.",
Award = "Killed all da Pirates on Dagger Wound Islands",
Killed = "You have killed all Pirates!", -- completion message
}
-- Quest 2: Kill all creatures in Abandoned Temple
KillMonstersQuest{
"ClearAbandonedTemple", -- quest name
{Map = "d05.blv", Monster = {7, 8, 9}},
"You have killed all Couatls! Serpentmen are yet to be killed.", -- partial completion message (optional)
{Map = "d05.blv", Monster = {94, 95, 96}},
"You have killed all Serpentmen! Some Couatls are alive.", -- partial completion message (optional)
Exp = 5000,
Gold = 5000,
}
.SetTexts{
Topic = "Snakes!",
Give = "Kill all of them creatures in Abandoned Temple.",
Done = "It's great that you've killed them! 5000 gold for you.",
Undone = "Just kill 'em, ok?",
TopicDone = false, -- hide the topic when the quest is completed
Quest = "Kill all creatures in Abandoned Temple. Return to Dirthic in Clan Leader's Hall.",
Award = "Conducted a massacre in the Abandoned Temple",
Killed = "You have killed all Couatls and Serpentmen!", -- completion message
}
--[[
KillMonstersQuest function takes a list of tasks that it checks with CheckMonstersKilled.
Here are ways of using CheckMonstersKilled:
CheckMonstersKilled{} -- killed all monsters on the map?
CheckMonstersKilled{Group = 1} -- killed all monsters belonging to group 1?
CheckMonstersKilled{Monster = 7} -- killed all monsters of kind 7 (Young Couatl)?
CheckMonstersKilled{NameId = 3} -- killed all monsters with NameId = 3 in placemon.txt (Dragon Hunter Pet)?
CheckMonstersKilled{MonsterIndex = 5} -- killed monster with index 5 in Map.Monsters array?
Instead of a single number you can pass a table with numbers, like I do in this example.
Additonal parameters:
Count -- need to kill at least this many monsters
InvisibleAsDead -- treat invisible (that is, currently disabled) monsters as dead when counting (MM8 only. 'true' by default)
MM6 currently isn't supported, because it doesn't have evt.CheckMonstersKilled command. InvisibleAsDead = false is only supported in MM8.
KillMonstersQuest tasks have an extra required Map parameter. A quest can span across multiple maps.
You can specify partial completion messages. They are placed after a group of tasks. For example:
{task1},
{task2},
{task3},
"first 3 tasks done",
{task4},
{task5},
"tasks 4 and 5 done",
{task6},
"task 6 done",
KillMonstersQuest adds completion messages to Texts under names Killed1, Killed2, Killed3 and so on, for localization.
KillMonstersQuest supports all parameters of Quest function. It sets Quest parameter to 'true' if it isn't specified.
]]
These are 2 example quests that require killing multiple kinds of monsters.
Go to the Clan Leader's Hall in Dagger Wound to see the quests in action.
--]]
QuestNPC = 182 -- Dirthic, an empty NPC in a house on an island
evt.MoveNPC(QuestNPC, 173) -- move him to Clan Leader's Hall
-- Quest 1: Kill all pirates
KillMonstersQuest{
-- quest name is optional
{Map = "out01.odm", Monster = {181, 182, 183}},
Exp = 2000,
Gold = 2000,
}
.SetTexts{
Greet = "Hi there!",
Topic = "Pirates!",
Give = "Kill all da Pirates on Dagger Wound Islands.",
Done = "It's great that you've killed them! 2000 gold for you.",
Undone = "Just kill 'em, ok?",
TopicDone = false, -- hide the topic when the quest is completed
Quest = "Kill all Pirates on Dagger Wound Islands. Return to Dirthic in Clan Leader's Hall.",
Award = "Killed all da Pirates on Dagger Wound Islands",
Killed = "You have killed all Pirates!", -- completion message
}
-- Quest 2: Kill all creatures in Abandoned Temple
KillMonstersQuest{
"ClearAbandonedTemple", -- quest name
{Map = "d05.blv", Monster = {7, 8, 9}},
"You have killed all Couatls! Serpentmen are yet to be killed.", -- partial completion message (optional)
{Map = "d05.blv", Monster = {94, 95, 96}},
"You have killed all Serpentmen! Some Couatls are alive.", -- partial completion message (optional)
Exp = 5000,
Gold = 5000,
}
.SetTexts{
Topic = "Snakes!",
Give = "Kill all of them creatures in Abandoned Temple.",
Done = "It's great that you've killed them! 5000 gold for you.",
Undone = "Just kill 'em, ok?",
TopicDone = false, -- hide the topic when the quest is completed
Quest = "Kill all creatures in Abandoned Temple. Return to Dirthic in Clan Leader's Hall.",
Award = "Conducted a massacre in the Abandoned Temple",
Killed = "You have killed all Couatls and Serpentmen!", -- completion message
}
--[[
KillMonstersQuest function takes a list of tasks that it checks with CheckMonstersKilled.
Here are ways of using CheckMonstersKilled:
CheckMonstersKilled{} -- killed all monsters on the map?
CheckMonstersKilled{Group = 1} -- killed all monsters belonging to group 1?
CheckMonstersKilled{Monster = 7} -- killed all monsters of kind 7 (Young Couatl)?
CheckMonstersKilled{NameId = 3} -- killed all monsters with NameId = 3 in placemon.txt (Dragon Hunter Pet)?
CheckMonstersKilled{MonsterIndex = 5} -- killed monster with index 5 in Map.Monsters array?
Instead of a single number you can pass a table with numbers, like I do in this example.
Additonal parameters:
Count -- need to kill at least this many monsters
InvisibleAsDead -- treat invisible (that is, currently disabled) monsters as dead when counting (MM8 only. 'true' by default)
MM6 currently isn't supported, because it doesn't have evt.CheckMonstersKilled command. InvisibleAsDead = false is only supported in MM8.
KillMonstersQuest tasks have an extra required Map parameter. A quest can span across multiple maps.
You can specify partial completion messages. They are placed after a group of tasks. For example:
{task1},
{task2},
{task3},
"first 3 tasks done",
{task4},
{task5},
"tasks 4 and 5 done",
{task6},
"task 6 done",
KillMonstersQuest adds completion messages to Texts under names Killed1, Killed2, Killed3 and so on, for localization.
KillMonstersQuest supports all parameters of Quest function. It sets Quest parameter to 'true' if it isn't specified.
]]
local A, B, C, D, E, F = 0, 1, 2, 3, 4, 5
--[[
This example demonstrates a complex branched dialog.
It isn't too easy to understand, but it's elegant.
It also shows how you can have item name as topic name and some other bells and whistles.
Go to the Clan Leader's Hall in Dagger Wound to see it in action.
--]]
local MyNPC = 2 -- Dadeross in the lizards tavern
-- For fun: play sound when entering the conversation
function events.EnterNPC(npc)
if npc == MyNPC then
Game.PlaySound(130)
end
end
local function SetBranch(t)
QuestBranch(t.NewBranch)
end
local function ItemTopic(t)
-- return 'false' if the quest is given, which means the item is given away
return not vars.Quests[t.Name] and Game.ItemsTxt[t.MyItem].Name
end
local function GiveItem(t)
evt.ForPlayer().Add("Inventory", t.MyItem) -- give item
-- Game.PlaySound(205) -- make only sound
evt.Add("Experience", 0) -- make sound and animation
evt.FaceAnimation(Game.CurrentPlayer, const.FaceAnimation.SmileHuge) -- a happy face
QuestBranch("Thanks")
end
-- sipmlify similar quests creation
local QuestBase = {}
local function MyQuest(t)
table.copy(QuestBase, t) -- copy common values
QuestBase.Slot = QuestBase.Slot and QuestBase.Slot + 1 -- auto-increment Slot
return Quest(t)
end
--[[
Now, let's get to the quests themselves.
Dialog structure:
- Weapons
- Swords
- Sword 1
- Sword 2
- Sword 3
- Daggers
- Dagger 1
- Axes
- Axe 1
- Axe 2
- Axe 3
- Blasters
- Magic
- Book 1
- Book 2
- Book 3
- Book 4
- Dadeross' Letter to Fellmoon
- Certificate of Authentication (for Axe of Balthazar)
(+ Thanks screen)
]]
QuestNPC = MyNPC
-- greeting
Greeting{
"I'm placed here to give you free stuff.",
"Please choose your free stuff.",
}
-- default topics
QuestBase = {Slot = A}
-- get rid of standard topics of this NPC
MyQuest{}
MyQuest{}
MyQuest{}
MyQuest{}
MyQuest{}
-- A topic that's shown when there are no items left. This one is a bit hackish.
-- Since the quest-matching function goes from slot A to slot F, by the time it
-- asks for topic in slot F it has done all other slots. So, I check that these
-- slots have no topics and then enable the topic of this slot
MyQuest{
Ungive = SetBranch,
NewBranch = "",
GetTopic = function(t)
for i = 0, 4 do
if Game.NPC[MyNPC].Events[i] ~= 0 then
return
end
end
return t.Texts.Topic
end,
Texts = {
Topic = "Sorry, I don't have any of these",
Ungive = "Sorry, I don't have any of these. Maybe you want something else?"
}
}
-- base branch topics
QuestBase = {Branch = "", Slot = A, Ungive = SetBranch}
MyQuest{
NewBranch = "Weapons",
Texts = {
Topic = "Weapons",
Ungive = "Choose which you prefer."
}
}
MyQuest{
NewBranch = "Magic",
Texts = {
Topic = "Magic",
Ungive = "I have a few books."
}
}
-- Weapons branch
QuestBase = {Branch = "Weapons", Slot = A, Ungive = SetBranch}
MyQuest{
NewBranch = "Swords",
Texts = {
Topic = "Swords",
}
}
MyQuest{
NewBranch = "Daggers",
Texts = {
Topic = "Daggers",
}
}
MyQuest{
NewBranch = "Axes",
Texts = {
Topic = "Axes",
}
}
MyQuest{
NewBranch = "Blasters",
Texts = {
Topic = "Blasters",
}
}
-- Swords branch
QuestBase = {
Branch = "Swords", Slot = A,
GetTopic = ItemTopic,
Give = GiveItem,
Texts = {
Give = "Use it well!"
}
}
MyQuest{MyItem = 1}
MyQuest{MyItem = 10}
MyQuest{MyItem = 502}
-- Daggers branch
QuestBase = table.copy(QuestBase, {
Branch = "Daggers", Slot = A,
})
MyQuest{MyItem = 508}
-- Axes branch
QuestBase = table.copy(QuestBase, {
Branch = "Axes", Slot = A,
})
MyQuest{MyItem = 34}
MyQuest{MyItem = 37}
MyQuest{MyItem = 541}
-- Magic branch
QuestBase = table.copy(QuestBase, {
Branch = "Magic", Slot = A,
})
MyQuest{MyItem = 460}
MyQuest{MyItem = 430}
MyQuest{MyItem = 444}
MyQuest{MyItem = 404}
-- Letter and Certificate of Authentication items in main branch
QuestBase = table.copy(QuestBase, {
Branch = "", Slot = C,
})
MyQuest{MyItem = 741} -- Dadeross' Letter to Fellmoon
MyQuest{MyItem = 732, -- Certificate of Authentication
CanShow = function()
return evt.All.Cmp("Inventory", 541) -- check for Axe of Balthazar
end
}
-- Thanks branch
NPCTopic{
Branch = "Thanks", Slot = A,
Ungive = SetBranch,
NewBranch = "",
"Thanks!",
"Maybe you want something else?",
}
--[[
Here's how dialog branches work:
A quest with Branch field is only shown if that's the current branch.
Current branch can be set with QuestBranch function:
QuestBranch("BranchName")
You can also make the branch persist after you reenter NPC dialog:
QuestBranch("BranchName", true)
Or you can obtain current branch:
local branch = QuestBranch()
If there is no persisted branch name, when you enter NPC dialog the branch is set to "".
Branches support is really simple. Previous version of this example implemented it manually.
]]
--[[
This example demonstrates a complex branched dialog.
It isn't too easy to understand, but it's elegant.
It also shows how you can have item name as topic name and some other bells and whistles.
Go to the Clan Leader's Hall in Dagger Wound to see it in action.
--]]
local MyNPC = 2 -- Dadeross in the lizards tavern
-- For fun: play sound when entering the conversation
function events.EnterNPC(npc)
if npc == MyNPC then
Game.PlaySound(130)
end
end
local function SetBranch(t)
QuestBranch(t.NewBranch)
end
local function ItemTopic(t)
-- return 'false' if the quest is given, which means the item is given away
return not vars.Quests[t.Name] and Game.ItemsTxt[t.MyItem].Name
end
local function GiveItem(t)
evt.ForPlayer().Add("Inventory", t.MyItem) -- give item
-- Game.PlaySound(205) -- make only sound
evt.Add("Experience", 0) -- make sound and animation
evt.FaceAnimation(Game.CurrentPlayer, const.FaceAnimation.SmileHuge) -- a happy face
QuestBranch("Thanks")
end
-- sipmlify similar quests creation
local QuestBase = {}
local function MyQuest(t)
table.copy(QuestBase, t) -- copy common values
QuestBase.Slot = QuestBase.Slot and QuestBase.Slot + 1 -- auto-increment Slot
return Quest(t)
end
--[[
Now, let's get to the quests themselves.
Dialog structure:
- Weapons
- Swords
- Sword 1
- Sword 2
- Sword 3
- Daggers
- Dagger 1
- Axes
- Axe 1
- Axe 2
- Axe 3
- Blasters
- Magic
- Book 1
- Book 2
- Book 3
- Book 4
- Dadeross' Letter to Fellmoon
- Certificate of Authentication (for Axe of Balthazar)
(+ Thanks screen)
]]
QuestNPC = MyNPC
-- greeting
Greeting{
"I'm placed here to give you free stuff.",
"Please choose your free stuff.",
}
-- default topics
QuestBase = {Slot = A}
-- get rid of standard topics of this NPC
MyQuest{}
MyQuest{}
MyQuest{}
MyQuest{}
MyQuest{}
-- A topic that's shown when there are no items left. This one is a bit hackish.
-- Since the quest-matching function goes from slot A to slot F, by the time it
-- asks for topic in slot F it has done all other slots. So, I check that these
-- slots have no topics and then enable the topic of this slot
MyQuest{
Ungive = SetBranch,
NewBranch = "",
GetTopic = function(t)
for i = 0, 4 do
if Game.NPC[MyNPC].Events[i] ~= 0 then
return
end
end
return t.Texts.Topic
end,
Texts = {
Topic = "Sorry, I don't have any of these",
Ungive = "Sorry, I don't have any of these. Maybe you want something else?"
}
}
-- base branch topics
QuestBase = {Branch = "", Slot = A, Ungive = SetBranch}
MyQuest{
NewBranch = "Weapons",
Texts = {
Topic = "Weapons",
Ungive = "Choose which you prefer."
}
}
MyQuest{
NewBranch = "Magic",
Texts = {
Topic = "Magic",
Ungive = "I have a few books."
}
}
-- Weapons branch
QuestBase = {Branch = "Weapons", Slot = A, Ungive = SetBranch}
MyQuest{
NewBranch = "Swords",
Texts = {
Topic = "Swords",
}
}
MyQuest{
NewBranch = "Daggers",
Texts = {
Topic = "Daggers",
}
}
MyQuest{
NewBranch = "Axes",
Texts = {
Topic = "Axes",
}
}
MyQuest{
NewBranch = "Blasters",
Texts = {
Topic = "Blasters",
}
}
-- Swords branch
QuestBase = {
Branch = "Swords", Slot = A,
GetTopic = ItemTopic,
Give = GiveItem,
Texts = {
Give = "Use it well!"
}
}
MyQuest{MyItem = 1}
MyQuest{MyItem = 10}
MyQuest{MyItem = 502}
-- Daggers branch
QuestBase = table.copy(QuestBase, {
Branch = "Daggers", Slot = A,
})
MyQuest{MyItem = 508}
-- Axes branch
QuestBase = table.copy(QuestBase, {
Branch = "Axes", Slot = A,
})
MyQuest{MyItem = 34}
MyQuest{MyItem = 37}
MyQuest{MyItem = 541}
-- Magic branch
QuestBase = table.copy(QuestBase, {
Branch = "Magic", Slot = A,
})
MyQuest{MyItem = 460}
MyQuest{MyItem = 430}
MyQuest{MyItem = 444}
MyQuest{MyItem = 404}
-- Letter and Certificate of Authentication items in main branch
QuestBase = table.copy(QuestBase, {
Branch = "", Slot = C,
})
MyQuest{MyItem = 741} -- Dadeross' Letter to Fellmoon
MyQuest{MyItem = 732, -- Certificate of Authentication
CanShow = function()
return evt.All.Cmp("Inventory", 541) -- check for Axe of Balthazar
end
}
-- Thanks branch
NPCTopic{
Branch = "Thanks", Slot = A,
Ungive = SetBranch,
NewBranch = "",
"Thanks!",
"Maybe you want something else?",
}
--[[
Here's how dialog branches work:
A quest with Branch field is only shown if that's the current branch.
Current branch can be set with QuestBranch function:
QuestBranch("BranchName")
You can also make the branch persist after you reenter NPC dialog:
QuestBranch("BranchName", true)
Or you can obtain current branch:
local branch = QuestBranch()
If there is no persisted branch name, when you enter NPC dialog the branch is set to "".
Branches support is really simple. Previous version of this example implemented it manually.
]]
Autonotes
To avoid hassle with autonote indexes and autonote count limit, use Autonote and AddAutonote functions to deal with your autonotes.Example:
Maps\out01.global.lua:
Autonote('hi', 1, 'Welcome to out01!')
local TXT = Localize{
AutonoteMessage = "Check Auto Notes, it's extremely important!",
}
if not CheckAutonote'hi' then -- just to demonstrate use of CheckLocalAutonote
Game.ShowStatusText(TXT.AutonoteMessage)
end
AddAutonote'hi'
AutonoteMessage = "Check Auto Notes, it's extremely important!",
}
if not CheckAutonote'hi' then -- just to demonstrate use of CheckLocalAutonote
Game.ShowStatusText(TXT.AutonoteMessage)
end
AddAutonote'hi'
Autonotes added this way are automatically made localizeable. By default they are local to the script to avoid naming conflicts. For autonotes shared by multiple scripts use ":" at the beginning of their names (e.g. ":hi").
Localization
In order to make a script localizeable you should call Localize function in it, passing a table with default strings. The function will return a table with localized strings. See an example above.For strings shared by multiple scripts you should use LocalizeAll function which works the same way, but normally you should only use the table returned by Localize. It automatically falls back to common localization strings if there is no script-specific one.
Also note that you aren't limited to strings, localization table can contain any values, including subtables.
Quests are localized automatically.
Localization is very much automated. GenerateLocalization() function automatically extracts localization information from all scripts and generates localization files. It requires scripts in Maps and Modules folders not to do anything prior to calling Localize and/or LocalizeAll functions. Also, each function should be called at most once per script, otherwise strings passed in subsequent calls may get ignored.
To generate localization files for all scripts, load any game, press Ctrl+F1, write GenerateLocalization() and press Ctrl+Enter. It will generate the following files in Scripts\Localization folder:
File | Description |
Quests.txt | Quests localization. |
Common.txt | Strings passed to LocalizeAll function. |
Scripts.txt | Strings passed to Localize function. |
Alternatively, you can run GenerateLocalization(true) to generate *.lua localization files. Choose whichever format you prefer.
To generate localization only for quests you can use GenerateQuestsLocalization() command.
To generate localization excluding quests you can use GenerateLocalization(false, false) command.
To test localization changes without restarting the game you can create a script in Global folder with this line:
ReloadLocalization()
Special Codes in Texts
In any text you can use special codes inserted with StrLeft, StrRight and StrColor functions.In various NPC messages and History.txt file you can also use these codes:
Code | Meaning |
---|---|
%01 | NPC name |
%02 | Current player name |
%03 | "his"/"her" (depending on NPC sex) |
%04 | Bribe cost |
%05 | "day"/"evening"/"morning" |
%06 | "lady"/"sir" (depending on sex of current player) |
%07 | "Lady"/"Sir" (depending on sex of current player) |
%08 | Random achieved award from a hardcoded list |
%09 | Same as %03 |
%10 | "Lady"/"Lord" (depending on sex of current player) |
%11 | Reputation category of the party |
%12 | Reputation category required by the NPC |
%13 | Any name starting with the same letter as that of current player |
%14 | "sister"/"brother" (depending on NPC sex) |
%15 | "daughter" |
%16 | Same as %14 |
%17 | Gold percentage an NPC takes |
%23 | Map name |
%24 | Item name (in yellow color) |
%25 | Standard buy/sell/repair cost |
%27 | Actual buy/sell/repair cost with regards to Merchant skill |
%28 | Shop owner title |
%29 | Identification price |
MM7, MM8: | |
%30 | History entry date |
%31 | Player 1 name |
%51 – %70 | Special date |
MM7: | |
%32 | Player 2 name |
%33 | Player 3 name |
%34 | Player 4 name |
MM8: | |
%32 | "his"/"her" (depending on player 1 sex) |
%33 | "he"/"she" (depending on player 1 sex) |
%34 | "him"/"her" (depending on player 1 sex) |
More on Data Tables
Having the Data\Tables folder is great for development of a mod, but simply distributing it with a big mod may present challenges for subsequent creation of smaller mods for it, because they'd be overwritten by any update of the big mod.First, I'd recommend setting up reading tables that don't correspond to *.bin files from LOD archive of your mod if it's a big one. This will allow other mods or power users to override the table more safely. Here's an example (for Scripts\General):
table.copy({
['Chest'] = 'Chest.txt',
['Class HP SP'] = 'ClsHPSP.txt',
['Class Skills'] = 'ClsSkill.txt',
['Class Starting Skills'] = 'ClsSkilB.txt',
['Class Starting Stats'] = 'ClsStats.txt',
['House Movies'] = 'HouseMov.txt',
['Monster Kinds'] = 'MonKind.txt',
['Spells2'] = 'Spells2.txt',
['Town Portal'] = 'TownPort.txt',
['Transport Index'] = 'TripIdx.txt',
['Transport Locations'] = 'TripLoc.txt',
['Shops'] = 'Shops.txt',
['Faces'] = 'Faces.txt',
['Face Animations'] = 'FaceAnim.txt',
}, DataTables.Files, true)
-- DataTables.LazyMode = true -- see below for an explanation
['Chest'] = 'Chest.txt',
['Class HP SP'] = 'ClsHPSP.txt',
['Class Skills'] = 'ClsSkill.txt',
['Class Starting Skills'] = 'ClsSkilB.txt',
['Class Starting Stats'] = 'ClsStats.txt',
['House Movies'] = 'HouseMov.txt',
['Monster Kinds'] = 'MonKind.txt',
['Spells2'] = 'Spells2.txt',
['Town Portal'] = 'TownPort.txt',
['Transport Index'] = 'TripIdx.txt',
['Transport Locations'] = 'TripLoc.txt',
['Shops'] = 'Shops.txt',
['Faces'] = 'Faces.txt',
['Face Animations'] = 'FaceAnim.txt',
}, DataTables.Files, true)
-- DataTables.LazyMode = true -- see below for an explanation
Second, for tables that generate *.bin files there are 2 ways to go:
Examples
You can find more examples in the MMExtension discussion thread.Players Skills And Spells
Below are some scripts demonstrating how you can work with skills and spells. You can test them by pasting the one you like into debug console.-- learn all spells
for _, pl in Party do
for i in pl.Spells do
pl.Spells[i] = true
end
end
for _, pl in Party do
for i in pl.Spells do
pl.Spells[i] = true
end
end
-- give Expert Perception for all players
for _, pl in Party do
local skill, mastery = SplitSkill(pl.Skills[const.Skills.Perception])
pl.Skills[const.Skills.Perception] = JoinSkill(math.max(skill, 4), math.max(mastery, const.Expert))
end
for _, pl in Party do
local skill, mastery = SplitSkill(pl.Skills[const.Skills.Perception])
pl.Skills[const.Skills.Perception] = JoinSkill(math.max(skill, 4), math.max(mastery, const.Expert))
end
-- get all skills at Master 12
for _, pl in Party do
for i, val in pl.Skills do
local skill, mastery = SplitSkill(val)
pl.Skills[i] = JoinSkill(math.max(skill, 12), math.max(mastery, const.Master))
end
end
for _, pl in Party do
for i, val in pl.Skills do
local skill, mastery = SplitSkill(val)
pl.Skills[i] = JoinSkill(math.max(skill, 12), math.max(mastery, const.Master))
end
end
-- get all learned skills to Master 12
for _, pl in Party do
for i, val in pl.Skills do
if val ~= 0 then
local skill, mastery = SplitSkill(val)
pl.Skills[i] = JoinSkill(math.max(skill, 12), math.max(mastery, const.Master))
end
end
end
for _, pl in Party do
for i, val in pl.Skills do
if val ~= 0 then
local skill, mastery = SplitSkill(val)
pl.Skills[i] = JoinSkill(math.max(skill, 12), math.max(mastery, const.Master))
end
end
end
-- learn all available skills at Expert
for _, pl in Party do
for i, learn in EnumAvailableSkills(pl.Class) do
if learn >= const.Expert then
local skill, mastery = SplitSkill(pl.Skills[i])
skill = math.max(skill, 4) -- learn at least level 4
mastery = math.max(mastery, const.Expert) -- learn the mastery
pl.Skills[i] = JoinSkill(skill, mastery)
end
end
end
for _, pl in Party do
for i, learn in EnumAvailableSkills(pl.Class) do
if learn >= const.Expert then
local skill, mastery = SplitSkill(pl.Skills[i])
skill = math.max(skill, 4) -- learn at least level 4
mastery = math.max(mastery, const.Expert) -- learn the mastery
pl.Skills[i] = JoinSkill(skill, mastery)
end
end
end
-- learn all available skills at their maximum level
local LearnLevel = (Game.Version > 6 and {1, 4, 7, 10} or {1, 4, 12})
for _, pl in Party do
for i, learn in EnumAvailableSkills(pl.Class) do
local skill, mastery = SplitSkill(pl.Skills[i])
skill = math.max(skill, LearnLevel[learn]) -- learn at least the usual needed level
mastery = math.max(mastery, learn) -- learn the mastery
pl.Skills[i] = JoinSkill(skill, mastery)
end
end
local LearnLevel = (Game.Version > 6 and {1, 4, 7, 10} or {1, 4, 12})
for _, pl in Party do
for i, learn in EnumAvailableSkills(pl.Class) do
local skill, mastery = SplitSkill(pl.Skills[i])
skill = math.max(skill, LearnLevel[learn]) -- learn at least the usual needed level
mastery = math.max(mastery, learn) -- learn the mastery
pl.Skills[i] = JoinSkill(skill, mastery)
end
end
Artifact Bonuses
Giving Hareck's Leather an 'Of Earth Magic' enhancement. (untested)function events.CalcStatBonusByItems(t)
if t.Stat ~= const.Stats.EarthMagic or t.Player.Skills[const.Skills.EarthMagic] == 0 then
return
end
for item, slot in t.Player:EnumActiveItems() do
if item.Number == 516 then
t:SetMagicBonus(SplitSkill(t.Player.Skills[const.Skill.EarthMagic]):div(2))
return
end
end
end
if t.Stat ~= const.Stats.EarthMagic or t.Player.Skills[const.Skills.EarthMagic] == 0 then
return
end
for item, slot in t.Player:EnumActiveItems() do
if item.Number == 516 then
t:SetMagicBonus(SplitSkill(t.Player.Skills[const.Skill.EarthMagic]):div(2))
return
end
end
end
Giving Hareck's Leather 'Armsmaster + 8' bonus. (untested)
function events.CalcStatBonusByItems(t)
if t.Stat ~= const.Stats.Armsmaster or t.Player.Skills[const.Skills.Armsmaster] == 0 then
return
end
for item, slot in t.Player:EnumActiveItems() do
if item.Number == 516 then
t:SetArtifactBonus(8)
return
end
end
end
if t.Stat ~= const.Stats.Armsmaster or t.Player.Skills[const.Skills.Armsmaster] == 0 then
return
end
for item, slot in t.Player:EnumActiveItems() do
if item.Number == 516 then
t:SetArtifactBonus(8)
return
end
end
end
Q: Is there a way to change values of enchantments? For example, to make Of the Gods one give say +30 stats instead of +10?
A: (untested)
function events.CalcStatBonusByItems(t)
if t.Stat >= const.Stats.Might and t.Stat <= const.Stats.Luck then
for it in t.Player:EnumActiveItems() do
if it.Bonus2 == 2 then
t.Result = t.Result + 20
end
end
end
end
if t.Stat >= const.Stats.Might and t.Stat <= const.Stats.Luck then
for it in t.Player:EnumActiveItems() do
if it.Bonus2 == 2 then
t.Result = t.Result + 20
end
end
end
end
Skill Bonuses
Making GM Dagger do +2 damage/skill instead of +1:function events.CalcStatBonusBySkills(t)
if t.Result ~= 0 and t.Stat == const.Stats.MeleeDamageBase then -- t.Result ~= 0 is for speedup
local sk, mas = SplitSkill(t.Player.Skills[const.Skills.Dagger])
if mas >= const.GM then
local it = t.Player:GetActiveItem(const.ItemSlot.MainHand)
if it and it:T().Skill == const.Skills.Dagger then
t.Result = t.Result + sk
end
end
end
end
if t.Result ~= 0 and t.Stat == const.Stats.MeleeDamageBase then -- t.Result ~= 0 is for speedup
local sk, mas = SplitSkill(t.Player.Skills[const.Skills.Dagger])
if mas >= const.GM then
local it = t.Player:GetActiveItem(const.ItemSlot.MainHand)
if it and it:T().Skill == const.Skills.Dagger then
t.Result = t.Result + sk
end
end
end
end
Spells Damage
Change damage of spell 2 – Flame Arrow. You can also make damage depend on t.Mastery (skill mastery) and t.HP (monster hit points).local function Randoms(min, max, count)
local r = 0
for i = 1, count do
r = r + math.random(min, max)
end
return r
end
function events.CalcSpellDamage(t)
if t.Spell == 2 then -- Flame Arrow
t.Result = 100 + Randoms(1, 100, t.Skill) -- 100 + (1-100) per skill level
end
end
local r = 0
for i = 1, count do
r = r + math.random(min, max)
end
return r
end
function events.CalcSpellDamage(t)
if t.Spell == 2 then -- Flame Arrow
t.Result = 100 + Randoms(1, 100, t.Skill) -- 100 + (1-100) per skill level
end
end
Controlling monsters aggression in MM6
Put this script into Scripts\Global folder. It would make archers in Free Haven friendly and female peasants aggressive. In other places archers would still be aggressive and peasants still friendly.function events.BeforeLoadMap()
if Game.Map.Name == "outc2.odm" then
LocalMonstersTxt()
Game.MonstersTxt[1].HostileType = 0 -- ArcherA
Game.MonstersTxt[2].HostileType = 0 -- ArcherB
Game.MonstersTxt[3].HostileType = 0 -- ArcherC
Game.MonstersTxt[121].HostileType = 4 -- PeasantF1A
Game.MonstersTxt[122].HostileType = 4 -- PeasantF1B
Game.MonstersTxt[123].HostileType = 4 -- PeasantF1C
end
end
if Game.Map.Name == "outc2.odm" then
LocalMonstersTxt()
Game.MonstersTxt[1].HostileType = 0 -- ArcherA
Game.MonstersTxt[2].HostileType = 0 -- ArcherB
Game.MonstersTxt[3].HostileType = 0 -- ArcherC
Game.MonstersTxt[121].HostileType = 4 -- PeasantF1A
Game.MonstersTxt[122].HostileType = 4 -- PeasantF1B
Game.MonstersTxt[123].HostileType = 4 -- PeasantF1C
end
end
Flowers you can pick up (MM8)
Put this script into Scripts\Global folder.-- You can find such flowers in Ravenshore at X = 16921, Y = 4112
-- Flowers disappear when picked up, which makes life easier than in Barrels script
-- The game remembers which sprites are hidden by itself
local SpriteEvents = 20000
local TXT = Localize{
FlowerHint = "Flowers",
}
local function Flower(EvtId)
local i = EvtId – SpriteEvents
evt.Add("Inventory", 208)
evt.SetSprite(i, false) -- alternatively, for the same effect: Map.Sprites[i].Invisible = true
end
local function InitFlower(i, a)
a.Event = SpriteEvents + i
evt.map[SpriteEvents + i] = Flower
evt.hint[SpriteEvents + i] = TXT.FlowerHint
end
function events.LoadMap()
for i, a in Map.Sprites do
if a.DecName == "plant27" then
InitFlower(i, a)
end
end
end
-- Flowers disappear when picked up, which makes life easier than in Barrels script
-- The game remembers which sprites are hidden by itself
local SpriteEvents = 20000
local TXT = Localize{
FlowerHint = "Flowers",
}
local function Flower(EvtId)
local i = EvtId – SpriteEvents
evt.Add("Inventory", 208)
evt.SetSprite(i, false) -- alternatively, for the same effect: Map.Sprites[i].Invisible = true
end
local function InitFlower(i, a)
a.Event = SpriteEvents + i
evt.map[SpriteEvents + i] = Flower
evt.hint[SpriteEvents + i] = TXT.FlowerHint
end
function events.LoadMap()
for i, a in Map.Sprites do
if a.DecName == "plant27" then
InitFlower(i, a)
end
end
end
Unusually looking barrels (MM7)
Put this script into Scripts\Global folder to turn trees into barrels.-- Turns trees into barrels
local SpriteEvents = 20000
local TopicBase = 383
local TextBase = 582
local Reorder = {[0] = 0, 1, 4, 3, 5, 2, 6, 7} -- NPC topics order for barrels is messed up
local AutonotesBase = 32
local function Barrel(EvtId)
local i = EvtId – SpriteEvents
local v = mapvars.Barrels[i]
Game.ShowStatusText(Game.NPCText[TextBase + Reorder[v]])
if v > 0 then
evt.Add(evt.VarNum.BaseStats[v – 1], 2)
evt.Set("AutonotesBits", AutonotesBase + v)
mapvars.Barrels[i] = 0
evt.hint[SpriteEvents + i] = Game.NPCTopic[TopicBase]
end
end
local function InitBarrel(i, a)
mapvars.Barrels = mapvars.Barrels or {}
mapvars.Barrels[i] = mapvars.Barrels[i] or math.random(1, 7)
a.Event = SpriteEvents + i
evt.map[SpriteEvents + i] = Barrel
evt.hint[SpriteEvents + i] = Game.NPCTopic[TopicBase + Reorder[mapvars.Barrels[i]]]
end
function events.LoadMap()
for i, a in Map.Sprites do
if a.DecName and a.DecName:match("^tree") then
InitBarrel(i, a)
end
end
end
local SpriteEvents = 20000
local TopicBase = 383
local TextBase = 582
local Reorder = {[0] = 0, 1, 4, 3, 5, 2, 6, 7} -- NPC topics order for barrels is messed up
local AutonotesBase = 32
local function Barrel(EvtId)
local i = EvtId – SpriteEvents
local v = mapvars.Barrels[i]
Game.ShowStatusText(Game.NPCText[TextBase + Reorder[v]])
if v > 0 then
evt.Add(evt.VarNum.BaseStats[v – 1], 2)
evt.Set("AutonotesBits", AutonotesBase + v)
mapvars.Barrels[i] = 0
evt.hint[SpriteEvents + i] = Game.NPCTopic[TopicBase]
end
end
local function InitBarrel(i, a)
mapvars.Barrels = mapvars.Barrels or {}
mapvars.Barrels[i] = mapvars.Barrels[i] or math.random(1, 7)
a.Event = SpriteEvents + i
evt.map[SpriteEvents + i] = Barrel
evt.hint[SpriteEvents + i] = Game.NPCTopic[TopicBase + Reorder[mapvars.Barrels[i]]]
end
function events.LoadMap()
for i, a in Map.Sprites do
if a.DecName and a.DecName:match("^tree") then
InitBarrel(i, a)
end
end
end
Other Examples
Summon monster (Peasant):local mon = SummonMonster(151, Party.X, Party.Y, Party.Z, true)
mon.NPC_ID = 52
mon.Hostile = false
mon.NPC_ID = 52
mon.Hostile = false
See global event number (copy to console to quickly test what event is triggered by a dialog item):
-- on:
function events.EvtGlobal(evt)
Message(evt)
end
function events.EvtGlobal(evt)
Message(evt)
end
-- off:
events.EvtGlobal.clear()
events.EvtGlobal.clear()
See current house (2DEvent) and NPC indeces (run it from console):
Game.GetCurrentHouse(), GetCurrentNPC()
Q: How to change the home position after the death of the team on current map?
A: Specify desired coordinates in the call to XYZ and set desired Direction:
function events.DeathMap(t)
t.Name = "out05.odm"
XYZ(Party, 0, 0, 0)
Party.Direction = 0
Party.LookAngle = 0
end
t.Name = "out05.odm"
XYZ(Party, 0, 0, 0)
Party.Direction = 0
Party.LookAngle = 0
end
Changing starting map (similar to previous example):
Game.NewGameMap = "out05.odm"
function events.NewGameMap()
XYZ(Party, 0, 0, 0)
Party.Direction = 0
Party.LookAngle = 0
end
function events.NewGameMap()
XYZ(Party, 0, 0, 0)
Party.Direction = 0
Party.LookAngle = 0
end
Q: How to run a method when a user presses a specific key?
Method 1:
function Keys.F1(t)
Message("F1 pressed")
end
Message("F1 pressed")
end
function events.KeyDown(t)
if t.Key == const.Keys.F1 then
Message("F1 pressed")
end
end
if t.Key == const.Keys.F1 then
Message("F1 pressed")
end
end
Keys[const.Keys.F1] = function()
Message("F1 pressed")
end
Message("F1 pressed")
end
Evt Commands
evt.EnterHouseParameters: |
|
Id |
In 2DEvents.txt 600 = you won 601 = you won 2 / you lost |
evt.PlaySoundParameters: |
|
Id | |
X | |
Y | |
evt.MoveToMapNotes:If cancel is pressed, event execution is stopped If X, Y, Z, Direction, LookAngle, SpeedZ are all 0, the party isn't moved If HouseId and Icon are 0, the enter dungeon dialog isn't shown Parameters: |
|
X | |
Y | |
Z | |
Direction | -1 = special case |
LookAngle | |
SpeedZ | |
HouseId | In 2DEvents.txt |
Icon | |
Name | if unspecified or starts with "0" => current map |
evt.OpenChestParameters: |
|
Id | |
evt.FaceExpressionParameters: |
|
Player | |
Frame | |
evt.DamagePlayerParameters: |
|
Player | |
DamageType | |
Damage | |
evt.SetSnowParameters: |
|
EffectId | Only 0 available |
On | |
evt.SetTextureParameters: |
|
Facet |
[MM6] Index in Map.Facets indoors. [MM7+] Id of facets group. |
Name | |
evt.SetTextureOutdoors[MM6]Parameters: |
|
Model | |
Facet | |
Name | |
evt.ShowMovie[MM7+]Parameters: |
|
DoubleSize | |
ExitCurrentScreen |
Use true only before using evt.MoveToMap command in houses and before showing game ending. Prevents loading of house anmation after the movie stops playing, but doesn't exit the screen properly. |
Name | |
evt.SetSpriteParameters: |
|
SpriteId | |
Visible | bit 0x20 of sprite |
Name | If Name is unspecified or "0", the sprite isn't changed |
evt.CmpUsually performs Variable >= Value comparisonParameters: |
|
VarNum | |
Value | |
evt.SetDoorStateParameters: |
|
Id | |
State |
0 – state (0), 1 – state (1), 2 – switch state if the door isn't moving, 3 – switch state |
evt.AddParameters: |
|
VarNum | |
Value | |
evt.SubtractAlso available as evt.Sub.Parameters: |
|
VarNum | |
Value | |
evt.SetParameters: |
|
VarNum | |
Value | |
evt.SummonMonstersParameters: |
|
TypeIndexInMapStats | |
Level | |
Count | |
X | |
Y | |
Z | |
NPCGroup | [MM7+] |
unk | [MM7+] |
evt.CastSpellParameters: |
|
Spell | |
Mastery | |
Skill | |
FromX | |
FromY | |
FromZ | |
ToX | |
ToY | |
ToZ | |
evt.SpeakNPCParameters: |
|
NPC | |
evt.SetFacetBitParameters: |
|
Id |
[MM6] Index in Map.Facets indoors. [MM7+] Id of facets group. |
Bit | |
On | |
evt.SetFacetBitOutdoors[MM6]Parameters: |
|
Model | Model index in Map.Models |
Facet | -1 = for all faces of the model |
Bit | |
On | |
evt.SetMonsterBit[MM7+]Parameters: |
|
Monster | |
Bit | |
On | |
evt.QuestionUse Question function instead, e.g.if Question("Restricted area - Keep out.", "What's the password?"):lower() == "jbard" then ... Parameters: |
|
Question | |
Answer1 | |
Answer2 | |
evt.StatusTextUse Game.ShowStatusText function instead, e.g.Game.ShowStatusText("Hi!") Parameters: |
|
Str | |
evt.SetMessageUse Message function instead, e.g.Message("Hi!") Parameters: |
|
Str | |
evt.SetLightParameters: |
|
Id |
[MM6, MM7] Map.Lights index [MM8] Light group id |
On | |
evt.SimpleMessageUse Message function instead, e.g.Message("Hi!") Has no parameters. |
|
evt.SummonObjectTo make your script compatible with all MM versions, instead of calling this function you can call SummonItem to create an item and Game.SummonObjects to create an object.Parameters: |
|
Item | [MM8] Item index. Index over 1000 means random item of the same kind as Item % 1000 of strength Item div 1000. For backward compatibility, this parameter can also be called Type. |
Type | [MM6, MM7] Object kind index (ObjList.txt) |
X | |
Y | |
Z | |
Speed | |
Count | |
RandomAngle | |
evt.ForPlayerSets current player and returns evt. You can thus write things like this: evt.ForPlayer("All").Add("Exp", 1000) You can also manipulate evt.Player and evt.CurrentPlayer variables directly. Usually a better approach is to specify player after evt, this way it only effects one call that follows: evt.All.Add("Exp", 1000) evt[0].Add("Gold", 1000) Parameters: |
|
Player | |
evt.SetNPCTopicParameters: |
|
NPC | |
Index | |
Event | |
evt.MoveNPCParameters: |
|
NPC | |
HouseId | In 2DEvents.txt |
evt.GiveItemParameters: |
|
Strength | 1-6 (like described at the end of STDITEMS.TXT) |
Type | |
Id |
If Id is 0, a random item is chosen from the specified class with specified strength, otherwise, Type and Strength determine the enchantments |
evt.ChangeEventParameters: |
|
NewEvent | Changes global event for barrels, pedestals etc. The kinds of sprites with such events are hard-coded. |
evt.CheckSkillChecks that the skill meets specified Level requirement and that Mastery exactly matches that of the player (Novice matches any mastery). Was supposed to include "Double effect" enchantments and NPC bonuses, but doesn't.Parameters: |
|
Skill | |
Mastery | |
Level | |
evt.SetNPCGroupNews[MM7+]Parameters: |
|
NPCGroup | |
NPCNews | |
evt.SetMonsterGroup[MM7+]Parameters: |
|
Monster | |
NPCGroup | |
evt.SetNPCItem[MM7+]Parameters: |
|
NPC | |
Item | |
On | |
evt.SetNPCGreeting[MM7+]Parameters: |
|
NPC | |
Greeting | |
evt.CheckMonstersKilled[MM7+]Parameters: |
|
CheckType | 0 – any monster, 1 – in group, 2 – of type, 3 – specific monster, 4 – specific monster by name (MM8) |
Id | Depending on CheckType: 0 – not used, 1 – group id, 2 – monster type minus 1, 3 – monster id, 4 – id in placemon.txt (MM8 only) |
Count | 0 – all must be killed, else a number of monsters that must be killed |
InvisibleAsDead | [MM8] 1 (default) – treat invisible (that is, currently disabled, like pirates in Ravenshore before you enter Regna) monsters as dead when counting, 0 – include invisible monsters |
evt.ChangeGroupToGroup[MM7+]Parameters: |
|
Old | |
New | |
evt.ChangeGroupAlly[MM7+]Parameters: |
|
NPCGroup | |
Ally | Monster class that guards this group. That is, (Id + 2):div(3), like in Hostile.txt. |
evt.CheckSeason[MM7+]Parameters: |
|
Season | |
evt.SetMonGroupBit[MM7+]Parameters: |
|
NPCGroup | |
Bit | |
On | |
evt.SetChestBit[MM7+]Parameters: |
|
ChestId | |
Bit | |
On | |
evt.FaceAnimation[MM7+]Parameters: |
|
Player | |
Animation | |
evt.SetMonsterItem[MM7+]Parameters: |
|
Monster | |
Item | |
Has | |
evt.StopDoor[MM8]Parameters: |
|
Id | |
evt.CheckItemsCount[MM8]Parameters: |
|
MinItemIndex | |
MaxItemIndex | |
Count | |
evt.RemoveItems[MM8]Parameters: |
|
MinItemIndex | |
MaxItemIndex | |
Count | |
evt.Jump[MM8]Parameters: |
|
Direction | |
ZAngle | |
Speed | |
evt.IsTotalBountyInRange[MM8]Parameters: |
|
MinGold | |
MaxGold | |
evt.CanPlayerAct[MM8]Parameters: |
|
Id | from Roster.txt |
evt.RefundChestArtifactsTo be added in 2.5.6 or 2.6 of the patches. Removes all artifacts randomly generated in the chest from Party.ArtifactsFound.Parameters: |
|
Id |
Events
Structs\After\ Spells.luaEvents: |
|
MonsterCastSpell{Action CallDefault Direction Handled Monster MonsterIndex ObjectType Skill [MM7+] Sound Spell } |
Can be used to change how a spell works when cast by a monster. Sound and ObjectType aren't assigned initially, you can set them to change what sound the spell makes and what projectiles it has. CallDefault(FakeSpell, FakeSkill) would use logic from FakeSpell with FakeSkill, but change sound and any created projectiles to match what you've defined in the table. Example: -- allow Poison Spray to be cast by monsters events.MonsterCastSpell = |t| if t.Spell == 24 then local sk, mas = SplitSkill(t.Skill) if mas >= const.Expert then t.CallDefault(15, JoinSkill(sk, mas - 1)) -- use Sparks as a template else t.CallDefault(2) -- use Fire Bolt as a template end end |
EventCastSpell{CallDefault FromX FromY FromZ Handled Mastery ObjectType Skill Sound Spell ToX ToY ToZ } |
Can be used to change how a spell works when cast by evt.CastSpell. Sound and ObjectType aren't assigned initially, you can set them to change what sound the spell makes and what projectiles it has. CallDefault(FakeSpell, FakeMastery, FakeSkill, x1, y1, z1, x2, y2, z2) would use logic from FakeSpell with FakeMastery and FakeSkill, but change sound and any created projectiles to match what you've defined in the table. Coordinates can also be changed. Complex example: -- Enables Paralyze spell to be used by monsters and evt.CastSpell
-- You would also need to add the projectiles to ObjList.txt and SFT.txt in Data\Tables\ local spPara = const.Spells.Paralyze local objPara = Game.SpellObjId[spPara] local handler = |t| if t.Spell == spPara then t.CallDefault(2) -- Fire Bolt, because it simply plays a sound and creates a projectile end events.MonsterCastSpell = handler events.EventCastSpell = handler events.ReadMonsterSpell = |t| if t.Name == "paralyze" then t.Result = spPara end events.PlayerAttacked = |t| if not t.Handled then local o = t.Attacker.Object if o and o.Type == objPara then t.Handled = true t.Player:DoBadThing(const.MonsterBonus.Paralyze, t.Attacker.Monster or Map.Monsters[0]) end end events.MonsterAttacked = |t| if not t.Handled then local o, mon = t.Attacker.Object, t.Monster if o and o.Type == objPara then t.Handled = true if mon:CalcHitByEffect(Game.SpellsTxt[spPara].DamageType) then mon.CurrentActionLength = 128 mon.AIState = 0 mon.SpeedX = 0 mon.SpeedY = 0 mon.SpeedZ = 0 mon:UpdateGraphicState() mon.SpellBuffs[const.MonsterBuff.Paralyze]:Set(Game.Time + 3*const.Minute*o.SpellSkill, o.SpellLevel) mon:ShowSpellEffect() end end end |
ReadMonsterSpell{word1, word2, ...;Name Result } |
If spell name is "Hour of Power", the table passed to event would be {"hour", "of", "power", Name = "hour of power"}, all in lower case. Example: -- allow Poison Spray to be read from Monsters.txt events.ReadMonsterSpell = |t| if t.Name == "poison spray" then t.Result = 24 end |
Core\ events.luaEvents: |
|
CalcSpellDamage{HP HitPoints Mastery Result Skill Spell } | |
WalkToMap{Days [MM7+] EnterMap EnterSide [MM7+] LeaveMap LeaveSide [MM7+] X Y } |
Sides: 0, "up", "down", "left", "right". 0 means "party start" sprite. |
DeathMap{Name Set } | |
NewGameMap{AutoFallStart Set } | Set(x, y, z, direction, lookAngle, speedZ) function sets both party position (saved in autosave) and map transition (used on start immediately). |
NewGameDefaultParty() | |
NewGameClearParty() | |
LoadedRosterTxt() | Loaded roster.txt and pcnames.txt |
GameInitialized0() | loaded icons.lod, events.lod[MM7], language LODs[MM8]; about to start loading global.txt |
GameInitialized1() | loaded all archives except games.lod, loaded global.txt and .bin data |
GameInitialized2() | loaded .txt data, global.evt and all archives, intro shown, various bitmaps, sprites and sounds loaded |
CanSaveGame{IsArena SaveKind } |
SaveKind: 0 – normal, 1 – autosave, 2 – quick save If IsArena is true, the "No saving on the Arena" message is displayed |
CanCastLloyd() | |
IsUnderwater{Map Result } | [MM7+] |
FogRange() | |
PopulateQuestLog() | Use this event to add quest indexes to Game.DialogLogic.List or rearrange them |
PopulateAutonotesList{Category } | Use this event to add autonote indexes to Game.DialogLogic.List or rearrange them |
PopulateAwardsList{NoShuffle Player PlayerIndex } | Use this event to add award indexes to Game.DialogLogic.List or rearrange them. Awards would later be arranged into groups of different colors. If NoShuffle is set to true, their order within groups would be preserved, otherwise default game code will sort them in an unpredictable manner. |
MonsterInfoPictureChanged(MonId) | Called when monster kind in monster info dialog changes. Game.DialogLogic.MonsterInfoMonster holds the monster prototype being displayed. This is the event you can use to change Game.PatchOptions.MonSpritesSizeMul. |
WindowMessage{Handled LParam Msg Result WParam Window } | |
KeyDown{Alt ExtendedKey Handled Key WasPressed } | |
KeyUp{Alt ExtendedKey Handled Key WasPressed } | |
PostRender() | |
Action{Action Handled Param Param2 } | |
MenuAction{Action Handled Param Param2 } | |
ExitMapAction{Action } | |
KeysFilter{Key On Result } | |
BeforeSaveGame() | |
BeforeNewGameAutosave() | |
AfterNewGameAutosave() | |
AfterSaveGame() | |
SkyBitmap{FirstVisit Result } | |
LoadSavedMap{Data = Raw ddm or dlv data FacetsCount = [MM7+] Total numuber of facets on the map IsIndoor IsOutdoor } |
You can do a number of things here: 1. Set Map.LastRefillDay to 0 to force a refill. 2. In MM7+ you could handle games saved in an older version of your mod. The game checks Map.SanitySpritesCount against Map.Sprites.Count, Map.SanityFacetsCount against FacetsCount, and on outdoor maps Map.SanityModelsCount against Map.Models.Count. If all sanity fields are non-zero and either of them doesn't match the real count, the game forcibly refills the map. You can do a similar check, and either change Data (use mem.free and mem.malloc if needed) or backup monsters, objects, chests and visible map data and restore them in CancelLoadingMapScripts event (that's the first of events that fires once the map is loaded). Not an easy task, but it will provide saves compatibility when signinficant changes to maps happen. Important: Map.Name at this point is changed from "map.odm"/"map.blv" to "map.ddm"/"map.dlv". Another note: Map.SanityDoorDataSize was added in MMExtension. At this point you can check it against Map.IndoorHeader.DoorDataSize, later on DoorDataSize gets replaced with SanityDoorDataSize if the latter is non-zero. |
PlayMapTrack{MapIndex Track } | |
ShowMovie{Allow CallDefault = function() DoubleSize ExitCurrentScreen Name Y } | |
PlaySound{Allow CallDefault = function() Loops ObjRef Sound Speed UnkParam Volume X Y } | |
FaceAnimation{Allow Animation CallDefault = function() Face ForceSound Player PlayerIndex Sound SoundCount SoundOffset } | |
CalcStatBonusByItems{ArtifactBonus [MM7+] IgnoreExtraHand MagicBonus [MM7+] Player PlayerIndex Result SetArtifactBonus SetMagicBonus Stat } |
Here's how SetArtifactBonus(value) method works: [MM7+] If value is bigger than ArtifactBonus, it modifies ArtifactBonus and increases Result. [MM6] It just adds the value to Result. The game does the same, but only takes one instance of each artifact into consideration. SetMagicBonus does the same to MagicBonus. |
CalcStatBonusByMagic{Player PlayerIndex Result Stat } | |
CalcStatBonusBySkills{Player PlayerIndex Result Stat } | |
GetSkill{Player PlayerIndex Result Skill } | [MM7+] |
GetAttackDelay{Player PlayerIndex Ranged Result } | |
CalcDamageToPlayer{Damage DamageKind Player PlayerIndex Result } | |
GetMerchantTotalSkill{Player PlayerIndex Result } | |
GetDisarmTrapTotalSkill{Player PlayerIndex Result } | |
GetDiplomacyTotalSkill{Player PlayerIndex Result } | [MM6] |
GetPerceptionTotalSkill{Player PlayerIndex Result } | [MM7+] |
GetLearningTotalSkill{Player PlayerIndex Result } | [MM7+] |
DoBadThingToPlayer{Allow Monster [MM7+] MonsterIndex [MM7+] Player PlayerIndex Thing } | |
GetStatisticEffect{Result Value } | |
UseMouseItem{ActivePlayer ActivePlayerIndex Allow CallDefault = function() IsPortraitClick Player PlayerSlot } | |
CanLearnSpell{NeedMastery Player PlayerIndex Spell } | |
Regeneration{HP Player PlayerIndex SP } |
HP and SP don't include regeneration values assigned by the game, but setting them takes care of conditions !k{Player :structs.Player} |
ModifyItemDamage{Damage Item MonsterId Player PlayerIndex Result Slot } | |
GenerateItem{AlwaysEnchant Handled Item Kind Strength } | |
ItemGenerated{AlwaysEnchant Handled Item Kind Strength } | |
MonsterKilled(mon, monIndex, defaultHandler) | |
MonsterKillExp{Exp Handled Monster MonsterIndex } | |
ItemAdditionalDamage{DamageKind Item Result Vampiric } | [MM7+] |
CalcDamageToMonster{Damage DamageKind Monster MonsterIndex Result } | |
PickCorpse{Allow CallDefault = function() Monster MonsterIndex } | |
CastTelepathy{Allow CallDefault = function() Monster MonsterIndex } | [MM7+] |
CanMonsterCastSpell{Allow Distance [MM8] Monster MonsterIndex Spell } | [MM7+] |
MonsterChooseAction{Action CallDefault = function() Distance [MM8] Monster MonsterIndex } | Action starts uninitialized. Each time you call CallDefault, it generates new result, assigns it to Action and returns the value. |
MonsterAttacked({Attacker = table returned by WhoHitMonster Handled Monster MonsterIndex }, attacker) |
Called when a player or a projectile tries to hit a monster. Can be used to completely replace what happens. Doesn't get triggered when using Armageddon or Finger of Death. |
AfterMonsterAttacked({Attacker = table returned by WhoHitMonster Handled = carried over from MonsterAttacked event Monster MonsterIndex }, attacker) | |
PlayerAttacked({Attacker = table returned by WhoHitPlayer Handled Player PlayerSlot }, attacker) |
Called when a monster or a projectile tries to hit a player. Can be used to completely replace what happens. Doesn't get triggered when using Armageddon. |
AfterPlayerAttacked({Attacker = table returned by WhoHitPlayer Handled = carried over from PlayerAttacked event Player PlayerSlot }, attacker) |
Core\ evt.luaEvents: |
|
LeaveMap() | |
LeaveGame() | |
BeforeLoadMap(WasInGame, WasLoaded) | |
CancelLoadingMapScripts(WasInGame) | Return true to cancel execution of map scripts. Used by the Editor. |
BeforeLoadMapScripts(WasInGame) | |
LoadMapScripts(WasInGame) | Use this event instead of LoadMap in Global and General scripts |
LoadMap(WasInGame, NoScripts) | NoScripts = true if map scripts execution was cancelled by CancelLoadingMapScripts event. |
AfterLoadMap(WasInGame) | |
GetEventHint(evtId) | |
GetMazeInfo() | |
EvtMap(evtId, seq) | |
EvtGlobal(evtId, seq) |
Core\ main.luaEvents: |
|
StructsLoaded() | |
ScriptsLoaded() |
Core\ npc.luaEvents: |
|
EnterAnyNPC({Index Kind NPC }, npc) | Happens before events.EnterNPC. Kind ("NPC", "HiredNPC", "StreetNPC") defines the array NPC belongs to and Index is index in that array. |
EnterNPC(Index) | Only called for NPCs from Game.NPC array, not for street or hired NPCs |
ShowNPCTopics(Index) | NPC topics are about to be shown and you can update them here |
ShowHiredNPCTopics(Index, RealNPC) |
Happens only in MM6 when you talk to a hired NPC. Index is the index in Party.HiredNPC array. RealNPC is the index of the prototype NPC in Game.NPC array or nil. |
DrawNPCGreeting{NPC Seen Text } | |
SpeakWithMonster{Monster MonsterIndex Result } | [MM7+] Called when you speak with a guard or any other monster with a generic message from NPCGroup.txt. Assign Result a string to override default monster group message. Assign an empty string to show no message. Initially Result is nil. |
CanExitNPC{Allow Must NPC } | If Must is true, the handler can still set Allow to false, but can't fully cancel the exit. After CanExitNPC cancels it 100 times, the exit will happen unconditionally. |
CanExitStreetNPC{Allow Index Kind Must NPC } | |
CanExitHiredNPC{Allow Index Kind Must NPC RealNPC } | RealNPC is the index of the prototype NPC in Game.NPC array or nil. |
ExitNPC(CurrentNPC) | |
ExitAnyNPC{Index Kind NPC } | |
ExitHouseScreen(Game.HouseScreen) | |
CanTeachSkillMastery{Allow Cost Mastery Skill Text } | |
CanTempleHealPlayer{House Player PlayerIndex Result } | |
GetShopItemTreatment{Action House HouseType Item Player PlayerIndex Result } |
Action: "buy", "sell", "identify", "repair" Result: 0-based option from merchant.txt GetDefault(HouseType, House, Item, Action, Player) function lets you get item treatment by another shop type (all parameters are optional) |
CanShopOperateOnItem{Action House HouseType Item Result } |
Action: "buy", "sell", "identify", "repair" GetDefault(House, Item) function lets you get item treatment by another shop type (all parameters are optional) |
ShopItemsGenerated{House HouseType } | |
GuildItemsGenerated{House HouseType } | Note that you'll have to update Game.GuildItemIconPtr if you change items in this event (see an example there). |
HouseMovieFrame{House } | |
ArcomageSetup(arcomage, house) | |
ArcomageText{House Result } |
Lets you modify Victory Conditions text. Result is not pre-initialized. Instead, you need to call GetDefault function to get the default string. |
ArcomageWin(house) | |
NewBountyHunt{House Index Result } | Assign Result to specify the target monster for the hunt |
BountyHuntDone{Gold House Index } | If you modify Gold here, it wouldn't be in line with the promised sum, but you may introduce other rewards for example |
SetMapNoNPC() | [MM7] Use this event if you need to set Map.NoNPC |
PopulateNPCDialog({DlgKind Index Kind NPC Result }, npc) |
Change topics of an NPC dialog. Result is an array of NPC commands from const.HouseScreens. Names from const.HouseScreens as text are also allowed. You can also add a table with extra parameters, see the example below. Kind ("NPC", "HiredNPC", "StreetNPC") defines the array NPC belongs to and Index is index in that array. The following DlgKind values are possible: "Main" – Regular NPC conversation. In MM6 this is also the Dismiss conversation with a hired NPC. "StreetNPC" – Conversation with a street NPC that you can hire. In MM7 this is also the Dismiss conversation with a hired NPC. In MM6 it ratains items you add to beg/threat/bribe menu. "LackFame" – Not enough fame to communicate with a street NPC. Unused. "BegThreatBribe" – Beg/Threat/Bribe menu in MM6. "ThreatBribe" – Beg/Threat/Bribe menu, but "Beg" option won't do anything, because you've already begged the NPC before. "JoinMenu" – Join party menu in MM6, MM7. "JoinRoster" – Join party menu in MM8. "TeachSkill" – Expert/Master/GM teaching. "JoinGuild" – Join a guild. "BountyHuntNPC" – Bounty Hunt NPC topic. Only utilized by MM6. "ArenaMenu" – Any Arena menu. "SeerPilgrimage" – Pilgrimage in MM6. |
AfterPopulateNPCDialog({DlgKind ExtraParams Index Kind NPC Result }, npc) | |
PopulateHouseDialog{House PicType Result } |
Change topics in the main dialog menu in a house, unless isn't an NPC conversation. Unfortunately, the captions displayed won't change as they are hard-coded in various places. Result is an array of topic numbers from const.HouseScreens. Names from const.HouseScreens as text are also supported. Example: events.PopulateHouseDialog = |t| if t.PicType == const.HouseType.Training then t.Result = {"Train"} -- disable learning skills at training halls end |
PopulateArcomageDialog{House PicType Result } | |
PopulateDisplayInventoryDialog{House PicType Result } | |
AfterPopulateHouseDialog{ExtraParams House PicType Result } | There are also similar AfterPopulateArcomageDialog and AfterPopulateDisplayInventoryDialog events |
PopulateLearnSkillsDialog{House PicType Result } |
[MM7+] Change skills in Learn Skills dialog. Result is an array of skill numbers. Skill names from const.Skills are also allowed. Example: events.PopulateLearnSkillsDialog = |t| if t.PicType == const.HouseType.Tavern then t.Result[#t.Result + 1] = "Blaster" -- devs apperently forgot to make taverns teach you Blaster skill end |
AfterPopulateLearnSkillsDialog{House PicType } | |
BeforeDrawDialogs() | |
BeforeDrawDialog{Dialog DlgID DrawnCount Index } | Called when a dialog is just about to be drawn. Index goes from 1 to Game.Dialogs.Count and you can change if you have to. |
DrawDialog{Dialog DlgID DrawnCount Index } | Note that some dialogs get destroyed once they are drawn. When that happens, Dialog is nil. |
AfterDrawDialog{Dialog DlgID DrawnCount Index } | |
AfterDrawDialogs(DrawnCount) | Called after drawing dialogs |
AfterDrawNoDialogs(0) | Called in place of drawing dialogs when no dialog is active. The reason it's separated from AfterDrawDialogs event is to improve performance, because this event would usually stay unused. |
NewDialog(dlg, id) | This function is called once a new dialog finishes creation. Note that in case of house dialogs, they internally create an extra dialog with DlgID = 1 for dialog topics, this leads to NewDialog event getting triggered for that extra dialog before the one for the base dialog, however both dialogs are already created and added to Game.Dialogs when either event fires. |
BeforeDestroyDialog(dlg, id) | |
DestroyDialog(dlg, id) | |
AfterDestroyDialog(dlg, id) | |
BeforeShowOODialog{ClassPtr = This lets you know what dialog is being created even if DialogPtr got changed by the event DialogPtr Param } | |
AfterShowOODialog{ClassPtr = This lets you know what dialog is being created even if DialogPtr got changed by the event DialogPtr Param Result } | |
CloseOODialog{DialogPtr } | |
DrawProgressBar() | |
HideProgressBar() |
Core\ timers.luaEvents: |
|
Tick() |
Modules\ Faces.luaEvents: |
|
BeforeUpdateFaces(JustRead) | Triggered when "Faces.txt" is read or when Update is called. |
AfterUpdateFaces(JustRead) | Triggered after the changes to Faces were applied. |
Modules\ PaperDoll.luaEvents: |
|
ReloadPaperDollGraphics() | When corresponding ReloadPaperDollGraphics function is called |
PaperDollHiddenPieces({arm1 = 1st arm when holding a weapon arm1f = 1st arm without a weapon arm2 = 2nd arm without 2-handed weapon arm2b = 2nd arm without 2-handed weapon, drawn behind armor arm2f = 2nd arm when not holding anything arm2fb = 2nd arm when not holding anything, drawn behind armor arm2h = 2nd arm for 2-handed weapon arm2hb = 2nd arm for 2-handed weapon, drawn behind armor evil = evil path interface in MM7 game = in game (for BackDoll in MM8) good = good path interface in MM7 hand1 = 1st hand when holding a weapon hand1a = 1st hand (always drawn) hand1f = 1st hand when not holding a weapon hand2 = 2nd hand for dual-wielding hand2f = 2nd hand when not holding anything hand2h = 2nd hand for 2-handed weapon menu = in new game menu (for BackDoll in MM8) neutral = neutral interface noring = rings menu closed ring = rings menu open shield = drawn when holding a shield }, player, InMenu, DrawOffsetX, DrawOffsetY) |
Here I've described pieces that PaperDoll module handles automatically. You can define when your own pieces are drawn through PaperDollDrawOrder array and hide them conditionally here. InMenu is true if it's a new game menu in MM8, in which case DrawOffsetX and DrawOffsetY are non-zero. |
PaperDollGetItems(t, player, InMenu, DrawOffsetX, DrawOffsetY) |
Lets you modify weared items, such as add new weared item slots. E.g. setting t.My = 1 would draw pl.Items[1]. Setting t.My = true would make ":My" drawn the same way ":Player" is drawn. You'll also need to add "My" to PaperDollDrawOrder. InMenu is true if it's a new game menu in MM8, in which case DrawOffsetX and DrawOffsetY are non-zero. |
PaperDollBeforeDoll(player, InMenu, DrawOffsetX, DrawOffsetY) |
Called before the paper doll is drawn after drawing the background. InMenu is true if it's a new game menu in MM8, in which case DrawOffsetX and DrawOffsetY are non-zero. |
PaperDollAfterDoll(player, InMenu, DrawOffsetX, DrawOffsetY) |
Called after the paper doll is drawn before drawing UI elements. InMenu is true if it's a new game menu in MM8, in which case DrawOffsetX and DrawOffsetY are non-zero. |
PaperDollAfterDraw(player, InMenu, DrawOffsetX, DrawOffsetY) |
Called after the paper doll is drawn. InMenu is true if it's a new game menu in MM8, in which case DrawOffsetX and DrawOffsetY are non-zero. |
General Functions
Structs\After\ Backup.luaFunctions: |
|
LocalFile(t) | Pass any table from Game.* to this function to make it restore its original state after the player exits the map. |
LocalMonstersTxt() | |
LocalHostileTxt() | [MM7+] |
LocalNPCNews() |
Structs\After\ Dialogs.luaFunctions: |
|
CustomDialog{...;CustomDialog DlgID Height Left Param StrParam Top Width } |
Structs\After\ Draw.luaFunctions: |
|
DrawScreenEffectD3D(BitmapIndex, u, v, du, dv, x1, y1, x2, y2, cl, cl2, cl3, cl4) |
Structs\After\ Functions.luaFunctions: |
|
SplitSkill(val) | |
JoinSkill(skill, mastery) | |
XYZ(t, x, y, z) |
Get X,Y,Z fields: local x, y, z = XYZ(Party) Set X,Y,Z fields: XYZ(Party, x, y, z) Enumerate "X", "Y", "Z" strings: for X in XYZ do print(Party[X]) end |
ClearConsoleEvents() | |
Message(text, KeepSound, global) | |
Question(text, qtext) |
Returns the reply. text is shown as message, qtext is shown at the beginning of reply field. Doesn't work in houses in MM7. |
ReplaceNPCTopic(old, new) | Searches through all NPC topics and replaces one topic with another |
SuppressSound(on) | |
ExitScreen(process) | Simulates Esc press |
ReloadHouse(id) |
Rebuilds house dialog, e.g. after you've moved an NPC to or from this house. Can seemlessly transition between different houses if id is specified. Simply closes the house if id is '-1'. |
SwitchHouseMovie(s, loop) | Exits currently playing movie and loads the specified one |
DrawSimpleMessage() | Draw simple message in screens where it isn't normally supposed to be |
HouseMessage(text) | show a message in houses in screens that don't normally support it |
AddGoldExp(gold, exp) | |
TakeItemFromParty(id, keep) | id can also be a table or a table of tables. See Quest Alchemy.lua from quest examples |
CheckMonstersKilled{Group Monster MonsterIndex NameId Count InvisibleAsDead } | See Quest Kill Monsters.lua from quest examples |
EnumAvailableSkills(class) | Returns Skill, MaxMastery |
RGB(r, g, b) |
else end end |
StrLeft(v) | |
StrRight(v) | |
StrColor(r, g, b, s) | |
PcxCache(EnglishD) |
Example – loading: local t = PcxCache(true) local win = t["winBG.PCX"] local lose = t["LOSEBG.PCX"] Drawing: Screen.DrawPcx(0, 0, win) Clearing cache deletes all loaded images: t("Clear") You can also reload all images, say, if you conditionally load a LOD with an inteface skin (all obtained addresses stay the same): t("Reload") Pass file name as second argument when invoking "Clear" or "Reload" to handle a specific file only. |
IconCache() | Same as PcxCache, but for in-place icons, which means loaded icons aren't stored in Game.IconsLod.Bitmaps, but instead stored in the cache until it's cleared |
SummonMonster(id, x, y, z, treasure, place) |
Unless treasure is true, the monster doesn't have any items or gold. place defines monster index in Map.Monsters array if specified. |
SummonItem(number, x, y, z, speed) | |
RebildIDList() | |
ChangeSprite(n, name) | |
CreateSprite{name, x, y, z;Bits X Y Z } |
Warning: In MM7+ sprite bits are stored in the save game. Thus, adding sprites would cause mismatch between Map.SanitySpritesCount and Map.Sprites.Count next time the game is loaded (see LoadSavedMap event for more info on that). If you do decide to use this function in a live MM7+ game, I recommend setting Map.Sprites.Count to normal amount in BeforeSaveGame event and restoring current amount in AfterSaveGame. |
MoveModel(m, dx, dy, dz, MoveParty) | MoveParty isn't supported yet |
Structs\After\ LocalizationAndQuests.luaVariables: |
|
TakeQuestOperation |
Should be either "Add" or "Set". This determines the overlay flash on character face when a quest is taken. [MM6, MM8] Defaults to "Add". [MM7] Defaults to "Set". |
Quests | |
GenerateLocalization_BakFiles | Set it to false in a General script to turn off *.bak files creation when generating localization |
GameLocalizationIgnore | List of table names in Game structure that shouldn't be included in localization by GenerateGameLocalization |
GameLocalizationSchema | Defines which files contain which names of Game structure tables and more (see in code) |
QuestNPC | |
vars.Quests[name] | Quest states: nil, "Given", "Done" or a custom state. |
vars.QuestAwards[name] | |
vars.QuestAutonotes[name] | |
Functions: |
|
LocalizeAll(t, over) |
See Localization. Possible values of over: true: permanent setup – overwrite all (used in Localization scripts only) false: temporary setup – overwrite temporary setup only "update": remove all previous temporary setup, overwrite temporary setup only |
Localize(t, over, lev = 1) |
See Localization. Possible values of over: true: permanent setup – overwrite all (used in Localization scripts only) false: temporary setup – overwrite temporary setup only "update": remove all previous temporary setup, overwrite temporary setup only |
GenerateQuestsLocalization(IsLua) | Generates localization file for quests created with functions from this file. |
GenerateLocalization(IsLua, IncludeQuests) |
Generates localization files for scripts that call Localize or LocalizeAll functions. Pass IsLua = true to generate *.lua files instead of *.txt tables. Whichever is more convenient for you. Unless IncludeQuests is false, localization for quests is also generated. |
GenerateGameLocalization() | Generates localization files for various game data found in TXT files. Returns(in text form) the list of files not included in the generated localization. |
ShowQuestEffect(flash_book, operation) |
Plays sound and shows visual effect on current character's face. If flash_book is true, the quest book will start flashing. |
ShowAwardEffect(exclude, operation) |
Plays sound and shows visual effect on all characters' faces. exclude can be a function(player, slot) that returns true if the character should be excluded from the effect. |
ShowAutonoteEffect(category, just_sound, operation) | Plays sound and shows visual effect on current character's face |
AutoQuest(t, text) | t can be a function(t), which returns true if quest should be visible in the quest log |
AutoAward(t, text, sort) | t can be a function(t, player_index, player), which returns true if award should be visible on the Awards page |
AutoAutonote(t, text, category) | t can be a function(t), which returns true if autonote should be visible |
GetLocalName(name, lev) | |
Autonote(name, cat, text) | Creates a named autonote. To operate on global named autonotes, use ":" at the beginning of its name. |
AddAutonote(name, force) | Adds a named autonote to Auto Notes. force = true makes it emit the sound even if autonote is already added |
CheckAutonote(name) | Returns true if named autonote was added to Auto Notes |
FindAutonote(name, must) | |
UpdateNPCQuests() | |
QuestBranch(branch, persist) |
Sets current dialog branch to branch if it's specified. persist parameter makes the branch persist after you reenter NPC dialog. Returns current branch when called without parameters. If there is no persisted branch name, when you enter NPC dialog the branch is set to "". |
QuestBranchScreen(branch) | Switches dialog branch to branch, but returns to current branch upon pressing Esc. |
ExitQuestBranch(all) | Exits current branch opened with QuestBranchScreen. If no branch screens are left open, simulates Esc press to exit the dialog. |
GetQuestBranchStack() | Returns a table with branch names stored by QuestBranchScreen function (on each call it adds previous branch to the end of this table). |
Quest{name;AddAutonote AddAward AddQuestBit After Award AwardIndex Awards BaseName Branch CanAddAutonote CheckDone CheckGive Done DoneState Event Execute Exp Experience FirstStdTopic GetGreeting GetTopic Give GivenItem GivenState Gold IsAwarded IsGiven KeepQuestItem NPC Name NeverDone NeverGiven Quest QuestGold QuestIndex QuestItem RewardItem SetTexts Slot StdTopic StdTopicDone StdTopicGiven StoreAwards TakeQuestOperation Texts Undone Ungive } | See quest examples |
Greeting{text, textSeen;Text TextSeen ... Quest() parameters ... } | See quest examples |
NPCTopic{topic, text;Topic Text ... Quest() parameters ... } | See quest examples |
KillMonstersQuest{name = nil, ... A list of either tables with CheckMonstersKilled() parameters or partial completion messages ...;... Quest() parameters ... } | See Quest Kill Monsters.lua from quest examples |
QCheck(name, state) |
Mostly for backward compatibility. A function to show a topic only when the name quest is in state state. name defaults to current quest name. Examples: QCheck('MyQuest') = || vars.Quests.MyQuest == nil QCheck('MyQuest', 'Done') = || vars.Quests.MyQuest == 'Done' QCheck() = |t| vars.Quests[t.BaseName] == nil QCheck(nil, 'Done') = |t| vars.Quests[t.BaseName] == 'Done' |
Structs\After\ Spells.luaFunctions: |
|
BeginGrabObjects() | Starts recording all spawned objects. You can call it prior to calling evt.SummonObject or Game.SummonObjects and then call GrabObjects afterwards to get all created objects. |
GrabObjects() | Returns all (up to 1000) objects spawned since last call to BeginGrabObjects |
Core\ Common.luaFunctions: |
|
assertnum(v, level, msg) | |
tget(t, k, ...) |
Returns t[k] if it exists, otherwise sets t[k] = {} and returns t[k]. The process is repeated for additional parameters, e.g. t[k][k1] |
pcall2(...) | Like pcall, but in case of an error shows error message |
xpcall2(...) | Like xpcall, but in case of an error shows error message |
cocall(f, ...) | Same as coroutine.resume(coroutine.create(f), ...) |
cocall2(f, ...) | Like cocall, but in case of an error shows error message |
dofile2(path, ...) | Loads file in protected mode, in case of an error shows error message. Return format is the same as that of pcall |
coroutine.resume2(...) | Like coroutine.resume, but in case of an error shows error message |
os.GetErrorText(code) | |
path.find(filter, dir) | |
path.FindFirst(filter, dir) | |
path.findfirst(filter, dir) | |
os.chdir([dir]) | Returns previous current directory. Call without parameters to look up current dirrectory |
os.mkdir(dir) | |
os.copy(old, new, FailIfExists) | |
path.GetRelativePath(from, to, isDir) | |
string.convert(s, encFrom, encTo, defChar) | |
debug.FunctionFile(f) |
Core\ Debug.luaFunctions: |
|
debug.HideConsole() |
Core\ ErrorFunction.luaFunctions: |
|
errorinfo(s) | |
tostring2(v, lim, tabs) |
Core\ RSMem.lua…Variables: |
|
mem.i1[p], mem.i2[p], mem.i4[p], mem.i8[p] |
Read/write signed integer at address p. Can also be called to convert a number to specified format. For example, for a function that returns a signed byte: return mem.i1(mem.call(func, 0)) This would treat function call result as a signed byte, ignoring 3 higher-order bytes. |
mem.u1[p], mem.u2[p], mem.u4[p], mem.u8[p] | Read/write unsigned integer at address p. Can also be called to convert a number to specified format. |
mem.r4[p], mem.r8[p], mem.r10[p] | Read/write a floating-point number at address p |
Functions: |
|
mem.string(p, size, ReadNull) |
mem.string(p) – read null-terminated string mem.string(p, size) – read null-terminated string no bigger than size bytes mem.string(p, size, true) – read size bytes as string |
mem.call(p, cc, ...) |
p is the function address, cc is the number of parameters passed through registers: 0 – __stdcall or __cdecl, 1 – __thiscall, 2 – __fastcall, 3 – 3rd parameter in eax (eventhough the game doesn't use it), ... are the parameters. Another way to call: mem.call{p = 0x441EFD, cc = 0, ...} |
mem.IgnoreProtection(on) | Pass true to be able to modify code with mem.i4 and such, pass false after you've finished. |
mem.prot(on) | Same as above. |
mem.malloc(size) | Allocate memory with the malloc function of the game. Shouldn't be called before the game starts initializing, it may cause slowness, because the allocator isn't yet initilized. Instead call mem.allocMM or mem.StaticAlloc. |
mem.alloc(size) | Same as above. |
mem.free(p) | |
mem.realloc(p, size) | |
mem.new(size, f, ...) | |
mem.StaticAlloc(size) | |
mem.copy(dest, src, count) | count can be omitted if src is a string or a structure |
mem.fill(ptr, n, c = 0) | |
mem.cmp(ptr1, ptr2, n) | |
mem.LuaAlloc(size) | |
mem.func(...) | |
mem.LoadDll(name, cc) | |
mem.UnloadDll(t) | |
mem.GetProcAddress(p, proc) | |
mem.structs.getdefine() | |
mem.structs.getunions() | |
mem.structs.CustomType(name, size, f) | |
mem.struct(f, class, p) | |
mem.GetHookSize(p) |
Returns n, short, long. n is the number of bytes occupied by instructions at address p. n is 5 or more, because placing a hook requires 5 bytes. short is true if the code contains a short jump leading outside of it. long is true if the code contains a near jump or call to a relative address. |
mem.GetInstructionSize(p) |
Returns n, short, long. n is the number of bytes occupied by the instruction at address p. short is true if the instruction is a short jump and it doesn't lead to itself. long is true if the instruction is a near jump or call to a relative address. |
mem.findcode(p, s, p2) | Takes a string to search for. Only checks for a match at the start of a new instruction. |
mem.findcall(p, fptr, p2) | Finds a call to fpts starting at p and stopping at p2 (optional). If fptr isn't specified, searches for any call instruction. |
mem.enumcalls(p1, p2, fptr) | Enumerates all calls to fptr. If fptr isn't specified, enumerates all function calls. |
mem.hook(p, f, size) | Primitive call hook: 5-byte call instruction that calls into Lua code |
mem.hookjmp(p, f, size) | Primitive jump hook |
mem.hookalloc(size) | Allocate memory for code |
mem.hookfree(p) | |
mem.copycode(ptr, size, MemPtr, NoJumpBack, DuplicateHooks) |
Copies standard code into a memory block and then writes a jump back into the function (the copied code must not contain short jumps that lead outside of it) MemPtr can optionally specify a pre-allocated memory address. If DuplicateHooks is true, Lua hooks are kept in both new and old code, otherwise they're moved to the new location. |
mem.autohook(p, f, size) |
hookjmp with automatic calling of overwritten code (see copycode note) If the function returns true, the jump to original code isn't performed |
mem.autohook2(p, f, size) |
hookjmp with automatic calling of overwritten code (see copycode note) The function is called after the overwritten code If the function returns true, the jump to original code isn't performed |
mem.bytecodehook(p, code, size) |
Like autohook, but takes a compiled Asm code string as parameter code can also be a function f(ptr). ptr is the address of memory allocated for hook code or nil (to calculate size) |
mem.bytecodehook2(p, code, size) |
Like autohook2, but takes a compiled Asm code string as parameter See note of bytecodehook about code |
mem.bytecodepatch(p, code, size) |
Replaces original instructions with new ones, jumping out if needed See note of bytecodehook about code |
mem.asm(code) | Compiles Asm code and returns resulting binary as string |
mem.asmhook(p, code, size) |
Like autohook, but takes an Asm code string as parameter. Example (from MM7): mem.asmhook(0x441D4C, [[ cmp dword [0xE31AF0], 0 jnz absolute 0x441D51 ]]) Original Asm code: .text:00441D4C call sub_4C2E6C .text:00441D51 mov ecx, offset unk_511768 Resulting Asm code: .text:00441D4C jmp @p .text:00441D51 mov ecx, offset unk_511768 @p: cmp dword [0xE31AF0], 0 jnz 0x441D51 call sub_4C2E6C jmp 0x441D51 |
mem.asmhook2(p, code, size) |
Like autohook2, but takes an Asm code string as parameter Example (from MM6): local p = mem.asmhook2(0x43C8E3, [[ mov [edi], esi ]]) Original Asm code: .text:0043C8E3 mov edi, offset CurrentEvtLines .text:0043C8E8 rep movsd Resulting Asm code: .text:0043C8E3 jmp @p .text:0043C8E8 rep movsd @p: mov edi, offset CurrentEvtLines mov [edi], esi jmp 0x43C8E8 |
mem.asmpatch(p, code, size) | Replaces original instructions with new ones, jumping out if needed |
mem.asmproc(code) | Creates an Asm function |
mem.hookfunction(p, nreg, nstack, f, size) |
Set hook at the beginning of a function, allows calling the original function (see copycode note) Function f is as follows: f(d, def, params...) Here d is HookData object, def is the default function defined as def(params...) and params... are the parameters. |
mem.hookcall(p, nreg, nstack, f) | Replaces an existing CALL instruction and uses the same protocol as hookfunction |
mem.luaproc(f, nreg, nstack) | Creates a Lua callback (because any use of FFI for function calls leads to random bugs) |
mem.nop(p, n) | Writes n NOPs. If n is omitted, replaces a single instruction at the given address with NOPs |
mem.nop2(p, p2) | Writes NOPs from p to p2 - 1 |
Core\ RSPersist.luaFunctions: |
|
persist(t, perm) | |
unpersist(buf, perm, data) |
Core\ events.luaVariables: |
|
VFlipUnfixed | [MM6] Textures on horizontal outdoor surfaces are flipped vertically. This is default to preserve look of standard maps. |
RespectMonsterExp |
Set it to true to take experience from map monster structure rather than from monsters.txt based on monster Id. In MM8 only Armageddon uses monster XP from monsters.txt, other ways of killing monsters use experience value from monster structure. |
Functions: |
|
HookManager(ref) |
Allows switching all hooks installed by it on/off. ref table is used for substitutions in Asm code: "%key%" is replaced with ref[key], "%%" is replaced with "%". All hook functions are supported, but memory-editing arrays are supported through set function. Example 1 – substitution: local ArtifactBonus = mem.StaticAlloc(8) HookManager{ buf = ArtifactBonus, }.asmhook2(0x48F60A, [[ mov [%buf%], eax mov [%buf%+4], ecx ]]) Example 2 – on/off: local hooks function ArcomageRequireDeck(on) if hooks then hooks.Switch(not on) elseif not on then hooks = HookManager() hooks.nop(0x4B3A06) -- no arcomage deck requirement hooks.nop(0x4B8A31) -- no arcomage deck requirement end end Example 3 – editing memory: local hooks = HookManager() hooks.set('i4', 0x469BBE+1, 0x10000) -- instead of mem.prot(true); mem.i4[0x469BBE+1] = 0x10000; mem.prot(false) hooks.set('u2', 0x472FB7, 0x9090) -- instead of mem.prot(true); mem.u2[0x472FB7] = 0x9090; mem.prot(false) hooks.asmpatch(0x472F5E, "add eax, ecx", 2) DisableVFlipFix = |on| hooks.Switch(not on) P.S. Don't try to run the code of these examples, they are for illustration. |
CallDefaultWindowProc(Msg, WParam, LParam) | |
EnableLuaDataCompression(on) | |
FixVFlip() | [MM6] Turns off texture flip on horizontal outdoor surfaces. Note that the editor accounts for vertical flip, so you probably shouldn't ever call this function. |
EnableMonstersCastingAnimation(on) | [MM7+] Makes monsters play their regular attack animation rather then idle animation when casting damage spells if on is true. |
IsAttackOnPlayer() |
As first value returns true if the target of attack currently being handled is a player, false if it's a monster As second value returns attack information structure as returned by WhoHitMonster and WhoHitPlayer functions |
WhoHitMonster() |
If a monster is being attacked, returns t, TargetMonsterIndex. t.Player and either t.PlayerSlot[MM6-7] or t.PlayerIndex[MM8] are set if monster is attacked by the party. t.Monster, t.MonsterIndex and t.MonsterAction fields are set if monster is attacked by another monster. t.Object and t.ObjectIndex are set if monster is hit by a missile. t.Spell, t.SpellSkill and t.SpellMastery are set if a spell is being used. Note that t.Object can be set at the same time as t.Monster or t.Player. |
WhoHitPlayer() |
If a player is being attacked, returns t, PlayerSlot. t.Monster, t.MonsterIndex and t.MonsterAction fields are set if player is attacked by a monster. t.Object and t.ObjectIndex are set if player is hit by a missile. t.Spell, t.SpellSkill and t.SpellMastery are set if a spell is being used. Note that t.Object and t.Monster can be set at the same time if the projectile was fired by a monster. |
Core\ evt.luaVariables: |
|
vars | Variables that are stored in saved game |
mapvars | Variables stored in a saved game that belong to current map. On map refill mapvars are cleared, but old table is stored in Map.Refilled. |
evt.CanShowTopic[] | [MM7+] Functions that can return true or false to change topic visibility |
evt.map[] |
Event handlers Event indexes convention: Indexes 20000 – 22999 are for sprite events, so that event (20000 + i) corresponds to Map.Sprites[i]. |
evt.hint[] | |
evt.MazeInfo | [MM6] |
evt.str[] | |
evt.house[] | House name used for hint |
evt.Player | |
evt.CurrentPlayer | |
Functions: |
|
evt.InGlobal() | |
evt.ForPlayer(n) | |
evt.HouseDoor(evtId, houseId) |
Here's what it does: evt.house[evtId] = houseId evt.map[evtId] = function() evt.EnterHouse(houseId) end |
Core\ evtdeco.luaFunctions: |
|
evt.Decompile(fileName, funcMode, outFile, asTxt) |
Core\ main.luaVariables: |
|
mem.EditPChar | Editable PChar |
mem.ConstPChar | Editable write-protected PChar |
Functions: |
|
mem.allocMM(size) | Calls allocation function that MM uses for most of things. Note that my patch intersects these calls and uses Delphi memory manager to do the allocation. |
mem.freeMM(p) | Free memory allocated by mem.allocMM. |
mem.reallocMM(p, OldSize, NewSize, NoFree) |
Resizes memory, reallocating as necessary, preserving content. Returns new (or the same if no reallocation occured) address. If NoFree is true, the memory at p isn't freed. This is useful for extending static arrays of the game. p can be a sctrucure, in which case its '?ptr' field is modified automatically. |
mem.resizeArrayMM(t, n) | Resizes an array allocated with mem.allocMM function. n is the new count. |
MessageBox(text, caption, typ) | typ can be a numeric value that MessageBox WinAPI function accepts or one of predefined strings: "error" (OK), "warning" (OK), "warn" (OK/Cancel), "confirm" (OK/Cancel), "confirmsnd" (OK/Cancel). Returns a number returned by the WinAPI funciton: 1 – OK, 2 – Cancel. |
debug.Message(...) | Shows debug console. Message includes file name and line number, and than each argument is added to the message as a new line of text. |
debug.ErrorMessage(msg) | |
ErrorMessage(msg) | |
AddScriptsPath(s) | |
ReloadLocalization() |
Core\ npc.luaFunctions: |
|
FindHiredNPC(NPC) | Finds the prototype NPC in Game.NPC array for the supplied hired NPC. Returns nil if it's a street NPC. |
HireAllPeasants(on) |
[MM7] Normally first level of each peasant kind just talls you some news and can't be hired. Pass on = true to this function to make all peasants hireable. Pass on = nil to obtain current state of it. |
GetCurrentNPC() | Returns NPC index |
GetCurrentAnyNPC() | Returns NPC, Index, Kind |
ArcomageRequireDeck(on) |
[MM7] Pass false to remove the deck requirement. Pass on = nil to obtain current state of it. |
TrainPerWeek(n) | [MM7+] Set number of levels the party can train per training session. 0 means any number of levels, as in MM6. 1 is MM7+ default. If n isn't specified, returns current setting. |
SkillToHouseTopic(i) | |
HouseTopicToSkill(i) |
Core\ timers.luaExamples:Damage selected player every minute: Timer(function() evt.DamagePlayer{Damage = 1} end, const.Minute) Check some condition (e.g. that you killed all monsters) every 5 minutes, including the moment you enter the map: Timer(CheckCondition, 5*const.Minute, true) Refill a well each time a week passes: RefillTimer(RefillWell, const.Week) Refill a well every week on Tuesday at 3 AM: RefillTimer(RefillWell, const.Week, const.Day + 3*const.Hour) Make "eat" sound at 3 AM every day: Timer(function() evt.PlaySound(Game.Version == 8 and 144 or 211) end, const.Day, 3*const.Hour, false) Variables: |
|
Keys[key] |
Example 1 (recommended): function Keys.F2(t) Message("F2 pressed") end Example 2 (not recommended): Keys[const.Keys.F2] = function() Message("F2 pressed") end These examples are actually handled differently. The first one is equivalent to using events.KeyDown and checking t.Key. The second is mostly for backward compatibility. Instead of relying on messages, it checks key state on every frame and calls the function if the key was pressed. It may miss a very short key press. |
Functions: |
|
timeGetTime() | Returns time since Windows has started in milliseconds |
Timer(f, Period = const.Minute, [Start,] [PastAware,] [Exact]) |
f = function(TriggerTime, Period, LastTick, Tick): Function to call when the timer is triggered. Start defaults to Game.Time + Period if not specified (unless PastAware = true, in which case it deafults to Game.Time). PastAware = remember last visit time and fire right away if timer condition was met in the period of your absence. Defaults to true if Start is specified and false otherwise. Possible Exact values: false: re-fires after at least Period passes since last invocation (this is the default if Start is not specified). true: fires whenever (Start + Period*N) line is crossed (this is the default if Start is specified). function(TriggerTime, Period, LastTick, IsInit): returns next trigger time when called. IsInit = true if it's called by Timer function itself which happens if Start has already passed. Note that the timer remembers last time you were in the location, so for example, an exact weekly timer would fire right away if you haven't visited the map for a week. |
RefillTimer(f, Period = const.Day, Start) |
f = function(TriggerTime, Period, LastTick, Tick): Called when the timer is triggered. If Start is not specified, triggers when Period passes since last time it was triggered and on map refill. If Start is specified, acts like exact Timer, but also fires at map refills. |
Sleep(time, realtime, screens, NoYield) | |
Sleep2(f, time, realtime, screens) | |
RemoveTimer(f) | You can remove the timer being executed by calling this function without a parameter |
Keys.IsPressed(key) | String representations of const.Keys are also supported, e.g. Keys.IsPressed"SHIFT" |
Keys.IsToggled(key) | String representations of const.Keys are also supported, e.g. Keys.IsToggled"CAPSLOCK" |
Modules\ Faces.luaTogether with PaperDoll.lua can be used to create new character portraits and voices, or add them from other games. Unlike PaperDoll.lua, everything here is 0-based. If included, creates and/or loads "Faces.txt" and "Face Animations.txt" data tables.Including the module: local P = require"Faces" You can add extra columns into "Faces.txt": AllowVoice – [MM7+] Enable/disable selecting voice on start (if not specified, the value of Allow is used). Voice – [MM7+] Use specified default voice instead of the one with the same number as the face. Expressions – Expressions list to use. Default is "Expressions". Use if you combine MM6 faces with faces from other games. Sounds – Sounds list to use. Default is "Sounds". Use if you combine MM6 voices with faces from other games. SoundsOffset – This value will be added to the sound id (the one being searched in dsounds.bin). You will need this if you add voices from all 3 games. Note that AllowVoice, Female, Sounds and sound counts are properties of voice, the rest are properties of face. If you leave any field empty, the default value would be used. You can add extra columns into "Face Animations.txt". If their names are valid identifiers, thay would be read as lists. E.g. imagine you've added MM6 characters into another game – then you can add "Expressions6" and "Sounds6" columns there and set Expressions and Sounds columns for these characters to "Expressions6" and "Sounds6" respectively. You would however also need to join PFT.txt files and replace indexes in expressions lists accordingly. The way animations work: random expression is picked from the list in "Expressions" column of "Face Animations.txt" (or "Expressions6" column in the above example). This number is looked up in PFT.txt and the animation is played. Expression 21 is special – it shows the character speaking the voice line as long as it lasts. When it comes to voice, sound index is picked at random from the "Sounds" column list (or "Sounds7" in the above example), then sounds count for the voice is looked up and one of the variations of the sound is played. Up to 2 variations are supported. Variables: |
|
P.ExtraFields | You can define you own functions to read extra fields from Faces.txt |
P.Faces | |
P.FaceAnimations | |
Functions: |
|
P.Update() | Call it if you change Faces table manually. Triggers UpdateFaces event. |
Modules\ KeepLogs.luaFunctions: |
|
ViewLog(n) |
Shows contents of console log from previous run of the game. n is the log index or 0 to show current output log. |
Modules\ PaperDoll.luaTo use, first place the PaperDol.txt file from here corresponding to your game of choice into DataFiles folder (create it right next to Data folder of the game if it doesn't exist). Modify it as needed. You'll later distribute this file inside a LOD achive of your mod.Including the module: require"PaperDoll" How it works: PaperDollDrawOrder defines the order in which pieces of body and clothing are drawn. PaperDollHiddenPieces event can be used to change which pieces would be drawn. PaperDol.txt can specify custom graphics and coordinates for any piece. Lines further in the table take precedence. If Doll includes current player, Piece matches piece being drawn and ItemPicture matches picture of the item being drawn (or ":Player" for a piece of player body), specified Image, X and Y values are used. A special case is when you specify item slot as ItemPicture, e.g. ":Armor". In this case X and Y act as offsets added to whatever coordinates are specified for the item in Armor slot. If you specify Mul for a slot, coordinates of the item are multiplied by it. It can also contain 4 values "a1,a2,a3,a4" that work this way: x = a1*X + a2*Y; y = a3*X + a4*Y. Setting Mul to "0,-1,1,0" or similar 90 degree rotation matrices would cause the item to be drawn rotated in MM8. Coordinates are given relative to the following point: MM6 and MM7 – (481, 0), MM8 – (467, 23). You can change it by modifying Game.DialogLogic.PaperDollPositionX and Game.DialogLogic.PaperDollPositionY, but changing it only affects paper doll position and not BACKDOLL or interface above paper doll. Options for ItemPicture values: item1 – Override "item1" graphics :Player – Override piece of the body :ExtraHand.item1 – Override "item1" graphics if equipped in the second hand :Player:if:Armor – Override piece of the body if any Armor is equipped :Player:if.item1 – Override piece of the body if "item1" is equipped :Player:if:Armor.item1 – Override piece of the body if "item1" is equipped as Armor :Belt:if:Armor.item1 – Override any belt if "item1" if equipped as Armor :Belt.item2:if:Armor – Override "item2" equipped as Belt if anything is equipped as Armor :Belt.item2:if.item1 – Override "item2" equipped as Belt if "item1" is equipped :Belt.item2:if:Armor.item1 – Override "item2" equipped as Belt if "item1" is equipped as Armor "insert" command: ItemPicture may start with ":insert" in order to insert a new piece to draw into PaperDollDrawOrder. It's an alternative to doing that in Lua code. TODO: I'll describe it some other day. Variables: |
|
PaperDollCategories | Maps category name to a function(i) that returns true if player face number i belongs to the category. Use PaperDollAddBodies or PaperDollAddRace to populate in a custom way. |
PaperDollMainPieces |
Specifies which pieces inherit item graphics by default. For slots not specified here piece with empty name is the main one. Default: {ExtraHand = {hand2 = true, shield = true}, Gauntlets = {ring = true}, Amulet = {ring = true}, Ring1 = {ring = true}, Ring2 = {ring = true}, Ring3 = {ring = true}, Ring4 = {ring = true}, Ring5 = {ring = true}, Ring6 = {ring = true}} |
PaperDollBaseRace |
[MM7] Races that share the "Base" paper doll. Default: {[const.Race.Human] = true, [const.Race.Goblin] = true, [const.Race.Elf] = true} After altering it, call this command: PaperDollAddRace('Base', PaperDollBaseRace) |
PaperDollSpecialBodies |
After altering it, call PaperDollAddBodies. Here are default MM8 values: {[20] = 'Minotaur', [21] = 'Minotaur', [22] = 'Troll', [23] = 'Troll', [24] = 'Dragon', [25] = 'Dragon'} |
PaperDollTwoHandedSpear | If set to true (default in MM6 and MM7), spears are treated as two-handed weapons when used without a second weapon. Gets initialized according to game version. |
PaperDollDrawOrder |
List of things to draw in the form of "Slot.Piece.DrawStyle" (or "Slot.Piece", or "Slot"). Slot is either one of the wearable item slots (e.g. "Bow") or a pseudo-slot like "Player". Piece defines what element of that item gets drawn. DrawStyle can be set to "opaque" for opaque bitmaps ("red" and "green" are also supported) or "rect" to have the whole item rect count as item in regards to clicks. Default: {'BackDoll..opaque', 'BackDoll.menu.opaque', 'BackDoll.game.opaque', 'BeginDoll', 'Bow', 'Cloak', 'Armor.back', 'Belt.back', 'Player', 'Player.arm1', 'Player.arm1f', 'Player.arm2b', 'Player.arm2hb', 'Player.arm2fb', 'Player.shield', 'Player.hair', 'Armor.back2', 'Helm.back', 'Boots.back', 'Armor', 'Boots', 'Armor.front', 'Armor.arm1', 'Armor.arm1f', 'Belt', 'Player.arm2', 'Player.arm2h', 'Armor.arm2', 'Armor.arm2h', 'Player.arm2f', 'Cloak.scarf', 'Player.scarf', 'Helm', 'Cloak.scarf2', 'MainHand', 'Player.hand1a', 'Player.hand1', 'Player.hand1f', 'Armor.hand1a', 'Armor.hand1', 'Armor.hand1f', 'ExtraHand.hand2', 'ExtraHand.shield', 'Player.hand2', 'Player.hand2f', 'Player.hand2h', 'Armor.hand2', 'Armor.hand2f', 'Armor.hand2h', 'EndDoll', 'Magnify.noring', 'BackHand.ring', 'Border.good', 'Border.evil', 'Border.neutral', 'Ring1.ring.rect', 'Ring2.ring.rect', 'Ring3.ring.rect', 'Ring4.ring.rect', 'Ring5.ring.rect', 'Ring6.ring.rect', 'Amulet.ring.rect', 'Gauntlets.ring.rect', 'Magnify.ring', } See PaperDollHiddenPieces event for conditions under which each piece is visible. |
Functions: |
|
PaperDollAddRace(name, races) | |
PaperDollAddBodies(bodies) |
Defines doll categories for bodies array together with male and female variations of each. If bodies table isn't specified, uses PaperDollSpecialBodies table and defines the "Base" paper dolls as well. |
AddPaperDollGraphics(t) |
t can be a string following the "PaperDol.txt" convention or a table that's similar to the result of calling ParseNamedColTable for such file. Example – loading additional paper doll file for mods: events.ReloadPaperDollGraphics = || AddPaperDollGraphics(Game.LoadTextFileFromLod'PaperMod.txt') |
ReloadPaperDollGraphics() | Reloads "PaperDol.txt" and calls ReloadPaperDollGraphics event. Very handy while tweaking "PaperDol.txt" |
Structures
GameFields: |
|
Version | (6 – 8) |
Arcomage | [MM7+] |
Races | [MM7] |
Classes | |
ClassKinds | |
Party | |
Map | |
Mouse | |
Weather | |
Screen | |
PatchOptions | |
CustomLods | |
WindowHandle | |
Windowed | |
CurrentPlayer | |
SkillRecoveryTimes[skill] | |
MinMeleeRecoveryTime | |
CurrentScreen | :const.Screens |
CurrentCharScreen | :const.CharScreens |
MainMenuCode | -1 = in game, 0 = in main menu, 1 = show new game, 2 = show credits, 3 = show load menu, 4 = exit, 5 = loading game, 6 = in new game screen, 8 = in credits, 9 = load game, 10 = load level [MM7+] |
LoadingScreen | |
DialogNPC | |
NPCCommand | |
HouseScreen | |
HouseNPCSlot |
If Game.HouseOwnerPic isn't 0, the value of 1 refers to the shop keeper and higher value needs to be reduced by 1 before accessing . If Game.HouseExitMap isn't 0, last slot is occupied by map enter icon. |
HouseNPCSlotsCount | |
HouseCost | |
HouseAllowAction | |
HouseActionInfo | |
HouseTeachMastery | |
HousePicType | |
HouseOwnerPic | |
HouseExitMap | |
HouseNPCs[] | If Game.HouseExitMap isn't 0, last slot is occupied by map enter pseudo-NPC. |
HouseItemsCount | Number of interactive items of the dialog. Items count of the dialog object gets changed to this or 0 depending on selected player being concious. |
DialogsArray[] | |
DialogIndexes[] | |
Dialogs[] |
List of currently open dialogs, from lowest to topmost. Doesn't include object-oriented dialogs in MM8. Dialog at index 0 contains adventure interface that you see when no dialog is open. Even when you boot into the main menu, this dialog is present. |
CurrentNPCDialog | |
CurrentHouseDialog | |
InQuestionDialog | [MM8] |
OODialogs | [MM8] |
OODialogPtr | [MM8] Same as Game.OODialogs.CurrentDialogPtr |
GuildJoinCost[] | |
StatsNames[stat] | |
StatsDescriptions[stat] | |
SkillNames[skill] | |
SkillDescriptions[skill] | |
SkillDesNormal[skill] | |
SkillDesExpert[skill] | |
SkillDesMaster[skill] | |
SkillDesGM[skill] | [MM7+] |
ClassNames[class] | |
ClassDescriptions[class] | |
Actions[] | |
ActionsNext[] | [MM7+] |
ExitMapAction | :const.ExitMapAction |
FlashHistoryBook | [MM7] |
FlashAutonotesBook | [MM7] |
FlashQuestBook | [MM7] |
NeedRedraw | |
StatusMessage | |
StatusMessageBytes[] | |
MouseOverStatusMessage | |
MouseOverStatusMessageBytes[] | |
StatusDisappearTime | |
NeedUpdateStatusBar | [MM7+] |
FoodGoldVisible | [MM7] Set it to false to skip 1 draw call of drawing food and gold |
TextInputLimit | |
TextInput | |
TextInputBytes[] | |
TextInputLength | |
TextInputMode | |
TextInputDialog | |
EscMessageLastScreen | |
EscMessageDialog | |
NPCMessage | Current message displayed in a dialog with some NPC |
StreetMessage | Message displayed by Message, Question, evt.SimpleMessage and evt.Question when not talking to NPC. |
DelayedFaceAnimation.Delay | |
DelayedFaceAnimation.Animation | |
DelayedFaceAnimation.PlayerIndex | |
ItemsTxt[] | |
StdItemsTxt[] | |
SpcItemsTxt[] | |
ScrollTxt[] | |
PotionTxt[][] | |
PotNotesTxt[][] | [MM7+] |
MonstersTxt[] | |
PlaceMonTxt[] | [MM7+] |
MapStats[] | |
MapDoorSound[] | |
MapFogChances[] | |
FlyCeiling | [MM6, MM7] 3000 in MM6, 4000 in MM7+, in MM8 it's configured per map (Map.OutdoorExtra.Ceiling) |
MoveToMap | |
ProgressBar | |
DialogLogic | |
Lucida_fnt | |
FontLucida | |
Smallnum_fnt | |
FontSmallnum | |
Arrus_fnt | |
FontArrus | |
Create_fnt | |
FontCreate | |
Comic_fnt | |
FontComic | |
Book_fnt | |
FontBook | |
Book2_fnt | |
FontBook2 | |
Cchar_fnt | [MM6, MM7] |
FontCchar | [MM6, MM7] |
Autonote_fnt | |
FontAutonote | |
Spell_fnt | |
FontSpell | |
TextBuffer | |
TextBufferBytes[] | Lets you get address of TextBuffer or access it byte by byte (note: traversing it like that is very slow) |
TextBuffer2 | |
TextBuffer2Bytes[] | Lets you get address of TextBuffer2 or access it byte by byte (note: traversing it like that is very slow) |
WordWrappedText | |
WordWrappedTextBytes[] | Lets you get address of WordWrappedText or access it byte by byte (note: traversing it like that is very slow) |
KeyCodes[] | |
KeyTypes[] | |
Time | Since 00 AM, January 1st, 1165/1168/1172 |
Year | Actual value, like 1172 |
Month | (0 – 11) |
WeekOfMonth | (0 – 3) |
DayOfMonth | (0 – 27) |
Hour | (0 – 23) |
Minute | (0 – 59) |
Second | (0 – 59) |
BaseYear | |
MaxBirthYear | |
NeedRender | Same as Party.NeedRender |
TurnBased | |
TurnBasedPhase | 1 = monsters move, 2 = combat, 3 = party walking |
TurnBasedDelays[] | |
RedbookHandle | |
MSSHandle | |
BinkVideo | [MM7+] |
DialogTopicsLimit | [MM7+] |
SmackVideo | |
EquipStat2ItemSlot[] | |
MonsterClassSex[] | [MM7] MonClass = (Id + 2):div(3) |
MonsterClassRace[] | [MM7] MonClass = (Id + 2):div(3) |
MonsterSex[] | [MM6] |
MonsterClassInfoY[] | |
Paused | Game logic is paused |
Paused2 | Updates of 3D view are paused |
TimeDelta | Time since last tick |
TimeDelta2 | Time since last tick of updating 3D view |
PauseCount | See Game.Pause and Game.Resume |
PauseCount2 | See Game.Pause2 and Game.Resume2 |
FrameCounter | |
ShopItems[house][slot] | |
ShopSpecialItems[house][slot] | |
GuildItems[house][school][slot] | In MM8 in each guild items for all 12 schools of magic are generated. In MM6 and MM7 school can only be 0. |
GuildAwards[] | |
GuildItemIconPtr[slot] |
Loaded icons for current guild's items. Example: function events.GuildItemsGenerated(t) local a = Game.GuildItems[t.House][0] -- items array for this guild (for MM6 and MM7) a[0]:Randomize(5, const.ItemType.Book) -- put random powerful book into first slot a[0].Identified = true Game.GuildItemIconPtr[0] = Game.IconsLod:LoadBitmapPtr(a[0]:T().Picture) end |
ShopNextRefill[house] | |
GuildNextRefill[house] | |
ShopWeaponKinds[] | |
ShopWeaponKindsSpecial[] | |
ShopArmorKinds[][] | |
ShopArmorKindsSpecial[][] | |
ShopMagicLevels[] | |
ShopMagicLevelsSpecial[] | |
TrainingLevels[] | |
GuildSpellLevels[] | |
ShopAlchemistLevels[] | [MM7+] |
ShopAlchemistLevelsSpecial[] | [MM7+] |
ShopTheftExpireTime[] | [MM7+] |
GeneralStoreItemKinds[] | [MM6] |
GeneralStoreItemKindsSpecial[] | [MM6] Yes, MM6 generates special items in general stores, but doesn't support buying them. |
ShopBackgroundByType[] | |
ScanlineOffset[] | [MM6, MM7] |
ObjectByPixel[y][x] | |
ArmageddonTimeLeft | maximum is 417 |
ArmageddonSkill | damage is 50 + skill |
OutdoorViewMul | Acts as the opposite of FOV |
OutdoorViewDiv | = math.floor(0x10000/Game.OutdoorViewMul) |
GlobalTxt[] | |
Houses[] | 2DEvents.txt |
HouseMovies[] | |
TransTxt[] | |
SpecialEnterX[] | Used for Free Haven Sewer entrances in MM6. Negative Questbit Restrictions field in 2DEvents.txt corresponds to array index |
SpecialEnterY[] | |
SpecialEnterZ[] | |
SpecialEnterDirection[] | |
NPCTopic[] | |
NPCText[] | |
NPCGreet[][] | [MM7+] |
NPCGroup[] | [MM7+] |
NPCNews[] | |
HistoryTxt[] | [MM7+] |
NPCNewsCountByMap[] | [MM6] |
NPCDataTxt[] | |
NPC[] | |
NPCProfNames[] | |
NPCNames[][] | [MM6, MM7] |
NPCProfTxt[] | [MM6, MM7] |
NPCNamesCount[] | [MM6, MM7] |
StreetNPC[] | |
Spells[] | |
SpellsTxt[] | |
SpellSounds[] | |
SpellObjId[] | |
TitleTrack | |
TitleTrackOffset | |
NarratorTrack | [MM6] |
MissileSetup[] | |
SummonElementalA | [MM7+] |
SummonElementalB | [MM7+] |
SummonElementalC | [MM7+] |
QuestsTxt[] | |
AwardsTxt[] | |
AwardsSort[] | |
AutonoteTxt[] | |
AutonoteCategory[] |
[MM7+] 0 = potion 1 = stat 2 = obelisk 3 = seer 4 = misc 5 = teacher |
AutonotesByCategory[][] | [MM6] |
MerchantTxt[][] | |
CtrlPressed | |
RightButtonPressed | |
TownPortalInfo[] | |
TownPortalX[] | (Town portal picture: townport) |
TownPortalY[] | (Town portal icons [MM7+]: tpharmndy, tpelf, tpwarlock, tpisland, tpheaven, tphell) |
TownPortalHeight[] | [MM6, MM7] |
TownPortalWidth[] | [MM6, MM7] |
TransportLocations[] | |
TransportIndex[][] | |
HostileTxt[mon1][mon2] | [MM7+] 0 – 4. Attitude of mon1 towards mon2. mon2 = 0 is party. mon1 and mon2 are monster classes: mon1 = (Id1 + 2):div(3) |
NewGameMap | |
WinMapIndex |
Number represented as a string. [MM6] Index in games.lod [MM7+] Index in mapstats.txt |
GlobalEvtLines[] | |
MapEvtLines[] | |
SFTBin | |
DecListBin[] | |
PFTBin[] | |
IFTBin[] | |
TFTBin[] | |
ChestBin[] | |
OverlayBin[] | |
ObjListBin[] | |
MonListBin[] | |
SoundsBin[] | |
TileBin[] | |
Tile2Bin[] | [MM8] |
Tile3Bin[] | [MM8] |
CurrentTileBin | [MM8] |
ExitLevelCode | 0 = in game, 2 = load other map, 8 = death |
SoundVolume | |
PlayerFaces[] | |
StandardFaceAnimations[] | |
StandardPlayerSoundsCount[][] | |
GamesLod | |
IconsLod | |
BitmapsLod | |
SpritesLod | |
SaveGameLod | |
EnglishTLod | [MM8] |
EnglishDLod | [MM8] |
EventsLod | [MM7] |
dist_mist | |
IsD3D | |
RendererD3D | [MM7+] |
ModelClimbRequirement | [MM7+] Minimum required Z coordinate of the normal to climb a building surface. MM6 default is 1 (any non-vertical surface), MM7+ default is 46378, which corresponds to Lua[[46378/0x10000 = 0.7]]. |
RandSeed | |
IsMovieLooped | |
MovieKind | 0 – No movie, 1 – Smack, 2 – Bink |
MonsterKinds[] | [MM7+] |
Functions: |
|
ExitHouseScreen() | |
OODialogProcessKey() | [MM8] |
GetAdventureInnInfo() |
[MM8] Returns PlayerIndex, InInventory or nothing if Adventure Inn dialog isn't active. PlayerIndex is the index of currently selected player in Party.PlayersArray. Compare it against Party.PlayersIndexes[Party.CurrentPlayer] to see if selected player is part of the party. InInventory is true if player inventory (or Stats, Skills, Awards) screen is open. |
ProcessActions() | |
Rand() | |
DoPause() | Pauses game logic |
DoResume() | Resumes game logic |
Pause() | Pauses game logic, increments Game.PauseCount by 1 and updates Game.BackupPaused |
Resume() | Subtracts 1 from Game.PauseCount and resumes game logic upon reaching 0 if the the game wasn't paused before Game.Pause was called |
DoPause2() | Pauses updating 3D view (honored by the game only if logic is also paused) |
DoResume2() | Resumes updating 3D view |
Pause2() | Pauses updating 3D view, increments Game.PauseCount2 by 1 and updates Game.BackupPaused2 (honored by the game only if logic is also paused) |
Resume2() | Subtracts 1 from Game.PauseCount2 and resumes updating 3D view upon reaching 0 if the the game wasn't paused before Game.Pause2 was called |
SetInterfaceColor(Color, Unk = 1) | [MM7] 0 = good, 1 = neutral, 2 = evil |
DoShowMovie(Name, Y, DoubleSize, ExitCurrentScreen) | Only call from events.ShowMovie, use evt.ShowMovie otherwise. |
IsMoviePlaying() | [MM7+] |
LoadHouseMovie(Name, Loop = true) | |
EndMovie() | |
RestartHouseMovie() | |
PlayShopSound(House, SoundIndex) | |
GetNPCPtrFromIndex(Index) | [MM7+] |
GetCurrentNPCPtr() | |
CalcSpellDamage(Spell, Skill, Mastery, MonsterHP) | |
GetSpellDamageType(Spell) | |
GetStatisticEffect(Stat) | |
SummonMonster(Id, X, Y, Z) | |
SummonObjects(Type, X, Y, Z, Speed, Count = 1, RandomAngle = false, Bits = 0, pItem [MM7+]) | This function is called by evt.SummonObject internally. If you specify the pItem parameter, it will only work properly in MM8. |
GenerateChests() | You can add random items (Number = -1 to -6 for different power or -7 for artifact) to some chests and then call this function to generate them |
IsMonsterOfKind(Id, Kind) | [MM7+] |
FileRead(pTarget, Size, Count, FileStream) | Reads Size*Count bytes from FileStream into pTarget buffer. The FileStream can be obtained by calling FindFile method of a Lod archive. |
FileSeek(FileStream, Offset, Origin = 0) |
Sets current position of FileStream. Origin = 0 sets absolute position to Offset. Origin = 1 adds Offset to current position. Origin = 2 sets position to end of file plus Offset. |
FileTell(FileStream) | Returns current position of FileStream. |
Uncompress(pTarget, pTargetSize, pSrc, SrcSize) | pTargetSize must point to a 4-byte buffer specifying unpacked size. |
Compress(pTarget, pTargetSize, pSrc, SrcSize, Compression[MM7+] = -1) | pTargetSize must point to a 4-byte buffer specifying max size. The function sets it to actual size it has used up. If successful, returns 0. |
PlayMapTrack() | |
PlayTrack(Index) | |
LoadSound(SoundId, Unk = 0, Unk2 = 0) | Unk2 is present only in MM8 |
PlaySound(SoundId, Object = 0, Loops = 0, X = -1, Y = 0, Unk = 0, Volume = 0, PlaybackRate = 0) | Each object kind has a number of sound channels to use. Special Object values of '-1' and '-2' also have 1 channel reserved for each (lower values have the same effect as '-2'). So, when many sounds are played at once you could use one of these negative values instead of the default Object = 0. |
StopAllSounds(KeepMin = -1, KeepMax = -1) | |
LoadDecSprite(Name) | Loads a sprite and returns its ID. |
LoadBitmap(Name) | Loads a texture and returns its ID. |
UpdateDialogTopics() | |
NewDialog(Left, Top, Width, Height, DlgID, Param, StrParam, InitProc, InitParam) | If specified, InitProc(dlg, InitParam) is called before any calls to the NewDialog event. |
GetDialogFromPoint(Returns the dialog that the mouse is over.) | |
GetButtonFromPoint(Returns item, dialog that the mouse is over. Uses the logic of hint updates rather than clicks. That is, stops iteration upon finding an item in specified position or encountering a dialog with Height equal to 480, beyond which it wouldn't go. Note that the logic of clicks is generally less permissive: it's GetDialogFromPoint followed by GetFromPoint of said dialog.) | |
GetTopDialog(Returns the dialog that the mouse is over.) | |
ShowStatusText(Text, Seconds = 2, NoRedraw) | |
ShowStatusHint(Text, AcceptEmpty) | Unless AcceptEmpty is true, passing an empty string won't do anything. |
EscMessage(Text, ActionOnClose = 0) | |
StartTextInput(MaxLength = 50, Numerical = false, Dialog = nil) | |
EndTextInput(State) | |
LoadPalette(PalNum) | |
LoadDataFileFromLod(Name, UseMalloc) | |
LoadTextFileFromLod(Name) | |
CanLoadFileFromLod(Name) |
Returns true if specified file exists in LOD archives that LoadTextFileFromLod and LoadTextFileFromLod functions use. The archives are: icons.lod in MM6, events.lod in MM7, EnglishD.lod and EnglishT.lod in MM8. |
LoadPcx(Name, PcxBuffer, FromEnglishD = false, LoadKind = 0) | Returns loaded pcx. PcxBuffer can be nil, a pcx strucutre or a pointer to the buffer. If it's nil, the buffer gets allocated and must freed via a call to Destroy function. |
LoadFont() | |
GetCurrentHouse() | |
GetCurrentNoHouseNPC() | |
GetNPCFromPtr() | |
GetNPCFromIndex() |
PartyFields: |
|
Pos[] | |
X | |
Y | |
Z | |
Direction | 0 – 2047. 0 is East, 512 is North and so on. |
LookAngle | -512 – 512. Values allowed with mouse look: -240 – 300 (prior to patch 2.5: -200 – 200). Without mouse look: -128 – 128 |
LastX | |
LastY | |
LastZ | |
LastDirection | |
LastLookAngle | |
LastEyeLevel | |
SpeedX | |
SpeedY | |
SpeedZ | |
StableZ | Z changes up and down while flying, StableZ stays the same |
LastStandFacet | |
FallStartZ | |
Flying | |
PlayersArray[] | Array of all players |
PlayersIndexes[] | [MM8] Array of players indexes in PlayersArray corresponding to each player slot |
Players[] | (Default) Array of players corresponding to each player slot |
HiredNPC[] | [MM6, MM7] |
HiredNPCName[] | [MM6, MM7] |
LastRegenerationTime | |
SpellBuffs[buff] | |
Gold | |
BankGold | |
Food | |
Deaths | |
PrisonTerms | |
BountiesCollected | |
Fine | [MM7+] |
BountyHuntTarget[] | Only index 0 is normally used in MM8 |
MonsHuntTarget[] | (deprecated old name) |
BountyHuntKilled[] | Only index 0 is normally used in MM8 |
MonsHuntKilled[] | (deprecated old name, integer in MM7+ instead of boolean) |
NextBountyHunt[] | |
MonsHuntReset[] | (deprecated old name) |
QBits[] | |
AutonotesBits[] | |
InArenaQuest | |
ArenaWinsPage | |
ArenaWinsSquire | |
ArenaWinsKnight | |
ArenaWinsLord | |
ArtifactsFound[] | |
Alignment | [MM7] 0 = good, 1 = neutral, 2 = evil |
Reputation | |
History[] | [MM7+] |
SpecialDates[] |
[MM7+] E.g. set date 1: evt.Add("SpecialDate1", 0) Use date 1: "%51" in any NPC message |
ArcomageWins[] | [MM7+] |
CurrentPlayer | |
StateBits | |
NeedRender | |
Drowning | |
InAir | |
EnemyDetectorRed | |
EnemyDetectorYellow | |
FlyingBit | |
WaterWalkingBit | |
InJumpSpell | |
InLava | |
Functions: |
|
RestAndHeal() | |
Wait(Minutes) | |
FindActivePlayer() | |
GetFame() | |
GetReputation() | |
AddGold(Gold, Kind = 0) |
Kind values: 0 = increase by Banker, give some part to followers 1 = take exect amount, ignore followers 2 = [MM7+] take all and don't show message, just clear status message 3 = [MM7+] take all and don't change status message |
AddKillExp(Experience) | Experience is shared among conscious players and effected by Learning skill |
HasNPCProfession(arg1) | [MM6, MM7] |
CountItems({item1, item2, ...}) | |
GetCurrentPlayer() | |
Methods: |
|
ResetStartingPlayer(arg1 = false, arg2 = false) | [MM8] |
MapFields: |
|
Refilled | If the map has been refilled this visit, contains the last mapvars table. |
Name | |
IndoorOrOutdoor | |
Monsters[] | |
Vars[] | Variables for barrels/contests/etc events start at 75 |
Objects[] | Items, spells effects |
Sprites[] | |
SoundSprites[] | |
Chests[] | |
MapStatsIndex | |
NoNPC | [MM7] |
OutdoorHeader | |
Tilesets[] | |
HeightMap[y][x] | [(64 - Party.Y / 0x200):round()] [(64 + Party.X / 0x200):round()] = (Party.Z / 32):floor() |
TileMap[y][x] | [(64 - Party.Y / 0x200):floor()] [(64 + Party.X / 0x200):floor()] |
UnknownMap[][] | |
Models[] | |
IDList[] | IDs of sprites on map (in ObjectRef form) |
IDOffsets[y][x] | OMAP – offsets in IDList |
LoadedSkyBitmap | |
OutdoorSpawns[] | |
OutdoorRefillCount | |
OutdoorLastRefillDay | The day of refill plus 1 |
OutdoorReputation | [MM7+] |
OutdoorAlertState | [MM7+] |
OutdoorSanityFacetsCount | [MM7+] |
OutdoorSanitySpritesCount | [MM7+] |
SanityModelsCount | [MM7+] |
OutdoorExtra | |
OutdoorLastVisitTime | |
VisibleMap1[][] | |
VisibleMap2[][] | |
TilesetsFile | [MM8] 0 = dtile.bin, 1 = dtile2.bin, 2 = dtile3.bin |
UnknownMap2[][] | [MM8] |
Notes[] | [MM8] |
TerNormDist[][][] | [MM7+] |
TerNormId[][][] | [MM7+] |
TerNorm[] | [MM7+] |
IndoorHeader | |
Vertexes[] | |
Facets[] | |
FacetData[] | |
Rooms[] | |
Lights[] | |
Doors[] | |
BSPNodes[] | |
Outlines | |
IndoorSpawns[] | |
IndoorRefillCount | |
IndoorLastRefillDay | The day of refill plus 1 |
IndoorExtra | |
IndoorLastVisitTime | |
VisibileOutlines[] | |
IndoorReputation | [MM7+] |
IndoorAlertState | [MM7+] |
IndoorSanityFacetsCount | [MM7+] |
IndoorSanitySpritesCount | [MM7+] |
SanityDoorDataSize | [MM7+] Added in MMExtension. Instead of being checked against, it actually replaces Map.IndoorHeader.DoorDataSize when loading the .dlv, if it's non-zero. |
Spawns | |
RefillCount | |
LastRefillDay | The day of refill plus 1 |
Extra | |
LastVisitTime | |
Reputation | [MM7+] |
AlertState | [MM7+] |
SanityFacetsCount | [MM7+] |
SanitySpritesCount | [MM7+] |
SpriteLights[] | [MM7+] |
Functions: |
|
RemoveObject(Index) | |
Render() | |
IsIndoor() | |
IsOutdoor() | |
LoadTileset(Id) | |
RoomFromPoint(x, y, z) | |
GetFloorLevel(x, y, z, room) | Returns FloorZ, FacetId. |
GetGroundLevel(x, y) | |
GetFacet(Id) |
MouseFields: |
|
Target | Use Mouse.GetTarget instead. |
X | |
Y | |
Item | |
Functions: |
|
GetTarget() | Returns ObjectRef of current mouse target |
GetPos() |
Returns floating-point mouse position that includes sub-pixel difference caused by upscaling. In UILayout mode this is the only way to find mouse coordinates when it's over 3D view. Mouse.X and Mouse.Y would just return the middle of the view. When mouse is over interface items, both this function and Mouse.X, Mouse.Y get coordinates within traditional interface to which UI layout is mapped. |
Methods: |
|
SetIcon(Icon = "MICON1") |
There are 3 special values: "MICON1" = arrow cursor "MICON2" = crosshair cursor for spells "MICON3" = this cursor doesn't exist, don't use it Other values change the picture of item carried by mouse. |
RemoveItem() | Deletes item carried by the mouse and restores arrow cursor. |
AddItem(Item) | If there already was an item carried by the mouse, it will be taken into inventory or dropped. |
ReleaseItem() | If there is an item carried by the mouse, it will be taken into inventory or dropped. |
ScreenFields: |
|
IsD3D | [MM7+] |
Width | |
Height | |
Pitch | |
cx1 | |
cy1 | |
cx2 | |
cy2 | |
Buffer | |
ObjectByPixel | |
RedBits | |
BlueBits | |
GreenBits | |
RedMask | |
GreenMask | |
BlueMask | |
Pitch2 | [MM7+] |
ClipTop | [MM7+] |
ClipLeft | [MM7+] |
ClipBottom | [MM7+] |
ClipRight | [MM7+] |
Methods: |
|
SaveToPcx(name, x = 0, y = 0, width = 640, height = 480) | x, y, width, height can only be specified in MM6. MM7 and MM8 save a shot of whole screen area. |
SaveBufferToPcx(name, buf, width = 640, height = 480) | [MM7+] |
SetClipRect(left = 0, top = 0, right = 640, bottom = 480) | [MM7+] |
Draw(x, y, pic, style, rotate, EnglishD) |
style: "transparent", false – draw treating color index 0 as transparent (default) "opaque", true – draw without transparency "red" – draw broken item "green" – draw unidentified item Adding "p" in the beginning of style string signals that pic is a pointer to an image rather than its index. Note that "pred" and "pgreen" aren't supported in MM6. |
DrawItemEffect(x, y, shapePic, effectPic, palShift, palAnimateFrom, palAnimateTo, rotate, EnglishD, effectEnglishD) | |
DrawToObjectByPixel(x, y, pic, index, rotate, EnglishD) | |
DrawToObjectByPixelOpaque(x, y, pic, index, EnglishD) | [MM7+] |
DrawPcx(x, y, LoadedPcx) | |
DrawMessageBox(Dialog, Text = nil, SnapToViewBox = false) |
Draws a message box border with optional text inside. In place of Dialog you can pass a table in form of {Left, Top, Width, Height}. The function modifies the dialog position to make it fit on the screen. If you pass a table with box position and size, it's modified accordingly. If Text is specified or Dialog.StrParamPtr is not 0, the text is drawn in the middle of the dialog and dialog box is drawn with appropriate height, but dialog height is not modified to reflect that. |
DrawMessageBoxBorder(Left, Top, Width, Height) |
Draws a message box border. Minimal dimensions for proper display are 64. |
structs.ActionItemFields: |
|
Action | |
Param | |
Param2 |
structs.Arcomage[MM7+]Fields: |
|
StartingTower | |
StartingWall | |
StartingIncome[3] | |
StartingIncomeBricks | |
StartingIncomeGems | |
StartingIncomeBeasts | |
CardsCount | Internally up to 10 cards are supported. |
MinIncome[3] | If you change these values, StartingIncome and player income would also change. |
TowerToWin | |
ResToWin | |
StartingRes[3] | |
StartingBricks | |
StartingGems | |
StartingBeasts | |
AI | 0 – 2 |
Players[2] | (Default) Player 0 is the human, player 1 is AI |
CardKinds[] | |
Deck[] |
structs.ArcomageAction[MM7+]Fields: |
|
Income[] | |
Res[] | |
Damage | |
Wall | |
Tower |
structs.ArcomageActions[MM7+]Fields: |
|
PlayAgain | |
DiscardCards | |
Me | |
Enemy | |
All |
structs.ArcomageCard[MM7+]Fields: |
|
Name | |
Sprite | |
CostKind | |
CostIncome[] | |
CostRes[] | |
Discardable | |
If | :const.ArcomageIf |
Then | |
Else |
structs.ArcomagePlayer[MM7+]Fields: |
|
Name | |
Human | |
Tower | |
Wall | |
Income[3] | |
Res[3] | |
Cards[10] | |
unk[10][2] |
structs.BSPNodeFields: |
|
FrontNode | |
BackNode | |
CoplanarOffset | |
CoplanarSize |
structs.BaseBonusFields: |
|
Base | |
Bonus |
structs.BaseLight[MM7+]Fields: |
|
Pos[] | |
X | |
Y | |
Z | |
Radius | |
R | |
G | |
B | |
Type |
structs.BitmapsLodFields: |
|
File | |
FileName | |
Loaded | |
IOBuffer | |
IOBufferSize | |
LodHeaderSignature | |
Description | |
ArchivesCount | |
ArchivesCArray | |
Type | |
ChapterHandle | |
ChapterSize | |
Files[] | |
FilesOffset | |
Bitmaps[] | |
BitmapsCount | |
RedBits | |
GreenBits | |
BlueBits | |
NonTmpCount | |
TmpIndex | |
KeepCompressed | |
IsHardware | [MM7+] |
D3D_Surfaces[] | [MM7+] |
D3D_Textures[] | [MM7+] |
Methods: |
|
HasFile(name) | Does a slow search for a file. For sorted LOD archives FindFile works faster. |
FindFile(name, unsorted = false) |
Finds a file and returns file stream address or 0 if file isn't found. By default performs fast binary search. Pass unsorted = true when searching in Game.GamesLod and Game.SaveGameLod, because these archives aren't sorted lexicographically and thus binary search can't be used for them. Returned file stream can be used in Game.FileRead, Game.FileSeek and Game.FileTell functions. Its position is set to the beginning of specified file. |
LoadBitmap() | |
LoadBitmapPtr() | |
LoadTFTBitmap() | |
LoadBitmapInPlace(bmp, name, unused_must_be_2 = 2) | Used for loading of progress bar bitmaps |
ReplaceBitmap(bmp, name, unused_must_be_2 = 2) | [MM7+] Used for changing party faces and in MM7 for recoloring of interface. Assumes the new bitmap has the same dimensions. |
BeginTmp() | |
EndTmp() | |
Cleanup() |
structs.BlvHeaderFields: |
|
Name | |
FacetDataSize | |
RoomDataSize | |
RoomLightDataSize | |
DoorDataSize |
structs.ButtonFields: |
|
Left | |
Top | |
Width | |
Height | |
RightPixel | = Left + Width - 1 (it was called Right before MMExtension v2.3, old name is supported for backward compatibility) |
BottomPixel | = Top + Height - 1 (it was called Bottom before MMExtension v2.3, old name is supported for backward compatibility) |
Shape |
1 – Rectangle. 2 – Ellipse. Left and Top are center coordinates, Width and Height are radii. In MM6 it's always a circle and Height is 0. 3 – Skill rectangle. |
HintAction | |
Action | Was called ActionType before MMExtension v2.3, old name is supported for backward compatibility |
ActionParam | |
ActionParam2 | [MM7+] |
Pressed | |
PrevItemPtr | |
NextItemPtr | |
Parent | |
ParentPtr | |
Sprites[] | A list of pointers to associated icons |
Key | Was called ShortCut before MMExtension v2.3, old name is supported for backward compatibility |
Hint | |
Methods: |
|
Destroy() | Make sure to update Parent.KeyboardItemsCount on your own if you delete one of them |
MoveBefore(Target) |
If Target is 0 or nil, moves the item to the end of the items list Note that first item in the list is topmost and the last one is at the bottom. |
MoveAfter(Target) | If Target is 0 or nil, moves the item to the beginning of the items list |
structs.CurrentTileBin[MM8]Fields: |
|
Items[] | (Default) |
structs.CustomLodsFields: |
|
RecordIndex | |
Records[] | |
Functions: |
|
Load(StdLod, Name) | |
Free(Ptr) |
structs.DChestItemFields: |
|
Name | |
Width | |
Height | |
ImageIndex |
structs.DialogLogicFields: |
|
List[] | List of indexes to be displayed |
ScrollPage | |
CountOnScreen | |
ListCount | |
ScrollPos | |
AutonoteTab6Clicked | [MM7+] |
AutonoteTab5Clicked | |
MapMoveRightClicked | |
AutonoteTab4Clicked | |
MapMoveLeftClicked | |
AutonoteTab3Clicked | |
MapMoveDownClicked | |
AutonoteTab2Clicked | |
MapMoveUpClicked | |
AutonoteTab1Clicked | |
ScrollDownClicked | |
ScrollUpClicked | |
SpellBookSelection | Selected spell index within current page (1..11) |
SpellBookSelectedNewSpell | [MM7+] |
AutonotesCategory | |
ArmorSkills[] | [MM7+] |
WeaponSkills[] | [MM7+] |
MiscSkills[] | [MM7+] |
MagicSkills[] | [MM7+] |
MonsterInfoMonster | |
PlayerRingsOpen | |
PaperDollPositionX | |
PaperDollPositionY |
structs.DlgFields: |
|
Left | |
Top | |
Width | |
Height | |
RightPixel | = Left + Width - 1 (it was called Right_ before MMExtension v2.3, old name is supported for backward compatibility) |
BottomPixel | = Top + Height - 1 (it was called Bottom_ before MMExtension v2.3, old name is supported for backward compatibility) |
DlgID | |
Param | 2D Events Id / Chest Id / ... |
ItemsCount | |
KeyboardItemsCount | |
KeyboardItem | |
KeyboardNavigationTrackMouse | |
KeyboardLeftRightStep | |
KeyboardItemsStart | |
Index | Current index in Game.Dialogs array. |
TextInputState | |
UseKeyboadNavigation | |
StrParam | |
StrParamPtr | |
FirstItemPtr | |
LastItemPtr | |
Methods: |
|
SetKeyboardNavigation(KeyboardItemsCount, KeyboardNavigationTrackMouse, KeyboardLeftRightStep, KeyboardItemsStart) | |
GetItemPtrByIndex(Index) | |
AddButton(Left, Top, Width, Height, Shape = 1, HintAction, ActionType, ActionParam, Key, Hint = "", [EnglishD] = true, Sprites...) |
Can also accept a table where some parameters are supplied by their name. In this case EnglishD and Sprites must be supplied by name. Returns button address and the wrap function. Pass the returned address to the wrap function to create a dialog item structure to access it. Example: -- make area to the left of first player portrait bring up the Maps dialog (note: this would interfere with object-oriented dialogs in MM8 and cause bugs when clicked inside them) local p, wrap = Game.Dialogs[0]:AddButton{0, 384, 31, 80, ActionType = 202, Hint = "Another Maps Button", Sprites = {"ib-td3-A"}} local btn = wrap(p) btn:MoveAfter(0) -- make it take priority over other buttons |
Destroy(KeepMonsterPicture [MM8]) | |
Enum(Backwards) |
To be used in a for statement. Enumerates dialog items returning item index, dialog item and the wrap function each time. If Backwards is true, enumeration goes in reverse order. Note that the same Lua table gets reused for the item structure during enumeration. If you want to create separate Lua tables for each item, use the Wrap function. Here's an example: local t = {} -- this table will be populated with all dialog items from adventure screen for i, it, wrap in Game.Dialogs[0]:Enum() do t[i] = wrap(it) end If you're wandering, the wrap function itself simply does the following: structs.Button:new(it['?ptr'] or tonumber(it)) |
GetByIndex(Index) | Returns dialog item at specified index in the items list. |
GetRelativePoint(X, Y) | Returns relative coordinates or nothing if the coordinates don't fall within the dialog. |
GetFromPoint(X, Y, Absolute) | Returns dialog item at specified relative or absolute coordinates. |
structs.FaceAnimationInfoFields: |
|
Sounds[] | |
Sound1 | |
Sound2 | |
Expressions[] | |
Expression1 | |
Expression2 | |
Expression3 | |
Expression4 | |
Expression5 |
structs.FacetDataFields: |
|
FacetIndex | |
BitmapIndex | |
TFTIndex | |
BitmapU | Bitmap U Offset |
BitmapV | Bitmap V Offset |
Id | |
Event |
structs.FloatVector[MM7+]Fields: |
|
1 | same as X |
X | |
2 | same as Y |
Y | |
3 | same as Z |
Z |
structs.FntFields: |
|
MinChar | |
MaxChar | |
Height | |
PalettesCount | |
Palettes[] | |
ABC[][] | |
GlyphOffsets[] | |
GlyphData[] | |
Methods: |
|
GetLineWidth(Text) | |
GetTextHeight(Text, Dialog = nil, X = 0, IgnoreStrRightSymbol = false [MM7+]) |
In place of Dialog you can pass a table in form of {Left, Top, Width, Height}. Calls WordWrap internally and stores the result in Game.WordWrappedText. |
WordWrap(Text, Dialog = nil, X = 0, IgnoreStrRightSymbol = false [MM7+], ReturnPointer = false) |
Returns text with extra line breaks inserted. In place of Dialog you can pass a table in form of {Left, Top, Width, Height}. If ReturnPointer = true, returns Game.WordWrappedTextBytes table instead of Lua string. You can then pass this table to any of the font methods. |
Draw(Text, Dialog = nil, X = 0, Y = 0, Color = 0, ShadowColor [MM7+], Bottom [MM7+], Opaque [MM7+]) |
In place of Dialog you can pass a table in form of {Left, Top, Width, Height}. If X is 0, in MM6 and MM7 it gets set to 12 by the function. Passing 0 as Color would draw text using default color. Unless Bottom is specified, it calls WordWrap internally and stores the result in Game.WordWrappedText. Bottom is specified in absolute coordinates, not relative to dialog. When specified, the text doesn't get word-wrapped, but doesn't get drawn below its value. If not specified, text gets ward-wrapped when it exceeds dialog width horizontally, but is not limited vertically. |
DrawLimited(Text, Dialog = nil, Width, X = 0, Y = 0, Color = 0, TruncateStart = false) |
In place of Dialog you can pass a table in form of {Left, Top, Width, Height}. X should not be 0 in MM6 and MM7, because it then gets set to 12 by the function if text fits and is kept at '0 if it doesn't. Passing 0 as Color would draw text using default color. If TruncateStart is true, end of the string is kept and beginning is truncated to fit the required size. Calls WordWrap internally and stores the result in Game.WordWrappedText. |
DrawCentered(Text, Dialog = nil, X = 0, Y = 0, Color = 0, ReduceLineHeight = 3) |
In place of Dialog you can pass a table in form of {Left, Top, Width, Height}. Passing 0 as Color would draw text using default color. Calls WordWrap internally and stores the result in Game.WordWrappedText. |
structs.FogChancesFields: |
|
Thick | |
Medium | |
Light |
structs.GameClassKindsFields: |
|
HPBase[] | [MM6, MM7] |
SPBase[] | [MM6, MM7] |
StartingStats[][stat] | [MM6] |
StartingSkills[][skill] |
[MM6] 0 = not available, 1 = given on start, 2 = can choose on start, 3 = can learn [MM7+] 0 = can't choose, 1 = can choose on start, 2 = given on start |
structs.GameClassesFields: |
|
HPFactor[class] | |
SPFactor[class] | |
HPBase[class] | [MM8] |
SPBase[class] | [MM8] |
Skills[class][skill] | [MM7+] 0 = not available, 1 = Basic, 2 = Expert, 3 = Master, 4 = GM |
StartingStats[class] | [MM8] |
SPStats[class] | 0 = no SP, 1 = Intellect, 2 = Personality, 3 = both |
structs.GameRaces[MM7]Fields: |
|
StartingStats[race][stat] |
structs.GeneralStoreItemKind[MM6]Fields: |
|
Level | |
Items[1..6] | (Default) If it's zero, random Boots or Gauntlets are generated. |
structs.HistoryTxtItem[MM7+]Fields: |
|
Text | |
Title | |
Time |
structs.HouseMovieFields: |
|
FileName | |
Background | EVTPAR* index, used only in MM6 |
NPCPic | |
HouseType | |
Sounds | 30000 + Sounds*100 is the Id in Sounds.txt |
structs.ItemFields: |
|
Number | |
Bonus | From STDITEMS.TXT. You can use const.Stats, just add 1 to a supported value from it. |
BonusStrength | |
Bonus2 | From SPCITEMS.TXT. Value in case of gold. |
Charges | |
Identified | |
Broken | |
TemporaryBonus | |
Stolen | |
Hardened | |
Refundable | Added in patch v2.5.4. Used internally to remove artifacts generated in unopened chests from ArtifactsFound upon map refill. |
Condition | |
BodyLocation | |
MaxCharges | |
Owner | |
BonusExpireTime | [MM7+] |
Methods: |
|
GetValue() | |
GetName() | |
GetIdentifiedName() | |
GenerateArtifact() | |
Clear() | |
Randomize(Strength, Type, AlwaysEnchant [MM8]) | Generates a random item. The chance that it would be enchanted depends on Strength. In MM8 you can guarantee its enchantment by passing true as AlwaysEnchant parameter. |
T() | Returns ItemsTxt entry. |
InitSpecial() | [MM7+] Sets up enchantments if the item is "Special" as marked by "material" column of items.txt |
structs.ItemsTxtItemFields: |
|
Picture | |
Name | |
NotIdentifiedName | |
Notes | |
Value | |
EquipStat | Subtract 1 from const.ItemType value |
Skill | |
Mod1DiceCount | |
Mod1DiceSides | |
Mod2 | |
Material | 0 = normal, 1 = artifact, 2 = relic, 3 = special |
ChanceByLevel[] | |
IdRepSt | |
SpriteIndex | |
EquipX | |
EquipY | |
Bonus2 | [MM7+] VarA |
Bonus | [MM7+] VarA |
BonusStrength | [MM7+] VarB |
structs.LanguageLod[MM8]Fields: |
|
File | |
FileName | |
Loaded | |
IOBuffer | |
IOBufferSize | |
LodHeaderSignature | |
Description | |
ArchivesCount | |
ArchivesCArray | |
Type | |
ChapterHandle | |
ChapterSize | |
Files[] | |
FilesOffset | |
Methods: |
|
FindFile(name, unsorted = false) |
Finds a file and returns file stream address or 0 if file isn't found. Performs fast binary search (unless unsorted is set to true, which you shouldn't do for language LODs). Returned file stream can be used in Game.FileRead, Game.FileSeek and Game.FileTell functions. Its position is set to the beginning of specified file. |
structs.LanguageLodFile[MM8]Fields: |
|
Name | |
Offset | |
Size |
structs.LloydBeaconSlotFields: |
|
ExpireTime | |
Pos[] | |
X | |
Y | |
Z | |
Direction | |
LookAngle | |
Active | |
MapIndex | |
Map |
structs.LodFields: |
|
File | |
FileName | |
Loaded | |
IOBuffer | |
IOBufferSize | |
LodHeaderSignature | |
Description | |
ArchivesCount | |
ArchivesCArray | |
Type | |
ChapterHandle | |
ChapterSize | |
Files[] | |
FilesOffset | |
Methods: |
|
HasFile(name) | Does a slow search for a file. For sorted LOD archives FindFile works faster. |
FindFile(name, unsorted = false) |
Finds a file and returns file stream address or 0 if file isn't found. By default performs fast binary search. Pass unsorted = true when searching in Game.GamesLod and Game.SaveGameLod, because these archives aren't sorted lexicographically and thus binary search can't be used for them. Returned file stream can be used in Game.FileRead, Game.FileSeek and Game.FileTell functions. Its position is set to the beginning of specified file. |
structs.LodFileFields: |
|
Name | |
Offset | |
Size |
structs.LodPcxFields: |
|
BufSize | |
Width | |
Height | |
WidthLn2 | |
HeightLn2 | |
WidthMinus1 | |
HeightMinus1 | |
Bits | |
Image | |
Methods: |
|
Destroy(FreeBuffer = true) |
structs.LodRecordFields: |
|
LodPtr | |
NamePtr | Pointer passed to Load* function |
Name |
structs.LodSpriteFields: |
|
Name | |
DataSize | |
Width | |
Height | |
Palette | |
YSkip | |
UnpackedSize | |
Lines[] | |
Buffer |
structs.LodSpriteD3DFields: |
|
Name | |
Palette | |
Surface | |
Texture | |
AreaX | |
AreaY | |
BufferWidth | |
BufferHeight | |
AreaWidth | |
AreaHeight |
structs.LodSpriteLineFields: |
|
L | |
R | |
Pos[] |
structs.MapChestFields: |
|
ChestPicture | 0..7 chest id |
Trapped | |
ItemsPlaced | |
Identified | |
Bits | |
Items[] | |
Inventory[] | (Items index) for main item cell, -(1 + main Inventory cell index) for other cells |
Methods: |
|
RefundArtifacts() |
structs.MapFacetFields: |
|
NormalF[] | [MM7+] normal float |
NormalFX | [MM7+] normal X float |
NormalFY | [MM7+] normal Y float |
NormalFZ | [MM7+] normal Z float |
NormalFDistance | [MM7+] normal distance float |
Normal[] | normal fixed pt (0..0x10000) |
NormalX | normal X fixed pt (0..0x10000) |
NormalY | normal Y fixed pt (0..0x10000) |
NormalZ | normal Z fixed pt (0..0x10000) |
NormalDistance | normal distance fixed pt (0..0x10000) |
ZCalc1 | = -(NormalX * 2^16) / NormalZ (or 0 if NormalZ is 0) |
ZCalc2 | = -(NormalY * 2^16) / NormalZ (or 0 if NormalZ is 0) |
ZCalc3 | = -(NormalDistance * 2^16) / NormalZ (or 0 if NormalZ is 0) |
IsPortal | |
IsSecret | [MM7+] show in red with Perception |
ScrollDown | [MM7+] moving texture |
AlignTop | [MM7+] align door texture in D3D |
IsWater | |
ScrollUp | [MM7+] moving texture |
ScrollLeft | [MM7+] moving texture |
ProjectToXY | |
ProjectToXZ | |
ProjectToYZ | |
ScrollRight | [MM7+] moving texture |
AlignLeft | [MM7+] align door texture in D3D |
Invisible | |
AnimatedTFT | |
AlignRight | [MM7+] align door texture in D3D |
AlignBottom | [MM7+] align door texture in D3D |
MoveByDoor | |
IsEventJustHint | [MM7+] |
AlternativeSound | |
IsSky | outdoor in software mode: horizontal flow |
FlipU | |
FlipV | |
TriggerByClick | |
TriggerByStep | |
DisableEventByCtrlClick | [MM8] indoor only: click event gets disabled by Ctrl+Click |
EventDisabledByCtrlClick | [MM8] |
TriggerByMonster | [MM6, MM7] happens even if there's no event assigned |
TriggerByObject | [MM6, MM7] happens even if there's no event assigned |
Untouchable | great for vertical facets of stairs. [MM7+] Shouldn't be used for sloped floor, like it's used in MM6. |
IsLava | |
HasData | |
Bits | |
VertexIds[] | |
XInterceptDisplacement[] | |
YInterceptDisplacement[] | |
ZInterceptDisplacement[] | |
UList[] | |
VList[] | |
DataIndex | |
BitmapId | Bitmap Index |
Room | Room # |
RoomBehind | Room # Behind facet |
MinX | Bounding Box Min X |
MaxX | Bounding Box Max X |
MinY | Bounding Box Min Y |
MaxY | Bounding Box Max Y |
MinZ | Bounding Box Min Z |
MaxZ | Bounding Box Max Z |
PolygonType |
Polygon type: 0 = empty 1 = wall 2 = unused 3 = horizontal floor 4 = irregular floor (non-horizontal) 5 = horizontal ceiling 6 = irregular ceiling (non-horizontal) |
VertexesCount | |
Methods: |
|
GetData(Create) |
Returns Map.FacetData structure associated with the facet. If Create is true, the data gets automatically allocated if it doesn't exist. On outdoor maps the method returns the facet itself, because all data is inside its own structure. |
structs.MapLightFields: |
|
Pos[] | |
X | |
Y | |
Z | |
Off | |
Bits | |
Brightness | |
Radius | |
R | [MM7+] |
G | [MM7+] |
B | [MM7+] |
Type | [MM7+] |
Id | [MM8] |
structs.MapModelFields: |
|
Name | |
Name2 | |
ShowOnMap | |
Bits | |
Vertexes[] | |
ConvexFacetsCount | |
Facets[] | |
Ordering[] | |
BSPNodes[] | |
GridX | center X |
GridY | center Y |
Pos[] | |
X | |
Y | |
Z | |
MinX | Bounding MIN X |
MinY | Bounding MIN Y |
MinZ | Bounding MIN Z |
MaxX | Bounding MAX X |
MaxY | Bounding MAX Y |
MaxZ | Bounding MAX Z |
BFMinX | |
BFMinY | |
BFMinZ | |
BFMaxX | |
BFMaxY | |
BFMaxZ | |
BoxCenterX | Bounding center X |
BoxCenterY | Bounding center Y |
BoxCenterZ | Bounding center Z |
BoundingRadius |
structs.MapMonsterFields: |
|
Name | [MM6] |
NPC_ID |
[MM6] Index in Game.StreetNPC + 1 [MM7+] Index in Game.NPC or index in Game.StreetNPC + 5000 |
Active | inside the active radius |
ShowOnMap | monster was once seen by party |
Invisible | |
NoFlee | |
Hostile | |
OnAlertMap | |
TreasureGenerated | treasure is in Items[0] and Items[1], gold is in Items[3] |
ShowAsHostile | show as hostile on map |
IsObeliskChest | [MM8] |
Bits | |
HP | |
HitPoints | |
Id | Changing may cause random crashes after loading the game! Be careful. |
Level | |
TreasureItemPercent | |
TreasureDiceCount | |
TreasureDiceSides | |
TreasureItemLevel | |
TreasureItemType | |
Fly | |
MoveType | |
AIType | |
HostileType | |
Prefers.Necro | [MM8] |
Prefers.Knight | |
Prefers.Paladin | [MM6, MM7] |
Prefers.Archer | [MM6, MM7] |
Prefers.Druid | [MM6, MM7] |
Prefers.Cleric | |
Prefers.Troll | [MM8] |
Prefers.Minotaur | [MM8] |
Prefers.DarkElf | [MM8] |
Prefers.Vampire | [MM8] |
Prefers.Dragon | [MM8] |
Prefers.Sorcerer | [MM6, MM7] |
Prefers.Ranger | [MM7] |
Prefers.Thief | [MM7] |
Prefers.Monk | [MM7] |
Prefers.Male | in monsters.txt it's "M" in MM6 and "X" in MM7 and MM8 |
Prefers.Female | |
Prefers.Human | [MM7] |
Prefers.Elf | [MM7] |
Prefers.Dwarf | [MM7] |
Prefers.Goblin | [MM7] |
PrefClass | Preferred target |
Bonus | (steal, curse, ...) |
BonusMul | Disease1x5 etc. The chance that a monster would use the bonus is Level*BonusMul |
Attack1 | |
Attack2Chance | |
Attack2 | |
SpellChance | |
Spell | |
Spell2Chance | [MM7+] |
Spell2 | [MM7+] |
SpellSkill | |
Resistances[kind] | For immunity use const.MonsterImmune |
FireResistance | |
AirResistance | [MM7+] |
WaterResistance | [MM7+] |
EarthResistance | [MM7+] |
MindResistance | [MM7+] |
SpiritResistance | [MM7+] |
BodyResistance | [MM7+] |
LightResistance | [MM7+] |
DarkResistance | [MM7+] |
ElecResistance | [MM6] |
ColdResistance | [MM6] |
PoisonResistance | [MM6] |
PhysResistance | |
Special | [MM7+] 1 = shot, 2 = summon, 3 = explode |
SpecialA |
[MM7+] shot: C = count summon: A = {RandomLevel = 0, fixed = 1} – monster level (0 means A, B or C is chosen randomly, monster index should be that of A variation. Values of 2 and 3 are the same as 1, but in MM7 before GrayFace Patch v2.1 it was causing a bug), B = {ground = 0, air = 1}, C = already summoned count (up to 3), D = monster index explode: AdB + C, D = attack type |
SpecialB | [MM7+] |
SpecialC | [MM7+] |
MagicResistance | [MM6] |
PrefNum | Number of party members to hit using Attack1 & Attack2 |
BloodSplat | [MM7+] |
Spell2Skill | [MM7+] |
SpecialD | [MM7+] (summoned monster or damage type in case of explosive attack) |
QuestItem | [MM6] |
FullHP | |
FullHitPoints | |
ArmorClass | |
Exp | |
Experience | |
MoveSpeed | |
AttackRecovery | |
RangeAttack | |
Id2 | |
BodyRadius | |
BodyHeight | |
Velocity | |
Pos[] | |
X | |
Y | |
Z | |
VelocityX | |
VelocityY | |
VelocityZ | |
Direction | |
LookAngle | |
Room | |
CurrentActionLength | |
StartX | |
StartY | |
StartZ | |
GuardX | |
GuardY | |
GuardZ | |
GuardRadius | |
AIState | |
GraphicState | |
Item | |
CurrentActionStep | |
Frames[] | |
FramesStand | |
FramesWalk | |
FramesAttack | |
FramesShoot | |
FramesStun | |
FramesGotHit | |
FramesDie | |
FramesDead | |
FramesFidget | |
Sounds[] | |
SoundAttack | |
SoundDie | |
SoundGetHit | |
SoundGotHit | |
SoundFidget | |
SpellBuffs[buff] | |
Items[] | [MM7+] Indexes 0 and 1 are used for stolen items, indexes 2 and 3 are used if TreasureGenerated bit is set: index 2 holds the item and index 3 holds the gold. |
Group | |
Ally | Monster class that guards or is guraded by this one. That is, (Id + 2):div(3), like in Hostile.txt. |
Schedules[] | |
Summoner | |
LastAttacker | Last one who hit the monster |
NameId | [MM7+] From PlaceMon.txt |
Methods: |
|
IsAgainst(Mon2) | [MM7+] Returns aggression number between 0 and 4. If Mon2 isn't specified, attitude towards party is checked. |
LoadFrames(SoundLoaded = false) | If SoundLoaded = false, sound indexes would be loaded for the monster as well. |
ChooseTargetPlayer() | Returns player slot index |
CalcTakenDamage(DamageKind, Damage) | Returns the amount of damage the monster has to actually receive |
CalcHitByEffect(DamageKind) | Returns true if the monster couldn't dodge the effect |
CalcHitOrMiss(Player) | |
UpdateGraphicState() | Updates GraphicState in accordance with AIState |
ShowSpellEffect(Color24 = 0) | [MM7+] Shows effect from a spell (as a cylinder of colored dots around the monster) |
GotHit(By = 4, ResetAnimation = false) | Shows monster's getting hit animation, produces sound and boosts aggression level. By is the attacker object reference value. |
PlaySound(SoundIndex) | [MM7+] SoundIndex is from 0 to 3 or any of these corresponding strings: "Attack", "Die", "GotHit", "Fidget" |
LoadFramesAndSounds() | |
ChangeLook(id) | Takes on the look of another monster kind |
GetPropertiesFromId(id) | Takes all properties, except appearance and sounds from another monster kind |
SetId(id) | Changes Id and, if it's not 0, Id2 |
SetCustomFrames(Stand, Walk, Attack, Shoot, Stun, GotHit, Die, Dead, Fidget) | Any argument can be nil. Also loads the frames. |
GetIndex() |
structs.MapNote[MM8]Fields: |
|
Active | |
X | |
Y | |
Text | |
Id |
structs.MapObjectFields: |
|
Type | look type (see Id in dobjlist.bin) |
TypeIndex | line in dobjlist.bin |
Pos[] | |
X | |
Y | |
Z | |
Velocity[] | |
VelocityX | |
VelocityY | |
VelocityZ | |
Direction | |
LookAngle | |
Visible | |
Temporary | |
HaltTurnBased | |
DroppedByPlayer | |
IgnoreRange | |
NoZBuffer | |
SkipAFrame | |
AttachToHead | |
Missile | |
Removed | |
Bits | |
Room | |
Age | |
MaxAge | |
LightMultiplier | |
Item | |
SpellType | |
Spell | same as SpellType |
SpellSkill | |
SpellLevel | |
SpellMastery | same as SpellLevel |
SpellEffect | [MM7+] |
Owner | |
Target | |
Range | Distance to target: 0 – less than 307.2, 1 – less than 1024, 2 – less then 2560, 3 – 2560 or more |
AttackType | 0 – Attack1, 1 – Attack2, 2 – Spell, 3 – Spell2, 4 – Explode |
StartPos[] | |
StartX | starting x |
StartY | starting y |
StartZ | starting z |
structs.MapOutlineFields: |
|
Vertex1 | |
Vertex2 | |
Facet1 | |
Facet2 | |
Z | |
Visible | |
Bits |
structs.MapOutlinesFields: |
|
Items[] | (Default) |
structs.MapSpriteFields: |
|
DecListId | |
DecName | |
TriggerByTouch | only for "Event Trigger" sprites – triggered when a player comes into TriggerRadius |
TriggerByMonster | only for "Event Trigger" sprites – triggered when a monster comes into TriggerRadius |
TriggerByObject | only for "Event Trigger" sprites – triggered when an object gets into TriggerRadius |
ShowOnMap | |
IsChest | |
Invisible | |
IsObeliskChest | [MM7+] |
IsShip | [MM6] |
Bits | |
Pos[] | |
X | |
Y | |
Z | |
Direction | |
Id | [MM7+] |
EventVariable | event variable for barrels etc. |
Event | normal event |
TriggerRadius | |
DirectionDegrees | only used if Direction is 0 |
structs.MapVertexFields: |
|
1 | same as X |
X | |
2 | same as Y |
Y | |
3 | same as Z |
Z |
structs.MissileSetupFields: |
|
AlwaysShowSprite | When there's a special way to display the object, still show the sprite as well |
HideSpecialDisplay | Don't display the object in a special way |
AutoCollision | When the object hits anything, show an explosion, play explosion sound of the spell and call MonsterAttacked or PlayerAttacked appropriately |
structs.ModelFacetFields: |
|
Normal[] | normal fixed pt (0..0x10000) |
NormalX | normal X fixed pt (0..0x10000) |
NormalY | normal Y fixed pt (0..0x10000) |
NormalZ | normal Z fixed pt (0..0x10000) |
NormalDistance | normal distance fixed pt (0..0x10000) |
ZCalc1 | = -(NormalX * 2^16) / NormalZ (or 0 if NormalZ is 0) |
ZCalc2 | = -(NormalY * 2^16) / NormalZ (or 0 if NormalZ is 0) |
ZCalc3 | = -(NormalDistance * 2^16) / NormalZ (or 0 if NormalZ is 0) |
IsPortal | |
IsSecret | [MM7+] show in red with Perception |
ScrollDown | [MM7+] moving texture |
AlignTop | [MM7+] align door texture in D3D |
IsWater | |
ScrollUp | [MM7+] moving texture |
ScrollLeft | [MM7+] moving texture |
ProjectToXY | |
ProjectToXZ | |
ProjectToYZ | |
ScrollRight | [MM7+] moving texture |
AlignLeft | [MM7+] align door texture in D3D |
Invisible | |
AnimatedTFT | |
AlignRight | [MM7+] align door texture in D3D |
AlignBottom | [MM7+] align door texture in D3D |
MoveByDoor | |
IsEventJustHint | [MM7+] |
AlternativeSound | |
IsSky | outdoor in software mode: horizontal flow |
FlipU | |
FlipV | |
TriggerByClick | |
TriggerByStep | |
DisableEventByCtrlClick | [MM8] indoor only: click event gets disabled by Ctrl+Click |
EventDisabledByCtrlClick | [MM8] |
TriggerByMonster | [MM6, MM7] happens even if there's no event assigned |
TriggerByObject | [MM6, MM7] happens even if there's no event assigned |
Untouchable | great for vertical facets of stairs. [MM7+] Shouldn't be used for sloped floor, like it's used in MM6. |
IsLava | |
HasData | |
Bits | |
VertexIds[] | |
UList[] | |
VList[] | |
XInterceptDisplacement[] | |
YInterceptDisplacement[] | |
ZInterceptDisplacement[] | |
BitmapId | |
BitmapU | |
BitmapV | |
MinX | Bounding Box Min X |
MaxX | Bounding Box Max X |
MinY | Bounding Box Min Y |
MaxY | Bounding Box Max Y |
MinZ | Bounding Box Min Z |
MaxZ | Bounding Box Max Z |
Id | |
Event | |
GradientVertexes[] | |
VertexesCount | |
PolygonType |
Polygon type: 0 = empty 1 = wall 2 = unused 3 = horizontal floor 4 = irregular floor (non-horizontal) 5 = horizontal ceiling 6 = irregular ceiling (non-horizontal) |
structs.ModelVertexFields: |
|
1 | same as X |
X | |
2 | same as Y |
Y | |
3 | same as Z |
Z |
structs.MonsterAttackInfoFields: |
|
Type | |
DamageDiceCount | |
DamageDiceSides | |
DamageAdd | |
Missile |
structs.MonsterKind[MM7+]Fields: |
|
Undead | |
Demon | [MM7] |
Dragon | |
Elf | [MM7] |
Swimmer | |
Immobile | |
Peasant | [MM8] |
Titan | [MM7] |
NoArena | |
Ogre | [MM8] |
Elemental | [MM8] |
structs.MonsterScheduleFields: |
|
Pos[] | |
X | |
Y | |
Z | |
Bits | (1 – on) |
Action | |
Hour | |
Day | |
Month |
structs.MonstersTxtItemFields: |
|
Name | |
Picture | |
Id | Changing may cause random crashes after loading the game! Be careful. |
Level | |
TreasureItemPercent | |
TreasureDiceCount | |
TreasureDiceSides | |
TreasureItemLevel | |
TreasureItemType | |
Fly | |
MoveType | |
AIType | |
HostileType | |
Prefers.Necro | [MM8] |
Prefers.Knight | |
Prefers.Paladin | [MM6, MM7] |
Prefers.Archer | [MM6, MM7] |
Prefers.Druid | [MM6, MM7] |
Prefers.Cleric | |
Prefers.Troll | [MM8] |
Prefers.Minotaur | [MM8] |
Prefers.DarkElf | [MM8] |
Prefers.Vampire | [MM8] |
Prefers.Dragon | [MM8] |
Prefers.Sorcerer | [MM6, MM7] |
Prefers.Ranger | [MM7] |
Prefers.Thief | [MM7] |
Prefers.Monk | [MM7] |
Prefers.Male | in monsters.txt it's "M" in MM6 and "X" in MM7 and MM8 |
Prefers.Female | |
Prefers.Human | [MM7] |
Prefers.Elf | [MM7] |
Prefers.Dwarf | [MM7] |
Prefers.Goblin | [MM7] |
PrefClass | Preferred target |
Bonus | (steal, curse, ...) |
BonusMul | Disease1x5 etc. The chance that a monster would use the bonus is Level*BonusMul |
Attack1 | |
Attack2Chance | |
Attack2 | |
SpellChance | |
Spell | |
Spell2Chance | [MM7+] |
Spell2 | [MM7+] |
SpellSkill | |
Resistances[kind] | For immunity use const.MonsterImmune |
FireResistance | |
AirResistance | [MM7+] |
WaterResistance | [MM7+] |
EarthResistance | [MM7+] |
MindResistance | [MM7+] |
SpiritResistance | [MM7+] |
BodyResistance | [MM7+] |
LightResistance | [MM7+] |
DarkResistance | [MM7+] |
ElecResistance | [MM6] |
ColdResistance | [MM6] |
PoisonResistance | [MM6] |
PhysResistance | |
Special | [MM7+] 1 = shot, 2 = summon, 3 = explode |
SpecialA |
[MM7+] shot: C = count summon: A = {RandomLevel = 0, fixed = 1} – monster level (0 means A, B or C is chosen randomly, monster index should be that of A variation. Values of 2 and 3 are the same as 1, but in MM7 before GrayFace Patch v2.1 it was causing a bug), B = {ground = 0, air = 1}, C = already summoned count (up to 3), D = monster index explode: AdB + C, D = attack type |
SpecialB | [MM7+] |
SpecialC | [MM7+] |
MagicResistance | [MM6] |
PrefNum | Number of party members to hit using Attack1 & Attack2 |
BloodSplat | [MM7+] |
Spell2Skill | [MM7+] |
SpecialD | [MM7+] (summoned monster or damage type in case of explosive attack) |
QuestItem | [MM6] |
FullHP | |
FullHitPoints | |
ArmorClass | |
Exp | |
Experience | |
MoveSpeed | |
AttackRecovery |
structs.MoveToMapFields: |
|
Pos[] | |
X | |
Y | |
Z | |
Direction | 0 – 2047. 0 is East. |
LookAngle | |
SpeedZ | |
Defined | |
Methods: |
|
Set() |
structs.NPCNewsItem[MM6]Fields: |
|
Topic | |
Text | |
Map |
structs.NPCProfTxtItem[MM6, MM7]Fields: |
|
Chance | [MM6] |
Cost | |
Personality | [MM6] |
Benefit | |
ActionText | [MM7] |
JoinText | |
DismissText | [MM7] |
ProfNewsTopic[] | [MM6] |
ProfNewsText[] | [MM6] |
structs.OODialogManager[MM8]Fields: |
|
VMT | |
CurrentDialogPtr | |
DialogPtrs[] | |
Count | |
Disabled | |
Methods: |
|
ShowDialog() | |
CloseCurrent(arg1 = 0, arg2 = true) | |
FindDialog() | |
CloseSpecific() |
structs.ObjectRefFields: |
|
ZBuf | |
Value | Raw value. In inventory screen this is item index, in other screens it equals Kind + Index*8. |
Kind | |
Index | |
Methods: |
|
Get() |
structs.OdmHeaderFields: |
|
Name | |
FileName | |
VersionStr | |
TilesetsFile | [MM8] 0 = dtile.bin, 1 = dtile2.bin, 2 = dtile3.bin |
Tilesets[] | |
Bits | [MM8] |
structs.OverlayItemFields: |
|
Id | |
Type | |
SFTIndex | |
SFTGroup |
structs.PFTItemFields: |
|
GroupId | |
FrameIndex | |
Time | time for this frame in 1/32 of a second |
TotalTime | total time for this group |
NotGroupEnd | |
GroupStart | |
Bits |
structs.PlayerFields: |
|
Biography | [MM8] |
Sex | [MM6, MM7] |
Cursed | |
Weak | |
Asleep | |
Afraid | |
Drunk | |
Insane | |
Poison1 | |
Disease1 | |
Poison2 | |
Disease2 | |
Poison3 | |
Disease3 | |
Paralyzed | |
Unconscious | |
Dead | |
Stoned | |
Eradicated | |
Zombie | [MM7+] |
Good | |
Conditions[cond] | |
Exp | |
Experience | |
Name | |
Class | |
Face | |
Stats[stat] | |
MightBase | |
MightBonus | |
IntellectBase | |
IntellectBonus | |
PersonalityBase | |
PersonalityBonus | |
EnduranceBase | |
EnduranceBonus | |
SpeedBase | |
SpeedBonus | |
AccuracyBase | |
AccuracyBonus | |
LuckBase | |
LuckBonus | |
ArmorClassBonus | |
LevelBase | |
LevelBonus | |
AgeBonus | |
Skills[skill] | |
Awards[] | |
Spells[spell] | |
UsedBlackPotions[] | |
Items[] | |
Inventory[] | (Items index) for main item cell, -(1 + main Inventory cell index) for other cells |
Resistances[kind] | |
FireResistanceBase | |
AirResistanceBase | [MM7+] |
WaterResistanceBase | [MM7+] |
EarthResistanceBase | [MM7+] |
SpiritResistanceBase | [MM7+] |
MindResistanceBase | [MM7+] |
BodyResistanceBase | [MM7+] |
FireResistanceBonus | |
AirResistanceBonus | [MM7+] |
WaterResistanceBonus | [MM7+] |
EarthResistanceBonus | [MM7+] |
SpiritResistanceBonus | [MM7+] |
MindResistanceBonus | [MM7+] |
BodyResistanceBonus | [MM7+] |
Voice | [MM7+] |
ColdResistanceBase | [MM6] |
ColdResistanceBonus | [MM6] |
ElecResistanceBase | [MM6] |
ElecResistanceBonus | [MM6] |
PoisonResistanceBase | [MM6] |
PoisonResistanceBonus | [MM6] |
MagicResistanceBase | [MM6] |
MagicResistanceBonus | [MM6] |
SpellBuffs[buff] | |
RecoveryDelay | |
SkillPoints | |
HP | |
HitPoints | |
SP | |
SpellPoints | |
BirthYear | |
EquippedItems[slot] | |
ItemExtraHand | |
ItemMainHand | |
ItemBow | |
ItemArmor | |
ItemHelm | |
ItemBelt | |
ItemCloak | |
ItemGauntlets | Was called ItemGountlets before MMExtension v2.3, old name is supported for backward compatibility |
ItemBoots | |
ItemAmulet | |
ItemRing1 | |
ItemRing2 | |
ItemRing3 | |
ItemRing4 | |
ItemRing5 | |
ItemRing6 | |
SpellBookPage | |
QuickSpell | |
AttackSpell | Added in version 2.5 of my patches |
PlayerBits[] | |
RosterBitIndex | [MM8] |
MeleeAttackBonus | [MM6, MM7] |
MeleeDamageBonus | [MM6, MM7] |
RangedAttackBonus | [MM6, MM7] |
RangedDamageBonus | [MM6, MM7] |
FullHPBonus | [MM6, MM7] |
FullHitPointsBonus | [MM6, MM7] |
FullSPBonus | [MM6, MM7] |
FullSpellPointsBonus | [MM6, MM7] |
Expression | |
ExpressionTimePassed | |
ExpressionLength | |
Beacons[] | |
DevineInterventionCasts | |
ArmageddonCasts | |
FaceBeforeZombie | [MM7] |
OriginalFace | [MM7] |
VoiceBeforeZombie | [MM7] |
OriginalVoice | [MM7] |
Methods: |
|
GetSex(BasedOnVoice[MM8] = false) | [MM7+] Determines sex based on Face or Voice |
GetBaseMight() | |
GetBaseIntellect() | |
GetBasePersonality() | |
GetBaseEndurance() | |
GetBaseAccuracy() | |
GetBaseSpeed() | |
GetBaseLuck() | |
GetBaseLevel() | |
GetLevel() | |
GetMight() | |
GetIntellect() | |
GetPersonality() | |
GetEndurance() | |
GetAccuracy() | |
GetSpeed() | |
GetLuck() | |
GetMeleeAttack(IgnoreExtraHand [MM7+] = false) | |
CalcMeleeDamage(JustWeaponDamage = false, IgnoreExtraHand = false, MonsterId = -1) | |
GetRangedAttack() | |
CalcRangedDamage(MonsterId = -1) | |
CalcHitOrMiss(Monster, Range = 0, Bonus = 0) | AttackType: 0 – melee, 1 – less than 1024, 2 – less then 2560, 3 – 2560 or more. See the Mechanics page on my site for more info on the formula. |
GetMeleeDamageRangeText() | |
GetRangedDamageRangeText() | |
CanTrain() | |
AddHP(Amount) | |
DoDamage(Damage, DamageKind = const.Damage.Phys) | |
DoBadThing(Thing, Monster[MM7+]) | Monster must be specified for stealing in MM7+ |
GetAttackDelay(Shoot = false) | |
GetFullHP() | |
GetFullSP() | |
GetMeleeDamageMin() | [MM7+] |
GetMeleeDamageMax() | [MM7+] |
GetRangedDamageMin() | [MM7+] |
GetRangedDamageMax() | [MM7+] |
GetBaseResistance(Res) | [MM7+] |
GetResistance(Res) | [MM7+] |
HasItemBonus(Bonus2) | [MM7+] Checks whether the player is wearing an item with specified Bonus2 See SPCITEMS.TXT for more info about each bonus. |
GetBaseFireResistance() | [MM6] |
GetBaseElectricityResistance() | [MM6] |
GetBaseColdResistance() | [MM6] |
GetBasePoisonResistance() | [MM6] |
GetBaseMagicResistance() | [MM6] |
GetFireResistance() | [MM6] |
GetElectricityResistance() | [MM6] |
GetColdResistance() | [MM6] |
GetPoisonResistance() | [MM6] |
GetMagicResistance() | [MM6] |
WearsItem(ItemNum, Slot = 16) | If Slot isn't specified, searches all slots for the item |
RemoveFromInventory(Slot) | |
GetStartingClass() | [MM8] |
GetRace() | [MM7] |
GetDiplomacyTotalSkill() | [MM6] |
GetBaseArmorClass() | |
GetArmorClass() | |
GetBaseAge() | |
GetAge() | |
Recover(ByAmount) | |
SetRecoveryDelayRaw(Delay) | |
CalcStatBonusByItems(Stat, IgnoreExtraHand [MM7+] = false) | |
CalcStatBonusByMagic(Stat) | |
CalcStatBonusBySkills(Stat) | |
GetMerchantTotalSkill() | |
GetDisarmTrapTotalSkill() | |
ShowFaceExpression(Expression, Time = 0) | |
ShowFaceAnimation(Animation) | |
IsConscious() | |
GetSkill(Skill) | [MM7+] |
GetPerceptionTotalSkill() | [MM7+] |
GetLearningTotalSkill() | [MM7+] |
AddCondition(Condition, CanResist = false) |
[MM7+] Passing const.Condition.Good isn't supported. CanResist only affects application of Protection from Magic spell. If it's true and the spell protects the player, spell strength is decreased instead of condition being applied. |
GetMainCondition() | [MM7+] Returns the condition that affects character stats. Also see GetDisplayedCondition. |
ResetToClass(arg1) | [MM6, MM7] |
GetDisplayedCondition() | Returns the condition displayed on character face and in character properties. Since pacth 2.5 it can differ from GetMainCondition. |
EnumActiveItems(includeBroken) | |
GetActiveItem(slot, includeBroken) | |
CountItems({item1, item2, ...}) | |
SetRecoveryDelay(Delay) | |
GetIndex() | Returns player index in Party.PlayersArray |
GetSlot() | Returns player slot index. Returns nil in MM8 if the player is not in the active party. |
structs.ProgressBarFields: |
|
Max | |
Current | |
Kind | |
SeenScreens[] | [MM7+] |
PcxLoading | |
PcxWomover | |
PcxDemover | |
PcxWomover2 | |
PcxDemover2 | |
BmpFireball | |
BmpBardata | |
BmpLoadprog | [MM7+] |
Methods: |
|
Show() | |
Hide() | |
Draw() | |
Increment() | |
SetMax(arg1) |
structs.SFTFields: |
|
MatchIndex | used when searching for a group by name |
Frames[] | (Default) |
Groups[][] | sorted by name |
GroupIndex[] | |
Methods: |
|
FindGroup(arg1 = "") | |
LoadGroup(arg1) |
structs.ShopItemKindFields: |
|
Level | |
Types[1..4] | (Default) |
structs.SpawnPointFields: |
|
Pos[] | |
X | |
Y | |
Z | |
Radius | |
Kind | |
Index | Index: monster (1-3: M1-M3, 4-6: M1a-M3a, 7-9: M1b-M3b, 10-12: M1c-M3c) or item (1-6 for regular items, 7 for artifact) |
OnAlertMap | |
Bits | |
Group | [MM7+] |
structs.SpcItemsTxtItemFields: |
|
NameAdd | |
BonusStat | |
ChanceForSlot[] | |
W1 | |
W2 | |
Miss | |
Arm | |
Shld | |
Helm | |
Belt | |
Cape | |
Gaunt | |
Boot | |
Ring | |
Amul | |
Value | |
Lvl |
structs.SpellBuffFields: |
|
ExpireTime | |
Power | |
Skill | |
OverlayId | |
Caster | |
Bits | |
Methods: |
|
Set(ExpireTime, Skill, Power, OverlayId, Caster) |
structs.SpellEffect[MM7+]Fields: |
structs.SpritesLodFields: |
|
File | |
FileName | |
Loaded | |
IOBuffer | |
IOBufferSize | |
LodHeaderSignature | |
Description | |
ArchivesCount | |
ArchivesCArray | |
Type | |
ChapterHandle | |
ChapterSize | |
Files[] | |
FilesOffset | |
SpritesSW[] | |
IsHardware | |
SpritesD3D[] | |
Methods: |
|
HasFile(name) | Does a slow search for a file. For sorted LOD archives FindFile works faster. |
FindFile(name, unsorted = false) |
Finds a file and returns file stream address or 0 if file isn't found. By default performs fast binary search. Pass unsorted = true when searching in Game.GamesLod and Game.SaveGameLod, because these archives aren't sorted lexicographically and thus binary search can't be used for them. Returned file stream can be used in Game.FileRead, Game.FileSeek and Game.FileTell functions. Its position is set to the beginning of specified file. |
LoadSprite() |
structs.StartStat[MM7+]Fields: |
|
Base | |
Max | |
Spend | how much you spend on it to add a point |
Add | how much is added when you spend a point |
structs.StdItemsTxtItemFields: |
|
NameAdd | |
BonusStat | |
ChanceForSlot[] | |
Arm | |
Shld | |
Helm | |
Belt | |
Cape | |
Gaunt | |
Boot | |
Ring | |
Amul |
structs.TFTItemFields: |
|
Name | texture name |
Index | index in bitmaps.lod |
Time | time for this frame in 1/32 of a second |
TotalTime | total time for this group |
NotGroupEnd | |
GroupStart | |
Bits |
structs.TileItemFields: |
|
Name | |
Id | |
Bitmap | |
TileSet | |
Section | |
Burn | |
Water | |
Block | |
Repulse | |
Flat | |
Wave | |
NoDraw | |
WaterTransition | |
Transition | |
ScrollDown | |
ScrollUp | |
ScrollLeft | |
ScrollRight | |
Bits |
structs.TilesetDefFields: |
|
Group | |
Offset |
structs.TownPortalTownInfoFields: |
|
Pos[] | |
X | |
Y | |
Z | |
Direction | |
LookAngle | |
MapStatsIndex | |
MapIndex | [MM6] |
Map |
structs.TravelInfoFields: |
|
MapIndex | |
DaysAvailable[] | |
Monday | |
Tuesday | |
Wednesday | |
Thursday | |
Friday | |
Saturday | |
Sunday | |
DaysCount | |
Pos[] | |
X | |
Y | |
Z | |
Direction | |
QBit | |
Map |
Constants
const.ActionsValues: |
|
Exit = 113 | |
CustomDialogButton = 1000 | |
CustomDialogHint = 1001 | |
CustomDialogMouseUp = 1002 |
const.CharScreensValues: |
|
Stats = 100 | |
Skills = 101 | |
Awards = 102 | |
Inventory = 103 |
const.ChestBitsValues: |
|
Trapped = 1 | |
ItemsPlaced = 2 | |
Identified = 4 |
const.DlgIDValues: |
|
Generic = 1 | a lot of dialogs use this Id |
Menu = 3 | |
Inventory = 4 | character screen, not necessarily Inventory |
Controls = 6 | |
Info = 9 | Quests, Autonotes, Map, Calendar, History, Town Portal, Lloyd Beacon. See const.InfoDialog for values of Param that define the dialog type. |
NPC = 10 | Param is NPC index |
QuickReference = 12 | |
Rest = 16 | |
WalkToMap = 17 | |
SpellBook = 18 | |
SimpleMessage = 19 | |
Chest = 20 | |
SaveGame = 23 | |
LoadGame = 24 | |
House = 25 | Param is the house index |
MapEntrance = 26 | |
SelectTarget = 27 | Heal and other such spells |
Scroll = 30 | When reading a message scroll |
ItemSpell = 31 | |
EscMessage = 70 | |
Query = 80 | |
CheatCreateItem = 89 | |
Button = 90 | shown for 1 frame when clicking most buttons |
ButtonImg2 = 91 | |
ButtonTransparent = 92 | |
ButtonTransparentImg2 = 93 | |
ButtonSaveLoad = 94 | clicking Save/Load button in corresponding dialog |
ButtonEscTransparent = 95 | |
ButtonEsc = 96 | |
ButtonEscImg2 = 97 | |
ButtonRestAndHeal = 98 | |
DrawImage = 99 | used in Info screen to draw the currently selected book |
CheatCreateMonster = 103 | |
ConfigureKeyboard = 105 | |
VideoOptions = 106 | |
CustomDialog = 1000 | used by CustomDialog function |
BlockDialogs = 1001 | in MM8 if custom dialogs exist, these screens are created for each OO dialog in order to prevent processing of said custom dialogs |
BlockDialogsNoDraw = 1002 | used in situation discribed above when it's also important to block drawing the dialogs |
const.HouseScreensValues: |
|
Teacher = -1 | |
ChoosePerson = 0 | |
Main = 1 | |
BuyStandard = 2 | |
Sell = 3 | |
Identify = 4 | |
BuySpecial | |
BuySpecialMM6 = 6 | |
BankDeposit = 7 | |
BankWithdraw = 8 | |
Heal = 10 | |
Donate = 11 | |
ProfNews = 12 | NPC command |
JoinMenu = 13 | NPC command |
News = 14 | NPC command |
TavernSleep = 15 | |
TavernFood = 16 | |
Train = 17 | |
BuySpells = 18 | |
A = 19 | NPC command |
B = 20 | NPC command |
C = 21 | NPC command |
D = 22 | [MM7+] NPC command |
E = 23 | [MM7+] NPC command |
F = 24 | [MM7+] NPC command |
Beg = 22 | [MM6] NPC command |
SeerHint = 22 | [MM6] NPC command |
Threat = 23 | [MM6] NPC command |
Bribe = 24 | [MM6] NPC command |
TavernDrink = 25 | |
TavernTip = 26 | |
Staff = 36 | |
Sword = 37 | |
Dagger = 38 | |
Axe = 39 | |
Spear = 40 | |
Bow = 41 | |
Mace = 42 | |
Blaster = 43 | |
Shield = 44 | |
Leather = 45 | |
Chain = 46 | |
Plate = 47 | |
Fire = 48 | |
Air = 49 | |
Water = 50 | |
Earth = 51 | |
Spirit = 52 | |
Mind = 53 | |
Body = 54 | |
Light = 55 | |
Dark = 56 | |
DarkElfAbility = 57 | [MM8] |
VampireAbility = 58 | [MM8] |
DragonAbility = 59 | [MM8] |
IdentifyItem | |
Merchant | |
Repair | |
Bodybuilding | |
Meditation | |
Perception | |
Regeneration = 66 | [MM8] |
Diplomacy = 63 | [MM6, MM7] |
Thievery = 64 | [MM6, MM7] |
DisarmTraps | |
Dodging | [MM7+] |
Unarmed | [MM7+] |
IdentifyMonster | [MM7+] |
Armsmaster | [MM7+] |
Stealing | [MM7+] |
Alchemy | [MM7+] |
Learning | |
Travel1 | |
Travel2 | |
Travel3 | |
HireOrDismiss | NPC command |
MoreInformation | NPC command |
TeachSkill | NPC command |
DoTeachSkill | NPC command |
JoinGuild | NPC command |
DoJoinGuild | NPC command |
BountyHuntNPC | NPC command |
SeerILostIt | NPC command |
ArenaPage | NPC command |
ArenaSquire | NPC command |
ArenaKight | NPC command |
ArenaLord | NPC command |
ArenaMenu | NPC command |
ArenaGoBack | NPC command |
ArenaWin | NPC command |
ArenaAlreadyWon | NPC command |
DisplayInventory = 94 | [MM7+] |
LearnSkills = 96 | [MM7+] |
BountyHunt = 99 | [MM7+] |
PayFine = 100 | [MM7+] |
ArcomageMenu = 101 | [MM7+] |
ArcomageRules = 102 | [MM7+] |
ArcomageConditions = 103 | [MM7+] |
ArcomagePlay = 104 | [MM7+] |
Travel4 = 109 | [MM7+] |
BuySpellsFire = 110 | [MM8] |
BuySpellsAir = 111 | [MM8] |
BuySpellsWater = 112 | [MM8] |
BuySpellsEarth = 113 | [MM8] |
BuySpellsSpirit = 114 | [MM8] |
BuySpellsMind = 115 | [MM8] |
BuySpellsBody = 116 | [MM8] |
BuySpellsLight = 117 | [MM8] |
BuySpellsDark = 118 | [MM8] |
JoinRoster = 119 | [MM8] Yes item in the join menu |
JoinRosterNo = 120 | [MM8] |
SeerPilgrimage = 88 | [MM6] NPC command |
StreetNPC = 200 | Not used by the game, only by MMExt for PopulateNPCDialog event. |
LackFame = 201 | Not used by the game, only by MMExt for PopulateNPCDialog event. |
BegThreatBribe = 202 | Not used by the game, only by MMExt for PopulateNPCDialog event. |
ThreatBribe = 203 | Not used by the game, only by MMExt for PopulateNPCDialog event. |
const.InfoDialogValues: |
|
LloydBeacon = 177 | |
TownPortal = 195 | |
Quests = 200 | |
Autonotes = 201 | |
Map = 202 | |
Calendar = 203 | |
History = 224 |
const.MonsterActionValues: |
|
Attack1 = 0 | |
Attack2 = 1 | |
Spell1 = 2 | |
Spell2 = 3 | [MM7+] |
const.MonsterKind[MM7+]Values: |
|
Undead = 1 | |
Demon = 2 | [MM7] |
Dragon | |
Elf = 4 | [MM7] |
Swimmer | |
Immobile | |
Peasant = 5 | [MM8] |
Titan = 7 | [MM7] |
NoArena | |
Ogre = 7 | [MM8] |
Elemental = 8 | [MM8] |
const.ObjectRefKindValues: |
|
Nothing = 0 | |
Door = 1 | |
Object = 2 | |
Monster = 3 | |
Party = 4 | Index is player index in Party.PlayersArray |
Sprite = 5 | |
Facet = 6 | Outdoors Index = ModelId*64 + FaceId |
Light = 7 |
const.Race[MM7]Values: |
|
Human = 0 | |
Elf = 1 | |
Goblin = 2 | |
Dwarf = 3 |
const.ScreensValues: |
|
Game = 0 | |
Menu = 1 | |
Controls = 2 | |
Info = 3 | Quests, Autonotes, Map, Calendar, History, Town Portal, Lloyd Beacon |
NPC = 4 | |
Rest = 5 | |
Query = 6 | like with hotkeys in Chinese debug MM6 |
Inventory = 7 | character screen, not necessarily Inventory |
SpellBook = 8 | |
NewGameBriefing = 9 | was called NewGameBreefing before MMExtension v2.3, old name is supported for backward compatibility |
Chest = 10 | |
SaveGame = 11 | |
LoadGame = 12 | |
House = 13 | |
InventoryInShop = 14 | double clicking a character in any Buy dialog in MM6 or in Buy Standard in MM7 |
InventoryInChest = 15 | |
MainManu = 16 | or movie |
WalkToMap = 17 | |
MapEntrance = 18 | or Question screen |
SimpleMessage = 19 | |
SelectTarget = 20 | Heal and other such spells in MM8 |
CreateParty = 21 | |
EscMessage = 22 | |
ItemSpell = 23 | |
KeyConfig = 26 | |
VideoOptions = 28 | |
AdventurersInn = 29 | |
ItemSpellMM6 = 103 | |
QuickReference = 104 |
const.SeasonValues: |
|
Automn = 0 | |
Summer = 1 | |
Fall = 2 | |
Winter = 3 |
const.StatsValues: |
|
Might = 0 | |
Intellect = 1 | |
Personality = 2 | |
Endurance = 3 | |
Accuracy = 4 | |
Speed = 5 | |
Luck = 6 | |
HP = 7 | |
HitPoints = 7 | |
SP = 8 | |
SpellPoints = 8 | |
ArmorClass = 9 | |
FireResistance = 10 | |
AirResistance = 11 | [MM7+] |
WaterResistance = 12 | [MM7+] |
EarthResistance = 13 | [MM7+] |
MindResistance = 14 | [MM7+] |
BodyResistance = 15 | [MM7+] |
Alchemy = 16 | [MM7+] |
Stealing = 17 | [MM7+] |
DisarmTraps = 18 | [MM7+] |
IdentifyItem = 19 | [MM7+] |
IdentifyMonster = 20 | [MM7+] |
Armsmaster = 21 | [MM7+] |
Dodging = 22 | [MM7+] |
Unarmed = 23 | [MM7+] |
ElecResistance = 11 | [MM6] |
ColdResistance = 12 | [MM6] |
PoisonResistance = 13 | [MM6] |
Level | |
MeleeAttack | |
MeleeDamageBase | |
MeleeDamageMin | For Stats screen. Only used in CalcStatBonusByItems, other events use MeleeDamageBase |
MeleeDamageMax | For Stats screen. Only used in CalcStatBonusByItems, other events use MeleeDamageBase |
RangedAttack | |
RangedDamageBase | |
RangedDamageMin | For Stats screen. Only used in CalcStatBonusByItems, other events use RangedDamageBase |
RangedDamageMax | For Stats screen. Only used in CalcStatBonusByItems, other events use RangedDamageBase |
SpiritResistance = 33 | [MM7+] |
FireMagic = 34 | [MM7+] |
AirMagic = 35 | [MM7+] |
WaterMagic = 36 | [MM7+] |
EarthMagic = 37 | [MM7+] |
SpiritMagic = 38 | [MM7+] |
MindMagic = 39 | [MM7+] |
BodyMagic = 40 | [MM7+] |
LightMagic = 41 | [MM7+] |
DarkMagic = 42 | [MM7+] |
Meditation = 43 | [MM7+] |
Bow = 44 | [MM7+] |
Shield = 45 | [MM7+] |
Learning = 46 | [MM7+] |
DarkElf = 47 | [MM8] |
Vampire = 48 | [MM8] |
Dragon = 49 | [MM8] |
MagicResistance = 23 | [MM6] |
evt.PlayersValues: |
|
0 = 0 | |
1 = 1 | |
2 = 2 | |
3 = 3 | |
4 = 4 | [MM8] |
Current | |
current | |
All = 5 | |
all = 5 | |
Random = 6 | |
random = 6 |
evt.VarNumValues: |
|
SexIs = 1 | |
ClassIs = 2 | |
HP = 3 | |
HasFullHP = 4 | |
SP = 5 | |
HasFullSP = 6 | |
AC = 7 | |
ArmorClass = 7 | |
ACBonus = 8 | |
ArmorClassBonus = 8 | |
BaseLevel = 9 | |
LevelBonus = 10 | |
AgeBonus = 11 | |
Awards = 12 | |
Exp = 13 | |
Experience = 13 | |
QBits = 16 | |
Inventory = 17 | |
Items = 17 | |
HourIs = 18 | |
DayOfYearIs = 19 | |
DayOfWeekIs = 20 | |
Gold = 21 | |
GoldAddRandom = 22 | |
Food = 23 | |
FoodAddRandom = 24 | |
MightBonus = 25 | |
IntellectBonus = 26 | |
PersonalityBonus = 27 | |
EnduranceBonus = 28 | |
SpeedBonus = 29 | |
AccuracyBonus = 30 | |
LuckBonus = 31 | |
BaseMight = 32 | |
BaseIntellect = 33 | |
BasePersonality = 34 | |
BaseEndurance = 35 | |
BaseSpeed = 36 | |
BaseAccuracy = 37 | |
BaseLuck = 38 | |
CurrentMight = 39 | |
CurrentIntellect = 40 | |
CurrentPersonality = 41 | |
CurrentEndurance = 42 | |
CurrentSpeed = 43 | |
CurrentAccuracy = 44 | |
CurrentLuck = 45 | |
FireResistance = 46 | |
AirResistance = 47 | [MM7+] |
WaterResistance = 48 | [MM7+] |
EarthResistance = 49 | [MM7+] |
SpiritResistance = 50 | [MM7+] |
MindResistance = 51 | [MM7+] |
BodyResistance = 52 | [MM7+] |
LightResistance = 53 | [MM7+] unused resistance |
DarkResistance = 54 | [MM7+] unused resistance |
ElecResistance = 47 | [MM6] |
ColdResistance = 48 | [MM6] |
PoisonResistance = 49 | [MM6] |
MagicResistance | unused resistance |
FireResBonus | |
AirResBonus = 58 | [MM7+] |
WaterResBonus = 59 | [MM7+] |
EarthResBonus = 60 | [MM7+] |
SpiritResBonus = 61 | [MM7+] |
MindResBonus = 62 | [MM7+] |
BodyResBonus = 63 | [MM7+] |
LightResBonus = 64 | [MM7+] unused resistance |
DarkResBonus = 65 | [MM7+] unused resistance |
ElecResBonus = 52 | [MM6] |
ColdResBonus = 53 | [MM6] |
PoisonResBonus = 54 | [MM6] |
MagicResBonus | unused resistance |
StaffSkill | |
SwordSkill | |
DaggerSkill | |
AxeSkill | |
SpearSkill | |
BowSkill | |
MaceSkill | |
BlasterSkill | |
ShieldSkill | |
LeatherSkill | |
ChainSkill | |
PlateSkill | |
FireSkill | |
AirSkill | |
WaterSkill | |
EarthSkill | |
SpiritSkill | |
MindSkill | |
BodySkill | |
LightSkill | |
DarkSkill | |
DarkElfAbilitySkill = 89 | [MM8] |
VampireAbilitySkill = 90 | [MM8] |
DragonAbilitySkill = 91 | [MM8] |
IdentifyItemSkill | |
MerchantSkill | |
RepairItemSkill | |
RepairSkill | |
BodybuildingSkill | |
MeditationSkill | |
PerceptionSkill | |
RegenerationSkill = 98 | [MM8] |
DiplomacySkill | [MM6, MM7] |
ThieverySkill | [MM6, MM7] |
DisarmTrapSkill | |
DisarmTrapsSkill | |
DodgingSkill | [MM7+] |
UnarmedSkill | [MM7+] |
IdentifyMonsterSkill | [MM7+] |
ArmsmasterSkill | [MM7+] |
StealingSkill | [MM7+] |
AlchemySkill | [MM7+] |
LearningSkill | |
Cursed | |
Weak | |
Asleep | |
Afraid | |
Drunk | |
Insane | |
Poison1 | |
PoisonedGreen | |
Disease1 | |
DiseasedGreen | |
Poison2 | |
PoisonedYellow | |
Disease2 | |
DiseasedYellow | |
Poison3 | |
PoisonedRed | |
Disease3 | |
DiseasedRed | |
Paralysed | |
Paralyzed | |
Unconscious | |
Dead | |
Stoned | |
Eradicated | |
MainCondition | |
MapVar0 | |
MapVar1 | |
MapVar2 | |
MapVar3 | |
MapVar4 | |
MapVar5 | |
MapVar6 | |
MapVar7 | |
MapVar8 | |
MapVar9 | |
MapVar10 | |
MapVar11 | |
MapVar12 | |
MapVar13 | |
MapVar14 | |
MapVar15 | |
MapVar16 | |
MapVar17 | |
MapVar18 | |
MapVar19 | |
MapVar20 | |
MapVar21 | |
MapVar22 | |
MapVar23 | |
MapVar24 | |
MapVar25 | |
MapVar26 | |
MapVar27 | |
MapVar28 | |
MapVar29 | |
MapVar30 | |
MapVar31 | |
MapVar32 | |
MapVar33 | |
MapVar34 | |
MapVar35 | |
MapVar36 | |
MapVar37 | |
MapVar38 | |
MapVar39 | |
MapVar40 | |
MapVar41 | |
MapVar42 | |
MapVar43 | |
MapVar44 | |
MapVar45 | |
MapVar46 | |
MapVar47 | |
MapVar48 | |
MapVar49 | |
MapVar50 | |
MapVar51 | |
MapVar52 | |
MapVar53 | |
MapVar54 | |
MapVar55 | |
MapVar56 | |
MapVar57 | |
MapVar58 | |
MapVar59 | |
MapVar60 | |
MapVar61 | |
MapVar62 | |
MapVar63 | |
MapVar64 | |
MapVar65 | |
MapVar66 | |
MapVar67 | |
MapVar68 | |
MapVar69 | |
MapVar70 | |
MapVar71 | |
MapVar72 | |
MapVar73 | |
MapVar74 | |
MapVar75 | |
MapVar76 | |
MapVar77 | |
MapVar78 | |
MapVar79 | |
MapVar80 | |
MapVar81 | |
MapVar82 | |
MapVar83 | |
MapVar84 | |
MapVar85 | |
MapVar86 | |
MapVar87 | |
MapVar88 | |
MapVar89 | |
MapVar90 | |
MapVar91 | |
MapVar92 | |
MapVar93 | |
MapVar94 | |
MapVar95 | |
MapVar96 | |
MapVar97 | |
MapVar98 | |
MapVar99 | |
AutonotesBits | |
IsMightMoreThanBase | |
IsIntellectMoreThanBase | |
IsPersonalityMoreThanBase | |
IsEnduranceMoreThanBase | |
IsSpeedMoreThanBase | |
IsAccuracyMoreThanBase | |
IsLuckMoreThanBase | |
PlayerBits | |
NPCs | [MM6, MM7] |
ReputationIs | |
DaysCounter1 | evt.Set starts the count, evt.Cmp compares the difference of dates |
DaysCounter2 | evt.Set starts the count, evt.Cmp compares the difference of dates |
DaysCounter3 | evt.Set starts the count, evt.Cmp compares the difference of dates |
DaysCounter4 | evt.Set starts the count, evt.Cmp compares the difference of dates |
DaysCounter5 | evt.Set starts the count, evt.Cmp compares the difference of dates |
DaysCounter6 | evt.Set starts the count, evt.Cmp compares the difference of dates |
Flying | |
HasNPCProfession | |
TotalCircusPrize | |
SkillPoints | |
MonthIs | |
Counter1 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter2 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter3 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter4 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter5 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter6 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter7 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter8 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter9 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
Counter10 | [MM7+] evt.Set starts the count, evt.Cmp compares the exact time spent in hours |
SpecialDate1 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %51 |
SpecialDate2 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %52 |
SpecialDate3 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %53 |
SpecialDate4 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %54 |
SpecialDate5 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %55 |
SpecialDate6 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %56 |
SpecialDate7 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %57 |
SpecialDate8 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %58 |
SpecialDate9 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %59 |
SpecialDate10 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %60 |
SpecialDate11 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %61 |
SpecialDate12 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %62 |
SpecialDate13 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %63 |
SpecialDate14 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %64 |
SpecialDate15 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %65 |
SpecialDate16 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %66 |
SpecialDate17 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %67 |
SpecialDate18 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %68 |
SpecialDate19 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %69 |
SpecialDate20 | [MM7+] evt.Set remembers current time and plays sound. The date can be used in messages as %70 |
Reputation | [MM7+] |
History1 | [MM7+] |
History2 | [MM7+] |
History3 | [MM7+] |
History4 | [MM7+] |
History5 | [MM7+] |
History6 | [MM7+] |
History7 | [MM7+] |
History8 | [MM7+] |
History9 | [MM7+] |
History10 | [MM7+] |
History11 | [MM7+] |
History12 | [MM7+] |
History13 | [MM7+] |
History14 | [MM7+] |
History15 | [MM7+] |
History16 | [MM7+] |
History17 | [MM7+] |
History18 | [MM7+] |
History19 | [MM7+] |
History20 | [MM7+] |
History21 | [MM7+] |
History22 | [MM7+] |
History23 | [MM7+] |
History24 | [MM7+] |
History25 | [MM7+] |
History26 | [MM7+] |
History27 | [MM7+] |
History28 | [MM7+] |
History29 | [MM7+] |
MapAlert | [MM7+] |
BankGold | [MM7+] |
Deaths | [MM7+] |
MontersHunted | [MM7+] |
PrisonTerms | [MM7+] |
ArenaWinsPage | [MM7+] |
ArenaWinsSquire | [MM7+] |
ArenaWinsKnight | [MM7+] |
ArenaWinsLord | [MM7+] |
Invisible | [MM7+] |
IsWearingItem | [MM7+] |
Players = 318 | [MM8] |
BaseStats[] | |
Conditions[] | |
Counters[] | [MM7+] |
CurrentStats[] | |
DaysCounters[] | |
History[] | [MM7+] |
MapVars[] | |
ResBonus[] | |
Resistance[] | |
Skills[] | |
SpecialDates[] | [MM7+] |