Справочник по скриптовому API HoMM V, версия 1.3

 

1. Про всякое. 6

2. Немножко про LUA. 6

3. Реализация скрипт-системы в HoMMV. 11

3.1. Функции, доступные везде. 11

1. number sqrt(number) 11

2. number mod(number1, number2) 11

3. number random(nHi) 11

4. void sleep(nSegments) 12

5. fProc parse(sToEval) 12

6. void print(s1, s2,...) 12

7. void print_to(sFileName, v1, string2,...) 12

8. sFileName create_file(sFileName) 12

9. sFileName open_file(sFileName) 12

10. void _ERRORMESSAGE(sMsg) 12

11. void errorHook(fCallback) 13

12. void startThread(fProc, vParam1, vParam2,...) 13

13. void doFile(spFileLUA) 14

14. nDifficulty GetDifficulty() 14

15. void consoleCmd(sCmd) 14

16. void SetGameVar(sVarName, sValue) 14

17. string GetGameVar(sVarName) 14

18. void Save(sSaveName) 14

19. void Load(sSaveName) 14

20. void TutorialSetBlink(sID, nOn) 14

21. void TutorialMessageBox(sID) 14

22. bool IsTutorialMessageBoxOpen() 14

23. bool IsTutorialItemEnabled(sID) 14

24. void TutorialActivateHint(sID) 14

25. void TutorialSetBlink(sID, nBlink) 14

3.2. Стратегический режим. 15

3.2.1 Общая работа с параметрами игрока. 15

26. nPlayerID GetCurrentPlayer() 15

27. nPlayerStateID GetPlayerState(nPlayerID) 15

28. number GetPlayerResource(nPlayerID, nResID) 15

29. void SetPlayerResource(nPlayerID, nResID, nCount) 15

30. void SetPlayerStartResources(nPlayerID, nWoodCnt, nOreCnt, nMercuryCnt, nCrystalCnt, nSulfurCnt, nGemCnt, nGoldCnt) 15

31. tsHeroes GetPlayerHeroes(nPlayerID) 16

32. void SetPlayerHeroesCountNotForHire(nPlayerID, nCount) 16

33. nPlayerID GetObjectOwner(sObjectName) 16

34. void SetObjectOwner(sObjectName, nPlayerID) 16

35. bool IsObjectVisible(nPlayerID, sObjectName) 16

36. void OpenCircleFog(nX, nY, nFloorID, nRadius, nPlayerID) 16

3.2.2 Работа с объективами (или с обжективами – кому как). 16

37. nState GetObjectiveState(sObjectiveName, nPlayerID = PLAYER_1) 16

38. void SetObjectiveState(sObjectiveName, nState, nPlayerID = PLAYER_1) 17

39. nProgress GetObjectiveProgress(sObjectiveName, nPlayerID = PLAYER_1) 17

40. void SetObjectiveProgress(sObjectiveName, nProgress, nPlayerID = PLAYER_1) 17

41. bool IsObjectiveVisible(sObjectiveName, nPlayerID = PLAYER_1) 17

42. void SetObjectiveVisible(sObjectiveName, bVisible, nPlayerID = PLAYER_1) 17

3.2.3 Работа с ключами. 17

43. bool HasBorderguardKey(nPlayerID, nKeyID) 17

44. void GiveBorderguardKey(nPlayerID, nKeyID) 17

3.2.4 Работа с регионами. 18

45. void SetRegionBlocked(sRegionName, bEnable, nPlayerID = -1) 18

46. bool IsRegionBlocked(sRegionName, nPlayerID) 18

47. tsObjects GetObjectsInRegion(sRegionName, nObjectsTypeID) 18

48. nX, nY, nFloorID RegionToPoint(sRegionName) 18

49. bool IsObjectInRegion(sHeroName, sRegionName) 18

50. void OpenRegionFog(nPlayerID, sRegionName) 18

3.2.5 Работа с AI. 18

51. void SetAIPlayerAttractor(sObjectName, nPlayerID, nPriority) 18

52. void SetAIHeroAttractor(sObjectName, sHeroName, nPriority) 19

53. void EnableHeroAI(sHeroName, bEnable) 19

54. void MoveHero(sHeroName, nX, nY, nFloorID = -1) 19

55. bool CanMoveHero(sHeroName, nX, nY, nFloorID = -1) 19

56. void EnableAIHeroHiring(nPlayerID, sTownName, bEnable) 19

3.2.6 Работа с героями. 19

57. nCount GetHeroStats(HeroName, nStatID) 19

58. void ChangeHeroStat(sHeroName, nStatID, nDelta) 19

59. bool HasHeroSkill(sHeroName, nSkillID) 20

60. bool GiveHeroSkill(sHeroName, nSkillID) 20

61. void KnowHeroSpell(sHeroName, nSpellID) 20

62. void TeachHeroSpell(sHeroName, nSpellID) 20

63. bool LevelUpHero(sHeroName) 20

64. nLevel GetHeroLevel(sHeroName) 20

65. bool IsHeroAlive(sHeroName) 20

66. void DeployReserveHero(sHeroName, nX, nY, nFloorID = GROUND) 21

67. void UnreserveHero(sHeroName) 21

68. nCost CalcHeroMoveCost(sHeroName, nX, nY, nFloorID = -1) 21

69. void MoveHeroRealTime(sHeroName, nX, nY, nFloorID = -1) 21

70. nCount GetHeroCreatures(sHeroName, nCreatureID) 21

71. void AddHeroCreatures(sHeroName, nCreateureID, nCount) 21

72. void RemoveHeroCreatures(sHeroName, nCreatureID, nCount) 21

73. void GiveArtefact(sHeroName, nArtID, nBindToHero = 0) 22

74. bool HasArtefact(sHeroName, nArtID) 22

75. void RemoveArtefact(sHeroName, nArtID) 22

76. void SetHeroLootable(sHeroName, bEnable) 22

77. bool IsHeroLootable(sHeroName) 22

78. void MarkObjectAsVisited(sObjectName, sHeroName) 22

79. sTownName GetHeroTown(sHeroName) 22

80. sHeroName GetTownHero(sTownName) 22

81. tsObjects GetObjectsFromPath(sHeroName, nX, nY, nFloorID = -1) 23

82. void GiveHeroWarMachine(sHeroName, nWarMachineID) 23

83. void HasHeroWarMachine(sHeroName, nWarMachineID) 23

84. bool RemoveHeroWarMachine(sHeroName, nWarMachineID) 23

85. void StartCombat(sHeroName, sEnemyHeroName, nEnemyIDCount, nCreatureID1, nCount1, nCreatureID2, nCount2,..., spScriptXDB, sFinishProc, spAdventureFlybySceneXDB = nil) 23

86. void SetHeroCombatScript(sHeroName, spScriptXDB) 24

87. void ResetHeroCombatScript(sHeroName) 24

88. void SiegeTown(sHeroName, spAdvMapTownXDB, spAdventureFlybySceneXDB = nil) 24

3.2.6 Работа с городами. 24

89. nLevel GetTownBuildingLevel(sTownName, nBuildingID) 24

90. nLevel GetTownBuildingLimitLevel(sTownName, nBuildingID) 24

91. nLevel GetTownBuildingMaxLevel(sTownName, nBuildingID) 24

92. void SetTownBuildingLimitLevel(sTownName, nBuildingID, nLevel) 24

93. void TransformTown(sTownName, nTownTypeID) 24

94. void RazeTown(sTownName) 25

3.2.7 Работа с объектами. 25

95. nX, nY, nFloorID GetObjectPosition(sObjectName) 25

96. void SetObjectPosition(sObjectName, nX, nY, nFloorID = -1) 25

97. void RemoveObject(sObjectName) 25

98. bool IsObjectExists(sObjectName) 25

99. bool IsObjectEnabled(sObjectName) 25

100.void SetObjectEnabled(sObjectName, bEnable) 26

101.tsObjects GetObjectNamesByType(sObjectTypeSubstr) 26

102.void AddObjectCreatures(sObjectName, nGreatureID, nCount) 26

103.nCount GetObjectCreatures(sObjectName, nCreatureID) 26

104.void RemoveObjectCreatures(sObjectName, nCreatureID, nCount) 26

105.void SetObjectDwellingCreatures(sObjectName, nCreatureID, nCount) 26

106.nCount GetObjectDwellingCreatures(sTownName, nCreatureID) 26

107.void CreateArtifact(sArtName, nArtID, nX, nY, nFloorID) 26

108.void ShowFlyingSign(spTXT, sObjectName, nPlayerID = -1, nTimeSec = 1.0) 26

109.void CreateMonster(sMonsterName, nCreatureID, nCount, nX, nY, nFloorID, nMonsterMoodID, nCreatureCourageID) 27

110.void RemoveAllMonsters(nCreatureID) 27

111.void GenerateMonsters(nCreatureID, nCountGroupsMin, nCountGroupsMax, nCountInGroupMin, nCountInGroupMax) 27

3.2.8 Работа с интерфейсом. 27

112.void QuestionBox(spTXT, sCallback) 27

113.void StartDialogScene(spDialogSceneXDB, sCallback = nil, sSaveName = nil) 28

114.void StartCutScene(sAnimSceneXDB, sCallback = nil, saveName = nil) 28

115.void MessageBox(spTXT, sCallback = nil, sSaveName = nil) 28

116.void StartDialogSceneInt(spDialogSceneXDB, sCallback, sSaveName) 28

117.void StartCutSceneInt(spAnimSceneXDB, sCallback, sSaveName) 28

118.void MessageBoxInt(spTXT, sCallback, sSaveName) 28

119.void MoveCamera(nX, nY, nFloorID, nZoom = 50, nPitch = pi/2, nYawl = 0, nNoZoom = 0, nNoRotate = 0) 28

3.2.9 Функции общей направленности. 29

120.void Loose() 29

121.void Win() 29

122.void BlockGame() 29

123.void UnblockGame() 29

124.void ExitGame() 29

125.number GetDate(nDateID) 29

126.nX, nY GetTerrainSize() 29

127.number GetMaxFloor() 29

128.bool IsTilePassable(nX, nY, nFloorID = GROUND) 29

129.string GetAllNames(nFilterCode) 30

130.void SetWarfogBehaviour(nOnLand, nOnSea) 30

131.void Trigger(nTriggerType,...) 30

3.2.10 Работа со звуком. 33

132.nSoundID Play3DSound(spSoundXDB, nX, nY, nFloorID) 33

133.nSoundID Play2DSound(spSoundXDB) 33

134.void StopPlaySound(nSoundID) 33

3.2.11 Работа с освещением. 33

135.void SetCombatAmbientLight(spAmbientLightXDB) 33

136.void SetObjectFlashlight(sObjectName, sLightID) 33

137.void SetAmbientLight(nFloorID, sLightID, bFade = false, nTime = 1) 33

3.2.12 Всякие непонятки. 33

138.number CalcAverageMonstersTier() 33

139.tnTier GetGuardsTier(sObjectName) 33

140.void SetStandState(sStandName, nState) 34

141.number GetStandState(sStandName) 34

142.number GetStandStatesCount(sStandName) 34

143.void StopTrigger() 34

144.void PlayObjectAnimation(sObjectID, sAnimID, nAction) 34

3.3. Тактический режим. 34

3.3.1 Общее положение дел. 34

3.3.2 Хуки. 35

145.bool DoPrepare() 35

146.bool DoStart() 35

147.bool UnitMove(sUnitName) 35

148.void UnitDeath(sUnitName) 35

3.3.3 Общие функции контроля боя. 36

149.void EnableDynamicBattleMode(bEnable) 36

150.void EnableAutoFinish(bEnable) 36

151.void combatEnableFinish(bEnable) 36

152.void EnableCinematicCamera(bEnable) 36

153.void SetControlMode(nSideID, nModeID) 36

154.void Finish(nSideID) 36

155.void Break() 36

156.void combatSetPause(n1, b1) 37

157.void showHighlighting(bShow = true) 37

3.3.3 Управление юнитами. 37

158.void SummonCreature(nSideID, nCreatureID, nCount, nX = -1, nY = -1) 37

159.void AddCreature(nSideID, nCreatureID, nCount, nX = -1, nY = -1) 37

160.void addUnit(nCreatureID, nSideID, nX, nY, nCount, sUnitName) 37

161.void removeUnit(sUnitName) 37

162.void UnitCastAimedSpell(sUnitName, nSpellID, sTarget) 38

163.void UnitCastGlobalSpell(sUnitName, nSpellID) 38

164.void UnitCastAreaSpell(sUnitName, nSpellID, nX, nY) 38

165.void SetUnitManaPoints(sUnitName, nPoints) 38

166.number GetUnitMaxManaPoints(sUnitName) 38

167.number GetUnitManaPoints(sUnitName) 38

168.void commandDefend(sUnitName) 38

169.void commandDoSpell(sUnitName, nSpellID, nX, nY) 39

170.void commandShot(sUnitName, sVictimName, bDontShowScene = false) 39

171.void commandMove(sUnitName, nX, nY, bCheckPath = false) 39

172.void commandMoveAttack(sAttacker, sVictim, nX = -1, nY = -1, bCheckPath = false) 39

173.void commandDoSpecial(sUnitName, nAbilityID, nX = -1, nY = -1, bCheckPath = false) 39

174.void displace(sUnitName, nX, nY) 40

175.void playAnimation(sUnitName, sAnimType, nActionTypeID = ONESHOT) 40

176.void combatPlayEmotion(nSideID, n1) 40

177.void RemoveAllUnits() 40

178.void setATB(sUnitName, n1) 40

3.3.4 Информационные и прочие функции. 41

179.tsUnits GetUnits(nSideID, nType) 41

180.nCreatureID GetCreatureType(sCreatureName) 41

181.nX, nY pos(sUnitName) 41

182.bool combatStarted() 41

183.sHeroName GetHeroName(sUnitName) 41

184.number GetCreatureNumber(sUnitName) 41

185.nX, nY GetUnitPosition(sUnitName) 41

186.bool exist(sUnitName) 42

187.nHostType GetHost(nSideID) 42

188.nSideID GetUnitSide(sUnitName) 42

189.number GetUnitType(sUnitName) 42

190.number GetWarMachineType(sUnitName) 43

191.number GetBuildingType(sUnitName) 43

192.sUnitName combatReadyPerson() 43

193.string unitNames() 43

194.void postEvent(sEvent, n1 = -1, n2 = -1) 43

3.4. Режим города. 44

3.4.1 Хуки. 44

195.void CreatureHired(nCreatureID, nCount) 44

196.void HeroHired(sHeroName) 44

3.4.2 Прочие функции. 44

 



Про всякое.

 

Во первых строках письма спешу сообщить, что все ниже написанное является плодом вдумчивого изучения существующих скриптов, выполняемого файла, существующей документации а так же результатов различных экспериментов над игрой версии 1.3. Т.е. может содержать (и, наверняка, содержит) ошибки. И всяко не претендует на полноту.

По ходу дела в тектсте неоднократно поминается «официальное руководство по скриптам». Строго говоря, это никакое не руководство, а просто список функций а описаниями, владельцы версии игры 1.3 могут найти его в файле Editor Documentation\HOMM5_Script_Functions.pdf.

Немножко про LUA.

 

С полным руководством по языку рекомендуется ознакомиться тут -http://www.lua.org/manual/5.1/. От начала и до пункта 2.6 включительно - дальнейшее не имеет отношения к нашему случаю. Ниже приведенная информация прочтение полного руководства не заменяет.

Тезисно изложу некоторые моменты, которые для меня показались интересными.

 

LUA - встроенный (embedded) язык. В связи с чем, речи о чем-либо вроде сишной функции main() не идет.

Понятие идентификатора в LUA совпадает с таковым в прочих алгоритмических языках. Это последовательность латинских букв, подчеркиваний и цифр, начинающаяся с буквы.

Регистр букв в идентификаторах имеет значение. Т.е. переменные Dummy и dummy - разные переменные.

Типизация в языке динамическая. Переменные не нужно описывать. Тип переменной определяется в зависимости от контекста ее использования. Порой в связи с этой особенностью приходится предпринимать определенные "финты ушами", например:

 

interval = GetGameVar("interval")

interval = interval + 0

sleep(interval)

 

Функция GetGameVar возвращает строку, а sleep принимает в качестве параметра число. Строчка с прибавлением нуля нужна только для того, чтобы показать интерпретатору, что в дальнейшем мы будем использовать переменную interval как число. Если данную строчку убрать - на вызове sleep получим ругань о несовпадении типов.

Впрочем, подобный подход имеет и массу преимуществ, например, можно написать что-то вроде

 

local currentSign = 2

local lastSign = "sign"..(CurrentSign-1)

local newSign = "sign"..(CurrentSign+1)

local sign = "sign"..CurrentSign

SetObjectEnabled(newSign, false)

SetObjectEnabled(lastSign, true)

ChangeHeroStat(HERO_NAME, STAT_EXPERIENCE, 500*currentSign)

 

В данном примере для формирования имени объектов используется операция конкатенации - ".."

 

Простых типов имеется три - число (обычный double), строка (последовательность октетов в одинарных либо двойных кавычках) и логический (false\true). Есть выделенное значение nil (привет Паскалю). Собственно, значений false и true как таковых в данном интерпретаторе языка нет. Они определены константами в файле /scripts/advmap-startup.lua

 

true = not nil

false = nil

 

Данный файл «работает» только на стратегической карте, а для тактических скриптов эти константы определить забыли. Это следует учитывать.

Из сложных типов представлены таблицы - нечто среднее между структурами и массивами. Вообщем, таблицы очень похожи на привычные массивы, за исключением того, что 1) они могут включать разнородные элементы 2) могут индексироваться не только числами. Допустима, например, такая запись:

 

cam_switch =

{

   { cam = 1, from = 0, to = 3000 },

   { cam = 2, from = 3000, to = 5000 },

   { cam = 1, from = 5000, to = 10000 },

   { cam = 3, from = 10000, to = 16000 },

   { cam = 1, from = 16000, to = 25000 },

}

 

и, соответственно, обращение из кода

 

cam_switch[2].cam (или cam_switch[2]["cam"], такая вот забавная форма)

                                                                                         

Отдельным интересным вопросом применительно к игре является нижняя граница индексации таблиц в случае числового индекса. С одной стороны, таблицы, заданные непосредственно в теле скрипта (как выше) по умолчанию индексируются от единицы, с другой – большинство таблиц, возвращаемых функциями наподобие GetPlayerHeroes,  имеют нижней границей ноль. «Умом это не понять, следует просто запомнить» (с).

 

Другим сложным типом является функция. Или указатель на функцию – зависит от восприятия. В игровых скриптах он практически не используется, вместо этого задействованы имена функций (типа строка) с последующим их вычислением посредством функции parse. Впрочем, я забегаю вперед.

 

Полная форма оператора присваивания весьма забавна. Например, выражение

 

x, y = y, x

 

приведет к обмену значений переменных x и y. Рекомендовал бы использовать простую форму - незачем путать себя и окружающих. Вместо

 

local state, new_state = GetGameVar(stname), -1

 

можно (и IMHO нужно) написать

 

local state = GetGameVar(stname)

local new_state = -1

 

Имеется стандартный набор операторов контроля выполнения кода. Т.е. if, while, repeat, и две формы цикла for. Синтаксис таков:

 

while exp do block end

 

Цикл while - выполнять блок block пока условие exp истинно.

 

Пример:

 

while IsTutorialMessageBoxOpen() do

sleep(1)      

end

 

Цикл repeat - выполнять блок block пока условие exp ложно. Всегда выполняется хотя бы один раз.

repeat block until exp

 

Пример:

 

repeat

Sleep(1)

until (GetCurrentPlayer() ~= PLAYER_2)

 

Условный оператор.

if exp then block {elseif exp then block} [else block] end

 

Пример:

 

if sID == "n1male" then

nUnitObjectID = 1

elseif sID == "n1female" then

nUnitObjectID = 2

else

nUnitObjectID = 3

end

 

Замечу - выражение exp в выше приведенных примерах может быть любого типа. Но только значения false и nil трактуются как false. Все прочее будет рассматриваться, как true. Посему товарищам, практикующим С и С++ следует быть осторожнее - выражения типа

 

if(0) then

end

 

тут не проходят. Блок внутри оператора if в вышеприведенном примере будет выполняться всегда.

 

С циклами for ситуация немного посложнее. Имеются две формы данного цикла. Первая форма:

 

for var = value, limit, step do block end

 

Выполнять block до тех пор, пока значение var не достигнет limit. На каждой итерации значение var увеличивается на step. Если step отсутствует, то принимается, что он равен единице. Понятно дело, явно изменять значение var внутри цикла нельзя.

 

Пример:

 

for i = 1, 11, 1 do

RemoveObject('inferno'..i)

end

 

в связи с последним замечанием, можно, впрочем написать и попроще -

 

for i = 1, 11 do

RemoveObject('inferno'..i)

end

 

Вторая форма оператора предназначена для работы с таблицами.

 

for index, var in explist1 do block end

 

Пример:

 

local heroes = GetPlayerHeroes(PLAYER_1);

for i, hero in heroes do

if GetTownHero('Bobruisk') ~= hero then

print(i, hero);

end

end

 

На каждой итерации цикла в index имеем счетчик (кстати, не обязательно числовой), в var - очередное значение из таблицы. Цикл крутится по разу для каждого элемента таблицы

Внутри циклов Вы можете пользовать оператор break и return. Занятный момент - данные операторы должны быть последними в блоке. Если хочется обойти это условие (обычно такая необходимость возникает при при отладке) необходимо пользовать конструкции do break end и do return end.

Замечу - оператора goto как и понятия меток в языке нет. "И это правильно, товарищи".

Операции сравнения ничем не отличаются от таковых в прочих языках. Разве что не совсем обычной формой записи операции "не равно".

 

== ~= < > <= >=

 

Результатом всегда является true или false. Если тип сравниваемых переменных не совпадает, то результатом сравнения всегда будет false. Т.е. блок внутри данного оператора

 

If("0"==0) then

end

 

не выполнится никогда.

Логические операторы -

 

and or not

 

Операторы как операторы. Ничего особенного. Единственное, что хотелось бы заметить - не следует полагаться на порядок вычисления операторов. Ставьте скобки. И вам уверенности больше, и другим понятнее. Иначе в один прекрасный момент Вы можете обнаружить, что конструкция

 

if objectname == "HIREPERS1" or objectname == "HIREPERS2" and NextBlockOrderID == 2 or NextBlockOrderID == 3 then

end

 

на самом деле вовсе не эквивалентна конструкции

 

if (objectname == "HIREPERS1" or objectname == "HIREPERS2") and (NextBlockOrderID == 2 or NextBlockOrderID == 3) then

end

 

что, конечно, не согласуется с мануалом. Но нам же ехать, а не шашечки, не правда ли?

 

Функции определяются так:

 

function funcname([parlist1]) block end

 

Функции могут возвращать несколько значений. Например, допустима следующая конструкция:

 

function f()

return 1,2,3

end

 

a,b,c = f()

 

Небольшая особенность, связанная с функциями. Лучше всего ее иллюстрирует следующий пример. Рассмотрим два фрагмента:

 

function GetCreatures(side)

return(GetUnits(side, CREATURE))

end

 

и

 

function GetCreatures(side)

local temp = GetUnits(side, CREATURE)

return(temp)

end

 

Казалось бы, функции идентичны, однако результатом работы первой всегда будет nil. Это ошибка (или особенность, кому как) реализации интерпретатора. Посему общее правило – функция должна возвращать результат через локальную переменную.

На этом с описанием особенностей языка закончу. Если написанного для Вас недостаточно - читайте оригинальный мануал.

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: