diff --git a/src/Backend.elm b/src/Backend.elm
index 56d7f06..04af0a8 100644
--- a/src/Backend.elm
+++ b/src/Backend.elm
@@ -258,22 +258,14 @@ getPlayerData_ worldName world player =
             |> SeqDict.map
                 (\quest ticksGivenPerPlayer ->
                     let
-                        engagements : List Quest.Engagement
-                        engagements =
+                        engagedPlayersCount : Int
+                        engagedPlayersCount =
                             players
-                                |> List.map (\player_ -> SPlayer.questEngagement player_ quest)
-
-                        playersActive : Int
-                        playersActive =
-                            engagements
-                                |> List.filter (\e -> e /= Quest.NotProgressing)
-                                |> List.length
+                                |> List.count (\player_ -> SeqSet.member quest player_.questsActive)
 
                         ticksPerHour : Int
                         ticksPerHour =
-                            engagements
-                                |> List.map Logic.ticksGivenPerQuestEngagement
-                                |> List.sum
+                            engagedPlayersCount * Logic.questTicksPerHour
 
                         ticksGiven : Int
                         ticksGiven =
@@ -290,7 +282,7 @@ getPlayerData_ worldName world player =
                     in
                     { ticksGiven = ticksGiven
                     , ticksPerHour = ticksPerHour
-                    , playersActive = playersActive
+                    , playersActive = engagedPlayersCount
                     , ticksGivenByPlayer = ticksGivenByPlayer
                     }
                 )
@@ -498,9 +490,11 @@ processGameTickForQuests worldName model =
                                                     player
                                                         |> Maybe.map
                                                             (\player_ ->
-                                                                quest
-                                                                    |> SPlayer.questEngagement player_
-                                                                    |> Logic.ticksGivenPerQuestEngagement
+                                                                if SeqSet.member quest player_.questsActive then
+                                                                    Logic.questTicksPerHour
+
+                                                                else
+                                                                    0
                                                             )
                                                         |> Maybe.withDefault 0
                                             in
@@ -2160,51 +2154,110 @@ stopProgressing quest clientId _ worldName player model =
 
 startProgressing : Quest.Name -> ClientId -> World -> World.Name -> SPlayer -> Model -> ( Model, Cmd BackendMsg )
 startProgressing quest clientId world worldName player model =
+    -- TODO: there are requirements other than Quest.playerRequirements, check them as well if we don't elsewhere already
     let
-        ensurePlayerIsInQuestProgressDict : World -> World
-        ensurePlayerIsInQuestProgressDict world_ =
-            if SPlayer.canStartProgressing quest world.tickPerIntervalCurve player then
-                { world_
-                    | questsProgress =
-                        world_.questsProgress
-                            |> SeqDict.update quest
-                                (\maybePlayersProgress ->
-                                    case maybePlayersProgress of
-                                        Nothing ->
-                                            Just (Dict.singleton player.name 0)
-
-                                        Just playersProgress ->
-                                            playersProgress
-                                                |> Dict.update player.name
-                                                    (\maybePlayerProgress ->
-                                                        case maybePlayerProgress of
-                                                            Nothing ->
-                                                                Just 0
-
-                                                            Just n ->
-                                                                Just n
-                                                    )
-                                                |> Just
-                                )
-                }
+        playerRequirements : List Quest.PlayerRequirement
+        playerRequirements =
+            Quest.playerRequirements quest
+
+        playerAlreadyPaidRequirements : Bool
+        playerAlreadyPaidRequirements =
+            world.questRequirementsPaid
+                |> SeqDict.get quest
+                |> Maybe.withDefault Set.empty
+                |> Set.member player.name
+
+        playerCanPayRequirements : Bool
+        playerCanPayRequirements =
+            playerRequirements
+                |> List.all
+                    (\req ->
+                        case req of
+                            Quest.SkillRequirement r ->
+                                case r.skill of
+                                    Quest.Combat ->
+                                        let
+                                            maxCombatSkill : Int
+                                            maxCombatSkill =
+                                                Logic.questRequirementCombatSkills
+                                                    |> List.map (Skill.get player.special player.addedSkillPercentages)
+                                                    |> List.maximum
+                                                    |> Maybe.withDefault 0
+                                        in
+                                        maxCombatSkill >= r.percentage
 
-            else
-                world_
+                                    Quest.Specific skill ->
+                                        Skill.get player.special player.addedSkillPercentages skill >= r.percentage
 
-        newModel =
-            if World.isQuestDone quest world then
-                model
+                            Quest.ItemRequirementOneOf itemsNeeded ->
+                                itemsNeeded
+                                    |> List.any
+                                        (\item ->
+                                            player.items
+                                                |> Dict.any (\_ { kind, count } -> kind == item && count >= 1)
+                                        )
 
-            else
-                model
-                    |> updatePlayer worldName player.name (SPlayer.startProgressing quest world.tickPerIntervalCurve)
-                    |> updateWorld worldName ensurePlayerIsInQuestProgressDict
+                            Quest.CapsRequirement capsNeeded ->
+                                player.caps >= capsNeeded
+                    )
     in
-    getPlayerData worldName player.name newModel
-        |> Maybe.map
-            (\data ->
-                ( newModel
-                , Lamdera.sendToFrontend clientId <| CurrentPlayer data
+    if playerAlreadyPaidRequirements || playerCanPayRequirements then
+        let
+            ensurePlayerPresent : World -> World
+            ensurePlayerPresent world_ =
+                if SPlayer.canStartProgressing world.tickPerIntervalCurve player then
+                    { world_
+                        | questsProgress =
+                            world_.questsProgress
+                                |> SeqDict.update quest
+                                    (\maybePlayersProgress ->
+                                        case maybePlayersProgress of
+                                            Nothing ->
+                                                Just (Dict.singleton player.name 0)
+
+                                            Just playersProgress ->
+                                                playersProgress
+                                                    |> Dict.update player.name (Maybe.withDefault 0 >> Just)
+                                                    |> Just
+                                    )
+                    }
+
+                else
+                    world_
+
+            newModel =
+                if World.isQuestDone quest world then
+                    model
+
+                else
+                    model
+                        |> updatePlayer worldName player.name (SPlayer.startProgressing quest world.tickPerIntervalCurve)
+                        |> updateWorld worldName ensurePlayerPresent
+                        |> (if playerAlreadyPaidRequirements then
+                                identity
+
+                            else
+                                updatePlayer worldName player.name (SPlayer.payQuestRequirements playerRequirements)
+                                    >> updateWorld worldName (notePlayerPaidRequirements quest player.name)
+                           )
+        in
+        getPlayerData worldName player.name newModel
+            |> Maybe.map
+                (\data ->
+                    ( newModel
+                    , Lamdera.sendToFrontend clientId <| CurrentPlayer data
+                    )
                 )
-            )
-        |> Maybe.withDefault ( model, Cmd.none )
+            |> Maybe.withDefault ( model, Cmd.none )
+
+    else
+        ( model, Cmd.none )
+
+
+notePlayerPaidRequirements : Quest.Name -> PlayerName -> World -> World
+notePlayerPaidRequirements quest player world =
+    { world
+        | questRequirementsPaid =
+            world.questRequirementsPaid
+                |> SeqDict.update quest (Maybe.withDefault Set.empty >> Set.insert player >> Just)
+    }
diff --git a/src/Data/Player/SPlayer.elm b/src/Data/Player/SPlayer.elm
index a88c87b..02c92c5 100644
--- a/src/Data/Player/SPlayer.elm
+++ b/src/Data/Player/SPlayer.elm
@@ -17,8 +17,8 @@ module Data.Player.SPlayer exposing
     , incSpecial
     , incWins
     , levelUpHereAndNow
+    , payQuestRequirements
     , preferAmmo
-    , questEngagement
     , readMessage
     , recalculateHp
     , removeAllMessages
@@ -51,20 +51,16 @@ import Data.Message as Message exposing (Content(..), Message)
 import Data.Perk as Perk exposing (Perk)
 import Data.Player exposing (SPlayer)
 import Data.Quest as Quest
-    exposing
-        ( Engagement(..)
-        , PlayerRequirement(..)
-        , SkillRequirement(..)
-        )
 import Data.Skill as Skill exposing (Skill)
 import Data.Special as Special
 import Data.Tick as Tick exposing (TickPerIntervalCurve)
 import Data.Trait as Trait
 import Data.Xp as Xp
 import Dict exposing (Dict)
+import Dict.Extra
 import Logic
 import SeqDict exposing (SeqDict)
-import SeqSet exposing (SeqSet)
+import SeqSet
 import Time exposing (Posix)
 
 
@@ -290,7 +286,7 @@ tick : Posix -> TickPerIntervalCurve -> SPlayer -> SPlayer
 tick currentTime worldTickCurve player =
     player
         |> addTicks (ticksPerHourAvailableAfterQuests worldTickCurve player)
-        |> addQuestProgressXp currentTime
+        |> addTickQuestProgressXp currentTime
         |> (if player.hp < player.maxHp then
                 addHp
                     (Logic.healOverTimePerTick
@@ -305,8 +301,8 @@ tick currentTime worldTickCurve player =
            )
 
 
-addQuestProgressXp : Posix -> SPlayer -> SPlayer
-addQuestProgressXp currentTime player =
+addTickQuestProgressXp : Posix -> SPlayer -> SPlayer
+addTickQuestProgressXp currentTime player =
     let
         xpPerQuest : SeqDict Quest.Name Int
         xpPerQuest =
@@ -315,10 +311,7 @@ addQuestProgressXp currentTime player =
                 |> List.map
                     (\quest ->
                         ( quest
-                        , quest
-                            |> questEngagement player
-                            |> Logic.ticksGivenPerQuestEngagement
-                            |> (*) (Quest.xpPerTickGiven quest)
+                        , Quest.xpPerTickGiven quest * Logic.questTicksPerHour
                         )
                     )
                 |> SeqDict.fromList
@@ -713,76 +706,19 @@ setPreferredAmmo preferredAmmo player =
     { player | preferredAmmo = preferredAmmo }
 
 
-questEngagement : SPlayer -> Quest.Name -> Quest.Engagement
-questEngagement player quest =
-    let
-        reqs : List PlayerRequirement
-        reqs =
-            Quest.playerRequirements quest
-
-        meetsRequirement : PlayerRequirement -> Bool
-        meetsRequirement req =
-            let
-                oneSkill : Int -> Skill -> Bool
-                oneSkill percentage skill =
-                    Skill.get player.special player.addedSkillPercentages skill >= percentage
-            in
-            case req of
-                SkillRequirement { skill, percentage } ->
-                    case skill of
-                        Combat ->
-                            List.any (oneSkill percentage) Skill.combatSkills
-
-                        Specific skill_ ->
-                            oneSkill percentage skill_
-
-                ItemRequirementOneOf items ->
-                    -- TODO consume the items once adding the quest? Only in some of these cases (chimpanzee brain) and not in others (lockpick)?
-                    let
-                        playerItemKinds : SeqSet ItemKind.Kind
-                        playerItemKinds =
-                            player.items
-                                |> Dict.values
-                                |> List.map .kind
-                                |> SeqSet.fromList
-                    in
-                    List.all (\kind -> SeqSet.member kind playerItemKinds) items
-
-                CapsRequirement amount ->
-                    -- TODO remove the caps once adding the quest?
-                    player.caps >= amount
-    in
-    if SeqSet.member quest player.questsActive then
-        if List.isEmpty reqs then
-            Progressing
-
-        else if List.all meetsRequirement reqs then
-            Progressing
-
-        else
-            {- TODO only allow this if the reqs not met are the skill ones!
-               We can't allow the user to progress if they don't meet the caps / items reqs!
-            -}
-            ProgressingSlowly
-
-    else
-        NotProgressing
-
-
 stopProgressing : Quest.Name -> SPlayer -> SPlayer
 stopProgressing quest player =
     { player | questsActive = SeqSet.remove quest player.questsActive }
 
 
-canStartProgressing : Quest.Name -> TickPerIntervalCurve -> SPlayer -> Bool
-canStartProgressing quest worldTickCurve player =
-    -- TODO is it expected that `quest` is not used?
-    ticksPerHourAvailableAfterQuests worldTickCurve player >= Logic.minTicksPerHourNeededForQuest
+canStartProgressing : TickPerIntervalCurve -> SPlayer -> Bool
+canStartProgressing worldTickCurve player =
+    ticksPerHourAvailableAfterQuests worldTickCurve player >= Logic.questTicksPerHour
 
 
 startProgressing : Quest.Name -> TickPerIntervalCurve -> SPlayer -> SPlayer
 startProgressing quest worldTickCurve player =
-    if canStartProgressing quest worldTickCurve player then
+    if canStartProgressing worldTickCurve player then
         { player | questsActive = SeqSet.insert quest player.questsActive }
 
     else
@@ -792,11 +728,42 @@ startProgressing quest worldTickCurve player =
 ticksPerHourUsedOnQuests : SPlayer -> Int
 ticksPerHourUsedOnQuests player =
     player.questsActive
-        |> SeqSet.toList
-        |> List.map (questEngagement player >> Logic.ticksGivenPerQuestEngagement)
-        |> List.sum
+        |> SeqSet.size
+        |> (*) Logic.questTicksPerHour
 
 
 ticksPerHourAvailableAfterQuests : TickPerIntervalCurve -> SPlayer -> Int
 ticksPerHourAvailableAfterQuests worldTickCurve player =
     Tick.ticksAddedPerInterval worldTickCurve player.ticks - ticksPerHourUsedOnQuests player
+
+
+payQuestRequirements : List Quest.PlayerRequirement -> SPlayer -> SPlayer
+payQuestRequirements reqs player =
+    List.foldl
+        (\req accPlayer ->
+            case req of
+                Quest.SkillRequirement _ ->
+                    accPlayer
+
+                Quest.ItemRequirementOneOf requiredItems ->
+                    -- Only pay the first item you find
+                    case Dict.Extra.find (\_ item -> List.member item.kind requiredItems && item.count >= 1) accPlayer.items of
+                        Nothing ->
+                            accPlayer
+
+                        Just ( id, item ) ->
+                            { accPlayer
+                                | items =
+                                    if item.count == 1 then
+                                        Dict.remove id accPlayer.items
+
+                                    else
+                                        Dict.insert id { item | count = max 0 (item.count - 1) } accPlayer.items
+                            }
+
+                Quest.CapsRequirement capsRequired ->
+                    -- It should have been checked outside this function that we _do_ have enough caps to pay
+                    { accPlayer | caps = max 0 (player.caps - capsRequired) }
+        )
+        player
+        reqs
diff --git a/src/Data/Quest.elm b/src/Data/Quest.elm
index b495329..fcb5e29 100644
--- a/src/Data/Quest.elm
+++ b/src/Data/Quest.elm
@@ -1,13 +1,11 @@
 module Data.Quest exposing
-    ( Engagement(..)
-    , GlobalReward(..)
+    ( GlobalReward(..)
     , Name(..)
     , PlayerRequirement(..)
     , PlayerReward(..)
     , Progress
     , SkillRequirement(..)
     , all
-    , allEngagement
     , allForLocation
     , decoder
     , encode
@@ -207,20 +205,6 @@ type alias Progress =
     }
 
 
-type Engagement
-    = NotProgressing
-    | ProgressingSlowly
-    | Progressing
-
-
-allEngagement : List Engagement
-allEngagement =
-    [ NotProgressing
-    , ProgressingSlowly
-    , Progressing
-    ]
-
-
 title : Name -> String
 title name =
     case name of
diff --git a/src/Data/World.elm b/src/Data/World.elm
index fca7f25..240ca32 100644
--- a/src/Data/World.elm
+++ b/src/Data/World.elm
@@ -25,6 +25,7 @@ import SeqDict exposing (SeqDict)
 import SeqDict.Extra as SeqDict
 import SeqSet exposing (SeqSet)
 import SeqSet.Extra as SeqSet
+import Set exposing (Set)
 import Time exposing (Posix)
 import Time.Extra as Time
 import Time.ExtraExtra as Time
@@ -47,6 +48,9 @@ type alias World =
     , vendorRestockFrequency : Time.Interval
     , questsProgress : SeqDict Quest.Name (Dict PlayerName Int)
     , questRewardShops : SeqSet Shop
+    , -- Which players have paid the item / caps requirement for a quest?
+      -- (They'll be able to pause/unpause their progress on the quest for free)
+      questRequirementsPaid : SeqDict Quest.Name (Set PlayerName)
     }
 
 
@@ -81,6 +85,10 @@ init { fast } =
             |> List.map (\q -> ( q, Dict.empty ))
             |> SeqDict.fromList
     , questRewardShops = SeqSet.empty
+    , questRequirementsPaid =
+        Quest.all
+            |> List.map (\q -> ( q, Set.empty ))
+            |> SeqDict.fromList
     }
 
 
@@ -102,6 +110,7 @@ encode world =
         , ( "vendorRestockFrequency", Time.encodeInterval world.vendorRestockFrequency )
         , ( "questsProgress", SeqDict.encode Quest.encode (JE.dict identity JE.int) world.questsProgress )
         , ( "questRewardShops", SeqSet.encode Shop.encode world.questRewardShops )
+        , ( "questRequirementsPaid", SeqDict.encode Quest.encode (JE.set JE.string) world.questRequirementsPaid )
         ]
 
 
@@ -131,7 +140,7 @@ lastItemId players vendors =
 decoder : Decoder World
 decoder =
     JD.succeed
-        (\players nextWantedTick nextVendorRestockTick vendors description startedAt tickFrequency tickPerIntervalCurve vendorRestockFrequency questsProgress questRewardShops ->
+        (\players nextWantedTick nextVendorRestockTick vendors description startedAt tickFrequency tickPerIntervalCurve vendorRestockFrequency questsProgress questRewardShops questRequirementsPaid ->
             { players = players
             , nextWantedTick = nextWantedTick
             , nextVendorRestockTick = nextVendorRestockTick
@@ -144,6 +153,7 @@ decoder =
             , vendorRestockFrequency = vendorRestockFrequency
             , questsProgress = questsProgress
             , questRewardShops = questRewardShops
+            , questRequirementsPaid = questRequirementsPaid
             }
         )
         |> JD.andMap
@@ -165,6 +175,7 @@ decoder =
         |> JD.andMap (JD.field "vendorRestockFrequency" Time.intervalDecoder)
         |> JD.andMap (JD.field "questsProgress" (SeqDict.decoder Quest.decoder (Dict.decoder JD.string JD.int)))
         |> JD.andMap (JD.field "questRewardShops" (SeqSet.decoder Shop.decoder))
+        |> JD.andMap (JD.field "questRequirementsPaid" (SeqDict.decoder Quest.decoder (JD.set JD.string)))
 
 
 isQuestDone : Quest.Name -> World -> Bool
diff --git a/src/Logic.elm b/src/Logic.elm
index dc776fb..00b7051 100644
--- a/src/Logic.elm
+++ b/src/Logic.elm
@@ -25,7 +25,6 @@ module Logic exposing
     , mainWorldName
     , maxPossibleMove
     , maxTraits
-    , minTicksPerHourNeededForQuest
     , naturalArmorClass
     , newCharAvailableSpecialPoints
     , newCharMaxTaggedSkills
@@ -34,13 +33,14 @@ module Logic exposing
     , playerCombatCapsGained
     , playerCombatXpGained
     , price
+    , questRequirementCombatSkills
+    , questTicksPerHour
     , regainConciousnessApCost
     , sequence
     , skillPointCost
     , skillPointsPerLevel
     , standUpApCost
     , tickHealPercentage
-    , ticksGivenPerQuestEngagement
     , totalTags
     , unaimedAttackStyle
     , unarmedApCost
@@ -61,7 +61,6 @@ import Data.Item.Effect as ItemEffect
 import Data.Item.Kind as ItemKind
 import Data.Item.Type as ItemType
 import Data.Perk as Perk exposing (Perk)
-import Data.Quest as Quest exposing (Engagement(..))
 import Data.Skill as Skill exposing (Skill)
 import Data.Special as Special exposing (Special)
 import Data.Trait as Trait exposing (Trait)
@@ -1780,26 +1779,9 @@ mainWorldName =
     "main"
 
 
-minTicksPerHourNeededForQuest : Int
-minTicksPerHourNeededForQuest =
-    Quest.allEngagement
-        |> List.map ticksGivenPerQuestEngagement
-        |> List.filter (\tph -> tph /= 0)
-        |> List.minimum
-        |> Maybe.withDefault 1
-
-
-ticksGivenPerQuestEngagement : Quest.Engagement -> Int
-ticksGivenPerQuestEngagement engagement =
-    case engagement of
-        NotProgressing ->
-            0
-
-        ProgressingSlowly ->
-            1
-
-        Progressing ->
-            2
+questTicksPerHour : Int
+questTicksPerHour =
+    2
 
 
 burstShotChanceToHitPenalty : Int
@@ -2259,3 +2241,16 @@ maxPossibleMove r =
 
     else
         0
+
+
+questRequirementCombatSkills : List Skill
+questRequirementCombatSkills =
+    [ Skill.SmallGuns
+    , Skill.BigGuns
+    , Skill.EnergyWeapons
+    , Skill.MeleeWeapons
+    , Skill.Throwing
+    , Skill.Unarmed
+
+    -- TODO traps?
+    ]