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'. 'smoke_msg'.
""" """
key = "smoke" key = "smoke"
aliases = ['puff', 'blow'] aliases = ['puff']
locks = "cmd:holds()" locks = "cmd:holds()"
def func(self): def func(self):

View file

@ -411,11 +411,19 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
else: 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.") 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(): for puppet in self.puppets_here():
puppet.other_leave(self) puppet.other_leave(self)
return super().at_pre_move(destination) 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): def at_pre_say(self, message, **kwargs):
"While we could/should do 'at_say', this should be easier." "While we could/should do 'at_say', this should be easier."
self.db.tutorstate = (self.db.tutorstate or 0) | TutorialState.SAY.value 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 for puppet in self.location.contents
if puppet.is_typeclass("typeclasses.puppets.Puppet") if puppet.is_typeclass("typeclasses.puppets.Puppet")
or puppet.is_typeclass("typeclasses.puzzles.StoryCube") 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): def deeper_search(self, item_str):
targets = self.search(item_str, quiet=True) targets = self.search(item_str, quiet=True)
if len(targets) > 0: if len(targets) > 0:

View file

@ -103,6 +103,62 @@ class ObjectParent:
tags=tags, 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): class Object(ObjectParent, ContribRPObject):
""" """
@ -294,59 +350,6 @@ class Object(ObjectParent, ContribRPObject):
at_desc(looker=None) 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): def at_post_move(self, source_location, move_type="move", **kwargs):
""" """
Delete ourselves if we are living inside a pet. 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, category=name) or
self.attributes.get(key=label)) 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: if seq:
self.trigger_sequence(seq, character) self.trigger_sequence(seq, character)
@ -448,13 +451,25 @@ class Listener:
""" """
Execute a command when a character arrives in the same location. 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): def other_leave(self, character):
""" """
Execute a command when a character arrives in the same location. 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): def say_triggers(self, label, character, speech):
trigs = self.get_attribute_trigger(label, character) 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)) self.do_gift(m.group(3), m.group(1), m.group(5), m.group(7))
return 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"): if self.is_typeclass("typeclasses.characters.Character"):
self.execute_cmd(cmd) self.execute_cmd(cmd)
else: else:
@ -558,9 +586,7 @@ class Listener:
'dice': {"typeclass": "typeclasses.things.Dice", 'dice': {"typeclass": "typeclasses.things.Dice",
"key": name or "pair of dice", "key": name or "pair of dice",
"desc": desc or "Two bone knuckles with painted dots.", "desc": desc or "Two bone knuckles with painted dots.",
"attr": { "attr": {"number": 2}
"number": 2
}
}, },
'pipe': {"typeclass": "typeclasses.things.Pipe", 'pipe': {"typeclass": "typeclasses.things.Pipe",
"key": name or gift, "key": name or gift,

View file

@ -10,7 +10,7 @@ Each level of pet requires more aspects for interaction.
""" """
from enum import Enum from enum import Enum
from re import sub from re import sub, split
from time import time from time import time
import random import random
@ -397,31 +397,38 @@ class Friendly(Pet):
self.do_action() self.do_action()
def do_action(self): def do_action(self):
# Do something based on the highest friendly level is the same """Do something based on the highest friendly level is the
# area! 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 msg = None
(level, chars) = self.friendly_reaction() (level, chars) = self.friendly_reaction()
if chars and len(chars) > 0: if chars and len(chars) > 0:
focus = random.choice(chars) focus = random.choice(chars)
if level == Reaction.SCARED: if level == Reaction.SCARED:
msg = choices(self.db.scared_actions) msg = choose_action(self.db.scared_actions)
elif level == Reaction.CONCERNED: elif level == Reaction.CONCERNED:
msg = choices(self.db.concerned_actions) msg = choose_action(self.db.concerned_actions)
elif level == Reaction.INTERESTED: elif level == Reaction.INTERESTED:
msg = choices(self.db.interested_actions) msg = choose_action(self.db.interested_actions)
elif level == Reaction.FRIENDLY: elif level == Reaction.FRIENDLY:
msg = choices(self.db.friendly_actions) msg = choose_action(self.db.friendly_actions)
else: else:
# If we have an ecstatic message, use it otherwise, # If we have an ecstatic message, use it otherwise,
# grab the friendly: # grab the friendly:
msg = choices(self.db.ecstatic_actions or self.db.friendly_actions) msg = choose_action(self.db.ecstatic_actions or self.db.friendly_actions)
if focus and msg:
if msg and msg not in self.db.last_actions: focus.announce_action(msg)
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)
def pet_response(self, petter): def pet_response(self, petter):
""" """

View file

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

View file

@ -52,6 +52,10 @@ class Sittable(Object):
sitter.db.is_sitting = self sitter.db.is_sitting = self
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)
for puppet in sitter.puppets_here():
puppet.other_sit(sitter)
def do_stand(self, stander): def do_stand(self, stander):
""" """
Called when trying to stand from this object. Called when trying to stand from this object.
@ -69,6 +73,9 @@ class Sittable(Object):
del stander.db.is_sitting del stander.db.is_sitting
stander.msg(self.stand_msg()) stander.msg(self.stand_msg())
stander.location.other_stand(stander)
for puppet in stander.puppets_here():
puppet.other_stand(stander)
class Sittables(Sittable): class Sittables(Sittable):
multiple = True multiple = True

View file

@ -1037,7 +1037,7 @@ send "create\n"
expectit "making it ready" expectit "making it ready"
send "bottle\n" send "bottle\n"
expectit "You fill a small vial with your stew." expectit "You fill a small vial"
send "empty\n" send "empty\n"
# Since almost anything can be written, we look for a period # 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]] # [[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 # Trampoli the Witch:8 ends here
# Lazy Dock # 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..." @set leave/traverse_msg = "You open the door and step outside..."
# Inside:5 ends here # 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 # Tapestry Story
# The tapestry should tell our /creation myth/. # The tapestry should tell our /creation myth/.
@ -3005,8 +3014,6 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:1]] # [[file:../../../projects/mud.org::*Stoat][Stoat:1]]
@create/drop wee beastie;Mochi;stoat: typeclasses.pets.WeeBeastie @create/drop wee beastie;Mochi;stoat: typeclasses.pets.WeeBeastie
#
# @typeclass/force/reset wee beastie
# Stoat:1 ends here # Stoat:1 ends here
@ -3098,7 +3105,7 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:11]] # [[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 # Stoat:11 ends here
@ -3116,7 +3123,7 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:13]] # [[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 # Stoat:13 ends here
@ -3125,7 +3132,7 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:14]] # [[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 # Stoat:14 ends here
@ -3143,18 +3150,29 @@ pose gnome/default = smoking his pipe
# [[file:../../../projects/mud.org::*Stoat][Stoat:16]] # [[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 # Stoat:16 ends here
# An this pet loves his gnome. # And this pet loves his gnome.
# [[file:../../../projects/mud.org::*Stoat][Stoat:17]] # [[file:../../../projects/mud.org::*Stoat][Stoat:17]]
@set gnome/wee_beastie_friendly_level=500 @set gnome/wee_beastie_friendly_level=500
# Stoat:17 ends here # 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 # Kitchen
# Better place to put the tea and scones. # Better place to put the tea and scones.