refill hearts like Duolingo: one every 5 hours, all of them after 24
Each heart used to take 24 hours, so a student who lost all five waited up to five days. Now one heart returns every 5 hours, and 24 hours after the first heart was lost they all come back at once (the owner's call). /api/user/hearts reports both times (nextRefillAt, fullRefillAt); an existing timer longer than 5 hours is shortened the first time it is read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,11 @@ from datetime import timedelta
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
HEART_REFILL_INTERVAL = timedelta(hours=24)
|
||||
# مثلِ دولینگو: هر ۵ ساعت یک قلب برمیگردد، و ۲۴ ساعت بعد از اولین قلبی که
|
||||
# از دست رفت همه یکجا پر میشوند (تصمیمِ صاحبِ پروژه، ۲۰۲۶-۰۹-۲۴). قبلاً هر
|
||||
# قلب ۲۴ ساعت بود؛ دانشآموزی که هر پنج را از دست میداد تا پنج روز میماند.
|
||||
HEART_REFILL_INTERVAL = timedelta(hours=5)
|
||||
HEART_FULL_REFILL_AFTER = timedelta(hours=24)
|
||||
|
||||
|
||||
class UserStats(models.Model):
|
||||
@@ -59,43 +63,73 @@ class UserHearts(models.Model):
|
||||
user_id = models.IntegerField(unique=True, db_index=True)
|
||||
current_hearts = models.PositiveSmallIntegerField(default=5)
|
||||
max_hearts = models.PositiveSmallIntegerField(default=5)
|
||||
# قلبِ بعدی کی برمیگردد (هر HEART_REFILL_INTERVAL یکی).
|
||||
next_refill_at = models.DateTimeField(null=True, blank=True)
|
||||
# همهی قلبها کی یکجا پر میشوند: HEART_FULL_REFILL_AFTER بعد از اولین
|
||||
# قلبی که از حالتِ پر کم شد.
|
||||
full_refill_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
REGEN_FIELDS = ['current_hearts', 'next_refill_at', 'full_refill_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"hearts-user{self.user_id}"
|
||||
|
||||
def _refill_all(self):
|
||||
self.current_hearts = self.max_hearts
|
||||
self.next_refill_at = None
|
||||
self.full_refill_at = None
|
||||
|
||||
def apply_regen(self):
|
||||
"""اگه از next_refill_at گذشته باشه، هارت(های) گذشته رو پر میکنه (چندتا هم
|
||||
اگه کاربر چند بازهی HEART_REFILL_INTERVAL غایب بوده باشه). True یعنی
|
||||
state عوض شده و باید save بشه."""
|
||||
"""
|
||||
قلبهایی را که وقتشان رسیده برمیگرداند. True یعنی state عوض شده و باید
|
||||
با REGEN_FIELDS ذخیره شود.
|
||||
|
||||
۱) اگر از full_refill_at گذشته باشد، همه یکجا پر میشوند.
|
||||
۲) وگرنه به ازای هر HEART_REFILL_INTERVAL که از next_refill_at گذشته،
|
||||
یک قلب (چندتا اگر کاربر مدتی نبوده).
|
||||
"""
|
||||
if self.current_hearts >= self.max_hearts:
|
||||
if self.next_refill_at is not None:
|
||||
if self.next_refill_at is not None or self.full_refill_at is not None:
|
||||
self.next_refill_at = None
|
||||
self.full_refill_at = None
|
||||
return True
|
||||
return False
|
||||
|
||||
now = timezone.now()
|
||||
if self.next_refill_at is None:
|
||||
changed = False
|
||||
if self.full_refill_at is None:
|
||||
self.full_refill_at = now + HEART_FULL_REFILL_AFTER
|
||||
changed = True
|
||||
if self.next_refill_at is None or self.next_refill_at > now + HEART_REFILL_INTERVAL:
|
||||
# نبودِ تایمر، یا تایمرِ ۲۴ساعتهی قدیمی از پیش از این تغییر.
|
||||
self.next_refill_at = now + HEART_REFILL_INTERVAL
|
||||
changed = True
|
||||
|
||||
if now >= self.full_refill_at:
|
||||
self._refill_all()
|
||||
return True
|
||||
if now < self.next_refill_at:
|
||||
return False
|
||||
return changed
|
||||
|
||||
elapsed_intervals = 1 + (now - self.next_refill_at) // HEART_REFILL_INTERVAL
|
||||
gained = min(elapsed_intervals, self.max_hearts - self.current_hearts)
|
||||
self.current_hearts += gained
|
||||
if self.current_hearts >= self.max_hearts:
|
||||
self.next_refill_at = None
|
||||
self._refill_all()
|
||||
else:
|
||||
self.next_refill_at += elapsed_intervals * HEART_REFILL_INTERVAL
|
||||
return True
|
||||
|
||||
def lose_heart(self):
|
||||
"""یک هارت کم میکنه؛ اگه تایمرِ refill از قبل در حال شمارش نیست، از الان شروعش میکنه."""
|
||||
"""یک قلب کم میکند و اگر تایمرها در حالِ شمارش نیستند، از الان شروعشان میکند."""
|
||||
if self.current_hearts > 0:
|
||||
self.current_hearts -= 1
|
||||
if self.next_refill_at is None and self.current_hearts < self.max_hearts:
|
||||
self.next_refill_at = timezone.now() + HEART_REFILL_INTERVAL
|
||||
if self.current_hearts < self.max_hearts:
|
||||
now = timezone.now()
|
||||
if self.next_refill_at is None:
|
||||
self.next_refill_at = now + HEART_REFILL_INTERVAL
|
||||
if self.full_refill_at is None:
|
||||
self.full_refill_at = now + HEART_FULL_REFILL_AFTER
|
||||
|
||||
|
||||
class Character(models.Model):
|
||||
|
||||
Reference in New Issue
Block a user