🧬 DiWorkOut: A Moddable XP‑Farming Habit Engine for the Livingrimoire
Gamified habit apps usually fall into two categories: task checklists wearing RPG cosmetics, or closed systems you can’t mod, extend, or understand. DiWorkOut takes a different path. It’s a fully moddable XP‑farming engine built as a Livingrimoire skill — meaning every behavior is transparent, every mechanic is hackable, every trigger is customizable, and every reward loop is yours to shape.
Instead of “mark task as done,” you get a stateful behavior loop with streak logic, rest‑day tolerance, XP curves, and reward gating. It’s closer to a tiny RPG engine than a habit tracker.
⚙️ What DiWorkOut Actually Does
DiWorkOut tracks:
- the last time you performed a habit
- how many days you’ve kept the streak
- how much XP you’ve earned
- how much “credit” you’ve unlocked for rewards
- how many rest days you’re allowed before the streak resets
It responds to natural‑language triggers like:
- “i worked out” → farm XP
- “what is my power level” → show level
- “may i play video games” → spend credit
And because it’s a Livingrimoire skill, it persists everything automatically.
🔧 Why It’s Moddable
Two hooks define the entire behavior loop:
-
on_farm_event()→ what counts as farming -
reward_xp(streak)→ how much XP to give
Modders can override these to create meditation skills, reading skills, sobriety streaks, coding practice loops, language learning streaks — anything becomes a gamified XP engine.
🧩 Full Skill Code (Copy‑Paste Ready)
class DiWorkOut(Skill):
def __init__(self):
super().__init__()
self.last_date = ""
self.xp_farmed = 0
self.accumulation = 0
self.max_rest_days = 2
self.credit = 0
self.xp_base = 10
# -------------------------
# Moddable hooks
# -------------------------
def on_farm_event(self) -> str:
return getattr(self, "farm_event", "i worked out")
def reward_request(self) -> str:
return getattr(self, "req", "may i play video games")
def reward_xp(self, streak: int) -> int:
return int(self.xp_base * (1 + math.log(1 + streak)))
# -------------------------
# Utility
# -------------------------
@staticmethod
def is_ymd_format(s: str) -> bool:
return bool(re.fullmatch(r"\d{4}-\d{2}-\d{2}", s))
@staticmethod
def to_int(s: str, default):
s = s.strip()
if s.startswith("-") and s[1:].isdigit():
return -int(s[1:])
if s.isdigit():
return int(s)
return default
@staticmethod
def days_from_today(date_str, fmt="%Y-%m-%d"):
target = datetime.strptime(date_str, fmt).date()
return (target - date.today()).days
def xp_to_level(self) -> float:
return round(self.xp_farmed ** (1 / 3), 2)
# -------------------------
# Persistence
# -------------------------
def load_state(self):
ld = self._kokoro.grimoireMemento.load(f"{self.skill_name}_last_date")
self.last_date = ld if self.is_ymd_format(ld) else date.today().strftime("%Y-%m-%d")
self.xp_farmed = self.to_int(
self._kokoro.grimoireMemento.load(f"{self.skill_name}_xp"), 0
)
self.accumulation = self.to_int(
self._kokoro.grimoireMemento.load(f"{self.skill_name}_accumulation"), 0
)
def save_state(self):
self._kokoro.grimoireMemento.save(f"{self.skill_name}_xp", str(self.xp_farmed))
self._kokoro.grimoireMemento.save(
f"{self.skill_name}_accumulation", str(self.accumulation)
)
self._kokoro.grimoireMemento.save(
f"{self.skill_name}_last_date", date.today().strftime("%Y-%m-%d")
)
# -------------------------
# Farming logic
# -------------------------
def register_workout(self):
self.credit += 1
dif = self.days_from_today(self.last_date)
if dif == 0:
streak = 1
elif dif < self.max_rest_days:
self.accumulation += 1
streak = self.accumulation
else:
self.accumulation = 1
streak = 1
self.xp_farmed += self.reward_xp(streak)
self.save_state()
self.setSimpleAlg(f"you farmed {self.xp_farmed} experience points")
# -------------------------
# Queries
# -------------------------
def query_power_level(self):
self.setSimpleAlg(f"your level is {self.xp_to_level()}")
def query_play_games(self):
if self.credit > 0:
self.credit = max(0, self.credit - 1)
self.setSimpleAlg("yes you may sweetheart")
else:
self.credit = 0
self.setSimpleAlg("no you may not you need to workout first")
# -------------------------
# Lifecycle
# -------------------------
def manifest(self):
self.load_state()
# -------------------------
# Input routing
# -------------------------
def input(self, ear: str, skin: str, eye: str):
if not ear:
return
match ear:
case _ if ear == self.on_farm_event():
self.register_workout()
case _ if ear == self.reward_request():
self.query_play_games()
case "what is my power level":
self.query_power_level()
# -------------------------
# Metadata
# -------------------------
def skillNotes(self, param: str) -> str:
if param == "notes":
return "moddable workout skill with override hooks for event and reward"
elif param == "triggers":
return f"{self.on_farm_event()}; what is my power level; {self.reward_request()}"
return "note unavailable"
🛠️ How to Mod It
A modder can create a new habit skill by subclassing:
class PushupSkill(DiWorkOut):
def __init__(self):
super().__init__()
self.farm_event = "i did pushups"
self.req = "may i watch anime"
self.xp_base = 7
def reward_xp(self, streak):
return streak * 5
Now the bot responds to:
- “i did pushups”
- “may i watch anime”
With a completely different XP curve.
🚀 Why This Hits Harder Than Habitica
Habitica gamifies tasks.
DiWorkOut gamifies behavior loops.
- streak logic
- rest‑day forgiveness
- XP curves
- reward gating
- natural‑language triggers
- full mod support
It’s a tiny RPG engine for real‑world habits — and it lives inside a framework designed for modular AI companions.
Top comments (0)