22pragma solidity 0.8.28 ;
33
44import {
5- BattleNad, BattleArea, StorageTracker, Inventory, BalanceTracker, Log, AbilityTracker, Ability
5+ BattleNad,
6+ BattleArea,
7+ StorageTracker,
8+ Inventory,
9+ BalanceTracker,
10+ Log,
11+ AbilityTracker,
12+ Ability,
13+ CharacterClass
614} from "./Types.sol " ;
715
816import { Balances } from "./Balances.sol " ;
@@ -152,6 +160,13 @@ abstract contract Handler is Balances {
152160
153161 // Flag for combat
154162 (monster, player) = _enterMutualCombatToTheDeath (monster, player);
163+ if (monster.isDead ()) {
164+ player = _exitCombat (player);
165+ player.stats.nextTargetIndex = 0 ;
166+ player.tracker.updateStats = true ;
167+ _storeArea (area, newDepth, newX, newY);
168+ return player;
169+ }
155170
156171 // Create tasks
157172 bool scheduledTask = false ;
@@ -161,17 +176,20 @@ abstract contract Handler is Balances {
161176 uint256 targetBlock = block .number + _cooldown (monster.stats) + COMBAT_COLD_START_DELAY_MONSTER;
162177 (monster, scheduledTask) = _createOrRescheduleCombatTask (monster, targetBlock);
163178 if (! scheduledTask) {
179+ monster.owner = _EMPTY_ADDRESS;
180+ monster.tracker.updateOwner = true ;
164181 emit Events.TaskNotScheduledInHandler (3 , monster.id, block .number , targetBlock);
165182 }
166183 } else {
167184 // If task is no longer active, start a new one
168185 (scheduledTask,) = _checkClearTasks (monster);
169- if (! scheduledTask) {
170- monster.owner = player.owner;
171- monster.tracker.updateOwner = true ;
172- uint256 targetBlock = block .number + _cooldown (monster.stats) + COMBAT_COLD_START_DELAY_MONSTER;
186+ if (! scheduledTask || ! _isValidAddress (monster.owner)) {
187+ uint256 targetBlock = block .number + _cooldown (monster.stats);
173188 (monster, scheduledTask) = _createOrRescheduleCombatTask (monster, targetBlock);
174- if (! scheduledTask) {
189+ if (scheduledTask) {
190+ monster.owner = player.owner;
191+ monster.tracker.updateOwner = true ;
192+ } else {
175193 emit Events.TaskNotScheduledInHandler (4 , monster.id, block .number , targetBlock);
176194 }
177195 }
@@ -281,6 +299,12 @@ abstract contract Handler is Balances {
281299 bytes32 defenderTargetID =
282300 areaCombatants[defender.stats.depth][defender.stats.x][defender.stats.y][defender.stats.nextTargetIndex];
283301 if (! _isValidID (defenderTargetID)) {
302+ uint256 defenderBitmap = uint256 (defender.stats.combatantBitMap);
303+ uint256 targetBit = 1 << uint256 (defender.stats.nextTargetIndex);
304+ if (defenderBitmap & targetBit != 0 ) {
305+ defenderBitmap &= ~ targetBit;
306+ defender.stats.combatantBitMap = uint64 (defenderBitmap);
307+ }
284308 defender.stats.nextTargetIndex = attacker.stats.index;
285309 defender.tracker.updateStats = true ;
286310 }
@@ -292,21 +316,20 @@ abstract contract Handler is Balances {
292316 // Create tasks
293317 // Only create for attacker and defendant if tasks don't already exist
294318 (bool scheduledTask ,) = _checkClearTasks (defender);
295- if (! scheduledTask) {
296- if (defender.isMonster ()) {
297- defender.owner = attacker.owner;
298- defender.tracker.updateOwner = true ;
299- }
300- (defender, scheduledTask) = _createOrRescheduleCombatTask (
301- defender, block .number + _cooldown (defender.stats) + COMBAT_COLD_START_DELAY_DEFENDER
302- );
303- if (! scheduledTask) {
304- emit Events.TaskNotScheduledInHandler (
305- 1 ,
306- defender.id,
307- block .number ,
308- block .number + _cooldown (defender.stats) + COMBAT_COLD_START_DELAY_DEFENDER
309- );
319+ if (defender.isMonster ()) {
320+ if (! scheduledTask || ! _isValidAddress (defender.owner)) {
321+ if (! scheduledTask) {
322+ (defender, scheduledTask) =
323+ _createOrRescheduleCombatTask (defender, block .number + _cooldown (defender.stats));
324+ if (scheduledTask) {
325+ defender.owner = attacker.owner;
326+ defender.tracker.updateOwner = true ;
327+ } else {
328+ emit Events.TaskNotScheduledInHandler (
329+ 1 , defender.id, block .number , block .number + _cooldown (defender.stats)
330+ );
331+ }
332+ }
310333 }
311334 }
312335
@@ -479,7 +502,9 @@ abstract contract Handler is Balances {
479502
480503 // CASE: No combatants remain
481504 if (! attacker.isInCombat ()) {
482- attacker = _checkClearAbility (attacker);
505+ if (! attacker.isMonster ()) {
506+ attacker = _checkClearAbility (attacker);
507+ }
483508
484509 reschedule = false ;
485510 nextExecutionBlock = 0 ;
@@ -505,14 +530,14 @@ abstract contract Handler is Balances {
505530 // Process attack
506531 (attacker, defender, log) = _attack (attacker, defender, log);
507532
508- // If it's a monster, update defender's owner to most recent attacker
533+ // If it's a monster, update defender's owner to n
509534 // Only do this if there was a funding issue with prev task
535+
510536 if (defender.isMonster () && ! attacker.isMonster ()) {
511- defender.owner = _loadOwner (defender.id);
512- if (defender.owner != attacker.owner) {
537+ if (defender.owner != attacker.owner && _isValidAddress (defender.owner)) {
513538 (bool hasActiveCombatTask ,) = _checkClearTasks (defender);
514539 if (! hasActiveCombatTask) {
515- defender.owner = attacker.owner ;
540+ defender.owner = _EMPTY_ADDRESS ;
516541 defender.tracker.updateOwner = true ;
517542 }
518543 }
@@ -718,6 +743,31 @@ abstract contract Handler is Balances {
718743 if (! attackerInCombat || ! defenderInCombat) {
719744 (attacker, defender) = _enterMutualCombatToTheDeath (attacker, defender);
720745 }
746+ if (defender.isMonster () && ! attacker.isMonster ()) {
747+ if (! _isTask ()) {
748+ (bool scheduledTask ,) = _checkClearTasks (defender);
749+ if (! scheduledTask || ! _isValidAddress (defender.owner)) {
750+ (defender, scheduledTask) =
751+ _createOrRescheduleCombatTask (defender, block .number + _cooldown (defender.stats));
752+ if (scheduledTask) {
753+ defender.owner = attacker.owner;
754+ defender.tracker.updateOwner = true ;
755+ } else {
756+ emit Events.TaskNotScheduledInHandler (
757+ 1 , defender.id, block .number , block .number + _cooldown (defender.stats)
758+ );
759+ }
760+ }
761+ } else {
762+ if (defender.owner != attacker.owner && _isValidAddress (defender.owner)) {
763+ (bool hasActiveCombatTask ,) = _checkClearTasks (defender);
764+ if (! hasActiveCombatTask) {
765+ defender.owner = _EMPTY_ADDRESS;
766+ defender.tracker.updateOwner = true ;
767+ }
768+ }
769+ }
770+ }
721771 } else {
722772 if (attacker.activeAbility.targetIndex != 0 && attacker.activeAbility.ability != Ability.Pray) {
723773 revert Errors.AbilityCantHaveTarget ();
@@ -794,6 +844,8 @@ abstract contract Handler is Balances {
794844 }
795845
796846 function _combatCheckLoop (BattleNad memory combatant , bool forceRemoveCombat ) internal returns (BattleNad memory ) {
847+ bool isBossEncounter = (combatant.stats.class != CharacterClass.Boss)
848+ && (_isBoss (combatant.stats.depth, combatant.stats.x, combatant.stats.y));
797849 combatant.tracker.updateStats = true ;
798850
799851 BattleArea memory area = _loadArea (combatant.stats.depth, combatant.stats.x, combatant.stats.y);
@@ -816,9 +868,18 @@ abstract contract Handler is Balances {
816868
817869 // Can't have an index of 0, start i at 1.
818870 uint256 targetIndex = uint256 (combatant.stats.nextTargetIndex);
819- if (targetIndex == uint256 (combatant.stats.index)) targetIndex = 0 ;
820- if (targetIndex == 0 ) {
821- targetIndex = uint256 (combatant.stats.index) == 1 ? 2 : 1 ;
871+ if (targetIndex < 2 ) {
872+ if (isBossEncounter) {
873+ targetIndex = 1 ; // uint8(RESERVED_BOSS_INDEX)
874+ } else {
875+ targetIndex = 2 ;
876+ }
877+ }
878+
879+ if (uint256 (combatant.stats.index) == targetIndex) {
880+ if (++ targetIndex > 64 ) {
881+ targetIndex = isBossEncounter ? 1 : 2 ;
882+ }
822883 }
823884
824885 while (gasleft () > 215_000 && combatantBitmap != 0 ) {
@@ -885,7 +946,7 @@ abstract contract Handler is Balances {
885946 // Increment loop
886947 unchecked {
887948 if (++ targetIndex > 64 ) {
888- targetIndex = 1 ;
949+ targetIndex = isBossEncounter ? 1 : 2 ;
889950 }
890951 }
891952 combatant.stats.nextTargetIndex = uint8 (targetIndex);
0 commit comments