diff --git a/Advanced templates/advanced_instructions.txt b/Advanced templates/advanced_instructions.txt index d563a507f7d85999f52b9834ab14ad6eac9ad75a..6db0755da4d818139afef17e2c9ef39ab65c6ea4 100644 --- a/Advanced templates/advanced_instructions.txt +++ b/Advanced templates/advanced_instructions.txt @@ -1,6 +1,6 @@ This text file provides instructions on how to edit templates under the "advanced" folder. It contains instructions, FAQ and troubleshooting, and an example of how to do it. -The advanced templates are designed to allow it to be edited by users, in order to add more frames to any follower state's animation. It provides every possible distinct sprite: all six states (idle, following, hurt, lose, win, hitconfirm), and an asymmetrical appearance. +The advanced templates are designed to allow it to be edited by users, in order to add more frames to any follower state's animation. It provides every possible distinct sprite: all seven states (idle, following, hurt, lose, win, hitconfirm, ring), and frames for a follower of either symmetrical or asymmetrical appearance. By default, each animation lasts for two frames. To add more, first increase the height of your spritesheet by 128 for each frame you want to add. If you're adding frames to several animations, increase the height based on whichever has the most. For example, if your most fluid animation is 4 frames, you'll be adding two new frames: that's two counts of 128, plus the original height of 256, which means your final spritesheet height should be 512. @@ -22,23 +22,23 @@ FAQ and troubleshooting: The solid horizontal line represents the origin point for sprites. You can treat it as "ground level" if you'd like, but it's not mandatory. The vertical dotted line simply indicates the center of each sprite's 128x128 box, to help with symmetry. "Followermaker.exe won't open/won't read my file." -Opening followermaker is possible, but not required: dragging a completed folder onto it in your file explorer is enough. If you've done this, and it still will not create a .wad, check the following: -That your commas are properly placed in properties.txt -That your files are named: -followersound.ogg -properties.txt -sprites.png -That your followersound is a .ogg file, as opposed to a .wav or .mp4 or other file type +Opening Followermaker is possible, but not required: dragging a completed folder onto it in your file explorer is enough. If you've done this, and it still will not create a .wad, check the following: +- That your commas are properly placed in properties.txt +- That your files are named: + - follower_sound.ogg + - properties.txt + - sprites.png +- That your follower_sound is a .ogg file, as opposed to a .wav or .mp4 or other file type If none of these work, then feel free to shout Superstarxalien about it on Discord or Twitter -"Does followermaker support 16 angle rotation?" -16 angle rotation is not supported, but it is possible. Essentially, if you can work out how to name the frames and order the template, you can do it: however, followermaker does not currently have a template or guide for it. +"Does Followermaker support 16 angle rotation?" +16 angle rotation is supported by the program, though there aren't any templates included with it. Essentially, if you can work out how to name the frames and order the template, you can do it. "Can I edit the simple templates in the same way as the advanced ones?" The short answer is no. The simple templates are in a grid format that's easier on the eyes, but the animations not being horizontally uniform on the spritesheet means adding new frames would either involve creating entire new rows or moving existing ones: the former means you would have to figure out layer steps on your own, and the latter breaks the sheet entirely unless you redid the whole thing. If you want a follower with more frames but less states, simply take an advanced template and remove the states you don't want from .properties. You don't even have to remove the relevant sections from your sprites.png: indeed, you should probably keep them, because the states you keep will be expecting their sprites to be in the same place, and so maintaining the empty space at exactly the same size can cause confusion. "Is 128x128 the maximum size? What about the intended size?" -Ring racers does not inherently have a maximum size. However, followermaker's templates use 128x128 because it is the largest any base game followers use, and upscaling the templates will cause them to break. If you're set on making a follower greater than 128x128, you'll need to alter "sprite_size" and "layer_step_size" to the new max size of your frames, and you'll need to substantially alter the template in order to make it the same size. (Multiplying the scale of the template skips having to rearrange it yourself, but you'll still have to change sprite_size and layer_step_size.) If you do alter the template like this, or in any way that involves changing the width of the template, remember to set graphics_icon back to normal. To change graphics_icon's location, simply indicate the coordinates of the top left of its box, and followermaker will take a 16x16 space starting from there as the follower's icon. graphics_icon overides layer steps, so "the coordinates" are the actual pixel coordinates on your sheet. +Ring racers does not inherently have a maximum size. However, Followermaker's templates use 128x128 because it is the largest any base game followers use, and upscaling the templates will cause them to break. If you're set on making a follower greater than 128x128, you'll need to alter "sprite_size" and "layer_step_size" to the new max size of your frames, and you'll need to substantially alter the template in order to make it the same size. (Multiplying the scale of the template skips having to rearrange it yourself, but you'll still have to change sprite_size and layer_step_size.) If you do alter the template like this, or in any way that involves changing the width of the template, remember to set graphics_icon back to normal. To change graphics_icon's location, simply indicate the coordinates of the top left of its box, and Followermaker will take a 16x16 space starting from there as the follower's icon. graphics_icon overides layer steps, so "the coordinates" are the actual pixel coordinates on your sheet. As for the intended size, it trends much smaller. For reference, a "small" follower might be something like 40x40. Really, though, it depends on your follower, so use your best judgement. Example: @@ -68,7 +68,4 @@ Here is an example of adding a single new frame of animation to the "following" On the symmetrical template, the left number in Layers starts at 5 and ends at 9, and so has been left unchanged. Because the template has a default of 2 frames, this is the 3rd, so the right number in Layers is 3 - 1, which is 2. Each instance of "A" has been replaced with "C." Which letter you choose to copy is not particularly relevant, but it is easier to always copy "A", so that when making many frames you only need to remove one comma. As opposed to copying "B", where you would need to add a comma to almost every new frame. The offset is unchanged, and should never be changed unless you're altering the size of the 128x128 box, which is not recommended unless you know what you're doing. -Finally, because this is the only new frame being added to "following", the final right square brackets does not a comma. It is not depicted here, but "B5" should have a comma added to it in this instance. - - - +Finally, because this is the only new frame being added to "following", the final right square brackets does not a comma. It is not depicted here, but "B5" should have a comma added to it in this instance. \ No newline at end of file diff --git a/Advanced templates/example_asymmetricalrotation_6states_animated/sprites.png b/Advanced templates/example_asymmetricalrotation_6states_animated/sprites.png deleted file mode 100644 index ab205c4f1b219e8baa353ee22940468c0d3d66fc..0000000000000000000000000000000000000000 Binary files a/Advanced templates/example_asymmetricalrotation_6states_animated/sprites.png and /dev/null differ diff --git a/Advanced templates/example_asymmetricalrotation_6states_animated/follower_sound.ogg b/Advanced templates/example_asymmetricalrotation_7states_animated/follower_sound.ogg similarity index 100% rename from Advanced templates/example_asymmetricalrotation_6states_animated/follower_sound.ogg rename to Advanced templates/example_asymmetricalrotation_7states_animated/follower_sound.ogg diff --git a/Advanced templates/example_asymmetricalrotation_6states_animated/properties.txt b/Advanced templates/example_asymmetricalrotation_7states_animated/properties.txt similarity index 87% rename from Advanced templates/example_asymmetricalrotation_6states_animated/properties.txt rename to Advanced templates/example_asymmetricalrotation_7states_animated/properties.txt index 784aa3d68be940c69917b622c2816c92b2aa89a8..0b1ead96c4252d56d9e9009a859fe253a1178887 100644 --- a/Advanced templates/example_asymmetricalrotation_6states_animated/properties.txt +++ b/Advanced templates/example_asymmetricalrotation_7states_animated/properties.txt @@ -52,9 +52,11 @@ "win_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the win state.", "hitconfirm_animation_speed": 35, "hitconfirm_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the hit confirm state.", + "ring_animation_speed": 35, + "ring_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the ring state.", - "follower_states_help": "As you may know, followers have several animations for things that can occur during the race; these are described as \"states\". Followers can have up to 6 states, in order: an idle state, a following state (as in, following the player), a hurt state (when you get hit by an item), a lose state (when you lose the race), a win state (win the race), and a \"hit confirm\" state (occurs when you hit another racer with an item). Of these, the idle state is obligatory in all followers. Additionally, the duration of the hit confirm state is controlled by a variable which can be edited above.", + "follower_states_help": "As you may know, followers have several animations for things that can occur during the race; these are described as \"states\". Followers can have up to 7 states, in order: an idle state, a following state (as in, following the player), a hurt state (when you get hit by an item), a lose state (when you lose the race), a win state (win the race), a \"hit confirm\" state (occurs when you hit another racer with an item), and a \"ring\" state (used whenever Auto Ring is enabled as the follower spends your rings). Of these, the idle state is obligatory in all followers. Additionally, the duration of the hit confirm state is controlled by a variable which can be edited above.", "sprite_help": "The template is divided into several regions, each for a unique sprite, described by the below values. These sprite regions are each categorized according to the follower state in which they belong, and are further split into frames detailing both the rotation angle (indicated by numbers) and animation index (indicated by letters) of the follower. The \"sprite_size\" field determines the general size for each sprite region. \"Layers\" refers to the exact location of each sprite, with \"layer_step_size\" indicating how many pixels are stepped over for the position of the layer; layers also allow for literal layer functionality, allowing you to insert up to four sets of sprite locations, with the first sprite location being applied on top of following locations. You can also edit the offset of sprites, with the default templates providing automatic X axis centering for sprites, offset 16 pixels down on the Y axis.", @@ -459,12 +461,78 @@ "layers": [[47, 1]] } }, + "ring": { + "A1": { + "offset": [64, 112], + "layers": [[48, 0]] + }, + "A2": { + "offset": [64, 112], + "layers": [[49, 0]] + }, + "A3": { + "offset": [64, 112], + "layers": [[50, 0]] + }, + "A4": { + "offset": [64, 112], + "layers": [[51, 0]] + }, + "A5": { + "offset": [64, 112], + "layers": [[52, 0]] + }, + "A6": { + "offset": [64, 112], + "layers": [[53, 0]] + }, + "A7": { + "offset": [64, 112], + "layers": [[54, 0]] + }, + "A8": { + "offset": [64, 112], + "layers": [[55, 0]] + }, + "B1": { + "offset": [64, 112], + "layers": [[48, 1]] + }, + "B2": { + "offset": [64, 112], + "layers": [[49, 1]] + }, + "B3": { + "offset": [64, 112], + "layers": [[50, 1]] + }, + "B4": { + "offset": [64, 112], + "layers": [[51, 1]] + }, + "B5": { + "offset": [64, 112], + "layers": [[52, 1]] + }, + "B6": { + "offset": [64, 112], + "layers": [[53, 1]] + }, + "B7": { + "offset": [64, 112], + "layers": [[54, 1]] + }, + "B8": { + "offset": [64, 112], + "layers": [[55, 1]] + } + }, "graphics": { "icon": { "overwrite_sprite_size": [16, 16], "overwrite_layer_step_size": [1, 1], - "layers": [[6154, 10]] + "layers": [[7178, 10]] } } } diff --git a/Advanced templates/example_asymmetricalrotation_7states_animated/sprites.png b/Advanced templates/example_asymmetricalrotation_7states_animated/sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..3a3e4201bd3b08de800bc8582a6f2906b1f8a531 Binary files /dev/null and b/Advanced templates/example_asymmetricalrotation_7states_animated/sprites.png differ diff --git a/Advanced templates/example_symmetricalrotation_6states_animated/sprites.png b/Advanced templates/example_symmetricalrotation_6states_animated/sprites.png deleted file mode 100644 index adfaef7e8b79441143bb18318f20842ac35e9dce..0000000000000000000000000000000000000000 Binary files a/Advanced templates/example_symmetricalrotation_6states_animated/sprites.png and /dev/null differ diff --git a/Advanced templates/example_symmetricalrotation_6states_animated/follower_sound.ogg b/Advanced templates/example_symmetricalrotation_7states_animated/follower_sound.ogg similarity index 100% rename from Advanced templates/example_symmetricalrotation_6states_animated/follower_sound.ogg rename to Advanced templates/example_symmetricalrotation_7states_animated/follower_sound.ogg diff --git a/Advanced templates/example_symmetricalrotation_6states_animated/properties.txt b/Advanced templates/example_symmetricalrotation_7states_animated/properties.txt similarity index 87% rename from Advanced templates/example_symmetricalrotation_6states_animated/properties.txt rename to Advanced templates/example_symmetricalrotation_7states_animated/properties.txt index 8676dc64da1245b5d6778599da11f1b6a08d51b7..4097e3fb980d565113879aa037c1ed8e5c2f880d 100644 --- a/Advanced templates/example_symmetricalrotation_6states_animated/properties.txt +++ b/Advanced templates/example_symmetricalrotation_7states_animated/properties.txt @@ -52,9 +52,11 @@ "win_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the win state.", "hitconfirm_animation_speed": 35, "hitconfirm_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the hit confirm state.", + "ring_animation_speed": 35, + "ring_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the ring state.", - "follower_states_help": "As you may know, followers have several animations for things that can occur during the race; these are described as \"states\". Followers can have up to 6 states, in order: an idle state, a following state (as in, following the player), a hurt state (when you get hit by an item), a lose state (when you lose the race), a win state (win the race), and a \"hit confirm\" state (occurs when you hit another racer with an item). Of these, the idle state is obligatory in all followers. Additionally, the duration of the hit confirm state is controlled by a variable which can be edited above.", + "follower_states_help": "As you may know, followers have several animations for things that can occur during the race; these are described as \"states\". Followers can have up to 7 states, in order: an idle state, a following state (as in, following the player), a hurt state (when you get hit by an item), a lose state (when you lose the race), a win state (win the race), a \"hit confirm\" state (occurs when you hit another racer with an item), and a \"ring\" state (used whenever Auto Ring is enabled as the follower spends your rings). Of these, the idle state is obligatory in all followers. Additionally, the duration of the hit confirm state is controlled by a variable which can be edited above.", "sprite_help": "The template is divided into several regions, each for a unique sprite, described by the below values. These sprite regions are each categorized according to the follower state in which they belong, and are further split into frames detailing both the rotation angle (indicated by numbers) and animation index (indicated by letters) of the follower. The \"sprite_size\" field determines the general size for each sprite region. \"Layers\" refers to the exact location of each sprite, with \"layer_step_size\" indicating how many pixels are stepped over for the position of the layer; layers also allow for literal layer functionality, allowing you to insert up to four sets of sprite locations, with the first sprite location being applied on top of following locations. You can also edit the offset of sprites, with the default templates providing automatic X axis centering for sprites, offset 16 pixels down on the Y axis.", @@ -315,12 +317,54 @@ "layers": [[29, 1]] } }, + "ring": { + "A1": { + "offset": [64, 112], + "layers": [[30, 0]] + }, + "A2A8": { + "offset": [64, 112], + "layers": [[31, 0]] + }, + "A3A7": { + "offset": [64, 112], + "layers": [[32, 0]] + }, + "A4A6": { + "offset": [64, 112], + "layers": [[33, 0]] + }, + "A5": { + "offset": [64, 112], + "layers": [[34, 0]] + }, + "B1": { + "offset": [64, 112], + "layers": [[30, 1]] + }, + "B2B8": { + "offset": [64, 112], + "layers": [[31, 1]] + }, + "B3B7": { + "offset": [64, 112], + "layers": [[32, 1]] + }, + "B4B6": { + "offset": [64, 112], + "layers": [[33, 1]] + }, + "B5": { + "offset": [64, 112], + "layers": [[34, 1]] + } + }, "graphics": { "icon": { "overwrite_sprite_size": [16, 16], "overwrite_layer_step_size": [1, 1], - "layers": [[3850, 10]] + "layers": [[4490, 10]] } } } diff --git a/Advanced templates/example_symmetricalrotation_7states_animated/sprites.png b/Advanced templates/example_symmetricalrotation_7states_animated/sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..81671dff44b4dc187d15bb3467d30b7a5fbb4d6e Binary files /dev/null and b/Advanced templates/example_symmetricalrotation_7states_animated/sprites.png differ diff --git a/Basic templates/example_symmetricalrotation_2states_animated/properties.txt b/Basic templates/example_symmetricalrotation_2states_animated/properties.txt index 1ac4726f5f562e856bbb570403766a048c4ea013..e54b26ecf5ca661bf70c21a5f0f3951258aae633 100644 --- a/Basic templates/example_symmetricalrotation_2states_animated/properties.txt +++ b/Basic templates/example_symmetricalrotation_2states_animated/properties.txt @@ -44,7 +44,7 @@ "following_animation_speed_explanation": "(in Tics) Number of Tics it takes to cycle through an animated sprite in the following state.", - "follower_states_help": "As you may know, followers have several animations for things that can occur during the race; these are described as \"states\". Followers can have up to 6 states, in order: an idle state, a following state (as in, following the player), a hurt state (when you get hit by an item), a lose state (when you lose the race), a win state (win the race), and a \"hit confirm\" state (occurs when you hit another racer with an item). Of these, the idle state is obligatory in all followers.", + "follower_states_help": "As you may know, followers have several animations for things that can occur during the race; these are described as \"states\". Followers can have up to 7 states, in order: an idle state, a following state (as in, following the player), a hurt state (when you get hit by an item), a lose state (when you lose the race), a win state (win the race), a \"hit confirm\" state (occurs when you hit another racer with an item), and a \"ring\" state (used whenever Auto Ring is enabled as the follower spends your rings). Of these, the idle state is obligatory in all followers.", "template_explanation": "This specific template is designed for followers with an idle and following state, with 2-frame animations for each state and 5 unique degrees of rotation. The second, third, and fourth rotation sprites are duplicated and mirrored, thus this template assumes a follower of symmetrical appearance.", diff --git a/src/main.c b/src/main.c index 77fbb31639743d6579e14489066eb4222fcaaae9..c5c0cca46174bf32daa1e1d0314e54156d7d03da 100644 --- a/src/main.c +++ b/src/main.c @@ -78,10 +78,11 @@ struct followerstructthingwhatever { char loseanimationspeed; char winanimationspeed; char hitconfirmanimationspeed; + char ringanimationspeed; char numstates; uint8_t highestanimframeletter; - uint8_t followerstateanimframestart[6]; + uint8_t followerstateanimframestart[7]; struct state { uint8_t idle:1; uint8_t following:1; @@ -89,7 +90,8 @@ struct followerstructthingwhatever { uint8_t lose:1; uint8_t win:1; uint8_t hitconfirm:1; - uint8_t padding:2; + uint8_t ring:1; + uint8_t padding:1; } states; }; @@ -224,6 +226,7 @@ void SetDefaultFollowerValues(void) kfollower.loseanimationspeed = 35; kfollower.winanimationspeed = 35; kfollower.hitconfirmanimationspeed = 35; + kfollower.ringanimationspeed = 35; kfollower.numstates = 0; kfollower.highestanimframeletter = 0x41; @@ -234,6 +237,7 @@ void SetDefaultFollowerValues(void) kfollower.states.lose = 0; kfollower.states.win = 0; kfollower.states.hitconfirm = 0; + kfollower.states.ring = 0; } // processes sprites on the template, which are separated by regions (usually visualized on the template image as squares) @@ -301,6 +305,8 @@ void processSprites(void) { kfollower.states.win = 1; if (strcmp(item->string, "hitconfirm") == 0) kfollower.states.hitconfirm = 1; + if (strcmp(item->string, "ring") == 0) + kfollower.states.ring = 1; } lastanimframeletterinstate = 0; @@ -367,8 +373,11 @@ void processSprites(void) { lastanimframeletterinstate = nesteditem->string[0]; + // once all alphabet letters are exhausted in the JSON file, the program simply uses whatever's next on the ascii table + // obviously the game won't like that, so those entries get converted to the proper format after the fact nesteditem->string[0] = getFixedAnimationIndex(curanimframeletter); + // for flipped sprites (e.g. A2A8) if (strlen(nesteditem->string) > 3) nesteditem->string[2] = nesteditem->string[0]; } @@ -691,6 +700,7 @@ unsigned char* imageInDoomFormat(struct RGB_Sprite* image, size_t* size) return img; } +// generate SOC void addFollower(struct wadfile* wad) { char buf[1<<16]; @@ -771,6 +781,8 @@ void addFollower(struct wadfile* wad) kfollower.winanimationspeed = cJSON_GetObjectItem(metadata, "win_animation_speed")->valueint; if (cJSON_GetObjectItem(metadata, "hitconfirm_animation_speed")) kfollower.hitconfirmanimationspeed = cJSON_GetObjectItem(metadata, "hitconfirm_animation_speed")->valueint; + if (cJSON_GetObjectItem(metadata, "ring_animation_speed")) + kfollower.winanimationspeed = cJSON_GetObjectItem(metadata, "ring_animation_speed")->valueint; sprintf(prebuf, "FREESLOT\nSPR_%s\nsfx_FH%s\nS_%sIDLE\n", prefix, prefix, prefix); @@ -784,6 +796,8 @@ void addFollower(struct wadfile* wad) sprintf(prebuf, "%sS_%sWIN\n", prebuf, prefix); if (kfollower.states.hitconfirm) sprintf(prebuf, "%sS_%sHITCONFIRM\n", prebuf, prefix); + if (kfollower.states.ring) + sprintf(prebuf, "%sS_%sRING\n", prebuf, prefix); if (kfollower.highestanimframeletter > 0x41 && !(kfollower.numstates > 0)) { @@ -934,18 +948,49 @@ void addFollower(struct wadfile* wad) } if (kfollower.states.hitconfirm) { - if (kfollower.highestanimframeletter > kfollower.followerstateanimframestart[5]) + if (kfollower.numstates > 5) + { + if ((kfollower.followerstateanimframestart[6] - kfollower.followerstateanimframestart[5]) > 1) + { + sprintf(ff_animate, "A|FF_ANIMATE"); + var1 = (kfollower.followerstateanimframestart[6] - kfollower.followerstateanimframestart[5]) - 1; + } + else + { + sprintf(ff_animate, "A"); + var1 = 0; + } + } + else + { + if (kfollower.highestanimframeletter > kfollower.followerstateanimframestart[5]) + { + sprintf(ff_animate, "A|FF_ANIMATE"); + var1 = kfollower.highestanimframeletter - kfollower.followerstateanimframestart[5]; + } + else + { + sprintf(ff_animate, "A"); + var1 = 0; + } + } + ff_animate[0] = getFixedAnimationIndex(kfollower.followerstateanimframestart[5]); + sprintf(prebuf, "%s\nSTATE S_%sHITCONFIRM\nSpriteName = SPR_%s\nSpriteFrame = %s\nDuration = -1\nVar1 = %d #no. of sprites (starts from 0)\nVar2 = %d #animation speed\nNext = S_%sHITCONFIRM\n", prebuf, prefix, prefix, ff_animate, var1, kfollower.hitconfirmanimationspeed, prefix); + } + if (kfollower.states.ring) + { + if (kfollower.highestanimframeletter > kfollower.followerstateanimframestart[6]) { sprintf(ff_animate, "A|FF_ANIMATE"); - var1 = kfollower.highestanimframeletter - kfollower.followerstateanimframestart[5]; + var1 = kfollower.highestanimframeletter - kfollower.followerstateanimframestart[6]; } else { sprintf(ff_animate, "A"); var1 = 0; } - ff_animate[0] = getFixedAnimationIndex(kfollower.followerstateanimframestart[5]); - sprintf(prebuf, "%s\nSTATE S_%sHITCONFIRM\nSpriteName = SPR_%s\nSpriteFrame = %s\nDuration = -1\nVar1 = %d #no. of sprites (starts from 0)\nVar2 = %d #animation speed\nNext = S_%sHITCONFIRM\n", prebuf, prefix, prefix, ff_animate, var1, kfollower.hitconfirmanimationspeed, prefix); + ff_animate[0] = getFixedAnimationIndex(kfollower.followerstateanimframestart[6]); + sprintf(prebuf, "%s\nSTATE S_%sRING\nSpriteName = SPR_%s\nSpriteFrame = %s\nDuration = -1\nVar1 = %d #no. of sprites (starts from 0)\nVar2 = %d #animation speed\nNext = S_%sRING\n", prebuf, prefix, prefix, ff_animate, var1, kfollower.ringanimationspeed, prefix); } } @@ -984,6 +1029,8 @@ void addFollower(struct wadfile* wad) sprintf(prebuf, "%sWinState = S_%sWIN\n", prebuf, prefix); if (kfollower.states.hitconfirm) sprintf(prebuf, "%sHitConfirmState = S_%sHITCONFIRM\n", prebuf, prefix); + if (kfollower.states.ring) + sprintf(prebuf, "%sRingState = S_%sRING\n", prebuf, prefix); } size = sprintf(buf, prebuf);