Jump to content
New account registrations are disabed. This website is now an archive. Read more here.

DoubleX

Member
  • Content Count

    81
  • Joined

  • Last visited

  • Days Won

    3

Everything posted by DoubleX

  1. Note While this plugin's already fully functional, there are still many more modules to be implemented, so the feature set isn't complete yet. Purpose To be the most flexible, performant and powerful ATB system framework with the greatest amount of freedom while being user-friendly Introduction * 1. This plugin aims to be the most flexible, performant and powerful * ATB system with the greatest amount of freedom for users to fulfill * as many functional needs as they want in as many ways as they want * 2. You may want to treat this as a nano ATB framework as part of the * system's written by you via parameters/configurations/notetags/calls * 3. Almost every parameters and notetags can be written as direct * JavaScript, thus giving you the maximum amount of control over them * 4. (VERY ADVANCED)You can even change most of those JavaScript codes * written by you on the fly(and let your players do so with a system * settings plugin), but you should only do so if you really know what * you're truly doing Video Games using this plugin None so far Finished Modules Addressed Foreign Plugins Upcoming Modules Possibly Upcoming Modules Todo Inherited Behaviors From The Default RMMV Battle System Current Technical Limitations Author Notes FAQ Prerequisites Terms Of Use Instructions Contributors Changelog Demo
  2. Purpose Lets you run some codes set by your notetags on some important state timings Introduction * 1. This plugin lets you use notetags to set what happens when an * action's just executed, and different cases like miss, evade, counter * attack, magic reflection, critical hit, normal execution, substitute, * right before starting to execute actions, and right after finished * executing the actions, can have different notetags * 2. You're expected to write JavaScript codes directly, as there are so * much possibilities that most of them are just impossible to be * covered by this plugin itself, so this plugin just lets you write * JavaScript codes that are executed on some important timings Video Games using this plugin None so far Parameters Notetags Script Calls Plugin Commands Prerequisites Plugins: 1. DoubleX RMMZ Enhanced Codebase Abilities: 1. Some RMMV plugin development proficiency (Basic knowledge on what RMMV plugin development does in general with several easy, simple and small plugins written without nontrivial bugs up to 1000 LoC scale but still being inexperienced) Terms Of Use Instructions Contributors Changelog Download Link
  3. Just given a positive rating on Steam, even though my feeling on MZ's somehow mixed, and my positive just slightly wins over my negative(but I still believes that MZ will get better and better over time) 🙂

  4. Purpose Lets you run some codes set by your notetags on some important state timings Introduction * 1. This plugin lets you use notetags to set what happens when a state's * added/removed/expired/turn's updated/turn's reset on the battler * involved * 2. You're expected to write JavaScript codes directly, as there are so * much possibilities that most of them are just impossible to be * covered by this plugin itself, so this plugin just lets you write * JavaScript codes that are executed on some important timings Video Games using this plugin None so far Parameters Notetags Script Calls Plugin Commands Prerequisites Plugins: 1. DoubleX RMMZ Enhanced Codebase Abilities: 1. Some RMMV plugin development proficiency (Basic knowledge on what RMMV plugin development does in general with several easy, simple and small plugins written without nontrivial bugs up to 1000 LoC scale but still being inexperienced) Terms Of Use Instructions Contributors Changelog Download Link
  5. Purpose Lets you edit some database data on the fly and such edits will be saved Introduction * 1. This plugins lets you change some database data on the fly, and those * changes will be saved in save files * 2. Changing too many database data in the same save can lead to the save * file being too big, so only make absolutely necessary changes * 3. This plugin doesn't work with dynamic map data, and I've no plans to * support this, as it's all too complicated and convoluted to make it * work well without creating even greater troubles, like the game file * being too big and map reload issues * 4. CHANGING DATA ON THE FLY SHOULD NEVER BE TAKEN LIGHTLY, SO THIS * PLUGIN'S SPECIFICALLY DESIGNED TO NOT HAVE RMMZ BEGINNERS IN MIND Video Games using this plugin None so far Script Calls Prerequisites Plugins: 1. DoubleX RMMZ Enhanced Codebase Abilities: 2. Little RMMZ plugin development proficiency (Elementary Javascript exposures being able to write beginner codes up to 300LoC scale) Terms Of Use Contributors Changelog Download Link
  6. Updates * { codebase: "1.0.0", plugin: "v1.01a" }(2020 Aug 28 GMT 0100): * 1. Added the following notetags - * - memWithAnyUsableSkill * - memWithAllUsableSkills * - memWithoutAnyUsableSkill * - memWithoutAllUsableSkills Note that you've to update DoubleX RMMZ Unit Filters to v1.01a as well
  7. Tried to use ES6 Map to store some battler properties, and while it's nice by accepting anything as key(and MDN claims that ES6 Map's faster as well), those properties are all gone upon load, due to Map not being serializable upon save. Now I just feel dumb on one hand, and hope that I'll be the only stupid noob making this beginner mistake on the other 🙂

  8. Purpose Lets you set some skills/items to have battler and skill/item cooldowns Introduction * 1. This plugins lets you set 2 kinds of skill/item cooldowns: * - Skill/Item cooldown - The number of turns(battle turn in turn based * individual turn in TPBS) needed for the * skill/item to cooldown before it becomes * usable again * - Battler cooldown - The number of turns(battle turn in turn based * individual turn in TPBS) needed for the battler * just executed the skill/item to cooldown before * that battler can input actions again * 2. If the skill/item cooldown is 1 turn, it means battlers with multiple * action slots can only input that skill/item once instead of as many * as the action slots allow * If the battler cooldown is negative, it means the TPB bar charging * value will be positive instead of 0 right after executing the * skill/item(So a -1 battler cooldown means the battler will become * able to input actions again right after executing such skills/items) * 3. When updating the battler individual turn count in TPBS, the decimal * parts of the battler will be discarded, but those parts will still be * used when actually increasing the time needed for that battler to * become able to input actions again * In the turn based battle system, the decimal parts of the battler * cooldown counts as 1 turn * The decimal parts of the final skill/item cooldown value will be * discarded * 4. Skill/item cooldown can be set to apply outside battles as well * Skill/item cooldown won't be updated when the battler has fully * charged the TPBS bar Video Games using this plugin None so far Parameters Notetags Script Calls Plugin Commands Prerequisites Terms Of Use Instructions Contributors Changelog Download Link
  9. Disclaimer: This post doesn't mean that I support the idea of releasing plugins with obfuscated implementation codes, and in fact I'm personally against it in most cases(but my feeling on this isn't the point of this post at all), it's just that there might be some really extreme and rare cases making this approach at least a considerable backup plan for some plugin developers(but making this move should still never be taken lightly). I've obfuscated 1 of my RMMV plugins(an advanced complex battle system plugin with way more than 3K LoC of implementation codes), which isn't published yet and is an older version, and I've posted it in my github repository: RMMV Plugin Obfuscation Test Please feel free to deobfuscate this plugin(and please submit a pull request with your fully deobfuscated version), as it's a very, very old and outdated version anyway, and the current version I'm working on already has much, much more contents than that highly deprecated version(and it's still far from being complete). I've tried to deobfuscate this obfuscated version using several online deobfuscators, but none of them can fully deobfuscate it, even though they do help quite a bit, so I don' think deobfuscating the whole thing all at once will be an easy, simple and small task(unless you're a deobsucation experts of course). The supposed workflow of revealing parts of codes is as follows: 1. Users asks plugin developers to make some tunes and tweaks on the plugins but failed to do so(goes to 2) 2. If such tunes and tweaks can be done by editing parameters/configurations/notetags/script calls/plugin commands, then teach those users to do so, otherwise go to 3 3. If these tunes and tweaks are(after considering all factors) worthwhile to be supported by the plugins directly, then upgrade the plugins to support them(or at least announce such plans), otherwise go to 4 4. If these tunes and tweaks can be independent from the plugins, then teach those users to make those workarounds, otherwise go to 5 5. Gives the minimum amount of relevant codes(minified then deminified) to the users, and ensures those codes won't work without the rest of the plugins Then scammers trying to steal the whole thing will have to ask for tunes and tweaks for tons of different plugin parts very quickly, and this will make their true nature very obvious, especially when it's clear that they aren't even taking time to actually implement their tunes and tweaks with the help of those revealed codes(plugin developers can also setup a policy to restrict the same user to ask, say, only 1 small group of revealed code parts per day per plugin or even per week per plugin). I've also added 3 examples on some users requesting some parts of the plugins to be tuned and tweaked, even though I know that real plugins really using this approach will have way more than 3 groups of revealed code parts: Add Delay Between Becoming Able To Act And Actually Inputting Actions Change ATB Bar Window Opacity And Padding Set The Actor And Enemy Starting ATB Values I've tried to use them to help further deobfuscating what's already deobfuscated by online deobfuscators, but I can't even recognize which parts of the still highly deobfuscated implementation codes correspond to the revealed original versions(being minified then deminified before being revealed), so again, I feel that deobfuscating the whole thing will still be complicated and convoluted, even with the help of these revealed codes(please exclude the deobfuscation experts here). Please STRICTLY FOCUS ON THESE 2 ASPECTS: 1. Does this approach let users effectively and efficiently tune and tweak the parts of the plugins they wanted, and what obstacles will they face if the reality isn't as ideal as it should be(possibly other than the cheap one time payment/periodic subscription needed to have such accesses)? 2. Will this approach help a scammer deobfuscate the whole plugin implementation codes, or at least enough parts of them to make a standalone plugin that can make profits way more than what those scammers costed(I'm specifically excluding the case of a highly coordinated team of scammers with many teammates)? But not anything involving the pros and cons/rights and wrongs of obfuscating plugin implementation codes, because I've made this example as a little experiment to test whether this idea can indeed let users tune and tweak the parts of the plugins they want while still stopping scammers from stealing plugins, and I don't want this post to be severely derailed or even closed
  10. Purpose Lets you control some skills/items target selection AI behaviors by notetags Introduction * 1. The default RMMZ only lets you control the targeting AI by tgr, which * is probabilistic rather than deterministic * 2. This plugin lets you use some notetags on actors, classes, learnt * skills/skills in action list, usable skills, posessed items, usable * items, weapons, armors, enemies and states, to control the targeting * AI by specifying some deterministic target filters * 3. Targets passing the filters will still be affected by the * probabilitic tgr when there are more than 1 such targets * 4. This plugin only works for skills/items having 1 target, and it * doesn't work well 1 random target either * 5. If a filter causes no target to pass, that filter will be discarded * upon such use cases Video Games using this plugin None so far Parameters Notetags Script Calls Plugin Commands Author Notes Prerequisites Terms Of Use Instructions Contributors Changelog Download Link
  11. Just accepted a full time job offer after being jobless for so long 🙂
    But it'd also mean I won't have much time to work with MV or MZ 😞

    1. Marked

      Marked

      Congratulations! What doing?

  12. Purpose Lets you use script calls to filter unit members with less codes and eventing Introduction * 1. Without any plugin, getting a member with specific conditions * relative to the belonging unit, like finding the party member with * the highest amount of hp, demands relatively heavy event setups, * even with the aid of common events, which boost event reusability. * 2. With this plugin, the same can be done using several easy, simple * and small script calls instead of writing several common events * from scratch, thus further improving effectiveness and efficiency. Games using this plugin None so far Script Calls Author Notes Prerequisites Terms Of Use Contributors Changelog Download Link
  13. Just checked the reactions to MZ on Steam and it seems to me that it's mixed in both reviews(right now 98 reviews with 63% being positive) and discussions.
    On the other hand, MV got about 80% positive reviews upon launch(108 positive and 27 negative), and now MV is rated as very positive(91% positive with 3515 reviews), so it seems to me that it'd be hard for MZ to be ever rated as overwhelming positive 😞

    image.gif

  14. Just checked the VisuStella MZ plugins again and there are already 22(or 23) or them, as if MZ's already released(so I don't know how they can be this effective/efficient). It seems to me that those guys are really burning their lives to let us use tons of plugins one day 1 🙂

  15. I've just checked the VisuStella Plugins for MZ and there are already 11 plugins. The Yanfly team(perhaps a big one?) really works so effectively and efficiently that some MZ users can likely use tons of Yanfly plugins right after MZ's released (still 13 days from now). I wonder if any other plugin developer(or team) will try to compete with Yanfly this time, no matter how slim the chance to win is 🙂

    1. Marked

      Marked

      The most important thing I guess is lots of high quality plugins out there for the community to use

    2. DoubleX

      DoubleX

      And sometimes healthy competition can exactly lead to this 🙂

  16. I'm slightly disappointed that the MZ codebase still uses core.js, managers.js, objects.js, scenes.js, sprites.js and windows.js instead of core/Bitmap.js, core/Graphics.js, managers/AudioManager.js, managers/DataManager.js, etc...

  17. There's a confirmed case of Covid-19 who lives in the same building as mine, but fortunately I've been staying at home all the time for more than a week already, even though I might be ordered to enter a quarantine center any time soon...

  18. With ES6 classes, trying to extend the parent class in plugins can lead to its children still using the old parent codes. For instance, you can place this in a JavaScript sandbox and verify the console output yourselves: So normally, you'll still have to directly type prototypes: But I wanted to offer an alternative for those not being familiar with ES5 or avoiding prototypes like a plague despite the fact that many RMMZ codes are written that way, and this is what I've come up with: I've tested that it works for adding new instance variables and prototype methods in base classes, and extending and overriding existing prototype methods there(you can place the linked snippet into a JavaScript sandbox and verify the console output yourselves). While I failed to inherit the changes in the static functions of the base classes from plugins as well, this can be easily mitigated by using BaseClass.staticFunc.call(this) instead of super.staticFunc(). Basically, the essence of the issue when aliasing ES6 class inheritance without direct prototyping is this: 1. The original child class inherits the original base class 2. A plugin extends the original base class to alias some of its prototype methods 3. The child class still inherits the original base class So to solve this, simply store the linkage between the child class and the base class right after creating that child class, then points the parent of the child class to the extended base class right after extending it. As for the static functions in classes, while I tried to use the linkage to let the exiting children class use the new static functions from the extended parent class, I failed to cover the case for aliasing existing parent class static functions, because it's just impossible: 1. The super in the static functions of the child class always points to the original parent class 2. The super in the static functions of the extened parent class always points to the original parent class 3. The super in the static functions of the child class's supposed to always point to the extended parent class Clearly, combining 1 and 2 will contradict with 3, which is the goal I've trying to achieve. For those not being familiar with ES5 or avoiding prototypes like a plague, I hope using ExtendedClassAlias won't be too demanding for you, as all you need to do is sticking to these: /* * Do these 2 additional things when using ES6 class inheritance aliasing * without directly typing prototypes: * 1. Add the following code right below a new class inheriting another one: * - ExtendedClassAlias.inherit(Klass); * Where Klass is the new class inheriting another one * 2. Add the following code right below extending an existing class as a way * to alias its methods: * - ExtendedClassAlias.updateClass(Klass); * Where Klass is the existing class being extended as a way to alias its * methods * Right now it doesn't work well with inheriting static functions in classes, * so those in children classes should use ParentClass.staticFunc.call(this) * instead of super.staticFunc() */ P.S.: I've spent almost 24 hours on this and I enjoyed the process a lot, even though this might not be practical enough to be used in MZ :)
  19. That's my fault that I forgot to mention that all these are for Es5 only, as RMMV uses ES5 :)
  20. I've made the following changes: 1. Changed the name of the 2nd pattern from Private Function to Composable Revealing Module. This change leads to a better indication that the 2nd pattern's a special case of the revealing module pattern. 2. Changed the name of the 3rd pattern from Protected Class to Parasitic Inheritance, which supports multiple inheritance. I feel very sorry for not realizing that I've just reinvented the wheel.
  21. Disclaimer: This topic's to provide some extra choices for those needing/wanting some protections from being able to access anything from anywhere while still allowing inheritance. As sometimes it completely makes sense for keeping everything public, these choices are entirely optional. Using any of them without any solid reason's just violating KISS for nothing, which should be avoided. This topic aims to use an easy, simple and small scenario to briefly cover some patterns illustrating Javascript ES5 access modifiers and inheritance. Bear in mind that those patterns can quickly become antipatterns if they're used without at least thinking twice. So you're assumed to have at least a basic knowledge on writing Javascript and have written at least several easy, simple and small js files which work without nontrivial bugs. The focus of this topic corresponds to 'Remembering' in the new version of the Bloom's Taxonomy. Please note that the following concepts will be used: 1. Final - Functions/variables that can't be redefined after their initial definitions 2. Private - Functions/variables only accessible by the enclosed class/instance/function 3. Protected - Functions/variables only accessible by the enclosed class/instance and their subclasses/instances 4. Public - Functions/variables accessible from anywhere 5. Static - Functions/variables shared by all instances of the same class On a side note: Strictly speaking, it's nearly impossible to always ensure an object method will always remain private/protected, as advanced programmers can, after thoroughly comprehended the object's API, reconstruct the whole object while preserving its external behaviors, even though its internal states will most likely be lost that way. However, it's such an unreasonably tedious task for nontrivial objects having nontrivial inheritance hierarchies that only truly trivial and/or valuable objects really worth such tremendous efforts. Therefore, let's just regard them as edge cases and move on. Warmup Recall that the only scoping instrument in Javascript is function, which goes hand in hand with closures. They're the very basis of emulating access modifiers in Javascript. Without functions, anything's accessible from anywhere in Javascript, meaning that everything's public. On the other hand, using the function scope lets one declare variables inside a function, making them only accessible within that function. These variables are thus local to that function, making them private. When it comes to emulating protected in Javascript, one must first bear in mind that protected only makes sense when there's inheritance, which nearly always involve this. Inheritance in Javascript are prototype based, which is different from almost(if not just) all the other programming languages supporting inheritance. Nevertheless, protected in prototypical inheritance can still mean variables only accessible by the prototype defining them and all the other prototypes having that prototype as their parent. Final in Javascript can be regarded as const in ES6. Static in Javascript can mean variables shared by all objects inheriting from the same prototype. With all these in mind, let's get started. Situation Suppose we're to create a lockable container that can store a single object as the encapsulated contents and can lock itself to control its access. This object's API consists of the following: 1. isLocked() - Check whether the lockable container is locked 2. lock() - Locks the lockable container 3. tryPutContents(contents) - Tries to put the contents to the lockable container It'll succeed only if the lockable container's unlocked and empty, otherwise it'll show the reasons of failure 4. tryTakeContents - Tries to take the contents inside the lockable container If it succeeds, the lockable container will become empty It'll succeed only if the lockable container's unlocked and not empty, otherwise it'll show the reasons of failure 5. tryUnlock(key) - Tries to unlock the lockable container It'll succeed only if the key's correct, otherwise it'll show the reasons of failure Now the lockable container's easy, simple and small, but some users might fear that eventually an outsider can pass the correct key to unlock the lockable container, take its contents, put something malicious inside, and then lock it again. The only way to know the lockable container's compromised is to inspect its contents, which can itself lead to disastrous results. So let's create a counted lockable container, which inherits from a lockable container, that counts the number of key mismatches, and permanently locks itself if the counter reaches a preset maximum. This object has a new method in its API: 6. keyMismatchCount - Returns the number of key mismatches Now the counted lockable container's noticeably safer, but it has a new problem - Given enough key mismatches, it won't be able to be unlocked anymore, even with the correct key. So let's create a resettable counted lockable container, which inherits from a counted lockable container, that lets users to reset the key mismatch count. This object has a new method in its API: 7. tryResetKeyMismatchCount - Tries to reset the key mismatch count to 0 It'll succeed only if the lockable container's unlocked, otherwise it'll show the reasons of failure Now the resettable counted lockable container's also noticeably safer from being not being able to be unlocked even with the correct key, but some users might just want the reset to take place automatically whenever becomes unlocked. So let's create a resetting counted lockable container, which inherits from a counted lockable container, that automatically resets the key mismatch count to 0 whenever it becomes unlocked. With the context in place, the following patterns can finally come into play. All files implementing all these patterns, as well as their unit tests and integration tests, can be found in my Lockable-Container github. You're highly encouraged and recommended to read the simplest thing that could possibly work first, which is demonstrated by lockableContainerKiss.js. Wrapped Prototype It's demonstrated by the following js files: 1. lockableContainerPrototype.js 2. countedLockableContainerPrototype.js 3. resettableCountedLockableContainerPrototype.js 4. resettingCountedLockableContainerPrototype.js This pattern simply declares the function in the global scope, and everything else, including its prototype, inside an immediately-invoked function expression. Note that: 1. Anything declared inside the anonymous function but not in the prototype's effectively private static final. 2. This pattern can lead to some duplicated private static constants if it's better than making them instance variables. 3. This pattern doesn't provide private instance variable nor protected function/variable. 4. This pattern causes every subclass to choose its direct parent but not vice versa. This pattern also generally leads to extremely fast object creation codes using very little memory, and it's very scalable in terms of performance when it comes to extending the prototype chain. It's shown by the below benchmark: // All performance tests are done in Google Chrome 56.0.2924.87 and i3-2330M // Base: Roughly 7MB // MOE: < 1MB; < 1 sec // Total: Roughly 25MB for 100,000 times memoryTest.push(new LockableContainer(_key1)); memoryTest.push(new LockableContainer(_key2)); // // Total: Roughly 25MB for 100,000 times memoryTest.push(new ResettableCountedLockableContainer(_key1, _maxKeyMismatchCount1)); memoryTest.push(new ResettableCountedLockableContainer(_key2, _maxKeyMismatchCount2)); // // Total: Roughly 25MB for 100,000 times memoryTest.push(new ResettingCountedLockableContainer(_key1, _maxKeyMismatchCount1)); memoryTest.push(new ResettingCountedLockableContainer(_key2, _maxKeyMismatchCount2)); // // Roughly 15 seconds for 100,000,000 times new LockableContainer(_key1); new LockableContainer(_key2); // // Roughly 17 seconds for 100,000,000 times new ResettableCountedLockableContainer(_key1, _maxKeyMismatchCount1); new ResettableCountedLockableContainer(_key2, _maxKeyMismatchCount2); // // Roughly 17 seconds for 100,000,000 times new ResettingCountedLockableContainer(_key1, _maxKeyMismatchCount1); new ResettingCountedLockableContainer(_key2, _maxKeyMismatchCount2); // So this pattern can be desirable when: 1. You don't need/want private/protected instance variables. 2. You don't need/want protected function. 3. You don't mind having duplicated private static constants. 4. You need/want extremely performant object creation codes that scale very well. 5. You need/want/don't mind every subclass to choose its direct parent but not vice versa. In the case of the lockable container, this pattern doesn't even try to stop outsiders from directly accessing its internals, thus letting them accessing its correct key and contents, making the lockable container meaningless, pointless and useless. This's proved by the integration test as this pattern doesn't pass it. Therefore, this pattern isn't suitable for its implementations at all. Composable Revealing Module It's demonstrated by the following js files: 1. lockableContainerFunction.js 2. immutableResettableCountedLockableContainerFunction.js 2. immutableResettableCountedLockableContainerFunction.js 3. mutableResettableCountedLockableContainerFunction.js This pattern simply wraps everything into a named function that's called rather than instantiated. It's a special case of the revealing module pattern, as it emphasize using a module as an object rather than a function, with that object effectively adhering to SRP and OCP by keeping it small and composable. Note that: 1. Anything not returned by the function's private. 2. This pattern can lead to some duplicated private functions/variables. 3. This pattern doesn't provide protected functions/variables. 4. This pattern uses the decorator pattern to extend objects. 5. There's no static function/variable at all. This pattern generally leads to quite some fast object creation codes when thew object's not extended but those codes uses quite a lot of memory, and it's not scalable at all in terms of performance when the object's extended. It's shown by the below benchmark: // All performance tests are done in Google Chrome 56.0.2924.87 and i3-2330M // Base: Roughly 7MB // MOE: < 1MB; < 1 sec // Total: Roughly 151MB for 100,000 times memoryTest.push(LockableContainerFunction(_key1)); memoryTest.push(LockableContainerFunction(_key2)); // // Total: Roughly 240MB for 100,000 times memoryTest.push(MutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key1), _maxKeyMismatchCount1, true, false)); memoryTest.push(MutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key2), _maxKeyMismatchCount2, false, true)); // // Total: Roughly 265MB for 100,000 times memoryTest.push(ImmutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key1), _maxKeyMismatchCount1, true, false)); memoryTest.push(ImmutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key2), _maxKeyMismatchCount2, false, true)); // // Roughly 48 seconds for 100,000,000 times LockableContainerFunction(_key1); LockableContainerFunction(_key2); // // Roughly 91 seconds for 100,000,000 times ImmutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key1), _maxKeyMismatchCount1, isResettable, isResetting); ImmutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key2), _maxKeyMismatchCount2, isResettable, isResetting); // // Roughly 17 seconds for 1,000,000 times MutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key1), _maxKeyMismatchCount1, isResettable, isResetting); MutableResettableCountedLockableContainerFunction( LockableContainerFunction(_key2), _maxKeyMismatchCount2, isResettable, isResetting); // Also, this pattern uses composition instead of inheritance to extend objects. As both a resettable counted lockable container and resetting counted lockable container needs to write the private/protected instance variable counting the number of key mismatches in a counted lockable container, the functionalities of the formers need to be included by the latter in order to extend objects only based on their APIs. So this pattern can be desirable when: 1. You don't need/want protected function/variables. 2. You don't mind having duplicated private functions/variables. 3. You don't need/want to extend the objects at all or you don't mind suffering from poor scalability. 4. You need/want extremely fast object creation codes, even at the cost of high memory usage, and you don't need/want to extend the objects at all. 5. You need/want private functions/variables. 6. You favor composition over inheritance. In the case of the lockable container, this pattern can effectively, efficiently and reliably stop outsiders from directly accessing its internals, at least when they're not using tons of ridiculously insane hacks. Unless exceptionally large amount of lockable containers can exist at the same time, this pattern should be fine. Parasitic Inheritance It's demonstrated by the following js files: 1. lockableContainerClass.js 2. countedLockableContainerClass.js 3. resettablecountedLockableContainerClass.js 4. resettingcountedLockableContainerClass.js This pattern simply wraps everything into a named function that's called by subclasses and instantiated to create new objects. Note that: 1. Anything not attached to the function's this pointer's private. 2. This pattern can lead to some duplicated private functions/variables. 3. This pattern provides loosely but not strictly protected functions/variables. 4. There's no static function/variable at all. 5. This pattern causes every subclass to choose its direct parent but not vice versa. 6. This pattern supports multiple inheritance, albeit with the diamond problem in the way that the last inherited parent dominates. 7. Subclasses can expose the protected functions/variables into public ones by using accessors. This pattern generally leads to considerably slow codes that uses quite some memory, and it's not that scalable in terms of performance when the object's extended. It's shown by the below benchmark: // All performance tests are done in Google Chrome 56.0.2924.87 and i3-2330M // Base: Roughly 7MB // MOE: < 1MB; < 1 sec // Total: Roughly 116MB for 100,000 times memoryTest.push(new LockableContainerClass(_key1, _maxKeyMismatchCount1)); memoryTest.push(new LockableContainerClass(_key2, _maxKeyMismatchCount2)); // // Total: Roughly 217MB for 100,000 times memoryTest.push(new ResettableCountedLockableContainerClass(_key1, _maxKeyMismatchCount1)); memoryTest.push(new ResettableCountedLockableContainerClass(_key2, _maxKeyMismatchCount2)); // // Total: Roughly 212MB for 100,000 times memoryTest.push(new ResettingCountedLockableContainerClass(_key1, _maxKeyMismatchCount1)); memoryTest.push(new ResettingCountedLockableContainerClass(_key2, _maxKeyMismatchCount2)); // // Roughly 74 seconds for 10,000,000 times new LockableContainerClass(_key1); new LockableContainerClass(_key2); // // Roughly 12 seconds for 1,000,000 times new ResettableCountedLockableContainerClass(_key1, _maxKeyMismatchCount1); new ResettableCountedLockableContainerClass(_key2, _maxKeyMismatchCount2); // // Roughly 13 seconds for 1,000,000 times new ResettingCountedLockableContainerClass(_key1, _maxKeyMismatchCount1); new ResettingCountedLockableContainerClass(_key2, _maxKeyMismatchCount2); // Moreover, the protected functions/variables in this pattern can be compromised without too much troubles, as demonstrated by these files: 1. unprotectedLockableContainerClass.js 2. unprotectedCountedLockableContainerClass.js A serious side effect is that the integrity of the whole object can be compromised, causing it to fail to work correctly. In this case, such a jailbreak causes the compromised object to use the new versions of the protected functions/variables, while the original codes are still using their old versions. That's why this pattern doesn't provide strictly protected functions/variables, only loosely protected ones, as it doesn't stop them to be compromised while still letting them express themselves and function as protected ones as long as they're not compromised. So this pattern can be desirable when: 1. You need/want private and protected functions/variables. 2. You don't mind having duplicated private constants. 3. You don't mind extremely unperformant object creation codes that doesn't scale well. 4. You need/want/don't mind every subclass to choose its direct parent but not vice versa. 5. You don't mind having protected functions/variables compromised, which can compromise the integrity of the whole object as well. 6. You need/want multiple inheritance. 7. You need/want/don't mind subclasses turning the protected functions/variables into public ones. In the case of the lockable container, whether this pattern's desirable 's mainly determined by the risk of the objects being compromised, which depends on actual use cases. Reversed Inheritance Hierarchy It's demonstrated by the following js files: 1. lockableContainerObject.js 2. countedLockableContainerObject.js 3. resettableCountedLockableContainerObject.js 4. resettingCountedLockableContainerObject.js This pattern simply wraps everything into a named function, including the declaration of all subclasses, that's instantiated to create new objects. Note that: 1. Anything not attached to the function's this pointer's private. 2. This pattern can lead to some duplicated private functions/variables. 3. This pattern provides strictly protected functions/variables, which are those attached to the function's this pointer but not returned by the function. 4. There's no static function/variable at all. 5. This pattern causes every parent to choose all its subclasses but not vice versa. This pattern generally leads to exceptionally slow object creation codes that uses a stunning amount of memory, and it's rather not scalable in terms of performance when the object's extended. It's shown by the below benchmark: // All performance tests are done in Google Chrome 56.0.2924.87 and i3-2330M // Base: Roughly 7MB // MOE: < 1MB; < 1 sec // Total: Roughly 210MB for 100,000 times memoryTest.push(new LockableContainerObject('LockableContainer', _key1)); memoryTest.push(new LockableContainerObject('LockableContainer', _key2)); // // Total: Roughly 336MB for 100,000 times memoryTest.push(new LockableContainerObject( 'ResettableCountedLockableContainer', _key1, _maxKeyMismatchCount1)); memoryTest.push(new LockableContainerObject( 'ResettableCountedLockableContainer', _key2, _maxKeyMismatchCount2)); // // Total: Roughly 329MB for 100,000 times memoryTest.push(new LockableContainerObject( 'ResettingCountedLockableContainer', _key1, _maxKeyMismatchCount1)); memoryTest.push(new LockableContainerObject( 'ResettingCountedLockableContainer', _key2, _maxKeyMismatchCount2)); // // Roughly 17 seconds for 1,000,000 times new LockableContainerObject('LockableContainer', _key1); new LockableContainerObject('LockableContainer', _key2); // // Roughly 31 seconds for 1,000,000 times new LockableContainerObject('ResettableCountedLockableContainer', _key1, _maxKeyMismatchCount1); new LockableContainerObject('ResettableCountedLockableContainer', _key2, _maxKeyMismatchCount2); // // Roughly 35 seconds for 1,000,000 times new LockableContainerObject('ResettingCountedLockableContainer', _key1, _maxKeyMismatchCount1); new LockableContainerObject('ResettingCountedLockableContainer', _key2, _maxKeyMismatchCount2); // Besides, the control in the inheritance hierarchy's reversed - Rather than letting subclasses to control which classes are their parents, this pattern lets parents to control which are their subclasses. This has the following implications: 1. In the normal inheritance hierarchy, anyone can create subclasses for any public class meant to be inherited; This pattern only lets foreign classes authorized by the classes in this pattern to be the latters' subclasses. 2. In the normal inheritance hierarchy, a class's protected functions/variables can risk being public if it's subclassed by classes exposing them; This pattern can prevent this from happening by strictly controlling the external behaviors of all subclasses. 3. If no one has access to the source code of a class using this pattern, it won't be able to be subclassed by any new class anymore. 4. This pattern isn't maintainable when the inheritance hierarchy becomes colossal, complex and convoluted. So this pattern can be desirable when: 1. You need/want private and protected functions/variables. 2. You don't mind having duplicated private constants. 3. You don't mind having excessively unperformant object creation codes that lacks scalability. 4. You need/want/don't mind having every parent to choose all its subclasses but not vice versa. 5. You don't mind leading to effectively final classes when no one has access to their source codes anymore. 6. You don't mind managing an unmaintainable mess with a colossal, complex and convoluted inheritance hierarchy. In the case of the lockable container, this pattern can effectively, efficiently and reliably stop outsiders from directly accessing its internals, at least when they're not using tons of ridiculously insane hacks. Unless exceptionally large amount of lockable containers can exist at the same time, this pattern should be fine. Reversed Prototype Chain It's demonstrated by the following js files: 1. lockableContainerFactory.js 2. countedLockableContainerFactory.js 3. resettableCountedLockableContainerFactory.js 4. resettingCountedLockableContainerFactory.js This pattern simply wraps everything into a named function, including the declaration of all subclasses and defintion of the prototype for creating objects, that's called once to return a function creating objects. Note that: 1. Anything not attached to the prototype's private static final function/variable. 2. This pattern can lead to some duplicated private static final functions/variables. 3. This pattern doesn't provide private instance variables. 4. This pattern provides strictly protected functions/variables, which are those attached to the function's this pointer but not returned by the function. 5. This pattern causes every parent to choose all its subclasses but not vice versa. 6. This pattern is largely based on the factory method pattern. 7. The prototype's created only once if this pattern's used properly. This pattern generally leads to slightly slow object creation codes that uses an acceptable amount of memory, and it's reasonably scalable in terms of performance when the object's extended. It's shown by the below benchmark: // All performance tests are done in Google Chrome 56.0.2924.87 and i3-2330M // Base: Roughly 7MB // MOE: < 1MB; < 1 sec // Total: Roughly 60MB for 100,000 times memoryTest.push(_createdLockableContainerFactory('LockableContainer', _key1)); memoryTest.push(_createdLockableContainerFactory('LockableContainer', _key2)); // // Total: Roughly 71MB for 100,000 times memoryTest.push(_createdLockableContainerFactory( 'ResettableCountedLockableContainer', _key1, _maxKeyMismatchCount1)); memoryTest.push(_createdLockableContainerFactory( 'ResettableCountedLockableContainer', _key2, _maxKeyMismatchCount2)); // // Total: Roughly 67MB for 100,000 times memoryTest.push(_createdLockableContainerFactory( 'ResettingCountedLockableContainer', _key1, _maxKeyMismatchCount1)); memoryTest.push(_createdLockableContainerFactory( 'ResettingCountedLockableContainer', _key2, _maxKeyMismatchCount2)); // // Roughly 21 seconds for 10,000,000 times _createdLockableContainerFactory('LockableContainer', _key1); _createdLockableContainerFactory('LockableContainer', _key2); // // Roughly 24 seconds for 10,000,000 times _createdLockableContainerFactory('ResettableCountedLockableContainer', _key1, _maxKeyMismatchCount1); _createdLockableContainerFactory('ResettableCountedLockableContainer', _key2, _maxKeyMismatchCount2); // // Roughly 25 seconds for 10,000,000 times _createdLockableContainerFactory('ResettingCountedLockableContainer', _key1, _maxKeyMismatchCount1); _createdLockableContainerFactory('ResettingCountedLockableContainer', _key2, _maxKeyMismatchCount2); // Similar to the Reversed Inheritance Hierarchy, the control in the inheritance hierarchy's reversed in this pattern as well, leading to the same implications. So this pattern can be desirable when: 1. You need/want protected functions/variables. 2. You don't need/want private instance variables. 3. You don't mind having duplicated private constants. 4. You don't mind having slightly unperformant object creation codes that's decently scalable. 5. You need/want/don't mind having every parent to choose all its subclasses but not vice versa. 6. You don't mind leading to effectively final classes when no one has access to their source codes anymore. 7. You don't mind managing an unmaintainable mess with a colossal, complex and convoluted inheritance hierarchy. In the case of the lockable container, this pattern can effectively, efficiently and reliably stop outsiders from directly accessing its internals, at least when they're not using tons of ridiculously insane hacks. Unless unbelivably large amount of lockable containers can exist at the same time, this pattern should be fine. Summary Wrapped Prototype simply declares the function in the global scope, and everything else, including its prototype, inside an immediately-invoked function expression. This pattern can be desirable when: 1. You don't need/want private/protected instance variables. 2. You don't need/want protected function. 3. You don't mind having duplicated private static constants. 4. You need/want extremely performant object creation codes that scale very well. 5. You need/want/don't mind every subclass to choose its direct parent but not vice versa. Composable Revealing Module simply wraps everything into a named function that's called rather than instantiated. This pattern can be desirable when: 1. You don't need/want protected function/variables. 2. You don't mind having duplicated private functions/variables. 3. You don't need/want to extend the objects at all or you don't mind suffering from poor scalability. 4. You need/want extremely fast object creation codes, even at the cost of high memory usage, and you don't need/want to extend the objects at all. 5. You need/want private functions/variables. 6. You favor composition over inheritance. Parasitic Inheritance simply wraps everything into a named function that's called by subclasses and instantiated to create new objects. This pattern can be desirable when: 1. You need/want private and protected functions/variables. 2. You don't mind having duplicated private constants. 3. You don't mind extremely unperformant object creation codes that doesn't scale well. 4. You need/want/don't mind every subclass to choose its direct parent but not vice versa. 5. You don't mind having protected functions/variables compromised, which can compromise the integrity of the whole object as well. 6. You need/want multiple inheritance. 7. You need/want/don't mind subclasses turning the protected functions/variables into public ones. Reversed Inheritance Hierarchy simply wraps everything into a named function, including the declaration of all subclasses, that's instantiated to create new objects. This pattern can be desirable when: 1. You need/want private and protected functions/variables. 2. You don't mind having duplicated private constants. 3. You don't mind having excessively unperformant object creation codes that lacks scalability. 4. You need/want/don't mind having every parent to choose all its subclasses but not vice versa. 5. You don't mind leading to effectively final classes when no one has access to their source codes anymore. 6. You don't mind managing an unmaintainable mess with a colossal, complex and convoluted inheritance hierarchy. Reversed Prototype Chain simply wraps everything into a named function, including the declaration of all subclasses and defintion of the prototype for creating objects, that's called once to return a function creating objects. This pattern can be desirable when: 1. You need/want protected functions/variables. 2. You don't need/want private instance variables. 3. You don't mind having duplicated private constants. 4. You don't mind having slightly unperformant object creation codes that's decently scalable. 5. You need/want/don't mind having every parent to choose all its subclasses but not vice versa. 6. You don't mind leading to effectively final classes when no one has access to their source codes anymore. 7. You don't mind managing an unmaintainable mess with a colossal, complex and convoluted inheritance hierarchy. I'm planning to open another topic to explain how these pattern works in details, which will lead to a solid understanding on using Javascript access modifers and inheritance. That's all for now. What do you think about these patterns? What do you think about Javascript access modifiers and inheritance in general? Let's drop your 2 cents here :)
  22. This topic aims to incite you to think about how incentive can be used to solve problems :) Let's begin by citing this video from Extra Credits: The Power Of Incentives - How Games Help Us Examine Our World Now my focus will be on the cobra example(1:09 - 1:35). Wikipedia has an article named Cobra effect specifically about this. (On a side note: In this cobra example, the government might want to just hire people with excellent salaries to work under it to reduce the number of cobras lol) Original Setup Alternative Setup So what do you think about using incentives to solve problems? What's your solution on the cobra example? How will you apply incentives to solve problems? Let's share your 2 cents here :P P.S.: There's probably no incentive system that will work everytime everywhere, instead it should be built specifically for the situation it's meant to be addressed and adapt accordingly as the situation changes.
  23. I wonder how many RMMV plugin users don't know that one does not simply change the filename of a RMMV plugin. Doing so could break that plugin entirely :)

  24. I've just read Yanfly's announcements regarding his/her health. I honestly think that, it's a bit unhealthy for the majority of the RMMV communities to rely on a single plugin developer. I feel that someone else has to step up. Doing so should do at least slightly more good than harm for Yanfly(for being less overburdened), those stepping up(for increased popularity), and the RMMV communities as a whole(for having more choices) :)

  25. First, let's cite what OODA is: - OODA loop - The Tao of Boyd: How to master the OODA Loop - Unlocking the power of Colonel John Boyd's OODA Loop After reading all those, you, as a game developer, might wonder: How can we use OODA to write battler AI? Let's start with exploring the following oversimplified version: (The below assumes that the battler AI are built to minimize the chance for the players to win. The OODA will be used slightly differently if that's not the battler AI's ultimate goal.) Observe To observe is to receive as many relevant info as accurately(including fake info detections by verifications) as possible. However, what can be observed by a battler? (For the sake of writing battler AI, issues with fake info can be safely ignored as all relevant info can always be accurately received by any RMVXA battler AI.) I think that any normal battler should be able to observe the opponents' current party/troop formations(like consisting of which actors/enemies), all their executed actions, and the allies' current statuses(like having which states/buffs/debuffs and how many hp/mp/tp). Sometimes it could, however, be unfair for a battler to be able to observe the opponents' current capabilities(like classes, equips, parameters, extra parameters, special parameters, level and skill lists) or carried items. For example, at the start of a battle, a boss facing a 6 actor party might be able to observe that all those actors are: - heavily geared towards dealing damage with significant sacrifice from every other battle aspect, and/or - mages only excelling at using magics, and/or - fighters only excelling at using physical moves, and/or - totally unresistant to some states/debuffs, and/or - drastically faster than that boss(in action battle system, active time battle, charge turn battle, tactical battle system, etc), and/or something else. Orient To orient is to use mental models to make sense of those observed info(including judging the underlying implications and meanings) to fathom the current situations to generate working action plans. This process are affected by the following factors: (For the sake of battler AI, mental models means the complete frameworks consisting of all the known strategies and tactics with all their known counters in the addressed RMVXA games that can be used by the allies, opponents and the battlers themselves.) - New Information - Previous Experience - Cultural Traditions - Genetic Heritage - Analysis and Synthesis For the sake of writing battler AI, cultural traditions and genetic heritage can be regarded as a battler's characters if they've to be taken info account, or simply ignored if they don't. About the rest: - New information must always be taken into account. Otherwise the battler AI will fail to realize that the situations have changed, let alone adapting to them(or better yet, controlling them). - Previous experience, which will be fed by the new information, must always be taken into account too. In terms of battler AI, it means the AI will have to store all the relevant info in the current battle so far. Sometimes the full pictures, like the opponents' strategies, can only be accurately formed by combining the previous experience with the new information. - Analysis means breaking down the existing mental models into smaller pieces, while synthesis means using those smaller pieces to form new mental models that can accurately address the new situations. This implies that the battler AI should be written to include as many known strategies and tactics with as many of their counters as possible, and they should all be able to be broken down into independent modular pieces that can be integrated into new strategies and tactics with their own counters. It also means that the battler AI should be able to judge which pieces are called for and run the integrated strategies and tactics by combining all those pieces. For example: - At the start of a battle, a boss fighting a 6 actor party observed that they're all heavily geared towards dealing damage with significant sacrifice from every other battle aspect. By recalling all the known strategies and tactics with their counters, the boss AI will realize that it's extremely likely that that 6 actor party will try to kill the boss as quickly as possible(strategies) by having as high damage throughput as possible(tactics). The boss AI will then use all the known strategies and tactics to generate all the ones that can counter the players' ones. - In action battle system, active time battle, charge turn battle, tactical battle system, or any other battle system having the time dimension, a boss noticed that some actors just halted momentarily even when they should have acted instead(whether they should act instead needs orientations to tell) in some situations. The boss AI will know that it's highly probable that the players aren't good at addressing those situations yet. The boss AI will then use all the known strategies and tactics to generate all the ones that can make those situations happen more frequently. Orientation's the most important part as it determines how accurate the observations will be interpreted and how useful the generated solutions will be executed. For the sake of the battler AI, orientation's also the hardest part to be implemented well as it needs to stores all the known strategies and tactics with all their known counters, and the algorithms decoding the opponents' ones and coming up with all the working counters. Decide To decide is to judge which of all the generated action plans will be the best calls for the addressed situations which are already accurately fathomed. All these judgments should be treated as the best hypothesis which are to be tested rather than absolutely correct choices. For example: - At the start of a battle, a boss fighting a 6 actor party figured out that the party tried to kill the boss as quickly as possible(strategies) by having as high damage throughput as possible(tactics), and the boss AI has already generated all counters that can work. The boss AI then forms the hypothesis that "it's better to mix several counters together to confuse the players by obfuscating the intentions" and decided to build action plans around that. Act To act is to build the action plans around the decisions and test those action plans by executing them(either simultaneously or treating the rest as backups that can always be quickly called). Then observations will be needed to check if those action plans worked and which ones work the best(and they'll be the main ones until the new best comes) in order to "finish"(and then restart) the "infinite" loop. For example: - A boss executes several counters together in a mixed manner to stop the players' strategies and tactics. As the players realized they've been countered, they begin to adapt and use new strategies and tactics instead. The boss AI noticed that the players are adapting to the new situations so the boss reorient in order to form new hypothesis to build and execute new action plans that will work. Tempo When it comes to oppositions, the faster one can effectively and efficiently run the OODA loop(higher velocity) and the faster one can change its tempo(higher acceleration), the more advantageous one will usually be in general. By controlling that tempo well via changing it rapidly and surprisingly, it's even possible to get inside the others' OODA loops to consistently reset them to the observe phase so they'll be confused and have to passively react to the new situations(they'll eventually end up being paralyzed in observations or making rash actions that won't work at all), while the one gaining the upper hand can constantly have an accurate full pictures and active control to the new situations. For example: - At the start of a battle(In action battle system, active time battle, charge turn battle, tactical battle system, or any other battle system having the time dimension) having 2 bosses, they act whenever they become able to by making moves that don't need charging(time delay between attempting to make a move and actually making that move). This possibly led to players think that "act whenever becoming able to" are those bosses' patterns and those players build their action plans around their hypothesis. Then suddenly those bosses delayed for a long time even when they become able to act, and that potentially lures players into thinking that those bosses are trying to make moves with long charging times so they make moves(which need to charge) that can cancel opponents' charging moves. But that's exactly what those bosses want the players to do so instead those bosses make some other moves that don't need charging when the players are charging their moves. The players now become confused and have to observe the new situations to form new mental models that can accurately address the new situations, while those bosses can take advantage from the resets of the players' OODA loops. Building such battler AI won't be easy nor simple even for AI professionals. The builders need to have a thorough understanding to the battle systems, the battles and all the possible battlers involved as well as a fluent command on programming AI in general. Nevertheless, I still think that such AI would be incredibly challenging and extraordinarily difficult to beat if it could ever be built. P.S.: Bear in mind that the aforementioned are still just an oversimplified version of the original OODA, which would certainly be overkill in writing battler AI.
×
×
  • Create New...