MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Hey, everyone... just popping in for my annual checkup on how this utility is progressing. Last I left off, there were a couple of key features that I was interested in that had yet to see implementation:
-Ability to change effects of potions (i.e. make "Restore MP" a compound potion instead of a basic one).
-Ability to edit bonuses on artifacts/relics
-Ability to edit skill levels/schools of spells (i.e. make Charm a basic-level mind spell and make Stun a Mind spell instead of Earth)
Have either of these become possible over the last year or so?
-Ability to change effects of potions (i.e. make "Restore MP" a compound potion instead of a basic one).
-Ability to edit bonuses on artifacts/relics
-Ability to edit skill levels/schools of spells (i.e. make Charm a basic-level mind spell and make Stun a Mind spell instead of Earth)
Have either of these become possible over the last year or so?
Last edited by BTB on 02 Nov 2017, 13:35, edited 1 time in total.
"You don't have to be a vampire to die like one... bitch." -Simon Belmont
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Yes! I was waiting for something like that very long time.Rodril wrote:Hello.
Have new tool, seems funny and useful - simple interface manager for MM8. It allows to create custom buttons, icons, texts and animations over or behind default interface, it does not allow to change existing elements, but, in general, by abusing events.Action we can just disable default interface and make new on top of it.
Here is link with description and example in it: https://www.dropbox.com/sh/rnikhe3nhbfc ... nUOfa?dl=0
Example is a simple reconstruction of MM67 alike NPC followers system in MM8. Readme does not describe it as well as it should, but i think script is pretty clear.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Rodril, what if I want to create text in game interface (when you look through the eyes of the party) that uses variable (like vars.a)? When the script for that is typed in "function events.GameInitialized2()" game can't find what thing is my variable, because it doesn't exist yet.
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
When you execute CustomUI.CreateText function, it returns table of text's settings, it have field "Text", change it when vars.a changes. Attaching vars.a to text during it's creation won't work, text will stay static.
Use this script as example, put it into "...Scripts\General" folder, some comments are inside:
https://www.dropbox.com/s/5uke44zfejpot ... e.lua?dl=0
Link to last version of Interface manager:
https://www.dropbox.com/s/iis6dvilbq9na ... r.lua?dl=0
Use this script as example, put it into "...Scripts\General" folder, some comments are inside:
https://www.dropbox.com/s/5uke44zfejpot ... e.lua?dl=0
Link to last version of Interface manager:
https://www.dropbox.com/s/iis6dvilbq9na ... r.lua?dl=0
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Thanks for fast reply! I will check this tomorrow.
I wonder if there is posibility to make a graphical interpretation of some data, like the line of character HP (some numerical fraction). Your script allows that? I guess that will be possible with many graphics for every level of data, but I wonder about something more automatic, like one graphic showed in a part compared to the size of some numeric fraction. Some advice?
Edit: My script works now. Thanks!
Ps. How to make cyan color on icons transparent?
Ps2. I have a problem with polish characters ("ś" "ć" etc.) in StatusText used in function in Action of Icon (instead of them are showed some weird characters). StatusText used separately shows polish characters corectly. This is my code:
Anyway, is there any way to make showing some StatusText just by moving mouse over icon (without clicking)?
I wonder if there is posibility to make a graphical interpretation of some data, like the line of character HP (some numerical fraction). Your script allows that? I guess that will be possible with many graphics for every level of data, but I wonder about something more automatic, like one graphic showed in a part compared to the size of some numeric fraction. Some advice?
Edit: My script works now. Thanks!
Ps. How to make cyan color on icons transparent?
Ps2. I have a problem with polish characters ("ś" "ć" etc.) in StatusText used in function in Action of Icon (instead of them are showed some weird characters). StatusText used separately shows polish characters corectly. This is my code:
Code: Select all
Action = function() Game.ShowStatusText("Kościół Słońca") end
Last edited by J. M. Sower on 22 Nov 2017, 19:42, edited 10 times in total.
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
It should be transparent by default, if .bmp encoded mm8-alike way. I don't know how exactly. Disabling color space information and using R5 G6 B5 color coding, during export from Gimp helps sometimes. If not, add "Masked = true" property into CustomUI.Create*Icon/Button* function, then color of upper left pixel of image will be used for transparent mask.J. M. Sower wrote:Ps. How to make cyan color on icons transparent?
Definetly it won't be easy, script does not provide any straight-forward instruments for that kind of tasks, but most of UI element fields can be binded to something and animated that way, like X, Y, source icon etc, use dump function in debug console to see them all (like "test = CustomUI.CreateIcon{*params*} ... dump(test)"). Also CustomUI.CreateIcon{*params*} allows to make animations, pass table of icon names to it's "Icon" property, and function which will return icon's index to "Animator" property. Default animator function is CustomUI.StdAnimator, which returns indexes in cycle. Each animator function gets "t" param - settings of current icon.J. M. Sower wrote:I wonder if there is posibility to make a graphical interpretation of some data, like the line of character HP (some numerical fraction). Your script allows that? I guess that will be possible with many graphics for every level of data, but I wonder about something more automatic, like one graphic showed in a part compared to the size of some numeric fraction. Some advice?
Not for now, i'll add it.J. M. Sower wrote:Anyway, is there any way to make showing some StatusText just by moving mouse over icon (without clicking)?
Maybe something related to font, don't know how to solve it, try to set "Layer" property of icon to 3 or 4 just to see if it can affect it anyhow.J. M. Sower wrote: I have a problem with polish characters ("ś" "ć" etc.)
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Interface manager supports mouse over action for icons and buttons now, link is same. Add *MouseOverAction = function() ... end* property during creation of new element. Function will fire once each time when cursor enters into bounding box of element.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Great! But did I understand good that it will show StatusText once for limited time during mouse entering over element? It is good, but I wonder about activating Status tex for all the time when mouse is over object. Anyway, thanks!Rodril wrote:Interface manager supports mouse over action for icons and buttons now, link is same. Add *MouseOverAction = function() ... end* property during creation of new element. Function will fire once each time when cursor enters into bounding box of element.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
This problem occurs also for CreateText with polish characters. But this problem can be fixed just by replacing "Text" value from the level of game. Replacing "Action" value to function with ShowStatusText from the level of game gives nothing. That problem exist on every layer.Rodril wrote:Maybe something related to font, don't know how to solve it, try to set "Layer" property of icon to 3 or 4 just to see if it can affect it anyhow.J. M. Sower wrote: I have a problem with polish characters ("ś" "ć" etc.)
Edit: Hmm... it works when I wrote eg. "Game.ShowStatusText(evt.str[34])" and this string have polish characters.
Edit2: I used this script in "Globals" and all "CreateText" have assigned this strings. Maybe this is not professional but it works.
Code: Select all
function events.AfterLoadMap()
evt.str[300] = "Kościół Słońca"
evt.str[301] = "Kościół Księżyca"
evt.str[302] = "Królestwo Karigoru"
evt.str[303] = "Czerwone Krasnoludy"
evt.str[304] = "Nekromanci"
evt.str[305] = "Syreny"
evt.str[306] = "Piraci"
evt.str[307] = "Elfickie Królestwo Vori"
evt.str[308] = "Państwo Tatalii"
end
Last edited by J. M. Sower on 02 Dec 2017, 15:22, edited 3 times in total.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Rodril, I copied "RemoveNPCTablesLimits.lua" from your Merge mod and after start of new game I have this error message. What is wrong? I realy need bigger count of npc topics. :/
Code: Select all
...II moded\Scripts\Structs\After\LocalizationAndQuests.lua:295: array index (998) out of bounds [1, 753]
stack traceback:
[C]: in function 'error'
Scripts/Core/RSMem.lua:1342: in function '__index'
...II moded\Scripts\Structs\After\LocalizationAndQuests.lua:295: in function 'UpdateCurrentQuests'
...II moded\Scripts\Structs\After\LocalizationAndQuests.lua:312: in function 'v'
Scripts/Core/EventsList.lua:68: in function <Scripts/Core/EventsList.lua:63>
arguments of '__index':
t = (table: 0x07d6c1a8)
a = 998
v = nil
local variables of '__index':
aorig = 998
a1 = (table: 0x02a9c750)
n = 753
upvalues of '__index':
ptr = nil
u4 = (table: 0x02a78fd0)
GetPtr = (function: 0x02a77240)
obj = (table: 0x031a6368)
o = 47040976
assertnum = (function: 0x02c3c7e0)
error = (function: 0x02aa0d70)
type = (function: builtin#3)
SetLen = nil
low = 1
GetLen = (function: 0x037c0480)
lenP = nil
lenA = nil
count = 753
size = 8
_index = nil
_newindex = nil
tonumber = (function: builtin#17)
beyondLen = nil
f = (function: 0x02c2e8a8)
format = (function: builtin#91)
sOutOfBounds = "array index (%s) out of bounds [%s, %s]"
tostring = (function: builtin#18)
tostring2 = (function: 0x02c3c1b8)
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
During limits removal script sets actual number of lines in npctopic.txt as new limit, fill 753 - 1001 lines in npctopic.txt with "placeholders" for future usage. MMExtension uses 995, 996, 997, 998, 999 and 1000 topics for it's purposes, those topics should exist and be reserved.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Rodril, I wrote small addon to your InterfaceManager script, that allows easy creating some bars (you can create even new HP bar or something). But I can't create possibility to changing values from game level. :/ Maybe you can fix that. This is the code. Just add it into your script.
And here you have my code for some bar for example (but it usues my own graphics so you must use other for testing).
Ps. And screenshot. That bars on left are created with my "CustomUI.CreateBar"
Code: Select all
local function CreateBar(p)
if #p.icons > 0 then
if p.baseFrame == true then
local range = p.var_max / (#p.icons - 1)
local move = range / 2
p.cricon = {}
for i=1, #p.icons, 1 do
local m = (i * range ) - move
local w = m - range
local cond1, cond2
if m < p.var_max then
cond1 = function() return p.value_var() < m end
else
cond1 = function() return p.value_var() <= p.var_max end
end
if w > p.var_min then
cond2 = function() return p.value_var() >= w end
else
cond2 = function() return p.value_var() >= p.var_min end
end
p.cricon[i] = CustomUI.CreateIcon{
Icon = p.icons[i],
X = p.x,
Y = p.y,
Condition = function() return p.general_condition() and (cond1() and cond2()) end,
Layer = p.layer,
Screen = p.screen}
end
else
local range = p.var_max / #p.icons
for i=1, #p.icons, 1 do
local m = i * range
local w = m - range
local cond1, cond2
if m < p.var_max then
cond1 = function() return p.value_var() < m end
else
cond1 = function() return p.value_var() <= p.var_max end
end
if w > p.var_min then
cond2 = function() return p.value_var() >= w end
else
cond2 = function() return p.value_var() >= p.var_min end
end
p.cricon[i] = CustomUI.CreateIcon{
Icon = p.icons[i],
X = p.x,
Y = p.y,
Condition = function() return p.general_condition() and (cond1() and cond2()) end,
Layer = p.layer,
Screen = p.screen}
end
end
end
end
CustomUI.CreateBar = CreateBar
Code: Select all
repA = CustomUI.CreateBar{
icons = {"rep00", "rep01", "rep02", "rep03", "rep04", "rep05", "rep06", "rep07", "rep08", "rep09", "rep10"},
baseFrame = true, -- with icon when bar is empty. Type "false" for simpler bar
x = 0,
y = 0,
layer = 1,
screen = 0,
general_condition = function() return RepPanelOpen end, -- main condition for all frames
value_var = function() return repa end, -- variable used
var_min = 0, -- maximal variable value
var_max = 100, -- minimal variable value
}
Last edited by J. M. Sower on 18 Feb 2018, 18:19, edited 7 times in total.
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Hello. In your example diffrent variables are used: "repA" at base and "repa" in condition, these are case sensitive, i've changed it, and it allows to control bar's status. Also "repA = CustomUI.CreateBar{..." construction is not necessary, because this function does not return anything, and if "repA" had any value, it will be changed to nil. Maybe I've understood wrong, could you control your bar by "repA" or was it just unconvenient?
I would suggest to let function "CreateBar" return table with controls: it should consist of all created icons, so we can get access directly to them, also "repA" variable should be field of this table, so it won't cross names of other variables. I will write example later, if you want, need a bit more time.
I would suggest to let function "CreateBar" return table with controls: it should consist of all created icons, so we can get access directly to them, also "repA" variable should be field of this table, so it won't cross names of other variables. I will write example later, if you want, need a bit more time.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
So I will be waiting for your proposition.Rodril wrote:Hello. In your example diffrent variables are used: "repA" at base and "repa" in condition, these are case sensitive, i've changed it, and it allows to control bar's status. Also "repA = CustomUI.CreateBar{..." construction is not necessary, because this function does not return anything, and if "repA" had any value, it will be changed to nil. Maybe I've understood wrong, could you control your bar by "repA" or was it just unconvenient?
I would suggest to let function "CreateBar" return table with controls: it should consist of all created icons, so we can get access directly to them, also "repA" variable should be field of this table, so it won't cross names of other variables. I will write example later, if you want, need a bit more time.
Edit: "repA" is just the variable created to assign to it the data of that bar. "repa" is my global variable that I created to use the value of my "vars.repa" variable in interface (vars.[...] cannot be used straight in interface so I write something like this: "function events.FGInterfaceUpd() repa = vars.repa end"). So:
repA - data of the bar
repa - value used in the bar, dynamically copied from vars.repa
Last edited by J. M. Sower on 20 Feb 2018, 18:45, edited 2 times in total.
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
I think it should look like this: https://www.dropbox.com/s/odrwuy2kft29w ... s.lua?dl=0
Function returns table with settings, which we can tweak any time without declaring extra global variables.
Function returns table with settings, which we can tweak any time without declaring extra global variables.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Hmm... something is wrong with RepBar:GetState(). It always returns "false".
Edit: There is another problem but I can't find the cause. With your script bar disappears if the "Value" equals "0". I think maybe good solution would be "cond1" equal always "true" for last icon, and "cond2" always "true" for first.
It should look like that:
Edit2: I found another defect. Something is wrong with automatic determining icons width and height in CustomUI.CreateButton (my button had too high and too narrow field of mouse over). So at this time I have added possibility to set "Width" and "Height" manually. Here is my newest version of "InterfaceManager.lua":
Edit3: ... and another defect. ;P If you will set some picture from EnglishD.lod as IconMouseOver for some CustomUI.CreateButton the button will disappear when mouse will be on it.
Also, I have a question. Is there any way to return some text from Global.txt?
Edit: There is another problem but I can't find the cause. With your script bar disappears if the "Value" equals "0". I think maybe good solution would be "cond1" equal always "true" for last icon, and "cond2" always "true" for first.
It should look like that:
Code: Select all
ocal function CreateBar(p)
if #p.icons == 0 then
return
end
local Settings = {
Icons = {},
Toggle = ToggleBar, -- Settings:Toggle(State) -- use : instead of . for these functions.
Remove = RemoveBar,
GetState = GetBarState,
SetCoords = SetBarCoords,
Condition = p.general_condition,
Value = p.Value or 0, -- Current bar's value.
VMax = p.var_max,
VMin = p.var_min,
Key = p.Key or "Bar_" .. p.X .. "_" .. p.Y
}
local range = p.var_max / (#p.icons - (p.baseFrame and 1 or 0))
local move = p.baseFrame and range / 2 or 0
for i = 1, #p.icons, 1 do
local m = (i * range ) - move
local w = m - range
local cond1, cond2, mCond
if i == 1 then
cond1 = function() return Settings.Value < m end
cond2 = function() return true end
elseif i == #p.icons then
cond1 = function() return true end
cond2 = function() return Settings.Value >= w end
else
if m < Settings.VMax then
cond1 = function() return Settings.Value < m end
else
cond1 = function() return Settings.Value <= Settings.VMax end
end
if w > Settings.VMin then
cond2 = function() return Settings.Value >= w end
else
cond2 = function() return Settings.Value >= Settings.VMin end
end
end
if Settings.Condition then
mCond = function() return Settings.Condition() and (cond1() and cond2()) end
else
mCond = function() return cond1() and cond2() end
end
Settings.Icons[i] = CustomUI.CreateIcon{
Active = true,
Icon = p.icons[i],
X = p.X,
Y = p.Y,
Condition = mCond,
Layer = p.layer,
Screen = p.screen}
end
Bars[Settings.Key] = Settings
return Settings -- use this structure to operate bar.
end
CustomUI.CreateBar = CreateBar
-- RepBar = CustomUI.CreateBar{...}
-- RepBar:GetState()
-- RepBar:SetCoords(X, Y)
-- RepBar:Toggle(true)
-- RepBar.Value = 50
-- RepBar:Remove()
-- for dynamic update:
--~ function events.FGInterfaceUpd()
--~ if RepBar:GetState() then -- better to keep this condition if source for bar stored in vars.
--~ RepBar.Value = Party.Reputation -- write here source of Bar's value.
--~ end
--~ end
-- Inspect possibilities to update bar during special events, for example, when player opens bar window, or only when specific value updates.
Code: Select all
---- Base functions
CustomUI = {}
local Params = mem.StaticAlloc(80)
local PicStructPtr = Params
local ImageNamePtr = Params + 8
local PicX = Params + 16
local PicY = Params + 24
local Font = Params + 32
local Text = Params + 36
local LShift = Params + 40
local TColor = Params + 44
local TextParams = Params + 48
--TextX
--TextY
--TextWidth
--TextHeight
--TextUnk1
--TextUnk2
local LoadIconAs = mem.asmproc([[
push 0
push 0
push 2
push dword[ds:]] .. ImageNamePtr .. [[]; ImageNamePtr
mov ecx, 0x70d3e8; Icons.LOD path
call absolute 0x410d70
lea eax, dword [ds:eax+eax*8]
lea eax, dword [ds:eax*8+0x70d624]
retn]]) -- returns pointer to icon struct.
local SetMaskAs = mem.asmproc([[
mov eax, dword [ds:]] .. PicStructPtr .. [[];
movzx edx, word [ds:eax+0x18]
movzx ecx, word [ds:eax+0x1a]
imul ecx, edx
mov eax, dword [ds:eax+0x30]
movzx edx, byte [ds:eax]
test edx, edx
je @end
@rep:
cmp byte [ds:eax], dl
jnz @next
mov byte [ds:eax], 0
@next:
inc eax
dec ecx
jnz @rep
@end:
retn
]]) -- replacing mask (upper left pixel) color with 00.
local UnloadIconAs = mem.asmproc([[
mov ecx, dword[ds:]] .. PicStructPtr .. [[];
call absolute 0x410a10
retn]]) -- removing loaded icon from memory and clearing it's appearance.
local ShowIconAs
ShowIconAs = mem.asmproc([[
mov eax, dword [ds:]] .. PicStructPtr .. [[];
cmp byte [ds:eax+0xe], 1
jnz @Std
push 0
@Std:
push dword[ds:]] .. PicStructPtr .. [[];
push dword[ds:]] .. PicY .. [[];
push dword[ds:]] .. PicX .. [[];
mov ecx, 0xec1980
je @trn
call absolute 0x4a3cd5
jmp @end
@trn:
call absolute 0x4a419b
@end:
retn]])
local ShowTextAs = mem.asmproc([[
pushfd
pushad
mov ecx, ]] .. TextParams .. [[;
xor esi, esi
mov eax, dword [ds:ecx-0xC];
mov edx, dword [ds:ecx-0x10]; Font
push dword [ds:ecx-0x8]; Shift between lines
push eax; String
push dword [ds:ecx-0x4]; Color
push dword [ds:ecx-0x14]; Y offset (does not affect box)
push dword [ds:ecx-0x18]; X offset (does not affect box)
call absolute 0x44aae3
popad
popfd
retn]])
local function LoadIcon(Icon, Masked)
local IconPtr
if type(Icon) == "string" then
mem.u4[ImageNamePtr] = mem.topointer(Icon)
elseif type(Icon) == "number" then
mem.u4[ImageNamePtr] = Icon
else
return false
end
IconPtr = mem.call(LoadIconAs)
if Masked then
mem.u4[PicStructPtr] = IconPtr
mem.call(SetMaskAs)
mem.u1[IconPtr+0xE] = 1
end
return IconPtr
end
CustomUI.LoadIcon = LoadIcon
local function UnloadIcon(IconPtr)
if IconPtr and type(IconPtr) == "number" then
mem.u4[PicStructPtr] = IconPtr
return mem.call(UnloadIconAs)
end
return false
end
CustomUI.UnloadIcon = UnloadIcon
local function ShowIcon(Icon, X, Y)
local IconPtr
if type(Icon) == "string" then
IconPtr = LoadIcon(Icon) -- in case icon is already in memory, function won't load it again, but return pointer to existing.
elseif type(Icon) == "number" then
IconPtr = Icon
else
return false
end
mem.u4[PicStructPtr] = IconPtr
mem.i4[PicX] = X or 0
mem.i4[PicY] = Y or 0
mem.call(ShowIconAs)
end
CustomUI.ShowIcon = ShowIcon
local function ShowText(Str, Fnt, X, Y, Shift, R, G, B, BoxWt, BoxHt, Xof, Yof)
if not Str then
return false
end
if not Fnt or Fnt == 0 then
Fnt = Game.Arrus_fnt
end
mem.u4[Text] = mem.topointer(Str)
mem.u4[Font] = Fnt
mem.i4[TextParams] = X or 0
mem.i4[TextParams+0x4] = Y or 0
mem.i4[TextParams+0x8] = BoxWt or 0
mem.i4[TextParams+0xc] = BoxHt or 0
mem.i4[PicX] = Xof or 0
mem.i4[PicY] = Yof or 0
mem.i4[LShift] = Shift or 3
mem.i1[TColor] = B*16 + B
mem.i1[TColor+1] = G + R*16
mem.call(ShowTextAs)
end
CustomUI.ShowText = ShowText
local function MouseInBox(X,Y,W,H)
return Mouse.X > X and Mouse.X < X + W and Mouse.Y > Y and Mouse.Y < Y + H
end
CustomUI.MouseInBox = MouseInBox
local function MouseInCircle(X,Y,R,Offset)
if Offset then
return R > math.sqrt((X+Offset-Mouse.X)^2 + (Y+Offset-Mouse.Y)^2)
else
return R > math.sqrt((X-Mouse.X)^2 + (Y-Mouse.Y)^2)
end
end
CustomUI.MouseInCircle = MouseInCircle
-- "t" is animated icon (CreateIcon{})
local function StdAnimator(t)
local CurT = timeGetTime()
t.CurFrame = math.floor((CurT - t.StartTime)/t.Period + 1)
if t.FramesCount < t.CurFrame then
t.StartTime = CurT
t.CurFrame = 1
end
return t.CurFrame
end
CustomUI.StdAnimator = StdAnimator
---- Elements
const.Screens.AdventurersInn = 29
const.Screens.Inventory2 = 15
const.Screens.SelectTarget = 20
const.Screens.SelectTarget2 = 23
local ActiveElements = {}
for k,v in pairs(const.Screens) do
ActiveElements[v] = {}
ActiveElements[v].Texts = {[0] = {}, {}, {}, {}}
ActiveElements[v].Icons = {[0] = {}, {}, {}, {}}
ActiveElements[v].Buttons = {[0] = {}, {}, {}, {}}
end
CustomUI.ActiveElements = ActiveElements
--[[ t - structure of settings:
-- IconUp - string - item name form icons.lod
-- IconDown - string - item name form icons.lod
-- IconMouseOver - string - item name form icons.lod
-- Action - function
-- Condition - function, which returns true if button can be shown
-- X - number - upper left corner of image
-- Y - number - upper left corner of image
-- Layer - number (0 - front, 1 - middle, 2 - back, 3 - background)
-- Screen - const.Screens or table
-- IsEllipse - boolean - shape of button
-- Active - boolean - true if button must appear right now.]]
local function CreateButton(t)
local MainIcon = t.IconUp or t.IconMouseOver or t.IconDown
if not MainIcon then return false end
t.X = t.X or 0
t.Y = t.Y or 0
local Layer = t.Layer or 0
local Key = MainIcon .. t.X .. t.Y .. "_" .. Layer
local Chk = MouseInBox
local Active = t.Active
local IUpPtr, IDwPtr, IMoPtr
local Width = t.Width
local Height = t.Height
if Active == nil then Active = true end
IUpPtr = LoadIcon(t.IconUp or MainIcon, t.Masked)
IDwPtr = LoadIcon(t.IconDown or MainIcon, t.Masked)
if t.IconMouseOver then
IMoPtr = LoadIcon(t.IconMouseOver or MainIcon, t.Masked)
end
if ( Width == nil ) and ( Height == nil ) then
Width, Height = mem.u2[IUpPtr+0x18], mem.u2[IUpPtr+0x1a]
end
if t.IsEllipse then
local R = Width/2
Width = R
Height = R
Chk = MouseInCircle
end
local Settings = {IUpPtr = IUpPtr, IDwPtr = IDwPtr, IMoPtr = IMoPtr,
IUpSrc = t.IconUp or MainIcon, IDwSrc = t.IconDown or MainIcon, IMoSrc = t.IconMouseOver,
Masked = t.Masked,
Act = t.Action, Cond = t.Condition,
X = t.X, Y = t.Y, Wt = Width, Ht = Height,
Layer = Layer,
Screen = t.Screen or 0,
Active = Active,
Pressed = false,
MouseOver = false,
MOAct = t.MouseOverAction,
Key = Key,
Chk = Chk,
Type = "Button"}
if type(t.Screen) == "table" then
for k,v in pairs(t.Screen) do
ActiveElements[v].Buttons[Layer][Key] = Settings
end
else
ActiveElements[t.Screen or 0].Buttons[Layer][Key] = Settings
end
return Settings
end
CustomUI.CreateButton = CreateButton
--[[ Text - string
-- Font - Game. ... _fnt
-- X - number
-- Y - number
-- Unnecessary:
-- Layer - 0 - front, 1 - middle, 2 - back, 3 - behind main interface
-- Screen - const.Screens
-- Condition - function
-- ColorStd - number - def == 0 - white
-- ColorMouseOver - number - def == 0 - white
-- Action - function - on click
-- Active - boolean]]
local function CreateText(t)
if type(t) ~= "table" or not t.Text then
return false
end
local LinesCount = table.maxn(string.split(t.Text, "\n"))
local Active = t.Active
if Active == nil then Active = true end
local Settings = {Text = t.Text, X = t.X or 0, Y = t.Y or 0,
Wt = t.Width or math.floor(string.len(t.Text)*8/LinesCount),
Ht = t.Height or 10*LinesCount,
Layer = t.Layer or 0, Cond = t.Condition,
CStd = t.ColorStd or 0, CMo = t.ColorMouseOver or 0, Act = t.Action,
HvAct = type(t.Action) == "function",
Pressed = false,
Font = t.Font,
Shift = t.Shift or 3,
R = t.R or 0,
G = t.G or 0,
B = t.B or 0,
Rm = t.Rm or 15,
Gm = t.Gm or 15,
Bm = t.Bm or 0,
Active = Active,
Screen = t.Screen or 0,
Xof = 0,
Yof = 0,
Type = "Text"}
local Layer = Settings.Layer
local Key = string.sub(Settings.Text, 1, 3) .. Settings.X .. Settings.Y .. "_" .. Layer
Settings.Key = Key
if type(t.Screen) == "table" then
for k,v in pairs(t.Screen) do
ActiveElements[v].Texts[Layer][Key] = Settings
end
else
ActiveElements[t.Screen or 0].Texts[Layer][Key] = Settings
end
return Settings
end
CustomUI.CreateText = CreateText
--[[ Icon - string (for static) or table of strings (for animated)
-- Condition- function
-- X - number
-- Y - number
-- Layer - number
-- Screen - const.Screens
-- Active - boolean
-- for animated:
-- Period - number - to define animation speed (in StdAnimator: higher - slower)
-- Animator - function(t) - which will return frame number, "t" is access to icon settings. Default - StdAnimator
--]]--
local function CreateIcon(t)
if not t.Icon then
return false
end
local Icon, MainIcon
local Settings = {}
local IsAnim = type(t.Icon) == "table"
local Width, Height
local Active = t.Active
if Active == nil then Active = true end
if IsAnim then
Icon = {}
MainIcon = t.Icon[1]
for i, v in ipairs(t.Icon) do
Icon[i] = {Fr = 0x70d624, Src = v} -- LoadIcon(v, t.Masked)
end
Settings.Period = t.Period or 25
Settings.FramesCount = table.maxn(t.Icon)
Settings.CurFrame = 1
Settings.StartTime = timeGetTime()
Settings.CF = t.Animator or StdAnimator
Width, Height = mem.u2[Icon[1].Fr+0x18], mem.u2[Icon[1].Fr+0x1a]
else
Icon = LoadIcon(t.Icon, t.Masked)
MainIcon = t.Icon
Width, Height = mem.u2[Icon+0x18], mem.u2[Icon+0x1a]
end
Settings.Icon = Icon
Settings.Masked = t.Masked
Settings.Cond = t.Condition
Settings.X = t.X or 0
Settings.Y = t.Y or 0
Settings.Wt = Width
Settings.Ht = Height
Settings.Layer = t.Layer or 0
Settings.MainIcon = MainIcon
Settings.Active = Active
Settings.IsAnim = IsAnim
Settings.Type = "Icon"
Settings.MouseOver = false
Settings.MOAct = t.MouseOverAction
local Layer = Settings.Layer
local Key = MainIcon .. Settings.X .. Settings.Y .. "_" .. Layer
Settings.Key = Key
if type(t.Screen) == "table" then
for k,v in pairs(t.Screen) do
ActiveElements[v].Icons[Layer][Key] = Settings
end
else
ActiveElements[t.Screen or 0].Icons[Layer][Key] = Settings
end
return Settings
end
CustomUI.CreateIcon = CreateIcon
---- Make global table for all bars to be able to access them any time.
local Bars = {}
CustomUI.ActiveElements.Bars = Bars -- ActiveElements also consisit of "Icons", "Buttons", "Texts" tables.
---- Make service functions for bars.
-- Returns true if Bar is visible now
local function GetBarState(t)
for k,v in pairs(t.Icons) do
return v.Active and v.Cond() and Game.CurrentScreen == v.Screen
end
end
-- Switches Active field of all icons or sets it to "State" param if it is defined.
local function ToggleBar(t, State)
local CurState = State ~= nil and State or not t:GetState()
for k,v in pairs(t.Icons) do
v.Active = CurState
end
end
-- Removes bar, unloads all icons.
local function RemoveBar(t)
for k,v in pairs(t.Icons) do
CustomUI.RemoveElement(v)
end
Bars[t.Key] = nil
collectgarbage("collect")
end
-- Move bar to defined coords.
local function SetBarCoords(t, X, Y)
for k,v in pairs(t.Icons) do
v.X = X or v.X
v.Y = Y or v.Y
end
end
----
local function CreateBar(p)
if #p.icons == 0 then
return
end
local Settings = {
Icons = {},
Toggle = ToggleBar, -- Settings:Toggle(State) -- use : instead of . for these functions.
Remove = RemoveBar,
GetState = GetBarState,
SetCoords = SetBarCoords,
Condition = p.general_condition,
Value = p.Value or 0, -- Current bar's value.
VMax = p.var_max,
VMin = p.var_min,
Key = p.Key or "Bar_" .. p.X .. "_" .. p.Y
}
local range = p.var_max / (#p.icons - (p.baseFrame and 1 or 0))
local move = p.baseFrame and range / 2 or 0
for i = 1, #p.icons, 1 do
local m = (i * range ) - move
local w = m - range
local cond1, cond2, mCond
if i == 1 then
cond1 = function() return Settings.Value < m end
cond2 = function() return true end
elseif i == #p.icons then
cond1 = function() return true end
cond2 = function() return Settings.Value >= w end
else
if m < Settings.VMax then
cond1 = function() return Settings.Value < m end
else
cond1 = function() return Settings.Value <= Settings.VMax end
end
if w > Settings.VMin then
cond2 = function() return Settings.Value >= w end
else
cond2 = function() return Settings.Value >= Settings.VMin end
end
end
if Settings.Condition then
mCond = function() return Settings.Condition() and (cond1() and cond2()) end
else
mCond = function() return cond1() and cond2() end
end
Settings.Icons[i] = CustomUI.CreateIcon{
Active = true,
Icon = p.icons[i],
X = p.X,
Y = p.Y,
Condition = mCond,
Layer = p.layer,
Screen = p.screen}
end
Bars[Settings.Key] = Settings
return Settings -- use this structure to operate bar.
end
CustomUI.CreateBar = CreateBar
-- RepBar = CustomUI.CreateBar{...}
-- RepBar:GetState()
-- RepBar:SetCoords(X, Y)
-- RepBar:Toggle(true)
-- RepBar.Value = 50
-- RepBar:Remove()
-- for dynamic update:
--~ function events.FGInterfaceUpd()
--~ if RepBar:GetState() then -- better to keep this condition if source for bar stored in vars.
--~ RepBar.Value = Party.Reputation -- write here source of Bar's value.
--~ end
--~ end
-- Inspect possibilities to update bar during special events, for example, when player opens bar window, or only when specific value updates.
local function RemoveElement(t)
if type(t) ~= "table" then
return false
end
t.Active = false
if t.Type == "Button" then
UnloadIcon(t.IUpPtr)
UnloadIcon(t.IDwPtr)
UnloadIcon(t.IMoPtr)
elseif t.Type == "Icon" then
if t.Anim then
for Ii, Iv in ipairs(v.Icon) do
UnloadIcon(Iv.Fr)
end
else
UnloadIcon(t.Icon)
end
end
t = nil
collectgarbage("collect")
return true
end
CustomUI.RemoveElement = RemoveElement
local function ProcessButtons(la)
local T = ActiveElements[Game.CurrentScreen].Buttons[la]
for k, v in pairs(T) do
if v.Active and (not v.Cond or v.Cond()) then
if v.IUpSrc ~= mem.string(v.IUpPtr) then
v.IUpPtr = LoadIcon(v.IUpSrc, v.Masked)
end
if v.IDwSrc ~= mem.string(v.IDwPtr) then
v.IDwPtr = LoadIcon(v.IDwSrc, v.Masked)
end
if v.ImoSrc and v.IMoSrc ~= mem.string(v.IMoPtr) then
v.IMoPtr = LoadIcon(v.IMoSrc, v.Masked)
end
if v.Chk(v.X,v.Y,v.Wt,v.Ht) then
if Keys.IsPressed(const.Keys.LBUTTON) then
ShowIcon(v.IDwPtr, v.X, v.Y)
v.Pressed = true
else
ShowIcon(v.IMoPtr or v.IUpPtr, v.X, v.Y)
if not v.MouseOver and v.MOAct then
v.MOAct()
end
if v.Pressed then
v.Act()
end
v.Pressed = false
end
v.MouseOver = true
else
ShowIcon(v.IUpPtr, v.X, v.Y)
v.Pressed = false
v.MouseOver = false
end
end
end
end
local function ProcessTexts(la)
local T = ActiveElements[Game.CurrentScreen].Texts[la]
for k, v in pairs(T) do
if v.Active and (not v.Cond or v.Cond()) then
if v.HvAct then
if MouseInBox(v.X,v.Y,v.Wt,v.Ht) then
ShowText(v.Text, v.Font, v.X, v.Y, v.Shift, v.Rm, v.Gm, v.Bm, v.Wt, v.Ht, v.Xof, v.Yof)
if Keys.IsPressed(const.Keys.LBUTTON) and not v.Pressed then
v.Act()
v.Pressed = true
end
else
ShowText(v.Text, v.Font, v.X, v.Y, v.Shift, v.R, v.G, v.B, v.Wt, v.Ht, v.Xof, v.Yof)
v.Pressed = false
end
else
ShowText(v.Text, v.Font, v.X, v.Y, v.Shift, v.R, v.G, v.B, v.Wt, v.Ht, v.Xof, v.Yof)
end
end
end
end
local function ProcessIcons(la)
local T = ActiveElements[Game.CurrentScreen].Icons[la]
for k,v in pairs(T) do
if v.Active and (not v.Cond or v.Cond()) then
if v.IsAnim then
local CurF = v.Icon[v:CF()] or v.Icon[1]
if CurF.Src ~= mem.string(CurF.Fr) then
CurF.Fr = LoadIcon(CurF.Src, v.Masked)
end
ShowIcon(CurF.Fr, v.X, v.Y)
else
if v.MainIcon ~= mem.string(v.Icon) then
v.Icon = LoadIcon(v.MainIcon, v.Masked)
end
ShowIcon(v.Icon, v.X, v.Y)
end
if v.MOAct then
if MouseInBox(v.X,v.Y,v.Wt,v.Ht) then
if not v.MouseOver then
v.MOAct()
end
v.MouseOver = true
else
v.MouseOver = false
end
end
end
end
end
---- Events
mem.autohook2(0x4d1d26, function()
events.cocall("BGInterfaceUpd")
ProcessIcons(3)
ProcessTexts(3)
ProcessButtons(3)
events.cocall("L2InterfaceUpd")
ProcessIcons(2)
ProcessTexts(2)
ProcessButtons(2)
end)
mem.autohook2(0x4a30a5, function(d)
if mem.u4[d.eax] > 0 then
ProcessIcons(1)
ProcessTexts(1)
ProcessButtons(1)
events.cocall("L1InterfaceUpd")
ProcessIcons(0)
ProcessTexts(0)
ProcessButtons(0)
events.cocall("FGInterfaceUpd")
end
end)
Also, I have a question. Is there any way to return some text from Global.txt?
Last edited by J. M. Sower on 25 Feb 2018, 12:56, edited 8 times in total.
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Ouch, there should be:J. M. Sower wrote:Hmm... something is wrong with RepBar:GetState(). It always returns "false".
return v.Active and v.Cond() and Game.CurrentScreen == t.Screen
instead of v.Screen.
I'll add other fixes too.
Script support only icons in icons.lod or *.icons.lod archives.J. M. Sower wrote:Edit3: ... and another defect. ;P If you will set some picture from EnglishD.lod as IconMouseOver for some CustomUI.CreateButton the button will disappear when mouse will be on it.
Yes, there is file "GlobalTxt.lua" inside "Scripts\Structs" folder in MM678 files, add it and you'll be able to use Game.GlobalTxt structure.J. M. Sower wrote:Also, I have a question. Is there any way to return some text from Global.txt?
Also I've finally separated new events from "MiscTweaks.lua" into "ExtraEvents.lua" in general folder, inspect this if you want, it does not require any additional scripts for work.
Last edited by Rodril on 04 Mar 2018, 17:57, edited 1 time in total.
- J. M. Sower
- Scout
- Posts: 188
- Joined: 25 Jan 2016
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
Thanks, but I have new questions. It is possible to add more lines into GlobalTxt than 750?
And I have a problem with new character dolls. I have dolls number 4 and 5, and they don't want to use pictures of armor with added "v5" and "v6" in the file name. Game always shows placeholder picture.
And I have a problem with new character dolls. I have dolls number 4 and 5, and they don't want to use pictures of armor with added "v5" and "v6" in the file name. Game always shows placeholder picture.
Last edited by J. M. Sower on 01 Apr 2018, 20:58, edited 1 time in total.
Re: MMExtension v2.1 + MMEditor v2.1 Level Editor [Apr 22, 2016]
No, limits of GlobalTxt have not been removed, why would you need it? I have not noticed any special functionality of GlobalTxt items, new lines can be stored in NPCText with same effect, or right in scripts, i think.J. M. Sower wrote:Thanks, but I have new questions. It is possible to add more lines into GlobalTxt than 750?
There is outdated part of script at lines 1252 - 1257, it was supposed to shrink amount of required memory for new dolls by excluding from item-handle mechanics ones that can not use armors at all (like dragons). I have not played with new dolls a lot, can not say if excluding armorless dolls have any sense now.J. M. Sower wrote:And I have a problem with new character dolls. I have dolls number 4 and 5, and they don't want to use pictures of armor with added "v5" and "v6" in the file name. Game always shows placeholder picture.
To make it work: comment or remove 1252-1257 lines of code in RemoveItemsLimits.lua (or download one from mm678 files), don't forget to add extra columns to "Complex items pictures.txt" (t4, t5).
Who is online
Users browsing this forum: No registered users and 13 guests