Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
perkristian

Smoother weapon sprite animations - Released!

Recommended Posts

@nightfright or any other capable member
I'd love to see an update for nightfright's enhanced version to make it compatible again with the new 1.3 version of Revenant100's sprite fixing project. can't decide to give up one for the other ^^

the latest version can be found here:
http://forum.zdoom.org/viewtopic.php?f=19&t=32628

edit:
never mind, was an error on my side. works fine with v1.3

Share this post


Link to post

Pardon the bump fellas, but I noticed that the dehacked version of Perk's stuff hasn't been touched in a while, so I spent the last week in between runs on the rescue to rebuild it using the latest frames found over at the zdoom forums.

I made sure that it doesn't sequence break anything designed for vanilla play, but I used dehacked 3.0 format (BEX) and embedded it into a wad file, therefore it is BOOM compatible only.

I have tested it with prBoom, Odamex, and Zdaemon and they all work perfectly. Demos even play fine with it in prBoom.

Some changes include:

-Arachnotron fires the same plasma as the player, so the green plasma frames were used for the weapons.
-The teleport fog and item respawn fog use the same frames. The teleport fog has also had a few frames removed, but you won't even notice.
-BFG explosion no longer has it's last "2 pixels thin" pointless frame. Cry about it.
-Torches are only 3 frames long in animation. Again, its hardly noticeable.
-Seeing eye only has 3 frames now.
-Armor pickups have no animation. I personally like them better this way...WHY WOULD ARMOR GLOW!?
-Health potion only has 3 frames.
-Soul sphere only has 4 frames like the other orbs now.
-Automap only has 3 frames.
-Liteamp pickup has only one frame at fullbright BECAUSE WHY WOULD THEY FLICKER!?
-A few other changes that I can't think of right now because OH MY GOD SLEEP GIVE IT TO ME!

It's up on best-ever's file host.
http://www.best-ever.org/download?file=boom_enhanced1_1.wad

If you find any bugs, give me a heads up and I'll make a revision.

FUCK...I can still smell that dude's vomit from the ambulance...

Please be gentle with the banhammer...it's my first time.

Share this post


Link to post

I just tested this on Gameception (Boom based ios port) and there's one critical bug. Firing the chaingun crashes the game.

I'll look into it now to see if I can get it to work, but it's pretty awesome!

Edit: Changed frame 55's next frame to 49 and it works now.

Share this post


Link to post
BlackFish said:

I just tested this on Gameception (Boom based ios port) and there's one critical bug. Firing the chaingun crashes the game.

I'll look into it now to see if I can get it to work, but it's pretty awesome!

Edit: Changed frame 55's next frame to 49 and it works now.


Just checked it now on whacked4, and the next frame is set to 846, which makes it so that the chaingun fires twice instead of just once. When you tap the chaingun with your fix to frame 55, does it only fire one bullet? The vanilla chaingun fires two before returning to weaponready state.

Share this post


Link to post

don't suppose you mind me using this patch as a base for other things, provided proper credit is given?

Share this post


Link to post
Tango said:

don't suppose you mind me using this patch as a base for other things, provided proper credit is given?


Credit goes to Perk and everyone here in this thread and the fellas over at zdoom forums. If anything, I reconsolidated the content and wrote the dehacked from scratch, which isn't much.

With that being said, I'm sure that no one would mind if you used it as a base. Creativity breeds creativity.

BlackFish said:

They way I have it now it shoots one bullet.


That's what I figured. That means gameception doesn't fully support dehacked 3.0. You just found a bug, mate! Or at least a change in code from doom and prboom for the chaingun in that gameception port. Vanilla fires two bullets with a tap, and the dehacked I wrote was made to do the exact same. The frame change you implemented forces the chaingun to return to the weaponready state early, bypassing the 2nd firecg state and refire as well. I'm surprised it works at all when you hold the fire button if that's the case.

Share this post


Link to post

Just posting another update.

Improved the BOOM-compatible dehacked smooth weapon sprites pack again.

Tested with PrBoom, Eternity, and Odamex. For me, its a must-have anytime I use Odamex. I can never go back to the stock frames...

Fixes from last version:

-Plasma rifle no longer has a muzzle flash, eliminating the "stuck frame" bug that would occur, i.e. the random torch sprite that would appear.

-Plasma rifle room illumination improved.

-Chaingun's frame 56 is apparently hidden in whacked4. Reworked the chaingun states a bit to improve the muzzle flash and room illumination.

-Aligned vanilla BFG sprites up with the center of the screen. Did you guys know that the BFG's offsets for vanilla doom were way off base?

-Had to steal useless frames from John Romero's head (extra states for it were unnecessary).

-Also had to steal the rest of the health potion frames. Health potion now has a single static sprite. Not sure if anyone really cares though...

Demo playback is broken and was apparently so with the last version too. Does anyone have any idea why demo playback breaks even if the ticks for the weapons are the same?

Link:
http://www.best-ever.org/download?file=boom_enhanced1_13.wad

Enjoy!

Share this post


Link to post

See if this helps - it's in a format that my port reads, but it should be pretty clear. It is vanilla demo compatible. My chainsaw turns bloody after being used, until it is switched (cause you wipe it off when you put it away :), but, my guess is that your chaingun or pistol are the issue (I haven't looked at your update yet.) I had to mess with those a bit more to achieve demo compatibility.

/*
----------------------------------------------------
               Smoother Weapon Sprites

  Smoother weapon sprite animations by Per Kristian
 Risvik (2009-06-08). [kb] Converted to GAMEINFO.
 Some timings altered for demo compatibility, and
 some tweaks for preference
----------------------------------------------------
*/

states
{
  // [kb] Let's hack the chainsaw as well
  If Chainsaw_Original
  {
    // new chainsaw states for bloody chainsaw effect
    BloodySAW CBLD C 4 1 WeaponReady
    BloodySAWB * D 4 -1 *
    BloodySAWDOWN * C 1 0 Lower
    BloodySAWUP * C 1 0 Raise
    BloodySAW1 * A 4 1 Saw
    BloodySAW2 * B 4 1 *
    BloodySAW3 * B 0 -6 ReFire
  }

  // Player Punch
  If Punch_Original
  {
    PUNCH PUNG A 1 0 WeaponReady
    PUNCHDOWN * A 1 0 Lower
    PUNCHUP * A 1 0 Raise
    PUNCH1 PKFS L 1
    PUNCH2 * B 1
    PUNCH3 * C 1
    PUNCH4 * D 1
    PUNCH5 * E 2 1 Punch
    PUNCH6 * F 2
    PUNCH7 * G 2
    PUNCH8 * H 2
    PUNCH9 * I 2
    PUNCH10 * J 1
    PUNCH11 * K 1
    PUNCH12 * L 1
    PUNCH13 * A 5 -15 ReFire
  }


  // Pistol
  // [kb] Reversed back to normal left-handed goodness
  If Pistol_Original
  {
    PISTOL PKPR A 1 0 WeaponReady
    PISTOLDOWN * A 1 0 Lower
    PISTOLUP * A 1 0 Raise
    PISTOL1 * A 4
    PISTOL2 * B 2 1 FirePistol
    PISTOL3 * C 2
    PISTOL4 * E 2
    PISTOL5 * C 2
    PISTOL6 * B 2
    PISTOL7 * A 5 -9 ReFire

    // [kb] Also put back the flash sprite. 3 tics
    // is too long here, but it's shorter than the
    // original, and the flash is barely noticable
    // with only 2 tics.
    PISTOLFLASH PKPF B* 3 LIGHTDONE Light1
  }

  // Shotgun
  If Shotgun_Original
  {
    SGUN PKSG A 1 0 WeaponReady
    SGUNDOWN * A 1 0 Lower
    SGUNUP * A 1 0 Raise
    SGUN1 * A 3
    SGUN2 * A 6 1 FireShotgun
    SGUN3 * B 2
    SGUN4 * C 1
    SGUN5 * D 2
    SGUN6 * E 3
    SGUN7 * F 3
    SGUN8 * G 1
    SGUN9 * H 3
    SGUN10 * G 2
    SGUN11 * F 2
    SGUN12 * E 2
    SGUN13 * D 2
    SGUN14 * C 2
    SGUN15 * B 2
    SGUN16 * A 1
    SGUN17 * A 7 -19 ReFire
    SGUNFLASH1 SHTF A* 4 1 Light1
    SGUNFLASH2 * B* 3 LIGHTDONE Light2
  }

  // Double Shotgun
  If DoubleBarrel_Original
  {
    DSGUN PKS2 A 1 0 WeaponReady
    DSGUNDOWN * A 1 0 Lower
    DSGUNUP * A 1 0 Raise
    DSGUN1 * A 3
    DSGUN2 * A 7 1 FireShotgun2
    DSGUN3 * A 3
    DSGUN4 * B 3
    DSGUN5 * C 1
    DSGUN6 * C 1 1 CheckReload
    DSGUN7 * D 3
    DSGUN8 * E 3
    DSGUN9 * F 3 1 OpenShotgun2
    DSGUN10 * G 2
    DSGUN11 * H 2
    DSGUN12 * I 2
    DSGUN13 * J 2
    DSGUN14 * K 3
    DSGUN15 * L 2 1 LoadShotgun2
    DSGUN16 * M 3
    DSGUN17 * N 2
    DSGUN18 * O 2
    DSGUN19 * P 2
    DSGUN20 * Q 2
    DSGUN21 * R 3 1 CloseShotgun2
    DSGUN22 * S 3
    DSGUN23 * A 5 DSGUN ReFire
    DSGUNFLASH1 SHT2 J* 3 1 Light2
    DSGUNFLASH2 * I* 2 LIGHTDONE Light1
  }

  // Chaingun
  If Chaingun_Original
  {
    CHAIN PKCG A 1 0 WeaponReady
    CHAINDOWN * A 1 0 Lower
    CHAINUP * A 1 0 Raise
  
    CHAIN1 * A 1 1 FireCGun
    CHAIN2 * B 1
    CHAIN3 * C 1
    CHAIN4 * D 1
    CHAIN5 * A 1 1 FireCGun
    CHAIN6 * B 1
    CHAIN7 * C 1
    CHAIN8 * D 1
    CHAIN9 * A 0 1 ReFire

    // [kb] spin down, converted to single tics for
    // demo compatibility
    CHAIN10 * A 1 1 WeaponReady
    CHAIN11 * B 1 1 WeaponReady
    CHAIN12 * C 1 1 WeaponReady
    CHAIN13 * C 1 1 WeaponReady
    CHAIN14 * D 1 1 WeaponReady
    CHAIN15 * D 1 CHAIN WeaponReady

    CHAINFLASH1 PKCF A* 2 1 Light1
    CHAINFLASH1A * B* 2 LIGHTDONE
    CHAINFLASH2 * C* 2 1 Light2
    CHAINFLASH2A * D* 2 LIGHTDONE
  }

  // Rocket Launcher
  If Launcher_Original
  {
    MISSILE PKRL A 1 0 WeaponReady
    MISSILEDOWN * A 1 0 Lower
    MISSILEUP * A 1 0 Raise
    MISSILE1 * A 2 1 GunFlashSprite
    MISSILE2 * A 4 1 GunFlashPsprite
    MISSILE3 * B 2
    MISSILE4 * D 3 1 FireMissile
    MISSILE5 * C 3
    MISSILE6 * B 2
    MISSILE7 * E 2
    MISSILE8 * F 1
    MISSILE9 * G 1
    MISSILE10 * H 0 -12 ReFire
    MISSILEFLASH1 PKRF A* 3 1 Light1
    MISSILEFLASH2 * B* 2
    MISSILEFLASH3 * C* 2 1 Light2
    MISSILEFLASH4 * D* 3
    MISSILEFLASH5 * E* 3 LIGHTDONE *

    ROCKETPUFF1 RSMO A 4
    ROCKETPUFF2 * B 4
    ROCKETPUFF3 * C 4
    ROCKETPUFF4 * D 4
    ROCKETPUFF5 * E 4 *
  }

  // Plasma Gun
  If Plasmagun_Original
  {
    PLASMA PKPL A 1 0 WeaponReady
    PLASMADOWN * A 1 0 Lower
    PLASMAUP * A 1 0 Raise
    PLASMA1 * A 3 1 FirePlasma
    PLASMA2 * B 1 1 ReFire
    PLASMA3 * D 1
    PLASMA4 * E 1
    PLASMA5 * F 1
    PLASMA6 * G 3
    PLASMA7 * F 3
    PLASMA8 * E 2
    PLASMA9 * D 2
    PLASMA10 * C 2
    PLASMA11 * B 2
    PLASMA12 * A 2 PLASMA

    PLASMAFLASH1 PLSF A* 1 1 Light1
    PLASMAFLASH1B * D* 1
    PLASMAFLASH1C * B* 1
    PLASMAFLASH1D * E* 1 LIGHTDONE
    PLASMAFLASH2 * C* 1 1 Light1
    PLASMAFLASH2B * E* 1
    PLASMAFLASH2C * F* 1
    PLASMAFLASH2D * D* 1 LIGHTDONE
  }
}

Share this post


Link to post

Regarding the dehacked version,

I wanted to try this out with Doom Retro port which is has dehached and bex support.

Two things. The game crashes when I try to fire the chaingun. And there is an extra fullbrite frame when firing the pistol after it has already fired. The glove gets brighter a second time after it had already gone dark again when the firing finishes.

Also, is there any chance of getting a black gloved version? Or would it be easy enough to just swap out the sprite frames for the ones here myself? http://forum.zdoom.org/viewtopic.php?f=19&t=32628

Share this post


Link to post

@Chronohunter45

Did you have any luck getting your DEHACKED code in sync, or did you get any use of the frames I posted?

Share this post


Link to post
Brewtal_Legend said:

Regarding the dehacked version,

I wanted to try this out with Doom Retro port which is has dehached and bex support.

Two things. The game crashes when I try to fire the chaingun. And there is an extra fullbrite frame when firing the pistol after it has already fired. The glove gets brighter a second time after it had already gone dark again when the firing finishes.

Also, is there any chance of getting a black gloved version? Or would it be easy enough to just swap out the sprite frames for the ones here myself? http://forum.zdoom.org/viewtopic.php?f=19&t=32628


I noticed the light1 tag on the fist frame last week too when I started going through frames looking for the demo playback failure causes. I fixed that one for the next release.

Black gloved can be done too. I will throw that together as well. On my 24 on the rescue today, so its a hit or miss if I get it all done today or not.

@kb1

Still working on it. Using vanilla frame tic counts and your post as reference. Here's the kicker: The amount of tics total are exact to vanilla, so its not an issue of too many or too few tics for frame duration. Perhaps the engine just gets uppity about certain frames? Was that why the chaingun needed those extra 2 frames for the spin down?

EDIT:

Here's the latest. I'll get started on a black gloves edition as well. DEH embedded in WAD.
http://www.best-ever.org/download?file=boom_enhanced1_14.wad

Share this post


Link to post
Chronohunter45 said:

Still working on it. Using vanilla frame tic counts and your post as reference. Here's the kicker: The amount of tics total are exact to vanilla, so its not an issue of too many or too few tics for frame duration. Perhaps the engine just gets uppity about certain frames?

It's not just the number of tics, but, rather, when (and if) the action functions get called - those must be identical. For example, if vanilla fires 2 bullets each time, so must the dehacked code, and at the same time. You can do practically anything on the visual side, just as long as the action functions happen on the proper tic.

Chronohunter45 said:

Was that why the chaingun needed those extra 2 frames for the spin down?

I can't remember - I think I added the spindown frames 'cause it looked good and unnatural without them.

The format I posted has a lot of shortcuts in it - I hope it's legible. I do know for sure that it will maintain vanilla sync. Good luck, and good job!

Share this post


Link to post

I understand how dehacked works - the issue that I am having does not seem to relate to tics between pointers. I'll post the current definitions and vanilla for comparison side by side. I will post this little by little, busy at class and work, so expect edits over next few hours.

STATE:::SPRITE:::NEXTSTATE:::DURATION:::ACTION:::::::::POINTER_TIC
====FIST====
SMOOTH
2 PUNGA 2 1 WEAPONREADY
5 PUNGJ 949 2 1-2
949 PUNGC 6 1 3
6 PUNGD 7 1 4
7 PUNGE 8 2 PUNCH 5-6
8 PUNGG 9 3 7-9
9 PUNGH 110 3 10-12
110 PUNGI 130 3 13-15
130 PUNGB 131 2 16-17
131 PUNGA 2 5 REFIRE 18-22

VANILLA
2 PUNGA 2 1 WEAPONREADY
5 PUNGB 6 4 1-4
6 PUNGC 7 4 PUNCH 5-8
7 PUNGD 8 5 9-13
8 PUNGC 9 4 14-17
9 PUNGB 2 5 REFIRE 18-22

====PISTOL====
SMOOTH
10 PISGA 10 1 WEAPONREADY
13 PISGA 14 4 1-4
14 PISGB 15 2 FIREPISTOL 5-6
15 PISGC 16 2 7-8
16 PISGE 17 2 9-10
17 PISGD 139 2 11-12
139 PISGB 140 2 13-14
140 PISGA 10 5 REFIRE 15-19

VANILLA
10 PISGA 10 1 WEAPONREADY
13 PISGA 14 4 1-4
14 PISGB 15 6 FIREPISTOL 5-10
15 PISGC 16 4 11-14
16 PISGB 10 5 REFIRE 15-19

I'll post more as I get more free time.

Share this post


Link to post

At this stage, I'd be inclined to revert back to original behavior. I'd create a dehacked file for all weapons, set just like vanilla, and prove to myself that that worked and stayed in sync. Then, I'd add modified weapons work, one at a time, testing at each stage, until I knew exactly what worked and what didn't. That would isolate and identify the problems.

Sure, it's a lot of work, but, maybe in the end, less work than debugging the whole thing. I guess you could even test individual frame changes that way too, once you know the trouble area.

I wish I had time to do some testing of my own.

Share this post


Link to post
kb1 said:

At this stage, I'd be inclined to revert back to original behavior. I'd create a dehacked file for all weapons, set just like vanilla, and prove to myself that that worked and stayed in sync. Then, I'd add modified weapons work, one at a time, testing at each stage, until I knew exactly what worked and what didn't. That would isolate and identify the problems.

Sure, it's a lot of work, but, maybe in the end, less work than debugging the whole thing. I guess you could even test individual frame changes that way too, once you know the trouble area.

I wish I had time to do some testing of my own.


Ha, way ahead of you - started doing exactly that Monday night.

Share this post


Link to post
Chronohunter45 said:

Ha, way ahead of you - started doing exactly that Monday night.

What did you find out? Can you tell which one's cause sync issues?

Share this post


Link to post

It's cool that someone is trying their hand, putting their spin on this. It's always good to gain experience and have this in your portfolio.

I can't tell how this is essentially different from Beautiful Doom, though... Aside from some different sound effects.

Share this post


Link to post
kb1 said:

What did you find out? Can you tell which one's cause sync issues?


Sorry that it took so long to get back to you, being a new father really eats up your free time...

So I think I actually made a discovery that will be useful to other vanilla compatible dehacked modders in the future. Remember how we thought that it had to do with the weapon sprite tic timings? It has essentially nothing to do with them as long as the proper tic count is present. In reality, it was the states that I set to have a frame set to be displayed infinitely that were the issue.

Example:
STATE:::NAME:::SPRITE:::FRAME:::ALWAYSLIT:::NEXT:::TIC:::ACTION
Green Armor
802 ARM1A 55 0 --------- 0 -1 ------

The above causes desync, because for some reason the engine includes inanimate things into the tic counter, even though they have no effect on gameplay other than whether they have been picked up or not. To fix all of my issues, I simple did this:

STATE:::NAME:::SPRITE:::FRAME:::ALWAYSLIT:::NEXT:::TIC:::ACTION
Green Armor
802 ARM1A 55 0 --------- 802 6 ------

I added the tics back in, and set the next frame to itself. So the lone sprite still loops (keeping the extra state free to use for another thing), and maintains demo compatibility. Perhaps Maes or Linguica can provide more insight on why this occurs.

RoboticRocketeer said:

It's cool that someone is trying their hand, putting their spin on this. It's always good to gain experience and have this in your portfolio.

I can't tell how this is essentially different from Beautiful Doom, though... Aside from some different sound effects.


Perk's work was made to be boom-compatible. Beautiful doom requires zdoom decorate extensions. This is for non-zdoom ports, and for those who want vanilla compatibility but with sprite enhancements. Welcome to doomworld btw! Always nice to see a new face.

Share this post


Link to post
Chronohunter45 said:

I added the tics back in, and set the next frame to itself. So the lone sprite still loops (keeping the extra state free to use for another thing), and maintains demo compatibility. Perhaps Maes or Linguica can provide more insight on why this occurs.

Good question. Looking at P_SetMobjState() and P_MobjThinker() I don't see offhand any calls to P_Random() or anything else that ought to cause demo desync. It must be something more subtle.

Share this post


Link to post
Linguica said:

Good question. Looking at P_SetMobjState() and P_MobjThinker() I don't see anything offhand any calls to P_Random() or anything else that ought to cause demo desync. It must be something more subtle.


Is there a global timer that proceeds at the level start and keeps count of all the tics that includes inanimate things?

Share this post


Link to post

I think I might have found it. In P_SpawnMapThing():

if (mobj->tics > 0)
	mobj->tics = 1 + (P_Random () % mobj->tics);
So when you changed an item to have -1 tics until the next frame, a call to P_Random() gets skipped at the very beginning, and the PRNG is out of step forever.

(This snippet is why all animated items / sleeping monsters aren't in perfect lockstep, BTW)

Share this post


Link to post
Linguica said:

I think I might have found it. In P_SpawnMapThing():

if (mobj->tics > 0)
	mobj->tics = 1 + (P_Random () % mobj->tics);
So when you changed an item to have -1 tics until the next frame, a call to P_Random() gets skipped at the very beginning, and the PRNG is out of step forever.

(This snippet is why all animated items / sleeping monsters aren't in perfect lockstep, BTW)


That's really funny, and it wasn't even a gameplay effecting state that was changed.

Cool find.

Share this post


Link to post

After the troubleshooting noted in the previous posts, here's the latest version again with fixes that make it boom compatible and no longer demo-breaking. Checking compatibility for DoomRetro now. Will edit later.

http://www.best-ever.org/download?file=boom_enhanced1_16a.wad

EDIT:
Yep, works fine with DoomRetro as well. Made a quick fix for the wrong frame displayed during the refire state of the pistol. Should be all set now.

Share this post


Link to post
Linguica said:

I think I might have found it. In P_SpawnMapThing():

if (mobj->tics > 0)
	mobj->tics = 1 + (P_Random () % mobj->tics);
So when you changed an item to have -1 tics until the next frame, a call to P_Random() gets skipped at the very beginning, and the PRNG is out of step forever.

(This snippet is why all animated items / sleeping monsters aren't in perfect lockstep, BTW)

Only if the > 0 frame is the spawn frame, though. EDIT: ...which it is for the green armor. Yes, absolutely. Good find. I failed to comprehend that this patch uses frames from non weapons.

Forgot to mention:
Great job Chronohunter45!

Share this post


Link to post

funny thing I was just looking for a dehacked version of perk's smoother weapon mod for using it with prboom+. and I remembered that the version which was available caused demos to desync so I was already prepared to debug it myself and here you are. great job Chronohunter45, you should upload it onto the idgames archive!

ps: also works great with revenant100's sprite fixing project, but you have to make sure to load the smooth weapon mod afterwards.

Share this post


Link to post

Hey fellas,

Just another small update.

I got tired of seeing the monsters "dancing" in their idle animation, and realized that I could knock out two birds with one stone by removing the 2nd state of the idle animation (making it a single, static frame) and using said remaining frames to work on a demo-compatible smooth BFG.

All monsters now have a single idle frame (minus the lost soul), and I gathered up some frames from the smooth doom project over at zdoom's forums for the BFG (credit for sprites goes to them) and used them for a test trial smooth bfg animation.

See if you like it. I don't want to submit this to the archives until I've squeezed as much out of the bex format states as I can.

http://www.best-ever.org/download?file=boom_enhanced1_17.wad

Sp00kyFox said:

funny thing I was just looking for a dehacked version of perk's smoother weapon mod for using it with prboom+. and I remembered that the version which was available caused demos to desync so I was already prepared to debug it myself and here you are. great job Chronohunter45, you should upload it onto the idgames archive!

ps: also works great with revenant100's sprite fixing project, but you have to make sure to load the smooth weapon mod afterwards.


Yep, that was one of the goals was to make it work with the sprite fixing project and Doom Retro. That port has so many features that should be standard by now that I could never neglect it's compatibility.

Share this post


Link to post

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
×