Create a friendly pet

This commit is contained in:
Howard Abrams 2025-04-07 22:45:45 -07:00
parent c731383915
commit 43956fa818

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python
# Emacs environement
# (setq python-shell-interpreter "/Users/howard/src/moss-n-puddles/.venv/bin/ipython")
"""
Pets
@ -194,5 +196,242 @@ class Fire(Pet):
# def at_post_move(self, source_location, move_type, **kwargs):
self.update_hunger(feeder=feeder, amount=300)
# ----------------------------------------------------------------------
# Friendly
class Reaction(Enum):
SCARED = 1
CONCERNED = 100
INTERESTED = 300
FRIENDLY = 850
ECSTATIC = 1000
class Friendly(Pet):
"""
This pet keeps track of the characters in the game.
It has different reactions based on the characters in the room.
"""
def at_object_creation(self):
"""
Called when object is first created.
"""
super().at_object_creation()
# The higher this value the more "spammy" a pet is in making
# comments in the room:
self.db.active_amount = 3
# If set to 'ignores', the pet is more concerned about the
# 'friendliest' character in Room, otherwise, it is more
# scared at the stranger:
self.db.new_character_reaction = "ignores"
@property
def friendly_var(self):
"""
Return variable name on character types use to gauge the
reaction of this pet towards that character.
"""
key = self.key.replace(" ", "_")
return f"{key}_friendly_level"
def friendly_level(self, character):
"""
Return reaction level of this pet towards a character.
"""
varname = self.friendly_var
return character.attributes.get(varname) or 0
def friendly_reaction(self, character=None):
"""
Return reaction enum of this pet towards a character.
If character not given, then looks at all characters in the room
"""
if character:
level = self.friendly_level(character)
if level < Reaction.SCARED.value:
return Reaction.SCARED
if level < Reaction.CONCERNED.value:
return Reaction.CONCERNED
if level < Reaction.INTERESTED.value:
return Reaction.INTERESTED
if level < Reaction.FRIENDLY.value:
return Reaction.FRIENDLY
return Reaction.ECSTATIC
else:
if self.db.new_character_reaction == "ignores":
return self.highest_friendly_reaction()
return self.lowest_friendly_reaction()
@property
def local_characters(self):
"""
Return a list of all Characters in the room with the Pet.
"""
return [c for c in self.location.contents
if c.is_typeclass("typeclasses.characters.Character")]
def lowest_friendly_reaction(self):
"""
Return reaction of this pet to the least friendliest
character(s) in the area (room) that this pet resides.
Returns a tuple, Reaction and a list of characters.
"""
# State is a tuple of the level and the character:
level = Reaction.ECSTATIC
characters = []
for c in self.local_characters:
this_level = self.friendly_reaction(c)
if this_level.value < level.value:
level = this_level
characters = [c]
if this_level.value == level.value:
characters += [c]
return (level, characters)
def highest_friendly_reaction(self):
"""
Return reaction of this pet to the friendliest
character(s) in the area (room) that this pet resides.
Returns a tuple, Reaction and a list of characters.
"""
# State is a tuple of the level and the character:
level = Reaction.SCARED
characters = []
for c in self.local_characters:
this_level = self.friendly_reaction(c)
if this_level.value > level.value:
level = this_level
characters = [c]
if this_level.value == level.value:
characters += [c]
return (level, characters)
def adjust_all(self, amount):
"""
Adjusts reaction level to all characters that have
interacted with this pet, whether they are near it or not.
This is essentially a loneliness measure, for out-of-sight,
out-of-mind.
"""
for c in Character.objects.get_objs_with_attr(self.friendly_var):
self.adjust_character(c, amount)
def adjust_all_locally(self, amount):
"""
Adjusts reaction level to all characters in the room
with this pet. Hanging out with the pet should be helpful.
"""
for c in self.local_characters:
self.adjust_character(c, amount)
def adjust_character(self, character, amount):
"""
Adjusts the reaction to 'character' by an 'amount.
Note that this should never go below zero.
"""
new_val = self.friendly_level(character) + amount
if new_val < 0:
new_val = 0
character.attributes.add(self.friendly_var, new_val)
def return_appearance(self, looker, **kwargs):
"""
Called by the 'look' command. This formats the description
of this object based on 'reaction', and the character's
_friendly_ level.
Args:
looker (Object): Object doing the looking.
"""
level = self.friendly_reaction(looker)
# looking at the friendly pets makes them nervous... just a little:
self.adjust_character(looker, -1)
if level == Reaction.SCARED:
return self.db.desc + " " + choices(self.db.scared_msg or "It seems scared of you.")
elif level == Reaction.CONCERNED:
return self.db.desc + " " + choices(self.db.concerned_msg or "It seems concerned you are here.")
elif level == Reaction.INTERESTED:
return self.db.desc + " " + choices(self.db.interested_msg or "It seems interested in you.")
elif level == Reaction.FRIENDLY:
return self.db.desc + " " + choices(self.db.friendly_msg or "It seems happy to see you.")
else:
# If we have an ecstatic message, use it otherwise, grab the friendly:
return self.db.desc + " " + choices(self.db.ecstatic_msg or self.db.friendly_msg or
"It seems ecstatic to see you.")
def update_state(self, *args, **kwargs):
"""
Hrm.
"""
super().update_state(*args, **kwargs)
self.adjust_all(self.db.loneliness_amount or -1)
self.adjust_all_locally(self.db.shyness_amount or 5)
# How spammy do we want the pet to be?
if random.randint(0, 100) < self.db.active_amount:
self.do_action()
else:
print("Nope")
def do_action(self):
# Do something based on the highest friendly level is the same area!
(level, chars) = self.friendly_reaction()
focus = random.choice(chars)
if level == Reaction.SCARED:
msg = choices(self.db.scared_actions)
elif level == Reaction.CONCERNED:
msg = choices(self.db.concerned_actions)
elif level == Reaction.INTERESTED:
msg = choices(self.db.interested_actions)
elif level == Reaction.FRIENDLY:
msg = choices(self.db.friendly_actions)
else:
# If we have an ecstatic message, use it otherwise, grab the friendly:
msg = choices(self.db.ecstatic_actions or self.db.friendly_actions)
focus.msg(
sub("<You>", "You", sub("<you>", "you", msg))
)
self.location.msg_contents(sub("<[Yy]ou>", focus.name.title(), msg),
exclude=focus)
class Imp(Friendly):
def action(self):
# Do something based on the highest friendly level is the same area!
# Need a ticker ..
# (state, chars) = self.highest_friendly_level()
pass
def update_state(self, *args, **kwargs):
pass
class BHB(Friendly):
"The Big Hairy Beast object."
def action(self):
# Do something based on the highest friendly level is the same area!
# Need a ticker ..
# (state, chars) = self.highest_friendly_level()
pass
# - < 10 : Attempts to hide
# - < 20 : Steers clear but doesnt hide
# - < 50 : Wags it tail/butt
# - > 50 : Follows you
# level = self.friendly_level(looker)
# if level == 0:
# return f"You see a massive shadow lurking "
# if level < 10:
# return f"{self.db.desc} {self.hunger_appearance()}"
def given(self, giver, thing):
# if obj.is_typeclass("typeclasses.things.Wood"):
return True