コード例 #1
0
 def add_mod(self):
     """Adds a modifier to target"""
     from server.utils.arx_utils import dict_from_choices_field
     choices = dict_from_choices_field(RollModifier, "CHECK_CHOICES")
     try:
         value = int(self.rhslist[0])
         tag_name = self.rhslist[1].lower()
         check = choices[self.rhslist[2].lower()]
     except (IndexError, AttributeError):
         self.msg(
             "You must provide value, tag name, and the type of check.")
     except KeyError:
         self.msg("Not a valid check type: %s" % ", ".join(choices.keys()))
     else:
         targ = self.caller.search(self.lhs)
         if not targ:
             return
         if "targetmod" in self.switches:
             mod = targ.add_modifier(value,
                                     check_type=check,
                                     target_tag=tag_name)
         else:
             mod = targ.add_modifier(value,
                                     check_type=check,
                                     user_tag=tag_name)
         self.msg("You have added a modifier to %s: %s." % (targ, mod))
コード例 #2
0
ファイル: plot_commands.py プロジェクト: hexagoncode/arxcode
 def set_property_for_dompc(self, plot, field, choices_attr):
     """Sets a property for someone involved in a plot"""
     choices = dict_from_choices_field(PCPlotInvolvement, choices_attr)
     try:
         name, choice = self.rhslist
     except (TypeError, ValueError):
         raise CommandError("You must give both a name and a value.")
     choice = choice.lower()
     try:
         choice_value = choices[choice]
     except KeyError:
         keys = [ob[1].lower() for ob in getattr(PCPlotInvolvement, choices_attr)]
         raise CommandError("Choice must be one of: %s." % ", ".join(keys))
     dompc = self.dompc_search(name)
     involvement, _ = plot.dompc_involvement.get_or_create(dompc=dompc)
     setattr(involvement, field, choice_value)
     involvement.save()
     self.msg("You have set %s as a %s in %s." % (dompc, getattr(involvement, "get_%s_display" % field)(), plot))
コード例 #3
0
ファイル: story_actions.py プロジェクト: earthsend/arxcode
class CmdAction(ActionCommandMixin, ArxPlayerCommand):
    """
    A character's story actions that a GM responds to.
    
    Usage:
        @action/newaction [<crisis #>=]<story of action>
        @action/tldr <action #>=<title>
        @action/category <action #>=<category>
        @action/roll <action #>=<stat>,<skill>
        @action/ooc_intent <action #>=<ooc intent, then post-submission questions>
        @action/question <action #>=<ask a question>
        @action/cancel <action #>
        @action/readycheck <action #>
        @action/submit <action #>
    Options:
        @action [<action #>]
        @action/invite <action #>=<character>[,<character2>,...]
        @action/setaction <action #>=<action text>
        @action/setsecret[/traitor] <action #>=<secret action>
        @action/setcrisis <action #>=<crisis #>
        @action/add <action#>=<resource or 'ap' or 'army'>,<amount or army ID#>
        @action/makepublic <action #>
        @action/toggletraitor <action #>
        @action/toggleattend <action #>
        @action/noscene <action #>
        
    Creating /newaction costs Action Points (ap). Requires 'tldr' which is a
    short summary in a few words of the action, a category, stat/skill for
    the dice check (just pick whatever might be plausible, GMs can adjust it
    if they disagree), and /ooc_intent which is the what sort of outcome you
    are hoping for, whether that's reputation adjustments, some advancement
    in a story, the favor of important npcs, magical power, whatever. It's just
    to make sure that GMs know what you have in mind for what you're trying
    to accomplish. Use /submit after all options, when ready for GM review. GMs
    may require more info or ask you to edit with /setaction and /submit again.
    Categories: combat, scouting, support, diplomacy, sabotage, research.
    
    With /invite you ask others to assist your action. They use /setaction or
    /cancel, and require all the same fields except category. A covert action 
    can be added with /setsecret. Optional /traitor (and /toggletraitor) switch
    makes your dice roll detract from goal. With /setcrisis this becomes your 
    response to a Crisis. Allocate resources with /add by specifying a type
    (ap, army, social, silver, etc.) and amount, or the ID# of your army. The 
    /makepublic switch allows everyone to see your action after a GM publishes 
    an outcome. If you prefer offscreen resolution, use /noscene toggle. To
    ask questions for GMs, use /question.
    
    Using /toggleattend switches whether your character is physically present,
    or arranging for the action's occurance in other ways. One action may be 
    attended per crisis update; all others must be passive to represent 
    simultaneous response by everyone involved. Up to 5 attendees are allowed
    per crisis response action, unless it is /noscene.

    Actions are private by default, but there's a small xp reward for marking
    a completed action as public with the /makepublic switch.
    """
    key = "@action"
    locks = "cmd:all()"
    help_category = "Story"
    aliases = ["@actions", "+storyrequest"]
    action_categories = dict_from_choices_field(PlotAction, "CATEGORY_CHOICES")
    requires_draft_switches = ("invite", "setcrisis", "readycheck")
    requires_editable_switches = ("roll", "tldr", "title", "category",
                                  "submit", "invite", "setaction", "setcrisis",
                                  "add", "toggletraitor", "toggleattend",
                                  "ooc_intent", "setsecret")
    requires_unpublished_switches = ("question", "cancel", "noscene")
    requires_owner_switches = ("invite", "makepublic", "category", "setcrisis",
                               "noscene", "readycheck")

    @property
    def dompc(self):
        """Shortcut for getting their dominion playerornpc object"""
        return self.caller.Dominion

    @property
    def actions_and_invites(self):
        """Lists non-cancelled actions for creator and those invited to it"""
        return PlotAction.objects.filter(
            Q(dompc=self.dompc) | Q(assistants=self.dompc)).exclude(
                status=PlotAction.CANCELLED).distinct()

    # noinspection PyUnusedLocal
    def get_help(self, caller, cmdset):
        """Overrides basic help, which defaults to the __doc__ string"""
        msg = self.__doc__
        recent_actions = caller.recent_actions
        max_actions = PlotAction.max_requests
        max_assists = PlotActionAssistant.MAX_ASSISTS
        recent_assists = caller.recent_assists
        msg += """
    You are permitted {w%s{n actions and {w%s{n assists every 60 days, and have currently
    taken {w%s{n actions and {w%s{n assists. Assists can be made instead of actions, and
    assists over %s count toward the action cap.""" % (
            max_actions, max_assists, recent_actions.count(),
            recent_assists.count(), max_assists)
        return msg

    def func(self):
        """Executes @action command"""
        if not self.args and not self.switches:
            return self.list_actions()
        if "newaction" in self.switches:
            return self.new_action()
        action = self.get_action(self.lhs)
        if not action:
            return
        if not self.switches:
            return self.view_action(action)
        if not self.check_valid_switch_for_action_type(action):
            return
        if "makepublic" in self.switches:
            return self.make_public(action)
        if "question" in self.switches:
            return self.ask_question(action)
        if self.check_switches(self.requires_draft_switches):
            return self.do_requires_draft_switches(action)
        if self.check_switches(self.requires_editable_switches):
            # PS - NV is f*****g amazing
            return self.do_requires_editable_switches(action)
        if self.check_switches(self.requires_unpublished_switches):
            return self.do_requires_unpublished_switches(action)
        else:
            self.msg("Invalid switch. See 'help @action'.")

    def check_valid_switch_for_action_type(self, action):
        """
        Checks if the specified switches require the main action, and if so, whether our action is the main action.
        
            Args:
                action (PlotAction or PlotActionAssistant): action or assisting action
                
            Returns:
                True or False for whether we're okay to proceed.
        """
        if not (set(self.switches) & set(self.requires_owner_switches)):
            return True
        if action.is_main_action:
            return True
        self.msg("Only the action leader can use that switch.")
        return False

    def make_public(self, action):
        """Makes an action public knowledge"""
        try:
            action.make_public()
        except ActionSubmissionError as err:
            self.msg(err)

    def do_requires_draft_switches(self, action):
        """Executes switches that require the action to be in Draft status"""
        if not action.status == PlotAction.DRAFT:
            return self.send_too_late_msg()
        elif "invite" in self.switches:
            return self.invite_assistant(action)
        elif "setcrisis" in self.switches:
            return self.set_crisis(action)
        elif "readycheck" in self.switches:
            return self.ready_check(action)

    def do_requires_editable_switches(self, action):
        """Executes switches that requires the action to be in an editable state"""
        if not action.editable:
            return self.send_no_edits_msg()
        if "roll" in self.switches:
            return self.set_roll(action)
        if "tldr" in self.switches or "title" in self.switches:
            return self.set_topic(action)
        elif "category" in self.switches:
            return self.set_category(action)
        elif "submit" in self.switches:
            return self.submit_action(action)
        elif "setaction" in self.switches:
            return self.set_action(action)
        elif "add" in self.switches:
            return self.add_resource(action)
        elif "toggletraitor" in self.switches:
            return self.toggle_traitor(action)
        elif "toggleattend" in self.switches:
            return self.toggle_attend(action)
        elif "ooc_intent" in self.switches:
            return self.set_ooc_intent(action)
        elif "setsecret" in self.switches:
            return self.set_secret_action(action)

    def do_requires_unpublished_switches(self, action):
        """Executes switches that require the action to not be Published"""
        if action.status in (PlotAction.PUBLISHED, PlotAction.PENDING_PUBLISH):
            return self.send_no_edits_msg()
        elif "cancel" in self.switches:
            return self.cancel_action(action)
        elif "noscene" in self.switches:
            return self.toggle_noscene(action)

    def send_no_edits_msg(self):
        """Tells the caller they can't edit the action."""
        self.msg("You cannot edit that action at this time.")

    def send_too_late_msg(self):
        """Tells the caller that they could only do that in Draft mode"""
        self.msg("Can only be done while the action is in Draft status.")

    def list_actions(self):
        """Prints a table of the actions we've taken"""
        table = EvTable("ID", "Crisis", "Date", "Owner", "Status")
        actions = self.actions_and_invites
        attending = list(
            actions.filter(
                Q(dompc=self.dompc, attending=True)
                | Q(assisting_actions__dompc=self.dompc,
                    assisting_actions__attending=True)))
        for action in actions:
            date = "--"
            if action.date_submitted:
                date = action.date_submitted.strftime("%x")

            def color_unsubmitted(string):
                """Colors the status display for assisting actions of the player that aren't ready to submit"""
                if action.status == PlotAction.DRAFT and action.check_unready_assistant(
                        self.dompc):
                    return "|r%s|n" % string
                return string

            is_attending = action in attending
            id_str = "{w*%s{n" % action.id if is_attending else str(action.id)
            table.add_row(id_str,
                          str(action.plot)[:20], date, str(action.dompc),
                          color_unsubmitted(action.get_status_display()))
        msg = "\nActions you're attending will be highlighted with {w*{n."
        self.msg(str(table) + msg)

    def send_no_args_msg(self, noun):
        """Sends failure message about what they're missing."""
        if not noun:
            noun = "args"
        self.msg("You need to include %s." % noun)

    def new_action(self):
        """Create a new action."""
        if not self.args:
            return self.send_no_args_msg("a story")
        crisis = None
        crisis_msg = ""
        actions = self.lhs
        if self.rhs:
            crisis = self.get_valid_crisis(self.lhs)
            actions = self.rhs
            crisis_msg = " to respond to %s" % str(crisis)
            if not crisis:
                return
        if not self.can_create(crisis):
            return
        diff = PlotAction.NORMAL_DIFFICULTY
        action = self.dompc.actions.create(actions=actions,
                                           plot=crisis,
                                           stat_used="",
                                           skill_used="",
                                           difficulty=diff)
        self.msg("You have drafted a new action {w(#%s){n%s: %s" %
                 (action.id, crisis_msg, actions))
        self.msg(
            "Please note that you cannot invite players to an action once it is submitted."
        )
        if crisis:
            self.warn_crisis_omnipresence(action)

    def get_action(self, arg):
        """Returns an action we are involved in from ID# args.
        
            Args:
                arg (str): String to use to find the ID of the crisis action.
                
            Returns:
                A CrisisAction or a CrisisActionAssistant depending if the caller is the owner of the main action or
                an assistant.
        """
        try:
            dompc = self.dompc
            action = self.actions_and_invites.get(id=arg)
            try:
                action = action.assisting_actions.get(dompc=dompc)
            except PlotActionAssistant.DoesNotExist:
                pass
            return action
        except (PlotAction.DoesNotExist, ValueError):
            self.msg("No action found by that ID.")
            self.list_actions()

    def get_my_actions(self, crisis=False, assists=False):
        """Returns caller's actions."""
        dompc = self.dompc
        if not assists:
            actions = dompc.actions.all()
        else:
            actions = PlotAction.objects.filter(
                Q(dompc=dompc) | Q(assistants=dompc)).distinct()
        if not crisis:
            actions = actions.filter(plot__isnull=True)
        return actions

    def get_valid_crisis(self, name_or_id):
        """Gets crisis they can participate in that matches name or ID"""
        try:
            qs = Plot.objects.viewable_by_player(self.caller)
            if name_or_id.isdigit():
                return qs.get(id=name_or_id)
            return qs.get(name__iexact=name_or_id)
        except Plot.DoesNotExist:
            self.msg("No crisis found by that name or ID.")

    def can_create(self, crisis=None):
        """Checks criteria for creating a new action."""
        if crisis and not self.can_set_crisis(crisis):
            return False
        my_draft = self.get_my_actions().filter(status=PlotAction.DRAFT).last()
        if my_draft:
            self.msg(
                "You have drafted an action which needs to be submitted or canceled: %s"
                % my_draft.id)
            return False
        if not self.caller.pay_action_points(50):
            self.msg("You do not have enough action points.")
            return False
        return True

    def can_set_crisis(self, crisis):
        """Checks criteria for linking to a Crisis."""
        try:
            crisis.raise_creation_errors(self.dompc)
        except ActionSubmissionError as err:
            self.msg(err)
            return False
        return True

    def set_category(self, action):
        """Sets the category for an action"""
        if not action.is_main_action:
            self.msg("Only the main action has a category.")
            return
        if not self.rhs:
            return self.send_no_args_msg("a category")
        category_names = self.action_categories.keys()
        if self.rhs not in category_names:
            category_names = set(ob.lower() for ob in category_names)
            self.send_no_args_msg("one of these categories: %s" %
                                  ", ".join(category_names))
            return
        self.set_action_field(action, "category",
                              self.action_categories[self.rhs])

    def set_roll(self, action):
        """Sets a stat and skill for action or assistant"""
        from world.stats_and_skills import VALID_SKILLS, VALID_STATS
        try:
            stat, skill = self.rhslist
            stat = stat.lower()
            skill = skill.lower()
        except (ValueError, TypeError, AttributeError):
            self.msg("Usage: @action/roll <action #>=<stat>,<skill>")
            return
        if stat not in VALID_STATS or skill not in VALID_SKILLS:
            self.msg("You must provide a valid stat and skill.")
            return
        field_name = "stat_used"
        self.set_action_field(action, field_name, stat, verbose_name="stat")
        field_name = "skill_used"
        return self.set_action_field(action,
                                     field_name,
                                     skill,
                                     verbose_name="skill")

    def set_topic(self, action):
        """Sets the topic for an action"""
        if not self.rhs:
            return self.send_no_args_msg("a title")
        if len(self.rhs) > 80:
            self.send_no_args_msg(
                "a shorter title; aim for under 80 characters")
            return
        return self.set_action_field(action, "topic", self.rhs)

    def set_ooc_intent(self, action):
        """
        Sets our ooc intent, or if we're already submitted and have an intent set, it asks a question.
        """
        if not self.rhs:
            self.msg("You must enter a message.")
            return
        action.set_ooc_intent(self.rhs)
        self.msg("You have set your ooc intent to be: %s" % self.rhs)

    def ask_question(self, action):
        """Submits an OOC question for an action"""
        if not self.rhs:
            self.msg("You must enter text for your question.")
            return
        action.ask_question(self.rhs)
        self.msg("You have submitted a question: %s" % self.rhs)

    def cancel_action(self, action):
        """Cancels the action"""
        action.cancel()
        self.msg("Action cancelled.")

    def submit_action(self, action):
        """I love a bishi. He too will submit."""
        try:
            action.submit()
        except ActionSubmissionError as err:
            self.msg(err)
        else:
            self.msg("You have submitted your action.")

    def toggle_noscene(self, action):
        """Toggles whether they prefer the action get offscreen resolution"""
        action.prefer_offscreen = not action.prefer_offscreen
        action.save()
        color = "{r" if action.prefer_offscreen else "{w"
        self.msg("Preference for offscreen resolution set to: %s%s" %
                 (color, action.prefer_offscreen))

    def toggle_traitor(self, action):
        """Toggles whether they're working against the main action/crisis"""
        action.traitor = not action.traitor
        color = "{r" if action.traitor else "{w"
        action.save()
        self.msg("Traitor is now set to: %s%s{n" % (color, action.traitor))

    def set_action(self, action):
        """Sets the main story of what they're doing in the action"""
        if not self.rhs:
            return self.send_no_args_msg("a story")
        if not action.is_main_action:
            try:
                action.set_action(self.rhs)
            except ActionSubmissionError as err:
                self.msg(err)
                return
            else:
                self.msg("%s now has your assistance: %s" %
                         (action.plot_action, self.rhs))
        else:
            self.set_action_field(action, "actions", self.rhs)
        if action.plot:
            self.do_passive_warnings(action)

    def set_secret_action(self, action):
        """Sets a secret story of what they're doing in the action"""
        if not self.rhs:
            return self.send_no_args_msg("a story of your secret actions")
        self.set_action_field(action,
                              "secret_actions",
                              self.rhs,
                              verbose_name="Secret actions")

    def warn_crisis_overcrowd(self, action):
        """Warns that too many people are attending"""
        try:
            action.check_plot_overcrowd()
        except ActionSubmissionError as err:
            self.msg("{yWarning:{n %s" % err)

    def warn_crisis_omnipresence(self, action):
        """Warns that they're already doing stuff for the crisis"""
        try:
            action.check_plot_omnipresence()
        except ActionSubmissionError as err:
            self.msg("{yWarning:{n %s" % err)

    def do_passive_warnings(self, action):
        """Delivers warnings of what would make submission fail"""
        self.warn_crisis_omnipresence(action)
        if not action.prefer_offscreen:
            self.warn_crisis_overcrowd(action)

    def set_crisis(self, action):
        """Sets the crisis for the action"""
        if not self.rhs:
            action.plot = None
            action.save()
            self.msg("Your action no longer targets any crisis.")
            return
        crisis = self.get_valid_crisis(self.rhs)
        if not crisis:
            return
        if not self.can_set_crisis(crisis):
            return
        action.plot = crisis
        action.save()
        self.msg("You have set the action to be for crisis: %s" % crisis)
        self.do_passive_warnings(action)

    def ready_check(self, action):
        """Checks whether assistants are ready for the player"""
        unready = action.get_unready_assisting_actions()
        if not unready:
            self.msg("All invited assistants are currently ready.")
        else:
            self.msg("The following assistants aren't ready: %s" %
                     ", ".join(str(ob.author) for ob in unready))

    def toggle_attend(self, action):
        """Toggles whether they're attending the action in-person, onscreen"""
        if action.attending:
            action.attending = False
            action.save()
            self.msg("You are marked as no longer attending the action.")
            return
        try:
            action.mark_attending()
        except ActionSubmissionError as err:
            self.msg(err)
            return
        self.msg(
            "You have marked yourself as physically being present for that action."
        )