uber's new mod system is just :shock: and shaping up to be FA modding done right (and we can already forsee it surpassing modding on FA and all other platforms) • it would be fantastic to see the mod folder go up two dirrectories, so as to allow modding over all of PA (and compatibility for all current PA mods, some of wich are very popular) • the readme info is sparse, it does not include a full howto, how it works and what it can do (me and cola colin worked out some of that. Check our mods and you'll understand how to utilize the mods folder). it does say that "Only .css and .js files are currently supported (html fragments will be supported latter)." this frightens me as I would like to make a mod (and all mods I make from now on will conform to this mod standart.) containing html files. I would like to change the modding files to recognize html, I don't know where they are and how to do this, HELP. (if Uber has the time to pass me some buggy buggy buggy buggy buggy reallly buggy edits of the PA files that at are at least even meant to do this I would be most most greatful.)
Imho the readme says all that needs to be told. You actually can edit the html indirectly via jquery from js files. Check out my mouseswitch mod, it's newest version does it. You can basically replace every piece of html however you want with it. Also I moved this thread to the mod developers forums.
ok, I'll try with Jquerry EDIT: wait that has no point, might as well do it the old fashion way and overwrite a file, this has none of the advantages the new mod format brings. since I don't know what Jquerry does I'm not quite sure what would happen, would it apply a "filter layer" to the file or rewrite it's content permanently?
You basically can express programmatically the changes you want to apply. So you say: "Add attribute X to element with id Y". Add "html-block Z as a child to element with id A" Mouseswitch adds attributes to bind element visibility like this: Code: $('#version_info').attr('data-bind', 'visible: mouseSwitchShow'); $('#player_info').attr('data-bind', 'visible: mouseSwitchShow'); $('.div_chat').attr('data-bind', 'visible: mouseSwitchShow'); $('.div_time_invoke_cont').attr('data-bind', 'visible: mouseSwitchShow'); $('#worldpopup01').attr('data-bind', 'visible: showsWorldHoverTarget'); I.e. to add a counter of how many units you have you can do this and load the js from the mod_list: Code: $('body').append('<div style="position:absolute; top:0px;"><span data-bind="text: statscontent">HERE</span></div>'); model.statscontent=ko.computed(function() { return "Units: " + model.armySize(); }); So this has the advantage that it stays intact even if the game is patched. Ofc if you say "add element after some other element" and the other element is removed by the patch you will have to fix your mod.
Ok how about using Code: document.write(); ? would this achieve the same thing? I don't know how to use it though. How do I point it to the right file?
You are modifying the html that is loaded for the current scene in memory. document.write is basically far less powerful, but it uses the same concept. So the code I posted is loaded for the live_game, so it works on the live_game html.
Oh I now see how it works. (I think?) so given that I already have the html document (and that I'm piss poor at writing code) could Code: $(whole original document).attr(whole edited document); work?
no, as attr is a function to add/replace an attribute of the selected html. jquers uses a concept known *** css selectors. If you want to replace the whole html you would probably use this function: http://api.jquery.com/replaceWith/ It would look like more or less like this to replace the whole body: $('body').replaceWith('new body'); But that is a bad idea, you would rather want to make your changes as small and specific as possible to have the mod stay compatible after patches. I.e. if you only tell it to add a new element after a specific other element it will keep just working as long as that other element is not patched away. If you tell it to replace the whole html instead it is less flexible and you will delete all changes that future patches might bring, making your mod break future patch changes.
ok, also would Code: 'live_game': [ '../../mods/changemod/changemod.js' 'scene_specific_live_game.css', 'scene_specific_live_game_alpha.js' (this file would have the changes to the html) work? In the readme they added "scene_specific_" must this be put in the ui_mod_list.js or was it just to say that live_game.css must be in a "live_game" subdirectory? does this mean I must write Code: live_game_live_game.css ?
I would write it like this: Code: 'live_game': [ '../../mods/changemod/changemod.js' '../../mods/changemod/mycss.css', '../../mods/changemod/htmlediting.js', ] I have not tried this with css, but I know this tells the game to load the js files directly after the live_game.js, in fact there is some js code in the live_game.js and probably all other js files that loads this, it is somewhere at the end of the live_game.js. The names of the files don't matter, just make sure to not forget the ../../mods/xxx/ to go to the directory of your mod.
I wrote up a reference for this system; if there's anything that should be added, let me know: viewtopic.php?f=72&t=50277
As someone who has done consulting for front end web page performance, I shudder to think of what you guys are going to be creating with this! I'm experiencing enough lag already without UI mods going rogue. I'd better at least throw some basic performance best practices your way before things get out of hand. I don't know what sort of HTML/JS/CSS engine is being used here, assuming webkit, but these best practices will probably apply. Avoid inefficient jQuery Selectors! Use the closest persistent ID to look up an element instead of just using a class. slower: Code: $('.className') faster: Code: $('#someAncestor').find('.className') Cache your jQuery selections People do this all the time and it looks so stupid: Code: $('.myFirstClassName').hide(); $('.myFirstClassName').append('YOYOYO'); $('.myFirstClassName').show(); Cache that selection: Code: var $myFirstClass = $('.myFirstClassName'); $myFirstClass.hide(); $myFirstClass.append('YOYOYO'); $myFirstClass.show(); jQuery implements chaining, meaning it returns a reference of itself from any function that isn't a getter, so you can achieve the same as above by doing this: Code: $('#someAncestor').find('.className').hide().append('YOYOYO').show(); Pseudo Selectors and Attribute Selectors are slow super slow: Code: $('.overlay:hidden'); super slow: Code: $('select[name=myAwesomeSelect]'); Finding elements by ID is fastest fastest way to select something: Code: $('#myUniqueElement'); possibly slightly faster, but don't bother: Code: $(document.getElementById('myUniqueElement')); slower: Code: $('#myUniqueElement').find('#myOtherUniqueElement') Once again, Cache jQuery Selections, even for elements with an ID This makes your code easier to maintain, too. Code: var $myElement = $('#myUniqueElement'); $myElement.addClass('open'); Remember that jQuery methods return the current jQuery selection: Code: var $myElement = $('#myUniqueElement').addClass('open'); $myElement.removeClass('open'); MINIMIZE DOM (html elements) MANIPULATION This is probably the most important one alongside using efficient selectors. Accessing and modifying the DOM (Document Object Model) is very expensive. Avoid this: Code: var container = $(‘#container’); for (var i=0, length = someArray.length; i < length; i++) { // Modifies the DOM on each iteration of the loop container.append(‘<li>…</li>'); } Create your whole HTML string and append it all at once, it is much faster: Code: var container = $(‘#container’); var markupString = ‘’; for(var i=0, length = someArray.length; i < length; i++) { markupString += ‘<li>…</li>’; } // Only modifies the DOM once container.append(markupString); Event Binding - Use Delegate If you are trying to capture mouse and keyboard events you will need to bind callback functions to them. If you need to bind a callback to more than one element, such as all the items in a list, use event delegation. It allows only one event to be bound to some ancestor element and it interprets what the original target of the event was and fires your callbacks for only those target elements. In the example below, let's say there are 20 'a' tags in #myList. The first method would bind 1 callback for each 'a' tag. The second method binds 1 callback to the #myList that will fire whenever any of those 'a' tags are clicked. slower: Code: var bigList = $('#myList'); bigList.find('a').bind('click', function() { /* do something */ }); faster: Code: bigList.delegate('click', 'a', function() { /* do something */ }); Also, don't just delegate everything to the document or the body. Delegate to the closest persistent ancestor. Don't mix CSS with JavaScript It's slower and less organized, amateur hour ****: Code: $('#foo').attr('style', 'color:blue'); $('#foo').css('color', 'blue'); Use styles from your CSS and apply them with classes: Code: $('#foo').addClass('myCoolBlueStyle'); Optimize Browser Layout Calculation Not sure how much this applies to PA but I assume it does. The browser has to calculate layout and reflow all the elements on the page when you do certain things like set styles and read styles. slower: Code: elementA.className = "a-style"; var heightA = elementA.offsetHeight; // layout calculated elementB.className = "b-style"; // invalidates the layout var heightB = elementB.offsetHeight; // layout calculated again faster: Code: elementA.className = "a-style"; elementB.className = "b-style"; var heightA = elementA.offsetHeight; // layout calculated var heightB = elementB.offsetHeight; // layout is up-to-date I hope you feel sufficiently stupid and afraid now :twisted: Let me know if you have any questions. I'll try to get into UI modding and maybe throw around some more tips here :ugeek:
It indeed is webkit based. Cool stuff, definitely an interesting read especially for people like me who come from a non webdevelopment background. However I am not sure how much it actually applies to PA, as PA uses knockout to bind pretty much all values that update a lot. I have no idea how knockout does it, but I assume they have competent developers. So the only jquery usage in mods should (I think) be in the initialization to modify the game's html a bit. That is executed exactly once when the UI loads. So while your tips are pretty interesting to read they will probably only improve the loading time of the mod by a few ms.
I hereby nominate rgrwkmn for an amazing first post award. I don't do web stuff (more of an embedded boy myself), but tutorials like that are exactly what I'd need to get into it.
Thanks rgrwkmn! definitely learned a few new things there. Another thing I like to do which apparently improves performance is to include the tag name in the selector. Code: $mySpans = $('span.myclass'); I remember reading this particularly helps older versions of IE. Maybe this isn't such an issue anymore, I'm curious to know if you think this is still a good thing to do or not?
Good call oxide246, can't believe I forgot to throw that one in there. With webkit I don't think that makes a difference anymore because it has document.getElementsByClassName natively. Thank the computer lords that you guys are coding for a single "browser" and don't have to deal with IE7/8, but pay tribute to the code gremlins early and often anyway. You won't have to worry about most of this stuff if you're making small tweaks since the interface is already built with Knockout, but I'd be really surprised if larger features and enhancements didn't use any extra jQuery DOM traversal and manipulation within the Knockout models. The fact that jQuery is included at all is proof of that. BTW, HELLO! :mrgreen: :mrgreen: :mrgreen:
HELLO! Pleased to meet you! Nice to see a fresh modder "face". Come, stay a while, we have tea and biscuits