Rooms can trigger actions like Puppets

Including sitting and standing.
This commit is contained in:
Howard Abrams 2025-08-16 22:21:25 -07:00
parent 989b3210a9
commit ec1881a7cf
8 changed files with 154 additions and 92 deletions

View file

@ -126,7 +126,7 @@ class CmdSmoke(MuxCommand):
'smoke_msg'.
"""
key = "smoke"
aliases = ['puff', 'blow']
aliases = ['puff']
locks = "cmd:holds()"
def func(self):

View file

@ -411,11 +411,19 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
else:
logger.warn(f"Found tethered, {thing} to #{to}, but that needs to be 'tethered:id(num) where num is the ID# of an object/place.")
# Tell the room and any puppets here that we are leaving:
self.location.other_leave(self)
for puppet in self.puppets_here():
puppet.other_leave(self)
return super().at_pre_move(destination)
def at_post_move(self, past_location, move_type="move", **kwargs):
super().at_post_move(past_location, move_type)
self.location.other_arrive(self)
for puppet in self.puppets_here():
puppet.other_arrive(self)
def at_pre_say(self, message, **kwargs):
"While we could/should do 'at_say', this should be easier."
self.db.tutorstate = (self.db.tutorstate or 0) | TutorialState.SAY.value
@ -530,13 +538,9 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
for puppet in self.location.contents
if puppet.is_typeclass("typeclasses.puppets.Puppet")
or puppet.is_typeclass("typeclasses.puzzles.StoryCube")
or puppet.is_typeclass("typeclasses.pets.FriendlyPet")
]
def at_post_move(self, past_location, move_type="move", **kwargs):
super().at_post_move(past_location, move_type)
for puppet in self.puppets_here():
puppet.other_arrive(self)
def deeper_search(self, item_str):
targets = self.search(item_str, quiet=True)
if len(targets) > 0:

View file

@ -103,6 +103,62 @@ class ObjectParent:
tags=tags,
)
def delay_sequence(self, sequence_str, time_delay=1, *args):
"""Run a sequence of messages or commands with a delay.
The 'sequence_str' is a number of commands separated by ';;'
character sequence. The command can by standard things like
'say' or 'give', but can be a message delivered to the room
location, if it begins with a # or 'gm'.
The command can also be a number, in which case, the next
command (and all subsequent commands), will be delayed by that
number of seconds.
For instance:
'say Hello there, want a drink? ;; 8 ;; # He works on shaking a cocktail. ;; 2 ;; shake whisky = avatar'
Which could show:
Blonde elf says, "Hello there, want a drink?"
<8 seconds pass>
He works on shaking a cocktail.
<2 seconds pass>
You now have a whisky.
"""
def convert(x):
try:
return int(x)
except ValueError:
return x
if self.ndb.current_sequence and self.ndb.current_sequence == sequence_str:
logger.info("Duplicate sequences. Ignoring.")
return
self.ndb.current_sequence = sequence_str
lines = [convert(line) for line in split(r" *;; *", sequence_str)]
pause = 0
for line in lines:
if isinstance(line, int):
time_delay = line
else:
pause = pause + time_delay
m = match(r"(^# *|^gm +)(.*)", line)
if m:
logger.info(f"GM'd: {m.group(2)}")
if self.location:
delay(pause, self.location.msg_contents, routput(m.group(2), *args))
else:
delay(pause, self.msg_contents, routput(m.group(2), *args))
else:
cmd = routput(line, *args)
delay(pause, self.do_cmd, cmd)
delay(pause, self.nattributes.remove, "current_sequence")
class Object(ObjectParent, ContribRPObject):
"""
@ -294,59 +350,6 @@ class Object(ObjectParent, ContribRPObject):
at_desc(looker=None)
"""
def delay_sequence(self, sequence_str, time_delay=1, *args):
"""Run a sequence of messages or commands with a delay.
The 'sequence_str' is a number of commands separated by ';;'
character sequence. The command can by standard things like
'say' or 'give', but can be a message delivered to the room
location, if it begins with a # or 'gm'.
The command can also be a number, in which case, the next
command (and all subsequent commands), will be delayed by that
number of seconds.
For instance:
'say Hello there, want a drink? ;; 8 ;; # He works on shaking a cocktail. ;; 2 ;; shake whisky = avatar'
Which could show:
Blonde elf says, "Hello there, want a drink?"
<8 seconds pass>
He works on shaking a cocktail.
<2 seconds pass>
You now have a whisky.
"""
def convert(x):
try:
return int(x)
except ValueError:
return x
if self.ndb.current_sequence and self.ndb.current_sequence == sequence_str:
logger.info("Duplicate sequences. Ignoring.")
return
self.ndb.current_sequence = sequence_str
lines = [convert(line) for line in split(r" *;; *", sequence_str)]
pause = 0
for line in lines:
if isinstance(line, int):
time_delay = line
else:
pause = pause + time_delay
m = match(r"(^# *|^gm +)(.*)", line)
if m:
logger.info(f"GM'd: {m.group(2)}")
delay(pause, self.location.msg_contents, routput(m.group(2), *args))
else:
cmd = routput(line, *args)
delay(pause, self.do_cmd, cmd)
delay(pause, self.nattributes.remove, "current_sequence")
def at_post_move(self, source_location, move_type="move", **kwargs):
"""
Delete ourselves if we are living inside a pet.
@ -436,11 +439,11 @@ class Listener:
self.attributes.get(key=label, category=name) or
self.attributes.get(key=label))
def move_triggers(self, label, character):
def do_trigger(self, action, character):
"""
Return a list of triggers matching 'label' and 'character'.
Return a list of triggers matching 'action' and 'character'.
"""
seq = self.get_attribute_trigger(label, character)
seq = self.get_attribute_trigger(action, character)
if seq:
self.trigger_sequence(seq, character)
@ -448,13 +451,25 @@ class Listener:
"""
Execute a command when a character arrives in the same location.
"""
self.move_triggers('arrive', character)
self.do_trigger('arrive', character)
def other_leave(self, character):
"""
Execute a command when a character arrives in the same location.
"""
self.move_triggers('leave', character)
self.do_trigger('leave', character)
def other_sit(self, character):
"""
Execute a command when a character sits on something in a location.
"""
self.do_trigger('sit', character)
def other_stand(self, character):
"""
Execute a command when a character stands in a location.
"""
self.do_trigger('stand', character)
def say_triggers(self, label, character, speech):
trigs = self.get_attribute_trigger(label, character)
@ -514,6 +529,19 @@ class Listener:
self.do_gift(m.group(3), m.group(1), m.group(5), m.group(7))
return
m = match(r"coin_all ([0-9]+)", cmd)
if m:
for c in self.characters_here(puppets=True):
c.adjust_coins(m.group(1))
return
m = match(r"coin ([0-9]+) *?( to|=)? *([^:]+)", cmd)
if m:
c = self.search(m.group(3))
if c:
c.adjust_coins(m.group(1))
return
if self.is_typeclass("typeclasses.characters.Character"):
self.execute_cmd(cmd)
else:
@ -558,9 +586,7 @@ class Listener:
'dice': {"typeclass": "typeclasses.things.Dice",
"key": name or "pair of dice",
"desc": desc or "Two bone knuckles with painted dots.",
"attr": {
"number": 2
}
"attr": {"number": 2}
},
'pipe': {"typeclass": "typeclasses.things.Pipe",
"key": name or gift,

View file

@ -10,7 +10,7 @@ Each level of pet requires more aspects for interaction.
"""
from enum import Enum
from re import sub
from re import sub, split
from time import time
import random
@ -397,31 +397,38 @@ class Friendly(Pet):
self.do_action()
def do_action(self):
# Do something based on the highest friendly level is the same
# area!
"""Do something based on the highest friendly level is the
same area.
"""
def choose_action(actions):
num = len(split(r" *;; *", actions))
last = self.db.last_actions
msg = choices(actions)
if msg and msg not in last:
self.db.last_actions.append(msg)
limit_num = -num // 2
self.db.last_actions = self.db.last_actions[limit_num:]
return msg
msg = None
(level, chars) = self.friendly_reaction()
if chars and len(chars) > 0:
focus = random.choice(chars)
if level == Reaction.SCARED:
msg = choices(self.db.scared_actions)
msg = choose_action(self.db.scared_actions)
elif level == Reaction.CONCERNED:
msg = choices(self.db.concerned_actions)
msg = choose_action(self.db.concerned_actions)
elif level == Reaction.INTERESTED:
msg = choices(self.db.interested_actions)
msg = choose_action(self.db.interested_actions)
elif level == Reaction.FRIENDLY:
msg = choices(self.db.friendly_actions)
msg = choose_action(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)
if msg and msg not in self.db.last_actions:
self.db.last_actions.append(msg)
# Limit this list so it doesn't grow too large:
self.db.last_actions = self.db.last_actions[-5:]
focus.announce_action(msg, focus)
msg = choose_action(self.db.ecstatic_actions or self.db.friendly_actions)
if focus and msg:
focus.announce_action(msg)
def pet_response(self, petter):
"""

View file

@ -16,7 +16,7 @@ from evennia.contrib.rpg.rpsystem import ContribRPRoom
from django.conf import settings
from typeclasses.pets import Hunger
from typeclasses.objects import ObjectParent
from typeclasses.objects import ObjectParent, Listener
from utils.word_list import fix_msg
_SEARCH_AT_RESULT = utils.object_from_module(settings.SEARCH_AT_RESULT)
@ -49,7 +49,7 @@ def captialize_characters(characters):
return "\n".join(updated)
class Room(ObjectParent, ExtendedRoom, ContribRPRoom):
class Room(ObjectParent, ExtendedRoom, ContribRPRoom, Listener):
"""
Rooms are like any Object, except their location is None
(which is default). They also use basetype_setup() to

View file

@ -52,6 +52,10 @@ class Sittable(Object):
sitter.db.is_sitting = self
sitter.announce_action(f"$You() $conj(sit) {adjective} {article} {self.key}.")
sitter.location.other_sit(sitter)
for puppet in sitter.puppets_here():
puppet.other_sit(sitter)
def do_stand(self, stander):
"""
Called when trying to stand from this object.
@ -69,6 +73,9 @@ class Sittable(Object):
del stander.db.is_sitting
stander.msg(self.stand_msg())
stander.location.other_stand(stander)
for puppet in stander.puppets_here():
puppet.other_stand(stander)
class Sittables(Sittable):
multiple = True

View file

@ -1037,7 +1037,7 @@ send "create\n"
expectit "making it ready"
send "bottle\n"
expectit "You fill a small vial with your stew."
expectit "You fill a small vial"
send "empty\n"
# Since almost anything can be written, we look for a period

View file

@ -1782,7 +1782,7 @@ py bt = self.search('old lady'); bt.db.pose = 'playing with a deck of cards'
# [[file:../../../projects/mud.org::*Trampoli the Witch][Trampoli the Witch:8]]
@set old lady/arrive = "45 ;; gm You hear someone in the loft up the stairs stirring in their bed. ;; 55 ;; pose looking confused ;; emote The /me wakes up and says, \"What's all this then?\" ;; 5 ;; say Who are you, dearie? ;; 10 ;; say I'd think I have intruders in my home! ;; 1 ;; emote grabs her broom and with a sweeping motion from the stairs, you find yourself flying out the door! ;; teleport {3} = Mellow Marsh ;; 3 ;; gm/#457 You hear a voice coming from the hut, \"Scat!\" ;; pose sleeping in a bed up in the loft"
@set old lady/arrive = "45 ;; gm You hear someone in the loft up the stairs stirring in their bed. ;; 55 ;; pose looking confused ;; emote The /me wakes up and says, \"What's all this then?\" ;; 5 ;; say Who are you, dearie? ;; 10 ;; say I'd think I have an intruder in my home! ;; 1 ;; emote grabs her broom and with a sweeping motion from the stairs, sends her intruder flying out the door! ;; teleport {3} = Mellow Marsh ;; 3 ;; gm/#457 You hear a voice coming from the hut, \"Scat!\" ;; emote heads up the stairs to bed. ;; pose sleeping in a bed up in the loft"
# Trampoli the Witch:8 ends here
# Lazy Dock
@ -2601,6 +2601,15 @@ Someone has set a nice |Ychair|n for viewing.
@set leave/traverse_msg = "You open the door and step outside..."
# Inside:5 ends here
# Add some initial ambience:
# [[file:../../../projects/mud.org::*Inside][Inside:6]]
@set here/arrive = "45 ;; gm The rain << patters ^ chatters ^ drums >> against a window << sill ^ pane >>. Nice to be << indoors ^ inside >>."
# Inside:6 ends here
# Tapestry Story
# The tapestry should tell our /creation myth/.
@ -3005,8 +3014,6 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:1]]
@create/drop wee beastie;Mochi;stoat: typeclasses.pets.WeeBeastie
#
# @typeclass/force/reset wee beastie
# Stoat:1 ends here
@ -3098,7 +3105,7 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:11]]
@set stoat/interested_msg = "<< Currently ^ It is >> curled into a ball on {0} lap."
@set stoat/interested_msg = "<< Currently ^ It is >> curled into a ball asleep."
# Stoat:11 ends here
@ -3116,7 +3123,7 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:13]]
@set stoat/interested_actions = "The << sleepy ^ wee >> beastie << on {0} lap ^ >> let's out an adorable yawn. ;; The << sleepy ^ wee >> beastie << on {0} lap ^ >> uncurls itself, stretches, and recurls again. ;; {0} pets the << sleepy ^ wee >> beastie << on |p lap ^ >> as it snuggles << close ^ >>. ;; You hear a deep purring sounds from the << sleepy ^ wee >> beastie << on {0} lap ^ >>. ;; The << sleepy ^ wee >> beastie twitches in its sleep, as if playing with dream toys."
@set stoat/interested_actions = "The << sleepy ^ wee >> beastie << on $your() lap ^ >> let's out an adorable yawn. ;; The << sleepy ^ wee >> beastie << on $your() lap ^ >> uncurls itself, stretches, and recurls again. ;; $You() $conj(pet) the << sleepy ^ wee >> beastie << on |p lap ^ >> as it snuggles << close ^ >>. ;; You hear a deep purring sounds from the << sleepy ^ wee >> beastie << on $your() lap ^ >>. ;; The << sleepy ^ wee >> beastie twitches in its sleep, as if playing with dream toys."
# Stoat:13 ends here
@ -3125,7 +3132,7 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:14]]
@set stoat/friendly_msg = "<< Currently ^ It is >> curled up and purring on {0} lap."
@set stoat/friendly_msg = "<< Currently ^ It is >> purring while curled up sleep."
# Stoat:14 ends here
@ -3143,18 +3150,29 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:16]]
@set stoat/friendly_actions = "The << sleepy ^ wee >> beastie << on the lap of {0} ^ >> let's out an adorable yawn. ;; The << sleepy ^ wee >> beastie << on the lap of {0} ^ >> uncurls itself, stretches, and recurls again. ;; {0} pets the << sleepy ^ wee >> beastie << on |p lap ^ >> as it snuggles << close ^ >>. ;; You hear a deep purring sounds from the << sleepy ^ wee >> beastie << on the lap of {0} ^ >>. ;; The << sleepy ^ wee >> beastie twitches in its sleep, as if playing with dream toys."
@set stoat/friendly_actions = "The << sleepy ^ wee >> beastie << on $your() lap ^ >> let's out an adorable yawn. ;; The << sleepy ^ wee >> beastie << on $your() lap ^ >> uncurls itself, stretches, and recurls again. ;; $You() $conj(pet) the << sleepy ^ wee >> beastie << on |p lap ^ >> as it snuggles << close ^ >>. ;; You hear a deep purring sounds from the << sleepy ^ wee >> beastie << on $your() lap ^ >>. ;; The << sleepy ^ wee >> beastie twitches in its sleep, as if playing with dream toys."
# Stoat:16 ends here
# An this pet loves his gnome.
# And this pet loves his gnome.
# [[file:../../../projects/mud.org::*Stoat][Stoat:17]]
@set gnome/wee_beastie_friendly_level=500
# Stoat:17 ends here
# Trigger some events:
# [[file:../../../projects/mud.org::*Stoat][Stoat:18]]
@set stoat/arrive = "5 ;; gm A sleeping << wee ^ >> beastie, opens one curious eye to see who arrived."
#
# @set stoat/sit = "3 ;; gm A sleeping << wee ^ >> beastie, opens one curious eye to see who arrived."
# Stoat:18 ends here
# Kitchen
# Better place to put the tea and scones.