# Released under the MIT License. See LICENSE for details.#"""Functionality related to individual levels in a campaign."""from__future__importannotationsimportcopyimportweakreffromtypingimportTYPE_CHECKING,overrideimportbabaseifTYPE_CHECKING:fromtypingimportAnyimportbascenev1classLevel:"""An entry in a bascenev1.Campaign. Category: **Gameplay Classes** """def__init__(self,name:str,gametype:type[bascenev1.GameActivity],settings:dict,preview_texture_name:str,*,displayname:str|None=None,):self._name=nameself._gametype=gametypeself._settings=settingsself._preview_texture_name=preview_texture_nameself._displayname=displaynameself._campaign:weakref.ref[bascenev1.Campaign]|None=Noneself._index:int|None=Noneself._score_version_string:str|None=None@overridedef__repr__(self)->str:cls=type(self)returnf"<{cls.__module__}.{cls.__name__} '{self._name}'>"@propertydefname(self)->str:"""The unique name for this Level."""returnself._name
[docs]defget_settings(self)->dict[str,Any]:"""Returns the settings for this Level."""settings=copy.deepcopy(self._settings)# So the game knows what the level is called.# Hmm; seems hacky; I think we should take this out.settings['name']=self._namereturnsettings
@propertydefpreview_texture_name(self)->str:"""The preview texture name for this Level."""returnself._preview_texture_name# def get_preview_texture(self) -> bauiv1.Texture:# """Load/return the preview Texture for this Level."""# return _bauiv1.gettexture(self._preview_texture_name)@propertydefdisplayname(self)->bascenev1.Lstr:"""The localized name for this Level."""returnbabase.Lstr(translate=('coopLevelNames',(self._displaynameifself._displaynameisnotNoneelseself._name),),subs=[('${GAME}',self._gametype.get_display_string(self._settings))],)@propertydefgametype(self)->type[bascenev1.GameActivity]:"""The type of game used for this Level."""returnself._gametype@propertydefcampaign(self)->bascenev1.Campaign|None:"""The baclassic.Campaign this Level is associated with, or None."""returnNoneifself._campaignisNoneelseself._campaign()@propertydefindex(self)->int:"""The zero-based index of this Level in its baclassic.Campaign. Access results in a RuntimeError if the Level is not assigned to a Campaign. """ifself._indexisNone:raiseRuntimeError('Level is not part of a Campaign')returnself._index@propertydefcomplete(self)->bool:"""Whether this Level has been completed."""config=self._get_config_dict()val=config.get('Complete',False)assertisinstance(val,bool)returnval
[docs]defset_complete(self,val:bool)->None:"""Set whether or not this level is complete."""old_val=self.completeassertisinstance(old_val,bool)assertisinstance(val,bool)ifval!=old_val:config=self._get_config_dict()config['Complete']=val
[docs]defget_high_scores(self)->dict:"""Return the current high scores for this Level."""config=self._get_config_dict()high_scores_key='High Scores'+self.get_score_version_string()ifhigh_scores_keynotinconfig:return{}returncopy.deepcopy(config[high_scores_key])
[docs]defset_high_scores(self,high_scores:dict)->None:"""Set high scores for this level."""config=self._get_config_dict()high_scores_key='High Scores'+self.get_score_version_string()config[high_scores_key]=high_scores
[docs]defget_score_version_string(self)->str:"""Return the score version string for this Level. If a Level's gameplay changes significantly, its version string can be changed to separate its new high score lists/etc. from the old. """ifself._score_version_stringisNone:scorever=self._gametype.getscoreconfig().versionifscorever!='':scorever=' '+scoreverself._score_version_string=scoreverassertself._score_version_stringisnotNonereturnself._score_version_string
@propertydefrating(self)->float:"""The current rating for this Level."""val=self._get_config_dict().get('Rating',0.0)assertisinstance(val,float)returnval
[docs]defset_rating(self,rating:float)->None:"""Set a rating for this Level, replacing the old ONLY IF higher."""old_rating=self.ratingconfig=self._get_config_dict()config['Rating']=max(old_rating,rating)
def_get_config_dict(self)->dict[str,Any]:"""Return/create the persistent state dict for this level. The referenced dict exists under the game's config dict and can be modified in place."""campaign=self.campaignifcampaignisNone:raiseRuntimeError('Level is not in a campaign.')configdict=campaign.configdictval:dict[str,Any]=configdict.setdefault(self._name,{'Rating':0.0,'Complete':False})assertisinstance(val,dict)returnval
[docs]defset_campaign(self,campaign:bascenev1.Campaign,index:int)->None:"""For use by baclassic.Campaign when adding levels to itself. (internal)"""self._campaign=weakref.ref(campaign)self._index=index