# HG changeset patch # User Jeremy Thurgood # Date 1336676200 -7200 # Node ID fb8037bc22f13f6ab56524c1fdf79e8639ca1556 # Parent 1a648d07d67e905fecf80691c20d9c1c480a8e27 More flexible (and less boilerplatey) mission stuff. diff -r 1a648d07d67e -r fb8037bc22f1 gamelib/gamestate.py --- a/gamelib/gamestate.py Thu May 10 19:20:57 2012 +0200 +++ b/gamelib/gamestate.py Thu May 10 20:56:40 2012 +0200 @@ -65,7 +65,7 @@ # Attempt the missions mission_results = [] for mission, equipment in self.cur_missions: - mission_results.append(mission.attempt(equipment, self)) + mission_results.append(mission.attempt_mission(equipment, self)) if not self.cur_missions and self.reputation > 0: # If you're not doing stuff, you're not in the news self.reputation -= M_VALS[self.milestone] @@ -80,7 +80,7 @@ messages = [] for result in mission_results: result.apply(self) - messages.append((result.outcome, result.message)) + messages.append((result.outcome, result.text)) if result.outcome == NEW_MILESTONE: self.milestone = MILESTONES[M_VALS[self.milestone] + 1] for science in new_stuff: diff -r 1a648d07d67e -r fb8037bc22f1 gamelib/missions.py --- a/gamelib/missions.py Thu May 10 19:20:57 2012 +0200 +++ b/gamelib/missions.py Thu May 10 20:56:40 2012 +0200 @@ -7,14 +7,17 @@ from gamelib.schematics import cat -class Result(object): - """Results of a mission""" +class Result(Exception): + """Results of a mission. - def __init__(self, outcome, money, reputation, msg): + This is an exception so we can throw it from inside helper methods. + """ + + def __init__(self, outcome, msg, money=0, rep=0): self.outcome = outcome self.money = money - self.reputation = reputation - self.message = msg + self.reputation = rep + self.text = msg self.applied = False def apply(self, state): @@ -39,6 +42,12 @@ MINIMUM_MILESTONE = "neighbourhood" MINIONS_REQUIRED = 1 + GENERIC_FAILURE = "You fail to inspire sufficient fear." + GENERIC_SUCCESS = "Victory is sweet." + NO_EQUIP_FAILURE = ( + "Really? You're going in completely unequipped? How brave. And " + " foolish. And ultimately unsuccessful.") + def __init__(self, init_data=None): self.data = {} @@ -83,7 +92,25 @@ return False return True - def attempt(self, equipment, state): + def fail(self, msg=None, money=0, rep=0): + if msg is None: + msg = self.GENERIC_FAILURE + raise Result(FAILURE, msg, money=money, rep=rep) + + def succeed(self, msg=None, money=0, rep=0): + if msg is None: + msg = self.GENERIC_SUCCESS + raise Result(SUCCESS, msg, money=money, rep=rep) + + def categorise_equipment(self, equipment): + # Categorise equipment for easier decision-making. + categorised = {} + for item in equipment: + for category in item.CATEGORIES: + categorised.setdefault(category, []).append(item) + return categorised + + def attempt_mission(self, equipment, state): """Attempt the mission with the given equipment list. Returns a result object with the results of the mission.""" @@ -91,32 +118,27 @@ if self.get_data('completed'): raise RuntimeError('Cannot attempt a completed mission') + try: + self.attempt(equipment, state) + except Result, e: + return e + + def attempt(self, equipment, state): # No equipment is usually a special case. if not equipment: return self.attempt_no_equipment(state) - # Categorise equipment for easier decision-making. - categorised = {} - for item in equipment: - for category in item.CATEGORIES: - categorised.setdefault(category, []).append(item) - result = self.attempt_with(categorised, state) + # Try with some equipment. + self.attempt_with(self.categorise_equipment(equipment), state) - if result is not None: - # We have a mission result. - return result - - # Generic fallback result. - return Result(FAILURE, 0, 0, ( - "You fail to inspire sufficient fear.")) + # No result, so generic failure. + self.fail() def attempt_no_equipment(self, state): - return Result(FAILURE, 0, 0, ( - "Really? You're going in completely unequipped?" - " How brave. And foolish. And ultimately unsuccessful.")) + self.fail(self.NO_EQUIP_FAILURE) def attempt_with(self, categorised, state): - return Result(FAILURE, 0, 0, "You can't succceed at this mission.") + self.fail("You can't succceed at this mission.") class PlaygroundBully(Mission): @@ -128,14 +150,13 @@ " cares how you earn the money.") MINIMUM_MILESTONE = "basement" + GENERIC_SUCCESS = ( + "You devote your resources to robbing kids in a playpark. It's not the" + " finest moment in your reign of terror, but at least you walked away" + " with a surprising amount of small change.") def attempt(self, equipment, state): - haul = randint(90, 110) - return Result(SUCCESS, haul, -1, ( - "You devote your resources to robbing kids in a playpark." - " It's not the finest moment in your reign of terror, but at" - " least you walked away with a surprising amount of small" - " change.")) + self.succeed(money=randint(90, 110), rep=-1) class RansomChina(Mission): @@ -155,39 +176,41 @@ self.data['prior_attempts'] = [] def attempt_no_equipment(self, state): - return Result(FAILURE, 0, -10, ( - "It takes three different interpreters before the Chinese" - " government finally understand that you're trying to hold" - " them ransom with... well... nothing. Nothing at all. This" - " one will probably make a good anecdote at diplomatic" - " cocktail parties. But not for you. No, not for you at all.")) + self.fail( + "It takes three different interpreters before the Chinese" + " government finally understand that you're trying to hold" + " them ransom with... well... nothing. Nothing at all. This" + " one will probably make a good anecdote at diplomatic" + " cocktail parties. But not for you. No, not for you at all.", + rep=-10) def attempt_with(self, categorised, state): dooms = categorised.get(cat.DOOMSDAY_DEVICE, []) if not dooms: - return Result(FAILURE, 0, -1, ( - "You completely fail to inspire the requisite level of" - " terror. Maybe a more impressive threat will fare" - " better.")) + self.fail( + "You completely fail to inspire the requisite level of" + " terror. Maybe a more impressive threat will fare better.", + rep=-1) if len(dooms) > 1: - return Result(FAILURE, 0, 0, ( - "Everyone seems confused as to how you actually plan" - " to cause widepsread distruction and mayhem, and" - " negotiations break down. Perhaps it's better to stick" - " to one weapon of mass destruction at a time.")) + self.fail( + "Everyone seems confused as to how you actually plan" + " to cause widepsread distruction and mayhem, and" + " negotiations break down. Perhaps it's better to stick" + " to one weapon of mass destruction at a time.") [doom] = dooms if doom.NAME in self.data['prior_attempts']: - return Result(FAILURE, 0, 0, ( - "'We have devised countermeasures since last time, doctor." - " You cannot threaten us with that again.'")) + self.fail( + "'We have devised countermeasures since last time, doctor." + " You cannot threaten us with that again.'") self.data['prior_attempts'].add(doom.NAME) - return Result(SUCCESS, 1000000, 10, ( - "Trembling at you threat of certain doom, the Chinese" - " government pays the ransom.")) + self.succeed( + "Trembling at you threat of certain doom, the Chinese" + " government pays the ransom.", + money=randint(800000, 1200000), rep=10) class ToppleThirdWorldGovernment(Mission): @@ -201,25 +224,21 @@ MINIMUM_REPUTATION = 50 - def attempt_no_equipment(self, state): - return Result(FAILURE, 0, 0, ( - "The border post may be poorly guarded, but you need to" - " bring *some* kind of weaponry along. Your troops sulk" - " on the way home.")) + GENERIC_FAILURE = ( + "Nobody seems to quite understand what it is you're threatening them" + " with. Eventually you have to give up and go home.") + + NO_EQUIP_FAILURE = ( + "The border post may be poorly guarded, but you need to bring *some*" + " kind of weaponry along. Your troops sulk on the way home.") def attempt_with(self, categorised, state): if any(c in categorised for c in (cat.VEHICLE, cat.HAND_WEAPON)): - return Result(SUCCESS, randint(5000, 15000), randint(3, 7), ( - "The corruption and oppression continue, but at least" - " the proceeds are making their way into *your*" - " pockets. And you don't even have to dirty your own" - " jackboots.")) - - if cat.DOOMSDAY_DEVICE in categorised: - return Result(FAILURE, 0, 0, ( - "Nobody seems to quite understand what it is you're" - " threatening them with. Eventually you have to give up" - " and go home.")) + self.succeed( + "The corruption and oppression continue, but at least the" + " proceeds are making their way into *your* pockets. And you" + " don't even have to dirty your own jackboots.", + money=randint(5000, 15000), rep=randint(3, 7)) class RobBank(Mission): @@ -232,40 +251,40 @@ MINIMUM_MILESTONE = "basement" - def attempt_no_equipment(self, state): - return Result(FAILURE, 0, 0, ( - "Your attempt to rob the bank barehanded is unsuccessful." - " Fortunately, everyone is too stunned to impede your" - " escape.")) + GENERIC_FAILURE = ( + "The operation was a complete fiasco. Maybe you should try something a" + " little less eclectic next time.") + NO_EQUIP_FAILURE = ( + "Your attempt to rob the bank barehanded is unsuccessful. Fortunately," + " everyone is too stunned to impede your escape.") def attempt_with(self, categorised, state): loot = randint(500, 1500) if cat.VEHICLE in categorised: - return Result(FAILURE, 0, 0, ( - "Your vehicles are impressively doom-laden, but not really" - " suitable for city traffic. You intimidate the traffic" - " wardens into letting you off without a fine, but by the" - " time you get to the bank it's already closed.")) + self.fail( + "Your vehicles are impressively doom-laden, but not really" + " suitable for city traffic. You intimidate the traffic" + " wardens into letting you off without a fine, but by the" + " time you get to the bank it's already closed.") if cat.HAND_WEAPON in categorised: - return Result(SUCCESS, loot, 0, ( - "The threat of your weapons is enough to inspire an" - " impressive level of cooperation. You make off with the" - " loot.")) + self.succeed( + "The threat of your weapons is enough to inspire an impressive" + " level of cooperation. You make off with the loot.", + money=loot) if cat.PATHOGEN in categorised: - if state.reputation < 10: - return Result(FAILURE, 0, 0, ( - "The clerk doesn't realise the threat of" - " the vial you hold, and, although watching him" - " die in agony would be statisfying, you decide" - " it's not worth wasting this on something so" - " trivial")) - else: - return Result(SUCCESS, loot, 1, ( - "Holding up a bank with only a small vial of clear" - " liquid. Now that is power.")) + if randint(5, 15) < state.reputation: + self.fail( + "The clerk doesn't realise the threat of the vial you" + " hold, and although watching him die in agony would be" + " statisfying, you decide it's not worth wasting this on" + " something so trivial.") + self.succeed( + "Holding up a bank with only a small vial of clear liquid. Now" + " that is power.", + money=loot, rep=1) class DestroyMountRushmore(Mission): @@ -279,8 +298,7 @@ def attempt(self, equipment, state): self.data['completed'] = True - return Result(SUCCESS, 0, 50, ( - "Mount Rushmore is remarkably easy to destroy.")) + self.succeed("Mount Rushmore is remarkably easy to destroy.", rep=50) class DistributePamphlets(Mission): @@ -297,29 +315,28 @@ "A small army of urchins delivers thousands of cheaply printed" " pamphlets. %s") + def _succ(self, message, rep): + self.succeed(self.SUCCESS_MESSAGE % (message,), rep=rep) + def attempt_no_equipment(self, state): rep = randint(-2, 5) if rep < 0: - result = ( + self._succ( "Sadly, the populace was so annoyed by the flood of flyers" - " that nobody took any notice of the content.") - elif rep == 0: - result = "Nobody seems to have noticed." - else: - result = "The public seemed mildly receptive to your propaganda." + " that nobody took any notice of the content.", rep) + if rep == 0: + self._succ("Nobody seems to have noticed.", rep) - return Result(SUCCESS, 0, rep, self.SUCCESS_MESSAGE % (result,)) + self._succ( + "The public seemed mildly receptive to your propaganda.", rep) def attempt_with(self, categorised, state): - rep = randint(5, 10) + if cat.MIND_CONTROL in categorised: + self._succ("Your creative use of science has paid off nicely.", + randint(5, 10)) - if cat.MIND_CONTROL in categorised: - result = ( - "Your creative use of science has paid off nicely.") - return Result(SUCCESS, 0, rep, self.SUCCESS_MESSAGE % (result,)) - else: - return self.attempt_no_equipment(state) + return self.attempt_no_equipment(state) class TakeOverTheWorld(Mission): @@ -334,16 +351,15 @@ MINIMUM_REPUTATION = 200 MINIMUM_MILESTONE = "city" - def attempt_no_equipment(self, state): - return Result(FAILURE, 0, 0, "It's going to take more than your bare" - " hands to take over the world!") + NO_EQUIP_FAILURE = ( + "It's going to take more than your bare hands to take over the world!") def attempt_with(self, categorised, state): if cat.MIND_CONTROL not in categorised: - return Result(FAILURE, 0, 5, "If you're going to take over the" - " world, first you must control key elements within" - " the populace.") - return Result(GAME_WIN, 0, 100, "The world is yours!") + self.fail( + "If you're going to take over the world, first you must" + " control key elements within the populace.", rep=-5) + raise Result(GAME_WIN, "The world is yours!", rep=100) class TakeOverTheNeighbourhood(Mission): @@ -359,23 +375,28 @@ MINIMUM_REPUTATION = 20 MINIMUM_MILESTONE = "basement" - def attempt_no_equipment(self, state): - return Result(FAILURE, 0, 0, "The neighbourhood isn't very big, but" - " you're still not going to take it over bare-handed.") + NO_EQUIP_FAILURE = ( + "The neighbourhood isn't very big, but you're still not going to take" + " it over bare-handed.") + GENERIC_FAILURE = ( + "Well, that didn't work. Maybe a better equipment selection is in" + " order.") def attempt_with(self, categorised, state): if all(c in categorised for c in (cat.MIND_CONTROL, cat.HAND_WEAPON)): self.data['completed'] = True - return Result(NEW_MILESTONE, 1000, 5, "Guns and persuasion, that's" - " all you need. It's early days still, but you're" - " finally out of that pokey basement and have some" - " elbow room to work with. Next step: the city!") + raise Result(NEW_MILESTONE, "Guns and persuasion, that's" + " all you need. It's early days still, but you're" + " finally out of that pokey basement and have some" + " elbow room to work with. Next step: the city!", + money=randint(1000, 2000), rep=randint(5, 15)) + if cat.HAND_WEAPON in categorised: - return Result(SUCCESS, 150, 0, "You'll need more than guns to" - " win the people over. But until then, you can take" - " their cash.") + self.succeed( + "You'll need more than guns to win the people over. But until" + " then, you can take their cash.", money=randint(100, 200)) + if cat.MIND_CONTROL in categorised: - return Result(SUCCESS, 0, 7, "Propaganda and persuasion are good" - " tools, but you'll need something to back them up.") - return Result(FAILURE, 0, 0, "Well, that didn't work. Maybe a better" - " equipment selection is in order.") + self.succeed( + "Propaganda and persuasion are good tools, but you'll need" + " some force to back them up.", rep=randint(2, 4))