diff --git a/game/combat/__init__.py b/game/combat/__init__.py index c66851ec..13d5f438 100644 --- a/game/combat/__init__.py +++ b/game/combat/__init__.py @@ -36,6 +36,15 @@ def enter_combat(camp, npc): camp.fight.activate_foe(npc) +def start_continuous_combat(camp): + # Start a combat that will not end even if there are no active enemies. In this case, it's up to the script + # controlling the combat to end combat. + if camp.fight: + camp.fight.keep_going_without_enemies = True + else: + camp.fight = Combat(camp, keep_going_without_enemies=True) + + class CombatStat(object): """Keep track of some stats that only matter during combat.""" @@ -588,8 +597,8 @@ def __init__(self, camp: gears.GearHeadCampaign): class Combat(object): - def __init__(self, camp: gears.GearHeadCampaign): - self.active = [] + def __init__(self, camp: gears.GearHeadCampaign, keep_going_without_enemies=False): + self.active = list(camp.get_active_party()) self.scene: gears.GearHeadScene = camp.scene self.camp = camp self.ap_spent = collections.defaultdict(int) @@ -597,6 +606,7 @@ def __init__(self, camp: gears.GearHeadCampaign): self.ai_brains = dict() self.no_quit = True self.n = 0 + self.keep_going_without_enemies = keep_going_without_enemies def roll_initiative(self): # Sort based on initiative roll. @@ -659,7 +669,11 @@ def num_enemies(self): def still_fighting(self): """Keep playing as long as there are enemies, players, and no quit.""" - return self.num_enemies() and self.camp.first_active_pc() and self.no_quit and self.camp.scene is self.scene and not pbge.my_state.got_quit and self.camp.keep_playing_scene() and self.camp.egg + return ( + (self.num_enemies() or self.keep_going_without_enemies) and + self.camp.first_active_pc() and self.no_quit and self.camp.scene is self.scene + and not pbge.my_state.got_quit and self.camp.keep_playing_scene() and self.camp.egg + ) def step(self, chara, dest): """Move chara according to hmap, return True if movement ended.""" @@ -878,3 +892,5 @@ def __setstate__(self, state): if not isinstance(mydict, CombatDict): state["cstat"] = CombatDict.from_dict(mydict) self.__dict__.update(state) + if "keep_going_without_enemies" not in state: + self.keep_going_without_enemies = False diff --git a/game/content/ghplots/ghquests.py b/game/content/ghplots/ghquests.py index 184fc924..a0ec3e96 100644 --- a/game/content/ghplots/ghquests.py +++ b/game/content/ghplots/ghquests.py @@ -1231,7 +1231,6 @@ def custom_init(self, nart): }, involvement=ghchallenges.InvolvedMetroNoFriendToFactionNPCs(self.elements["METROSCENE"], self.elements["_ENEMY_FACTION"]), outcome=my_outcome ) self.quest_record._needed_lore.add(base_lore) - print("Find Enemy Base Task Active") return True diff --git a/game/content/ghplots/mission_bigobs.py b/game/content/ghplots/mission_bigobs.py index d0604865..33b45773 100644 --- a/game/content/ghplots/mission_bigobs.py +++ b/game/content/ghplots/mission_bigobs.py @@ -8,7 +8,7 @@ import pygame import random from game import teams, ghdialogue -from game.content import gharchitecture, ghterrain, ghwaypoints, plotutility +from game.content import gharchitecture, ghterrain, ghwaypoints, plotutility, megaprops from pbge.dialogue import Offer, ContextTag, Reply from game.ghdialogue import context from game.content.ghcutscene import SimpleMonologueDisplay @@ -214,7 +214,7 @@ def custom_init(self, nart): self.reinforcement_rooms = list() for t, anc in enumerate(pbge.randmaps.anchors.EDGES): if anc is not entry_anchor: - roomtype = self.elements["ARCHITECTURE"].get_a_room() + roomtype = self.elements["ARCHITECTURBAME_ME"].get_a_room() myroom = self.register_element("CORNER_ROOM_{}".format(t), roomtype(10, 10, anchor=anc), dident="LOCALE") self.reinforcement_rooms.append(myroom) if anc is pbge.randmaps.anchors.OPPOSITE_EDGE[entry_anchor]: @@ -321,6 +321,7 @@ def t_ENDCOMBAT(self, camp): class BAM_EscortShip(Plot): LABEL = BAMO_ESCORT_SHIP + LABEL = BAMO_TEST_MISSION active = True scope = "LOCALE" @@ -328,108 +329,103 @@ def custom_init(self, nart): myscene = self.elements["LOCALE"] allyfac = self.elements.get("ALLIED_FACTION") myfac = self.elements.get("ENEMY_FACTION") - entry_room: pbge.randmaps.rooms.Room = self.elements.get("ENTRANCE_ROOM") - entry_anchor = entry_room.anchor + if "ESCORT_PATH" in self.elements and self.elements["ESCORT_PATH"]: + std_dident = "ESCORT_PATH" + else: + std_dident = "LOCALE" - self.reinforcement_rooms = list() - for t, anc in enumerate(pbge.randmaps.anchors.EDGES): - if anc is not entry_anchor: - roomtype = self.elements["ARCHITECTURE"].get_a_room() - myroom = self.register_element("CORNER_ROOM_{}".format(t), roomtype(10, 10, anchor=anc), dident="LOCALE") - self.reinforcement_rooms.append(myroom) + if not ("ESCORT_ROOM" in self.elements and self.elements["ESCORT_ROOM"]): + self.register_element( + "ESCORT_ROOM", ghrooms.OpenRoom(12, 12), dident=std_dident + ) + myship = self.register_element("SHIP", megaprops.CivilianWaterShip(rank=self.rank), dident="ESCORT_ROOM") - team2: teams.Team = self.register_element("_eteam", teams.Team(faction=myfac, enemies=(myscene.player_team,))) + entry_anchor = self.elements["ENTRANCE_ANCHOR"] + troom = self.register_element( + "TARGET_ROOM", ghrooms.IndicatedRoom( + 12, 12, anchor=pbge.randmaps.anchors.OPPOSITE_EDGE[entry_anchor] + ), dident=std_dident + ) - myroom = self.register_element("CENTER_ROOM", - ghrooms.IndicatedRoom(16, 16, anchor=pbge.randmaps.anchors.middle), - dident="LOCALE") + reinforcement_rooms = list() + for n, anc in enumerate(pbge.randmaps.anchors.EDGES): + if anc is not entry_anchor and anc is not pbge.randmaps.anchors.OPPOSITE_EDGE[entry_anchor]: + roomtype = self.elements["ARCHITECTURE"].get_a_room() + rroom = self.register_element("_rroom_{}".format(n), roomtype(10, 10), dident="LOCALE") + reinforcement_rooms.append(rroom) + self.elements[CTE_REINFORCEMENT_ROOMS] = reinforcement_rooms - myroom2 = self.register_element("URBAN_ROOM", - ghrooms.MechaScaleResidentialArea(12, 12, anchor=pbge.randmaps.anchors.middle), - dident="CENTER_ROOM") + team2: teams.Team = self.register_element( + CTE_ENEMY_TEAM, teams.Team(faction=myfac, enemies=(myscene.player_team,)) + ) + self.add_sub_plot(nart, CTHREAT_MECHA, ident="THREAT") + self.did_init = False - bunker_team = self.register_element("_bunkerteam", teams.Team(faction=allyfac, enemies=(team2,), allies=(myscene.player_team,)), dident="URBAN_ROOM") - myfort = self.register_element("_bunker", gears.selector.generate_fortification(self.rank, myfac, myscene.environment)) - bunker_team.contents.append(myfort) + return True - self.round_counter = 0 - self.round_target = max(5, self.rank//10 + 2) + def t_START(self, camp: gears.GearHeadCampaign): + if not self.did_init: + self.subplots["THREAT"].activate(camp) + self.did_init = True + + +# ******************************* +# *** CONTINUOUS THREATS *** +# ******************************* +# +# Sometimes for a mission you don't want just one enemy, you want an endless supply of them! +# Needed Elements: +# ENEMY_TEAM: The team to which to add the reinforcements; if no team is provided then a new team will be created +# each time reinforcements are called. +# REINFORCEMENT_ROOMS: A list of rooms in which to place reinforcements. + +CTHREAT_MECHA = "CTHREAT_MECHA" +CTHREAT_MONSTERS = "CTHREAT_MONSTERS" +CTE_MONSTER_TAGS = "CTE_MONSTER_TAGS" + +CTE_ENEMY_TEAM = "ENEMY_TEAM" +CTE_REINFORCEMENT_ROOMS = "REINFORCEMENT_ROOMS" + +class CTMecha(Plot): + LABEL = CTHREAT_MECHA + active = False + scope = "LOCALE" - self.obj0 = adventureseed.MissionObjective("Proceed to indicated area", 1) - self.adv.objectives.append(self.obj0) - self.obj = adventureseed.MissionObjective("Defend buildings for {} rounds".format(self.round_target), MAIN_OBJECTIVE_VALUE * 3) - self.adv.objectives.append(self.obj) + def custom_init(self, nart): + if CTE_REINFORCEMENT_ROOMS not in self.elements or not self.elements[CTE_REINFORCEMENT_ROOMS]: + roomtype = self.elements["ARCHITECTURE"].get_a_room() + myroom = self.register_element("ROOM", roomtype(10, 10), dident="LOCALE") + self.elements[CTE_REINFORCEMENT_ROOMS] = [myroom,] - self.init_ready = True - self.combat_started = False + self.reinforcements_counter = 1 return True def t_START(self, camp: gears.GearHeadCampaign): - if self.init_ready: - myroom = self.elements["CENTER_ROOM"] - for x in range(myroom.area.x, myroom.area.x + myroom.area.width): - for y in range(myroom.area.y, myroom.area.y + myroom.area.height): - camp.scene.set_visible(x, y, True) + game.combat.start_continuous_combat(camp) - self.init_ready = False + def activate(self, camp): + game.combat.start_continuous_combat(camp) + super().activate(camp) - def t_PCMOVE(self, camp: gears.GearHeadCampaign): - if not self.combat_started: - in_dest_zone = list() - outta_dest_zone = list() - dest_zone: pygame.Rect = self.elements["CENTER_ROOM"].area - for pc in camp.get_active_party(): - if dest_zone.collidepoint(*pc.pos): - in_dest_zone.append(pc) - else: - outta_dest_zone.append(pc) - # Remove mobility kills from the list of mecha that haven't made it to the end zone. They aren't making it. - # Because this check is expensive, only do it if at least some mecha are in the end zone. - if in_dest_zone: - for pc in list(outta_dest_zone): - if pc.get_current_speed() < 10: - pc.gear_up(camp.scene) - if pc.get_current_speed() < 10: - outta_dest_zone.remove(pc) - - if in_dest_zone and not outta_dest_zone: - self.obj0.win(camp, 100) - self.combat_started = True - self._add_reinforcements(camp) + def deactivate(self, camp): + if camp.fight: + camp.fight.keep_going_without_enemies = False + super().deactivate(camp) - def _add_reinforcements(self, camp: gears.GearHeadCampaign): - myunit = gears.selector.RandomMechaUnit(self.rank, 75, self.elements.get("ENEMY_FACTION"), + def add_reinforcements(self, camp): + myscene = self.elements["LOCALE"] + myrooms = self.elements[CTE_REINFORCEMENT_ROOMS] + myunit = gears.selector.RandomMechaUnit(self.rank, 100, self.elements.get("ENEMY_FACTION"), camp.scene.environment, add_commander=False) - team2 = self.elements["_eteam"] - #print(myunit.mecha_list) mek1 = myunit.mecha_list[0] - camp.scene.deploy_team(myunit.mecha_list, team2, random.choice(self.reinforcement_rooms).area) + team2 = self.elements.get("ENEMY_TEAM", teams.Team(enemies=(myscene.player_team,))) + camp.scene.deploy_team(myunit.mecha_list, team2, random.choice(myrooms).area) game.combat.enter_combat(camp, mek1) - game.combat.enter_combat(camp, self.elements["_bunker"]) def t_COMBATROUND(self, camp): - if self.combat_started: - myteam = self.elements["_bunkerteam"] - if len(myteam.get_members_in_play(camp)) < 1: - pbge.alert("Buildings Destroyed".format(self.round_counter), font=pbge.HUGEFONT, justify=0) - self.obj.failed = True - camp.check_trigger("FORCE_EXIT") - - else: - self.round_counter += 1 - pbge.alert("Survived Round {}".format(self.round_counter), font=pbge.HUGEFONT, justify=0) - if self.round_counter >= self.round_target: - # Victory! - self.obj.win(camp, 100) - camp.check_trigger("FORCE_EXIT") - else: - self._add_reinforcements(camp) - - def t_ENDCOMBAT(self, camp): - if self.combat_started: - myteam = self.elements["_eteam"] - if len(myteam.get_members_in_play(camp)) < 1: - self.round_counter += 1 - self._add_reinforcements(camp) + if self.reinforcements_counter > 0: + self.reinforcements_counter -= 1 + else: + self.add_reinforcements(camp) diff --git a/game/content/ghplots/missionbuilder.py b/game/content/ghplots/missionbuilder.py index 34876625..622a56f9 100644 --- a/game/content/ghplots/missionbuilder.py +++ b/game/content/ghplots/missionbuilder.py @@ -136,8 +136,10 @@ def __init__(self, camp, name, metroscene, return_wp, enemy_faction=None, allied cms_pstate.elements["WIN_MESSAGE"] = win_message if loss_message: cms_pstate.elements["LOSS_MESSAGE"] = loss_message - cms_pstate.elements["COMBAT_MUSIC"] = combat_music or camp.campdata.get(gears.CAMPDATA_DEFAULT_MISSION_COMBAT_MUSIC) or "Komiku_-_03_-_Battle_Theme.ogg" - cms_pstate.elements["EXPLO_MUSIC"] = exploration_music or camp.campdata.get(gears.CAMPDATA_DEFAULT_MISSION_EXPLO_MUSIC) or "Chronos.ogg" + cms_pstate.elements["COMBAT_MUSIC"] = combat_music or camp.campdata.get( + gears.CAMPDATA_DEFAULT_MISSION_COMBAT_MUSIC) or "Komiku_-_03_-_Battle_Theme.ogg" + cms_pstate.elements["EXPLO_MUSIC"] = exploration_music or camp.campdata.get( + gears.CAMPDATA_DEFAULT_MISSION_EXPLO_MUSIC) or "Chronos.ogg" self.solo_mission = solo_mission if not mission_grammar: mission_grammar = MissionGrammar() @@ -282,16 +284,11 @@ def custom_init(self, nart): self.register_scene(nart, myscene, myscenegen, ident="LOCALE", temporary=True, dident="METROSCENE") self.adv.world = myscene - myanchor = self.elements.get("ENTRANCE_ANCHOR", None) or random.choice(pbge.randmaps.anchors.EDGES) - self.register_element("ENTRANCE_ROOM", pbge.randmaps.rooms.OpenRoom(7, 7, anchor=myanchor), dident="LOCALE") - myent = self.register_element("_ENTRANCE", game.content.ghwaypoints.Exit(anchor=pbge.randmaps.anchors.middle, - plot_locked=True), - dident="ENTRANCE_ROOM") + self._generate_entrance_room() for ob in self.elements["OBJECTIVES"]: self.add_sub_plot(nart, ob) - self.mission_entrance = myent self.started_mission = False self.gave_mission_reminder = False self.gave_ending_message = False @@ -301,8 +298,16 @@ def custom_init(self, nart): return True + def _generate_entrance_room(self): + # Set ENTRANCE_ANCHOR, ENTRANCE_ROOM, and _ENTRANCE + myanchor = self.elements.setdefault("ENTRANCE_ANCHOR", random.choice(pbge.randmaps.anchors.EDGES)) + self.register_element("ENTRANCE_ROOM", pbge.randmaps.rooms.OpenRoom(7, 7, anchor=myanchor), dident="LOCALE") + self.register_element("_ENTRANCE", game.content.ghwaypoints.Exit(anchor=pbge.randmaps.anchors.middle, + plot_locked=True), + dident="ENTRANCE_ROOM") + def start_mission(self, camp): - camp.go(self.mission_entrance) + camp.go(self.elements["_ENTRANCE"]) if not self.started_mission: self.started_mission = True @@ -343,7 +348,8 @@ def t_UPDATE(self, camp): def _ENTRANCE_menu(self, camp, thingmenu): if self.adv.is_completed(): - thingmenu.desc = "Your mission is finished. Are you ready to return to {}?".format(self.elements["METROSCENE"]) + thingmenu.desc = "Your mission is finished. Are you ready to return to {}?".format( + self.elements["METROSCENE"]) thingmenu.add_item("End Mission", self.exit_the_mission) thingmenu.add_item("Stay Here Longer", None) else: @@ -352,7 +358,6 @@ def _ENTRANCE_menu(self, camp, thingmenu): thingmenu.add_item("Cancel Mission", self.exit_the_mission) thingmenu.add_item("Continue Mission", None) - def exit_the_mission(self, camp: gears.GearHeadCampaign): camp.go(self.elements["ADVENTURE_RETURN"]) self.exited_mission = True @@ -391,6 +396,7 @@ class RoadMissionPlot(BuildAMissionPlot): def t_ENDCOMBAT(self, camp): # If the player team gets wiped out, end the mission. + # If the player team wins, also end the mission. if not camp.first_active_pc(): self.exit_the_mission(camp) elif self.adv.is_completed(): @@ -410,6 +416,32 @@ def exit_the_mission(self, camp): self.adv.end_adventure(camp) +class EscortMissionPlot(BuildAMissionPlot): + # Kinda like the road mission, but we've got a ESCORT_ROOM in which to deploy whatever we're escorting and a + # clear path to the other side of the map. + LABEL = "BAM_ESCORT_MISSION" + + def _generate_entrance_room(self): + # Set ENTRANCE_ANCHOR, ENTRANCE_ROOM, and _ENTRANCE + myanchor = self.elements.setdefault("ENTRANCE_ANCHOR", random.choice(pbge.randmaps.anchors.CARDINALS)) + if myanchor not in pbge.randmaps.anchors.CARDINALS: + myanchor = random.choice(pbge.randmaps.anchors.CARDINALS) + self.elements["ENTRANCE_ANCHOR"] = myanchor + myscene: gears.GearHeadScene = self.elements["LOCALE"] + if myanchor in (pbge.randmaps.anchors.east, pbge.randmaps.anchors.west): + corridor = pbge.randmaps.rooms.OpenRoom(width=myscene.width, height=14, anchor=pbge.randmaps.anchors.west) + else: + corridor = pbge.randmaps.rooms.OpenRoom(width=14, height=myscene.height, anchor=pbge.randmaps.anchors.north) + self.register_element("ESCORT_PATH", corridor, dident="LOCALE") + interior_anchors = list(pbge.randmaps.anchors.ADJACENT_ANCHORS[myanchor]) + random.shuffle(interior_anchors) + self.register_element("ENTRANCE_ROOM", pbge.randmaps.rooms.OpenRoom(5, 5, anchor=interior_anchors[0]), dident="ESCORT_PATH") + self.register_element("_ENTRANCE", + game.content.ghwaypoints.Exit(anchor=pbge.randmaps.anchors.middle, plot_locked=True), + dident="ENTRANCE_ROOM") + self.register_element("ESCORT_ROOM", pbge.randmaps.rooms.OpenRoom(7, 7, anchor=interior_anchors[1]), dident="ESCORT_PATH") + + # **************************** # *** OBJECTIVE MIXINS *** # **************************** @@ -717,13 +749,13 @@ def custom_init(self, nart): self.elements["CONVO_CANT_RETREAT"] = True self.add_sub_plot(nart, "MC_ENEMY_DEVELOPMENT", elements={"NPC": mynpc}) - #mynpc = self.seek_element(nart, "_commander", self.adv.is_good_enemy_npc, must_find=False, lock=True, + # mynpc = self.seek_element(nart, "_commander", self.adv.is_good_enemy_npc, must_find=False, lock=True, # backup_seek_func=self.adv.is_good_backup_enemy) - #if mynpc: + # if mynpc: # plotutility.CharacterMover(nart.camp, self, mynpc, myscene, team2) # myunit = gears.selector.RandomMechaUnit(self.rank, 120, myfac, myscene.environment, add_commander=False) # self.add_sub_plot(nart, "MC_ENEMY_DEVELOPMENT", elements={"NPC": mynpc}) - #else: + # else: # myunit = gears.selector.RandomMechaUnit(self.rank, 150, myfac, myscene.environment, add_commander=True) # self.register_element("_commander", myunit.commander, lock=True) # self.add_sub_plot(nart, "MC_NDBCONVERSATION", elements={"NPC": myunit.commander.get_pilot()}) @@ -1031,8 +1063,8 @@ def custom_init(self, nart): def _npc_is_good(self, nart, candidate): return isinstance(candidate, gears.base.Character) and candidate.combatant and \ - nart.camp.are_faction_allies(candidate.faction, self.elements["ALLIED_FACTION"]) and \ - candidate not in nart.camp.party + nart.camp.are_faction_allies(candidate.faction, self.elements["ALLIED_FACTION"]) and \ + candidate not in nart.camp.party def PILOT_offers(self, camp): mylist = list() @@ -1132,8 +1164,8 @@ def custom_init(self, nart): def _npc_is_good(self, nart, candidate): return isinstance(candidate, gears.base.Character) and candidate.combatant and \ - nart.camp.are_faction_allies(candidate.faction, self.elements["ALLIED_FACTION"]) and \ - candidate not in nart.camp.party + nart.camp.are_faction_allies(candidate.faction, self.elements["ALLIED_FACTION"]) and \ + candidate not in nart.camp.party def PILOT_offers(self, camp): mylist = list() @@ -1328,8 +1360,8 @@ def custom_init(self, nart): myscene = self.elements["LOCALE"] roomtype = self.elements["ARCHITECTURE"].get_a_room() myroom = self.register_element("ROOM", roomtype(15, 15), dident="LOCALE") - eroom = self.register_element("EROOM", pbge.randmaps.rooms.Room(6,6), dident="ROOM") - b_room = self.register_element("BROOM", pbge.randmaps.rooms.FuzzyRoom(4,4), dident="ROOM") + eroom = self.register_element("EROOM", pbge.randmaps.rooms.Room(6, 6), dident="ROOM") + b_room = self.register_element("BROOM", pbge.randmaps.rooms.FuzzyRoom(4, 4), dident="ROOM") team2 = self.register_element("_eteam", teams.Team(enemies=(myscene.player_team,)), dident="EROOM") myfac = self.elements.get("ENEMY_FACTION") @@ -1635,7 +1667,7 @@ def PILOT_offers(self, camp): def _npc_is_good(self, nart, candidate): return isinstance(candidate, gears.base.Character) and candidate.combatant and candidate.faction != \ - self.elements["ENEMY_FACTION"] and candidate not in nart.camp.party and candidate.job.name == "Trucker" + self.elements["ENEMY_FACTION"] and candidate not in nart.camp.party and candidate.job.name == "Trucker" def _eteam_ACTIVATETEAM(self, camp): if self.intro_ready: @@ -1924,9 +1956,9 @@ def custom_init(self, nart): team2 = self.register_element("_eteam", teams.Team(enemies=(myscene.player_team,)), dident="ROOM") team3 = self.register_element("_ateam", teams.Team(enemies=(team2,), allies=(myscene.player_team,)), dident="ROOM") - #myunit = gears.selector.RandomMechaUnit(self.rank, 200, self.elements.get("ENEMY_FACTION"), myscene.environment, + # myunit = gears.selector.RandomMechaUnit(self.rank, 200, self.elements.get("ENEMY_FACTION"), myscene.environment, # add_commander=False) - #team2.contents += myunit.mecha_list + # team2.contents += myunit.mecha_list mysurvivor = self.register_element("SURVIVOR", gears.selector.generate_ace(25, None, myscene.environment)) mynpc = mysurvivor.get_pilot() @@ -1961,7 +1993,7 @@ def t_START(self, camp: gears.GearHeadCampaign): npc = self.elements["PILOT"] mek = self.elements["SURVIVOR"] camp.freeze(npc) - #ghdialogue.start_conversation(camp, camp.pc, self.adv, cue=ghdialogue.ATTACK_STARTER) + # ghdialogue.start_conversation(camp, camp.pc, self.adv, cue=ghdialogue.ATTACK_STARTER) def _eteam_ACTIVATETEAM(self, camp): if self.intro_ready: diff --git a/game/content/ghplots/wmw_occupation.py b/game/content/ghplots/wmw_occupation.py index f2aa250c..521ba7bb 100644 --- a/game/content/ghplots/wmw_occupation.py +++ b/game/content/ghplots/wmw_occupation.py @@ -257,7 +257,6 @@ def _resistance_wins(self, camp: gears.GearHeadCampaign): class OccupationRestoreOrder(Plot): LABEL = WMWO_MARTIAL_LAW - LABEL = TEST_WAR_PLOT scope = "METRO" active = True diff --git a/game/content/megaprops.py b/game/content/megaprops.py index 4270b008..bff30e56 100644 --- a/game/content/megaprops.py +++ b/game/content/megaprops.py @@ -2,10 +2,42 @@ import gears class MegaProp(teams.Team): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + POSITIONS = ((0,0), (1,0), (0,1), (-1,0), (0,-1)) + + @staticmethod + def _generate_spiral(n): + # The Junji Ito algorithm. + my_coords = list() + x = y = 0 + dx = 0 + dy = -1 + while n > 0: + my_coords.append((x,y)) + if x == y or (x < 0 and x == -y) or (x > 0 and x == 1 - y): + dx, dy = -dy, dx + x += dx + y += dy + n -= 1 + return my_coords def predeploy(self, gb, room): - for p in self.contents: - pass + if len(self.POSITIONS) < len(self.contents): + self.POSITIONS = self._generate_spiral(len(self.contents)) + ox,oy = room.area.center + for n, part in enumerate(self.contents): + part.pos = (self.POSITIONS[n][0]+ox, self.POSITIONS[n][1]+oy) + + super().predeploy(gb, room) + + +class CivilianWaterShip(MegaProp): + POSITIONS = ((0,0), (0,1), (0,-1), (0,2), (0,-2)) + def __init__(self, *args, rank=50, **kwargs): + super().__init__(*args, **kwargs) + self.rank = rank + size = max(min(rank//10 + 1, 10), 2) + length = max(min(rank//20 + 1, 5), 2) + for t in range(length): + self.contents.append(gears.base.Prop(size=size, name="Ship", imagename="prop_block.png")) +#print(MegaProp._generate_spiral(20)) diff --git a/history.md b/history.md index 98d9271b..f46f0092 100644 --- a/history.md +++ b/history.md @@ -1,3 +1,5 @@ +* Combat can be set to keep going even if there are no active enemies on map +* Room contents with fixed positions will now be placed on map * Props are immovable even after they are destroyed * Props will not be placed somewhere they can block movement * calc_mission_reward can automatically round the reward to an round number diff --git a/pbge/plots.py b/pbge/plots.py index af94aace..07e210c5 100644 --- a/pbge/plots.py +++ b/pbge/plots.py @@ -171,7 +171,7 @@ class Plot(object): memo = None - def __init__(self, nart, pstate): + def __init__(self, nart, pstate: PlotState): """Initialize + install this plot, or raise PlotError""" # nart = The Narrative object # pstate = The current plot state diff --git a/pbge/randmaps/anchors.py b/pbge/randmaps/anchors.py index d4a58734..e403c12d 100644 --- a/pbge/randmaps/anchors.py +++ b/pbge/randmaps/anchors.py @@ -59,9 +59,10 @@ def southeast(par,chi): return par.bottomright - EDGES = (west,northwest,north,northeast,east,southeast,south,southwest) +CARDINALS = (west,north,east,south) + OPPOSING_CARDINALS = ((north,south),(east,west),(south,north),(west,east)) OPPOSING_PAIRS = ((northwest,southeast), (north,south), (northeast,southwest), @@ -73,3 +74,9 @@ def southeast(par,chi): south: north, southwest: northeast, west: east, northwest: southeast } +ADJACENT_ANCHORS = { + north: (northwest, northeast), northeast: (north, east), + east: (northeast, southeast), southeast: (south, east), + south: (southeast, southwest), southwest: (south, west), + west: (southwest, northwest), northwest: (north, west) +} \ No newline at end of file diff --git a/pbge/randmaps/rooms.py b/pbge/randmaps/rooms.py index f04d1b07..f291f477 100644 --- a/pbge/randmaps/rooms.py +++ b/pbge/randmaps/rooms.py @@ -228,8 +228,12 @@ def deploy(self, gb, archi): for i in list(self.contents): if hasattr(i, "predeploy"): i.predeploy(gb, self) + + for i in list(self.contents): # Remove the spaces occupied by map contents from the good spots lists. if hasattr(i, "pos") and i.pos: + if hasattr(i, "place"): + i.place(gb, i.pos) if i.pos in good_walls: good_walls.remove(i.pos) if i.pos in good_spots: @@ -237,38 +241,40 @@ def deploy(self, gb, archi): for i in list(self.contents): # Only place contents which can be placed, but haven't yet. - if hasattr(i, "place") and not (hasattr(i, "pos") and i.pos): - if hasattr(i, "anchor"): - myrect = pygame.Rect(0, 0, 1, 1) - i.anchor(self.area, myrect) - i.place(gb, (myrect.x, myrect.y)) - if (myrect.x, myrect.y) in good_walls: - good_walls.remove((myrect.x, myrect.y)) - if (myrect.x, myrect.y) in good_spots: - good_spots.remove((myrect.x, myrect.y)) - elif hasattr(i, "ATTACH_TO_WALL") and i.ATTACH_TO_WALL and good_walls: - p = random.choice(good_walls) - good_walls.remove(p) - i.place(gb, p) - elif good_spots: - i.pos = None - if hasattr(i, "IMMOVABLE") and i.IMMOVABLE: - candidates = list(good_spots) - while candidates and not i.pos: - p = random.choice(candidates) - candidates.remove(p) - if gb.wall_wont_block(*p, mmode=archi.mmode): - i.place(gb, p) + if hasattr(i, "place"): + if not (hasattr(i, "pos") and i.pos): + if hasattr(i, "anchor"): + myrect = pygame.Rect(0, 0, 1, 1) + i.anchor(self.area, myrect) + i.place(gb, (myrect.x, myrect.y)) + if (myrect.x, myrect.y) in good_walls: + good_walls.remove((myrect.x, myrect.y)) + if (myrect.x, myrect.y) in good_spots: + good_spots.remove((myrect.x, myrect.y)) + elif hasattr(i, "ATTACH_TO_WALL") and i.ATTACH_TO_WALL and good_walls: + p = random.choice(good_walls) + good_walls.remove(p) + i.place(gb, p) + elif good_spots: + i.pos = None + if hasattr(i, "IMMOVABLE") and i.IMMOVABLE: + candidates = list(good_spots) + while candidates and not i.pos: + p = random.choice(candidates) + candidates.remove(p) + if gb.wall_wont_block(*p, mmode=archi.mmode): + i.place(gb, p) + good_spots.remove(p) + if not i.pos: + p = random.choice(good_spots) good_spots.remove(p) - if not i.pos: + i.place(gb, p) + + else: p = random.choice(good_spots) good_spots.remove(p) i.place(gb, p) - else: - p = random.choice(good_spots) - good_spots.remove(p) - i.place(gb, p) def fill(self, gb, dest, floor=-1, wall=-1, decor=-1): # Fill the provided area with the provided terrain.