Jump to content

Ricepigeon's Coding Tutorial & Code Snippet Repository


RicePigeon

Recommended Posts

Workaround for Missing "ParentExist" Trigger

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
no.png
yes.png

 

 

Have you ever had a situation where you needed to create a complex projectile or effect that utilized a single helper creating multiple helpers that were bound to it, only to encounter a glitch where, in the event that the parent helper was destroyed, the child helpers would just sorta stay on screen and Mugen's debug is spitting out complaints that all your child helper's bindtoparent controllers have no parent to bind to. You rush to alter your child helper's Destroyself controller, only to realize that there is no "ParentExist" trigger you can use to force them to destroy themselves. Sounds like all hope is lost right? Fortunately there is a clever workaround using trigger redirection.

 

For each parent helper, you'll need to reserve one variable for a unique ID, while each child helper will require two variables; one to keep track of the parent's unique ID, and one to keep track of the parent helper's playerID, which is created by the engine each time a helper is created and are periodically recycled as helpers are destroyed and new helpers created to take their place.

 

In the first state of parent helper, at the following line of code to the beginning of its state:

[State 20000, Parent ID]
type = varset
trigger1 = time = 0
var(0) = random*1000+random


This will generate a unique, 8-digit ID when the parent helper is created. You only have to set this once each time the helper is created, even if the helper goes through multiple states. Note that there is a very small chance (approx 0.0001%) that the same ID will be created twice, but the chance is so miniscule that this will rarely, if ever, happen. That's all for the parent helper.

 

The child helper is where things start to get a bit complicated, so I'll go through each part here. In the first state the child helper is created in, add the following to the beginning of the state:

[State 20010, Parent Destruct Safeguard]
type = varset
trigger1 = time = 0
var(1) = parent,id ;Set to parent's PlayerID

[State 20010, Parent Destruct Safeguard]
type = varset
trigger1 = time = 0
trigger1 = playeridexist(var(1))
var(0) = playerid(var(1)),var(0)

 

What you are basically doing is copying the values of both the parent helper's playerID (which is set by the engine) and the 8-digit ID that you generated earlier, into the child helper. The reason for copying both of these values into the child helper is to check for two possible scenarios:

 

  • Scenario 1: Parent no longer exists, Helper with Parent's Player ID doesn't exist
  • Scenario 2: Helper with Parent's Player ID exists, but unique ID don't match

 

Scenario 1 is pretty straightforward; if a helper with the parent helper's PlayerID doesn't exist, then that means that the parent helper doesn't exist and you can safely destroy the child helper.

 

Scenario 2 is a bit more complicated to understand, and requires a bit of understanding of how the MUGEN engine assigns PlayerIDs. As stated before, PlayerIDs are recycled by the engine each time a helper is destroyed; MUGEN will typically use the first available free PlayerID in these cases. In other words, you may run into situations where a helper with the parent's PlayerID exists, but the helper with that ID was actually created after the actual parent helper destroyed itself! It is for this reason that we needed that unique 8-digit ID number, as PlayerID alone is not enough to check if the parent helper no longer exists.

 

To satisfy both of these scenarios when checking to see if a child helper's parent exists, your triggers should typically look something like this:

 

trigger1 = !playeridexist(var(1))
trigger2 = playeridexist(var(1))
trigger2 = playerid(var(1)),var(0) != var(0)

 

Trigger1 will satisfy the first scenario; because there is no helper that exists with the parent helper's PlayerID, we can safely say that the parent was destroyed.
Both Trigger2s satisfy the second scenario; a helper exists with the same PlayerID as our child helper's parent, but their unique 8-digit ID number does not match what we initially recorded, indicating that the original parent helper was destroyed.

 

If you want to prevent any unusual debug errors, you should also add the following triggers to all State Controllers utilizing a parent that isn't the root (ie ParentVarSet, BindToParent, etc.):

 

triggerall = playeridexist(var(1))
triggerall = playerid(var(1)),var(0) = var(0)

 

These triggers will check for the same consistency as the ones we defined above. If everything checks out, then the state controllers will execute without any problems. Otherwise, the controllers will not execute and prevent unnecessary debug flood.

Link to comment
Share on other sites

Hitdefs with Multiple Attributes

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
no.png

 

 

Ever wondered if you could assign multiple properties to a hitdef, but didn't want multiple hitdefs conflicting with each other each tick? Thanks to a discovery by a friend of mine, you might not need to:

Quote

DoomBowser: ...
DoomBowser: Why the hell is it possibly to assign multiple attributions to a hitdef?
DoomBowser: Elecbyte wtf
Midori: WAIT WHAT
Jenngra505: Does this mean we can make Command Normals by having it be both a Normal and a Special?
DoomBowser: No
DoomBowser: It means it's both a Normal and a Special
DoomBowser: At the same time
DoomBowser: I need to look further into this
DoomBowser: Set a character's LP to be both NA and NP
DoomBowser: The attack still interacts with counters that only counter melee attacks, but also interacts with things like Yukari's gap, which only absorbs projectiles
DoomBowser: So it's both a melee attack and a projectile at the same time
DoomBowser: But why is this possible, and for what reason?
Midori: so you can type A,SA,SP?
DoomBowser: Yes, and it would probably be both
Midori: oh damn
Midori: Question
Midori: what happens if you put a hitdef that's NA,NT and there was a NotHitBy by NT
DoomBowser: I need to double check that one
DoomBowser: The opponent gets hit
Midori: !!!!
Jenngra505: Can you have an attack that's standing, crouching and aerial all at once?
DoomBowser: By the looks of it, yes
DoomBowser: SCA,NA,NP,NT,SA,SP,ST,HA,HP,HT is completely valid for a hitdef's attr parameter
DoomBowser: What's odd is that this is an extension of what I found with that crappy Futo the other day where there's AA, AP and AT functions
DoomBowser: S,AA = Standing Normal/Special/Hyper Attack
DoomBowser: Then I just happen do find the multi-function parameter in Aperson's Robotnik
DoomBowser: Robotnik's Pissed off slap worked both against Youmu's melee counter and Yukari's gap, because the hitdef attribution is S, SA,AP
RicePigeon: can we get a gif of him using it on the gap just for lulz?
DoomBowser: Sure
DoomBowser: .eJwNzNERgyAMANBdGIBgxFx0jG5AEZGrEg_iV6-

DoomBowser: It's a bit of a cheaply made gif, but you get the point
RicePigeon: oh man thats fresh
RicePigeon: lol
RicePigeon: and its not even Yukari's fault this time :p
RicePigeon: assuming Doom hasnt done it already, Im gonna go post that finding about the hitdef's attr parameter on guild
RicePigeon: out of curiousity if you have an attack classed as S,SA,SP and the opponent has two different hitoverrides active, one for melee and the other for projectiles, which one takes precedence?
DoomBowser: I haven't checked that one
DoomBowser: Is there a character I can test this on? I'm not sure if I have a character like that on me
RicePigeon: none that I know of, but I can probably whip up something real quick for testing purposes
DoomBowser: Alrighty then
RicePigeon: Okay so just tested it
RicePigeon: it seems like the hitoverride with the lower slot number takes precedence
RicePigeon: tested it first with the melee override in slot 0 and projectile in slot 1, the melee override took effect
RicePigeon: reversed the slots and the projectile override occurred
RicePigeon: Imagine what kind of broken shit you can do with this discovery

 

The tested code in question:

 

[Statedef -2]
[State 0, HitOverride]
type = HitOverride
trigger1 = 1
attr = SCA,AA
slot = 1;0
stateno = 1200 ;Melee counter state
time = 1
forceair = 0
ignorehitpause = 1
[State 0, HitOverride]
type = HitOverride
trigger1 = 1
attr = SCA,AP
slot = 0;1
stateno = 1300 ;Projectile counter state
time = 1
forceair = 0
ignorehitpause = 1

 

Other stuff to make note of due to this bug:

 

  • If an attack is attributed as multiple levels and KOs the opponent, MUGEN will flag the victory condition in the order of Special, Hyper, and Normal (ie: S,NA,SA,HA will be attributed as a Special).
  • Similarly, MUGEN will prioritize the Throw win flag if the attack is attributed as a combination of Throw and either Attack or Projectile.

 

Both of these conditions will ignore the ordering of multiple hitoverrides.

 

Some practical uses of this bug include the ability to code melee attacks that can destroy helper-based projectiles without needing to code in an additional Helper to handle projectile collisions:

.eJwFwdsNgCAMAMBdGIACbcC6DUGCJvII1C_j7t6

 

Link to comment
Share on other sites

Asymmetrical Palette Display using ReMapPal (aka The Gill Effect)

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
no.png
yes.png
yes.png
yes.png

 

 

You've probably heard of the term "Gill Effect" or something similar before if you've ever played any of the Street Fighter III games; a character with an asymmetrical color scheme on their body. In Mugen, attempting to replicate this would require a duplicate of every sprite with the reversed palette, but even then custom states cause a problem. With the ReMapPal controller introduced in Mugen 1.0, this can now be done easily with a simple controller:

 

 

The idea is simple; using the new SFFv2 format, instead of duplicating each sprite, duplicate each palette and then remap the palettes with the new controller depending on the direction the character is facing. However, keep in mind that executing a ReMapPal controller conflicts with an earlier state controller; PalFx.

To get an idea of what I'm talking about, here we have the same attack which puts p2 into a custom state that applies a PalFX controller to simulate being frozen. One default Kung Fu Man, while the other is a character that continuously applies a ReMapPal controller in her -2 state in order to simulate the Gill effect;

 

0JcsXpn.png

 

Purely discovered by accident, I was easily able to find a workaround to this by, rather than continuously applying ReMapPal, instead only apply the controller when it needs to be. This can be done with a variable to act as a flag and compare it to the current direction the character is facing. If they do not match, only then should ReMapPal be applied, with the flag set to that facing value to avoid the controller being executed again until needed. This isn't a perfect workaround, however, as custom states that both apply a PalFX controller and repeatedly changing the opponent's facing direction may still cause the PalFX to end prematurely.

 

In the end, to address the issue outlined above, your code should look something like this:

 

Quote

 

[State -2, GILL SYNDROME]

type = RemapPal

trigger1 = facing = 1

trigger1 = var(26)!=1

source = 1,1

dest = 1+0*(var(26):=1),palno

 

[State -2, GILL SYNDROME]

type = RemapPal

trigger1 = facing != 1

trigger1 = var(26)!=2

source = 1,1

dest = 2+0*(var(26):=2),palno

 

 

The way it works is simple: You'll need a variable to keep track of which direction your character is facing: 1 for facing right, 2 for facing left. Additionally, you'll need to have two palette groups for each direction as well. The above code will check which direction you're currently facing and if you were previously facing the opposite direction; if you were, change the palette to the opposite palette group, otherwise don't change anything. This will also simultaneously set the value of the palette group to use.

Link to comment
Share on other sites

More Efficient Random Number Spreads

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

Many times I've seen authors use modulo division to generate a spread of random numbers for whatever purpose (be it for AI triggers, random animations, or whatever). For those who don't know what I'm talking about, I'm referring to pieces of code like this:

 

Quote

trigger1 = random%2 = 0

 

To understand why the above code is inefficient, we have to take a deeper look into the results its producing.

For those who don't already know, in MUGEN, random generates a random integer from 0 to 999 (effectively producing 1 of 1000 different values each time random is called), each with an equal probability of being returned by random. With the % operator, such as in expression X%Y, we are dividing value X by value Y and returning only the remainder as a result. 17%10, for example, would return 7 as its value (17/10 = 1 R7). If we were to apply modulo division to random, for instance, we would be generating a random value from 0 to 999 and then dividing this number by an arbitrary value and returning its remainder as a result. By applying modulo division to random, we expect to see results that have an equal probability of being generated, as follows:

 

Random%10 generates:

Quote

0 (10% probability)
1 (10% probability)
2 (10% probability)
3 (10% probability)
4 (10% probability)
5 (10% probability)
6 (10% probability)
7 (10% probability)
8 (10% probability)
9 (10% probability)

 

As we can see above, we have a nice even spread of the 10 possible values that random%10 can generate. While this works great for numbers that are factors of 1000 (such as 2, 4, 5, 10, 20, 50, 100, etc), more arbitrary values won't be as neat and organized.

 

As an arbitrary example, instead of 10 values, we try for 666 (which should return an even spread of numbers ranging from 0 to 665). The results are listed below:

Random%666 generates:

Spoiler

Number    Probability
0    0.20%
1    0.20%
2    0.20%
3    0.20%
4    0.20%
5    0.20%
6    0.20%
7    0.20%
8    0.20%
9    0.20%
10    0.20%
11    0.20%
12    0.20%
13    0.20%
14    0.20%
15    0.20%
16    0.20%
17    0.20%
18    0.20%
19    0.20%
20    0.20%
21    0.20%
22    0.20%
23    0.20%
24    0.20%
25    0.20%
26    0.20%
27    0.20%
28    0.20%
29    0.20%
30    0.20%
31    0.20%
32    0.20%
33    0.20%
34    0.20%
35    0.20%
36    0.20%
37    0.20%
38    0.20%
39    0.20%
40    0.20%
41    0.20%
42    0.20%
43    0.20%
44    0.20%
45    0.20%
46    0.20%
47    0.20%
48    0.20%
49    0.20%
50    0.20%
51    0.20%
52    0.20%
53    0.20%
54    0.20%
55    0.20%
56    0.20%
57    0.20%
58    0.20%
59    0.20%
60    0.20%
61    0.20%
62    0.20%
63    0.20%
64    0.20%
65    0.20%
66    0.20%
67    0.20%
68    0.20%
69    0.20%
70    0.20%
71    0.20%
72    0.20%
73    0.20%
74    0.20%
75    0.20%
76    0.20%
77    0.20%
78    0.20%
79    0.20%
80    0.20%
81    0.20%
82    0.20%
83    0.20%
84    0.20%
85    0.20%
86    0.20%
87    0.20%
88    0.20%
89    0.20%
90    0.20%
91    0.20%
92    0.20%
93    0.20%
94    0.20%
95    0.20%
96    0.20%
97    0.20%
98    0.20%
99    0.20%
100    0.20%
101    0.20%
102    0.20%
103    0.20%
104    0.20%
105    0.20%
106    0.20%
107    0.20%
108    0.20%
109    0.20%
110    0.20%
111    0.20%
112    0.20%
113    0.20%
114    0.20%
115    0.20%
116    0.20%
117    0.20%
118    0.20%
119    0.20%
120    0.20%
121    0.20%
122    0.20%
123    0.20%
124    0.20%
125    0.20%
126    0.20%
127    0.20%
128    0.20%
129    0.20%
130    0.20%
131    0.20%
132    0.20%
133    0.20%
134    0.20%
135    0.20%
136    0.20%
137    0.20%
138    0.20%
139    0.20%
140    0.20%
141    0.20%
142    0.20%
143    0.20%
144    0.20%
145    0.20%
146    0.20%
147    0.20%
148    0.20%
149    0.20%
150    0.20%
151    0.20%
152    0.20%
153    0.20%
154    0.20%
155    0.20%
156    0.20%
157    0.20%
158    0.20%
159    0.20%
160    0.20%
161    0.20%
162    0.20%
163    0.20%
164    0.20%
165    0.20%
166    0.20%
167    0.20%
168    0.20%
169    0.20%
170    0.20%
171    0.20%
172    0.20%
173    0.20%
174    0.20%
175    0.20%
176    0.20%
177    0.20%
178    0.20%
179    0.20%
180    0.20%
181    0.20%
182    0.20%
183    0.20%
184    0.20%
185    0.20%
186    0.20%
187    0.20%
188    0.20%
189    0.20%
190    0.20%
191    0.20%
192    0.20%
193    0.20%
194    0.20%
195    0.20%
196    0.20%
197    0.20%
198    0.20%
199    0.20%
200    0.20%
201    0.20%
202    0.20%
203    0.20%
204    0.20%
205    0.20%
206    0.20%
207    0.20%
208    0.20%
209    0.20%
210    0.20%
211    0.20%
212    0.20%
213    0.20%
214    0.20%
215    0.20%
216    0.20%
217    0.20%
218    0.20%
219    0.20%
220    0.20%
221    0.20%
222    0.20%
223    0.20%
224    0.20%
225    0.20%
226    0.20%
227    0.20%
228    0.20%
229    0.20%
230    0.20%
231    0.20%
232    0.20%
233    0.20%
234    0.20%
235    0.20%
236    0.20%
237    0.20%
238    0.20%
239    0.20%
240    0.20%
241    0.20%
242    0.20%
243    0.20%
244    0.20%
245    0.20%
246    0.20%
247    0.20%
248    0.20%
249    0.20%
250    0.20%
251    0.20%
252    0.20%
253    0.20%
254    0.20%
255    0.20%
256    0.20%
257    0.20%
258    0.20%
259    0.20%
260    0.20%
261    0.20%
262    0.20%
263    0.20%
264    0.20%
265    0.20%
266    0.20%
267    0.20%
268    0.20%
269    0.20%
270    0.20%
271    0.20%
272    0.20%
273    0.20%
274    0.20%
275    0.20%
276    0.20%
277    0.20%
278    0.20%
279    0.20%
280    0.20%
281    0.20%
282    0.20%
283    0.20%
284    0.20%
285    0.20%
286    0.20%
287    0.20%
288    0.20%
289    0.20%
290    0.20%
291    0.20%
292    0.20%
293    0.20%
294    0.20%
295    0.20%
296    0.20%
297    0.20%
298    0.20%
299    0.20%
300    0.20%
301    0.20%
302    0.20%
303    0.20%
304    0.20%
305    0.20%
306    0.20%
307    0.20%
308    0.20%
309    0.20%
310    0.20%
311    0.20%
312    0.20%
313    0.20%
314    0.20%
315    0.20%
316    0.20%
317    0.20%
318    0.20%
319    0.20%
320    0.20%
321    0.20%
322    0.20%
323    0.20%
324    0.20%
325    0.20%
326    0.20%
327    0.20%
328    0.20%
329    0.20%
330    0.20%
331    0.20%
332    0.20%
333    0.20%
334    0.10%
335    0.10%
336    0.10%
337    0.10%
338    0.10%
339    0.10%
340    0.10%
341    0.10%
342    0.10%
343    0.10%
344    0.10%
345    0.10%
346    0.10%
347    0.10%
348    0.10%
349    0.10%
350    0.10%
351    0.10%
352    0.10%
353    0.10%
354    0.10%
355    0.10%
356    0.10%
357    0.10%
358    0.10%
359    0.10%
360    0.10%
361    0.10%
362    0.10%
363    0.10%
364    0.10%
365    0.10%
366    0.10%
367    0.10%
368    0.10%
369    0.10%
370    0.10%
371    0.10%
372    0.10%
373    0.10%
374    0.10%
375    0.10%
376    0.10%
377    0.10%
378    0.10%
379    0.10%
380    0.10%
381    0.10%
382    0.10%
383    0.10%
384    0.10%
385    0.10%
386    0.10%
387    0.10%
388    0.10%
389    0.10%
390    0.10%
391    0.10%
392    0.10%
393    0.10%
394    0.10%
395    0.10%
396    0.10%
397    0.10%
398    0.10%
399    0.10%
400    0.10%
401    0.10%
402    0.10%
403    0.10%
404    0.10%
405    0.10%
406    0.10%
407    0.10%
408    0.10%
409    0.10%
410    0.10%
411    0.10%
412    0.10%
413    0.10%
414    0.10%
415    0.10%
416    0.10%
417    0.10%
418    0.10%
419    0.10%
420    0.10%
421    0.10%
422    0.10%
423    0.10%
424    0.10%
425    0.10%
426    0.10%
427    0.10%
428    0.10%
429    0.10%
430    0.10%
431    0.10%
432    0.10%
433    0.10%
434    0.10%
435    0.10%
436    0.10%
437    0.10%
438    0.10%
439    0.10%
440    0.10%
441    0.10%
442    0.10%
443    0.10%
444    0.10%
445    0.10%
446    0.10%
447    0.10%
448    0.10%
449    0.10%
450    0.10%
451    0.10%
452    0.10%
453    0.10%
454    0.10%
455    0.10%
456    0.10%
457    0.10%
458    0.10%
459    0.10%
460    0.10%
461    0.10%
462    0.10%
463    0.10%
464    0.10%
465    0.10%
466    0.10%
467    0.10%
468    0.10%
469    0.10%
470    0.10%
471    0.10%
472    0.10%
473    0.10%
474    0.10%
475    0.10%
476    0.10%
477    0.10%
478    0.10%
479    0.10%
480    0.10%
481    0.10%
482    0.10%
483    0.10%
484    0.10%
485    0.10%
486    0.10%
487    0.10%
488    0.10%
489    0.10%
490    0.10%
491    0.10%
492    0.10%
493    0.10%
494    0.10%
495    0.10%
496    0.10%
497    0.10%
498    0.10%
499    0.10%
500    0.10%
501    0.10%
502    0.10%
503    0.10%
504    0.10%
505    0.10%
506    0.10%
507    0.10%
508    0.10%
509    0.10%
510    0.10%
511    0.10%
512    0.10%
513    0.10%
514    0.10%
515    0.10%
516    0.10%
517    0.10%
518    0.10%
519    0.10%
520    0.10%
521    0.10%
522    0.10%
523    0.10%
524    0.10%
525    0.10%
526    0.10%
527    0.10%
528    0.10%
529    0.10%
530    0.10%
531    0.10%
532    0.10%
533    0.10%
534    0.10%
535    0.10%
536    0.10%
537    0.10%
538    0.10%
539    0.10%
540    0.10%
541    0.10%
542    0.10%
543    0.10%
544    0.10%
545    0.10%
546    0.10%
547    0.10%
548    0.10%
549    0.10%
550    0.10%
551    0.10%
552    0.10%
553    0.10%
554    0.10%
555    0.10%
556    0.10%
557    0.10%
558    0.10%
559    0.10%
560    0.10%
561    0.10%
562    0.10%
563    0.10%
564    0.10%
565    0.10%
566    0.10%
567    0.10%
568    0.10%
569    0.10%
570    0.10%
571    0.10%
572    0.10%
573    0.10%
574    0.10%
575    0.10%
576    0.10%
577    0.10%
578    0.10%
579    0.10%
580    0.10%
581    0.10%
582    0.10%
583    0.10%
584    0.10%
585    0.10%
586    0.10%
587    0.10%
588    0.10%
589    0.10%
590    0.10%
591    0.10%
592    0.10%
593    0.10%
594    0.10%
595    0.10%
596    0.10%
597    0.10%
598    0.10%
599    0.10%
600    0.10%
601    0.10%
602    0.10%
603    0.10%
604    0.10%
605    0.10%
606    0.10%
607    0.10%
608    0.10%
609    0.10%
610    0.10%
611    0.10%
612    0.10%
613    0.10%
614    0.10%
615    0.10%
616    0.10%
617    0.10%
618    0.10%
619    0.10%
620    0.10%
621    0.10%
622    0.10%
623    0.10%
624    0.10%
625    0.10%
626    0.10%
627    0.10%
628    0.10%
629    0.10%
630    0.10%
631    0.10%
632    0.10%
633    0.10%
634    0.10%
635    0.10%
636    0.10%
637    0.10%
638    0.10%
639    0.10%
640    0.10%
641    0.10%
642    0.10%
643    0.10%
644    0.10%
645    0.10%
646    0.10%
647    0.10%
648    0.10%
649    0.10%
650    0.10%
651    0.10%
652    0.10%
653    0.10%
654    0.10%
655    0.10%
656    0.10%
657    0.10%
658    0.10%
659    0.10%
660    0.10%
661    0.10%
662    0.10%
663    0.10%
664    0.10%
665    0.10%

 

The problem should be immediately clear: the values ranging from 0-333 each have a 0.20% probability, while values ranging from 334-665 each have a 0.10% probability. If this were an even spread, we should expect any value from 0-332 to be generated only 50% of the time, but instead we see that numbers in this range are generated 66.6% of the time! If we were relying on a number being generated from this range, random%666 would be returning these values TWICE as often as the rest of the spread.

 

The solution? Rather than using modulo division, we use the following:

Quote

floor(X*random/1000.0)

What this does is multiply arbitrary value X by random, then divide by 1000, then floor the result to produce an integer value. This results in a spread ranging from 0-X, but in a much more even fashion. Say we revise our above code to the following:

 

floor(666*random/1000.0) generates:
 

Spoiler

Number    Probability
0    0.20%
1    0.20%
2    0.10%
3    0.20%
4    0.10%
5    0.20%
6    0.10%
7    0.20%
8    0.10%
9    0.20%
10    0.10%
11    0.20%
12    0.10%
13    0.20%
14    0.10%
15    0.20%
16    0.10%
17    0.20%
18    0.10%
19    0.20%
20    0.10%
21    0.20%
22    0.10%
23    0.20%
24    0.10%
25    0.20%
26    0.10%
27    0.20%
28    0.10%
29    0.20%
30    0.10%
31    0.20%
32    0.10%
33    0.20%
34    0.10%
35    0.20%
36    0.10%
37    0.20%
38    0.10%
39    0.20%
40    0.10%
41    0.20%
42    0.10%
43    0.20%
44    0.10%
45    0.20%
46    0.10%
47    0.20%
48    0.10%
49    0.20%
50    0.10%
51    0.20%
52    0.10%
53    0.20%
54    0.10%
55    0.20%
56    0.10%
57    0.20%
58    0.10%
59    0.20%
60    0.10%
61    0.20%
62    0.10%
63    0.20%
64    0.10%
65    0.20%
66    0.10%
67    0.20%
68    0.10%
69    0.20%
70    0.10%
71    0.20%
72    0.10%
73    0.20%
74    0.10%
75    0.20%
76    0.10%
77    0.20%
78    0.10%
79    0.20%
80    0.10%
81    0.20%
82    0.10%
83    0.20%
84    0.10%
85    0.20%
86    0.10%
87    0.20%
88    0.10%
89    0.20%
90    0.10%
91    0.20%
92    0.10%
93    0.20%
94    0.10%
95    0.20%
96    0.10%
97    0.20%
98    0.10%
99    0.20%
100    0.10%
101    0.20%
102    0.10%
103    0.20%
104    0.10%
105    0.20%
106    0.10%
107    0.20%
108    0.10%
109    0.20%
110    0.10%
111    0.20%
112    0.10%
113    0.20%
114    0.10%
115    0.20%
116    0.10%
117    0.20%
118    0.10%
119    0.20%
120    0.10%
121    0.20%
122    0.10%
123    0.20%
124    0.10%
125    0.20%
126    0.10%
127    0.20%
128    0.10%
129    0.20%
130    0.10%
131    0.20%
132    0.10%
133    0.20%
134    0.10%
135    0.20%
136    0.10%
137    0.20%
138    0.10%
139    0.20%
140    0.10%
141    0.20%
142    0.10%
143    0.20%
144    0.10%
145    0.20%
146    0.10%
147    0.20%
148    0.10%
149    0.20%
150    0.10%
151    0.20%
152    0.10%
153    0.20%
154    0.10%
155    0.20%
156    0.10%
157    0.20%
158    0.10%
159    0.20%
160    0.10%
161    0.20%
162    0.10%
163    0.20%
164    0.10%
165    0.20%
166    0.10%
167    0.20%
168    0.10%
169    0.20%
170    0.10%
171    0.20%
172    0.10%
173    0.20%
174    0.10%
175    0.20%
176    0.10%
177    0.20%
178    0.10%
179    0.20%
180    0.10%
181    0.20%
182    0.10%
183    0.20%
184    0.10%
185    0.20%
186    0.10%
187    0.20%
188    0.10%
189    0.20%
190    0.10%
191    0.20%
192    0.10%
193    0.20%
194    0.10%
195    0.20%
196    0.10%
197    0.20%
198    0.10%
199    0.20%
200    0.10%
201    0.20%
202    0.10%
203    0.20%
204    0.10%
205    0.20%
206    0.10%
207    0.20%
208    0.10%
209    0.20%
210    0.10%
211    0.20%
212    0.10%
213    0.20%
214    0.10%
215    0.20%
216    0.10%
217    0.20%
218    0.10%
219    0.20%
220    0.10%
221    0.20%
222    0.10%
223    0.20%
224    0.10%
225    0.20%
226    0.10%
227    0.20%
228    0.10%
229    0.20%
230    0.10%
231    0.20%
232    0.10%
233    0.20%
234    0.10%
235    0.20%
236    0.10%
237    0.20%
238    0.10%
239    0.20%
240    0.10%
241    0.20%
242    0.10%
243    0.20%
244    0.10%
245    0.20%
246    0.10%
247    0.20%
248    0.10%
249    0.20%
250    0.10%
251    0.20%
252    0.10%
253    0.20%
254    0.10%
255    0.20%
256    0.10%
257    0.20%
258    0.10%
259    0.20%
260    0.10%
261    0.20%
262    0.10%
263    0.20%
264    0.10%
265    0.20%
266    0.10%
267    0.20%
268    0.10%
269    0.20%
270    0.10%
271    0.20%
272    0.10%
273    0.20%
274    0.10%
275    0.20%
276    0.10%
277    0.20%
278    0.10%
279    0.20%
280    0.10%
281    0.20%
282    0.10%
283    0.20%
284    0.10%
285    0.20%
286    0.10%
287    0.20%
288    0.10%
289    0.20%
290    0.10%
291    0.20%
292    0.10%
293    0.20%
294    0.10%
295    0.20%
296    0.10%
297    0.20%
298    0.10%
299    0.20%
300    0.10%
301    0.20%
302    0.10%
303    0.20%
304    0.10%
305    0.20%
306    0.10%
307    0.20%
308    0.10%
309    0.20%
310    0.10%
311    0.20%
312    0.10%
313    0.20%
314    0.10%
315    0.20%
316    0.10%
317    0.20%
318    0.10%
319    0.20%
320    0.10%
321    0.20%
322    0.10%
323    0.20%
324    0.10%
325    0.20%
326    0.10%
327    0.20%
328    0.10%
329    0.20%
330    0.10%
331    0.20%
332    0.10%
333    0.20%
334    0.20%
335    0.10%
336    0.20%
337    0.10%
338    0.20%
339    0.10%
340    0.20%
341    0.10%
342    0.20%
343    0.10%
344    0.20%
345    0.10%
346    0.20%
347    0.10%
348    0.20%
349    0.10%
350    0.20%
351    0.10%
352    0.20%
353    0.10%
354    0.20%
355    0.10%
356    0.20%
357    0.10%
358    0.20%
359    0.10%
360    0.20%
361    0.10%
362    0.20%
363    0.10%
364    0.20%
365    0.10%
366    0.20%
367    0.10%
368    0.20%
369    0.10%
370    0.20%
371    0.10%
372    0.20%
373    0.10%
374    0.20%
375    0.10%
376    0.20%
377    0.10%
378    0.20%
379    0.10%
380    0.20%
381    0.10%
382    0.20%
383    0.10%
384    0.20%
385    0.10%
386    0.20%
387    0.10%
388    0.20%
389    0.10%
390    0.20%
391    0.10%
392    0.20%
393    0.10%
394    0.20%
395    0.10%
396    0.20%
397    0.10%
398    0.20%
399    0.10%
400    0.20%
401    0.10%
402    0.20%
403    0.10%
404    0.20%
405    0.10%
406    0.20%
407    0.10%
408    0.20%
409    0.10%
410    0.20%
411    0.10%
412    0.20%
413    0.10%
414    0.20%
415    0.10%
416    0.20%
417    0.10%
418    0.20%
419    0.10%
420    0.20%
421    0.10%
422    0.20%
423    0.10%
424    0.20%
425    0.10%
426    0.20%
427    0.10%
428    0.20%
429    0.10%
430    0.20%
431    0.10%
432    0.20%
433    0.10%
434    0.20%
435    0.10%
436    0.20%
437    0.10%
438    0.20%
439    0.10%
440    0.20%
441    0.10%
442    0.20%
443    0.10%
444    0.20%
445    0.10%
446    0.20%
447    0.10%
448    0.20%
449    0.10%
450    0.20%
451    0.10%
452    0.20%
453    0.10%
454    0.20%
455    0.10%
456    0.20%
457    0.10%
458    0.20%
459    0.10%
460    0.20%
461    0.10%
462    0.20%
463    0.10%
464    0.20%
465    0.10%
466    0.20%
467    0.10%
468    0.20%
469    0.10%
470    0.20%
471    0.10%
472    0.20%
473    0.10%
474    0.20%
475    0.10%
476    0.20%
477    0.10%
478    0.20%
479    0.10%
480    0.20%
481    0.10%
482    0.20%
483    0.10%
484    0.20%
485    0.10%
486    0.20%
487    0.10%
488    0.20%
489    0.10%
490    0.20%
491    0.10%
492    0.20%
493    0.10%
494    0.20%
495    0.10%
496    0.20%
497    0.10%
498    0.20%
499    0.10%
500    0.20%
501    0.10%
502    0.20%
503    0.10%
504    0.20%
505    0.10%
506    0.20%
507    0.10%
508    0.20%
509    0.10%
510    0.20%
511    0.10%
512    0.20%
513    0.10%
514    0.20%
515    0.10%
516    0.20%
517    0.10%
518    0.20%
519    0.10%
520    0.20%
521    0.10%
522    0.20%
523    0.10%
524    0.20%
525    0.10%
526    0.20%
527    0.10%
528    0.20%
529    0.10%
530    0.20%
531    0.10%
532    0.20%
533    0.10%
534    0.20%
535    0.10%
536    0.20%
537    0.10%
538    0.20%
539    0.10%
540    0.20%
541    0.10%
542    0.20%
543    0.10%
544    0.20%
545    0.10%
546    0.20%
547    0.10%
548    0.20%
549    0.10%
550    0.20%
551    0.10%
552    0.20%
553    0.10%
554    0.20%
555    0.10%
556    0.20%
557    0.10%
558    0.20%
559    0.10%
560    0.20%
561    0.10%
562    0.20%
563    0.10%
564    0.20%
565    0.10%
566    0.20%
567    0.10%
568    0.20%
569    0.10%
570    0.20%
571    0.10%
572    0.20%
573    0.10%
574    0.20%
575    0.10%
576    0.20%
577    0.10%
578    0.20%
579    0.10%
580    0.20%
581    0.10%
582    0.20%
583    0.10%
584    0.20%
585    0.10%
586    0.20%
587    0.10%
588    0.20%
589    0.10%
590    0.20%
591    0.10%
592    0.20%
593    0.10%
594    0.20%
595    0.10%
596    0.20%
597    0.10%
598    0.20%
599    0.10%
600    0.20%
601    0.10%
602    0.20%
603    0.10%
604    0.20%
605    0.10%
606    0.20%
607    0.10%
608    0.20%
609    0.10%
610    0.20%
611    0.10%
612    0.20%
613    0.10%
614    0.20%
615    0.10%
616    0.20%
617    0.10%
618    0.20%
619    0.10%
620    0.20%
621    0.10%
622    0.20%
623    0.10%
624    0.20%
625    0.10%
626    0.20%
627    0.10%
628    0.20%
629    0.10%
630    0.20%
631    0.10%
632    0.20%
633    0.10%
634    0.20%
635    0.10%
636    0.20%
637    0.10%
638    0.20%
639    0.10%
640    0.20%
641    0.10%
642    0.20%
643    0.10%
644    0.20%
645    0.10%
646    0.20%
647    0.10%
648    0.20%
649    0.10%
650    0.20%
651    0.10%
652    0.20%
653    0.10%
654    0.20%
655    0.10%
656    0.20%
657    0.10%
658    0.20%
659    0.10%
660    0.20%
661    0.10%
662    0.20%
663    0.10%
664    0.20%
665    0.10%

 

While this isnt foolproof, as we still have values that are twice as likely to be returned than the other values, you'll notice that the spread is much more even. Values ranging from 0-332 now have a 50% chance of being generated, as we would expect them to be, as opposed to the 66.6% probability we were generating before.

Link to comment
Share on other sites

Multiple Attack Variants Using a Single State

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

This one is fairly simple, and makes use the assignment operator. Instead of defining Light, Medium, and Heavy variants of a special move in separate states, have the changestate in -1 as follows:

 

[State -1, Shoryuken]

type = ChangeState
value = 1100
triggerall = ctrl
trigger1 = command="DP_X"
trigger1 = (var(10) := 1)||1
trigger2 = command="DP_Y"
trigger2 = (var(10) := 2)||1
trigger3 = command="DP_Z"
trigger3 = (var(10) := 3)||1

 

As we can see here, each command will still bring us to our main Shoryuken state, but will keep track of which version we are using. If you have multiple different animations or anything else that varies depending on the version of the move used, you can simply refer to the variable as follows:

 

[Statedef 1100]
type = S
movetype = A
physics = S
ctrl = 0
poweradd = 20
anim = 1100+1*(var(10)=2)+2*(var(10)>=3)
sprpriority = 1
facep2 = 1

[State 1100, Hitdef]
type = HitDef
trigger1 = movecontact = 0 && movereversed = 0
trigger1 = animelemtime(1)>=0
attr = S, SA
damage = 60+10*(var(10)=2)+20*(var(10)=3),11+2*(var(10)=2)+4*(var(10)=3)

As we can see here, animations 1100, 1101, and 1102 each correspond to the Light, Medium, and Heavy versions of our DP command, and the damage values can be modified with this as well.

Link to comment
Share on other sites

Hitdef Animtype Quirk

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

This is more of a bug in Mugen/IKEMEN's parser. Basically, Mugen's parser only checks the first letter of the Animtype parameter (L for Light, M for Medium, H for Heavy, B for Back, U for Up, and D for Diagup), while ignoring the rest of the parameter's value. This means that Animtype = Hard and Animtype = Heavy are functionally equivalent. This also means that some silly parameter values that normally shouldn't be accepted by the engine are valid values:

 

Animtype = Hamburger

resolves to Animtype = H (Heavy)
 

Animtype = Methamphetamine

resolves to Animtime = M (Medium)

 

Animtype = Boobies

resolves to Animtype = B (Back)

 

Basically, as long as the parameter's argument begins with an L, M, H, B, U, or D, the engine will accept it as a valid parameter.

Link to comment
Share on other sites

Improved Guarding States for Crossup Detection

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

In most fighting games, you need to hold the joystick in the direction away from the opponent in order to guard, which is straightforward when the opponent is directly in front of you. But when the opponent is behind you, such as during an attack that crossed up the opponent, holding back should not allow you to guard an opponent's attack, only when holding forward. By default, Mugen's default guarding states cannot take into account these cross-up attacks. The following modifications to Mugen's guard states will replicate this proper behavior.

 

First, go through States 120, 130, 131, and 132 and search for the following block of code:

 

[State 132, Stop Guarding]
type = ChangeState
trigger1 = command!="holdback"
trigger2 = !inguarddist
value = 140

Replace this with the following:

[State 132, Stop Guarding]
type = ChangeState
trigger1 = cond(p2dist x<0,(command!="holdfwd"),(command!="holdback"))
trigger2 = !inguarddist
value = 140

What this does is tells the engine to stop guarding if we are holding back and player 2 is behind us.

 

In a similar fashion, check states 132 and 155 for the following:

[State 132, 6]
type = ChangeState
trigger1 = sysvar(0)
trigger1 = command="holdback"
trigger1 = inguarddist
value = 130

Replace with this block of code:

[State 132, 6]
type = ChangeState
trigger1 = sysvar(0)
trigger1 = cond(p2dist x<0,(command="holdfwd"),(command="holdback"))
trigger1 = inguarddist
value = 130

This will do the opposite: Tell the engine to start guarding if we are holding back ONLY when the opponent is in front of us, and when holding forward ONLY when the opponent is behind us.

Link to comment
Share on other sites

Homing/Tracking/Aimed Projectiles

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

Another relatively simple concept; you have a projectile spawned at a stationary location and want it to either aim in the opponent's direction or to home in on the opponent. For this you'll need a few variables to spare and simply place the following at the beginning of the projectile's state:

 

[State 24010, Conditional Varset]
type = null
triggerall = time = 0 ;<-- See notes below
trigger1 = numenemy
trigger1 = (fvar(0):= (enemynear(0),pos x - pos x) * facing) || 1
trigger1 = (fvar(1):= enemynear(0),pos y - pos y + enemynear(0),const(size.mid.pos.y)) || 1
trigger1 = (fvar(2):=((fvar(0)**2.0)+(fvar(1)**2.0))**0.5) || 1
trigger2 = !numenemy
trigger2 = (fvar(0):= 320.0 * facing) || 1
trigger2 = (fvar(1):= 0.0) || 1
trigger2 = (fvar(2):=((fvar(0)**2.0)+(fvar(1)**2.0))**0.5) || 1

What this does is uses simple trigonometry to calculate the vectors between the opponent and the projectile. The second set of triggers (trigger 2) is necessary in the event that an opponent doesn't exist so that the code doesn't bug out, and will simply cause the projectile to fire in a straight line if there is no opponent. You might be wondering about that triggerall line at the top. Depending on what this value is will determine on whether or not the projectile is aimed or homing; an aimed projectile will simply track the opponent's position at the time of creation and fire at that direction, while a homing projectile will continuously chase the opponent for as long as the projectile exists. The above code is for an aimed projectile, but to change this to a homing projectile, simply replace "triggerall = time = 0" with "triggerall = 1".

 

But we're not done yet. We still need to apply these vectors to the projectile's velocity. Assuming that the projectile's base velocity is 10:

 

[State 24010, Velset]
type = velset
trigger1 = 1
x = 10.0*fvar(0)/fvar(2)
y = 10.0*fvar(1)/fvar(2)
ignorehitpause = 1

What we're doing here is normalizing our vectors and applying them to our projectile's base velocity. This is what will cause the aimed/homing behavior of the projectile.

 

If for some reason you also need your projectile to rotate in the direction it's traveling, make sure to add the following under your velset controller:

[State 2010, AS]
type = varset
trigger1 = vel x != 0
fvar(3) = atan(vel y/vel x)*(-180.0/pi)+((vel x<0)*180)

[State 2010, AS]
type = varset
trigger1 = vel x = 0
fvar(3) = ifelse(vel y<0,90,-90)

[State 2010, AS]
type = angledraw
trigger1 = 1
value = fvar(3)
ignorehitpause = 1

Just make sure that your projectile's graphic is facing horizontally or you'll need to make adjustments to the above code. This also checks if the projectile is traveling straight up or down; since the tangent of 90 degree angles are undefined, we need that second state controller there as a failsafe or else the code will bug out and you won't get a proper rotation.

Link to comment
Share on other sites

Damage Proration System

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

Proration, also known as Damage Dampening, is a system implemented in many fighting games that prevent combos from becoming too overpowered by decreasing the amount of damage dealt by each successive hit in a combo. The exact method of how proration is achieved differs by game; most Capcom games such as the Street Fighter and Marvel vs Capcom games use a linear proration system based on the number of hits in a combo, while most games by Arc System Works tend to use a scalar method where each hit of an attack applies its own scaling value. This tutorial will deal with the latter method, which gives you greater control over how damage is scaled.

 

First, you'll need two floating point variables for this to work; we'll be using fvar(10) and fvar(11). We'll be dealing with fvar(11) as our final dampener later on, but for now, start with the following in your -2 state:

[State -2, Reset var when the opponent recovers]
type = Varset
trigger1 = numenemy
trigger1 = (enemynear,movetype!=H)
trigger2 = !numenemy
fvar(10) = 1.0

[State -2, Final Dampen]
type = null
trigger1 = (fvar(11) := ifelse(0.1>fvar(10),0.1,fvar(10)) || 1
ignorehitpause = 1

[State -2, Dampener itself]
type = Attackmulset
trigger1 = 1
value = fvar(11)
ignorehitpause = 1

The first state controller will initialize our character's proration to 100% damage (the default), as well as reset our proration value whenever the opponent is not being comboed. The second state controller is optional, but is handy to have if you want there to be a minimum proration value, as many games that use this system tend to have one. In our above example, our minimum proration is set to 0.1 or 10%, so our damage scaling will never go below 10% of an attack's base damage. You can also further modify fvar(11) for any other system mechanics you might need, including counterhit bonuses or other scaling such as the damage penalty incurred with systems such as Custom Combo. The final controller here will apply fvar(11) as our final proration value and scale our Attack stat accordingly. Note that this will only apply to damage dealt by the player and not any helper projectiles spawned by the player; in order to apply our damage proration to those, you'll need to apply the proration value directly to the attack's damage value in the hitdef:

 

damage = ceil(220.0*root,fvar(11)),44

Here, we see a helper projectile with a base damage of 220 having the root's (aka the player's) proration value applied. The ceil function is needed to ensure that the damage never falls below 1 and always returns an integer value. Note that chip damage (block damage) is never scaled.

 

Now that we have a proration system in place, all that's left is to actually apply the proration values. Go through each of your character's attacks and add a varset under their hitdef with the amount of proration you want to apply on hit:

 

[State 200, Damage Dampen]
type = varset
trigger1 = movehit
fvar(10) = fvar(10)*0.92
ignorehitpause = 1
persistent = 0

Above, we see a controller for attack that deals 92% proration on hit. As mentioned at the beginning, this will multiply the proration value by whatever proration is set by the attack, which is then applied to the damage of the next attack in the combo. If you're doing this in a helper projectile, you'll want to substitute this for an equivalent parentvarset controller:

Quote

 

[State 200, Damage Dampen]

type = parentvarset

trigger1 = movehit

fvar(10) = (root,fvar(10))*0.92

ignorehitpause = 1

persistent = 0

 

 

If an attack deals multiple hits, you'll need to apply a proration controller for each individual hit in a similar fashion; this can be accomplished through the use of the animelemno() and animelemtime() triggers, or whatever system you are using to keep track of multiple hits.

Link to comment
Share on other sites

  • 3 weeks later...

Fix for Combo Counter Reset Bug

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

For those of you who may or may not already know, but whenever you have a helper-based projectile hit an opponent and then destroy itself, this causes a peculiar bug to occur in where Mugen's combo counter will reset to 0, even if you continue the combo. The combo itself is a still valid, but the misleading aesthetics can be jarring. Fortunately, this problem is easily circumvented if one understands how targets work in Mugen.

 

To put it simply, whenever a player or helper hits something else, that something else, whether it be another player or a helper, will be registered by the attacker by its playerid. Whenever something else hits that same object, the new attacker gains that object as a target, while the previous attacks drops the object as a target. As an example, suppose you do a simple combo of S.MP into Hadouken, the Hadouken in this case being a helper-based projectile; when P1 hits P2 with S.MP, P1 gains P2 on its list of targets. The moment P1 fires the Hadouken and the Hadouken hits P2, the Hadouken helper gains P2 as a target, while P1 drops P2 from its target list. The combo counter reset bug occurs whenever a helper destroys itself, which forces it to drop all targets it has prematurely while nothing else is claiming P2 as a target. The solution; have the helper last long enough until either a combo ends or a proper transfer of "target ownership" can occur by sending it to an intermediary "destruct" state;

 

; Universal Projectile Destruction
[Statedef 999]
type = A
physics = N
movetype = I
anim = 1998 ; your blank animation
velset = 0, 0
ctrl = 0
movehitpersist = 1

[State 1112, nothitby]
type = nothitby
trigger1 = ishelper
time = 1
value = SCA
ignorehitpause = 1

[State 1112, NoShadow]
type = AssertSpecial
trigger1 = ishelper
flag = noshadow
ignorehitpause = 1

[State 1112, End]
type = destroyself
triggerall = ishelper
trigger1 = !numtarget
trigger2 = numtarget
trigger2 = target, time <= 1 && !(target, hitshakeover)
ignorehitpause = 1

Rather than calling a Destroyself controller directly, you'll be using a changestate controller to send the helper into this state instead, which satisfies both conditions outlined above. If your character has any system-specific mechanics such as Safe Fall, you'll also need to copy the associated targetstate controllers into this state as well.

 

One thing to take note of is that, if you have anything that relies on numhelper=0 triggers, you'll need to accomodate for the new intermediate state. In Mugen 1.0 and 1.1, as well as in Ikemen, you can easily accomplish this through a cond statement, usually something along the lines of;

 

triggerall = cond(numhelper(xxx)>0,cond((helper(xxx),stateno=999),1,0),1)

Basically this is a substitute for numhelper(xxx)=0 that checks for the following logic; if helper #xxx exists, check to see if its in state 999; if it is, then return true, otherwise return false. Else, if helper #xxx doesnt exist, then return true.


 

 

Link to comment
Share on other sites

  • 3 months later...

On a side note, your damage dampen code doesn't really work on Simul Mode as it uses enemynear trigger, which makes fvar(10) always resets to 1, results in no damage dampen at all (which happens in all of your Touhou characters I can think of). The only way I think that can make it somehow work in Simul Mode:

[State -2, Log the first target's PlayerID]
type = varSet
triggerall = numtarget
triggerall = target,IsHelper = 0 ;Do not trigger for Helpers
trigger1 = !(PlayerIDExist(var(55)) && PlayerIDExist(var(57))) ;no logged IDs
trigger2 = PlayerIDExist(var(55)) ;one has been logged
trigger2 = target,ID != var(55) ;it's not me
trigger3 = PlayerIDExist(var(57)) ;one has been logged
trigger3 = target,ID != var(57) ;it's not me
var(55) = target,ID
ignorehitpause = 1
 
[State -2, Log the second target's PlayerID]
type = varSet
triggerall = numtarget
triggerall = target,IsHelper = 0 ;Do not trigger for Helpers
trigger1 = PlayerIDExist(var(55)) ;one has been logged
trigger1 = target,ID != var(55) ;it's not me
trigger2 = PlayerIDExist(var(57)) ;one has been logged
trigger2 = target,ID != var(57) ;it's not me
var(57) = target,ID
ignorehitpause = 1

[State -2, Reset var when the opponent recovers]
type = Varset
triggerall = numtarget && (var(55) || var(57))
trigger1 = PlayerIDExist(var(55))
trigger1 = target,ID = var(55)
trigger1 = PlayerID(var(55)),movetype!=H
trigger2 = PlayerIDExist(var(57))
trigger2 = target,ID = var(57)
trigger2 = PlayerID(var(57)),movetype!=H
fvar(10) = 1

[State -2, Reset var if no enemy]
type = Varset
trigger1 = !numtarget
fvar(10) = 1

Even so, that will not work if the first attack is from a helper.

Link to comment
Share on other sites

 Tbf, Simul Mode is a absolute broken mess and nightmare to work with. It's hardly worth catering to that mode, not to mention most people don't really use it nowadays as they did 10 years back. The damage scaling code otherwise works perfectly fine in a 1-on-1 environment, which is the intention RicePigeon had with his characters anyway.

 

 I'm personally eager for more coding snippets in the future, a lot of this stuff has been helpful to me.

Link to comment
Share on other sites

  • 3 months later...

Well the forums are back, so time to post this recent discovery

 

Invalid Combo Display System

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
yes.png
yes.png
yes.png
yes.png

 

 

This was something I ended up studying around late 2018 when I ran into issues involving my own code, so the majority of this code is loosely based on a similar method used by 41's characters.

If you've ever played any fighter that allows P2 to air tech out of a combo, such as Blazblue or Melty Blood, you'll know that the game usually gives you some type of indicator when you perform an invalid combo, where the opponent could have recovered during your combo but decided not to. This tutorial attempts to replicate such a system.

NOTE: You'll need at least 1 free variable in your character's code. For this example, I'll be using Var(35).

Assuming that you have the sprites needed for your "INVALID" indicator, you'll want to have 4 different animations: a fade in, and a fade out, one for each side (p1 and p2). Make sure that the last animation element of your fade-in animation has an infinite (-1) duration, while your fade-out animations have a finite time, otherwise you'll get weird instances of multiple explods; In this case, the fadeout animation lasts for 8f but you can adjust these to your liking:


 

;Invalid - Left fadein
[Begin Action 7944]
7900,8, -100,0, 1
7900,8, -80,0, 1
7900,8, -60,0, 1
7900,8, -40,0, 1
7900,8, -20,0, 1
7900,8, 0,0, -1

;Invalid - Left fadeout
[Begin Action 7946]
7900,8, 0,0, 1,, AS256D0
7900,8, 0,0, 1,, AS224D32
7900,8, 0,0, 1,, AS192D64
7900,8, 0,0, 1,, AS160D96
7900,8, 0,0, 1,, AS128D128
7900,8, 0,0, 1,, AS96D160
7900,8, 0,0, 1,, AS64D192
7900,8, 0,0, 1,, AS32D224

;Invalid - Right Fadein
[Begin Action 7945]
7901,8, 100,0, 1
7901,8, 80,0, 1
7901,8, 60,0, 1
7901,8, 40,0, 1
7901,8, 20,0, 1
7901,8, 0,0, -1

;Invalid - Right Fadeout
[Begin Action 7947]
7901,8, 0,0, 1,, AS256D0
7901,8, 0,0, 1,, AS224D32
7901,8, 0,0, 1,, AS192D64
7901,8, 0,0, 1,, AS160D96
7901,8, 0,0, 1,, AS128D128
7901,8, 0,0, 1,, AS96D160
7901,8, 0,0, 1,, AS64D192
7901,8, 0,0, 1,, AS32D224

Next, place the following code in your -2 state:

 

    [State -2, Invalid Combo Explod]
    type = Explod
    trigger1 = numhelper(27944)
    trigger1 = numexplod(27944)=0
    trigger1 = helper(27944),var(59)=1
    anim = 1998 ;<-Invisible anim
    ID = 27944
    pos = 0,0
    postype = p1  ;p2,front,back,left,right
    facing = 1
    vel = 0,0
    removetime = 999999999
    pausemovetime = 999999999
    supermovetime = 999999999
    sprpriority = 5
    ownpal = 1
    removeongethit = 0
    ignorehitpause = 1
     
    [State -2, Invalid Combo RemoveExplod]
    type = RemoveExplod
    triggerall = numexplod(27944)
    trigger1 = numhelper(27944)
    trigger1 = helper(27944),var(59)=1
    trigger1 = (helper(27944),var(0)=0)||(helper(27944),var(0)>100)
    trigger1 = numenemy
    trigger1 = enemynear(0),movetype!=H
    trigger2 = numhelper(27944)=0
    id = 27944
    ignorehitpause = 1
     
    [State -2, Invalid Combo Helper]
    type = helper
    trigger1 = numexplod(27944)=0
    trigger1 = numenemy
    trigger1 = P2StateType = A && P2MoveType = H
    trigger1 = EnemyNear,CanRecover
    trigger1 = (p2stateno != [120,155]) && (p2stateno!=[250,262]) && (p2stateno!=[450,451])
    trigger1 = movehit=0
    helpertype = normal
    name = "Invalid Combo"
    ID = 27944
    pos = 0,0
    postype = p1
    stateno = 27944
    ownpal = 1
    facing = 1
    pausemovetime = 999999999
    supermovetime = 999999999
     
    [State -2, Helper Movehit Flag]
    type = varadd
    trigger1 = var(35)>0
    var(35)=-1
    ignorehitpause = 1

 

In addition, add the following to each of your Helper states that have any Hitdefs (NOTE: Make sure these Helpers are created directly by the root and do not have another helper as its parent, otherwise this method will not work):

 

    [State 2011, Helper Hit Flag]
    type = parentvarset
    trigger1 = MoveHit=1
    trigger1 = p2stateno != [120,159]
    var(35)=2
    ignorehitpause = 1
    persistent = 0

 

This code will create a helper that will handle the check for the conditions for displaying the Invalid Combo explod. It also creates an invisible Explod whenever this helper is active and removes it whenever this helper does not exist or when the opponent is not in a hitstate. The helper will also check to see if this invisible explod exists and, if at any point it does not, then the conditions for displaying the actual explod are satisfied. For this example, anim 1998 is being used as a blank animation.

Var(35) acts as a flag to tell us when a helper-based projectile has hit an opponent, and then counts down. The reason we use a variable here instead of helper(xxx),movehit is not only to streamline the code instead of having to write down all Helper IDs, but also to avoid situations where multiple helpers with the same ID exist, such as any move that spawns multiple projectiles, which can easily cause the movehit triggers to return false negative results.

Next is the helper state:

    ;=====================================================
    ; Invalid Combo Helper
    [StateDef 27944]
    type = A
    physics = N
    moveType = I
    anim = 1998
    velSet = 0,0
    ctrl = 0
    ;var(0) - Elapsed time measurement after hit (for Invalid)
    ;var(2) - helper attack or hit flag (for invalid display)
    ;var(59) - Flag (0=Default, 1=Invalid)
     
    [State -2, Varset]
    type = varset
    triggerall = var(2)=0
    trigger1 = root,var(35)>0
    trigger2 = root,projhittime(XXXX)=1 ;PROJECTILE CONTROLLER IDS GO HERE
    var(2)=1
     
    [State -2, Varset]
    type = varset
    triggerall = time=0
    triggerall = var(59)=0
    trigger1 = root,numexplod(27944)=0
    trigger1 = numenemy
    trigger1 = p2statetype=A
    trigger1 = root,movehit=0
    trigger1 = EnemyNear(0),CanRecover
    trigger1 = (p2stateno != [120,155]) && (p2stateno!=[250,262]) && (p2stateno!=[450,451])
    var(59)=1
     
    [State -2, Varset]
    type = varset
    triggerall = var(0)=0 && var(59)=1
    trigger1 = root,movehit=1
    trigger1 = numenemy
    trigger1 = p2statetype=A
    trigger1 = enemynear(0),hitshakeover
    trigger2 = var(2)!=0
    var(0)=1
     
    [State -2, Counterhit Explod]
    type = Explod
    triggerall = numexplod(7944)=0
    trigger1 = var(59)=1
    trigger1 = var(0)>0
    anim = 7944+1*(root,teamside!=1)
    ID = 7944
    pos = ifelse(teamside=1,0,320),120
    postype = left
    facing = 1
    vfacing = 1
    bindtime = -1
    removetime = -1
    pausemovetime = 999999999
    supermovetime = 999999999
    sprpriority = 999
    scale = 0.5,0.5
    ontop = 1
    ownpal = 1
    ignorehitpause = 1
     
    [State -2, Counterhit Explod]
    type = Explod
    triggerall = numexplod(7945)=0
    triggerall = numexplod(7944)=1
    triggerall = numenemy
    trigger1 = enemynear(0),stateno=5150 || enemynear(0),alive=0
    trigger2 = p2movetype!=H && p2stateno!=5120
    trigger3 = enemynear(0),ctrl = 1
    anim = 7946+1*(root,teamside!=1)
    ID = 7945
    pos = ifelse(teamside=1,0,320),120
    postype = left
    facing = 1
    vfacing = 1
    bindtime = -1
    removetime = -2
    pausemovetime = 999999999
    supermovetime = 999999999
    sprpriority = 999
    scale = 0.5,0.5
    ontop = 1
    ownpal = 1
    ignorehitpause = 1
     
    [State -2, REX]
    type = removeexplod
    triggerall = numenemy
    trigger1 = enemynear(0),stateno=5150 || enemynear(0),alive=0
    trigger2 = p2movetype!=H && p2stateno!=5120
    trigger3 = enemynear(0),ctrl = 1
    id = 7944
    ignorehitpause = 1
     
    [State -2, Timer]
    type = varadd
    trigger1 = var(0)>0
    trigger1 = var(59)=1
    var(0)=1
     
    [State -2, Destroyself]
    type = destroyself
    trigger1 = var(59)=1
    trigger1 = var(0)=0 || var(0)>101
    trigger1 = numenemy
    trigger1 = (p2movetype!=H && p2stateno!=5120)||(enemynear(0),ctrl = 1)||(enemynear(0),stateno=5150 || enemynear(0),alive=0)
    trigger2 = var(59)=0
    trigger2 = time>=1

 

One important thing to note is the first varset controller. If you have any attacks that utilize any projectiles using a Projectile state controller (as opposed to a Helper-based projectile), you'll need to write down each of their IDs as a separate trigger here, as this method won't be able to account for them otherwise. Effectively what this helper does is toggle between states where the opponent is in a state where they can recover (using var(59)). The reason why can't just use the canrecover trigger by itself is that this will immediately be reset the next time P2 is hit, so we need to be able to keep track of their previous state before the next hit connects. The way this helper is set up is that once these conditions are true, the Fade-in animation for our explod will display until the combo ends, at which point it will transition into the Fade-out animation.

Link to comment
Share on other sites

Psuedocode for the above in case anyone wants to devise their own implementations of this method:

 

--------------------------
In the player's State -2:
--------------------------

If the Invalid Check Helper exists and has its var(59)=1, create a permanent explod(99) if it does not exist.
The explod should be active during all pauses.

Remove the explod if one of the following cases is true:
Case 1:
- Invalid Helper exists and its var(59)=1
- Enemy is not in a hitstate
- Helper's var(0) is either 0 or greater than 100 (see below)
Case 2:
- Invalid Helper does not exist

Create the Invalid Helper under one of the following conditions:
Case 1:
- Explod(99) does not exist
- P2 is in an aerial state and a hit state
- P2 can recover
- P2 is not in a guarding state or any other custom state
- P1's move did not hit (movehit=0)
Case 2:
- Explod(99) does not exist
- P2 is in Specific Custom State for a certain duration

The Invalid Helper must be active during all pauses


----------------------------
In the Invalid Helper State:
----------------------------

var(0) - Elapsed time measurement after hit (for Invalid)
var(2) - Helper attack hit flag (for invalid display)
var(59) - Call type determination flag (0 = default, 1 = invalid)

When var(2)=0, if root,numhelper(x) and (helper(x),movehit=1), set var(2)=1

At Invalid Helper time=0, when var(59)=0, set var(59)=1 under any of the following conditions:
Case 1:
- Root,numexplod(99)=0 (see above)
- Enemy is in an aerial state
- root's move did not hit (movehit=0)
- Enemy can recover, and is not in any Custom States

Set var(0)=1 when one of the following cases are true:
Case 1: 
- var(59)=1 and var(0)=0
- root,movehit=1
- Opponent is in an aerial state with hitshakeover = 1
Case 2: 
- var(59)=1 and var(0)=0
- var(2)!=0

Display the Invalid Combo explod when all of the following are true:
- Explod does not exist
- var(59)=1
- var(0)>0

Remove the explod when one of the following are true:
- enemy is not in hitstate and is not in state 5120
- enemy regains control
- enemy is KOed

Increase var(0) by 1 when the following are true:
- var(59) = 1
- var(0) is nonzero

Destroy the helper when one of the following is true:
Case 1:
- Var(59)=1
- var(0)=0 OR >=102
- Opponent is not in a hitstate and not in 5120, has control, or is KOed
Case 3:
- Var(59)=0
- helper's time>=1

 

Link to comment
Share on other sites

  • 4 months later...

Cond Recursive Redirection Exploit

Compatibility:

WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN
no.png
yes.png
yes.png
yes.png

 

Note: This is based on a discovery by intrebuchet, David11, & Tendou, so credits to them. You can find out more information about this discovery here.

 

So the Cond() trigger, which was first introduced in Mugen 1.0, has a peculiar behavior when executed using any kind of trigger redirection (ie; root,var(x), etc) in that the cond statement will run as if executed by the entity it is redirected to, rather than the actual entity calling it. What can we possibility do with this? Recursive redirection. For those who don't know, recursive redirection (ie: root,helper(xxx),stateno) is not currently supported by any version of Mugen. This exploit completely bypasses this:.

 

root,cond(1,(helper(xxx),stateno),0)

Using this syntax, we can now execute the functional equivalent of root,helper(xxx),stateno if such a trigger were possible. Of course, this exploit can also accept nested cond triggers to support more than 2 redirects. However, this same exploit can also be used in tandem with the assignment operator (":=") to produce some rather peculiar effects:

 

root,cond(1,var(10):=12,0)

When executed from a helper, this is set the root (player)'s var(10) to a value of 12, regardless of what the helper's parent is; If done from a helper that is spawned directly by the root, this line of code is functionally equivalent to using the ParentVarSet controller, but if the helper calling this statement is not directly spawned by the root (ie: by another helper), then this line of code would be equivalent to the nonexistent RootVarSet state controller, if such a state controller were to exist. The only other option would have to involve using ParentVarSet controllers in both the child and parent helpers which, given how state processing order is executed in Mugen, would produce a 1 tick delay, which increases linearly with each nested helper. This method, on he other hand, provides no such delay.

 

But perhaps the most dangerous application of this exploit is that this works with any form of trigger redirection, including enemy trigger redirects:

 

enemy,cond(1,var(10):=20,0)

This trigger will set the opponent's var(10) to a value of 20, without the player ever making any kind of physical contact with the opponent. While this may have practical applications in a fullgame environment, you can imagine how disastrous this can be if the opponent relies on their variables for something system related (ie: damage proration) only to have their opponent overwrite their variables without warning.

 

LIMITATIONS:

 

If using this exploit with a helper redirect, the exploit can only return the first helper found (or, in the case of helperid(xxx), the first helper found with an ID of xxx):

unknown.png

In this example, the exploit was used by Sanae to spontaneously change the angle (and therefore, the trajectory) of Cirno's helper-based projectiles; but because of the aforementioned limitation, only the first helper found was affected by this.

 

As an additional note for IKEMEN users, IKEMEN uses additional sysvars not found in MUGEN in order to handle certain constants (ie: such as life, maxlife, power, etc), which are not affected by this exploit, most likely due to the fact that these variables are used as a cache for loading this constants.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...