diff --git a/game/combat/__init__.py b/game/combat/__init__.py index 13d5f438..9ca511b3 100644 --- a/game/combat/__init__.py +++ b/game/combat/__init__.py @@ -362,6 +362,9 @@ def name_current_option(self): return '/'.join(op_list) def record_hotkey(self, mykey): + if not mykey: + return + option_string = self.name_current_option() pbge.util.config.set("HOTKEYS", mykey, option_string) @@ -521,7 +524,7 @@ def go(self): self.active_ui.update(gdi, self) if gdi.type == pygame.KEYDOWN: - if gdi.unicode in self.ACCEPTABLE_HOTKEYS and gdi.mod & pygame.KMOD_ALT: + if gdi.unicode and gdi.unicode in self.ACCEPTABLE_HOTKEYS and gdi.mod & pygame.KMOD_ALT: # Record a hotkey. self.record_hotkey(gdi.unicode) elif gdi.unicode in self.ACCEPTABLE_HOTKEYS and pbge.util.config.has_option("HOTKEYS", gdi.unicode) and not pbge.my_state.key_is_in_use(gdi.unicode): diff --git a/game/combat/aibrain.py b/game/combat/aibrain.py index abc6455e..82f6b444 100644 --- a/game/combat/aibrain.py +++ b/game/combat/aibrain.py @@ -76,9 +76,9 @@ def get_target(self, camp, optrange): best_candidates = [tar for tar in candidates if camp.scene.distance(self.npc.pos, tar.pos) <= optrange] if best_candidates: return max(best_candidates, key=lambda a: [ + self.closest_target_selector(camp, a), self.easiest_target_selector(camp, a), self.most_damaged_target_selector(camp, a), - self.closest_target_selector(camp, a) ]) elif candidates: return max(candidates, key=lambda a: [ @@ -374,7 +374,7 @@ def act(self, camp: gears.GearHeadCampaign): # Otherwise attempt skill use again. if not (self.target and self.target in camp.scene.contents and self.target.is_operational()): self.target = self.targeter.get_target(camp, self.midr) - elif random.randint(1, 3) == 1: + elif random.randint(1, 3) != 1: self.target = self.targeter.get_target(camp, self.midr) if self.target: self.attempt_attack(camp) diff --git a/game/content/gharchitecture.py b/game/content/gharchitecture.py index 610c6e7e..56232e01 100644 --- a/game/content/gharchitecture.py +++ b/game/content/gharchitecture.py @@ -168,7 +168,15 @@ class UlsaniteOfficeDecor(OfficeDecor): DESK_TERRAIN = (ghterrain.UlsaniteDesk,) CHAIR_TERRAIN = (ghterrain.UlsaniteChair,) WALL_DECOR = (ghterrain.UlsaniteBookshelfTerrain, ghterrain.UlsaniteFilingCabinetTerrain) - # WALL_DECOR = (ghterrain.TekruinsWallDecor,) + + +class MilitaryOfficeDecor(OfficeDecor): + DESK_TERRAIN = (ghterrain.UlsaniteDesk,) + CHAIR_TERRAIN = (ghterrain.UlsaniteChair,) + WALL_DECOR = ( + ghterrain.UlsaniteBookshelfTerrain, ghterrain.UlsaniteFilingCabinetTerrain, ghterrain.LockersTerrain, + ghterrain.MapTerrain, ghterrain.EarthMapTerrain + ) class WorldScaleDeadzone(GearHeadArchitecture): diff --git a/game/content/ghplots/dd_roadstops.py b/game/content/ghplots/dd_roadstops.py index 8384006e..037424fe 100644 --- a/game/content/ghplots/dd_roadstops.py +++ b/game/content/ghplots/dd_roadstops.py @@ -223,8 +223,9 @@ def custom_init(self, nart): scale=gears.scale.HumanScale) intscenegen = pbge.randmaps.SceneGenerator(intscene, gharchitecture.ResidentialBuilding()) self.register_scene(nart, intscene, intscenegen, ident="INTERIOR", dident="LOCALE") - foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom(anchor=pbge.randmaps.anchors.south, ), - dident="INTERIOR") + foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom( + anchor=pbge.randmaps.anchors.south, decorate=gharchitecture.UlsaniteOfficeDecor(), + ), dident="INTERIOR") mycon2 = plotutility.TownBuildingConnection(nart, self, self.elements["LOCALE"], intscene, room1=building, @@ -306,8 +307,9 @@ def custom_init(self, nart): scale=gears.scale.HumanScale) intscenegen = pbge.randmaps.SceneGenerator(intscene, gharchitecture.FortressBuilding()) self.register_scene(nart, intscene, intscenegen, ident="INTERIOR", dident="LOCALE") - foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom(anchor=pbge.randmaps.anchors.south, ), - dident="INTERIOR") + foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom( + anchor=pbge.randmaps.anchors.south, decorate=gharchitecture.MilitaryOfficeDecor(), + ), dident="INTERIOR") mycon2 = plotutility.TownBuildingConnection(nart, self, self.elements["LOCALE"], intscene, room1=building, @@ -390,8 +392,9 @@ def custom_init(self, nart): intscenegen = pbge.randmaps.SceneGenerator(intscene, gharchitecture.DefaultBuilding( floor_terrain=ghterrain.WhiteTileFloor)) self.register_scene(nart, intscene, intscenegen, ident="INTERIOR", dident="LOCALE") - foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom(anchor=pbge.randmaps.anchors.south, ), - dident="INTERIOR") + foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom( + anchor=pbge.randmaps.anchors.south, decorate=gharchitecture.UlsaniteOfficeDecor(), + ), dident="INTERIOR") mycon2 = plotutility.TownBuildingConnection(nart, self, self.elements["LOCALE"], intscene, room1=building, @@ -463,8 +466,9 @@ def custom_init(self, nart): scale=gears.scale.HumanScale) intscenegen = pbge.randmaps.SceneGenerator(intscene, gharchitecture.DefaultBuilding()) self.register_scene(nart, intscene, intscenegen, ident="INTERIOR", dident="LOCALE") - foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom(anchor=pbge.randmaps.anchors.south, ), - dident="INTERIOR") + foyer = self.register_element('_introom', pbge.randmaps.rooms.ClosedRoom( + anchor=pbge.randmaps.anchors.south, decorate=gharchitecture.MilitaryOfficeDecor(), + ), dident="INTERIOR") entrance_room = self.register_element("_EXTERIOR", pbge.randmaps.rooms.FuzzyRoom( tags=[pbge.randmaps.IS_CITY_ROOM, pbge.randmaps.IS_CONNECTED_ROOM]), @@ -517,6 +521,15 @@ def LEADER_offers(self, camp): # *** DZRS_GARAGE *** # *********************** +class OutsourcedGarage(Plot): + LABEL = "DZRS_GARAGE" + SHOP_TYPES = ("SHOP_MECHA", "SHOP_GARAGE") + + def custom_init(self, nart): + self.add_sub_plot(nart, random.choice(self.SHOP_TYPES)) + return True + + # Prototypical road stop Garage. class SomewhatOkayGarage(Plot): LABEL = "DZRS_GARAGE" @@ -683,6 +696,16 @@ def custom_init(self, nart): # *** DZRS_HOSPITAL *** # ************************* +class OutsourcedHospital(Plot): + LABEL = "DZRS_HOSPITAL" + SHOP_TYPES = ("SHOP_CYBERCLINIC", "SHOP_HOSPITAL") + + def custom_init(self, nart): + self.add_sub_plot(nart, random.choice(self.SHOP_TYPES)) + return True + +GenericHospital = OutsourcedHospital + class DeadzoneClinic(Plot): LABEL = "DZRS_HOSPITAL" @@ -851,17 +874,6 @@ def _ask_other_question(self, camp): self.asked_other_question = True -class GenericHospital(Plot): - LABEL = "DZRS_HOSPITAL" - - active = False - scope = None - - def custom_init(self, nart): - self.add_sub_plot(nart, "SHOP_HOSPITAL", elements={"LOCALE": self.elements["METROSCENE"]}) - return True - - # ************************ # *** DZRS_FEATURE *** # ************************ diff --git a/game/content/ghplots/lancedev.py b/game/content/ghplots/lancedev.py index 552cd936..6d6a6cc2 100644 --- a/game/content/ghplots/lancedev.py +++ b/game/content/ghplots/lancedev.py @@ -85,6 +85,74 @@ def lose_mission(self, camp): # The actual plots... +class BetterCallAPlumber(LMMissionPlot): + LABEL = "LANCEDEV" + active = True + scope = True + UNIQUE = True + + MISSION_OBJECTIVES = (missionbuilder.BAMOP_DUNGEONLIKE, missionbuilder.BAMOP_REPAIR_MACHINE) + MISSION_SCALE = gears.scale.HumanScale + CASH_REWARD = 150 + EXPERIENCE_REWARD = 150 + MISSION_NAME = "Fix the pipes with {NPC}" + + def custom_init(self, nart): + npc = self.seek_element(nart, "NPC", self._is_good_npc, scope=nart.camp.scene) + + self.prep_mission( + nart.camp, custom_elements={lancedev_objectives.BAME_LANCEMATE: npc, + missionbuilder.BAMEP_MONSTER_TYPE: ("SYNTH", "MUTANT", "ROBOT")}, + archi_override=gharchitecture.ScrapIronWorkshop(decorate=gharchitecture.TechDungeonDecor()) + ) + self.had_convo = False + + return True + + def _is_good_npc(self, nart, candidate): + if self.npc_is_ready_for_lancedev(nart.camp, candidate): + return ( + gears.tags.Laborer in candidate.get_tags() and + not candidate.relationship.expectation + ) + + def METROSCENE_ENTER(self, camp): + if not self.had_convo: + npc = self.elements["NPC"] + pbge.alert( + "As you enter {METROSCENE}, {NPC} approaches you.".format( + **self.elements)) + + mymenu = ghcutscene.SimpleMonologueMenu( + "[I_GOT_A_MISSION_OFFER] A pipe burst somewhere under {METROSCENE} and it's going to take cavalier backup to find and fix it.".format(**self.elements), + npc, camp + ) + mymenu.no_escape = True + mymenu.add_item("Let's get to it, then.", self._accept_offer) + mymenu.add_item("Sounds like a dirty job... I'd really rather not.", self._reject_offer) + choice = mymenu.query() + if choice: + choice(camp) + + def _accept_offer(self, camp): + npc: gears.base.Character = self.elements["NPC"] + ghcutscene.SimpleMonologueDisplay( + "This sort of work isn't glamorous, but it's the kind of work that keeps the city functioning. [LETSGO]", + npc)(camp, False) + self.elements["NPC"].relationship.expectation = relationships.E_PROFESSIONAL + self.had_convo = True + self.mission_active = True + + def _reject_offer(self, camp): + npc: gears.base.Character = self.elements["NPC"] + ghcutscene.SimpleMonologueDisplay( + "Yeah, me either. Avoiding jobs like this is one of the reasons I'm trying to become a full time cavalier. Let's go find something a bit more glamorous to do!", + npc)(camp, False) + self.elements["NPC"].relationship.expectation = relationships.E_ADVENTURE + + self.proper_end_plot(camp) + + class PartyPlanner(LMPlot): LABEL = "LANCEDEV" active = True diff --git a/game/content/ghplots/missionbuilder.py b/game/content/ghplots/missionbuilder.py index c55ec630..7e12d480 100644 --- a/game/content/ghplots/missionbuilder.py +++ b/game/content/ghplots/missionbuilder.py @@ -55,6 +55,7 @@ BAMOP_DUNGEONLIKE = "BAMOP_DUNGEONLIKE" BAMEP_MONSTER_TYPE = "BAMEP_MONSTER_TYPE" BAMOP_EXTERMINATE_MONSTERS = "BAMOP_EXTERMINATE_MONSTERS" +BAMOP_REPAIR_MACHINE = "BAMOP_REPAIR_MACHINE" BAMOP_RESCUE_VICTIM = "BAMOP_RESCUE_VICTIM" BAMEP_VICTIM_NAME = "BAMEP_VICTIM_NAME" @@ -1908,6 +1909,77 @@ def t_ENDCOMBAT(self, camp): self.obj.win(camp, 100) +class BAM_RepairMachine(Plot): + LABEL = BAMOP_REPAIR_MACHINE + active = True + scope = "LOCALE" + + MACHINE_DESC = ( + "This machine is oozing black fluid. That can't possibly be good.", + "This machine is completely shut down. It does not appear to be receiving power.", + "Someone or something smashed this machine real good. It's going to take a lot of work to put it back together.", + "This machine appears to be in perfect working order except for the flashing red light on top." + ) + + MACHINE_CLASSES = ( + ghwaypoints.OldTerminal, ghwaypoints.OldMainframe + ) + + def custom_init(self, nart): + myscene = self.elements["LOCALE"] + roomtype = self.elements["ARCHITECTURE"].get_a_room() + myroom = self.register_element("ROOM", roomtype( + random.randint(5,10), random.randint(5,10), decorate=gharchitecture.DefiledFactoryDecor() + ), dident="LOCALE") + + myteam = self.register_element("_eteam", teams.Team(enemies=(myscene.player_team,)), dident="ROOM") + + myclass = random.choice(self.MACHINE_CLASSES) + mymachine = self.register_element("MACHINE", myclass( + name="Machine", plot_locked=True, anchor=pbge.randmaps.anchors.middle, + desc=random.choice(self.MACHINE_DESC) + ), dident="ROOM") + + self.obj = adventureseed.MissionObjective("Locate and repair the broken machinery", MAIN_OBJECTIVE_VALUE*2) + self.adv.objectives.append(self.obj) + + self.repaired_machine = False + self.tries = random.randint(1,3) + + return True + + def MACHINE_menu(self, camp, thingmenu): + if not self.repaired_machine and not camp.fight: + thingmenu.add_item("Repair machine", self._repair_machine) + + def _repair_machine(self, camp: gears.GearHeadCampaign): + if camp.do_skill_test( + gears.stats.Craft, gears.stats.Repair, self.rank, difficulty=gears.stats.DIFFICULTY_HARD + ): + pbge.alert("You have repaired the machine.") + self.elements["MACHINE"].desc = "The machine appears to be working normally now." + self.repaired_machine = True + self.obj.win(camp, 100) + camp.dole_xp(100, gears.stats.Repair) + elif self.tries < 1: + pbge.alert("After several failed attempts, you have bodged this machine into a semblance of normal operation. Hopefully it will keep running long enough for you to get out of here.") + self.elements["MACHINE"].desc = "The machine appears to be working now. Mostly. You hope." + self.repaired_machine = True + self.obj.win(camp, 65) + camp.dole_xp(100, gears.stats.Repair) + else: + self.tries -= 1 + pbge.alert("You attempt to repair the machine. The noise you make attracts monsters!") + myunit = gears.selector.RandomMonsterUnit( + self.rank, random.randint(90, 120), camp.scene.environment, + self.elements.get(BAMEP_MONSTER_TYPE, ("ROBOT", "FACTORY")), camp.scene.scale + ) + mek1 = myunit.contents[0] + team2 = self.elements.get("_eteam") + camp.scene.deploy_team(myunit.contents, team2, self.elements["ROOM"].area) + game.combat.enter_combat(camp, mek1) + + class BAM_RescueVictim(Plot): LABEL = BAMOP_RESCUE_VICTIM active = True diff --git a/game/content/ghplots/shops_plus.py b/game/content/ghplots/shops_plus.py index 501ec299..57b42932 100644 --- a/game/content/ghplots/shops_plus.py +++ b/game/content/ghplots/shops_plus.py @@ -152,7 +152,7 @@ class RandomShop(Plot): SHOP_TYPES = ( "SHOP_ARMORSTORE", "SHOP_WEAPONSTORE", "SHOP_MECHA", - "SHOP_BLACKMARKET", "SHOP_GENERALSTORE", "SHOP_TAVERN" + "SHOP_BLACKMARKET", "SHOP_GENERALSTORE", "SHOP_TAVERN", "SHOP_GARAGE" ) def custom_init(self, nart): @@ -304,7 +304,7 @@ def custom_init(self, nart): self.register_scene(nart, intscene, intscenegen, ident="INTERIOR", dident="LOCALE") foyer = self.register_element('FOYER', pbge.randmaps.rooms.ClosedRoom( random.randint(10,15), random.randint(10,15), anchor=pbge.randmaps.anchors.south, - decorate=gharchitecture.ArmoryDecor() + decorate=gharchitecture.BlackMarketDecor() ), dident="INTERIOR") mycon2 = plotutility.TownBuildingConnection( diff --git a/game/fieldhq/__init__.py b/game/fieldhq/__init__.py index 0febd554..6ffd69d3 100644 --- a/game/fieldhq/__init__.py +++ b/game/fieldhq/__init__.py @@ -190,6 +190,7 @@ def open_training(self, wid, ev): self.fhq.active = False my_trainer = training.TrainingMenu(self.camp, self.pc) my_trainer() + self.fhq.update_party() self.fhq.active = True def open_backpack(self, wid, ev): diff --git a/game/ghdialogue/ghgrammar.py b/game/ghdialogue/ghgrammar.py index 62acdb3e..e19a52a3 100644 --- a/game/ghdialogue/ghgrammar.py +++ b/game/ghdialogue/ghgrammar.py @@ -2813,6 +2813,25 @@ ], }, + "[I_GOT_A_MISSION_OFFER]": { + # A lancemate is telling the PC that they just received a mission offer. + Default: ["I just got a mission offer.", "I've received a mission offer.", + "I got an urgent message from [foaf]..." + ], + personality.Cheerful: ["We're in luck; someone wants to hire us." + ], + personality.Grim: ["There's trouble afoot... and we've been asked to deal with it.", + ], + personality.Easygoing: ["Hey, if you're interested, [foaf] just sent me a mission offer.", + ], + personality.Passionate: ["I just got a message- someone needs our talents!", + ], + personality.Sociable: ["[audience], I was chatting with [foaf] and [subject_pronoun] has a mission we might be able to do.", + ], + personality.Shy: ["I received a mission offer.", + ], + }, + "[I_HAVE_BEEN_IMMOBILIZED]": { # A lancemate has suffered a mobility kill. Default: ["I have been immobilized!", "[SWEAR] I can't move this thing." diff --git a/gears/geffects.py b/gears/geffects.py index 1c4fea19..524b6915 100644 --- a/gears/geffects.py +++ b/gears/geffects.py @@ -1851,7 +1851,7 @@ def calc_modifier(self, camp, attacker, pos): class OverwhelmModifier(object): # Every time you are attacked, the next attack gets a bonus to hit. name = 'Overwhelmed' - MOD_PER_ATTACK = 3 + MOD_PER_ATTACK = 4 def calc_modifier(self, camp, attacker, pos): my_mod = 0 diff --git a/history.md b/history.md index 9ec02e31..3787232a 100644 --- a/history.md +++ b/history.md @@ -1,3 +1,8 @@ +v0.970 September 3, 2024 +* Experience display in FieldHQ updated after training +* Increased Overwhelmed modifier from 3 to 4 +* Altered NPC combat target selector +* Fixed bug with pressing "Alt" key during combat * Wall terrain types can be marked to not accept wall decor * Added extra dialogue to Bear Bastard's Mecha Camp for Criminal PCs * PlacableThing's altitude property now does something diff --git a/main.py b/main.py index 42f94fba..27cd9eb4 100755 --- a/main.py +++ b/main.py @@ -30,7 +30,7 @@ import logging import traceback -VERSION = "v0.962" +VERSION = "v0.970" class TitleScreenRedraw(object):