moss-n-puddles/typeclasses/fishing.py
Howard Abrams 43a1aefb49 Add a Scoring system to character
As character do things, they can now get a "score" for everything I
think could be interesting/challenging.
2025-08-24 22:34:11 -07:00

243 lines
8.1 KiB
Python
Executable file

#!/usr/bin/env python
from evennia import (
Command,
CmdSet,
TICKER_HANDLER,
syscmdkeys,
create_script,
)
from evennia.prototypes.spawner import spawn
from enum import Enum
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
import requests
class Fishing(Enum):
"The acceptable states of fishing."
CAST = True
REEL = False
class CmdThrowFish(Command):
"""
Throw the fish, if you are holding one, back in the water.
A helper can escort the fish to the nearest body of water if you aren't adjacent to one.
"""
key = "throw"
locks = "holds(fish)"
def func(self):
self.obj.do_delete(self.caller)
class CmdSetFish(CmdSet):
def at_cmdset_creation(self):
self.add(CmdThrowFish)
class CmdCast(Command):
"""
Cast the pole.
"""
key = "cast"
locks = "holds(pole)"
def func(self):
self.obj.do_cast(self.caller)
class CmdReel(Command):
"""
Reel the pole.
"""
key = "reel"
locks = "holds(pole)"
def func(self):
self.obj.do_reel(self.caller)
class CmdSetFishing(CmdSet):
def at_cmdset_creation(self):
self.add(CmdCast)
self.add(CmdReel)
class Fish(CarriableNPC):
"""
Everyone wants a fish that tells dad jokes, right!?
"""
# The number of seconds to check for the time for a message:
fish_tick = 3
def at_object_creation(self):
self.cmdset.add_default(CmdSetFish)
self.db.name = Fish.get_name()
self.db.spoken = 0
TICKER_HANDLER.add(interval=self.fish_tick,
callback=self.do_speak)
def at_heard_say(self, message, from_obj):
"""
A simple listener and response. This makes it easy to change for
subclasses of NPCs reacting differently to says.
"""
return "say", "What was that? I must have water in my ear."
def at_say(self, message):
"""
Spoken out loud to the world, even if owned.
"""
owner = self.location
if owner.is_typeclass(Character) and owner.is_connected:
self.location.announce_action(f"$Your() fish says, \"{message}\"")
def do_speak(self):
"""
Called at a repeatable sequence by the ticker, and
it calls at_say() in order to do a type of monologue.
"""
if self.db.spoken == 1:
self.at_say("Whew! Thanks for removing the sharp hook. Not sure how I got stuck on that.")
elif self.db.spoken == 5:
self.at_say(f"My name's {self.db.name}. What's yours?")
self.db.desc = f"{self.db.name}. {self.db.desc}"
elif self.db.spoken == 10:
self.at_say("Did you say something? I think I have water in my ears, as I can't hear a thing.")
elif self.db.spoken == 20:
self.at_say("So... how're you getting along?")
elif self.db.spoken == 30:
self.at_say("Right, right. Still can't hear. Hrm.")
elif self.db.spoken == 60:
self.at_say("Do you know why we fish are so easy to weigh? ")
elif self.db.spoken == 63:
self.at_say("Because we have are own scales.")
elif self.db.spoken == 70:
self.at_say("I suppose you could |gthrow|n me back in the water at any time.")
elif self.db.spoken == 120:
self.at_say("I suppose I should pay you back for helping me out with that hook thing. I guess you know I keep all my money at ... the riverbank.")
elif self.db.spoken == 200:
self.at_say("You know the easiest way to catch a fish, right?")
elif self.db.spoken == 201:
self.at_say("Have someone toss it to you.")
elif self.db.spoken == 205:
self.at_say("Ouch. Tough crowd.")
elif self.db.spoken == 300:
self.at_say("Me and my friends started a musical band.")
elif self.db.spoken == 301:
self.at_say("We all play bass.")
elif self.db.spoken == 302:
self.at_say("Alright guys, I said, drop the instruments. We are singing aquapella.")
elif self.db.spoken == 400:
self.at_say("Did you meet the owners of that new fishing store?")
elif self.db.spoken == 401:
self.at_say("Their names are Rod and Annette.")
elif self.db.spoken == 800:
self.at_say("I'm not that smart.")
elif self.db.spoken == 801:
self.at_say("My friends tell me I'm a dumb bass.")
elif self.db.spoken == 803:
self.at_say("Sorry for all the puns. I feel so GILL-ty.")
# Let's attempt to tell a bad joke once a day:
elif self.db.spoken > 1000 and self.db.spoken % (24 * 60 * 60 / self.fish_tick) == 0:
self.at_say(get_joke())
self.db.spoken += 1
def do_delete(self, fisher):
"""
A visual way to delete the fish.
"""
if fisher.location == fisher.search("Lazy Dock", quiet=True):
fisher.msg(routput("You <<toss ^ heave ^ throw>> the fish back into the <<water ^ sea>>."))
fisher.msg(routput("The fish says, \"Bye for now. If you want to talk again, just drop me a line!\""))
else:
fisher.msg(routput("You <<toss ^ heave ^ throw>> the fish, and an << eagle ^ hawk >> swoops << down ^ >> and snatches it. I'm sure it will carry the fish back to the sea for you."))
self.delete()
def get_name():
return random.choice([
"Bennie", "Flipper", "Finegan", "Count Bassie"
])
def get_desc():
return routput(random.choice([
"A walleye with big bulbous eyes that clearly doesn't get no respect.",
"A bass with amazing neck confidence giving it a most excellent head bob.",
"A rainbow trout missing the <<red ^ yellow ^ green ^ blue>> from its iridescent stripe.",
"A brown trout colored <<red ^ blue ^ purple ^ orange>>.",
# "A spiny perch",
# "A salmon",
# "A pike",
]))
def get_joke():
"Fetch a random joke from the internet."
r = requests.get("https://icanhazdadjoke.com/",
headers={'Accept':'text/plain'})
return r.text
class FishingPole(Object):
"""
Can produce a Fish.
"""
failure_msgs = [
"You reel in an empty line.",
"You didn't catch anything.",
"Caught nothing but a bit of weeds, yeck.",
"Caught nothing, but this sure is enjoyable.",
"Did you catch a boot? Nah, it isn't even that good.",
"Anything better that sitting on the dock of the bay?",
]
def at_object_creation(self):
self.cmdset.add_default(CmdSetFishing)
def do_cast(self, fisher):
if fisher.location.key != "Lazy Dock":
fisher.msg("You can't do that without being near the water.")
elif fisher.db.fishing == Fishing.CAST:
fisher.msg("You need to reel the line in first.")
else:
fisher.db.fishing = Fishing.CAST
fisher.msg(routput(random.choice([
"You cast out far into the <<water ^ sea>>.",
"You cast close to the <<dock ^ shore>>.",
"You cast off to the <<right ^ left>> where you <<think you ^ >> see a dark pocket.",
])))
def do_reel(self, fisher):
if fisher.db.fishing != Fishing.CAST:
fisher.msg("You need to |gcast|n before you can reel the line back in.")
else:
fisher.db.fishing = Fishing.REEL
if random.randint(1, 100) < 35:
self.give_fish(fisher)
else:
fisher.msg(random.choice(self.failure_msgs))
def give_fish(self, fisher):
fish = spawn({
"typeclass": self.db.make_class or "typeclasses.fishing.Fish",
"key": "fish",
"aliases": [Fish.get_name()],
"desc": Fish.get_desc(),
})[0]
fish.location = fisher
fisher.msg(f"You caught a fish!")
fisher.score(Scores.catch_fish)