Source Code

;; wanted-hogger.clar - Quest logic for Wanted: Hogger

(define-constant err-unauthorized (err u401))
(define-constant err-hogger-defeated (err u402))
(define-constant err-insufficient-energy (err u403))
(define-constant err-invalid-edk (err u404))

(define-constant contract (as-contract tx-sender))

(define-data-var blocks-per-epoch uint u14) ;; Roughly daily epochs (assuming 10-minute blocks)
(define-data-var last-reset-block uint u0)
(define-data-var current-epoch uint u0)

(define-map player-damage principal uint)
(define-data-var player-list (list 200 principal) (list))

;; Whitelisted Contract Addresses
(define-map whitelisted-edks principal bool)

(define-trait edk-trait
	(
		(tap (uint) (response (tuple (type (string-ascii 256)) (land-id uint) (land-amount uint) (energy uint)) uint))
	)
)

;; --- Authorization check
(define-private (is-dao-or-extension)
    (or (is-eq tx-sender 'SP2D5BGGJ956A635JG7CJQ59FTRFRB0893514EZPJ.dungeon-master) 
        (contract-call? 'SP2D5BGGJ956A635JG7CJQ59FTRFRB0893514EZPJ.dungeon-master is-extension contract-caller))
)

(define-read-only (is-authorized)
    (ok (asserts! (is-dao-or-extension) err-unauthorized))
)

;; Whitelist Functions
(define-public (set-whitelisted-edk (edk principal) (whitelisted bool))
    (begin
        (try! (is-authorized))
        (ok (map-set whitelisted-edks edk whitelisted))
    )
)

(define-read-only (is-whitelisted-edk (edk principal))
    (default-to false (map-get? whitelisted-edks edk))
)

;; Attack Hogger function
(define-public (tap (land-id uint) (edk-contract <edk-trait>))
    (let
        (
            (tapped-out (unwrap-panic (contract-call? edk-contract tap land-id)))
            (energy (get energy tapped-out))
            (player tx-sender)
            (is-reset (try-reset))
        )
        (asserts! (is-whitelisted-edk (contract-of edk-contract)) err-invalid-edk)
        (asserts! (not (contract-call? .hogger-v0 is-defeated)) err-hogger-defeated)

        (print {event: "attack-hogger", player: player, energy-spent: energy, epoch-reset: is-reset})
        
        ;; Tap energy and calculate damage
        (let 
            (
                (damage (contract-call? .combat-v1 calculate-damage energy player))
                (new-hogger-health (unwrap-panic (contract-call? .hogger-v0 take-damage damage)))
            )
            
            ;; Update player's damage and add to player list if not already present
            (try! (record-player-damage player damage))
            
            ;; Check if Hogger is defeated
            (and (is-eq new-hogger-health u0) (unwrap-panic (distribute-rewards)))

            (print {event: "attack-result", player: player, damage-dealt: damage, new-hogger-health: new-hogger-health, hogger-defeated: (is-eq new-hogger-health u0)})
            (ok {
              land-id: land-id,
              energy-spent: energy,
              damage: damage,
              hogger-health: new-hogger-health
            })
        )
    )
)

;; Helper function to check if an epoch has passed
(define-private (epoch-passed)
    (> (- block-height (var-get last-reset-block)) (var-get blocks-per-epoch))
)

;; Reset the epoch if it has passed
(define-private (try-reset)
    (if (epoch-passed)
        (begin
            (var-set current-epoch (+ (var-get current-epoch) u1))
            (var-set last-reset-block block-height)
            (unwrap-panic (contract-call? .hogger-v0 reset-for-new-epoch))
            (clear-player-data)
            true
        )
        false
    )
)

(define-private (remove-player-damage (player principal))
    (map-delete player-damage player)
)

(define-public (try-reset-epoch)
    (let ((reset-result (try-reset)))
        (print {event: "result-epoch", reset-occurred: reset-result, new-epoch: (var-get current-epoch)})
        (ok reset-result)
    )
)

;; Helper function to get player damage
(define-private (get-player-damage (player principal))
    (default-to u0 (map-get? player-damage player))
)

;; Distribute rewards when Hogger is defeated
(define-private (distribute-rewards)
    (begin
        (map distribute-player-reward (var-get player-list))
        (ok true)
    )
)

(define-private (distribute-player-reward (player principal))
    (let
        (
            (players (var-get player-list))
            (total-damage (fold + (map get-player-damage players) u0))
            (total-experience (pow u10 u9)) ;; 1000 experience in total
            (total-cha (pow u10 u9)) ;; 1000 sCHA in total
            (damage (get-player-damage player))
            (experience-share (/ (* total-experience damage) total-damage))
            (cha-share (/ (* total-cha damage) total-damage))
        )
        (begin
            (print {event: "distributing-rewards", total-players: (len players), total-damage: total-damage})
            (try! (contract-call? .experience mint experience-share player))
            (try! (contract-call? 'SP2D5BGGJ956A635JG7CJQ59FTRFRB0893514EZPJ.dme000-governance-token dmg-mint cha-share player))
            (print {event: "rewards-distributed", player: player, experience: experience-share, cha-amount: cha-share})
            (ok true)
        )
    )
)

;; Utility function to add a player to the list and record their damage
(define-private (record-player-damage (player principal) (damage uint))
    (begin
        (map-set player-damage player (+ (get-player-damage player) damage))
        (var-set player-list (unwrap! (as-max-len? (append (var-get player-list) player) u200) (err u404)))
        (ok true)
    )
)

;; Clear player data after rewards distribution
(define-private (clear-player-data)
    (begin
        (map delete-player-damage (var-get player-list))
        (var-set player-list (list))
    )
)

(define-private (delete-player-damage (player principal))
    (map-delete player-damage player)
)

;; Getters
(define-read-only (get-hogger-health)
    (contract-call? .hogger-v0 get-health)
)

(define-read-only (get-current-epoch)
    (var-get current-epoch)
)

;; Utility functions

(define-read-only (get-blocks-until-next-epoch)
    (let
        (
            (blocks-since-last-reset (- block-height (var-get last-reset-block)))
            (blocks-in-current-epoch (mod blocks-since-last-reset (var-get blocks-per-epoch)))
        )
        (- (var-get blocks-per-epoch) blocks-in-current-epoch)
    )
)

(define-read-only (get-epoch-progress)
    (let
        (
            (blocks-since-last-reset (- block-height (var-get last-reset-block)))
            (blocks-in-current-epoch (mod blocks-since-last-reset (var-get blocks-per-epoch)))
        )
        (/ (* blocks-in-current-epoch u100) (var-get blocks-per-epoch))
    )
)

Functions (19)

FunctionAccessArgs
is-dao-or-extensionprivate
is-authorizedread-only
set-whitelisted-edkpublicedk: principal, whitelisted: bool
is-whitelisted-edkread-onlyedk: principal
tappublicland-id: uint, edk-contract: <edk-trait>
epoch-passedprivate
try-resetprivate
remove-player-damageprivateplayer: principal
try-reset-epochpublic
get-player-damageprivateplayer: principal
distribute-rewardsprivate
distribute-player-rewardprivateplayer: principal
record-player-damageprivateplayer: principal, damage: uint
clear-player-dataprivate
delete-player-damageprivateplayer: principal
get-hogger-healthread-only
get-current-epochread-only
get-blocks-until-next-epochread-only
get-epoch-progressread-only