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, CmdWhisper, CmdRead, CmdEat, CmdDrink,
CmdUse, CmdPush, CmdPull, CmdUse, CmdPush, CmdPull,
CmdOpen, CmdClose, CmdFeed) CmdOpen, CmdClose, CmdFeed)
from commands.scoring import CmdScore
from commands.hint import CmdHint from commands.hint import CmdHint
from commands.misc import CmdLight from commands.misc import CmdLight
from commands.wizards import (CmdGM, CmdSpell, CmdGMTrigger, from commands.wizards import (CmdGM, CmdSpell, CmdGMTrigger,
@ -70,6 +71,7 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
self.add(CmdMakeCocktail) self.add(CmdMakeCocktail)
self.add(CmdGift) self.add(CmdGift)
self.add(CmdFeed) self.add(CmdFeed)
self.add(CmdScore)
class AccountCmdSet(default_cmds.AccountCmdSet): 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.objects import Object
from typeclasses.scripts import Script from typeclasses.scripts import Script
from typeclasses.drinkables import Container from typeclasses.drinkables import Container
from utils.scoring import Scores
class CmdEmpty(Command): class CmdEmpty(Command):
""" """
@ -241,6 +241,7 @@ class Cauldron(Object):
seq, brew_func = self.can_create_laughter() seq, brew_func = self.can_create_laughter()
if seq: if seq:
maker.score(Scores.make_potion)
maker.announce_action(good) maker.announce_action(good)
for idx, msg in enumerate(seq): for idx, msg in enumerate(seq):
delay(idx * 3 + 2, maker.announce_action, msg) delay(idx * 3 + 2, maker.announce_action, msg)

View file

@ -105,6 +105,14 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
if obj.is_typeclass(typeclass): if obj.is_typeclass(typeclass):
obj.delete() 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): def new_account_setup(self):
""" """
New accounts should connect the tutorial. New accounts should connect the tutorial.
@ -137,6 +145,7 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
# Reset the "running state" that character may have # Reset the "running state" that character may have
# experienced. This way, new guests get to _re-experience_ it: # experienced. This way, new guests get to _re-experience_ it:
self.db.score = 0
self.db.visited = False self.db.visited = False
self.db.jumped_times = 0 self.db.jumped_times = 0
self.db.tutorstate = 0 self.db.tutorstate = 0

View file

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

View file

@ -15,6 +15,7 @@ from urllib.request import urlopen
from typeclasses.objects import Object from typeclasses.objects import Object
from typeclasses.characters import Character from typeclasses.characters import Character
from typeclasses.npcs import CarriableNPC from typeclasses.npcs import CarriableNPC
from utils.scoring import Scores
from utils.word_list import routput from utils.word_list import routput
import random import random
@ -238,3 +239,4 @@ class FishingPole(Object):
})[0] })[0]
fish.location = fisher fish.location = fisher
fisher.msg(f"You caught a fish!") 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 import delay, logger
from evennia.utils.search import search_object from evennia.utils.search import search_object
from utils.scoring import Scores
from utils.word_list import routput from utils.word_list import routput
@ -562,6 +563,21 @@ class Listener:
c.adjust_coins(int(m.group(1))) c.adjust_coins(int(m.group(1)))
return 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"): if self.is_typeclass("typeclasses.characters.Character"):
self.execute_cmd(cmd) self.execute_cmd(cmd)
else: else:

View file

@ -23,6 +23,7 @@ from evennia.utils.search import search_object
from typeclasses.objects import Object from typeclasses.objects import Object
from typeclasses.characters import Character from typeclasses.characters import Character
from commands.pets import CmdPetSet from commands.pets import CmdPetSet
from utils.scoring import Scores
from utils.word_list import squish, choices 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}." 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) self.adjust_character(feeder, 100)
feeder.has('scone').delete() feeder.has('scone').delete()
feeder.score(Scores.feed_bhb)
elif is_berry(item): 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}." 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) self.adjust_character(feeder, 40)
feeder.has('berries').delete() feeder.has('berries').delete()
feeder.score(Scores.feed_bhb)
else: else:
msg = f"{noun} doesn't appear interested in anything you have." 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.objects import Object
from typeclasses.consumables import Producer from typeclasses.consumables import Producer
from utils.scoring import Scores
from utils.user_info import location from utils.user_info import location
from utils.word_list import routput from utils.word_list import routput
@ -135,6 +136,7 @@ class WriteableBook(Book):
self.db.post_write_msg or self.db.post_write_msg or
"$You() $conj(put) down the quill, and $conj(stop) writing.") "$You() $conj(put) down the quill, and $conj(stop) writing.")
writer.score(Scores.sign_guest_book)
class Journal(WriteableBook): class Journal(WriteableBook):
""" """
@ -315,6 +317,7 @@ class Bookshelf(Producer):
book.location = reader book.location = reader
reader.msg(f"You pick up {filler} book, |w{title}|n.") 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_title = None
self.db.last_desc = None self.db.last_desc = None

View file

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

View file

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

View file

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