Skip to content

Commit 6dd6d78

Browse files
committed
code fixes
1 parent c0381ab commit 6dd6d78

3 files changed

Lines changed: 156 additions & 11 deletions

File tree

Source/missiles.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,11 @@ bool Plr2PlrMHit(const Player &player, int p, int mindam, int maxdam, int dist,
289289

290290
*blocked = false;
291291

292-
if (target.isOnArenaLevel() && target._pmode == PM_WALK_SIDEWAYS)
293-
return false;
294-
295292
if (target._pInvincible) {
296293
return false;
297294
}
298295

299-
if (mtype == MissileID::HolyBolt) {
296+
if (mtype == MissileID::HolyBolt && !player.isOnArenaLevel()) {
300297
return false;
301298
}
302299

@@ -1112,6 +1109,22 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil
11121109

11131110
if (player._pHitPoints >> 6 > 0) {
11141111
StartPlrHit(player, dam, false);
1112+
1113+
// Arena Holy Bolt knockback: enable knockback from Holy Bolt against players in arena
1114+
if (player.isOnArenaLevel() && mtype == MissileID::HolyBolt && monster != nullptr) {
1115+
if (player._pmode != PM_GOTHIT)
1116+
StartPlrHit(player, 0, true);
1117+
1118+
Direction knockbackDir = GetDirection(monster->position.tile, player.position.tile);
1119+
Point newPosition = player.position.tile + knockbackDir;
1120+
if (PosOkPlayer(player, newPosition)) {
1121+
player.position.tile = newPosition;
1122+
FixPlayerLocation(player, player._pdir);
1123+
FixPlrWalkTags(player);
1124+
dPlayer[newPosition.x][newPosition.y] = player.getId() + 1;
1125+
SetPlayerOld(player);
1126+
}
1127+
}
11151128
}
11161129

11171130
return true;

Source/player.cpp

Lines changed: 135 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ void StartAttack(Player &player, Direction d, bool includesFirstFrame)
204204
// Combining Fastest Attack with any other attack speed modifier skips over the fourth frame, reducing the effectiveness of Fastest Attack.
205205
// Faster Attack makes up for this by also skipping the sixth frame so this case only applies when using Quick or Fast Attack modifiers.
206206
skippedAnimationFrames = 3;
207-
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastestAttack)) {
207+
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastestAttack)
208+
|| player.isOnArenaLevel() && player._pClass == HeroClass::Warrior) {
208209
skippedAnimationFrames = 4;
209210
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FasterAttack)) {
210211
skippedAnimationFrames = 3;
@@ -219,7 +220,8 @@ void StartAttack(Player &player, Direction d, bool includesFirstFrame)
219220
skippedAnimationFrames = 2;
220221
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastAttack)) {
221222
skippedAnimationFrames = 1;
222-
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastestAttack)) {
223+
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastestAttack)
224+
|| player.isOnArenaLevel() && player._pClass == HeroClass::Warrior) {
223225
// Fastest Attack is skipped if Fast or Faster Attack is also specified, because both skip the frame that triggers Fastest Attack skipping.
224226
skippedAnimationFrames = 2;
225227
}
@@ -246,7 +248,8 @@ void StartRangeAttack(Player &player, Direction d, WorldTileCoord cx, WorldTileC
246248
if (includesFirstFrame && HasAnyOf(player._pIFlags, ItemSpecialEffect::QuickAttack | ItemSpecialEffect::FastAttack)) {
247249
skippedAnimationFrames += 1;
248250
}
249-
if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastAttack)) {
251+
if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastAttack)
252+
|| player.isOnArenaLevel() && player._pClass == HeroClass::Rogue) {
250253
skippedAnimationFrames += 1;
251254
}
252255
}
@@ -301,10 +304,18 @@ void StartSpell(Player &player, Direction d, WorldTileCoord cx, WorldTileCoord c
301304
if (!isValid)
302305
return;
303306

307+
int8_t skippedAnimationFrames = 0;
308+
// Arena Fast Cast
309+
if (player.isOnArenaLevel() && (player._pClass == HeroClass::Rogue)) {
310+
skippedAnimationFrames = 4;
311+
} else if (player.isOnArenaLevel() && player._pClass == HeroClass::Warrior) {
312+
skippedAnimationFrames = 6;
313+
}
314+
304315
auto animationFlags = AnimationDistributionFlags::ProcessAnimationPending;
305316
if (player._pmode == PM_SPELL)
306317
animationFlags = static_cast<AnimationDistributionFlags>(animationFlags | AnimationDistributionFlags::RepeatedAction);
307-
NewPlrAnim(player, GetPlayerGraphicForSpell(player.queuedSpell.spellId), d, animationFlags, 0, player._pSFNum);
318+
NewPlrAnim(player, GetPlayerGraphicForSpell(player.queuedSpell.spellId), d, animationFlags, skippedAnimationFrames, player._pSFNum);
308319

309320
PlaySfxLoc(GetSpellData(player.queuedSpell.spellId).sSFX, player.position.tile);
310321

@@ -401,6 +412,8 @@ void InitLevelChange(Player &player)
401412
RemovePlrMissiles(player);
402413
player.pManaShield = false;
403414
player.wReflections = 0;
415+
player.arenaLastStunTime = 0;
416+
player.arenaStunHitCount = 0;
404417
if (&player != MyPlayer) {
405418
// share info about your manashield when another player joins the level
406419
if (myPlayer.pManaShield)
@@ -763,6 +776,11 @@ bool PlrHitPlr(Player &attacker, Player &target)
763776
int blkper = target.GetBlockChance() - (attacker._pLevel * 2);
764777
blkper = clamp(blkper, 0, 100);
765778

779+
// Arena balance: cap block chance at 75%
780+
if (target.isOnArenaLevel() && blkper > 75) {
781+
blkper = 75;
782+
}
783+
766784
if (hit >= hper) {
767785
return false;
768786
}
@@ -797,11 +815,70 @@ bool PlrHitPlr(Player &attacker, Player &target)
797815
}
798816
RedrawComponent(PanelDrawComponent::Health);
799817
}
818+
819+
// Arena steal effects: enable all mana/life steal against players in arena
820+
if (target.isOnArenaLevel()) {
821+
int stealAmount = 0;
822+
823+
// Mana steal effects
824+
if (HasAnyOf(attacker._pIFlags, ItemSpecialEffect::StealMana3 | ItemSpecialEffect::StealMana5) && HasNoneOf(attacker._pIFlags, ItemSpecialEffect::NoMana)) {
825+
if (HasAnyOf(attacker._pIFlags, ItemSpecialEffect::StealMana3)) {
826+
stealAmount = 3 * dam / 100;
827+
}
828+
if (HasAnyOf(attacker._pIFlags, ItemSpecialEffect::StealMana5)) {
829+
stealAmount = 5 * dam / 100;
830+
}
831+
attacker._pMana += stealAmount;
832+
if (attacker._pMana > attacker._pMaxMana) {
833+
attacker._pMana = attacker._pMaxMana;
834+
}
835+
attacker._pManaBase += stealAmount;
836+
if (attacker._pManaBase > attacker._pMaxManaBase) {
837+
attacker._pManaBase = attacker._pMaxManaBase;
838+
}
839+
RedrawComponent(PanelDrawComponent::Mana);
840+
}
841+
842+
// Life steal effects
843+
if (HasAnyOf(attacker._pIFlags, ItemSpecialEffect::StealLife3 | ItemSpecialEffect::StealLife5)) {
844+
if (HasAnyOf(attacker._pIFlags, ItemSpecialEffect::StealLife3)) {
845+
stealAmount = 3 * dam / 100;
846+
}
847+
if (HasAnyOf(attacker._pIFlags, ItemSpecialEffect::StealLife5)) {
848+
stealAmount = 5 * dam / 100;
849+
}
850+
attacker._pHitPoints += stealAmount;
851+
if (attacker._pHitPoints > attacker._pMaxHP) {
852+
attacker._pHitPoints = attacker._pMaxHP;
853+
}
854+
attacker._pHPBase += stealAmount;
855+
if (attacker._pHPBase > attacker._pMaxHPBase) {
856+
attacker._pHPBase = attacker._pMaxHPBase;
857+
}
858+
RedrawComponent(PanelDrawComponent::Health);
859+
}
860+
}
800861
if (&attacker == MyPlayer) {
801862
NetSendCmdDamage(true, target.getId(), skdam, DamageType::Physical);
802863
}
803864
StartPlrHit(target, skdam, false);
804865

866+
// Arena knockback: enable knockback against players in arena
867+
if (target.isOnArenaLevel() && HasAnyOf(attacker._pIFlags, ItemSpecialEffect::Knockback)) {
868+
if (target._pmode != PM_GOTHIT)
869+
StartPlrHit(target, 0, true);
870+
871+
Direction knockbackDir = GetDirection(attacker.position.tile, target.position.tile);
872+
Point newPosition = target.position.tile + knockbackDir;
873+
if (PosOkPlayer(target, newPosition)) {
874+
target.position.tile = newPosition;
875+
FixPlayerLocation(target, target._pdir);
876+
FixPlrWalkTags(target);
877+
dPlayer[newPosition.x][newPosition.y] = target.getId() + 1;
878+
SetPlayerOld(target);
879+
}
880+
}
881+
805882
return true;
806883
}
807884

@@ -2376,6 +2453,8 @@ void CreatePlayer(Player &player, HeroClass c)
23762453
player.pManaShield = false;
23772454
player.pDamAcFlags = ItemSpecialEffectHf::None;
23782455
player.wReflections = 0;
2456+
player.arenaLastStunTime = 0;
2457+
player.arenaStunHitCount = 0;
23792458

23802459
InitDungMsgs(player);
23812460
CreatePlrItems(player);
@@ -2510,6 +2589,8 @@ void InitPlayer(Player &player, bool firstTime)
25102589
player.queuedSpell.spellType = player._pRSplType;
25112590
player.pManaShield = false;
25122591
player.wReflections = 0;
2592+
player.arenaLastStunTime = 0;
2593+
player.arenaStunHitCount = 0;
25132594
}
25142595

25152596
if (player.isOnActiveLevel()) {
@@ -2632,7 +2713,8 @@ void StartPlrBlock(Player &player, Direction dir)
26322713
PlaySfxLoc(IS_ISWORD, player.position.tile);
26332714

26342715
int8_t skippedAnimationFrames = 0;
2635-
if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastBlock)) {
2716+
if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastBlock)
2717+
|| player.isOnArenaLevel()) {
26362718
skippedAnimationFrames = (player._pBFrames - 2); // ISPL_FASTBLOCK means we cancel the animation if frame 2 was shown
26372719
}
26382720

@@ -2656,6 +2738,32 @@ void FixPlrWalkTags(const Player &player)
26562738
}
26572739
}
26582740

2741+
bool CanPlayerBeStunnedInArena(Player &player)
2742+
{
2743+
if (!player.isOnArenaLevel())
2744+
return true; // Normal stun rules apply outside arena
2745+
2746+
const uint32_t currentTime = SDL_GetTicks();
2747+
const uint32_t stunCooldown = 1000; // 1 second cooldown
2748+
const uint8_t maxHitsPerCooldown = 1; // can only be stunned once per second
2749+
2750+
// Check if enough time has passed since last stun
2751+
if (currentTime - player.arenaLastStunTime >= stunCooldown) {
2752+
// Reset hit counter after cooldown period
2753+
player.arenaStunHitCount = 0;
2754+
}
2755+
2756+
// Check if player has been hit too many times recently
2757+
if (player.arenaStunHitCount >= maxHitsPerCooldown) {
2758+
return false; // Player is immune to stun
2759+
}
2760+
2761+
// Allow stun and increment hit counter
2762+
player.arenaStunHitCount++;
2763+
player.arenaLastStunTime = currentTime;
2764+
return true;
2765+
}
2766+
26592767
void StartPlrHit(Player &player, int dam, bool forcehit)
26602768
{
26612769
if (player._pInvincible && player._pHitPoints == 0 && &player == MyPlayer) {
@@ -2674,10 +2782,16 @@ void StartPlrHit(Player &player, int dam, bool forcehit)
26742782
return;
26752783
}
26762784

2785+
// Arena stun resistance - check if player can be stunned
2786+
if (!CanPlayerBeStunnedInArena(player)) {
2787+
return; // Player is immune to stun in arena
2788+
}
2789+
26772790
Direction pd = player._pdir;
26782791

26792792
int8_t skippedAnimationFrames = 0;
2680-
if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastestHitRecovery)) {
2793+
if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FastestHitRecovery)
2794+
|| player.isOnArenaLevel()) {
26812795
skippedAnimationFrames = 3;
26822796
} else if (HasAnyOf(player._pIFlags, ItemSpecialEffect::FasterHitRecovery)) {
26832797
skippedAnimationFrames = 2;
@@ -2690,7 +2804,21 @@ void StartPlrHit(Player &player, int dam, bool forcehit)
26902804
NewPlrAnim(player, player_graphic::Hit, pd, AnimationDistributionFlags::None, skippedAnimationFrames);
26912805

26922806
player._pmode = PM_GOTHIT;
2693-
FixPlayerLocation(player, pd);
2807+
2808+
// Fix southward walking escape bug: use position.old to return to the original tile
2809+
if (player.isWalking()) {
2810+
player.position.tile = player.position.old;
2811+
player.position.future = player.position.old;
2812+
if (&player == MyPlayer) {
2813+
ViewPosition = player.position.tile;
2814+
}
2815+
ChangeLightXY(player.lightId, player.position.tile);
2816+
ChangeVisionXY(player.getId(), player.position.tile);
2817+
player._pdir = pd;
2818+
} else {
2819+
FixPlayerLocation(player, pd);
2820+
}
2821+
26942822
FixPlrWalkTags(player);
26952823
dPlayer[player.position.tile.x][player.position.tile.y] = player.getId() + 1;
26962824
SetPlayerOld(player);

Source/player.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@ struct Player {
378378
uint16_t wReflections;
379379
ItemSpecialEffectHf pDamAcFlags;
380380

381+
// Arena stun resistance tracking
382+
uint32_t arenaLastStunTime;
383+
uint8_t arenaStunHitCount;
384+
381385
void CalcScrolls();
382386

383387
bool CanUseItem(const Item &item) const;

0 commit comments

Comments
 (0)