YesNoOk
avatar

Ginyu's Body Change, or an essay on how to switch character controls in Mugen (Read 74600 times)

Started by XGargoyle, December 30, 2014, 07:13:44 pm
Share this topic:
Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#1  December 30, 2014, 07:13:44 pm
  • ******
  • Hedgehog Whisperer
  • Red Bull addict
    • Spain
    • xgargoyle.mgbr.net
This will be a long read, you've been warned ;)

You’ve seen it in Dragon Ball: Captain Ginyu swaps his body for Goku’s one, but things don’t turn quite as expected, resulting in the characters unable to reach full potential on the new hosts. If you don’t know what I’m talking about, here’s a short clip of the Body Change’s side effects
https://www.youtube.com/watch?feature=player_detailpage&v=MRZziD6WxoE#t=74

How can we replicate this effect in Mugen? The engine doesn’t allow switching control directly, but there are workarounds to achieve a similar effect.

I’m going to propose one of them, which I’d like to discuss with you guys on how to improve it or even find a better method.

The current implementations from other authors either rely on heavy customization of Ginyu and the other enemies (such as including other character’s sprites within the SFF) or requiring lots of code that need to be updated with every character “compatible” with Ginyu. Also, some of the current implementations are really cheap such as characters exchanging life values or similar tricks, unbalancing whatever balance had the original characters.

My goal was to create a code that didn’t have the abusive behavior of the previous implementations, was balanced enough to bring an added value to the fight strategy, and required the least modification for the other characters to make it compatible with Ginyu.

So, this is the result:


This is what you need to make it work:
Ginyu needs to place the enemy on a custom state. In the concept video I’m using a projectile, but it really doesn’t matter if you use a hitdef, a projectile or a helper to place the enemy into a custom state.

The custom state in which the enemy is placed is:
Code:
[Statedef XXXX]
type = U
movetype = I
physics = U
ctrl = 0
velset = 0,0
poweradd = 0
[State Enemy Helper on Player]
type = Helper
trigger1 = !time
helpertype = normal ;player
name = "P2onP1"
ID = 9123
stateno = 9123
pos = 0,0
postype = p2   
facing = 1
keyctrl = 0
ownpal = 0
supermovetime = 0
pausemovetime = 0


[State ChangeState]
type = SelfState
trigger1 =  !time
value = 0
ctrl = 1

This code will make P2 create a Helper that will be placed on top of Ginyu (P1). The State for the Enemy’s Helper needs to be located on the Enemy’s files.
Ginyu also needs the following state, which will be the “P1onP2” Helper:
Code:
;GINYU
[Statedef 9124]
type    = U                      ;State-type: S-stand, C-crouch, A-air, L-liedown
movetype= U                      ;Move-type: A-attack, I-idle, H-gethit
physics = U                      ;Physics: S-stand, C-crouch, A-air
juggle  = 0                      ;Number of air juggle points move takes
;Commonly-used controllers:
velset = 0,0                     ;Set velocity (x,y) (Def: no change)
ctrl = 0                         ;Set ctrl (Def: no change)                     ;Change animation (Def: no change)
sprpriority = 10                 

[State 9124, NotHitBy]
type = NotHitBy
trigger1 = 1
value = SCA,NA,SA,HA,NP,SP,HP,NT,ST,HT
time = 1
ignorehitpause = 1
;persistent =

[State 9124, Pos] ;bind the helper to a fixed position
type = PosSet
trigger1 = 1
x = (enemy, pos x)
y = enemy, pos y
ignorehitpause = 1

[State 9124, turn] ;the helper will face the enemy
type = Turn
trigger1 = pos Y = 0
trigger1 = facing = root,facing
ignorehitpause = 1

[State 9124, 2] ;this will allow the helper to replicate the same animation than the root
type = ChangeAnim
trigger1 = Anim != (enemy, anim)
trigger2 = AnimElemNo(0) > (enemy, AnimElemNo(0))
value = enemy, anim
elem = enemy,AnimElemNo(0)
ignorehitpause = 1

[State 9124, DestroySelf]
type = DestroySelf
trigger1 = root,var(51)=0 ;time = 500
ignorehitpause = 1
;persistent =

This code basically places a Helper on top of the Enemy (P2) and it replicates the same animation number as the Enemy.
Ginyu also needs the following -2 states:

Code:
[State -2, Ginyu Mode ON]
type = VarSet
trigger1 =1
var(51)= IfElse((enemy,Numhelper(9123)=1),1,0)

 [State -2, Ginyu missing animation check]
type = VarSet
triggerall = Teamside = 1
triggerall = NumHelper(9124)=1
trigger1 = (helper(9124),AnimElemNo(0)) = (enemy, AnimElemNo(0))
var(53) = Ifelse((helper(9124),anim)!=(enemy,anim),0,1)

[State -2, Ginyu missing animation check]
type = VarSet
triggerall = Teamside = 2
triggerall = NumHelper(9124)=1
trigger1 = (helper(9124),AnimElemNo(0)) = (enemy, AnimElemNo(0)+1)
var(53) = Ifelse((helper(9124),anim)!=(enemy,anim),0,1)

[State -2, AssertSpecial]
type = AssertSpecial
trigger1 = var(51)=1
flag = invisible
ignorehitpause = 1

[state -3, remove afterimage]
type = afterimagetime
trigger1 = var(51)=1
time = 0

[State -2, Ginyu Helper on Enemy]
type = Helper
trigger1 = !NumHelper(9124)
trigger1 = var(51)=1
helpertype = normal ;player
name = "P1onP2"
ID =  9124
stateno = 9124
pos = 0,0
postype = p2    ;p2,front,back,left,right
facing = 1
keyctrl = 0
ownpal = 0
supermovetime = 0
pausemovetime = 0

var(51) basically tells the game that the Body Swap has been enabled. Var(53) keeps track of the animation of the enemy versus the helper’s animation. Due that P2 code is executed with 1 tick after P1 code, we need to do a workaround to keep that 1 tick delay, otherwise the code will not work when performed by P2.

Now, let’s see the code that needs to be added to the enemy’s files to make it compatible with Ginyu:
Code:
;GINYU’s Helper
[Statedef 9123]
type    = U                      ;State-type: S-stand, C-crouch, A-air, L-liedown
movetype= U                       ;Move-type: A-attack, I-idle, H-gethit
physics = U                      ;Physics: S-stand, C-crouch, A-air
juggle  = 0                      ;Number of air juggle points move takes
;Commonly-used controllers:
velset = 0,0                     ;Set velocity (x,y) (Def: no change)
ctrl = 0                          ;Set ctrl (Def: no change)
sprpriority = 10                 

[State 9123, NotHitBy]
type = NotHitBy
trigger1 = 1
value = SCA,NA,SA,HA,NP,SP,HP,NT,ST,HT

time = 1
ignorehitpause = 1

[State 9123, Pos] ;bind the helper to a fixed position
type = PosSet
trigger1 = 1
x = (enemy, pos x)
y = enemy, pos y
ignorehitpause = 1

[State 9123, turn] ;the helper will face the enemy
type = Turn
trigger1 = pos Y = 0
trigger1 = facing = root,facing
ignorehitpause = 1

[State 9123, 2] ;this will allow the helper to replicate the same animation than the root
type = ChangeAnim
trigger1 = anim != (enemy, anim)
trigger2 = AnimElemNo(0) > (enemy, AnimElemNo(0))
value = enemy, anim
elem = enemy,AnimElemNo(0)
ignorehitpause = 1

[State 0, DestroySelf]
type = DestroySelf
trigger1 = time = 500 ;it’s a good idea to kill the helper after some time, but we can find other ways to kill it. This is up to the author
ignorehitpause = 1

The enemy also needs to include this code in their -2/-3 states:
Code:
[State -3, AssertSpecial]
type = AssertSpecial
triggerall = enemy,Name=" Ginyu "
trigger1 = enemy,var(51)=1 ;Ginyu Mode ON
flag = invisible
ignorehitpause = 1

[state -3, remove afterimage]
type = afterimagetime
triggerall = enemy,Name=" Ginyu "
trigger1 = enemy,var(51)=1 ;Ginyu Mode ON
time = 0
ignorehitpause = 1


[State -3, Prevent playing missing anim] ;HACK
type = ChangeAnim
triggerall = enemy,Name=" Ginyu "
triggerall = enemy,var(51)=1 ;Ginyu Mode ON
trigger1 = stateno = 0
trigger1 = !time
trigger2 = enemy,var(53)=0
value = 0
;elem = 1
ignorehitpause = 1


[State -3, Prevent Wrong State S]
type = ChangeState
triggerall = enemy,Name=" Ginyu "
triggerall = enemy,var(51)=1 ;Ginyu Mode ON
triggerall = statetype=S
trigger1 = enemy,var(53)=0
value = 0
ctrl = 1
ignorehitpause = 1

[State -3, Prevent Wrong State C]
type = ChangeState
triggerall = enemy,Name=" Ginyu "
triggerall = enemy,var(51)=1 ;Ginyu Mode ON
triggerall =  statetype=C
trigger1 = enemy,var(53)=0
value = 11
ctrl = 1
ignorehitpause = 1

[State -3, Prevent Wrong State A]
type = ChangeState
triggerall = enemy,Name="Ginyu"
triggerall = enemy,var(51)=1 ;Ginyu Mode ON
triggerall = statetype=A
trigger1 = enemy,var(53)=0
value = 50
ctrl = 1
ignorehitpause = 1

I’m using a enemy,name trigger to check the identity of Ginyu and then execute the necessary code. Other alternatives to this would be through a helper or anim check, but as this is a concept move, I’ll leave the possible methods of “standardization” open to discussion.

The full code results in a helper on top of an invisible character repeating the same animation as the invisible character. That means if P1 plays animation 200, the Helper will play the same animation 200 using his own sprites (actually the other player’s ones). If the character plays an animation not available or if the animation from the character is longer or shorter than the one from the Helper, then it will revert to an idle state. This replicates the original concept of the move in which the characters weren’t able to reach the full potential of the new bodies. It also brings a new strategy layer as both characters are nerfed down having most of his attacks disabled for the duration of the move.

The full code is also to implement in other characters as it can be placed at the end of statedef -2 or -3, requiring one single copy&paste process to make it compatible. There are no sprites to be added or modified, making it easier the compatibility with other characters Ginyu also doesn’t need to update his code every time a new character is added to the compatibility list.

The code isn’t exempt of problems though. First of all, whenever a character tries to play a missing animation, there will be a debug warning. Also, the characters could get stuck for some ticks while performing some moves. Another known issue is that the actual animation and clsn boxes are the ones from the invisible character. This means that if animation 200 for the character is a high punch, but the enemy’s animation 200 is from a low kick, the hitsparks and the movehit behavior will not reflect on the animation being displayed. Effects such as Explods around a particular animation will still be played on the Helper (which in some cases will not match with what's shown on screen)

Also, in the same fashion, the Helper could turn into a fireball if the animation numbers from one character match the Helper’s ones. A full game implementation using my “helper on top of character” would easily prevent such problems by restricting the attacks that could be performed during the Body Change being enabled -remember Ginyu’s var(51)- as well as limiting the explods that should be displayed during Ginyu’s Body Change mode. Obviously this will require more customization on the enemy’s files, but for full game authors, my method could be a viable option to implement a true Body Change attack.

Let me know your thoughts or comments on this :)
XGargoyle: Battle posing since 1979
http://xgargoyle.mgbr.net
http://www.pandorabots.com/pandora/talk?botid=e71c0d43fe35093a  <-- Please click that link
http://paypal.me/XGargoyle  <-- Donations welcome!
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#2  December 30, 2014, 07:41:57 pm
  • *****
  • Smooches
    • USA
    • Skype - Neocide
oh I love you <3 I've been trying to figure something out with this for my ginyu since I made his sheet.


I'll have to test this out later on.
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#3  December 30, 2014, 07:59:22 pm
  • ***
  • #1 Boss Lady
    • UK
Brilliant. I do wonder if it would ever be possible to switch health values as well, now that'd be interesting.
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#4  December 31, 2014, 09:01:26 am
  • ******
  • Hedgehog Whisperer
  • Red Bull addict
    • Spain
    • xgargoyle.mgbr.net
As long as the enemy is a target or in a custom state, you can modify his life without problems
XGargoyle: Battle posing since 1979
http://xgargoyle.mgbr.net
http://www.pandorabots.com/pandora/talk?botid=e71c0d43fe35093a  <-- Please click that link
http://paypal.me/XGargoyle  <-- Donations welcome!
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#5  January 01, 2015, 01:49:33 am
  • ******
  • Video Game Veteran
  • Can you do it? SUREYOUCAN!
    • USA
    • gcnmario.free.fr
There was a SFA custom Dhalsim, which had a similar effect. But instead of switching bodies, it turned the other character into another Dhalsim.
Also, like how SvC Zero's metool helper transforms an enemy into a Metool upon contact.

It's basically just transformation frames from one end, to another.  But I see what you did there. ;)

"You must defeat my flaming
dragon punch to stand a chance."
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#6  January 01, 2015, 04:40:28 am
  • ***
  • Non est hoc propter hoc sed propter est hoc
    • USA
    • doubletrend-zeta.neocities.org/
Is there really no way to manually force the opponent back to idle without any debug error message?

The way I'm reading the ChangeAnim code, if the animation isn't the enemy's normal one or the animelemno of the specified animation (200 or whatever it may be), is greater than the enemy's normal one, then it uses the animation of the enemy? (wow this is a lot more confusing to try to convey in words than I originally thought it would be, am I right in my understanding of it?)
Spoiler: code snippet for reference (click to see content)

Couldn't you have a separate ChangeAnim sctrl that takes precedence if said animation doesn't exist by forcing specific basic animation numbers to be used? I would think that if we could specify, say, 200-999 as a block of animations to pull from, then we can limit the helper to those animations and then if they try to use any other animation it would force them back to idle, hopefully without any debug error.
However, I don't have the greatest knowledge of the nuances of the CNS, so I'm having trouble figuring out what trigger you would even use for that, or even if it's possible at all, seeing how Enemy, Anim would require them to already be in that animation, and Enemy, AnimExist only takes into account whether the animation exists within the AIR.
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#7  January 02, 2015, 09:32:46 pm
  • ******
  • Hedgehog Whisperer
  • Red Bull addict
    • Spain
    • xgargoyle.mgbr.net
The code snippet you refer basically forces the Helper to have the same animation timings (ticks) than the other player. This prevents the case of a helper having an animation shorter than the enemy. Without that code, the Helper would loop the animation over and over as long as the enemy was still doing his animation.

However, I took notice of your suggestion of checking if the animation actually exists on the enemy and came up with the following code improvement.

On Ginyu's statedef -2/-3, add this:
Quote
[State -2, Ginew AnimExist]
type = VarSet
trigger1 = NumHelper(9124)=1
var(52)= IfElse((enemy,AnimExist(Anim)!=Helper(9124),AnimExist(enemy,Anim)),0,1)

and on the enemy's cns, add these states (in bold the modifications versus the original code:
Quote
[State -3, Prevent Wrong State S]
type = ChangeState
triggerall = enemy,Name="Ginyu"
triggerall = enemy,var(51)=1
triggerall = statetype=S
trigger1 = enemy,var(53)=0
trigger2 = enemy,var(52)=0
value = 0
ctrl = 1
ignorehitpause = 1

[State -3, Prevent Wrong State C]
type = ChangeState
triggerall = enemy,Name="Ginyu"
triggerall = enemy,var(51)=1
triggerall =  statetype=C
trigger1 = enemy,var(53)=0
trigger2 = enemy,var(52)=0
value = 11
ctrl = 1
ignorehitpause = 1

[State -3, Prevent Wrong State A]
type = ChangeState
triggerall = enemy,Name="Ginyu"
triggerall = enemy,var(51)=1
triggerall = statetype=A
trigger1 = enemy,var(53)=0
trigger2 = enemy,var(52)=0
value = 50
ctrl = 1
ignorehitpause = 1

This addition will not prevent the debug flood of a missing animation, but it will prevent the enemy from entering a state in which the helper has no animation to play. This will prevent for example when a 4-button helper is replicating a 6-button character. With the new code, it will disable the buttons for these extra attacks.

This code revision has been a fast addition, so I'll be testing it in the following days. In any case, tanks for the feedback. I really appreciate it ;)
XGargoyle: Battle posing since 1979
http://xgargoyle.mgbr.net
http://www.pandorabots.com/pandora/talk?botid=e71c0d43fe35093a  <-- Please click that link
http://paypal.me/XGargoyle  <-- Donations welcome!
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#8  January 04, 2015, 01:35:27 pm
  • ******
  • Limited time to use Infinite power !
    • France
    • network.mugenguild.com/cybaster/
This is some very interesting piece of code right there. :)
I really like how you kept it simple to maintain while implementing the "not full potential" stuff !

PS : I'll do this readme update today, as I promised long time ago !!! :ninja:
Re: Ginyu's Body Change, or an essay on how to switch character controls in Mugen
#9  September 15, 2019, 03:16:42 am
  • avatar
  • ****
  • Karate ni senshi nashi
    • Brazil
    • www.youtube.com/user/karate867
Sorry for the bump but its possíble use this code to make twoelve’s x-copy?