From ec1881a7cf04c72aa04e916f641e34c232968927 Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Sat, 16 Aug 2025 22:21:25 -0700 Subject: [PATCH] Rooms can trigger actions like Puppets Including sitting and standing. --- commands/misc.py | 2 +- typeclasses/characters.py | 14 ++-- typeclasses/objects.py | 148 ++++++++++++++++++++++---------------- typeclasses/pets.py | 35 +++++---- typeclasses/rooms.py | 4 +- typeclasses/sittables.py | 7 ++ world/test-run.exp | 2 +- world/version1.ev | 34 ++++++--- 8 files changed, 154 insertions(+), 92 deletions(-) diff --git a/commands/misc.py b/commands/misc.py index 8fd142e..adc862a 100755 --- a/commands/misc.py +++ b/commands/misc.py @@ -126,7 +126,7 @@ class CmdSmoke(MuxCommand): 'smoke_msg'. """ key = "smoke" - aliases = ['puff', 'blow'] + aliases = ['puff'] locks = "cmd:holds()" def func(self): diff --git a/typeclasses/characters.py b/typeclasses/characters.py index 90adb98..a7ab2e4 100644 --- a/typeclasses/characters.py +++ b/typeclasses/characters.py @@ -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: diff --git a/typeclasses/objects.py b/typeclasses/objects.py index 8ac2658..3db0a30 100755 --- a/typeclasses/objects.py +++ b/typeclasses/objects.py @@ -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, diff --git a/typeclasses/pets.py b/typeclasses/pets.py index 99fe2f0..6b368e3 100755 --- a/typeclasses/pets.py +++ b/typeclasses/pets.py @@ -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): """ diff --git a/typeclasses/rooms.py b/typeclasses/rooms.py index 329fa46..e00f325 100644 --- a/typeclasses/rooms.py +++ b/typeclasses/rooms.py @@ -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 diff --git a/typeclasses/sittables.py b/typeclasses/sittables.py index 6ae8650..89b5a49 100755 --- a/typeclasses/sittables.py +++ b/typeclasses/sittables.py @@ -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 diff --git a/world/test-run.exp b/world/test-run.exp index 80ac22e..83fcf6c 100755 --- a/world/test-run.exp +++ b/world/test-run.exp @@ -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 diff --git a/world/version1.ev b/world/version1.ev index 7424301..56b10d4 100644 --- a/world/version1.ev +++ b/world/version1.ev @@ -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.