1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Hey Guest, is it this your first time on the forums?

    Visit the Beginner's Box

    Introduce yourself, read some of the ins and outs of the community, access to useful links and information.

    Dismiss Notice

v6steeda's Modding Tutorials

Discussion in 'Modding [KAG]' started by Blubahub, Sep 3, 2018.

?

Helpful, enjoyable, and/or informative?

  1. Yes.

    87.5%
  2. No.

    12.5%
  1. Soooo, I figured since I'm beginning to figure out how to mod, and there being a great lack of modding tutorials, it would be okay for me to make some modding tutorials (hopefully as an I learn more, they'll become more complicated over time). Most of the tutorials can be done standalone..!

    Below, I have a listing of what I've done, in order of release/posting. Feel free to post any questions and show off anything you make using my tutorials as a post! I love seeing what other people are modding..!

    EDIT 8/19/2019: I will also be doing "tutorials" that are short and sweet and more informative (e.g. saying how SColor works) versus having a full in-depth guide on how to directly add or change something.

    Prep Talk
    First Written: 9/3/2018, EST
    Last Edited: 9/8/2018, EST
    Thread Page: 1 (https://forum.thd.vg/threads/v6steedas-modding-tutorials.27816/)

    Making Your First Mod
    First Written: 9/7/2018, EST
    Last Edited: 9/8/2018, EST
    Thread Page: 1 (https://forum.thd.vg/threads/v6steedas-modding-tutorials.27816/)

    Adding Emotes (OLD, Broken)
    First Written: 9/8/2018, EST
    Last Edited: 1/26/2019, EST
    Thread Page: 1 (https://forum.thd.vg/threads/v6steedas-modding-tutorials.27816/)

    Teams
    First Written: 9/10/2018, EST
    Last Edited: 3/18/2019, EST
    Thread Page: 1 (https://forum.thd.vg/threads/v6steedas-modding-tutorials.27816/)

    ?Cloning Blobs?

    ?Making Shops?

    ?Guide to Spriting KAG?
     
    Last edited: Aug 19, 2019
    epsilon likes this.
  2. Prep Talk
    First off, let's explain what the basics of KAG modding, starting with finding the folder where King Arthur's Gold is stored, then going on to the modding basics. If you bought it on Steam and you use the default path (for Steam and KAG), the path should be [This PC\]C:\Program Files (x86)\Steam\steamapps\common\King Arthur's Gold. Here are a few important folders and files that we'll be using a lot:
    • Base [folder] - The vanilla (main base game) files. Note that a part of KAG's files is sadly unavailable for modding (as in, you can't even find it - let alone modify it), thought the .png (image) files I have found to be always available, even if you can't access the code for it. Adding and/or modifying anything in the Base folder WILL BE REMOVED (including modified .png files, regardless if they have the same name or not) when you download a new update of KAG, or when you run forceupdate.bat to restore vanilla KAG - if you do override a file(s), then it will only appear changed to you (and probably break the game if you've modded the code). You have been warned!
    • Mods [folder] - Where you can safely store your mods. Each mod is stored as a folder When you join a modded server and it downloads/updates a mod, it downloads it in your King Arthur's Gold\Mods folder. Any recently overridden ("updated") mods can be found in the King Arthur's Gold\old\Mods folder (its contents will be deleted after a set amount of time - if you need something from there, extract it immediately!).
    • autoconfig.cfg [code file] - The file that contains some base game "stats," such as what gender you have your user set to, what vanilla head you're using, etc. The important thing here (for modding) is sv_gamemode (line 22, under auth_remember and above sv_mapcycle) and sv_test (line 29, below sv_canpause and above sv_restart_on_updated). More about them later.
    • mods.cfg [code file] - Here, you set what mods you use (only a few actually will run if you play normal KAG and have it set - most only run when you connect to a/the server running that mod), by typing the mod's (your/the folder's name in KAG\Mods) name on a blank empty line. This is case sensitive, but extra spaces from either the tab button or spacebar before the mod's name and after is allowed. You can have multiple mods active at once (one mod per line). To deactivate one, either make it a comment by typing a pound or number sign ("#"), or delete the mod's name.
    • runlocalhost.bat [executable file] - This is what we run when we want to test or play our mod(s) only on our computer (running dedicatedserver.bat will run a server that others, LAN and public, can join, assuming you have properly set it up - anyways, it takes longer to join the server if you run this .bat verses runlocalhost.bat).
    Now, let me explain what sv_gamemode and sv_test is. sv_gamemode is a stat aka variable in autoconfig.cfg that lets you set what your server's game mode is ("sv" stands for "server" - the server can/do/would be hosted on your PC). This can be an official game mode (e.g. CTF, RTDM, TTH) or a modded one. Put the desired gamemode on the same line as sv_gamemode, but 1 space after the equal sign ("="). sv_test is the variable that allows you to i.e. spawn entities (referred to as "CBlob" or "blobs" in .as programming files) - make it "sv_test = 1" to activate it, or "sv_test = 0" to stop it - for purposes of these tutorials, set it to = 1.

    I will mostly be talking about any programming basics as we encounter them in these tutorials. Variables, or "vars," are stats that contain information (strings aka text, booleans aka true/false, numbers, etc) for easy reference. .cfg (standing for "config") files are totally composed of stats and comments (use # to make any code or text after it [to the right of the sign] on that line note run in-code - use for disabling code and making notes), that are then used and translated to AngelScript. .as (standing for AngelScript - KAG's [main] programming language) files are the programming files. As a general ruling, .cfg files are a lot easier to modify/code then .as files, so our next tutorial will be focused on making a simple ball with a .cfg file.

    A few closing notes: Images or "sprites" (in-game pictures) in KAG should be .png ("portable network graphic") file. Modded servers in the KAG (in-game) server browser have a blue background and brighter blue text, and on the server's panel (to the right of the server listing) have (below the description) the list of all the mods on the server. Pressing the "tab" button (assuming you are using the default KAG mapped controls) will at the top above the scoreboard that appears show the game mode's name and it's description.

    --- Double Post Merged, Sep 7, 2018, Original Post Date: Sep 3, 2018 ---


    Making Your First Mod
    In this tutorial, we'll be making a single simple "entity" (plural "entities") or blob (blobs are characters, items, blocks, structures, etc) - a ball! We're going to be starting out more or less basic, copying entities, renaming them, and changing them. First off, let us make a ball. I've made a ball or two in the past (one of them is actually published as a KAG mod on here at https://forum.thd.vg/resources/magical-wooden-ball.555/), so I'm just gonna make a little ball that has a weird name - Blood Ball, or bloodball when using commands to spawn it. I have already made the sprite that I am going to use for it:

    [​IMG]

    The sprite is a .png (note to spriters - when making sprites for KAG mods, make sure they are in the Portable Network Graphic format aka have the .png extension!) that the game will use graphically as the entity's appearance, and it is 6x6 in pixels. I have named the image BloodBall.png - all these little facts I'm telling you are important and will be used in a second. Now, before we start making our mod, we need to make a folder in the King Arthur's Gold\Mods directory. I'm naming this "TutorialStuff" - here we will put our mod's files. Then, we'll go back to the KAG folder (I suggest having an at least 2-3 folder browsers or explorers open at the same time, since you'll need to hop around a lot between your mod and the KAG folder, and possibly KAG's code, hence having a third explorer open), than find the Keg folder in KAG\Base\Entities\Items\Explosives, and grab (copy) the file named Keg.cfg (NOT Keg.as), and paste the file into our mod's folder. I will then rename this file to BloodBall.cfg since I'm calling my entity "Blood Ball" (the file name does not matter - we won't be referencing the name ever in modding when dealing with config files, but make sure you keep the .cfg file extension), and open it up in my preferred text/programming text editor - Notepad++. You can use other programs too such as Sublime Text and the regular Notepad on Windows. Config files or .cfg files are (in the case of how KAG uses it) just a way for KAG's engine to know this is a blob's configuration TEXT file, so don't worry if your coding editor doesn't support/show ".cfg" as a recognized type.

    The first line will read out "# Keg.cfg" - you can change this to whatever you want, for as we said before, any text after the "#" is a human-read comment that the game ignores. I'm going to keep it the same, though, so I can easily remember what file this is based upon if i.e. I want to make more balls but with different heaviness, bounciness and so forth (doesn't really matter in most cases, though - anyways, you could always come back here to find out what the original file was). So, feel free to make comments anywhere that you please! Just remember that the "#" only comments everything to the right of it on that single line (hence it is a single-line comment).

    Now, let's get into what we want to change in this file. On line 4, we're gonna delete the sprite scripts Keg.as and Wooden.as (I'm removing Wooden.as because that means the ball would get hurt every time it hits the ground/moves because of the bounciness, and also because for me my ball isn't wooden), so the line reads "@$sprite_scripts = " - note that tabbing and extra blank lines do not mess up the code, but I recommend to keep the current KAG format for reading ease. Now for the next line (I deleted that extra blank space that Keg.as; was on, so for me it is line 5, but it might be line 6 for you - note though that I'm going to assume for now on that you delete these blank spaces where code was once before that we deleted), we're going to change "Keg.png" to our ball .png file's name, which is for me BloodBall.png. At the next 2 lines (6 and 7), we're gonna state what the sprite's (image) size is 6 px or pixels high, and 6 px wide (so, replace the two "16" answers after the "=" with "6", one per each of the two lines), because that is what mine is - if say your's is 32x32, well then put replace "16" with "32," and so forth. We're gonna ignore the two lines after that which deal with the x (width aka left to right) and y (height aka up to down) axis (dimensions) offsets.

    Next, since I don't want my ball to have any wooden or dirt "gibs" (the little chunks that fly off when something dies or gets hurt), I'm going to remove everything in between "$sprite_gibs_start = *start*" and "$sprite_gibs_end = *end*" lines (also gonna make it so there's only 1 blank line in between the aforementioned lines). Now, we'll start getting into the physics of the object. I'm not gonna explain them all, only the ones that I know very well what they do in KAG and that I change. f32 shape_mass is where you set how heavy or light something is. A good thing to know is that all these f32 shape_ physics variables all affect each other. I'm changing the shape_mass from "30.0" to "5.0" (line 26 for me). Then I'm changing shape_elasticity from 0.03 to 0.7 (elasticity equals bounciness in relation to mass).

    While you can technically change these shape variables to whatever you want, I'm just giving you what I'm using for this mod (also making it about the most bouncy it can go without making it so high that it will never stop bouncing/sit still). Now, if we were to play this right now (remember that we haven't changed the name or spawning name yet), THIS would happen:

    [​IMG]

    (Note it would actually be smaller - accidentally, I forgot to change the image size, so it just scaled it up! Opps!) Why is it doing this - as in, acting like a keg, and exploding? Because we haven't removed all the Keg.as references (specifically under $name, which we also need to change, since the knight class looks for certain blobs with certain $names for deciding what are lightable/activatable - in many cases, explodable)..! So, let's skip to line 51 and change the answer to "$name = keg" to "$name = bloodball," or whatever you want to type when sv_test is on and you want to spawn it using chat commands (if you do make it bloodball, then spawn it by typing !bloodball). This is NOT the name that appears when you hover your mouse over it, select it when your about to pick it up (C key) and/or when it is in your/ an inventory. But we'll change later. At line 52 (directly under the $name line), it starts to define [most of] the scripts (code files) by saying "@$scripts = " - delete all those lines except the one containing FakeRolling.as (allows rotating of the sprite), and optionally NoPlayerCollision.as (which makes it so blobs of any team/class can move through it without any effect unless modded in). I want my ball to be collidable with the player and other blobs, including itself, so I'm gonna delete "NoPlayerCollision.as" for my entity. Thus, for me at least, there is only 1 line of scripts. The difference between the sprite scripts from before is that they are used for rendering/loading the sprite.

    f32 health sets the hit points or health of the object until it dies, gets destroyed, and/or despawns (0.5 health is the same as 1 heart - so, archers have 1.0 health aka 2 hearts, builders 1.5, and knights 2.0). I'm going to keep it the same since I'm not using any scripts (e.g. Wooden.as) that would apply damage if it fell from a high height or impacted really hard (such as when it bounces, in the case of our ball). Now, we're gonna change the inventory/human-read name of the blob at line 55 ("$inventory_name =," below the health stat) from Keg to the name we want players to see that our ball is called (for me, Blood Ball). Save the .cfg file (you can close it now if you want)..!

    Now, let's test our mod! Remember, we haven't activated our mod yet, since we didn't add it as a new uncommented line in King Arthur's Gold\mods.cfg. So, going into mods.cfg, we add at the bottom a new, empty line, and put the name of the mod's folder in (for me, TutorialStuff). Make sure you have no other [uncommented] mods [on different lines] here, because if you do they'll be activated, and they may conflict with our mod (as a general playtesting rule you should only use your mod and any mods you want it to work with when playtesting it). Now, save it (and possibly close it), then again in the KAG folder run or execute (use) runlocalhost.bat. A command prompt (aka console) -type window will come up, and it will in a few seconds start KAG where it immediately auto-loading or connects your private playtest "server" (it isn't really a server at that point since only you on the computer running it can connect)!

    Now, if you run this, it should work, except that if you are using my BloodBall.png sprite provided above (or if you get the same image from somewhere else), then you'll notice that the ball hovers in the air/has an incorrect collision box. Note that it may do this to other sprites too, so if your encountering this problem (or even if you're not), read on. We can fix this by going back to BloodBall.cfg (or whatever you called it), and going to the middle of the config file (line 35, below bool shape_platform and above u8 block_support), and changing the answer to "@f32 verticesXY = " to "0.0; 0.0; 5.0; 0.0; 5.0; 5.0; 0.0; 5.0;" (1 pixel smaller then the actual size of the sprite - note that before and still now I have been assuming your sprite has no blank/clear pixels on the side aka parts of the sprite touches the borders), which changes the collision "box."

    Save the file (you can exit it - actually, keep it open), exit the/any runlocalhost.bat's that are active (either the command prompt window, which is by default black background with white text on Windows) or the KAG window that popped out that the .bat file is using, and then running it again (which is basically restarting so it refreshes and used the new updated version of the mod). Now, it should be working! But, you'll won't be able to put in in your inventory, because it is too big...

    [​IMG]

    So, at the bottom of the ball's .cfg file, change "u8 inventory_used_width" and the height version's answer from "3" to "1" (or, if you have a larger ball, you can change it to "2") for each of those two lines (lines 63-64), which decides how many inventory spaces or slots it takes up (the archer, knight, and builder has a 2x2 inventory). The line below that (line 65), u8 inventory_max_stacks, decides how many of the same exact item/object/blob (itself) can be "stacked" (have multiple of the same entity stored in the same slot/space). I'm gonna leave it at "1" (so only 1 can be stacked/stored in the designated amount of slots/space). Now it will work!

    Before we go, I just want to say two things. First, characters (e.g. classes) only use the inventory_icon, inventory_used, and inventory_max stats (typically) when the entity dies and it turns into a (pick-up-able) dead body - otherwise, it never uses it (again, normally - this is the case in vanilla KAG, but it rarely may not be the case in KAG mods). Secondly, the BloodBall.cfg code is directly below for copying/reference..!

    Code:
    # Keg.cfg
    
    $sprite_factory                                   = generic_sprite
    @$sprite_scripts                                  =
    $sprite_texture                                   = BloodBall.png
    s32_sprite_frame_width                            = 6
    s32_sprite_frame_height                           = 6
    f32 sprite_offset_x                               = 0
    f32 sprite_offset_y                               = 0
    
    $sprite_gibs_start                                = *start*
    
    $sprite_gibs_end                                  = *end*
    
    $sprite_animation_start                           = *start*
    
        $sprite_animation_default_name                = default
        u16 sprite_animation_default_time             = 0
        u8_sprite_animation_default_loop              = 0
        @u16 sprite_animation_default_frames          = 0; 1; 2; 3;
    
    $sprite_animation_end                             = *end*
    
    $shape_factory                                    = box2d_shape
    @$shape_scripts                                   =
    f32 shape_mass                                    = 5.0
    f32 shape_radius                                  = 6.0
    f32 shape_friction                                = 0.35
    f32 shape_elasticity                              = 0.7
    f32 shape_buoyancy                                = 0.8
    f32 shape_drag                                    = 0.6
    bool shape_collides                               = yes
    bool shape_ladder                                 = no
    bool shape_platform                               = no
    @f32 verticesXY                                   = 0.0; 0.0;
                                                        5.0; 0.0;
                                                        5.0; 5.0;
                                                        0.0; 5.0;
    u8 block_support                                  = 0
    bool block_background                             = no
    bool block_lightpasses                            = no
    bool block_snaptogrid                             = no
    
    $movement_factory                                 =
    
    $brain_factory                                    =
    
    $attachment_factory                               = box2d_attachment
    @$attachment_scripts                              =
    @$attachment_points                               = PICKUP; 7; 3; 1; 0; 0;
    
    $inventory_factory                                =
    
    $name                                             = bloodball
    @$scripts                                         = FakeRolling.as;
                                                        #NoPlayerCollision.as;
    f32 health                                        = 3.0
    $inventory_name                                   = Blood Ball
    $inventory_icon                                   = -
    u8 inventory_icon_frame                           = 0
    u8 inventory_icon_frame_width                     = 0
    u8 inventory_icon_frame_height                    = 0
    u8 inventory_used_width                           = 1
    u8 inventory_used_height                          = 1
    u8 inventory_max_stacks                           = 1
    
     
    Last edited: Sep 8, 2018
    gausscone, platy1knight and epsilon like this.
  3. Adding Emotes (OLD, Broken)
    Most of this code is usable; Once I update this to fix it so it works with current KAG I will reply to this/the forum post with an update when it is fixed, and I'll remove the "(OLD, Broken)" tagline.
    In this tutorial, we'll be covering on how to add "Emotes" (referred to as "Emoticons" in the emote wheel and in the sprite, but they are called "Emotes" in-code). It actually is pretty easy. I've already started/made an unreleased work-in-progress "MoreEmotes" mod, so I'm just gonna show you how to add 1 single new emote, but the process is the exact same for adding more than 1. Note that the emotes canNOT be animated and/or a .gif file (an animated image that autoplays and loops - not a video). For this, I'll be using a emote from the forums here (that's not in-game):​

    :X3:

    This/the KAG forums refers it to as X3. To grab that image/forum emote, right click on it and click on "Save image" or "Save image as..." (make sure it is a .png file), and you can either save it in your mod's folder (I'm going to be assuming for the rest of these tutorials that you are only using 1 mod/folder for these tutorials, or that you are making multiple folders in the KAG\Mods path, and if you do make multiple mods remember to add them to mods.cfg - regardless, I'm not going to tell you to "make a mod folder" anymore usually), or save it in downloads. Now, while I could right now dive in on spriting emotes, including ripping sprites from other places and make them work with KAG, but I'm going to save that for later in a "Guide to Spriting KAG" tutorial - and if you really want to know/add them, you SHOULD be able to figure it out. So, here's the modded Emoticons.png file I'm using (originally gotten from Base\Entities\Common\Emotes\Emoticons.png), that has the "X3" face (to be honest, I don't understand what it is - it sure doesn't look like a "cute face" - so I'm just going to call it "X3"):

    [​IMG]

    That white space right there at the bottom IS present in the vanilla version, so I'm going to keep it the same (thus it won't bug-out). So, download and save that in your mods folder, and make sure the file is called Emoticons.png. I suggest creating a folder named "Emotes" within your mod's folder since we'll be having multiple files (5 files including the sprite), and putting all the files from this tutorial into there. If your curious, I scaled the forum emote down to half it's original size (its "pixels" are 2x2 px instead of 1x1 px), removed a single dark grey pixel, and copied the "..." emote in the .png and changed the bubble a bit to accommodate the face. Now that we have the sprite, let's code..!

    First, copy and paste KAG\Base\Entities\Common\Emotes\EmotesCommon.as into our mods. Open it up, and go to the bottom of the enum Emote Indices list contained within the { } brackets - this is this is one of the two lists that set/add the emotes. Go near the bottom, and at the blank line above "emotes_total," (which is on line 56), and below "raised," (line 54). I'm going to add two blank lines, so that we have 3 blank line separating "raised" and "emotes_total" - in the middle, I'm going to write an AngelScript single-line comment (.as), which is denoted by writing 2 forward slashes (no shift + "? and /" button), "// New Emote(s)," and hit enter again for a blank line where I'll write the following script: X3, (with the comma) on line 57 under my comment.

    You see, in lists in AngelScript, all the "answers" in the list (which are basically "sub-vars" of the "master-var" list) except the last one end with a comma (","), with the very last one having no comma at all. I'm not sure which of the two lists in EmotesCommon.as does this (or maybe it's both?), but one or both of them have their emoticons listed left to right, top to bottom. And since I/you'd just merely add another emote right next to the last emote that is existing at the time of writing ("raised" - the alternate "huh" -type emote), we can just write it after the last emote (raised). Note that in lists blank spaces and comments do not affect them (assuming they are not trapped in " " used for strings).

    Now, we're gonna add X3 (they need to be the same/in sync) to the new list that starts at line 64 (const string[] names). And do the same thing as before (add it to the very end of the list, the line above the closing } curly bracket), but add quotation marks (" ") around the emote's name so it's a string (so it will look like "X3" - case-sensitive). Make sure you add a comma directly after "raised" and have no comma at the last emote ("X3")! If you don't do this, sadly you'll get a "[rules] core not found" error in the command prompt window when you run runlocalhost.bat (or dedicatedserver.bat), and you'll be stuck in an odd GUI-less spectator mode. Now, save EmotesCommon.as and close it (the editor window/tab), then "grab" (copy and paste) EmoteBubble.as (again from the Base folder). Here, we can add emotes to the emote wheel (if you don't want to do that, instead wanting to add/change a new hotkey for it, then go down to the last paragraph!).

    Opening the scripting file, copy line 146 (or any of them really), "this.AddBubble("Musical Note", Emotes::note);" and paste below that within the curly brackets, changing "note" as in Emotes::note to what the new emote is called in EmotesCommon.as, and replacing the words Musical Note in the string (quotation marks " ") with what you want to see the game say when you hover over the emote in the emote wheel (I'm going to write X3). Unfourantly, this means that the up, down, left, and right pointing hand emotes are not perfectly aligned - if you want to "fix" this, just delete some other random emote, preferably the one you hate/dislike the most. And now we're done!

    [​IMG]

    I could just leave it off right there, but I won't - I'll teach you how to make more/new hotkeys for emotes, specifically for the "0" number key! Note though that by doing this you'll override any one's hotkey settings when they are playing your mod (so everyone will have the same hotkeys). This is totally independent from the emote wheel. I'm going to set the emoticon for this new hotkey to the dots (writing) emote. Copy and paste EmoteHotkey.as in Base\Entities\Common\Emotes, open it, and copy line 14 (u8 emote_9 = 0;) and paste it directly below it, changing "emote_9" to "emote_0" (I'm keeping the naming convention, but it shouldn't break if you call it i.e. emote_X3 - just remember we're gonna reference this in 2 other places in this script file). Then copy line 84 and paste directly below it, again replace "9" with "0." Finally, go down and copy and paste directly below the else if statement, including the brackets (line 124 to 127 - 4 lines), yet again replacing "9" (including in the KEY_KEY_9) with "0" in our new copied and pasted block of code. And now it should work! Below I've gotten all the keys that you can replace KEY_KEY_9 or the 0 version with for using different keys (it is after all the other real script files), gotten from KAG\Manual\interface\Enums.txt. Note that these hotkeys cannot (as far as I know) be remapped by players without modifying your modded EmoteHotkey.as script.

    EmotesCommon.as
    Code:
    //handy dandy frame lookup
    namespace Emotes
    {
        //note: it's recommended to use the names in-config
        //        for future compatibility; emote indices _may_ get re-ordered
        //        but we will try not to rename emoticons
    
        enum Emote_Indices
        {
            skull = 0,  //0
            blueflag,
            note,
            right,
            smile,
            redflag,
            flex,
            down,
            frown,
            troll,
            finger,        //10
            left,
            mad,
            archer,
            sweat,
            up,
            laugh,
            knight,
            question,
            thumbsup,
            wat,        //20
            builder,
            disappoint,
            thumbsdown,
            drool,
            ladder,
            attn,
            okhand,
            cry,
            wall,
            heart,        //30
            fire,
            check,
            cross,
            dots,
            cog,
            think,
            laughcry,
            derp,
            awkward,
            smug,       //40
            love,
            kiss,
            pickup,
            raised,
        
            //New Emote(s)
            X3,
    
            emotes_total,
            off
        };
    
        //careful to keep these in sync!
        const string[] names = {
            "skull",
            "blueflag",
            "note",
            "right",
            "smile",
            "redflag",
            "flex",
            "down",
            "frown",
            "troll",
            "finger",
            "left",
            "mad",
            "archer",
            "sweat",
            "up",
            "laugh",
            "knight",
            "question",
            "thumbsup",
            "wat",
            "builder",
            "disappoint",
            "thumbsdown",
            "drool",
            "ladder",
            "attn",
            "okhand",
            "cry",
            "wall",
            "heart",
            "fire",
            "check",
            "cross",
            "dots",
            "cog",
            "think",
            "laughcry",
            "derp",
            "awkward",
            "smug",
            "love",
            "kiss",
            "pickup",
            "raised",
        
            //New Emote(s)
            "X3"
        };
    }
    
    void set_emote(CBlob@ this, u8 emote, int time)
    {
        if (emote >= Emotes::emotes_total)
        {
            emote = Emotes::off;
        }
    
        this.set_u8("emote", emote);
        this.set_u32("emotetime", getGameTime() + time);
        bool client = this.getPlayer() !is null && this.isMyPlayer();
        this.Sync("emote", !client);
        this.Sync("emotetime", !client);
    }
    
    void set_emote(CBlob@ this, u8 emote)
    {
        if (this.isInInventory())
        {
            CBlob@ inventoryblob = this.getInventoryBlob();
            if (inventoryblob !is null && inventoryblob.getName() == "crate"
                && inventoryblob.exists("emote"))
            {
                CBitStream params;
                params.write_u8(emote);
                params.write_u32(getGameTime() + 90);
                inventoryblob.SendCommand(inventoryblob.getCommandID("emote"), params);
                this.SendCommand(this.getCommandID("emote"), params);
            }
        }
        else
        {
            set_emote(this, emote, 90);
        }
    }
    
    bool is_emote(CBlob@ this, u8 emote = 255, bool checkBlank = false)
    {
        u8 index = emote;
        if (index == 255)
            index = this.get_u8("emote");
    
        u32 time = this.get_u32("emotetime");
    
        return time > getGameTime() && index != Emotes::off && (!checkBlank || (index != Emotes::dots));
    }
    
    
    EmoteBubble.as
    Code:
    // Draw an emoticon
    
    #include "EmotesCommon.as";
    
    void onInit(CBlob@ blob)
    {
        blob.addCommandID("emote");
    
        CSprite@ sprite = blob.getSprite();
        blob.set_u8("emote", Emotes::off);
        blob.set_u32("emotetime", 0);
        //init emote layer
        CSpriteLayer@ emote = sprite.addSpriteLayer("bubble", "Entities/Common/Emotes/Emoticons.png", 32, 32, 0, 0);
        emote.SetIgnoreParentFacing(true);
        emote.SetFacingLeft(false);
    
        if (emote !is null)
        {
            emote.SetOffset(Vec2f(0, -sprite.getBlob().getRadius() * 1.5f - 16));
            emote.SetRelativeZ(100.0f);
            {
                Animation@ anim = emote.addAnimation("default", 0, true);
    
                for (int i = 0; i < Emotes::emotes_total; i++)
                {
                    anim.AddFrame(i);
                }
            }
            emote.SetVisible(false);
            emote.SetHUD(true);
        }
    
        AddBubblesToMenu(blob);
    }
    
    
    
    void onTick(CBlob@ blob)
    {
        blob.getCurrentScript().tickFrequency = 6;
        // if (blob.exists("emote"))     will show skull if none existant
        if (!blob.getShape().isStatic())
        {
            CSprite@ sprite = blob.getSprite();
            CSpriteLayer@ emote = sprite.getSpriteLayer("bubble");
    
            const u8 index = blob.get_u8("emote");
            if (is_emote(blob, index) && !blob.hasTag("dead") && !blob.isInInventory())
            {
                blob.getCurrentScript().tickFrequency = 1;
                if (emote !is null)
                {
                    emote.SetVisible(true);
                    emote.animation.frame = index;
    
                    emote.ResetTransform();
    
                    CCamera@ camera = getCamera();
                    if (camera !is null)
                    {
                        f32 angle = -camera.getRotation() + blob.getAngleDegrees();
                        emote.RotateBy(-angle, Vec2f(0, 20));
                    }
                }
            }
            else
            {
                emote.SetVisible(false);
            }
        }
    }
    
    void onCommand(CBlob@ this, u8 cmd, CBitStream @params)
    {
        if (cmd == this.getCommandID("emote"))
        {
            u8 emote = params.read_u8();
            u32 emotetime = params.read_u32();
            this.set_u8("emote", emote);
            this.set_u32("emotetime", emotetime);
        }
    }
    
    void onClickedBubble(CBlob@ this, int index)
    {
        set_emote(this, index);
    }
    
    void AddBubblesToMenu(CBlob@ this)
    {
        this.LoadBubbles("Entities/Common/Emotes/Emoticons.png");
    
        //for (int i = Emotes::skull; i < Emotes::cog; i++) {
        //    if (i != Emotes::pickup && i != Emotes::blank && i != Emotes::dots) {
        //        this.AddBubble( "", i );
        //    }
        //}
    
        //this.AddBubble("", Emotes::blueflag);
        //this.AddBubble("", Emotes::redflag);
    
        this.AddBubble("Point Right", Emotes::right);
    
        this.AddBubble("Laughing", Emotes::laugh);
        this.AddBubble("Smiling", Emotes::smile);
        this.AddBubble("Skull", Emotes::skull);
        this.AddBubble("Laughing Crying", Emotes::laughcry);
        this.AddBubble("Awkward Laughing", Emotes::awkward);
        this.AddBubble("Smug", Emotes::smug);
        this.AddBubble("Troll", Emotes::troll);
        //this.AddBubble("", Emotes::raised);
        this.AddBubble("What", Emotes::wat);
    
        this.AddBubble("Point Down", Emotes::down);
    
        this.AddBubble("Derp", Emotes::derp);
        this.AddBubble("Mad", Emotes::mad);
        this.AddBubble("Disappointed", Emotes::disappoint);
        this.AddBubble("Frown", Emotes::frown);
        this.AddBubble("Crying", Emotes::cry);
        this.AddBubble("Archer", Emotes::archer);
        this.AddBubble("Knight", Emotes::knight);
        this.AddBubble("Builder", Emotes::builder);
    
        this.AddBubble("Point left", Emotes::left);
    
        this.AddBubble("Heart", Emotes::heart);
        this.AddBubble("Love", Emotes::love);
        this.AddBubble("Kiss", Emotes::kiss);
        this.AddBubble("Flex", Emotes::flex);
        this.AddBubble("Rude Gesture", Emotes::finger);
        //this.AddBubble("", Emotes::drool);
        this.AddBubble("Thumbs Down", Emotes::thumbsdown);
        this.AddBubble("Thumbs Up", Emotes::thumbsup);
        this.AddBubble("OK", Emotes::okhand);
    
        this.AddBubble("Point Up", Emotes::up);
    
        this.AddBubble("Thinking", Emotes::think);
        this.AddBubble("Sweat Drop", Emotes::sweat);
        this.AddBubble("Ladder", Emotes::ladder);
        this.AddBubble("Attention", Emotes::attn);
        this.AddBubble("Question", Emotes::question);
        this.AddBubble("Fire", Emotes::fire);
        this.AddBubble("Wall", Emotes::wall);
        this.AddBubble("Musical Note", Emotes::note);
    
        //derp note
    
        //New Emote(s)
        this.AddBubble("X3", Emotes::X3);
    }
    
    EmoteHotkeys.as
    Code:
    #include "EmotesCommon.as";
    
    bool init = false;
    u8 emote_1 = 0;
    u8 emote_2 = 0;
    u8 emote_3 = 0;
    u8 emote_4 = 0;
    u8 emote_5 = 0;
    u8 emote_6 = 0;
    u8 emote_7 = 0;
    u8 emote_8 = 0;
    u8 emote_9 = 0;
    u8 emote_0 = 0;
    
    const string emote_config_file = "EmoteBindings.cfg";
    
    //helper - allow integer entries as well as name entries
    u8 read_emote(ConfigFile@ cfg, string name, u8 default_value)
    {
        string attempt = cfg.read_string(name, "");
        if (attempt != "")
        {
            //replace quoting and semicolon
            //TODO: how do we not have a string lib for this?
            string[] check_str = {";",   "\"", "\"",  "'",  "'"};
            bool[] check_pos =   {false, true, false, true, false};
            for(int i = 0; i < check_str.length; i++)
            {
                string check = check_str[i];
                if(check_pos[i]) //check front
                {
                    if(attempt.substr(0, 1) == check)
                    {
                        attempt = attempt.substr(1, attempt.size() - 1);
                    }
                }
                else //check back
                {
                    if(attempt.substr(attempt.size() - 1, 1) == check)
                    {
                        attempt = attempt.substr(0, attempt.size() - 1);
                    }
                }
            }
            //match
            for(int i = 0; i < Emotes::names.length; i++)
            {
                if(attempt == Emotes::names[i])
                {
                    return i;
                }
            }
    
            //fallback to u8 read
            u8 read_val = cfg.read_u8(name, default_value);
            return read_val;
        }
        return default_value;
    }
    
    void onInit(CBlob@ this)
    {
        this.getCurrentScript().runFlags |= Script::tick_myplayer;
        this.getCurrentScript().removeIfTag = "dead";
    
        if (!init)
        {
            //only load the cfg once to avoid
            //too much file access!
            init = true;
    
            ConfigFile cfg = ConfigFile();
            cfg.loadFile(emote_config_file);
    
            emote_1 = read_emote(cfg, "emote_1", Emotes::X3);
            emote_2 = read_emote(cfg, "emote_2", Emotes::smile);
            emote_3 = read_emote(cfg, "emote_3", Emotes::frown);
            emote_4 = read_emote(cfg, "emote_4", Emotes::mad);
            emote_5 = read_emote(cfg, "emote_5", Emotes::laugh);
            emote_6 = read_emote(cfg, "emote_6", Emotes::wat);
            emote_7 = read_emote(cfg, "emote_7", Emotes::troll);
            emote_8 = read_emote(cfg, "emote_8", Emotes::disappoint);
            emote_9 = read_emote(cfg, "emote_9", Emotes::ladder);
            emote_0 = read_emote(cfg, "emote_0", Emotes::dots);
        }
    }
    
    void onTick(CBlob@ this)
    {
        CControls@ controls = getControls();
        if (controls.isKeyJustPressed(KEY_KEY_1))
        {
            set_emote(this, emote_1);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_2))
        {
            set_emote(this, emote_2);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_3))
        {
            set_emote(this, emote_3);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_4))
        {
            set_emote(this, emote_4);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_5))
        {
            set_emote(this, emote_5);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_6))
        {
            set_emote(this, emote_6);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_7))
        {
            set_emote(this, emote_7);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_8))
        {
            set_emote(this, emote_8);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_9))
        {
            set_emote(this, emote_9);
        }
        else if (controls.isKeyJustPressed(KEY_KEY_0))
        {
            set_emote(this, emote_0);
        }
    }
    
    List of Keys Recognized by KAG
    Code:
    //these are more general ones (mostly)
    //as you'll see, some of them are both the left and right versions of the keys
    //i.e. KEY_SHIFT = KEY_LSHIFT and KEY_RSHIFT
    KEY_LBUTTON
    KEY_RBUTTON
    KEY_CANCEL
    KEY_MBUTTON
    KEY_XBUTTON1
    KEY_XBUTTON2
    KEY_BACK
    KEY_TAB
    KEY_CLEAR //back space
    KEY_RETURN
    KEY_SHIFT
    KEY_CONTROL
    KEY_MENU
    KEY_PAUSE
    KEY_CAPITAL // exclamation point?
    KEY_ESCAPE
    KEY_SPACE //space bar
    KEY_PRIOR// not sure what this is...
    
    //here are some keys that are above the arrow keys on a standard keyboard:
    KEY_NEXT
    KEY_END
    KEY_HOME
    
    //arrow keys
    KEY_LEFT
    KEY_UP
    KEY_RIGHT
    KEY_DOWN
    
    //other specialized non-typing keys
    KEY_SELECT
    KEY_PRINT
    KEY_EXECUT
    KEY_INSERT
    KEY_DELETE // not back space button
    KEY_HELP
    
    //number keys
    KEY_KEY_0
    KEY_KEY_1
    KEY_KEY_2
    KEY_KEY_3
    KEY_KEY_4
    KEY_KEY_5
    KEY_KEY_6
    KEY_KEY_7
    KEY_KEY_8
    KEY_KEY_9
    
    //letter keys
    KEY_KEY_A
    KEY_KEY_B
    KEY_KEY_C
    KEY_KEY_D
    KEY_KEY_E
    KEY_KEY_F
    KEY_KEY_G
    KEY_KEY_H
    KEY_KEY_I
    KEY_KEY_J
    KEY_KEY_K
    KEY_KEY_L
    KEY_KEY_M
    KEY_KEY_N
    KEY_KEY_O
    KEY_KEY_P
    KEY_KEY_Q
    KEY_KEY_R
    KEY_KEY_S
    KEY_KEY_T
    KEY_KEY_U
    KEY_KEY_V
    KEY_KEY_W
    KEY_KEY_X
    KEY_KEY_Y
    KEY_KEY_Z
    
    //???
    KEY_LWIN
    KEY_RWIN
    KEY_APPS
    KEY_SLEEP
    
    //numpad
    KEY_NUMPAD0
    KEY_NUMPAD1
    KEY_NUMPAD2
    KEY_NUMPAD3
    KEY_NUMPAD4
    KEY_NUMPAD5
    KEY_NUMPAD6
    KEY_NUMPAD7
    KEY_NUMPAD8
    KEY_NUMPAD9
    KEY_MULTIPLY // *
    KEY_ADD // +
    KEY_SEPARATOR // ???
    KEY_SUBTRACT // -
    KEY_DECIMAL // Del or .
    KEY_DIVIDE // /
    
    //F-number keys
    KEY_F1
    KEY_F2
    KEY_F3
    KEY_F4
    KEY_F5
    KEY_F6
    KEY_F7
    KEY_F8
    KEY_F9
    KEY_F10
    KEY_F11
    KEY_F12
    
    //random
    KEY_NUMLOCK
    KEY_SCROLL
    KEY_LSHIFT
    KEY_RSHIFT
    KEY_LCONTROL
    KEY_RCONTROL
    KEY_LMENU
    KEY_RMENU
    KEY_PLUS
    KEY_COMMA
    KEY_MINUS
    KEY_PERIOD
    KEY_PLAY
     
    Last edited: Apr 19, 2019
  4. Teams
    Before we go onto other subjects such as shops, I want to go talk about a subject that is, in this case, is more learning how KAG's programming works vs. coding (it is important for you to understand everything in here). But this is actually useful for people who play in "commands" servers - so maybe this isn't a scripting thing?

    When your part of a team such as blue or red (the standard teams), by default all members part of your team are allies who can't do anything to hurt you (there are some exceptions to this, i.e. a keg's explosion, dropped spikes, and of course trolling). Teams, obviously, work together to ultimately achieve the same goal(s). Teammates can go/use correctly special blocks and use certain objects if they're on their team - otherwise, they either need to capture it or to destroy it if they can't use it. "Trap Blocks" as they are called in King Arthur's Gold are just blocks that let enemies fall down that aren't part of the same team - used for making traps. Retracted (stone) spikes only hurt those who aren't flying their team color. Obvious and basic KAG stuff, right?

    By default, there are 2 named teams (Blue Team and Red Team, each defined in team1.cfg and team2.cfg respectfully), blue and red, 7 uniquely-colored teams including blue and red (the other ones are lime green, light purple, orange, aquamarine, and a barely darker purple-hinted blue), and a grey color used for all other teams. If say, you want to change the colors, you can find the TeamPalette.png file in King Arthur's Gold\Base\Sprites (it is a tiny little file - 8x4 px)..!

    [​IMG]
    [​IMG]

    There's also a special "neutral" team (it still uses the grey color), which allows that team and all others to use it (in the case of trap blocks, though, all others except this neutral team DO fall down/go through them - you'll see this used in TDM maps, especially ones from mods). You'll almost always see this in every single TDM map, whether as trap blocks, spikes, or doors.

    In-game with chat commands (sv_test = 1) on, you can type "!team -1" OR "!team 255" to become part of (it should also change your team color, but not on the scoreboard nor the kill feed) the special neutral team. To join blue, type !team 0 (the team.cfg files are actually named incorrectly if your assuming that you need to type e.g. "!team 1" in order to turn to blue), and to join red type !team 1 (team2.cfg). The other 5 colored teams you can join by typing !team 2, !team 3, !team 4, !team 5, and !team 6, for green, purple, orange, aquamarine (odd blue), and the alternate blue coloration, respectfully. In case you haven't figured it out, "!team" is to show that you're about to type a command to change you to a different team [number].

    Note that when you join, there will NOT be a "NicknameHere (Username) has joined TeamName" printed to the chat, and that once you die you'll be reset to your "default" team (the one that the scoreboard/chat message shows you are part of) and spawn there. If you type in letters as the team you want to join e.g. !team Dog, then it won't work, but if you type !team 90sDogT0wn it will work, AND if you type !team 900, the game will think your part of the same team! Why is that? Because it ignores the letters and only searches for numbers (this won't work if set it in a .as file since it will break the object having its team set to the team i.e. C4tPl4gue)! In an AngelScript file, you can actually set an entity to e.g. !team -1 (more about that later).

    [​IMG]

    If a sprite uses the colors RGB 44, 175, 222, RGB 29, 133, 171, RGB 26, 78, 131, and finally the RGB color 34, 39, 96, then the colors in-game will change to the blob's team color(s). In vanilla, there are 4 colors per team, with the top/uppermost pixel being the bright "normal" color, and the lower/other 3 getting progressively darker (used for shading), but technically you could mod this into what colors you want! The last color in the list (far right, aka grey) is used for its team (!team 8) and all other ones (e.g. !team -1). You can, though, add unique team colors by extending the file, so e.g. !team 8 would have its own special team color (note that you need to keep the 1x4 grey column at the end on the far right, otherwise the neutral team won't have the same color), adding it right before the grey color. Here's an example TeamPalette that I extended to support more colors:

    [​IMG]
    [​IMG]

    Now with this, we have more teams (visually)! Lastly, I'm going to show you how to make certain blobs be part of a certain team (on spawning). Let's say you want to make it so you can "shoot" away kegs [with arrows], regardless of what team you are on (characters, vehicles, animals, and so forth still won't collide with it, though). If we wanted to make it like this, we can create a new .as script in your mod's folder, (which I'll call SetTeamToNeut), and type "void onInit(CBlob@ this) { this.server_setTeamNum(-1); }" - when the blob is spawned, make the server/game change the blob that uses this file (aka is running this script) to make or set that blob's team [number] to -1 (the special neutral team)! "this.server_setTeamNum" is the actual line used whenever you want to either set the entity's team at the start (void onInit) or anywhere else..! Now, let's copy and paste Keg.cfg from KAG\Base\Entities\Items\Explosives into our mod, and add at the bottom of the list of $@scripts the line "SetTeamToNeut.as;" (be sure to include the ";" or semi-colon, and don't add the quotation " marks), and we're done!

    And besides a few gamemode.cfg related things (e.g. setting what teams are playing against each other, such as Blue Team vs. Red Team, specifically the team's name and the chat color that member's chat messages send out as), you know about everything concerning teams !!!

    EDIT (3/18/2019): I have discovered that vanilla KAG already has a script that does the same thing as my custom SetTeamToNeut.as - this vanilla script is called UnsetTeam.as. You can use that instead of SetTeamToNeut.as. Do note that UnsetTeam.as contains 1 more line of code: "this.getCurrentScript().runFlags |= Script::remove_after_this;." This basically means once this script has run the particular piece of code (in this case, once the blob's initiation or void onInit (CBlob@ this) has been executed), it'll remove the script from the blob. This line is technically not needed for either script(s), but I've decided to explain it so you can understand what it is and/or does.

    SetTeamToNeut.as
    Code:
    void onInit(CBlob@ this) // once spawned
    {
        this.server_setTeamNum(-1); //team -1 is the same as team 255
    }
    Keg.cfg
    Code:
    # Keg.cfg
    
    $sprite_factory                                   = generic_sprite
    @$sprite_scripts                                  = Keg.as;
                                                        Wooden.as;
    $sprite_texture                                   = Keg.png
    s32_sprite_frame_width                            = 16
    s32_sprite_frame_height                           = 16
    f32 sprite_offset_x                               = 0
    f32 sprite_offset_y                               = 0
    
    $sprite_gibs_start                                = *start*
    
        $gib_type                                     = predefined
        $gib_style                                    = dirt
        u8_gib_count                                  = 5
        @u8_gib_frame                                 = 4; 5; 6; 7;
        f32 velocity                                  = 20.0
        f32 offset_x                                  = 0.0
        f32 offset_y                                  = 0.0
    
        $gib_type                                     = predefined
        $gib_style                                    = wood
        u8_gib_count                                  = 10
        @u8_gib_frame                                 = 1; 2; 3; 4; 5; 6; 7;
        f32 velocity                                  = 15.0
        f32 offset_x                                  = 0.0
        f32 offset_y                                  = 0.0
    
    $sprite_gibs_end                                  = *end*
    
    $sprite_animation_start                           = *start*
    
        $sprite_animation_default_name                = default
        u16 sprite_animation_default_time             = 0
        u8_sprite_animation_default_loop              = 0
        @u16 sprite_animation_default_frames          = 0; 1; 2; 3;
    
    $sprite_animation_end                             = *end*
    
    $shape_factory                                    = box2d_shape
    @$shape_scripts                                   =
    f32 shape_mass                                    = 30.0
    f32 shape_radius                                  = 6.0
    f32 shape_friction                                = 0.35
    f32 shape_elasticity                              = 0.03
    f32 shape_buoyancy                                = 0.8
    f32 shape_drag                                    = 0.6
    bool shape_collides                               = yes
    bool shape_ladder                                 = no
    bool shape_platform                               = no
    @f32 verticesXY                                   =
    u8 block_support                                  = 0
    bool block_background                             = no
    bool block_lightpasses                            = no
    bool block_snaptogrid                             = no
    
    $movement_factory                                 =
    
    $brain_factory                                    =
    
    $attachment_factory                               = box2d_attachment
    @$attachment_scripts                              =
    @$attachment_points                               = PICKUP; 7; 3; 1; 0; 0;
    
    $inventory_factory                                =
    
    $name                                             = keg
    @$scripts                                         = FakeRolling.as;
                                                        Activatable.as;
                                                        KegVoodoo.as;
                                                        Keg.as;
                                                        ExplodeOnDie.as;
                                                        NoPlayerCollision.as;
                                                        BehindWhenAttached.as;
                                                        SetTeamToCarrier.as;
                                                        SetDamageToCarrier.as;
                                                        SetTeamToNeut.as; // NEW
    f32 health                                        = 3.0
    $inventory_name                                   = Keg
    $inventory_icon                                   = -
    u8 inventory_icon_frame                           = 0
    u8 inventory_icon_frame_width                     = 0
    u8 inventory_icon_frame_height                    = 0
    u8 inventory_used_width                           = 3
    u8 inventory_used_height                          = 3
    u8 inventory_max_stacks                           = 1
    
     
    Last edited: Aug 19, 2019
    epsilon, joshua12131415 and jonipro like this.