Add a Scoring system to character

As character do things, they can now get a "score" for everything I
think could be interesting/challenging.
This commit is contained in:
Howard Abrams 2025-08-24 22:34:11 -07:00
parent 5edc657678
commit 43a1aefb49
13 changed files with 123 additions and 3 deletions

View file

@ -24,6 +24,7 @@ from commands.everyone import (CmdTake, CmdThink, CmdSay,
CmdWhisper, CmdRead, CmdEat, CmdDrink,
CmdUse, CmdPush, CmdPull,
CmdOpen, CmdClose, CmdFeed)
from commands.scoring import CmdScore
from commands.hint import CmdHint
from commands.misc import CmdLight
from commands.wizards import (CmdGM, CmdSpell, CmdGMTrigger,
@ -70,6 +71,7 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
self.add(CmdMakeCocktail)
self.add(CmdGift)
self.add(CmdFeed)
self.add(CmdScore)
class AccountCmdSet(default_cmds.AccountCmdSet):

49
commands/scoring.py Executable file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env python
from evennia import CmdSet
from commands.command import Command
from utils.scoring import Scores
class CmdScore(Command):
"""
Display your score.
Usage:
score
The more things you do in this game, the better your scorecard.
"""
key = "score"
aliases = ["scorecard"]
def func(self):
"""
Implements the score command.
"""
def checked(tick):
return " |g✔|n " if score & tick.value else " |r-|n "
score = self.caller.attributes.get('score', 0)
total = len([1 for tick in list(Scores) if score & tick.value])
msg = "Your scorecard is:|/" + "|/".join([
checked(Scores.jump) + "Jumped in a puddle",
checked(Scores.moss_sit) + "Sat on some moss",
checked(Scores.throw_stick) + "Thrown a stick",
checked(Scores.catch_fish) + "Caught a fish",
checked(Scores.feed_bhb) + "Fed Big Hairy Beast",
checked(Scores.eat_candy) + "Eaten a candy",
checked(Scores.get_cocktail) + "Got a cocktail",
checked(Scores.finish_cocktail) + "Finished a cocktail",
checked(Scores.make_tea) + "Made a pot of tea",
checked(Scores.read_a_book) + "Read a book from Dabbler's library",
checked(Scores.blow_horn) + "Called on the Mist Horn",
checked(Scores.get_pipe) + "Received a smoking pipe",
checked(Scores.get_blue_medal) + "Found the Blue Medal",
checked(Scores.make_potion) + "Made a potion",
]) + f"|/Total: {total}"
self.caller.msg(msg)

View file

@ -12,7 +12,7 @@ from commands.command import Command
from typeclasses.objects import Object
from typeclasses.scripts import Script
from typeclasses.drinkables import Container
from utils.scoring import Scores
class CmdEmpty(Command):
"""
@ -241,6 +241,7 @@ class Cauldron(Object):
seq, brew_func = self.can_create_laughter()
if seq:
maker.score(Scores.make_potion)
maker.announce_action(good)
for idx, msg in enumerate(seq):
delay(idx * 3 + 2, maker.announce_action, msg)

View file

@ -105,6 +105,14 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
if obj.is_typeclass(typeclass):
obj.delete()
def score(self, tick):
"""
Convenience method for keeping track of activities.
"""
score = self.attributes.get('score', 0)
score |= tick.value
self.attributes.add('score', score)
def new_account_setup(self):
"""
New accounts should connect the tutorial.
@ -137,6 +145,7 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
# Reset the "running state" that character may have
# experienced. This way, new guests get to _re-experience_ it:
self.db.score = 0
self.db.visited = False
self.db.jumped_times = 0
self.db.tutorstate = 0

View file

@ -5,6 +5,7 @@ from evennia.utils import logger
from typeclasses.objects import Object
from commands.consumables import CmdSetTeapot, CmdSetFillable
from utils.scoring import Scores
from utils.word_list import routput, choices
import re
@ -282,6 +283,7 @@ class Teapot(Object):
self.db.tea_choice = tea_choice
self.db.desc = f"A large, brown teapot full of {desc}."
drinker.announce_action(f"$You() $conj(make) a teapot of {desc}.")
drinker.score(Scores.make_tea)
def do_fill(self, drinker):
teatype = self.db.tea_choice
@ -517,9 +519,10 @@ class Cocktail(Object):
pre_msg = details.get("pre", "")
drink.location.msg(routput(pre_msg + " The << bartender ^ barkeep ^ elf >> << passes ^ slides ^ gives ^ hands >> you a |y{0}|n.", drink.name))
owner.score(Scores.get_cocktail)
theroom = drink.location.location
char = owner.db._sdesc or owner.get_display_name(theroom)
char = owner.sdesc.get() or owner.get_display_name(theroom)
msg = routput(pre_msg + " The << bartender ^ barkeep ^ elf >> << passes ^ slides ^ gives ^ hands >> a {0} to {1}.",
drink.db.cocktail_type, char)
theroom.msg_contents(msg, exclude=owner)
@ -537,6 +540,7 @@ class Cocktail(Object):
drinker.msg(choices(DRINK_COCKTAIL_MSGS, self.key, cocktail_type, flavor))
elif amount > 0:
drinker.msg(choices(self.db.effects, self.key, cocktail_type))
drinker.score(Scores.finish_cocktail)
else:
self.key = f"{self.db.cocktail_type} glass"
self.aliases.add('glass')

View file

@ -15,6 +15,7 @@ from urllib.request import urlopen
from typeclasses.objects import Object
from typeclasses.characters import Character
from typeclasses.npcs import CarriableNPC
from utils.scoring import Scores
from utils.word_list import routput
import random
@ -238,3 +239,4 @@ class FishingPole(Object):
})[0]
fish.location = fisher
fisher.msg(f"You caught a fish!")
fisher.score(Scores.catch_fish)

View file

@ -19,6 +19,7 @@ from evennia.prototypes.spawner import spawn
from evennia.utils import delay, logger
from evennia.utils.search import search_object
from utils.scoring import Scores
from utils.word_list import routput
@ -562,6 +563,21 @@ class Listener:
c.adjust_coins(int(m.group(1)))
return
m = match(r"score_all ([a-z_]+)", cmd)
if m:
tick = Scores[m.group(1)]
for c in self.characters_here(puppets=True):
c.score(tick)
return
m = match(r"score ([a-z_]+) *?( to|=)? *([a-z_]+)", cmd)
if m:
tick = Scores[m.group(1)]
c = self.search(m.group(3))
if c:
c.score(tick)
return
if self.is_typeclass("typeclasses.characters.Character"):
self.execute_cmd(cmd)
else:

View file

@ -23,6 +23,7 @@ from evennia.utils.search import search_object
from typeclasses.objects import Object
from typeclasses.characters import Character
from commands.pets import CmdPetSet
from utils.scoring import Scores
from utils.word_list import squish, choices
@ -627,10 +628,12 @@ class BHB(Friendly):
msg = f"{noun} {how_sniff} sniffs $your() outstretched arm holding a scone. It {how_eat} eats it, and {and_then}."
self.adjust_character(feeder, 100)
feeder.has('scone').delete()
feeder.score(Scores.feed_bhb)
elif is_berry(item):
msg = f"{noun} {how_sniff} sniffs $your() outstretched arm with a handful of berries. It {how_eat} eats them, and {and_then}."
self.adjust_character(feeder, 40)
feeder.has('berries').delete()
feeder.score(Scores.feed_bhb)
else:
msg = f"{noun} doesn't appear interested in anything you have."

View file

@ -9,6 +9,7 @@ from evennia.prototypes.spawner import spawn
from typeclasses.objects import Object
from typeclasses.consumables import Producer
from utils.scoring import Scores
from utils.user_info import location
from utils.word_list import routput
@ -135,6 +136,7 @@ class WriteableBook(Book):
self.db.post_write_msg or
"$You() $conj(put) down the quill, and $conj(stop) writing.")
writer.score(Scores.sign_guest_book)
class Journal(WriteableBook):
"""
@ -315,6 +317,7 @@ class Bookshelf(Producer):
book.location = reader
reader.msg(f"You pick up {filler} book, |w{title}|n.")
reader.score(Scores.read_a_book)
self.db.last_title = None
self.db.last_desc = None

View file

@ -6,10 +6,11 @@ from re import match
from evennia import CmdSet
from evennia.utils import logger, delay, int2str, search
from commands.command import Command
from typeclasses.puzzles import StoryCube
from typeclasses.scripts import Script
from typeclasses.objects import Object
from commands.command import Command
from utils.scoring import Scores
PORT = "Lazy Dock"
PORT_EXIT = "dock"
@ -70,6 +71,7 @@ class CallingHorn(Object):
blower.announce_action("$You() $conj(blow) $pron(your) horn "
"and $conj(create) a long, resonating "
"sound echoing over the water.")
blower.score(Scores.blow_horn)
script_results = search.scripts("sailing")
if script_results:

View file

@ -2,6 +2,7 @@
from typeclasses.objects import Object
from commands.sittables import CmdSetSit
from utils.scoring import Scores
from utils.word_list import routput
@ -50,6 +51,7 @@ class Sittable(Object):
self.db.sitter = sitter
sitter.db.is_sitting = self
sitter.score(Scores.moss_sit)
sitter.announce_action(f"$You() $conj(sit) {adjective} {article} {self.key}.")
sitter.location.other_sit(sitter)

View file

@ -23,6 +23,7 @@ from commands.misc import (CmdSetPuddle,
from commands.consumables import CmdSetMakeConsumable
from commands.wizards import CmdSetWand
from utils.word_list import routput, choices, paragraph
from utils.scoring import Scores
from typeclasses.consumables import Litterable
from typeclasses.objects import Object
from typeclasses.scripts import KnockScript
@ -339,6 +340,7 @@ class Stick(Object):
self.cmdset.add_default(CmdSetStick)
def do_throw(self, thrower):
thrower.score(Scores.throw_stick)
beast = thrower.location.search('beast')
if beast and beast.db.is_awake:
beast.thrown_stick(thrower)
@ -369,6 +371,7 @@ class Puddle(Object):
self.cmdset.add_default(CmdSetPuddle)
def do_jump(self, player):
player.score(Scores.jump)
player.db.jumped_times = (player.db.jumped_times or 0) + 1
if player.db.jumped_times == 1:
player.msg("You jump in the puddle! "

24
utils/scoring.py Executable file
View file

@ -0,0 +1,24 @@
#!/usr/bin/env python
from enum import Enum
class Scores(Enum):
"""
Keep track of the scores we can do.
"""
jump = 2**0
throw_stick = 2**1
catch_fish = 2**2
feed_bhb = 2**3
blow_horn = 2**4
make_tea = 2**5
read_a_book = 2**6
eat_candy = 2**7
get_pipe = 2**8
get_blue_medal = 2**9
get_cocktail = 2**10
finish_cocktail = 2**11
make_potion = 2**12
sign_guest_book = 2**13
moss_sit = 2**14