Mercurial > sypikslang
view gamelib/missions.py @ 180:8868ac7ece8b
Tweak steal research mission
author | Neil Muller <drnlmuller@gmail.com> |
---|---|
date | Sat, 12 May 2012 12:28:39 +0200 |
parents | 71db080a1fbf |
children | 07e81cd0cdd6 |
line wrap: on
line source
# -*- coding: utf-8 -*- # vim:fileencoding=utf-8 ai ts=4 sts=4 et sw=4 from random import randint, random, choice from gamelib.constants import SUCCESS, FAILURE, GAME_WIN, M_VALS from gamelib.schematics import cat class Result(Exception): """Results of a mission. This is an exception so we can throw it from inside helper methods. """ def __init__(self, outcome, msg, money=0, rep=0, **special): self.outcome = outcome self.money = money self.reputation = rep self.text = msg self.special = special self.applied = False @property def loot(self): loot = {} if self.money != 0: loot['money'] = self.money if self.reputation != 0: loot['rep'] = self.reputation loot.update(self.special) loot.pop('new_milestone', None) # This one's special. return loot def apply(self, state): if not self.applied: state.money += self.money state.reputation += self.reputation state.apply_mission_special(**self.special) self.applied = True else: raise RuntimeError('attempted to apply result twice') class Mission(object): """Base class for the mission objects. Missions have a name, short description (for list displays) and long description (which may contain clues about approaches)""" NAME = "Generic Mission" SHORT_DESCRIPTION = None LONG_DESCRIPTION = None MINIMUM_REPUTATION = None 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 = {} if init_data is not None: # Load stored state. self._load_data(init_data) else: # New instance. self._new_data() def _new_data(self): pass def _load_data(self, init_data): # Note: this does not deep-copy. self.data = init_data.copy() def save_data(self): # Note: this does not deep-copy. return self.data.copy() def get_data(self, key): return self.data.get(key, None) @classmethod def sanity_check(cls): pass def can_attempt(self, state): """Can we currently attempt the mission""" if (M_VALS[self.MINIMUM_MILESTONE] > M_VALS[state.milestone]): # Our base of operations is too small return False if self.get_data('completed'): return False if (self.MINIMUM_REPUTATION is not None and self.MINIMUM_REPUTATION > state.reputation): # Don't have the reputation required return False if self.MINIONS_REQUIRED > state.minions: # Need more minions! return False return True def fail(self, msg=None, money=0, rep=0, **special): if msg is None: msg = self.GENERIC_FAILURE raise Result(FAILURE, msg, money=money, rep=rep, **special) def succeed(self, msg=None, money=0, rep=0, **special): if msg is None: msg = self.GENERIC_SUCCESS raise Result(SUCCESS, msg, money=money, rep=rep, **special) 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.""" # Handle error case 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) # Try with some equipment. self.attempt_with(self.categorise_equipment(equipment), state) # No result, so generic failure. self.fail() def attempt_no_equipment(self, state): self.fail(self.NO_EQUIP_FAILURE) def attempt_with(self, categorised, state): self.fail("You can't succceed at this mission.") def combat_power(self, categorised, cats=None): if cats is None: cats = [cat.HAND_WEAPON, cat.VEHICLE, cat.COUNTERMEASURE, cat.INTELLIGENCE] combat_equipment = set() for category in cats: combat_equipment.update(categorised.get(category, [])) power = 0 for equipment in combat_equipment: power += equipment.power() return power def check_failure(self, categorised): equipment = set() for items in categorised.values(): equipment.update(items) for item in equipment: if item.reliability() < random(): return item.FAILURE_TEXT return None def use_equipment(self, categorised, msg="Disaster strikes! %s", **loot): failure = self.check_failure(categorised) if failure is not None: self.fail(msg % (failure,), **loot) class PlaygroundBully(Mission): NAME = "Rob kids in the playground" SHORT_DESCRIPTION = "Steal from those significantly weaker than yourself." LONG_DESCRIPTION = ( "It's not menancing, or lucrative, but when the bills are due, no one" " 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): self.succeed(money=randint(90, 110), rep=-1) class RansomChina(Mission): NAME = "Hold China to ransom" SHORT_DESCRIPTION = "Surely a path to riches and fame." LONG_DESCRIPTION = ( "Holding China to ransom. The rewards for successfully threatening" " the largest country in the world are great, but the risks are" " significant. Without some serious firepower, the chances of success" " are small.") MINIMUM_REPUTATION = 100 MINIMUM_MILESTONE = "city" def _new_data(self): self.data['prior_attempts'] = [] def attempt_no_equipment(self, state): 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: 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: 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']: self.fail( "'We have devised countermeasures since last time, doctor." " You cannot threaten us with that again.'") self.use_equipment(categorised, rep=randint(-5, -2)) self.data['prior_attempts'].add(doom.NAME) self.succeed( "Trembling at you threat of certain doom, the Chinese" " government pays the ransom.", money=randint(800000, 1200000), rep=10) class ToppleThirdWorldGovernment(Mission): NAME = "Topple a third-world government" SHORT_DESCRIPTION = "We could use a more amenable dictator there." LONG_DESCRIPTION = ( "It's a small and fairly useless country, but it's still an actual" " government that can be toppled. A good test bed for some of the" " larger toys in the armory.") MINIMUM_REPUTATION = 50 GENERIC_FAILURE = ( "Your invasion force was outclassed by the incumbent military, Not" " surprising, since it turns out they've had a lot of practice in the" " recent series of revolutions.") 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): self.use_equipment(categorised) if self.combat_power(categorised) > randint(60, 120): 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): NAME = "Rob the local bank" SHORT_DESCRIPTION = "A trivial challenge, but easy money." LONG_DESCRIPTION = ( "The security guards and local police are of minor concern. Walk in," " clean out the vault, walk out. Couldn't be simpler.") MINIMUM_MILESTONE = "basement" GENERIC_FAILURE = ( "The bank's security arrangements are rather more impressive than you" " were led to believe. Bring bigger guns 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) self.use_equipment(categorised) if cat.VEHICLE in categorised: 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.PATHOGEN in categorised: 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) if cat.AI in categorised: self.success( "Your cunning AI easily penetrates the bank's computing" " systems and transfers the money to you account.", money=loot) if self.combat_power(categorised) > randint(5, 20): self.succeed( "The threat of your weapons is enough to inspire an impressive" " level of cooperation. You make off with the loot.", money=loot) class DestroyMountRushmore(Mission): NAME = "Destroy Mount Rushmore" SHORT_DESCRIPTION = "Monuments to other people? Intolerable" LONG_DESCRIPTION = ( "While potentially expensive, destroying a major monument is a" " good way to secure your reputation.") MINIMUM_REPUTATION = 20 def attempt(self, equipment, state): self.data['completed'] = True self.succeed("Mount Rushmore is remarkably easy to destroy.", rep=50) class DistributePamphlets(Mission): NAME = "Distribute pamphlets" SHORT_DESCRIPTION = "The populace need to be told the truth!" LONG_DESCRIPTION = ( "A focused pamphlet distribution campaign will combat the lies being" " spread about you. Replacing them with better lies, of course.") MINIMUM_MILESTONE = "basement" SUCCESS_MESSAGE = ( "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: self._succ( "Sadly, the populace was so annoyed by the flood of flyers" " that nobody took any notice of the content.", rep) if rep == 0: self._succ("Nobody seems to have noticed.", rep) self._succ( "The public seemed mildly receptive to your propaganda.", rep) def attempt_with(self, categorised, state): if cat.MIND_CONTROL in categorised: self.use_equipment( {cat.MIND_CONTROL: categorised[cat.MIND_CONTROL]}) self._succ("Your creative use of science has paid off nicely.", randint(5, 10)) return self.attempt_no_equipment(state) class TakeOverTheWorld(Mission): NAME = "Take over the world!" SHORT_DESCRIPTION = "It's for their own good." LONG_DESCRIPTION = ( "Someone has to rule the world and if it's not you it'd just be" " someone less well qualified -- and that would be worse for" " everyone.") MINIMUM_REPUTATION = 200 MINIMUM_MILESTONE = "city" 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: 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): NAME = "Take over the neighbourhood!" SHORT_DESCRIPTION = "First step toward greatness." LONG_DESCRIPTION = ( "A basement lab is a good starting point, but it's getting a bit" " cramped in here. You need to expand, but don't quite have the" " resources to take the whole world yet. Or even the city. But the" " neighbourhood... that's quite feasible.") MINIMUM_REPUTATION = 20 MINIMUM_MILESTONE = "basement" 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 self.succeed( "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), new_milestone="neighbourhood") if cat.HAND_WEAPON in categorised: 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: self.succeed( "Propaganda and persuasion are good tools, but you'll need" " some force to back them up.", rep=randint(2, 4)) class ShowThemAll(Mission): NAME = "And they called me mad!" SHORT_DESCRIPTION = "Time for revenge." LONG_DESCRIPTION = ( "You've outgrown the chains of convention your teachers tried" " to force you into. It's time to show the world what true" " genius can accomplish. And what better way than with a small" " slice of revenge?") MINIMUM_REPUTATION = 20 MINIMUM_MILESTONE = "neighbourhood" NO_EQUIP_FAILURE = ( "A security guard escorts you off campus after finding you clawing" " at the science building with your bare hands. He doesn't say" " anything, but you can already hear the gossip.") def attempt_no_equipment(self, state): self.fail(self.NO_EQUIP_FAILURE, rep=-5) def attempt_with(self, categorised, state): self.use_equipment(categorised) if cat.DOOMSDAY_DEVICE in categorised: self.data['completed'] = True self.succeed( "The fools cower in terror at your display of power. Finally" " they are forced to acknowledge your genius.", rep=randint(10, 15)) if cat.BEAST in categorised: if all(cat.AQUATIC in x.CATEGORIES for x in categorised[cat.BEAST]): self.fail( "While the beast is terrifying, the effect is" " unfortuantely significantly lessened the need for a " " large water tank, and the it's inability to move " " independantly. Perhaps you need to rethink your plan?") else: self.data['completed'] = True self.succeed( "Your monstrous creation rampages through campus in a" " most statisfying way. They will not forgot this.", rep=randint(5, 12)) if cat.HAND_WEAPON in categorised: self.fail( "Mere crude force is not the answer. You need some more" " fitting demonstration of your power.") class SubvertNews(Mission): NAME = "Subvert the news network" SHORT_DESCRIPTION = "Stop the lies!" LONG_DESCRIPTION = ( "Worringly, people appear to be easily duped by the lies broadcast" " about you. Time to fight fire with fire!") MINIMUM_REPUTATION = 15 MINIMUM_MILESTONE = "neighbourhood" NO_EQUIP_FAILURE = ( "You fail to even get into the building. Perhaps you need to" " rethink your approach") def attempt_with(self, categorised, state): self.use_equipment(categorised) if cat.MIND_CONTROL in categorised: self.succeed( "With the proper equipment, it's a simple matter to" " convince people of the correctness of your point" " of view. If only everything were so simple", rep=randint(5, 15)) if cat.AI in categorised: self.succeed( "Your AI has complete control of the broadcasters computer" " system. After all, it's not censorship if you're stopping" " them broadcasting blatant lies, now is it?", rep=randint(5, 10)) if any(c in categorised for c in (cat.VEHICLE, cat.DOOMSDAY_DEVICE, cat.BEAST)): self.succeed( "Cowering in fear, the broadcaster agrees to change the" " tone of their stories.", rep=randint(2, 5)) if cat.HAND_WEAPON in categorised: self.fail( "The news station's security is surprisingly well prepared." " Perhaps you should rethink your approach?", rep=-randint(5, 10)) class RaidLab(Mission): NAME = "Raid Rival Lab" SHORT_DESCRIPTION = "Why not let other people work for you?" LONG_DESCRIPTION = ( "While clearly no match for your genius, sometimes your rivals stumble" " onto something interesting. Surely their results can be put to" " better use helping your plans?") MINIMUM_MILESTONE = "neighbourhood" NO_EQUIP_FAILURE = ( "Your rival may not be the sharpest tool in the shed, but even a" " fool invests in some defenses. You'll need to be better prepared" " in the future.") def _succ(self, msg, state): reward = choice(('schematic', 'science', 'money', 'money', 'money', 'money', 'money', 'nothing', 'nothing', 'nothing')) if reward == 'nothing': self.succeed("%s Unfortunately, not only are these people working" " on ideas you've already covered, they're flat broke." % msg, money=0, rep=1) elif reward == 'money': self.succeed("%s While their research yields nothing of interest," " you can repurpose their funding to more worthy causes." % msg, money=randint(1000, 2000), rep=1) elif reward == 'schematic': self.succeed("%s You find the plans for a new device. How did" " these fools stumble upon this?" % msg, money=0, rep=randint(2, 5), new_schematic=choice(state.lab.new_schematics)) # New science self.succeed("%s Their notes are most illuminating. You realise you" " have sadly neglected research into this field." % msg, money=0, rep=randint(2, 5), new_science=choice(state.lab.new_research)) def attempt_with(self, categorised, state): self.use_equipment(categorised) if cat.DOOMSDAY_DEVICE in categorised: self.fail( "While overwhelming force is always a tempting choice," " total destruction of the lab will not help you cause.") if cat.AI in categorised: self._succ("Your AI easily takes control of the lab's network.", state) if self.combat_power(categorised) > randint(20, 40): self._succ( "The resistance is stiff, but your forces prevail" " thanks to your superior technology.", state) else: self.fail( "The lab is surprisingly well defended for an operation" " run by a total fool. You vow to return with a better" " prepared force.")