Jump to content

Yemachu

Administrator

Yemachu last won the day on April 16

Yemachu had the most liked content!

Community Reputation

46 Valued

3 Followers

User Groups

About Yemachu


  • User Group: Administrator


  • Member ID: 135


  • Title: Card maker maker


  • Post Count: 46


  • Post Ratio: 0.04


  • Total Rep: 46


  • Member Of The Days Won: 6


  • Joined: 01/12/2019


  • Been With Us For: 1263 Days


  • Last Activity:


  • Currently:


  • Age: 29


Clubs

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Eurovision song contest 2022 has started.

    1. UltimateIRS

      UltimateIRS

      nobody cares

    2. LordCowCow
    3. Yemachu

      Yemachu

      "Give that wolf a banana, before it eats my grandma". Interesting song text… though with the various songs in their native language there might be some other weird ones that fly completely under the radar.

  2. Recently came across a site that uses AI to generate images of new "Pokémon".

    https://nokemon.eloie.tech/

    1. Cibryll

      Cibryll

      Kinda makes me wonder if there's something like that for Yu-Gi-Oh!

    2. Yemachu

      Yemachu

      Given there is quite some variation in art styles between different cards (i.e. "Burning Abyss" vs "Live Twin"), I reckon you could just look for AI that generates images.

      • Dall-E generates images based on a prompt, but it requires a subscription. It does provide a 3 month trial period, with a free credit taht is worth about $18.
      • Artbreeder lets you select a few images and have it generate another one based on it, or tweak some parameters (such as age) to create a different image. An account is required, and any image you generate (upload?, not entirely sure; be careful wih copyrighted works) can be used by anyone.

      With some other, more specialized options out there (focussing on creating paintings for example).

      Also once ran into an AI that turns text to speech using famous voices (FakeYou.com), such as: Spongebob, Yami Yugi, Obama, etc. You could recognize the voices, but there was little control over which parts to emphasize.

  3. The "A Hero Rises" banner is live. Despite the banner not having increased odds, the results have been amazing for me so far: Lewyn, Hatari Azura, Thórr, Amelia, Saber, Cyl Eliwood, Cyl Micaiah, Thórr; and that within 16 summons. Currently only missing Byleth from the featured units. Amelia, Saber, Micaiah, and Thórr have been merged. Lewyn will donate "Special spiral" to a unit I have yet to decide on (currently between Riev and Yuri), Eliwood and Azura are my first copies of them and will stick around for collection purposes.
  4. "Vital Astra" is apparently inheritable to infantry sword/lance/axe users. So we now have a inheritable speed scaling special that even grants damage reduction as an additional effect. I wasn't expecting that to be the case when I saw the trailer.
  5. New Pokémon games have been announced, scheduled to release at the end of this year. Seems to be inspired by Spain. Note from cow: When more info comes out make sure to spoiler warning/spoiler tag things
  6. Caeda is the next legendary hero. Seems like she provides stats in addition to being able to be paired up.
  7. Voted for Dagr and Azura, forgot to vote on the remaining days. From the top 8, I only have Edelgard and Corrin. I'm hoping another unit than those wins (for collection purposes), though I wouldn't mind a spare Corrin.
  8. Out of the units I voted for, only Lyon reached the top 20 in their respective division. As for the winners, I wonder how they will handle the next Choose Your Legends now that female Byleth has reached the top two. Usually different iterations of a unit are no longer votable after one of their versions has won. Given their votes are tallied separately, I suppose male Byleth still could have a chance. Though it didn't stop them from booting Flame emperor after Gatekeeper and Marianne won. It could get especially awkward if both versions of Robin/Kris/Corrin would win next round, in the case where male Byleth is dropped. Time will tell.
  9. It is that time of year again where quite a few people celebrate some event or another; often involving either receiving presents, or given them out. Did you get (others) anything? Presents I have received this year include: Dragon shaped slippers A board game Chocolate Some undergarments
  10. Yemachu

    Card maker Devlog

    It has been more than a month since the previous blog post, despite feeling like it was posted last week. Time sure flies. Last time the topic was about component librarie; they certainly help with getting a uniform look-and-feel for any application. But having an application that has a consistent design isn't particularly useful if none of the controls (such as buttons, sliders, and so on) do anything. This brings us to the topic of this post: managing state and reacting to changes. In TypeScript there are a few "primitives" which can be used to model your state. string: Represents text. A card's name or effect could be represented this way. Doesn't inherently represent any number: A numeric value. A card's level could be represented this way, as well as the horizontal and vertical position at which an image should be drawn Object: Group of "primitives" that can be accessed under a specific name. Useful for having closely related data together, such as all properties of a single card, the width and height of a rectangle, X and Y component of a coordinate, and so on. Array: List of things in a specific order. Typically similarly typed content is put in an array (such as all different cards in the project). A relatively simple representation of a card could be as follows: { "name": "Dark Magician", "template": "NORMAL", "attribute": "DARK", "level": 7, "ATK": "2500", "DEF": "2100", "type": ["Spellcaster"], "effect": "The ultimate wizard in terms of attack and defense.", } A few of the standard traits of a Yu-Gi-Oh! cards have been represented in that block of text. Modifying those properties from code is easy enenough; simply reassign one of the values. const card = { /* All of the above properites */ } const nameEditor = document.getElementById("nameEditor"); nameEditor.addEventListener("change", function(evt){ card.name = evt.currentTarget.value; }); If one were to type some text in the "nameEditor" textfield, it would update the card's name. But is the rest of the application aware of the change that has been made? The above code does not notify that the name property has been changed, so other code that might have something to do with that property (such as the renderer that creates an image from it) might not act on the change. True, the code could constantly be rerun so the lastest values are used, but this can be a bit pointless if nothing has been changed. It is however not entirely without merit; games tend to work this way. An alternative is to invoke a function notifying others that changes have been made. This could be a tad repetive, and is easy to forget if the code is not structured in a way that forces you to do so. Irrespective of how other parts of the code are notified about changes in the state, it needs to somehow figure out what has changed and how to respond to that, or it could start from scratch working from the new state. The former is rather finicky and error prone, the latter leads to a lot of extra work. React (detailed in the previous post) takes an approach somewhere in between. Rather than making changes in place, you create a new immutable object that represents the state and notify the application about that new object. The immutable nature allows you to reuse components from the previous state; they will not be changing after all. This makes figuring out what elements need to be updated a whole lot easier: is this part of the state the same as the previous time? Yes: leave everything as is. No: Start from "scratch" (nested components might not need to be updated however). var card = { name: "Dark Magician", level: 7 }; // Create a new object ({}), copy all properties from the previous state to it (card), // and overwrite any of them with the newly specified values (level: 7). card = Object.assign({}, card, { level: 8 }); // Output: { name: "Dark Magician", level: 8 } Assigning a new value has become a tad more unwieldy, but we have created a completely new object, rahter than having changed something in place. The above might not work correctly with deeply nested properties, but there are utilites for that available, such as Immer.js. Leaving the issues of responding to updates aside for a bit, there is another thing with the state that needs to be addressed: maintaining integrity. In the above example a new value gets created where the level is overwritten. In this case it is an acceptable level, but in the more general case there might need to be checks in place making sure it falls between zero and twelve (inclusive)… or make that thirteen since "Number iC1000: Numeronious Numeronia" exists. Placing the logic in the event handler could work, but could become clunky if multiple sources were to work on same value. This could happen when a slider with a text field both control the value. Extracting the logic into its own function could help. function UpdateLevel(card, level) { if (0 <= level && level <= 13) { return Object.assign({}, card, { level }); } return card; // Invalid level; leave it unchanged? } // Somewhere in an event handler setState(UpdateLevel(card, evt.target.value)); In this case it is only one value that needs to be updated, but what if some more things were related? Say… changing the name should also update the effect so any references to the card's "old" name now reference its new name. This could of course be added to the function that updates the name: function UpdateName(card, newName) { const newEffect = card.effect.replaceAll(card.name, newName); return Object.assign({}, card, { name: newName, effect: newEffect }); } That works… but what about the pendulum effect? It can simply be added. It is however a solution that does not scale well. In the actual game various cards refer to other cards by name (i.e. Fusion Materials). Having the "UpdateName" function run over all cards in the project to update any effects that refers to its old name quickly becomes unwieldy. It would be more convenient if we could tell the state that we intend to update the name of a particular card, and have related state figure out for itself if it needs to update as well. This, to my knowledge is kind of the idea behind Flux/Redux. import { createAction, createReducer, combineReducers } from "@reduxjs/toolkit"; const rename = createAction("card/rename", function(card, newName) { // When every instance is informed about a name change, it might be a good // idea to add a field indicating the intended target to prevent all cards // from taking on the same new name. Hence "id". return { payload: newName, meta: { id: card.id, oldName: card.name} }; }); const name = createReducer("Einzelgänger", function(builder) { builder.addMatcher(rename.match, function(state, action) { return action.payload; }) }); const effect = createReducer("You can only control 1 \"Einzelgänger\".", function(builder) { builder.addMatcher(rename.match, function(state, action) { // Not a great implementation, but it should suffice for a demonstation. return state.replaceAll(action.meta.oldName, action.payload); }); }); const card = combineReducers({ name, effect, // … }); In order to rename a card you would then dispatch the "rename" action from your event handler. dispatch(rename(card, evt.target.value)); The state then decides how it should respond to that action, without the event handler needing to worry about any of the other details about how the state should be changed. Isn't that wonderful? Another advantage of using this method of working with state is that you can test everything in isolation before integrating it with other code to make sure individual parts work correctly. Something worth noting, is that this example might be a bit convoluted. It is probably a better idea to have a special syntax where any references get resolved upon rendering the card. This to avoid all letters 'M' getting replaced with "Mokey Mokey" when typing that name after writing its effect first, or affecting other cards with the same name. That should be enough for this post. Not sure which topic to address next, but there are enough topics to cover. Till next time!
  11. Just finished playing the main story of "Bug Fables". I found it to be quite an enjoyable game. If you are a fan of the old Paper Mario games, you'll probably also like this game.

    The theme has been implemented quite nicely (though the texturing has some room for improvement).

  12. Yemachu

    Card maker Devlog

    Components When developing a website HTML, CSS and JavaScript are a given. Though to what degree tends to depend on the purpose of the website. A website for your local hairdresser/dentist/etc. might only need to put opening hours and contact information online, maybe including a contact form; for those sites some simple HTML, CSS, and JavaScript would suffice. On the other hand, if you are creating a web application, standard HTML, CSS, and JavaScript might not be quite as convenient to use. Partially because different browsers tend to be, well…, different (his sometimes makes it hard to have certain features behave consistently cross-browser), and partially because HTML, CSS and JavaScript were not originally desgined for creating applications. As a result, linking different components can be a bit involded (for example: detailing why your password is not accepted). Things become even more complicated when you want to let users drag-and-drop images, add rich text editors, rearrange lists, open dialogs, create custom context menus, and so on. Luckily there are quite a few software libraries out there that take care of normalizing user input, and "rendering" HTML based on some state. Unfortunately, there are quite a few libraries out there, making it hard to choose. Some big ones include: React Angular Vue React They go about things somewhat differently, but also have certain things in common. And an argument could be used for using any of them; coming somewhat down to preference. In the case of this site's card maker, React is used. Out of the listed options it was the least involvedfor me to to get it to work. But I could have equally well chosen one of the many other options. Now, with React there are two main ways in which you can write your code: using plain JavaScript, or using special JSX syntax. The former works straight out of the box, where the other requires some configuration for your build tools. Due to not actually using a toolchain when initially developing the card maker, I used the "createElement" option; though I have since switched over. Unfortunately the current version still uses the plain JavaScript version, thus making maintaining it a tad… annoying. // Plain JavaScript example import React from "react"; export function Card(props) { return React.createElement( "div", { className: "card" }, React.createElement("h1", { className: "name" }, props.name), React.createElement("div", { className: "effect" }, props.effect) ); } // JSX example import React from "react"; export function Card(props) { return <div className="card"> <h1 className="name">{props.name}</h1> <div className="effect">{props.effect}</div> </div> } Those two examples are equavelent to one another. The syntax of the latter is closer to the output that is generated, and is thus why I would consider it more readable. If we were to use this "Card" component to render Dark Magician, the output would look like the following. <div class="card"> <h1 class="name">Dark Magician</h1> <div class="effect">The ultimate wizard in terms of attack and defense.</div> </div> That is all fine and dandy, but how does this help compared to writing the output directy? In context of those examples it is indeed a bit excessive, but the use becomes apparent once user input comes into the picture. Say we still wanted the above output, but instead of rendering "Dark Magician's" name and effect, the output should reflect what the user has typed into a set of text fields. The way the state is handled in the example could be improved, but that is a topic for a different time. import React, { useState, useCallback } from "react"; import { Card } from "./Card"; export function CardMaker() { const [name, setName] = useState(""); const [effect, setEffect] = useState(""); return <div> <Card name={name} effect={effect} /> <input type="text" value={name} onChange={(evt) => {setName(evt.currentTarget.value);}} /> <textarea value={effect} onChange={(evt) => {setEffect(evt.currentTarget.value);}} /> </div> } Component libraries Up until this point, the code only involved regular HTML components like buttons, textfields and file inputs. After all, that is the only thing the browser knows how to render. They can however be a tad bland and don't look all that consistent accross browsers. You can get nicer results with some CSS, but some components are somewhat annoying to style, such as checkboxes, radio buttons and file inputs. Doing this manually is possible, and allows you to style them exactly as you like. But I prefer to use some ready made components that have been tested to work in various different browsers. Similar to before with React, Angular, Vue, and so on, there are a lot of component libraries (for use with React). Those include but are not limited to: Material UI React Bootstrap Ant design Blueprintjs Fluent design Prime React All of those listed are available under a permissive license, and provide variying amounts of customization of the components. Picking one is hard, and I have switched back and forth between some of the listed options a few times. Currently I'm using Material UI for development.
  13. Yemachu

    Card maker Devlog

    Logo by me Quite a while a ago, the first version of this site's card maker has gone online. While it does do its thing, there are quite a few points on which it could be improved. This entails both reworking the code (as it is quite a mess that hardly uses any of the available tooling), and adding new features that makes using it a great experience. Some parts of the development process will be detailed in this blog. Tooling improvements This first instalment will be about the original implementation and improvements to the toolchain for developing the newer version of the card maker. Bundling the code As indicated in the intro of this blog, the original implementation hardly used any tooling. Most of the code was written in plain HTML, CSS, and JavaScript. I beleive that, at the time, browsers hadn't standardized how to import dependencies from the code itself at the time (and even if they did, support among different browsers was subpar). This meant that you'd have to orchetrate the imports yourself, or place everything in one big file. Plain JavaScript <script src="level-editor.js"></script> <script src="name-editor.js"></script> <!-- And many more such lines… --> <script src="card-maker.js"></script> As some parts of the code depend on the content of another file, you'd need to make sure the order which the files are imported is correct; files without dependencies first, any others after all of their dependencies. As you can imagine, this is quite error prone. You could probably hack something together like the following to have a file specify it's own dependencies. But there are some edge cases involved that are easy to get wrong. function dependOn(url) { var dependency = document.createElement("script"); dependency.src = url; dependency.onLoad = callback; document.body.appendChild(dependency); } dependOn("level-editor.js"); // At this point, the dependency would not have been loaded yet, // so using any of the code in the relevant file would lead to // errors. new LevelEditor(); // Using a callback instead that is invoked when the file has loaded // is more likely to work out correctly. dependOn("level-editor.js", function() { // There is however no guarantee that the code in "level-editor.js" has // run at this point. Any definitions might thus not be in effect yet. }); Another issue with the naïve implementation above is that it does not check whether the file has already been imported, for example when multiple different files have the same dependency. This could lead to useless duplication of code. RequireJS Luckily there was a project available that took care of most of these issues, and had some other utilities that you might not have thought about. I'm referring to RequireJS. You'd wrap the content of your file in a function called "define", where you specify on which other files it depends, and what to do once all dependencies have loaded. // level-editor.js define([], function(){ return function LevelEditor() { // … } }); // card-maker.js define(["level-editor"], function(LevelEditor){ return function CardMaker() { // … } }); Optionally, you could even specify what to do when the dependency failed to load. Most code was thus wrapped in those "define" function calls. Now only the entry point needed to be included, and the dependencies would automatically resolve themselves. This was nice for development purposes, but for users and the server it isn't as great. First you download the HTML that specifies it needs a script file; fair enough lets fetch that as well. The browser then finds out that it needs another file(s) first, so download those as well; same story. This could go on for a little while, making the site slow to load. Luckily RequireJS also provided an optimizer that bundles all those files into a single file (or split something into different files that can be loaded later when actually needed). It also performed other optimizations, such as shortening function names, removing comments and whitespace. This would all lead to faster load times. This all sounds great, so why change it? Well… as it turns out not every dependency does support RequireJS. And in cases where they do, everything but the kitchensink is included. Not great for efficiency (as is typically noted in the relevant documentation). Manually managing software versions of used libraries is also not ideal. NPM/Yarn/etc As it turns out, there are a few options out there for managing dependencies of your project, irrespecitive of whether those are part of the output, or solely for helping with development (such as bundlers, linters that check whether your code follows a standard, or transpilers that take care of turning modern code into code understood by older browsers). Most of them are interchangeable; they pull in the code from a software library (and its dependencies), and keep track of which versions have been installed. I have picked "yarn". So if I were to add some dependencies I'd write something akin to: $ yarn add react react-dom $ yarn add --dev typescript Most libraries that you pull in this way also document how you should import them; effectively the same in all instances, but different compared to the code listed prior ("define"). Not too big of a deal, and looks somewhat neater when a lof of dependencies are involved. import React from "react"; import { render } from "react-dom"; import { Rating } from "@mui/material"; // Rating (by default) uses stars to let the users input a number; ideal for the level editor. render(React.createElement(Rating, { min: 0, max: 12 }), document.getElementById("root")); Now, if we were to directly use the above file, we'd probably run into the issue of the browser not understanding import statements; it is syntax that was introduced for a different code execution environment (Node). A transpiler/bundler could take care of resolving those dependencies. Webpack But wait, didn't we just go over RequireJS, and it not being suitable for the job? Yes, but there are quite a few other options out there. And unlike RequireJS they support the import syntax that is actually supported in some contexts. In my experience Webpack is most commonly used, including in scripts that help set-up boiler-plate code. So for example, if you have the following two files: // card-maker.js import { LevelEditor } from "./level-editor.js"; function CardMaker() { LevelEditor(); } CardMaker(); // level-editor.js export function LevelEditor() { console.log("Editing levels!"); } Webpack would generate output akin to the following (which has been somewhat simplified compared to actual output). (function(modules) { var installedModules = { }; function require(id) { if (installedModules[id]) { return installedModules[id].exports; } var module = installedModules[id] = { id: id, loaded: false, exports: {} }; modules[id](module, module.exports, require); module.loaded = true; return module.exports; } require("./card-maker.js"); })({ "./card-maker.js": (function(module, exports, require) { var LevelEditor = require("./level-editor.js")["default"]; (function CardMaker() { LevelEditor() })(); }), "./level-editor.js": (function(module, exports, require) { exports["LevelEditor"] = function() { console.log("Editing levels!"); } } }); This way the dependencies get correctly resolved in the required order, making it far easier to work with compared to plain JavaScript. Now at this point you might wonder whether all that hassle was worth the somewhat hard to read output, compared to where we started: manually ordering imports. I would say very much so. Though it might not be all that apparent from the code examples. Some benefits you also get (sometimes requiring plugins for Webpack) include: Optimizing output by stripping useless whitespace and comments, as well as renaming functions and variables to something less verbose, saving a lot of bytes in the long run Splitting code that is relevant for different pages into reusable packages, or only importing code when actually necessary Hot reloading, so you do not manually need to rebuild the code and refresh the page each time you make a change Allow for importing different file types, to make sure they are bundled. And I'm probably forgetting a few other advantages. That might have sounded like quite a few hoops to jump through, and might actually be the case. However, there have been some kind developers that have created some script that take care of most this stuff. Thanks to those scripts, it essentially boils down to running a single command, akin to the following (this is but one such command). $ yarn create next-app --typescript There might still be some configuration required depending on your use case, but I reckon it still greatly simplifies things fo you. And with that I'll end of this post; it has already gone on for longer than I initally intended, despite hardly involving any code of the card maker itself.
  14. Got some very nice results on the Legendary Remix banner on the way to the guaranteed 5⭐ summon (Fire Emblem Heroes):

    • Siegbert: Future King
    • Ryoma: Supreme Samurai
    • Lyon: Demon King
    • Robin: Fell Vessel
    • Hríd: Icy Blade
    • Ephraim: Legendary Lord (guaranteed 5⭐)

    Also got 2 "Knoll: Darkness Watcher", only one more is needed for him to reach +10.

    1. LordCowCow

      LordCowCow

      I've debated +10ing Knoll mostly because most of the red tomes that are plausible to +10 I don't have much interest in and he was the one I liked most.

    2. Yemachu

      Yemachu

      Already had a +10 Lyon, who has a similar stat-line (and preferred weapon), so Knoll doesn't really do anything special for my barracks. Not that it matters; I like the Sacred Stones cast, and he could still be useful for modes like Arena Assault.

      Building a green mage would have been more practical, as I don't have any merged beyond +3 (Boey), but most do not really stand out to me, and the ones that do tend to be 5⭐-exclusive or seasonal. Might finish Boey. Also I'm planning to merge Orochi, but I don't have any copies to merge (yet). And with a bit of luck a nice other option drops soon.

    3. LordCowCow

      LordCowCow

      I feel that, I try and get some of each type of unit but its sometimes hard to find one that I like and is easy enough to +10

×
×
  • Create New...