RicePigeon Posted April 16, 2018 Posted April 16, 2018 This will mostly be a repository for anything that I've come across during my experience with the engine. CoolAnimeHustler 1
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 Workaround for Missing "ParentExist" Trigger Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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. Ori-Ori 1
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 Hitdefs with Multiple Attributes Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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: 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:
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 Asymmetrical Palette Display using ReMapPal (aka The Gill Effect) Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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; 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.
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 More Efficient Random Number Spreads Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 Multiple Attack Variants Using a Single State Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 Hitdef Animtype Quirk Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
RicePigeon Posted April 16, 2018 Author Posted April 16, 2018 Improved Guarding States for Crossup Detection Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
RicePigeon Posted April 17, 2018 Author Posted April 17, 2018 Homing/Tracking/Aimed Projectiles Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
RicePigeon Posted April 17, 2018 Author Posted April 17, 2018 Damage Proration System Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
RicePigeon Posted May 9, 2018 Author Posted May 9, 2018 Fix for Combo Counter Reset Bug Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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.
Mr.Giang Posted August 29, 2018 Posted August 29, 2018 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.
Nep Heart Posted August 30, 2018 Posted August 30, 2018 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.
Mr.Giang Posted August 30, 2018 Posted August 30, 2018 While I agree with you, I think it's still better to note this issue than to ignore it completely. However, this thread is still one of the most valuable MUGEN tutorials IMO.
RicePigeon Posted December 12, 2018 Author Posted December 12, 2018 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 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. Nep Heart and CoolAnimeHustler 1 1
RicePigeon Posted December 12, 2018 Author Posted December 12, 2018 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 CoolAnimeHustler 1
RicePigeon Posted April 22, 2019 Author Posted April 22, 2019 Cond Recursive Redirection Exploit Compatibility: WinMUGEN Mugen 1.0 Mugen 1.1b IKEMEN 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): 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.
PlasmoidThunder Posted April 22, 2019 Posted April 22, 2019 It's worth noting that IKEMEN does support recursive redirects by default, so while root,target(###),stateno won't work in MUGEN without the cond exploit, it will in IKEMEN. But for compatibility, you might as well use the cond exploit regardless. Project LG said: God knows you shit out characters like a friggin' gumball machine.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now