Skip to content

Commit b07417f

Browse files
authored
Merge pull request #3777 from potsmugen/push2
fix: noguard during pause, shadow palettes, zero width, GetHitVarSet attr
2 parents 8dbd863 + 5d6e479 commit b07417f

10 files changed

Lines changed: 181 additions & 117 deletions

File tree

src/anim.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,9 +1112,10 @@ func (a *Animation) ShadowDraw(window *[4]int32, x, y, xscl, yscl, vscl, rxadd f
11121112
if a.spr.coldepth <= 8 && (color != 0 || intensity > 0) {
11131113
if a.sff.header.Version[0] == 2 && a.sff.header.Version[2] == 1 {
11141114
pal, _ := a.pal(pfx)
1115-
if a.spr.PalTex == nil {
1116-
a.spr.PalTex = a.spr.CachePalTex(pal)
1117-
}
1115+
// This nil check broke the rare instance where the palette affects the shadow, such as palettes with alpha
1116+
//if a.spr.PalTex == nil {
1117+
a.spr.PalTex = a.spr.CachePalTex(pal)
1118+
//}
11181119
rp.paltex = a.spr.PalTex
11191120
} else {
11201121
rp.paltex = sys.whitePalTex

src/bytecode.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3004,9 +3004,9 @@ func (be BytecodeExp) run_ex(c *Char, i *int, oc *Char) {
30043004
case OC_ex_matchover:
30053005
sys.bcStack.PushB(sys.matchOver())
30063006
case OC_ex_matchno:
3007-
sys.bcStack.PushI(sys.match)
3007+
sys.bcStack.PushI(sys.matchNo)
30083008
case OC_ex_roundno:
3009-
sys.bcStack.PushI(sys.round)
3009+
sys.bcStack.PushI(sys.roundNo)
30103010
case OC_ex_roundsexisted:
30113011
sys.bcStack.PushI(c.roundsExisted())
30123012
case OC_ex_ishometeam:

src/char.go

Lines changed: 125 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,7 +2089,7 @@ func (e *Explod) update() {
20892089
for i := range e.velocity {
20902090
e.velocity[i] *= e.friction[i]
20912091
e.velocity[i] += e.accel[i]
2092-
if math.Abs(float64(e.velocity[i])) < 0.1 && math.Abs(float64(e.friction[i])) < 1 {
2092+
if Abs(e.velocity[i]) < 0.1 && Abs(e.friction[i]) < 1 {
20932093
e.velocity[i] = 0
20942094
}
20952095
}
@@ -4340,7 +4340,8 @@ func (c *Char) loadPalettes() {
43404340

43414341
// Allocate space if necessary
43424342
gi.palettedata.palList.SetSource(targetIdx, pl)
4343-
gi.palettedata.palList.PalTable[[...]uint16{1, uint16(i + 1)}] = targetIdx
4343+
gi.palettedata.palList.PalTable[[2]uint16{1, uint16(i + 1)}] = targetIdx
4344+
gi.palettedata.palList.numcols[[2]uint16{1, uint16(i + 1)}] = 256 // ACT files are always 256 colors
43444345

43454346
// Redirect index 0 without destroying the unique SFFv1 palette at physical index 0
43464347
if tmp == 0 && i > 0 {
@@ -6013,7 +6014,7 @@ func (c *Char) roundsExisted() int32 {
60136014
return 0
60146015
}
60156016
}
6016-
return sys.round - 1
6017+
return sys.roundNo - 1
60176018
}
60186019
return sys.roundsExisted[c.playerNo&1]
60196020
}
@@ -6471,14 +6472,6 @@ func (c *Char) stateChange1(no int32, pn int) bool {
64716472
LogMessage("Maximum ChangeState loops: %v, %v, %v -> %v -> %v", sys.changeStateNest, c.name, c.ss.prevno, c.ss.no, no)
64726473
return false
64736474
}
6474-
var ctrlsps_backup []int32
6475-
if c.hitPause() {
6476-
// If in hitpause, back up the current state's persistent.
6477-
ctrlsps_backup = make([]int32, len(c.ss.sb.ctrlsps))
6478-
copy(ctrlsps_backup, c.ss.sb.ctrlsps)
6479-
} else {
6480-
ctrlsps_backup = nil
6481-
}
64826475

64836476
c.ss.prevno = c.ss.no
64846477
c.ss.no = Max(0, no)
@@ -6527,7 +6520,7 @@ func (c *Char) stateChange1(no int32, pn int) bool {
65276520

65286521
c.localscl = newLs
65296522
}
6530-
var ok bool
6523+
65316524
// Check if player is trying to change to a negative state.
65326525
if no < 0 {
65336526
sys.appendToConsole(c.warn() + "attempted to change to negative state")
@@ -6542,7 +6535,16 @@ func (c *Char) stateChange1(no int32, pn int) bool {
65426535
LogMessage("Changed to out of bounds state number: P%v:%v", pn+1, no)
65436536
}
65446537
}
6538+
6539+
// If in hitpause, back up the current state's persistent.
6540+
var ctrlsps_backup []int32
6541+
if c.hitPause() {
6542+
ctrlsps_backup = make([]int32, len(c.ss.sb.ctrlsps))
6543+
copy(ctrlsps_backup, c.ss.sb.ctrlsps)
6544+
}
6545+
65456546
// Always attempt to change to the state we set to.
6547+
var ok bool
65466548
if c.ss.sb, ok = sys.cgi[pn].states[c.ss.no]; !ok {
65476549
sys.appendToConsole(c.warn() + fmt.Sprintf("changed to invalid state %v (from state %v)", no, c.ss.prevno))
65486550
if !sys.ignoreMostErrors {
@@ -6551,6 +6553,7 @@ func (c *Char) stateChange1(no int32, pn int) bool {
65516553
c.ss.sb = *newStateBytecode(pn)
65526554
c.ss.sb.stateType, c.ss.sb.moveType, c.ss.sb.physics = ST_U, MT_U, ST_U
65536555
}
6556+
65546557
// Reset persistent counters for this state (Ikemen chars)
65556558
// This used to belong to (*StateBytecode).init(), but was moved outside there
65566559
// due to a MUGEN 1.1 problem where persistent was not getting reset until the end
@@ -6575,6 +6578,7 @@ func (c *Char) stateChange1(no int32, pn int) bool {
65756578
c.hitStateChangeIdx = -1
65766579
}
65776580
}
6581+
65786582
c.stchtmp = true
65796583
return true
65806584
}
@@ -8975,12 +8979,12 @@ func (c *Char) rdDistX(rd *Char, oc *Char) BytecodeValue {
89758979
return BytecodeUndefined()
89768980
}
89778981
dist := c.facing * c.distX(rd, oc)
8978-
if c.stWgi().ikemenver[0] == 0 && c.stWgi().ikemenver[1] == 0 {
8979-
if c.stWgi().mugenver[0] != 1 {
8980-
// Before Mugen 1.0, rounding down to the nearest whole number was performed.
8981-
dist = float32(int32(dist))
8982-
}
8982+
8983+
// Before Mugen 1.0, rounding down to the nearest whole number was performed.
8984+
if c.stWgi().ikemenver[0] == 0 && c.stWgi().ikemenver[1] == 0 && c.stWgi().mugenver[0] != 1 {
8985+
dist = float32(int32(dist))
89838986
}
8987+
89848988
return BytecodeFloat(dist)
89858989
}
89868990

@@ -8989,12 +8993,12 @@ func (c *Char) rdDistY(rd *Char, oc *Char) BytecodeValue {
89898993
return BytecodeUndefined()
89908994
}
89918995
dist := c.distY(rd, oc)
8992-
if c.stWgi().ikemenver[0] == 0 && c.stWgi().ikemenver[1] == 0 {
8993-
if c.stWgi().mugenver[0] != 1 {
8994-
// Before Mugen 1.0, rounding down to the nearest whole number was performed.
8995-
dist = float32(int32(dist))
8996-
}
8996+
8997+
// Before Mugen 1.0, rounding down to the nearest whole number was performed.
8998+
if c.stWgi().ikemenver[0] == 0 && c.stWgi().ikemenver[1] == 0 && c.stWgi().mugenver[0] != 1 {
8999+
dist = float32(int32(dist))
89979000
}
9001+
89989002
return BytecodeFloat(dist)
89999003
}
90009004

@@ -11551,18 +11555,19 @@ func (c *Char) actionPrepare() {
1155111555
if c.minus != 3 || c.csf(CSF_destroy) || c.scf(SCF_disabled) {
1155211556
return
1155311557
}
11558+
1155411559
c.pauseBool = false
11555-
if c.cmd != nil {
11556-
if sys.supertime > 0 {
11557-
c.pauseBool = c.superMovetime == 0
11558-
} else if sys.pausetime > 0 && c.pauseMovetime == 0 {
11559-
c.pauseBool = true
11560-
}
11560+
if sys.supertime > 0 {
11561+
c.pauseBool = c.superMovetime == 0
11562+
} else if sys.pausetime > 0 && c.pauseMovetime == 0 {
11563+
c.pauseBool = true
1156111564
}
11562-
c.acttmp = -int8(Btoi(c.pauseBool)) * 2
1156311565
// Due to the nature of how pauses are processed, these are needed to fix an "off by 1" error in the PauseTime trigger
1156411566
c.prevSuperMovetime = c.superMovetime
1156511567
c.prevPauseMovetime = c.pauseMovetime
11568+
11569+
c.acttmp = -int8(Btoi(c.pauseBool)) * 2
11570+
1156611571
if !c.pauseBool {
1156711572
// Perform basic actions
1156811573
if c.keyctrl[0] && c.cmd != nil && (c.helperIndex == 0 || c.controller >= 0) {
@@ -11623,7 +11628,7 @@ func (c *Char) actionPrepare() {
1162311628
c.setCSF(CSF_playerpush)
1162411629
}
1162511630
}
11626-
// Reset player pushing priority
11631+
// Reset player pushing properties
1162711632
c.pushPriority = 0
1162811633
c.pushAffectTeam = 1
1162911634
// HitBy timers
@@ -11668,6 +11673,11 @@ func (c *Char) actionPrepare() {
1166811673
// This AssertSpecial flag is special in that it must always reset regardless of hitpause
1166911674
c.unsetASF(ASF_animatehitpause)
1167011675

11676+
// This variable is necessary because NoStandGuard is reset before the walking instructions are checked
11677+
// https://github.com/ikemen-engine/Ikemen-GO/issues/1966
11678+
// Update: No longer strictly necessary but we'll leave it in for now
11679+
c.prevNoStandGuard = c.asf(ASF_nostandguard)
11680+
1167111681
// The flags in this block are to be reset even during hitpause
1167211682
// Exception for WinMugen chars, where they persisted during hitpause
1167311683
if c.stWgi().ikemenver[0] != 0 || c.stWgi().ikemenver[1] != 0 || c.stWgi().mugenver[0] == 1 || !c.hitPause() {
@@ -11677,8 +11687,10 @@ func (c *Char) actionPrepare() {
1167711687
c.alpha = [2]int32{255, 0}
1167811688
c.offset = [2]float32{}
1167911689
// Reset all AssertSpecial flags except the following, which are reset elsewhere in the code
11680-
c.assertFlag = (c.assertFlag&ASF_nostandguard | c.assertFlag&ASF_nocrouchguard | c.assertFlag&ASF_noairguard |
11681-
c.assertFlag&ASF_runfirst | c.assertFlag&ASF_runlast)
11690+
// TODO: Maybe these don't need special treatment anymore either
11691+
// All this does right now is make IsAsserted more accurate, but that's already inaccurate in other places
11692+
keptflags := ASF_runfirst | ASF_runlast
11693+
c.assertFlag &= keptflags
1168211694
}
1168311695

1168411696
// The flags below also reset during hitpause, but are new to Ikemen and don't need the exception above
@@ -11777,6 +11789,7 @@ func (c *Char) actionRun() {
1177711789
c.minus = 0
1177811790
c.ss.sb.run(c)
1177911791
}
11792+
1178011793
// Guarding instructions
1178111794
c.unsetSCF(SCF_guard)
1178211795
if ((c.scf(SCF_ctrl) || c.ss.no == 52) &&
@@ -11787,6 +11800,7 @@ func (c *Char) actionRun() {
1178711800
c.ss.stateType == ST_A && !c.asf(ASF_noairguard)) {
1178811801
c.setSCF(SCF_guard)
1178911802
}
11803+
1179011804
if !c.pauseBool {
1179111805
if c.keyctrl[0] && c.cmd != nil {
1179211806
if c.ctrl() && (c.controller >= 0 || c.helperIndex == 0) {
@@ -12005,6 +12019,7 @@ func (c *Char) actionFinish() {
1200512019
if c.minus < 1 || c.csf(CSF_destroy) || c.scf(SCF_disabled) {
1200612020
return
1200712021
}
12022+
1200812023
if !c.pauseBool {
1200912024
if c.palfx != nil && c.ownpal {
1201012025
c.palfx.step()
@@ -12013,18 +12028,18 @@ func (c *Char) actionFinish() {
1201312028
c.ghv.frame = false
1201412029
c.mhv.frame = false
1201512030
}
12031+
1201612032
// Reset inguarddist flag before running hit detection (where it will be updated)
1201712033
// https://github.com/ikemen-engine/Ikemen-GO/issues/2328
1201812034
c.inguarddist = false
12019-
// This variable is necessary because NoStandGuard is reset before the walking instructions are checked
12020-
// https://github.com/ikemen-engine/Ikemen-GO/issues/1966
12021-
c.prevNoStandGuard = c.asf(ASF_nostandguard)
12022-
c.unsetASF(ASF_nostandguard | ASF_nocrouchguard | ASF_noairguard)
12035+
1202312036
// Save current HitFall value before hit detection
1202412037
c.prevfallflag = c.ghv.fallflag
12038+
1202512039
// Update Z scale
1202612040
// Must be placed after posUpdate()
1202712041
c.zScale = sys.updateZScale(c.pos[2], c.localscl)
12042+
1202812043
// KO behavior
1202912044
if !c.hitPause() && !c.pauseBool {
1203012045
if c.alive() && c.life <= 0 && !sys.gsf(GSF_globalnoko) && !c.asf(ASF_noko) && (!c.ghv.guarded || !c.asf(ASF_noguardko)) {
@@ -12050,13 +12065,15 @@ func (c *Char) actionFinish() {
1205012065
}
1205112066
}
1205212067
}
12068+
1205312069
// Over flags (char is finished for the round)
1205412070
if c.alive() && c.life > 0 && !sys.roundEnded() {
1205512071
c.unsetSCF(SCF_over_alive | SCF_over_ko)
1205612072
}
1205712073
if c.ss.no == 5150 && !c.scf(SCF_over_ko) { // Actual KO is not required in Mugen
1205812074
c.setSCF(SCF_over_ko)
1205912075
}
12076+
1206012077
// Signal that "actionFinish" has finished
1206112078
c.minus = 2
1206212079
}
@@ -13669,30 +13686,11 @@ func (cl *CharList) pushDetection(getter *Char) {
1366913686
gybot := (getter.pos[1] + gbox[3]) * getter.localscl
1367013687

1367113688
overlapY := Min(cybot, gybot) - Max(cytop, gytop)
13672-
if overlapY <= 0 {
13673-
continue
13674-
}
1367513689

13676-
// Clamp width
13677-
// Mugen secretly does this for some reason
13678-
// https://github.com/ikemen-engine/Ikemen-GO/issues/3164
13679-
if c.stWgi().ikemenver[0] == 0 && c.stWgi().ikemenver[1] == 0 {
13680-
minwidth := 5.0 / c.localscl
13681-
if cbox[0] > -minwidth {
13682-
cbox[0] = -minwidth
13683-
}
13684-
if cbox[2] < minwidth {
13685-
cbox[2] = minwidth
13686-
}
13687-
}
13688-
if getter.stWgi().ikemenver[0] == 0 && getter.stWgi().ikemenver[1] == 0 {
13689-
minwidth := 5.0 / getter.localscl
13690-
if gbox[0] > -minwidth {
13691-
gbox[0] = -minwidth
13692-
}
13693-
if gbox[2] < minwidth {
13694-
gbox[2] = minwidth
13695-
}
13690+
// For the y-axis, an overlap of exactly 0 is also valid for pushing characters away from each other, hence '<'
13691+
// We don't need a "zero-height case" because the y-overlap is only used as a filter, not to calculate the push distance
13692+
if overlapY < 0 {
13693+
continue
1369613694
}
1369713695

1369813696
// X-axis check
@@ -13716,25 +13714,84 @@ func (cl *CharList) pushDetection(getter *Char) {
1371613714

1371713715
overlapX := Min(gxright, cxright) - Max(gxleft, cxleft)
1371813716

13719-
// X-axis fail
13717+
// Zero width case
13718+
// These can also push in Mugen
13719+
// https://github.com/ikemen-engine/Ikemen-GO/issues/3164
13720+
if overlapX == 0 && (cxleft == cxright || gxleft == gxright) {
13721+
cHalfW := (cxright - cxleft) * 0.5
13722+
gHalfW := (gxright - gxleft) * 0.5
13723+
cCenterX := (cxright + cxleft) * 0.5
13724+
gCenterX := (gxright + gxleft) * 0.5
13725+
13726+
overlapX = (cHalfW + gHalfW) - Abs(cCenterX - gCenterX)
13727+
}
13728+
13729+
/*
13730+
// In addition to the normal width check, Mugen also checks overlap between an undocumented "internal width" of 5 pixels
13731+
// The normal and fallback width checks are not mixed with each other
13732+
// Update: The addition of the zero width case makes this seemingly unnecessary
1372013733
if overlapX <= 0 {
13734+
// We will only do it for Mugen characters because it defeats the purpose of lowering width
13735+
cIsOld := c.stWgi().ikemenver[0] == 0 && c.stWgi().ikemenver[1] == 0
13736+
gIsOld := getter.stWgi().ikemenver[0] == 0 && getter.stWgi().ikemenver[1] == 0
13737+
if cIsOld || gIsOld {
13738+
if cIsOld {
13739+
minwidth := 5.0 / c.localscl
13740+
cxleft = cposx - minwidth
13741+
cxright = cposx + minwidth
13742+
}
13743+
if gIsOld {
13744+
minwidth := 5.0 / getter.localscl
13745+
gxleft = gposx - minwidth
13746+
gxright = gposx + minwidth
13747+
}
13748+
overlapX = Min(gxright, cxright) - Max(gxleft, cxleft)
13749+
}
13750+
}
13751+
*/
13752+
13753+
// X-axis fail
13754+
// An overlap of exactly 0 is still valid because pushing may happen along the z-axis
13755+
if overlapX < 0 {
1372113756
continue
1372213757
}
1372313758

1372413759
// Z-axis check
1372513760
// We don't use the zAxisCheck function because we need the actual overlap amount
13726-
cposz := c.pos[2] * c.localscl
13727-
cztop := cposz - c.sizeDepth[0]*c.localscl
13728-
czbot := cposz + c.sizeDepth[1]*c.localscl
13761+
// We'll also declare all the vars upfront but only use them if z-axis is enabled
13762+
var overlapZ float32
13763+
var cposz, cztop, czbot, gposz, gztop, gzbot float32
13764+
13765+
if sys.zEnabled() {
13766+
cposz = c.pos[2] * c.localscl
13767+
cztop = cposz - c.sizeDepth[0]*c.localscl
13768+
czbot = cposz + c.sizeDepth[1]*c.localscl
13769+
13770+
gposz = getter.pos[2] * getter.localscl
13771+
gztop = gposz - getter.sizeDepth[0]*getter.localscl
13772+
gzbot = gposz + getter.sizeDepth[1]*getter.localscl
13773+
13774+
overlapZ = Min(gzbot, czbot) - Max(gztop, cztop)
1372913775

13730-
gposz := getter.pos[2] * getter.localscl
13731-
gztop := gposz - getter.sizeDepth[0]*getter.localscl
13732-
gzbot := gposz + getter.sizeDepth[1]*getter.localscl
13776+
// Zero depth case
13777+
if overlapZ == 0 && (cztop == czbot || gztop == gzbot) {
13778+
cHalfD := (czbot - cztop) * 0.5
13779+
gHalfD := (gzbot - gztop) * 0.5
13780+
cCenterZ := (czbot + cztop) * 0.5
13781+
gCenterZ := (gzbot + gztop) * 0.5
1373313782

13734-
overlapZ := Min(gzbot, czbot) - Max(gztop, cztop)
13783+
overlapZ = (cHalfD + gHalfD) - Abs(cCenterZ-gCenterZ)
13784+
}
13785+
}
1373513786

1373613787
// Z-axis fail
13737-
if overlapZ <= 0 {
13788+
// An overlap of exactly 0 is still valid because pushing may happen along the x-axis
13789+
if overlapZ < 0 {
13790+
continue
13791+
}
13792+
13793+
// If players are barely touching but the pushing distance will be 0, just skip the no-op math
13794+
if overlapX == 0 && overlapZ == 0 {
1373813795
continue
1373913796
}
1374013797

0 commit comments

Comments
 (0)