Tabletop Simulator

Tabletop Simulator

Gloomhaven - Fantasy Setup (Scripted UI)
Kraytul Mar 30, 2020 @ 12:26am
EDIT: Implemented! (Pull Request - Combat Pack)
EDIT: As of 04/06/2020 the mod includes improvements equivalent/superior to this combat pack.

I will leave the rest of this post and thread as it is in case anyone would like to reference, but consider these changes implemented.

Thank you GabrialM and Kijan for all your work on this amazing mod!!


Originally posted by kraytul:

EDIT: Instructions for replacement are now included as a readme in the drive. A fresh full-save with the changes is included as well.

Hello! There's no github or anything formal for pull requests, but I think that some changes I've made might be reasonably easily integrated and would be widely useful to players. Especially considering the newest changes to the initiative tracker.

I've been working on some quality of life improvements to the monster UI for my group since we recently moved our game online. We were tired of referencing back to the monster stat cards so I've created a new UI panel and assets to display (nearly) all of the base stat information at the monsters themselves.

Google drive with files & example images: https://drive.google.com/open?id=1ckwfqVk7ea5RZYVEPs63XyY7fFxhZIY_

User-level changes:

* Monster traits now reflected at the standee: movement (icon changes for flight), attack, range, targets, retaliate (range where applicable), shield, effects inflicted on attacks - curse, disarm, immobilize, wound, muddle, poison, push, pierce, stun, strengthen. Assets for these effects have been uploaded to steam cloud and were derived from the creator pack for the game posted online.

* The customization interface for max health, monster number, and elite vs non-elite can be found by clicking on the monster number button rather than the health bar.

* "Defeat" button now replaces health when monster has 0 health remaining

* Summon indicator now replaces the monster number and has 4 potential values.

Code notes:

* Scripts.75ab50.ttslua - only minor changes to code. Updates to HPBarOffset values to accommodate new combat stats bar

* CustomUIAssets.txt - new assets definitions for the CustomUIAssets array

* MonsterXML.xml - code-format XML file for changes

* MonsterLUA.ttslua - code-format ttslua file for changes

* MonsterXMLUI-JSON.txt-format - the monsterXML formatted for find/replace in the save.json file

* MonsterLuaScript-JSON-format - the monsterLUA formatted for find/replace in the save.json file

Some further notes on the code: the combat stats are parsed directly from the existing combat stats arrays. Changes to the stats there will automatically update into the new UI. I tried to avoid hard-coding values into the code.

All monsters and bosses share the same UI and LUA code. This makes it far easier to find/replace all monster & boss code when making updates.
Last edited by Kraytul; Apr 6, 2020 @ 11:46pm
< >
Showing 1-15 of 21 comments
Vindikaëll Mar 30, 2020 @ 6:31am 
Wha ! It's great ! I will add it in my french mod ! Thank for sharing !
Watafaka  [developer] Mar 30, 2020 @ 6:55am 
Great job with this, we`ve thought about it and we`ll incorporate it into the mod, but we have to do some tweaking first to make sure everything works as intended.
Kraytul Mar 30, 2020 @ 10:44am 
Originally posted by GabrielM:
Great job with this, we`ve thought about it and we`ll incorporate it into the mod, but we have to do some tweaking first to make sure everything works as intended.

Awesome!

The files I uploaded last night had the wrong versions for the code files for the xml, ttslua, and scripts file. I've updated the drive files now and that should make it much more clear what changes I made. Sorry about that.

Please let me know if there's anything I can do to assist with that process. I tried to make the changes as sensible as possible for the UI but I know that my sensibilities aren't the same as everyone's. Shield and retaliate felt like they needed to be bigger and more visible since those are used when doing attacks against the monsters not just on the monster turn, so that's why I pulled them out to the side and made them larger.



Originally posted by Vindikaëll:
Wha ! It's great ! I will add it in my french mod ! Thank for sharing !
Very cool. Hope everything works well!
Last edited by Kraytul; Mar 30, 2020 @ 10:52am
Watafaka  [developer] Mar 30, 2020 @ 11:04am 
For now I`m just in the design phase of this. I`m just trying to figure how I can re arrange everything to maximize the use of space, while also redoing the icons for a better quality. This is step 1 we don on our every update, then we`ll get into coding :)
Last edited by Watafaka; Mar 30, 2020 @ 11:05am
Kraytul Mar 30, 2020 @ 11:19am 
Originally posted by GabrielM:
For now I`m just in the design phase of this. I`m just trying to figure how I can re arrange everything to maximize the use of space, while also redoing the icons for a better quality. This is step 1 we don on our every update, then we`ll get into coding :)

Cool. The assets I uploaded to steam cloud are pretty high quality images, but they struggle some when being scaled down. The images themselves are 625x625 png files taken directly from the creator pack PDF.

The combat stats line currently divides itself into sections for each of movement, attack, range, targets, and attack effects. From what I've loaded with the base stats on monsters, there isn't any monster that has stats too much for all fitting in that space.

The shield and retaliate location being off to the side made a little more sense to me because I wanted them visible but didn't want them overlapping with negative status effects applied to the monster (current implementation)
Kraytul Mar 30, 2020 @ 11:20am 
I will upload the art assets directly to the drive folder as well.
Watafaka  [developer] Mar 30, 2020 @ 11:26am 
No it`s not that, but I assume you cut them down from a jpg image, and when you cut them this way you get pixelated artifacts around the borders. I`m doing this with vector files, to make them small and high quality, because a 600px image will increase loading times a lot...and there are a few images to load.
I have a question though, since I don't understand much from the code.
Where does the standees, pull this stats from, and what happens when the monsters are higher level? Does the code need to be added to each and every monster standee in game?
Kraytul Mar 30, 2020 @ 11:42am 
Originally posted by GabrielM:
No it`s not that, but I assume you cut them down from a jpg image, and when you cut them this way you get pixelated artifacts around the borders. I`m doing this with vector files, to make them small and high quality, because a 600px image will increase loading times a lot...and there are a few images to load.
I have a question though, since I don't understand much from the code.
Where does the standees, pull this stats from, and what happens when the monsters are higher level? Does the code need to be added to each and every monster standee in game?

The stats are being pulled from the same place as the stats array for all the monsters, I accessed it through the existing methods that were defined and used in the "setTyp(typ)" method. Previously, this method only retrieved the stats object to set max health, I extended and revised this method it to get the other stats and parse out values for each attribute.

-- Set base stats move.base = stats.move; attack.base = stats.attack; range.base = stats.range; -- Set attributes mAttributes = {} if(nil ~= stats.attributes) then for _, attr in pairs(stats.attributes) do local checkRange = false for key, val in string.gmatch(attr, "%%(%w+)%%%s*(%w*)") do -- NOTE: retaliate is the only current attribute to use this structure, if more attributes use this structure, a new schema should be designed -- Special case for formatting of ranged retaliate if true == checkRange then key = "retaliateRange" end -- If the attributes are in the list, the monster has them mAttributes[key] = tonumber(val) or 1 -- If the attribute is retaliate, another value will be the range if (key == "retaliate") then checkRange = true end end end end

So, while the code would have to be added to every monster standee, the same code can be added to every monster standee and it will work the same for all of them.

For both the new UI I wrote and the new lua I wrote, it can be exactly copy/paste to every monster standee and works correctly without any code-level customization for which monster standee it is. The code will detect which monsters are bosses and make the appropriate changes to the UI.

As for the images, there may be some tearing on the images, I saved them as .png files from the original TIFF files exported from the creators docs posted online. The file sizes are extremely small actually, under 50kb per icon for the icons and under/around 100kb for the status effects. Not as small as vectors though, for sure.
Last edited by Kraytul; Mar 30, 2020 @ 11:52am
Kraytul Mar 30, 2020 @ 11:56am 
When monsters change level, just the same way that they currently pull the new health values, they pull the same stat changes for every other stat value and attribute. The stats are pulled from the stat object retrieved by the setTyp method
local table = getObjectFromGUID(statsObj).getTable("stats")[getNameWoNumber()][level+1]
Last edited by Kraytul; Mar 30, 2020 @ 11:56am
Watafaka  [developer] Mar 30, 2020 @ 12:19pm 
Ok, got it, so you`re giving us more work, haha. Kidding, we`ll add this after we finish all the fine tunning!
Kraytul Mar 30, 2020 @ 2:12pm 
Originally posted by GabrielM:
Ok, got it, so you`re giving us more work, haha. Kidding, we`ll add this after we finish all the fine tunning!

I uploaded a "GHFS Combat Pack Full Save.json" as an example of the most recent workshop mod version with the following find/replace operations done that implements everything I did.

Also, in case anyone would like to reproduce those find/replace steps they can be found in "Conversion Steps.txt" and as follows (splits due to comment 18000 character limit)
Kraytul Mar 30, 2020 @ 2:13pm 
Unify the three different UI implementations for monster standees with a placeholder

Find:
"XmlUI": "<Defaults>\r\n <Button onClick=\"onClick\" fontSize=\"80\" fontStyle=\"Bold\" textColor=\"#FFFFFF\" color=\"#00000000\"/>\r\n <Text fontSize=\"80\" fontStyle=\"Bold\" color=\"#FFFFFF\"/>\r\n</Defaults>\r\n\r\n<Panel id=\"panel\" position=\"0 0 -220\" rotation=\"270 0 0\" scale=\"0.2 0.2\">\r\n <ProgressBar id=\"progressBar\" height=\"100\" width=\"600\" showPercentageText=\"false\" color=\"#000000E0\" percentage=\"100\" fillImageColor=\"#710000\"></ProgressBar>\r\n <Button id=\"editButton\" height=\"100\" width=\"600\" text=\"10/10\"></Button>\r\n <Image id=\"number\" height=\"150\" width=\"150\" image=\"1\" offsetXY=\"-390 0\" preserveAspect=\"true\"></Image>\r\n <Image id=\"summonIcon\" active=\"false\" height=\"150\" width=\"150\" image=\"S\" offsetXY=\"390 0\" preserveAspect=\"true\"></Image>\r\n <Panel id=\"editPanel\" height=\"300\" width=\"600\" offsetXY=\"0 200\" active=\"False\">\r\n <VerticalLayout>\r\n <HorizontalLayout minheight=\"100\">\r\n <Button id=\"normal\" text=\"Normal\" color=\"#FFFFFF\" textColor=\"#000000\"></Button>\r\n <Button id=\"elite\" text=\"Elite\" color=\"#FFFF00\" textColor=\"#000000\"></Button>\r\n </HorizontalLayout>\r\n <HorizontalLayout spacing=\"0\" minheight=\"100\">\r\n <Button id=\"subNumber\" text=\"◄\"></Button>\r\n <Text>Number</Text>\r\n <Button id=\"addNumber\" text=\"►\"></Button>\r\n </HorizontalLayout>\r\n <HorizontalLayout spacing=\"73\" minheight=\"100\">\r\n <Button id=\"subMax\" text=\"◄\"></Button>\r\n <Text>Max</Text>\r\n <Button id=\"addMax\" text=\"►\"></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n <Panel id=\"defeatPanel\" height=\"260\" width=\"700\" offsetXY=\"0 150\" active=\"false\">\r\n <Button onClick=\"defeateClick\" text=\"Defeat\"/>\r\n </Panel>\r\n <Panel id=\"statePanel\" height=\"260\" width=\"-5\" offsetXY=\"0 210\">\r\n <VerticalLayout>\r\n <HorizontalLayout spacing=\"5\">\r\n <Button id=\"Disarm\" active=\"false\"><Image image=\"Disarm\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Immobilize\" active=\"false\"><Image image=\"Immobilize\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Invisible\" active=\"false\"><Image image=\"Invisible\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Muddle\" active=\"false\"><Image image=\"Muddle\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Poison\" active=\"false\"><Image image=\"Poison\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Strengthen\" active=\"false\"><Image image=\"Strengthen\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Stun\" active=\"false\"><Image image=\"Stun\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Wound\" active=\"false\"><Image image=\"Wound\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Tracker\" active=\"false\"><Image image=\"Tracker\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Regenerate\" active=\"false\"><Image image=\"Regenerate\" preserveAspect=\"true\"></Image></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n</Panel>\r\n",
Replace:
"XmlUI": "MonsterXmlUI",

Find:
"XmlUI": "<Defaults>\r\n <Button onClick=\"onClick\" fontSize=\"80\" fontStyle=\"Bold\" textColor=\"#FFFFFF\" color=\"#00000000\"/>\r\n <Text fontSize=\"80\" fontStyle=\"Bold\" color=\"#FFFFFF\"/>\r\n</Defaults>\r\n\r\n<Panel id=\"panel\" position=\"0 0 -220\" rotation=\"270 0 0\" scale=\"0.2 0.2\">\r\n <ProgressBar id=\"progressBar\" height=\"100\" width=\"600\" showPercentageText=\"false\" color=\"#000000E0\" percentage=\"100\" fillImageColor=\"#710000\"></ProgressBar>\r\n <Button id=\"editButton\" height=\"100\" width=\"600\" text=\"10/10\"></Button>\r\n <Panel id=\"editPanel\" height=\"100\" width=\"600\" offsetXY=\"0 200\" active=\"False\">\r\n <VerticalLayout>\r\n <HorizontalLayout spacing=\"73\" minheight=\"100\">\r\n <Button id=\"subMax\" text=\"◄\"></Button>\r\n <Text>Max</Text>\r\n <Button id=\"addMax\" text=\"►\"></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n <Panel id=\"statePanel\" height=\"260\" width=\"-5\" offsetXY=\"0 210\">\r\n <VerticalLayout>\r\n <HorizontalLayout spacing=\"5\">\r\n <Button id=\"Disarm\" active=\"false\"><Image image=\"Disarm\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Immobilize\" active=\"false\"><Image image=\"Immobilize\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Invisible\" active=\"false\"><Image image=\"Invisible\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Muddle\" active=\"false\"><Image image=\"Muddle\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Poison\" active=\"false\"><Image image=\"Poison\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Strengthen\" active=\"false\"><Image image=\"Strengthen\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Stun\" active=\"false\"><Image image=\"Stun\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Wound\" active=\"false\"><Image image=\"Wound\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Tracker\" active=\"false\"><Image image=\"Tracker\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Regenerate\" active=\"false\"><Image image=\"Regenerate\" preserveAspect=\"true\"></Image></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n</Panel>\r\n",
Replace:
"XmlUI": "MonsterXmlUI",

Find:
"XmlUI": "<Defaults>\r\n <Button onClick=\"onClick\" fontSize=\"80\" fontStyle=\"Bold\" textColor=\"#FFFFFF\" color=\"#00000000\"/>\r\n <Text fontSize=\"80\" fontStyle=\"Bold\" color=\"#FFFFFF\"/>\r\n</Defaults>\r\n\r\n<Panel id=\"panel\" position=\"0 0 -220\" rotation=\"270 0 0\" scale=\"0.2 0.2\">\r\n <ProgressBar id=\"progressBar\" height=\"100\" width=\"600\" showPercentageText=\"false\" color=\"#000000E0\" percentage=\"100\" fillImageColor=\"#710000\"></ProgressBar>\r\n <Button id=\"editButton\" height=\"100\" width=\"600\" text=\"10/10\"></Button>\r\n <Image id=\"number\" height=\"150\" width=\"150\" image=\"1\" offsetXY=\"-390 0\" preserveAspect=\"true\"></Image>\r\n <Panel id=\"editPanel\" height=\"300\" width=\"600\" offsetXY=\"0 200\" active=\"False\">\r\n <VerticalLayout>\r\n <HorizontalLayout spacing=\"73\" minheight=\"100\">\r\n <Button id=\"subMax\" text=\"◄\"></Button>\r\n <Text>Max</Text>\r\n <Button id=\"addMax\" text=\"►\"></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n <Panel id=\"statePanel\" height=\"260\" width=\"-5\" offsetXY=\"0 210\">\r\n <VerticalLayout>\r\n <HorizontalLayout spacing=\"5\">\r\n <Button id=\"Disarm\" active=\"false\"><Image image=\"Disarm\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Immobilize\" active=\"false\"><Image image=\"Immobilize\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Invisible\" active=\"false\"><Image image=\"Invisible\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Muddle\" active=\"false\"><Image image=\"Muddle\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Poison\" active=\"false\"><Image image=\"Poison\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Strengthen\" active=\"false\"><Image image=\"Strengthen\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Stun\" active=\"false\"><Image image=\"Stun\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Wound\" active=\"false\"><Image image=\"Wound\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Regenerate\" active=\"false\"><Image image=\"Regenerate\" preserveAspect=\"true\"></Image></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n</Panel>",
Replace:
"XmlUI": "MonsterXmlUI",
Last edited by Kraytul; Mar 30, 2020 @ 2:23pm
Kraytul Mar 30, 2020 @ 2:13pm 
Replace the placeholder with new UI

Find:
"XmlUI": "MonsterXmlUI",
Replace:
"XmlUI": "<Defaults>\r\n <Button onClick=\"onClick\" fontSize=\"80\" fontStyle=\"Bold\" textColor=\"#FFFFFF\" color=\"#00000000\"/>\r\n <Text fontSize=\"80\" fontStyle=\"Bold\" color=\"#FFFFFF\" outline=\"#000000\"/>\r\n</Defaults>\r\n\r\n<Panel id=\"panel\" position=\"0 0 -240\" rotation=\"270 0 0\" scale=\"0.2 0.2\">\r\n <ProgressBar id=\"progressBar\" height=\"100\" width=\"600\" showPercentageText=\"false\" color=\"#000000\" percentage=\"100\" fillImageColor=\"#710000\">\r\n <!-- Health Bar -->\r\n <HorizontalLayout>\r\n <Button id=\"subHP\" onClick=\"hits(sub)\" active=\"true\" rectAlignment=\"MiddleLeft\" width=\"100\">\r\n <Image image=\"Health-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n <Text color=\"#000000\" offsetXY=\"-2 -8\">-</Text>\r\n </Button>\r\n <Panel id=\"healthSubPanel\" active=\"false\">\r\n <Button id=\"subMax\" onClick=\"hits(subMax)\" text=\"◄\"></Button>\r\n </Panel>\r\n <Panel id=\"health\" width=\"300\">\r\n <Text id=\"healthbar\" width=\"600\"></Text>\r\n <!-- Defeat monster at 0 hp -->\r\n <Panel id=\"defeatPanel\" height=\"100\" width=\"600\" offsetXY=\"0 0\" active=\"false\">\r\n <Button onClick=\"defeatClick\" text=\"Defeat\"/>\r\n </Panel>\r\n </Panel>\r\n <Panel id=\"healthAddPanel\" active=\"false\">\r\n <Button id=\"addMax\" onClick=\"hits(addMax)\" text=\"►\"></Button>\r\n </Panel>\r\n <Button id=\"addHP\" onClick=\"hits(add)\" active=\"true\" rectAlignment=\"MiddleRight\" width=\"100\">\r\n <Image image=\"Health-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n <Text color=\"#000000\" offsetXY=\"2 -11\">+</Text>\r\n </Button>\r\n </HorizontalLayout>\r\n </ProgressBar>\r\n\r\n <!-- Combat stats display -->\r\n <Panel id=\"stats\" height=\"100\" width=\"600\" padding=\"0\" offsetXY=\"0 -95\" active=\"true\">\r\n <HorizontalLayout height=\"100\" width=\"600\" spacing=\"10\" childAlignment=\"MiddleCenter\">\r\n\r\n <!-- Movement -->\r\n <Panel id=\"movePanel\" minWidth=\"100\" height=\"100\">\r\n <HorizontalLayout childForceExpandWidth=\"false\" childAlignment=\"MiddleCenter\">\r\n <Button id=\"flying\" minWidth=\"100\" active=\"false\">\r\n <Image id=\"flyIcon\" image=\"Flight-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n </Button>\r\n <Button id=\"moving\" minWidth=\"100\" active=\"true\">\r\n <Image id=\"moveIcon\" image=\"Move-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n </Button>\r\n <Text id=\"moveVal\" fontSize=\"70\" minWidth=\"50\"></Text>\r\n </HorizontalLayout>\r\n </Panel>\r\n\r\n <!-- Attack Panel -->\r\n <Panel id=\"attackPanel\" minWidth=\"100\" height=\"100\">\r\n <HorizontalLayout childForceExpandWidth=\"false\" childAlignment=\"MiddleCenter\">\r\n <Button minWidth=\"100\">\r\n <Image id=\"attackIcon\" image=\"Attack-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n </Button>\r\n <Text id=\"attackVal\" fontSize=\"70\" minWidth=\"50\"></Text>\r\n </HorizontalLayout>\r\n </Panel>\r\n\r\n <!-- Range Panel -->\r\n <Panel id=\"rangePanel\" minWidth=\"100\" height=\"100\" active=\"false\">\r\n <HorizontalLayout childForceExpandWidth=\"false\" childAlignment=\"MiddleCenter\">\r\n <Button minWidth=\"100\" offsetXY=\"0 -5\">\r\n <Image id=\"rangeIcon\" image=\"Range-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n </Button>\r\n <Text id =\"rangeVal\" fontSize=\"70\" color=\"#FFFFFF\" outline=\"#000000\"></Text>\r\n </HorizontalLayout>\r\n </Panel>\r\n\r\n <!-- Target Panel -->\r\n <Panel id=\"targetPanel\" minWidth=\"100\" height=\"100\" active=\"false\" childAlignment=\"MiddleCenter\">\r\n <Button minWidth=\"100\">\r\n <Image id=\"targetIcon\" image=\"Targets-W\" preserveAspect=\"true\" height=\"90\" outline=\"#000000\"></Image>\r\n <Text id=\"targetVal\" fontSize=\"50\" color=\"#FFFFFF\" outline=\"#000000\" outlineSize=\"3 3\"></Text>\r\n </Button>\r\n </Panel>\r\n\r\n <!-- Effects Panel -->\r\n <Panel id=\"attackEffectsPanel\" minWidth=\"100\" height=\"100\" offsetXY=\"0 -10\" active=\"false\">\r\n <HorizontalLayout childAlignment=\"MiddleCenter\">\r\n <Button id=\"muddleEffect\" minWidth=\"50\">\r\n <Image image=\"Muddle\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"poisonEffect\" minWidth=\"50\">\r\n <Image image=\"Poison\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"woundEffect\" minWidth=\"50\">\r\n <Image image=\"Wound\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"immobilizeEffect\" minWidth=\"50\">\r\n <Image image=\"Immobilize\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"disarmEffect\" minWidth=\"50\">\r\n <Image image=\"Disarm\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"stunEffect\" minWidth=\"50\">\r\n <Image image=\"Stun\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"strengthenEffect\" minWidth=\"50\">\r\n <Image image=\"Strengthen\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"curseEffect\" minWidth=\"50\">\r\n <Image image=\"Curse\" preserveAspect=\"true\"></Image>\r\n </Button>\r\n <Button id=\"pierceEffect\" minWidth=\"50\">\r\n <Image image=\"Pierce\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n <Text id=\"pierceVal\" fontSize=\"50\" color=\"#FFFFFF\" outline=\"#000000\" outlineSize=\"3 3\" offsetXY=\"-20 0\"></Text>\r\n </Button>\r\n <Button id=\"pushEffect\" minWidth=\"50\">\r\n <Image image=\"Push\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n <Text id=\"pushVal\" fontSize=\"50\" color=\"#FFFFFF\" outline=\"#000000\" outlineSize=\"3 3\" offsetXY=\"-20 0\"></Text>\r\n </Button>\r\n </HorizontalLayout>\r\n </Panel>\r\n </HorizontalLayout>\r\n </Panel>\r\n\r\n <!-- Shield Panel -->\r\n <Panel id=\"shieldPanel\" minWidth=\"100\" width=\"100\" height=\"100\" offsetXY=\"350 0\" active=\"false\">\r\n <Button minWidth=\"100\">\r\n <Image id=\"shieldIcon\" image=\"Shield-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n <Text id=\"shieldVal\" color=\"#FFFFFF\" outline=\"#000000\" outlineSize=\"3 3\" fontSize=\"60\"></Text>\r\n </Button>\r\n </Panel>\r\n\r\n <Panel id=\"retaliatePanel\" minWidth=\"150\" width=\"100\" height=\"100\" offsetXY=\"350 -82\" active=\"false\">\r\n <Panel id=\"retaliateDmg\" minWidth=\"100\">\r\n <Button id=\"retaliate\">\r\n <Image id=\"retaliateIcon\" image=\"Retaliate-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n </Button>\r\n <Text id=\"retaliateVal\" color=\"#FFFFFF\" outline=\"#000000\" outlineSize=\"3 3\" fontSize=\"50\"></Text>\r\n </Panel>\r\n <Panel id=\"retaliateRangePanel\" minWidth=\"50\" offsetXY=\"0 -65\" active=\"false\">\r\n <Button id=\"retaliateRange\">\r\n <Image id=\"rangeIcon\" image=\"Range-W\" preserveAspect=\"true\" outline=\"#000000\"></Image>\r\n </Button>\r\n <Text id=\"retaliateRangeVal\" fontSize=\"50\" minWidth=\"30\" color=\"#FFFFFF\" outline=\"#000000\" outlineSize=\"3 3\"></Text>\r\n </Panel>\r\n </Panel>\r\n\r\n <!-- Monster number / customization button -->\r\n <Panel id=\"monsterNumber\" height=\"200\" width=\"150\" offsetXY=\"-390 -20\">\r\n <HorizontalLayout width=\"150\" offsetXY=\"0 -100\">\r\n <Button id=\"subNumber\" onClick=\"subNumber\" text=\"▼\" active=\"false\">▼</Button>\r\n <Button id=\"addNumber\" onClick=\"addNumber\" text=\"▲\" active=\"false\">▲</Button>\r\n </HorizontalLayout>\r\n <Image id=\"number\" height=\"150\" width=\"150\" image=\"1\" offsetXY=\"0 0\" preserveAspect=\"true\">\r\n <Button id=\"editButton\" onClick=\"toggleCustomMonsterUI\" height=\"150\" width=\"150\"></Button>\r\n </Image>\r\n <Image id=\"summonIcon\" active=\"false\" height=\"150\" width=\"150\" image=\"Summon-1\" offsetXY=\"0 0\" preserveAspect=\"true\"></Image>\r\n </Panel>\r\n\r\n <!-- Elite / normal monster customization -->\r\n <Panel id=\"elitePanel\" height=\"90\" width=\"600\" offsetXY=\"0 -86\" active=\"false\">\r\n <HorizontalLayout minheight=\"80\">\r\n <Button id=\"normal\" text=\"Normal\" color=\"#FFFFFF\" textColor=\"#000000\" rectAlignment=\"MiddleLeft\"></Button>\r\n <Button id=\"elite\" text=\"Elite\" color=\"#FFFF00\" textColor=\"#000000\" rectAlignment=\"MiddleRight\"></Button>\r\n </HorizontalLayout>\r\n </Panel>\r\n\r\n\r\n <!-- Status effects display -->\r\n <Panel id=\"statePanel\" height=\"260\" width=\"0\" offsetXY=\"0 210\">\r\n <VerticalLayout>\r\n <HorizontalLayout spacing=\"5\">\r\n <Button id=\"Disarm\" active=\"false\"><Image image=\"Disarm\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Immobilize\" active=\"false\"><Image image=\"Immobilize\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Invisible\" active=\"false\"><Image image=\"Invisible\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Muddle\" active=\"false\"><Image image=\"Muddle\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Poison\" active=\"false\"><Image image=\"Poison\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Strengthen\" active=\"false\"><Image image=\"Strengthen\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Stun\" active=\"false\"><Image image=\"Stun\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Wound\" active=\"false\"><Image image=\"Wound\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Tracker\" active=\"false\"><Image image=\"Tracker\" preserveAspect=\"true\"></Image></Button>\r\n <Button id=\"Regenerate\" active=\"false\"><Image image=\"Regenerate\" preserveAspect=\"true\"></Image></Button>\r\n </HorizontalLayout>\r\n </VerticalLayout>\r\n </Panel>\r\n</Panel>\r\n",
Last edited by Kraytul; Mar 30, 2020 @ 2:23pm
Kraytul Mar 30, 2020 @ 2:14pm 
Replace monster scripts with placeholder (split over comments due to markdown issue)

Find:
"LuaScript": "statNames = {Wound = false, Muddle = false, Immobilize = false, Invisible = false, Disarm = false, Strengthen = false, Stun = false, Poison = false, Tracker = false, Regenerate = false}\r\n\r\nhealth = {\r\n value = 10,\r\n max = 10\r\n}\r\n\r\nstatsObj = \"6c1b20\"\r\n\r\nfunction onLoad(save_state)\r\n if save_state ~= \"\" and self.getDescription() ~= \"\" then\r\n saved_data = JSON.decode(save_state)\r\n if saved_data.statNames then\r\n for i,j in pairs(saved_data.statNames) do\r\n statNames = saved_data.statNames\r\n end\r\n end\r\n if saved_data.health then\r\n health = saved_data.health\r\n Wait.condition(setHealth, checkLoaded)\r\n end\r\n else\r\n Wait.condition(function() setTyp({\"normal\"}) end, checkLoaded)\r\n end\r\n Wait.condition(checkStats, checkLoaded)\r\n createBtns()\r\nend\r\n\r\nfunction checkLoaded()\r\n if not self.loading_custom and not self.spawning then\r\n return true\r\n end\r\n return false\r\nend\r\n\r\nfunction checkStats()\r\n for i,j in pairs(statNames) do\r\n if j == true then\r\n self.UI.setAttribute(i, \"active\", true)\r\n end\r\n end\r\n Wait.frames(function() self.UI.setAttribute(\"statePanel\", \"width\", 300 + (getStatsCount()-1)*350) end, 1)\r\n local num = self.getName():match(\"%d+\")\r\n if num ~= nil then\r\n -- Wait.frames(function() self.UI.setAttribute(\"number\", \"image\", num) end, 1)\r\n self.UI.setAttribute(\"number\", \"image\", num)\r\n end\r\nend\r\n\r\nfunction onSave()\r\n local save_state = JSON.encode({health = health, statNames = statNames})\r\n -- local save_state = \"\"\r\n self.script_state = save_state\r\nend\r\n\r\nfunction createBtns()\r\n local buttonParameter = {click_function = \"add\", function_owner = self, position = {-0.2, 0.04, -0.3}, label = \"+\", width = 250, height = 250, font_size = 300, color = {0,0,0,0}, font_color = {0,0,0,100}}\r\n self.createButton(buttonParameter)\r\n buttonParameter.position = {0.2, 0.04, -0.3}\r\n buttonParameter.click_function = \"sub\"\r\n buttonParameter.label = \"-\"\r\n self.createButton(buttonParameter)\r\nend\r\n\r\nfunction add() onClick(-1, - 1, \"add\") end\r\nfunction sub() onClick(-1, - 1, \"sub\") end\r\nfunction onClick(player, value, id)\r\n if id == \"editButton\" then\r\n if self.UI.getAttribute(\"editPanel\", \"active\") == \"False\" or self.UI.getAttribute(\"editPanel\", \"active\") == nil then\r\n self.UI.setAttribute(\"editPanel\", \"active\", true)\r\n else\r\n self.UI.setAttribute(\"editPanel\", \"active\", false)\r\n end\r\n elseif statNames[id] ~= nil then\r\n self.UI.setAttribute(id, \"active\", false)\r\n self.UI.setAttribute(\"statePanel\", \"width\", 260 + (getStatsCount()-1)*280)\r\n statNames[id] = false\r\n elseif id == \"normal\" or id == \"elite\" then\r\n setTyp({id})\r\n elseif id == \"addNumber\" or id == \"subNumber\" then\r\n local temp = -1\r\n if id == \"addNumber\" then temp = 1 end\r\n local number = tonumber(self.UI.getAttribute(\"number\", \"image\")) + temp\r\n if number > 10 then number = 10 end\r\n if number < 1 then number = 1 end\r\n self.UI.setAttribute(\"number\", \"image\", number)\r\n else\r\n if id == \"add\" then health.value = health.value + 1\r\n elseif id == \"sub\" then health.value = health.value - 1\r\n elseif id == \"addMax\" then health.value = health.value + 1\r\n health.max = health.max + 1\r\n elseif id == \"subMax\" then health.value = health.value - 1\r\n health.max = health.max - 1\r\n end\r\n if health.value > health.max then health.value = health.max end\r\n if health.value < 0 then health.value = 0 end\r\n if health.value < 1 then self.UI.setAttribute(\"defeatPanel\", \"active\", true) else self.UI.setAttribute(\"defeatPanel\", \"active\", false) end\r\n setHealth()\r\n end\r\n self.UI.setAttribute(\"editButton\", \"textColor\", \"#FFFFFF\")\r\nend\r\n\r\nfunction defeateClick(player, value, id)\r\n if self.UI.getAttribute(\"summonIcon\", \"active\") ~= \"True\" then\r\n getObjectFromGUID(\"a41a54\").takeObject({position = self.positionToWorld({0, 1, 0}), rotation = {0.00, 180.00, 0.00}, smooth = false})\r\n broadcastToAll(self.getName() .. \" was defeated and dropped a coin.\", {0, 1, 0})\r\n else\r\n broadcastToAll(self.getName() .. \" was defeated and was a summon.\", {0, 1, 0})\r\n end\r\n destroyObject(self)\r\nend\r\n\r\nfunction onCollisionEnter(a)\r\n local newState = a.collision_object.getName()\r\n if newState == \"Summon\" then\r\n self.UI.setAttribute(\"summonIcon\", \"active\", true)\r\n a.collision_object.destruct()\r\n else\r\n if statNames[newState] ~= nil then\r\n statNames[newState] = true\r\n a.collision_object.destruct()\r\n self.UI.setAttribute(newState, \"active\", true)\r\n Wait.frames(function() self.UI.setAttribute(\"statePanel\", \"width\", 260 + (getStatsCount()-1)*265) end, 1)\r\n end\r\n end\r\nend\r\n\r\nfunction getStatsCount()\r\n local count = 0\r\n for i,j in pairs(statNames) do\r\n if self.UI.getAttribute(i, \"active\") == \"True\" then\r\n count = count + 1\r\n end\r\n end\r\n return count\r\nend\r\n\r\nfunction setHealth()\r\n self.UI.setAttribute(\"progressBar\", \"percentage\", health.value / health.max * 100)\r\n self.UI.setAttribute(\"editButton\", \"text\", health.value .. \"/\" .. health.max)\r\n self.setDescription(health.value .. \"/\" .. health.max)\r\n self.UI.setAttribute(\"editButton\", \"textColor\", \"#FFFFFF\")\r\n self.UI.setAttribute(\"panel\", \"position\", \"0 0 -\" .. self.getBounds().size.y / self.getScale().y * 110 + getObjectFromGUID(\"75ab50\").getTable(\"monsters\")[getNameWoNumber()].HPBarOffset + getObjectFromGUID(\"75ab50\").getTable(\"monsters\")[getNameWoNumber()].HPBarOffset)\r\nend\r\n\r\nfunction setTyp(typ)\r\n if typ[1] == \"normal\" then\r\n self.setColorTint({1,1,1})\r\n else\r\n self.setColorTint({1,1,0})\r\n end\r\n local partySheet = nil\r\n for _, obj in pairs(getAllObjects()) do\r\n if obj.getDescription() == \"Party Sheet\" then\r\n partySheet = obj\r\n end\r\n end\r\n if partySheet ~= nil then\r\n local level = tonumber(partySheet.UI.getAttribute(\"scenarioLevel\", \"text\"))\r\n -- getObjectFromGUID(statsObj).getTable(\"stats\")[getNameWoNumber()][level+1][typ[1]]\r\n local table = getObjectFromGUID(statsObj).getTable(\"stats\")[getNameWoNumber()][level+1]\r\n if table.normal ~= nil then\r\n health.max = table[typ[1]].health\r\n else\r\n return\r\n end\r\n health.value = health.max/r/n setHealth()\r\n end\r\nend\r\n\r\nfunction getNameWoNumber()\r\n local name = self.getName()\r\n local num = name:match(\"%d+\")\r\n if num ~= nil then\r\n return name:sub(1,name:find(num)-2)/r/n else\r\n return name\r\n end\r\nend\r\n",
Last edited by Kraytul; Mar 30, 2020 @ 2:23pm
Kraytul Mar 30, 2020 @ 2:16pm 
Replace:
"LuaScript": "MonsterLuaScript",

Replace monster script placeholder with new monster script

Find:
"LuaScript": "MonsterLuaScript",
Replace:
"LuaScript": "statNames = {\r\n Wound = false,\r\n Muddle = false,\r\n Immobilize = false,\r\n Invisible = false,\r\n Disarm = false,\r\n Strengthen = false,\r\n Stun = false,\r\n Poison = false,\r\n Tracker = false,\r\n Regenerate = false\r\n}\r\n\r\nhealth = {\r\n value = 10,\r\n max = 10\r\n}\r\n\r\nmove = {\r\n base = 0,\r\n mod = 0\r\n}\r\n\r\nattack = {\r\n base = 0,\r\n mod = 0\r\n}\r\n\r\nrange = {\r\n base = 0,\r\n mod = 0\r\n}\r\n\r\nattributes = {\r\n -- Implemented\r\n flying = 0,\r\n shield = 0,\r\n retaliate = 0,\r\n retaliateRange = 0,\r\n target = 0,\r\n muddle = 0,\r\n poison = 0,\r\n wound = 0,\r\n immobilize = 0,\r\n disarm = 0,\r\n stun = 0,\r\n strengthen = 0,\r\n curse = 0,\r\n pierce = 0,\r\n push = 0,\r\n\r\n -- Not implemented\r\n pull = 0,\r\n bless = 0,\r\n attackers = 0, -- attackers gain disadvantage\r\n advantage = 0 -- has advantage\r\n}\r\n\r\nisBoss = false\r\n\r\nstatsPanelStatWidth = 265\r\n\r\nstatsObj = \"6c1b20\"\r\n\r\nfunction onLoad(save_state)\r\n if save_state ~= \"\" and self.getDescription() ~= \"\" then\r\n saved_data = JSON.decode(save_state)\r\n\r\n -- Status conditions\r\n if saved_data.statNames then\r\n for i,j in pairs(saved_data.statNames) do\r\n statNames = saved_data.statNames\r\n end\r\n end\r\n\r\n -- Health\r\n if saved_data.health then\r\n health = saved_data.health\r\n Wait.condition(updateHealthUI, checkLoaded)\r\n end\r\n\r\n -- Combat data\r\n if saved_data.move then\r\n move = saved_data.move/r/n attack = saved_data.attack\r\n range = saved_data.range\r\n if saved_data.attributes then\r\n for i,j in pairs(saved_data.attributes) do\r\n attributes = j\r\n end\r\n end\r\n Wait.condition(updateCombatUI, checkLoaded)\r\n end\r\n\r\n -- Boss\r\n isBoss = saved_data.isBoss or false\r\n else\r\n Wait.condition(function() setTyp({\"normal\"}) end, checkLoaded)\r\n end\r\n Wait.condition(checkStats, checkLoaded)\r\nend\r\n\r\nfunction checkLoaded()\r\n if not self.loading_custom and not self.spawning then\r\n return true\r\n end\r\n return false\r\nend\r\n\r\nfunction onSave()\r\n local save_state = JSON.encode({health = health, statNames = statNames, move = move, attack = attack, range = range, attributes = attributes, isBoss = isBoss})\r\n -- local save_state = \"\"\r\n self.script_state = save_state\r\nend\r\n\r\n-- Check current status effects\r\nfunction checkStats()\r\n for i,j in pairs(statNames) do\r\n if j == true then\r\n self.UI.setAttribute(i, \"active\", true)\r\n end\r\n end\r\n updateUIStatPanel()\r\n local num = self.getName():match(\"%d+\")\r\n if num ~= nil then\r\n -- Wait.frames(function() self.UI.setAttribute(\"number\", \"image\", num) end, 1)\r\n self.UI.setAttribute(\"number\", \"image\", num)\r\n end\r\nend\r\n\r\n-- This interface is updated often enough to have multiple calls\r\nfunction updateUIStatPanel()\r\n Wait.frames(function() self.UI.setAttribute(\"statePanel\", \"width\", getStatsCount() * statsPanelStatWidth) end, 1)\r\nend\r\n\r\n-- Count the number of status effects on the monster\r\nfunction getStatsCount()\r\n local count = 0\r\n for i,j in pairs(statNames) do\r\n if self.UI.getAttribute(i, \"active\") == \"True\" then\r\n count = count + 1\r\n end\r\n end\r\n return count\r\nend\r\n\r\n-- Apply a status effect by dropping a token on the standee\r\nfunction onCollisionEnter(a)\r\n local newState = a.collision_object.getName()\r\n if newState == \"Summon\" then\r\n self.UI.setAttribute(\"summonIcon\", \"active\", true)\r\n a.collision_object.destruct()\r\n else\r\n if statNames[newState] ~= nil then\r\n statNames[newState] = true\r\n a.collision_object.destruct()\r\n self.UI.setAttribute(newState, \"active\", true)\r\n updateUIStatPanel()\r\n end\r\n end\r\nend\r\n\r\n-- Do the appropriate healing / damaging\r\nfunction hits(player, change)\r\n if change == \"add\" then\r\n health.value = health.value + 1\r\n elseif change == \"addMax\" then\r\n health.value = health.value + 1\r\n health.max = health.max + 1\r\n elseif change == \"sub\" then\r\n health.value = health.value - 1\r\n elseif change == \"subMax\" then\r\n health.value = health.value - 1\r\n if health.max > 0 then health.max = health.max - 1 end\r\n end\r\n\r\n -- Boundary-check health values\r\n if health.value > health.max then health.value = health.max end\r\n if health.value < 0 then health.value = 0 end\r\n\r\n updateHealthUI()\r\nend\r\n\r\n-- Sets health in the UI\r\nfunction updateHealthUI()\r\n -- Bosses don't have a number or summon icon\r\n toggleUIActivity(\"monsterNumber\", true ~= isBoss)\r\n\r\n -- Activate defeat interface\r\n if health.value < 1 then\r\n self.UI.setAttribute(\"defeatPanel\", \"active\", true)\r\n self.UI.setAttribute(\"healthbar\", \"active\", false)\r\n else\r\n self.UI.setAttribute(\"defeatPanel\", \"active\", false)\r\n self.UI.setAttribute(\"healthbar\", \"active\", true)\r\n end\r\n\r\n self.UI.setAttribute(\"progressBar\", \"percentage\", health.value / health.max * 100)\r\n self.UI.setAttribute(\"healthbar\", \"text\", health.value .. \"/\" .. health.max)\r\n self.setDescription(health.value .. \"/\" .. health.max)\r\n self.UI.setAttribute(\"panel\", \"position\", \"0 0 -\" .. self.getBounds().size.y / self.getScale().y * 110 + getObjectFromGUID(\"75ab50\").getTable(\"monsters\")[getNameWoNumber()].HPBarOffset + getObjectFromGUID(\"75ab50\").getTable(\"monsters\")[getNameWoNumber()].HPBarOffset)\r\nend\r\n\r\n-- Toggle the customize monster interface (elite, max health, number)\r\nfunction toggleCustomMonsterUI()\r\n -- Toggle the elite / normal monster panel\r\n toggleUIActivity(\"elitePanel\")\r\n\r\n -- Toggle max health modifier panel\r\n toggleUIActivity(\"healthAddPanel\")\r\n toggleUIActivity(\"healthSubPanel\")\r\n\r\n -- Toggle current HP buttons\r\n toggleUIActivity(\"addHP\")\r\n toggleUIActivity(\"subHP\")\r\n\r\n -- Toggle monster number buttons\r\n toggleUIActivity(\"addNumber\")\r\n toggleUIActivity(\"subNumber\")\r\nend\r\n\r\n-- isUIElementActive(string:elementID)\r\n-- Returns boolean:True if element attribute \"active\" matches \"True\" or \"true\"\r\n-- Returns boolean:False for all other circumstances\r\nfunction isUIElementActive(elementID)\r\n local attributeValue = self.UI.getAttribute(elementID, \"active\")\r\n return (attributeValue == \"True\") or (attributeValue == \"true\")\r\nend\r\n\r\n-- toggleUIActivity(string:elementID)\r\n-- Toggle \"active\" attribute of UI element to opposite boolean value\r\nfunction toggleUIActivity(elementID) toggleUIActivity(elementID, nil) end\r\n\r\n-- toggleUIActivity(string:elementID, boolean:state)\r\n-- Set \"active\" attribute of UI element to given boolean value\r\nfunction toggleUIActivity(elementID, state)\r\n if (nil == state) then state = (true ~= isUIElementActive(elementID)) end\r\n self.UI.setAttribute(elementID, \"active\", state)\r\nend\r\n\r\n\r\n-- For other miscellaneous button clicks\r\nfunction onClick(player, value, id)\r\n if statNames[id] ~= nil then\r\n self.UI.setAttribute(id, \"active\", false)\r\n updateUIStatPanel()\r\n elseif id == \"normal\" or id == \"elite\" then\r\n setTyp({id})\r\n else\r\n -- nadda\r\n end\r\nend\r\n\r\n-- Change monster number / summon number\r\nfunction addNumber() changeNumber(1) end\r\nfunction subNumber() changeNumber(-1) end\r\nfunction changeNumber(delta)\r\n local isSummon = isUIElementActive(\"summonIcon\")\r\n if isSummon then\r\n local numberString = string.gsub(self.UI.getAttribute(\"summonIcon\", \"image\"), \"[^%d]\", \"\")\r\n local number = tonumber(numberString) + delta\r\n if number > 4 then number = 4 end\r\n if number < 1 then number = 1 end\r\n self.UI.setAttribute(\"summonIcon\", \"image\", \"Summon-\"..number)\r\n else\r\n local number = tonumber(self.UI.getAttribute(\"number\", \"image\")) + delta\r\n if number > 10 then number = 10 end\r\n if number < 1 then number = 1 end\r\n self.UI.setAttribute(\"number\", \"image\", number)\r\n end\r\nend\r\n\r\n-- Destroy an enemy and make a coin if appropriate\r\nfunction defeatClick(player, value, id)\r\n if self.UI.getAttribute(\"summonIcon\", \"active\") ~= \"True\" then\r\n getObjectFromGUID(\"a41a54\").takeObject({position = self.positionToWorld({0, 1, 0}), rotation = {0.00, 180.00, 0.00}, smooth = false})\r\n broadcastToAll(self.getName() .. \" was defeated and dropped a coin.\", {0, 1, 0})\r\n else\r\n broadcastToAll(self.getName() .. \" was defeated and was a summon.\", {0, 1, 0})\r\n end\r\n destroyObject(self)\r\nend\r\n\r\n-- Sets combat stats in the UI\r\nfunction updateCombatUI()\r\n\r\n -- Movement\r\n local moveValue = move.base/r/n self.UI.setValue(\"moveVal\", moveValue)\r\n if (1 == attributes[\"flying\"]) then\r\n toggleUIActivity(\"moving\", false)\r\n toggleUIActivity(\"flying\", true)\r\n else\r\n toggleUIActivity(\"flying\", false)\r\n toggleUIActivity(\"moving\", true)\r\n end\r\n toggleUIActivity(\"movePanel\", 0 < moveValue)\r\n -- Attack\r\n local attackValue = attack.base/r/n self.UI.setValue(\"attackVal\", attackValue)\r\n\r\n -- Range\r\n local rangeValue = range.base/r/n self.UI.setValue(\"rangeVal\", rangeValue)\r\n toggleUIActivity(\"rangePanel\", 0 < rangeValue)\r\n\r\n -- Shield\r\n local shieldValue = attributes[\"shield\"]\r\n self.UI.setValue(\"shieldVal\", shieldValue)\r\n toggleUIActivity(\"shieldPanel\", 0 < shieldValue)\r\n\r\n -- Retaliate\r\n local retaliateValue = attributes[\"retaliate\"]\r\n self.UI.setValue(\"retaliateVal\", retaliateValue)\r\n toggleUIActivity(\"retaliatePanel\", 0 < retaliateValue)\r\n local retaliateRangeValue = attributes[\"retaliateRange\"]\r\n self.UI.setValue(\"retaliateRangeVal\", retaliateRangeValue)\r\n toggleUIActivity(\"retaliateRangePanel\", 0 < retaliateRangeValue)\r\n\r\n -- Targets\r\n local targetValue = attributes[\"target\"]\r\n self.UI.setValue(\"targetVal\", targetValue)\r\n toggleUIActivity(\"targetPanel\", 0 < targetValue)\r\n\r\n -- Attack Effects\r\n local hasAttackEffect = false\r\n for _, effect in pairs({\"muddle\", \"poison\", \"wound\", \"immobilize\", \"disarm\", \"stun\", \"strengthen\", \"curse\", \"pierce\", \"push\"}) do\r\n local val = attributes[effect]\r\n if (0 < val) then hasAttackEffect = true end\r\n self.UI.setValue(effect..\"Val\", val)\r\n toggleUIActivity(effect..\"Effect\", 0 < val)\r\n end\r\n toggleUIActivity(\"attackEffectsPanel\", hasAttackEffect)\r\nend\r\n\r\nfunction setBossFlag()\r\n isBoss = true\r\nend\r\n\r\n-- Sets the type of monster, stats, and attributes\r\nfunction setTyp(typ)\r\n if true ~= isBoss then\r\n if typ[1] == \"normal\" then\r\n self.setColorTint({1,1,1})\r\n else\r\n self.setColorTint({1,1,0})\r\n end\r\n end\r\n\r\n local partySheet = nil\r\n for _, obj in pairs(getAllObjects()) do\r\n if obj.getDescription() == \"Party Sheet\" then\r\n partySheet = obj\r\n end\r\n end\r\n if partySheet ~= nil then\r\n local level = tonumber(partySheet.UI.getAttribute(\"scenarioLevel\", \"text\"))\r\n local stats = getObjectFromGUID(statsObj).getTable(\"stats\")[getNameWoNumber()][level+1]\r\n\r\n -- Boss standees have health calculated elsewhere\r\n if true ~= isBoss then\r\n stats = stats[typ[1]]\r\n health.max = stats.health\r\n health.value = health.max/r/n end\r\n\r\n -- Set base stats\r\n move.base = stats.move;\r\n attack.base = stats.attack;\r\n range.base = stats.range;\r\n\r\n -- Set attributes\r\n mAttributes = {}\r\n if(nil ~= stats.attributes) then\r\n for _, attr in pairs(stats.attributes) do\r\n local checkRange = false\r\n for key, val in string.gmatch(attr, \"%%(%w+)%%%s*(%w*)\") do\r\n -- NOTE: retaliate is the only current attribute to use this structure, if more attributes use this structure, a new schema should be designed\r\n -- Special case for formatting of ranged retaliate\r\n if true == checkRange then key = \"retaliateRange\" end\r\n\r\n -- If the attributes are in the list, the monster has them\r\n mAttributes[key] = tonumber(val) or 1\r\n\r\n -- If the attribute is retaliate, another value will be the range\r\n if (key == \"retaliate\") then checkRange = true end\r\n end\r\n end\r\n end\r\n\r\n -- Old attributes either acquire new values or are set to zero\r\n for k,v in pairs(attributes) do\r\n newVal = mAttributes[k]\r\n attributes[k] = newVal or 0\r\n end\r\n\r\n updateHealthUI()\r\n updateCombatUI()\r\n end\r\nend\r\n\r\nfunction getNameWoNumber()\r\n local name = self.getName()\r\n local num = name:match(\"%d+\")\r\n if num ~= nil then\r\n return name:sub(1,name:find(num)-2)/r/n else\r\n return name\r\n end\r\nend\r\n",[/code]
Last edited by Kraytul; Mar 30, 2020 @ 2:22pm
< >
Showing 1-15 of 21 comments
Per page: 1530 50