ba_data update

This commit is contained in:
Ayush Saini 2024-01-27 21:25:16 +05:30
parent 2174ae566d
commit 7ba24ecbcf
146 changed files with 1756 additions and 347 deletions

View file

@ -9,7 +9,7 @@
"Danish": "Dansk",
"Dutch": "Nederlands",
"Esperanto": "Esperanto",
"Filipino": "Tagalog ",
"Filipino": "Wikang Tagalog ",
"French": "Français",
"German": "Deutsch",
"Gibberish": "Abuktarika",
@ -44,10 +44,12 @@
"\"9۝ÅЇρѺ۝ƬǀGΞЯ",
"\"Unknown\"",
"/in/dev/",
"0Globalsters",
"1.4.139",
"11",
"123",
"123123123",
"1234abcdS",
"1Platinumpatty",
"1SpaZ",
"228варенье",
@ -148,12 +150,14 @@
"Shadiq Ali",
"alireza",
"alirezaalidokht",
"AliSh8787",
"ALISSON",
"Virgile Allard",
"Allinol",
"ahmed alomari",
"Alonso",
"Alp",
"Alpcik910",
"Alper",
"Alpha",
"AlphaT",
@ -250,6 +254,7 @@
"awase2020@gmail.com",
"sev alaslam Awd",
"Axel",
"Aynursargi",
"ayub",
"masoud azad(fireboy)",
"Md azar",
@ -260,6 +265,7 @@
"Myth B.",
"B4likeBefore",
"Praveen Babu",
"Badmoss",
"Baechu",
"Balage8",
"BalaguerM",
@ -326,6 +332,7 @@
"Daniel Block",
"BlueBlur",
"bob bobber",
"Bombay De Bom",
"BomBillo",
"The Bomboler 💣",
"bombsquad",
@ -346,6 +353,7 @@
"bouabdellah",
"Antoine Boulanger",
"Thomas Bouwmeester",
"Boxan",
"Ali x boy",
"boyhero7779",
"Bořivoj",
@ -473,6 +481,7 @@
"Die or Dead",
"Привет от детей DeadLine",
"deepjith",
"DEFENDER",
"Defiant",
"dekarl",
"deliciouspudding43",
@ -554,6 +563,7 @@
"Emanuel_12",
"Emil",
"Kürti Emil",
"55Hamza Emin",
"Muhammed emir",
"Muhammet Emir",
"EmirSametEr",
@ -825,6 +835,7 @@
"Jacek",
"Jack556",
"Jhon Jairo",
"jakecato1602@gmail.com",
"wahid jamaludin",
"Tarun Jangra",
"Aleksandar Janic",
@ -864,6 +875,7 @@
"joke",
"Jonatas",
"Jop",
"Jor",
"JoseANG3L",
"Joseetion",
"Joseph",
@ -1025,6 +1037,7 @@
"Loex",
"Loko",
"Longkencok",
"longmouses",
"杰瑞 longmouses",
"looooooooou",
"LordHiohi",
@ -1083,6 +1096,8 @@
"Wagdy mamdouh",
"Mani",
"Manimutharu",
"Manmath",
"ManmathTheGreat",
"Ahmed Mansy",
"Manu",
"Mapk58",
@ -1188,6 +1203,7 @@
"mr",
"mr.Dark",
"Mr.Smoothy",
"MR0000000001",
"MrDaniel715",
"MrGlu10free",
"Mrmaxmeier",
@ -1503,6 +1519,7 @@
"Guilherme Santana",
"Santiago",
"Ivan Santos :)",
"santosamerica880@gmail.com",
"Diamond Sanwich",
"SAO_OMH",
"Dimas Saptandi",
@ -1654,6 +1671,7 @@
"Tiberiu",
"Cristian Ticu",
"Robert Tieber",
"TieDan",
"TIGEE",
"Tim",
"Tingis2",
@ -1679,6 +1697,7 @@
"Konstantin Tsvetkov",
"Kontantin Tsvetkov",
"Tudikk",
"Romioza TV",
"Jan Tymll",
"Zacker Tz",
"Zoltán Tóth",
@ -1730,6 +1749,7 @@
"Vaibhav Wakchaure",
"Simon Wang",
"Will Wang",
"WeanCZ",
"Tilman Weber",
"webparham",
"Wesley",
@ -1795,6 +1815,7 @@
"Z@p€g@m€r",
"Zac",
"Dawn Zac",
"zahra",
"Zaidan64GT",
"Zain",
"Zajle",
@ -1804,6 +1825,7 @@
"ZaraMax",
"zecharaiah",
"Daniele Zennaro",
"Zenotaiko",
"zFliws",
"zfuw668",
"Alex Zhao",
@ -1819,6 +1841,7 @@
"Lukáš Zounek",
"ZOUZ",
"ZpeedTube",
"Zwizard",
"|_Jenqa_|",
"¥¥S.A.N.A¥",
"Danijel Ćelić",
@ -1913,6 +1936,7 @@
"علی",
"سيد عمر",
"عيسى",
"مجتبی",
"اللهم صل على محمد وآل محمد",
"امیر محمد",
"هادی مرادی",

View file

@ -336,6 +336,8 @@
"getMoreGamesText": "الحصول على المزيد من الألعاب",
"titleText": "إضافة لعبة"
},
"addToFavoritesText": "الإضافة إلى المفضلات",
"addedToFavoritesText": ".إلى المفضلات '${NAME}' تمت إضافة",
"allText": "جميع",
"allowText": "السماح",
"alreadySignedInText": "تم تسجيل الدخول من حسابك من جهاز آخر.\n يرجى تبديل الحسابات أو إغلاق اللعبة على الأجهزة الأخرى\n وحاول مرة أخرى.",
@ -565,6 +567,8 @@
"disableXInputDescriptionText": "يسمح أكثر من 4 وحدات تحكم ولكن قد لا تعمل كذلك.",
"disableXInputText": "xinput تعطيل",
"disabledText": "معطل",
"discordFriendsText": "تريد المزيد من الناس للعب معهم؟\n!إنضم إلى سيرفر الديسكورد و أوجد اصدقاء جدد",
"discordJoinText": "إنضم إلى الديسكورد",
"doneText": "تم",
"drawText": "تعادل",
"duplicateText": "مكرر",
@ -750,6 +754,7 @@
"manualYourLocalAddressText": "عنوانك المباشر",
"nearbyText": "الأقرب",
"noConnectionText": "<لا يوجد اتصال>",
"noPartiesAddedText": "لا مجموعات مضافة",
"otherVersionsText": "(اصدارات اخرى)",
"partyCodeText": "رمز السيرفر",
"partyInviteAcceptText": "قبول",
@ -1064,6 +1069,7 @@
"noContinuesText": "(لا يستمر)",
"noExternalStorageErrorText": "لم يتم العثور على وحدة تخزين خارجية على هذا الجهاز",
"noGameCircleText": "خطأ: لم يتم تسجيل الدخول الئ gamecircle",
"noPluginsInstalledText": "لا إضافات مثبتة",
"noScoresYetText": "لا نقاط حتى الآن.",
"noServersFoundText": "لا توجد اي سيرفرات.",
"noThanksText": "لا شكرا",

View file

@ -337,6 +337,8 @@
"getMoreGamesText": "Атрымаць больш гульняў...",
"titleText": "Дадаць гульню"
},
"addToFavoritesText": "Дадаць у абранае",
"addedToFavoritesText": "'${NAME}' дададзены ў абранае.",
"allText": "Усё",
"allowText": "Дазволіць",
"alreadySignedInText": "Ваш уліковы запіс увайшоў з іншай прылады;\nкалі ласка, пераключыце ўліковыя запісы альбо зачыніце гульню на вашым\nіншыя прылады і паспрабуйце яшчэ раз",
@ -755,6 +757,7 @@
"manualYourLocalAddressText": "Ваш лакальны адрас:",
"nearbyText": "Побач",
"noConnectionText": "<няма злучэння>",
"noPartiesAddedText": "Партыі не дададзены",
"otherVersionsText": "(іншыя версіі)",
"partyCodeText": "Код вечарыны",
"partyInviteAcceptText": "Згадзіцца",
@ -1073,6 +1076,7 @@
"noContinuesText": "(без працягу)",
"noExternalStorageErrorText": "Знешняя памяць не знойдзена",
"noGameCircleText": "Памылка: вы не ўвайшлі ў GameCircle",
"noPluginsInstalledText": "Убудовы не ўстаноўлены",
"noProfilesErrorText": "У вас няма ніводнага профіля, таму вас будуць называць '${NAME}'.\nЗайдзіце ў \"Налады -> Профілі\", каб стварыць уласны профіль.",
"noScoresYetText": "Вынікаў пакуль няма.",
"noServersFoundText": "Серверы не знойдзены.",

View file

@ -339,6 +339,8 @@
"getMoreGamesText": "获取更多游戏模式…",
"titleText": "添加比赛"
},
"addToFavoritesText": "添加到收藏",
"addedToFavoritesText": "将'${NAME}'添加到收藏",
"allText": "全部",
"allowText": "允许",
"alreadySignedInText": "您的账号已在其他设备登录;\n请切换账号或者退出已登录的设备\n然后再试一次",
@ -758,6 +760,7 @@
"manualYourLocalAddressText": "本地地址:",
"nearbyText": "附近",
"noConnectionText": "<无连接>",
"noPartiesAddedText": "没有加入派对",
"otherVersionsText": "(其他版本)",
"partyCodeText": "派对代码",
"partyInviteAcceptText": "接受",
@ -1075,6 +1078,7 @@
"noContinuesText": "(无可继续)",
"noExternalStorageErrorText": "该设备上未发现外部存储器",
"noGameCircleText": "错误未登入GameCircle",
"noPluginsInstalledText": "没有安装插件",
"noProfilesErrorText": "您没有玩家档案,所以还得忍受“${NAME}”这个名字。\n进入设置->玩家档案,为自己创建档案。",
"noScoresYetText": "还未有得分记录。",
"noServersFoundText": "未找到服务器",
@ -1292,6 +1296,7 @@
"netTestingText": "网络测试",
"resetText": "恢复默认值",
"showBombTrajectoriesText": "显示炸弹轨迹",
"showDemosWhenIdleText": "当游戏空闲时播放演示画面",
"showDevConsoleButtonText": "显示开发者控制台按钮",
"showInGamePingText": "显示游戏延迟",
"showPlayerNamesText": "显示玩家名字",

View file

@ -29,6 +29,7 @@
"signInWithGooglePlayText": "用play商店登入",
"signInWithTestAccountInfoText": "(舊有的帳號登入方式;使用後來的創設的帳號)",
"signInWithTestAccountText": "用測試帳號登入",
"signInWithText": "通過${SERVICE}登錄",
"signInWithV2InfoText": "(可用於所有平臺的賬戶)",
"signInWithV2Text": "使用Bombsquad賬戶登入",
"signOutText": "登出",
@ -332,6 +333,8 @@
"getMoreGamesText": "獲取更多比賽模式",
"titleText": "新增比賽"
},
"addToFavoritesText": "添加到收藏",
"addedToFavoritesText": "已將'${NAME}'添加到收藏",
"allText": "全部",
"allowText": "允許",
"alreadySignedInText": "你的賬號已在其他設備上登錄\n請退出其他設備的登錄\n然後重試",
@ -558,6 +561,8 @@
"disableXInputDescriptionText": "允許使用四個以上的控制器,但可能不會正常工作",
"disableXInputText": "禁用XInput",
"disabledText": "禁用",
"discordFriendsText": "想要尋找新的朋友一起遊玩嗎?\n快來加入我們的Discord社區發現新夥伴",
"discordJoinText": "加入Discord社區",
"doneText": "完成",
"drawText": "平局",
"duplicateText": "複製",
@ -743,6 +748,7 @@
"manualYourLocalAddressText": "本地地址:",
"nearbyText": "附近",
"noConnectionText": "<無連接>",
"noPartiesAddedText": "未添加派對",
"otherVersionsText": "(其他版本)",
"partyCodeText": "派對代碼",
"partyInviteAcceptText": "接受",
@ -814,6 +820,7 @@
"alwaysText": "總是",
"fullScreenCmdText": "全屏顯示Cmd-F",
"fullScreenCtrlText": "全屏顯示Ctrl-F",
"fullScreenText": "全屏",
"gammaText": "Gamma",
"highText": "高",
"higherText": "最高",
@ -1056,7 +1063,9 @@
"noContinuesText": "(無可繼續)",
"noExternalStorageErrorText": "該設備未發現外部存儲器",
"noGameCircleText": "錯誤未登錄GameCircle",
"noPluginsInstalledText": "未安裝插件",
"noScoresYetText": "沒有得分記錄",
"noServersFoundText": "未找到服務器",
"noThanksText": "不,謝謝",
"noTournamentsInTestBuildText": "注意:此測試版本的比賽分數將會被作廢",
"noValidMapsErrorText": "沒有發現該比賽類型的有效地圖",
@ -1358,6 +1367,8 @@
"storeText": "商店",
"submitText": "提交",
"submittingPromoCodeText": "正在提交代碼...",
"successText": "大功告成!",
"supportEmailText": "如果您在使用應用中遇到任何問題,\n請發送郵件至${EMAIL}.",
"teamNamesColorText": "團隊名稱/顏色...",
"telnetAccessGrantedText": "Telnet訪問以啟用",
"telnetAccessText": "檢測到Telnet訪問是否允許",
@ -1811,6 +1822,7 @@
"unlockThisInTheStoreText": "這必須從商店解鎖",
"unlockThisProfilesText": "如需創建超過${NUM}個檔案,你需要",
"unlockThisText": "你需要這些來解鎖",
"unsupportedControllerText": "抱歉,不支持控制器\"${NAME}\"",
"unsupportedHardwareText": "抱歉,此版本的遊戲不支持該硬件",
"upFirstText": "進入第一局",
"upNextText": "進入比賽${COUNT}第二局",
@ -1822,6 +1834,7 @@
"usingItunesText": "使用音樂軟件設置背景音樂...",
"v2AccountLinkingInfoText": "要鏈接 V2 帳戶,請使用“管理帳戶”按鈕。",
"validatingTestBuildText": "測試版驗證中",
"viaText": "其他賬戶",
"victoryText": "勝利!",
"voteDelayText": "你不能在${NUMBER}內發起一個新的投票",
"voteInProgressText": "已經有一個投票正在進行中了",

View file

@ -1297,6 +1297,7 @@
"netTestingText": "Testování sítě",
"resetText": "Obnovit",
"showBombTrajectoriesText": "Ukazovat trajektorii bomb",
"showDemosWhenIdleText": "Zobrazit ukázky při nečinnosti",
"showDevConsoleButtonText": "Ukaž tlačítko vývojářské konzoly",
"showInGamePingText": "Ukazovat ping při hře",
"showPlayerNamesText": "Ukazovat jména hráčů",

View file

@ -9,6 +9,7 @@
"changeOncePerSeasonError": "U moet wachten tot het volgende seizoen om dit te veranderen (${NUM} dagen)",
"customName": "Aangepaste naam",
"deviceSpecificAccountText": "U gebruikt nu het apparaat-specifieke account: ${NAME}",
"googlePlayGamesAccountSwitchText": "Als je een ander Google-account \nwilt gebruiken, gebruik dan de Google Play Games-app om over te schakelen.",
"linkAccountsEnterCodeText": "Voer Code In",
"linkAccountsGenerateCodeText": "Genereer Code",
"linkAccountsInfoText": "(deel voortgang over verschillende platformen)",
@ -16,6 +17,7 @@
"linkAccountsInstructionsText": "Om twee accounts te koppelen, genereer je bij een\ndaar van een code die je invult bij de ander.\nVoortgang en inventaris worden dan samengevoegd.\nJe kan maximaal ${COUNT} accounts koppelen.\n\nBELANGRIJK: Koppel alleen accounts die jij bezit!\nAls je accounts van vrienden koppelt kan je niet \nmeer tegelijkertijd spelen!\n\nOok: Dit kan momenteel niet ongedaan gemaakt worden, dus pas op!",
"linkAccountsText": "Koppel Accounts",
"linkedAccountsText": "Verbonden Accounts:",
"manageAccountText": "Beheer account",
"nameChangeConfirm": "Verander je account naam naar ${NAME}?",
"notLoggedInText": "<niet ingelogd>",
"resetProgressConfirmNoAchievementsText": "Dit reset uw co-op campagne voortgang en\nlokale high-scores (maar niet uw tickets).\nDit kan niet ongedaan worden gemaakt. Weet u het zeker?",
@ -31,6 +33,7 @@
"signInWithGooglePlayText": "Log in met Google Play",
"signInWithTestAccountInfoText": "(oudere account type; gebruik device account gaat door)",
"signInWithTestAccountText": "Log in met test account",
"signInWithText": "Log in met ${SERVICE}",
"signInWithV2InfoText": "(een account dat op alle platforms werkt)",
"signInWithV2Text": "Inloggen met een BombSquad rekening",
"signOutText": "Log Uit",
@ -43,6 +46,7 @@
"titleText": "Profiel",
"unlinkAccountsInstructionsText": "Selecteer een account om los te koppelen.",
"unlinkAccountsText": "Koppel accounts los.",
"unlinkLegacyV1AccountsText": "Ontkoppel oude (V1) accounts",
"v2LinkInstructionsText": "Gebruik deze link om een account aan te maken of in te loggen.",
"viaAccount": "(via account ${NAME})",
"youAreLoggedInAsText": "Je bent ingelogd als:",
@ -332,10 +336,14 @@
"achievementsRemainingText": "Resterende Prestaties:",
"achievementsText": "Prestaties",
"achievementsUnavailableForOldSeasonsText": "Sorry, prestatie gegevens voor oude seizoenen zijn niet beschikbaar.",
"activatedText": "${THING} geactiveerd.",
"addGameWindow": {
"getMoreGamesText": "Verkrijg Meer Spellen...",
"titleText": "Voeg Game toe"
},
"addToFavoritesText": "Toevoegen aan favorieten",
"addedToFavoritesText": "'${NAME}' toegevoegd aan Favorieten.",
"allText": "Alle",
"allowText": "Toestaan",
"alreadySignedInText": "Uw account is al ingelogd op een ander apparaat;\nVerander van account of sluit het spel op uw andere\napparaten en probeer het opnieuw.",
"apiVersionErrorText": "Kan module ${NAME} niet laden; deze gebruikt api-versie ${VERSION_USED}; benodigd is ${VERSION_REQUIRED}.",
@ -371,6 +379,7 @@
"chatMutedText": "berichten gedempt",
"chatUnMuteText": "maak de chat ongedaan",
"choosingPlayerText": "<speler kiezen>",
"codesExplainText": "Codes worden door de \nontwikkelaar verstrekt om accountproblemen te diagnosticeren en te corrigeren.",
"completeThisLevelToProceedText": "U moet dit level voltooien\nom door te gaan!",
"completionBonusText": "Voltooiing Bonus",
"configControllersWindow": {
@ -454,6 +463,7 @@
"titleText": "Configureer Touchscreen",
"touchControlsScaleText": "Touch Besturing Schalen"
},
"configureDeviceInSystemSettingsText": "${DEVICE} kan worden geconfigureerd in de Systeeminstellingen-app.",
"configureItNowText": "Nu configureren?",
"configureText": "Configureren",
"connectMobileDevicesWindow": {
@ -509,6 +519,7 @@
"welcome2Text": "U kunt ook tickets verdienen van veel van dezelfde activiteiten.\nTickets kunnen gebruikt worden om mee te doen aan toernooien,\nvoor het vrijspelen van nieuwe karakters, speelvelden, mini-spellen en meer.",
"yourPowerRankingText": "Uw Macht Klassement:"
},
"copyConfirmText": "Gekopieerd naar het klembord.",
"copyOfText": "${NAME} Kopie",
"copyText": "Kopiëren",
"copyrightText": "© 2013 Eric Froemling",
@ -562,7 +573,9 @@
"deleteText": "Verwijder",
"demoText": "demonstratie",
"denyText": "Weigeren",
"deprecatedText": "Verouderd",
"desktopResText": "Bureaublad Resolutie",
"deviceAccountUpgradeText": "Waarschuwing: u bent ingelogd met een apparaataccount (${NAME}). \nApparaataccounts worden in een toekomstige update verwijderd. \nUpgrade naar een V2-account als u uw voortgang wilt \nbehouden.",
"difficultyEasyText": "Makkelijk",
"difficultyHardOnlyText": "Alleen moeilijk",
"difficultyHardText": "Moeilijk",
@ -571,6 +584,9 @@
"disableRemoteAppConnectionsText": "Schakel Afstandsbediening-App connecties uit",
"disableXInputDescriptionText": "Staat meer dan 4 controllers toe, maar werkt misschien niet helemaal goed.",
"disableXInputText": "Schakel XInput uit",
"disabledText": "Gehandicapt",
"discordFriendsText": "Wil je op zoek naar nieuwe mensen om mee te spelen? \nSluit je aan bij onze Discord en vind nieuwe vrienden!",
"discordJoinText": "Sluit je aan bij de onenigheid",
"doneText": "Klaar",
"drawText": "Gelijk",
"duplicateText": "Kopieer",
@ -644,12 +660,15 @@
"useMusicFolderText": "Map met Muziek Bestanden"
},
"editText": "Bewerk",
"enabledText": "Ingeschakeld",
"endText": "Einde",
"enjoyText": "Geniet!",
"epicDescriptionFilterText": "${DESCRIPTION} In epische slow motion.",
"epicNameFilterText": "Epische ${NAME}",
"errorAccessDeniedText": "toegang geweigerd",
"errorDeviceTimeIncorrectText": "De tijd van je apparaat is ${HOURS} uur \nonjuist. Dit zal waarschijnlijk problemen \nveroorzaken. Controleer uw tijd- en tijdzone-instellingen.",
"errorOutOfDiskSpaceText": "schuifruimte vol",
"errorSecureConnectionFailText": "Kan geen veilige cloudverbinding tot stand brengen; netwerkfunctionaliteit kan mislukken.",
"errorText": "Fout",
"errorUnknownText": "onbekende fout",
"exitGameText": "${APP_NAME} Verlaten?",
@ -764,6 +783,7 @@
"manualYourLocalAddressText": "Uw lokale adres:",
"nearbyText": "Dichtbij",
"noConnectionText": "<geen verbinding>",
"noPartiesAddedText": "Geen partijen toegevoegd",
"otherVersionsText": "(andere versies)",
"partyCodeText": "Partijcode",
"partyInviteAcceptText": "Accepteren",
@ -838,15 +858,19 @@
"youHaveText": "U heeft ${COUNT} tickets"
},
"googleMultiplayerDiscontinuedText": "Sorry, de Google Play multi-player functie is nu even niet beschikbaar. \nIk werk hard aan een vervanger. \nProbeer nu alsjeblieft een andere connectie mogelijkheid.\n-Eric",
"googlePlayPurchasesNotAvailableText": "Google Play-aankopen zijn niet beschikbaar. \nMogelijk moet u uw winkel-app updaten.",
"googlePlayServicesNotAvailableText": "Google Play-services zijn niet beschikbaar. \nSommige app-functionaliteit is mogelijk uitgeschakeld.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Altijd",
"fullScreenCmdText": "Volledig scherm (Cmd-F)",
"fullScreenCtrlText": "Volledig scherm (Cmd-F)",
"fullScreenText": "Volledig scherm",
"gammaText": "Gamma",
"highText": "Hoog",
"higherText": "Hoger",
"lowText": "Laag",
"maxFPSText": "Maximale FPS",
"mediumText": "Gemiddeld",
"neverText": "Nooit",
"resolutionText": "Resolutie",
@ -1041,6 +1065,7 @@
"creditsText": "Credits",
"demoMenuText": "Voorbeeld Menu",
"endGameText": "Beëindig Spel",
"endTestText": "Einde proef",
"exitGameText": "Verlaat Spel",
"exitToMenuText": "Verlaat naar menu?",
"howToPlayText": "Hoe te Spelen",
@ -1061,6 +1086,7 @@
"maxConnectionsText": "Max Connecties",
"maxPartySizeText": "Max Partij Grootte",
"maxPlayersText": "Max Spelers",
"merchText": "Koopwaar!",
"modeArcadeText": "Speelhal Modus",
"modeClassicText": "Klassieke Modus",
"modeDemoText": "Demo Modus",
@ -1091,8 +1117,10 @@
"noExternalStorageErrorText": "Geen externe opslag gevonden op dit apparaat",
"noGameCircleText": "Fout: niet aangemeld bij GameCircle",
"noJoinCoopMidwayText": "Je kan niet halverwegen inspringen bij coöperatieve spellen.",
"noPluginsInstalledText": "Geen plug-ins geïnstalleerd",
"noProfilesErrorText": "U heeeft geen speler profielen, dus zit u vast aan '${NAME}'.\nGa naar Instellingen->Speler Profielen om een profiel te maken voor uzelf.",
"noScoresYetText": "Nog geen scores.",
"noServersFoundText": "Geen servers gevonden.",
"noThanksText": "Nee Bedankt",
"noTournamentsInTestBuildText": "PAS OP: De punten van deze test tournament worden niet meegerekend.",
"noValidMapsErrorText": "Geen geldige gebieden gevonden voor dit speltype.",
@ -1102,6 +1130,7 @@
"notSignedInErrorText": "Je moet inloggen om dit te doen.",
"notSignedInGooglePlayErrorText": "Je moet ingelogd zijn met Google Play om dit te doen.",
"notSignedInText": "niet ingelogd",
"notUsingAccountText": "Opmerking: ${SERVICE}-account negeren. \nGa naar 'Account -> Inloggen met ${SERVICE}' als je hier gebruik van wilt maken.",
"nothingIsSelectedErrorText": "Er is niks geselecteerd!",
"numberText": "#${NUMBER}",
"offText": "Uit",
@ -1163,7 +1192,14 @@
"playlistsText": "Speellijsten",
"pleaseRateText": "Als u geniet van ${APP_NAME}, zou u dan een moment willen\nnemen om een waardering te geven of recensie te schrijven.\nDit geeft ons nuttige feedback voor toekomstige ontwikkelingen.\n\nBedankt!\n-eric",
"pleaseWaitText": "Even geduld...",
"pluginClassLoadErrorText": "Fout bij het laden van plug-inklasse '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Fout bij het starten van plug-in '${PLUGIN}': ${ERROR}",
"pluginSettingsText": "Plugin-instellingen",
"pluginsAutoEnableNewText": "Schakel nieuwe plug-ins automatisch in",
"pluginsDetectedText": "Nieuw contact gedecteerd. Schakel in/configureer in de instellingen.",
"pluginsDisableAllText": "Schakel alle plug-ins uit",
"pluginsEnableAllText": "Schakel alle plug-ins in",
"pluginsRemovedText": "${NUM} plug-in(s) niet meer gevonden.",
"pluginsText": "Contacten",
"practiceText": "Oefenen",
"pressAnyButtonPlayAgainText": "Druk een knop om opnieuw te spelen...",
@ -1259,6 +1295,8 @@
"runText": "Ren",
"saveText": "Opslaan",
"scanScriptsErrorText": "Fout(en) bij inlezen scripts; zie log voor details.",
"scanScriptsMultipleModulesNeedUpdatesText": "${PATH} en ${NUM} andere module(s) moeten worden bijgewerkt voor api ${API}.",
"scanScriptsSingleModuleNeedsUpdatesText": "${PATH} moet worden bijgewerkt voor API ${API}.",
"scoreChallengesText": "Score Uitdagingen",
"scoreListUnavailableText": "Score lijst niet beschikbaar.",
"scoreText": "Score",
@ -1305,6 +1343,9 @@
"netTestingText": "Netwerk Testen",
"resetText": "Reset",
"showBombTrajectoriesText": "Baan van de bom weergeven",
"showDemosWhenIdleText": "Toon demo's wanneer deze niet actief zijn",
"showDevConsoleButtonText": "Toon dev-consoleknop",
"showInGamePingText": "Toon in-game ping",
"showPlayerNamesText": "Toon Namen Spelers",
"showUserModsText": "Toon Aanpassingen Map",
"titleText": "Geavanceerd",
@ -1399,6 +1440,8 @@
"storeText": "Winkel",
"submitText": "Voorleggen",
"submittingPromoCodeText": "Code verzenden ...",
"successText": "Succes!",
"supportEmailText": "Als u problemen ondervindt met de app, \nkunt u een e-mail sturen naar ${EMAIL}.",
"teamNamesColorText": "Teamnamen / kleuren ...",
"teamsText": "Teams",
"telnetAccessGrantedText": "Telnet toegang ingeschakeld.",
@ -1427,6 +1470,7 @@
"tournamentStandingsText": "Toernooi Stand",
"tournamentText": "Toernooi",
"tournamentTimeExpiredText": "Toernooi Tijd Verlopen",
"tournamentsDisabledWorkspaceText": "Toernooien zijn uitgeschakeld als werkruimten actief zijn. \nOm toernooien opnieuw in te schakelen, schakelt u uw werkruimte uit en start u opnieuw op.",
"tournamentsText": "Toernooien",
"translations": {
"characterNames": {
@ -1598,6 +1642,7 @@
"Italian": "Italiaans",
"Japanese": "Japans",
"Korean": "Koreaans",
"Malay": "Maleis",
"Persian": "Perzisch",
"Polish": "Pools",
"Portuguese": "Portugees",
@ -1878,6 +1923,7 @@
"unlockThisInTheStoreText": "Hiervoor moet een aankoop gedaan worden in de winkel.",
"unlockThisProfilesText": "Om meer dan ${NUM} profielen te maken heb je nodig:",
"unlockThisText": "Om dit te ontgrendelen, moet u:",
"unsupportedControllerText": "Sorry, controller \"${NAME}\" wordt niet ondersteund.",
"unsupportedHardwareText": "Sorry, deze hardware wordt niet ondersteunt door deze versie van het spel.",
"upFirstText": "Als eerste:",
"upNextText": "Daarna volgt in potje ${COUNT}:",
@ -1888,8 +1934,10 @@
"usesExternalControllerText": "Dit spel maakt gebruik van een externe controller als input.",
"usingItunesText": "De muziek app wordt gebruikt voor de muziek...",
"usingItunesTurnRepeatAndShuffleOnText": "Zorg er er voor dat shuffle AAN staat en herhalen op ALLES staat in iTunes.",
"v2AccountLinkingInfoText": "Om V2-accounts te koppelen, gebruikt u de knop 'Account beheren'.",
"validatingBetaText": "Beta wordt Gevalideerd...",
"validatingTestBuildText": "Valideren Test Versie...",
"viaText": "via",
"victoryText": "Overwinning!",
"voteDelayText": "Je kan geen andere stemming starten voor ${NUMBER} seconden",
"voteInProgressText": "Er is al een stemming bezig.",
@ -1922,6 +1970,7 @@
},
"waveText": "Ronde",
"wellSureText": "Maar Natuurlijk!",
"whatIsThisText": "Wat is dit?",
"wiimoteLicenseWindow": {
"licenseTextScale": 0.62,
"titleText": "DarwiinRemote Copyright"
@ -1946,6 +1995,8 @@
"winsPlayerText": "${NAME} Wint!",
"winsTeamText": "${NAME} Wint!",
"winsText": "${NAME} Wint!",
"workspaceSyncErrorText": "Fout bij synchroniseren van ${WORKSPACE}. Zie logboek voor details.",
"workspaceSyncReuseText": "Kan ${WORKSPACE} niet synchroniseren. Hergebruik van de vorige gesynchroniseerde versie.",
"worldScoresUnavailableText": "Wereldwijde scores niet beschikbaar.",
"worldsBestScoresText": "'s Werelds Beste Scores",
"worldsBestTimesText": "'s Werelds Beste Tijden",

View file

@ -1277,6 +1277,7 @@
"netTestingText": "Network Testing",
"resetText": "Reset",
"showBombTrajectoriesText": "Show Bomb Trajectories",
"showDemosWhenIdleText": "Show Demos When Idle",
"showDevConsoleButtonText": "Show Dev Console Button",
"showInGamePingText": "Show In-Game Ping",
"showPlayerNamesText": "Show Player Names",

View file

@ -331,9 +331,11 @@
"achievementsUnavailableForOldSeasonsText": "Pasensya na, hindi available ang mga detalye ng achievements para sa mga lumang seasons.",
"activatedText": "Na-aktibo ang ${THING}.",
"addGameWindow": {
"getMoreGamesText": "Kukuha ng higit pang mga laro…",
"getMoreGamesText": "Kumuha ng Higit pang mga Laro…",
"titleText": "Magdagdag Ng Laro"
},
"addToFavoritesText": "Ilagay sa Mga Paborito",
"addedToFavoritesText": "Nakalagay na ang '${NAME}' sa Mga Paborito.",
"allText": "Lahat",
"allowText": "Payagan",
"alreadySignedInText": "Ang iyong account ay naka-sign in mula sa isa pang device;\nMangyaring lumipat ng mga accounts o isara ang laro sa iyong\niba pang mga device at subukan muli.",
@ -341,16 +343,16 @@
"audioSettingsWindow": {
"headRelativeVRAudioInfoText": "(Lalabas ang “Auto” ng ito kapag nakasaksak ang headphones)",
"headRelativeVRAudioText": "Head-Relative VR Audio",
"musicVolumeText": "Volume ng Musika",
"musicVolumeText": "Lakas ng Musika",
"soundVolumeText": "Lakas ng Tunog",
"soundtrackButtonText": "Mga Soundtrack",
"soundtrackDescriptionText": "(I-assign ang iyong sariling musika para magtugtug kapag naglalaro)",
"titleText": "Audio"
"titleText": "Tugtugan"
},
"autoText": "Auto",
"backText": "Bumalik",
"banThisPlayerText": "I-ban ang Manlalarong Ito",
"bestOfFinalText": "Pinakamahusay-sa-${COUNT} Final",
"banThisPlayerText": "Bawalin ang Manlalarong Ito",
"bestOfFinalText": "Wakas ng Pinakamahusay-sa-${COUNT}",
"bestOfSeriesText": "Pinakamahusay sa ${COUNT} series:",
"bestOfUseFirstToInstead": 0,
"bestRankText": "Ang iyong pinakamahusay ay #${RANK}",
@ -366,10 +368,10 @@
"challengeEndedText": "Natapos na ang challenge na ito.",
"chatMuteText": "Tahimikin Ang Chat Mo",
"chatMutedText": "Na-mute ang Chat",
"chatUnMuteText": "I-unmute ang Chat",
"chatUnMuteText": "Ipaganahin Mo Na ang Chat",
"choosingPlayerText": "<pumipili ng manlalaro>",
"codesExplainText": "Binigay ng developer ang mga codes \npara i-diagnose at i-tama ang mga issues sa account.",
"completeThisLevelToProceedText": "I complete mo muna\nang level na ito bago ka mag-proceed!",
"codesExplainText": "Binigay ng developer ang mga kowd \npara kilalanin at maitama ang mga problema sa account.",
"completeThisLevelToProceedText": "Tapusin mo muna\nang antas na ito bago tumuloy!",
"completionBonusText": "Bonus sa Pagkumpleto nito:",
"configControllersWindow": {
"configureControllersText": "I-configure ang mga Controller",
@ -380,7 +382,7 @@
"ps3Text": "PS3 Controllers",
"titleText": "Mga Controller",
"wiimotesText": "Wiimotes",
"xbox360Text": "Xbox 360 Controllers"
"xbox360Text": "Controllers ng Xbox 360"
},
"configGamepadSelectWindow": {
"androidNoteText": "Tandaan: nag-iiba-iba ang suporta sa controller sa mga devices at bersyon ng Andriod.",
@ -396,17 +398,17 @@
"autoRecalibrateDescriptionText": "(paganahin ito kung ang iyong karakter ay hindi gumagalaw ng buong bilis)",
"autoRecalibrateText": "Auto-Recalibrate Analog Stick",
"axisText": "aksis",
"clearText": "i-bura",
"clearText": "ibura",
"dpadText": "DPAD",
"extraStartButtonText": "Extra na Start Button",
"ifNothingHappensTryAnalogText": "Kapag walang gumagana, i-try na i-assign sa analog stick.",
"ifNothingHappensTryDpadText": "Kapag hindi gumana, i-try na i-assign sa d-pad.",
"ignoreCompletelyDescriptionText": "(pigilan ang controller na ito na maapektuhan ang alinman sa laro o mga menu)",
"ignoreCompletelyText": "Huwag Pansinin",
"ignoredButton1Text": "Pindutan Na Di Pansinin 1",
"ignoredButton2Text": "Pindutan Na Di Pansinin 2",
"ignoredButton3Text": "Pindutan Na Di Pansinin 3",
"ignoredButton4Text": "Pindutan Na Di Pansinin 4",
"ignoredButton1Text": "Pindutan Na Di Pinansin 1",
"ignoredButton2Text": "Pindutan Na Di Pinansin 2",
"ignoredButton3Text": "Pindutan Na Di Pinansin 3",
"ignoredButton4Text": "Pindutan Na Di Pinansin 4",
"ignoredButtonDescriptionText": "(gamitin ito para ma-prevent ang home o sync buttons na nakakaapekto sa UI)",
"pressAnyAnalogTriggerText": "Pindutin ang anumang analog trigger…",
"pressAnyButtonOrDpadText": "Pindutin ang anumang pindutan o dpad…",
@ -751,6 +753,7 @@
"manualYourLocalAddressText": "Iyong lokal na address:",
"nearbyText": "Malapit",
"noConnectionText": "<walang koneksyon>",
"noPartiesAddedText": "Walang Nadagdag na mga Partido",
"otherVersionsText": "<iba pang mga bersyon>",
"partyCodeText": "Kowd ng Partido",
"partyInviteAcceptText": "Tanggapin",
@ -776,7 +779,7 @@
"publicText": "Publiko",
"requestingAPromoCodeText": "Humihiling ng kowd...",
"sendDirectInvitesText": "I-send ng direktang imbitasyon",
"shareThisCodeWithFriendsText": "Ibahagi ang code na ito sa iyong mga kaibigan:",
"shareThisCodeWithFriendsText": "Ibahagi ang kowd nito sa iyong mga kaibigan:",
"showMyAddressText": "Ipakita Ang Address Ko",
"startHostingPaidText": "Mag-host ngayon ng ${COST}",
"startHostingText": "Host",
@ -789,7 +792,7 @@
"wifiDirectText": "Wi-Fi Direct",
"worksBetweenAllPlatformsText": "(itoy gumagana sa pagitan ng lahat ng mga platform)",
"worksWithGooglePlayDevicesText": "(itoy gumagana sa mga devices na tumatakbo ang bersyon ng Google Play (android) ng larong ito)",
"youHaveBeenSentAPromoCodeText": "Pinadalhan ka ng promo code na ${APP_NAME}:"
"youHaveBeenSentAPromoCodeText": "Pinadalhan ka ng promo kowd ng ${APP_NAME}:"
},
"getTicketsWindow": {
"freeText": "LIBRE!",
@ -887,7 +890,7 @@
"holdAnyButtonText": "<hawakan ang anumang pindutan>",
"holdAnyKeyText": "<hawakan ang anumang key>",
"hostIsNavigatingMenusText": "- Ang ${HOST} ay nagna-navigate sa mga menu tulad ng isang boss -",
"importPlaylistCodeInstructionsText": "Gamitin ang sumusunod na code upang i-import ang playlist na ito sa ibang lugar:",
"importPlaylistCodeInstructionsText": "Gamitin ang sumusunod na kowd upang i-import ang playlist na ito sa ibang lugar:",
"importPlaylistSuccessText": "Na-import na ${TYPE} na playlist '${NAME}'",
"importText": "I-Import",
"importingText": "Nag-Iimport…",
@ -1054,21 +1057,22 @@
"nameDiedText": "${NAME} ay namatay.",
"nameKilledText": "Pinatay ni ${NAME} si ${VICTIM}",
"nameNotEmptyText": "Hindi pwede ang walang pangalan!",
"nameScoresText": "${NAME} Naka-score!",
"nameScoresText": "Nakaiskor si ${NAME}!",
"nameSuicideKidFriendlyText": "Hindi sinasadyang namatay si ${NAME}.",
"nameSuicideText": "Nagpakamatay si ${NAME}.",
"nameText": "Pangalan",
"nativeText": "Natural",
"newPersonalBestText": "Bagong personal na pinakamahusay!",
"newTestBuildAvailableText": "Available ang isang mas bagong pagsubok na build! (${VERSION} build ${BUILD}).\nKunin ito sa ${ADDRESS}",
"newText": "Bago",
"newText": "Gumawa ng Bago",
"newVersionAvailableText": "Available ang isang mas bagong bersyon ng ${APP_NAME}! (${VERSION})",
"nextAchievementsText": "Mga Susunod na Nakamit:",
"nextLevelText": "Mga Susunod na Level",
"nextLevelText": "Susunod na Antas",
"noAchievementsRemainingText": "- wala",
"noContinuesText": "(hindi i-continue)",
"noExternalStorageErrorText": "Walang nakitang external na storage sa device na ito",
"noGameCircleText": "Error: hindi naka-log in sa GameCircle",
"noPluginsInstalledText": "Hindi Nakabit ang mga Plugin",
"noScoresYetText": "Wala pang score.",
"noServersFoundText": "Walang nahanap na servers.",
"noThanksText": "Salamat Nalang",
@ -1159,7 +1163,7 @@
"pressToSelectProfileText": "pindutin ang ${BUTTONS} upang pumili ng manlalaro",
"pressToSelectTeamText": "pindutin ang ${BUTTONS} para pumili ng team",
"promoCodeWindow": {
"codeText": "Code",
"codeText": "Kowd",
"enterText": "I-enter"
},
"promoSubmitErrorText": "Error sa pagsusumite ng kowd; suriin ang iyong koneksyon sa internet",
@ -1245,12 +1249,12 @@
"pointsText": "Puntos",
"secondsText": "Segundo"
},
"scoreWasText": "(ay nasa ${COUNT})",
"scoreWasText": "(mula sa ${COUNT})",
"selectText": "Pilihin",
"seriesWinLine1PlayerText": "ANG NANALO SA",
"seriesWinLine1TeamText": "ANG NANALO SA",
"seriesWinLine1Text": "ANG NANALO SA",
"seriesWinLine2Text": "SERYENG NITO!",
"seriesWinLine2Text": "SERYE NITO!",
"settingsWindow": {
"accountText": "Account",
"advancedText": "Mga Iba Pa",
@ -1269,7 +1273,7 @@
"disableThisNotice": "(maaari mong i-disable ang notice na ito sa mga advanced na setting)",
"enablePackageModsDescriptionText": "(nagpapagana ng mga karagdagang kakayahan sa pag-modding ngunit hindi pinapagana ang net-play)",
"enablePackageModsText": "Paganahin ang Lokal na Package Mods",
"enterPromoCodeText": "Ilagay ang Code",
"enterPromoCodeText": "Ilagay ang Kowd",
"forTestingText": "Tandaan: ang mga value na ito ay para lamang sa pagsubok at mawawala kapag lumabas ang app.",
"helpTranslateText": "Ang mga pagsasalin na hindi Ingles ng ${APP_NAME} ay isang komunidad\nsuportadong pagsisikap. Kung gusto mong mag-ambag o magtama\nisang pagsasalin, sundan ang link sa ibaba. Salamat!",
"kickIdlePlayersText": "I-kick Ang Mga Idle na Manlalaro",
@ -1280,6 +1284,7 @@
"netTestingText": "Pagsusuri ng Network",
"resetText": "I-reset",
"showBombTrajectoriesText": "Ipakita ang Mga Trajectory ng Bomba",
"showDemosWhenIdleText": "Ipakita Ang Demo Habang Nakatigil",
"showDevConsoleButtonText": "Ipakita ang Dev Console Button",
"showInGamePingText": "Ipakita ang In-Game Ping",
"showPlayerNamesText": "Ipakita ang Mga Pangalan ng Manlalaro",
@ -1289,14 +1294,14 @@
"translationFetchErrorText": "hindi available ang katayuan ng wika",
"translationFetchingStatusText": "sinusuri ang status ng lengguwahe…",
"translationInformMe": "Ipaalam sa akin kapag ang aking wika ay nangangailangan ng mga update",
"translationNoUpdateNeededText": "ang kasalukuyang wika ay makabago; woohoo!",
"translationNoUpdateNeededText": "ang kasalukuyang wika ay makabago na muli; yehey!",
"translationUpdateNeededText": "** ang kasalukuyang wika ay nangangailangan ng mga update!! **",
"vrTestingText": "Testing ng VR"
},
"shareText": "I-share",
"sharingText": "Nagbabahagi….",
"showText": "Ipakita",
"signInForPromoCodeText": "Dapat kang mag-sign in sa isang account para magkabisa ang mga code.",
"signInForPromoCodeText": "Dapat kang mag-sign in sa isang account para magkabisa ang mga kowd.",
"signInWithGameCenterText": "Para gumamit ng Game Center account,\nmag-sign in gamit ang Game Center app.",
"singleGamePlaylistNameText": "${GAME} lang",
"singlePlayerCountText": "1 manlalaro",
@ -1371,7 +1376,7 @@
},
"storeText": "Tindahan",
"submitText": "Ipasa",
"submittingPromoCodeText": "Nagsusumite ng Code...",
"submittingPromoCodeText": "Pinapasa ang Kowd...",
"successText": "Wakas!",
"supportEmailText": "Pag may problema sa app, \npaki-email ang ${EMAIL}.",
"teamNamesColorText": "Mga Pangalan/Kulay ng Team…",
@ -1514,7 +1519,7 @@
"Conquest": "Pagsakop",
"Death Match": "Laban ng Kamatayan",
"Easter Egg Hunt": "Paghahanap ng mga Easter Egg",
"Elimination": "Pagbabawas sa away",
"Elimination": "Pagbabawasan",
"Football": "Putbol",
"Hockey": "Hockey",
"Keep Away": "Layuan Mo",
@ -1522,7 +1527,7 @@
"Meteor Shower": "Ulan ng mga Bulalakaw",
"Ninja Fight": "Labanan ng mga Ninja",
"Onslaught": "Pagsalakay",
"Race": "Takbuan",
"Race": "Takbuhan",
"Runaround": "Bantayan",
"Target Practice": "Pagsasanay ng Patamaan",
"The Last Stand": "Ang Huling Labanan"
@ -1532,44 +1537,44 @@
"Keyboard P2": "Keyboard F2"
},
"languages": {
"Arabic": "Arabe",
"Belarussian": "Belaruso",
"Chinese": "Tsino",
"ChineseTraditional": "Tsinong Tradisyonal",
"Croatian": "Kroatyano",
"Czech": "Tsek",
"Danish": "Makadenmark",
"Dutch": "Olandes",
"English": "Ingles",
"Esperanto": "Esperanto",
"Filipino": "Tagalog",
"Finnish": "Finnish",
"French": "Pranses",
"German": "Aleman",
"Gibberish": "Hindi Naiitindihan na Linguahe",
"Greek": "Griyego",
"Hindi": "Indiyano",
"Hungarian": "Hanggaryan",
"Indonesian": "Indonesiyo",
"Italian": "Italiyano",
"Japanese": "Nippongo",
"Korean": "Koreano",
"Malay": "Malay",
"Persian": "Persyano",
"Polish": "Polish",
"Portuguese": "Portuges",
"Romanian": "Rumano",
"Russian": "Ruso",
"Serbian": "Serbyan",
"Slovak": "Eslobako",
"Spanish": "Espanyol",
"Swedish": "Suweko",
"Tamil": "Tamil",
"Thai": "Siyam",
"Turkish": "Turko",
"Ukrainian": "Ukranyo",
"Venetian": "Benesiya",
"Vietnamese": "Byetnam"
"Arabic": "Wikang Arabik",
"Belarussian": "Wikang Belaruso",
"Chinese": "Wikang Tsino",
"ChineseTraditional": "Wikang Tsinong Tradisyonal",
"Croatian": "Wikang Kroatyano",
"Czech": "Wikang Tsek",
"Danish": "Wikang Denmark",
"Dutch": "Wikang Olandes",
"English": "Wikang Ingles",
"Esperanto": "Wikang Esperanto",
"Filipino": "Wikang Tagalog",
"Finnish": "Wikang Finnish",
"French": "Wikang Pranses",
"German": "Wikang Alemanya",
"Gibberish": "Hindi Maintindihan nito",
"Greek": "Wikang Griyego",
"Hindi": "Wikang Indiyano",
"Hungarian": "Wikang Hanggaryan",
"Indonesian": "Wikang Indonesiyo",
"Italian": "Wikang Italiyano",
"Japanese": "Wikang Hapon",
"Korean": "Wikang Koreano",
"Malay": "Wikang Malay",
"Persian": "Wikang Persyano",
"Polish": "Wikang Polish",
"Portuguese": "Wikang Portuges",
"Romanian": "Wikang Rumano",
"Russian": "Wikang Ruso",
"Serbian": "Wikang Serbyan",
"Slovak": "Wikang Eslobako",
"Spanish": "Wikang Espanyol",
"Swedish": "Wikang Suweko",
"Tamil": "Wikang Tamil",
"Thai": "Wikang Thai",
"Turkish": "Wikang Turko",
"Ukrainian": "Wikang Ukranyo",
"Venetian": "Wikang Benesiya",
"Vietnamese": "Wikang Byetnam"
},
"leagueNames": {
"Bronze": "Tanso",
@ -1580,7 +1585,7 @@
"mapsNames": {
"Big G": "Malaking G",
"Bridgit": "Tawiring Tulay",
"Courtyard": "Looban Patyo",
"Courtyard": "Loobang Patyo",
"Crag Castle": "Kastilyong Bangin",
"Doom Shroom": "Itim na Kabute",
"Football Stadium": "Istadyum",
@ -1598,7 +1603,7 @@
},
"playlistNames": {
"Just Epic": "Epic Lang",
"Just Sports": "Shorts Lang"
"Just Sports": "Sports Lang"
},
"scoreNames": {
"Flags": "Watawat",
@ -1606,10 +1611,10 @@
"Score": "Iskor",
"Survived": "Nakaligtas",
"Time": "Oras",
"Time Held": "Oras na Nahawak"
"Time Held": "Oras na Gaganapin"
},
"serverResponses": {
"A code has already been used on this account.": "Nagamit na ang isang code sa account na ito.",
"A code has already been used on this account.": "Nagamit na ang isang kowd sa account na ito.",
"A reward has already been given for that address.": "Naibigay na ang reward para sa address na iyon.",
"Account linking successful!": "Matagumpay ang pag-link ng account!",
"Account unlinking successful!": "Matagumpay ang pag-unlink ng account!",
@ -1628,9 +1633,9 @@
"Could not establish a secure connection.": "Hindi makapagtatag ng secure na koneksyon.",
"Daily maximum reached.": "Naabot na ang pang-araw-araw ng request.",
"Entering tournament...": "Papasok sa paligsahan…",
"Invalid code.": "Di-wastong code.",
"Invalid code.": "Di-wasto ang kowd.",
"Invalid payment; purchase canceled.": "Di-wastong pagbabayad; kinansela ang pagbili.",
"Invalid promo code.": "Di-wastong promo code.",
"Invalid promo code.": "Di-wasto ang promo kowd.",
"Invalid purchase.": "Di-wastong bilihin",
"Invalid tournament entry; score will be ignored.": "Di-wastong entry sa paligsahan; hindi papansinin ang mga iskor.",
"Item unlocked!": "Na-unlock ang aytem!",
@ -1638,7 +1643,7 @@
"Link account ${ACCOUNT} to this account?\nAll existing data on ${ACCOUNT} will be lost.\nThis can not be undone. Are you sure?": "I-link ang account na ${ACCOUNT} sa account na ito?\nMawawala ang lahat ng umiiral na data sa ${ACCOUNT}.\nHindi na ito maaaring bawiin. Sigurado ka ba?",
"Max number of playlists reached.": "Naabot na ang maximum na bilang ng mga playlist.",
"Max number of profiles reached.": "Naabot na ang maximum na bilang ng mga profile.",
"Maximum friend code rewards reached.": "Naabot ang maximum na mga reward sa code ng kaibigan.",
"Maximum friend code rewards reached.": "Naabot ang maximum na mga reward sa kowd ng kaibigan.",
"Message is too long.": "Ang mensahe ay napakahaba.",
"No servers are available. Please try again soon.": "Walang makakuha na mga server. Pakisubukang muli sa lalong madaling oras.",
"Profile \"${NAME}\" upgraded successfully.": "Matagumpay na na-upgrade ang profile na \"${NAME}\".",
@ -1646,10 +1651,10 @@
"Purchase successful!": "Matagumpay ang pagbili!",
"Received ${COUNT} tickets for signing in.\nCome back tomorrow to receive ${TOMORROW_COUNT}.": "Nakatanggap ng ${COUNT} na tiket para sa pag-sign in.\nBumalik bukas para makatanggap ng ${TOMORROW_COUNT}.",
"Server functionality is no longer supported in this version of the game;\nPlease update to a newer version.": "Hindi na sinusuportahan ang functionality ng server sa bersyong ito ng laro;\nMangyaring mag-update sa isang mas bagong bersyon.",
"Sorry, there are no uses remaining on this code.": "Pasensya na, wala nang natitirang gamit sa code na ito.",
"Sorry, this code has already been used.": "Pasensya na, nagamit na ang code na ito.",
"Sorry, this code has expired.": "Pasensya na, nag-expire na ang code na ito.",
"Sorry, this code only works for new accounts.": "Pasensya na, gumagana lang ang code na ito para sa mga bagong account.",
"Sorry, there are no uses remaining on this code.": "Pasensya na, hindi na magagamit ang kowd na ito.",
"Sorry, this code has already been used.": "Pasensya na, nagamit na ang kowd na ito.",
"Sorry, this code has expired.": "Pasensya na, nag-expire na ang kowd na ito.",
"Sorry, this code only works for new accounts.": "Pasensya na, gumagana lang ang kowd na ito para sa mga bagong account.",
"Still searching for nearby servers; please try again soon.": "Naghahanap pa rin ng mga kalapit na server; mangyaring subukan muli sa lalong madaling oras.",
"Temporarily unavailable; please try again later.": "Pansamantalang hindi magagamit; Subukang muli mamaya.",
"The tournament ended before you finished.": "Natapos ang tournament bago ka natapos.",

View file

@ -340,6 +340,8 @@
"getMoreGamesText": "Obtenir plus de jeux...",
"titleText": "Ajouter un Jeu"
},
"addToFavoritesText": "Ajouter aux favoris",
"addedToFavoritesText": "'${NAME}' a été ajouté aux favoris.",
"allText": "Tous",
"allowText": "Autoriser",
"alreadySignedInText": "Votre compte est connecté sur un autre appareil;\nveuillez changer de compte ou fermez le jeu sur \nles autres appareils et réessayez.",
@ -781,6 +783,7 @@
"manualYourLocalAddressText": "Votre adresse locale:",
"nearbyText": "Proche",
"noConnectionText": "<aucune connexion>",
"noPartiesAddedText": "Aucune partie ajoutée",
"otherVersionsText": "(autres versions)",
"partyCodeText": "Code de la partie",
"partyInviteAcceptText": "Accepter",
@ -1124,6 +1127,7 @@
"noExternalStorageErrorText": "Aucun stockage externe a été trouvé pour cet appareil",
"noGameCircleText": "Erreur: vous n'êtes pas connecté au GameCircle",
"noJoinCoopMidwayText": "Vous ne pouvez pas rejoindre une partie co-cop en plein milieu.",
"noPluginsInstalledText": "Aucun Plug-in installé",
"noProfilesErrorText": "Vous avez aucun profil de joueur, vous êtes donc coincés avec '${NAME}'.\nAllez à Paramètres->Profils des Joueurs pour vous créer un profil.",
"noScoresYetText": "Aucun score pour le moment.",
"noServersFoundText": "Aucun serveur trouvé.",
@ -1348,6 +1352,7 @@
"netTestingText": "Tester Votre Réseau",
"resetText": "Réinitialiser",
"showBombTrajectoriesText": "Montrer les trajectoires de bombe",
"showDemosWhenIdleText": "Afficher les démos en cas d'inactivité",
"showDevConsoleButtonText": "Afficher le Bouton de la Console de Développeur",
"showInGamePingText": "Afficher La Latence En Jeu",
"showPlayerNamesText": "Montrer les Noms des Joueurs",

View file

@ -1385,6 +1385,7 @@
"netTestingText": "Ntwkrz Tsstcg",
"resetText": "Rsttz",
"showBombTrajectoriesText": "Shzlz Bomf Tfwoejcwoefz",
"showDemosWhenIdleText": "Sho cwoefj c wdofdfjdfwf",
"showDevConsoleButtonText": "Sho c weroiw c wo cwoije fwois",
"showInGamePingText": "Shoe o co fowl Png",
"showPlayerNamesText": "SHzlfjl Plzlrr Nmzlzlls",

View file

@ -31,6 +31,7 @@
"signInWithGooglePlayText": "गूगल प्ले से साईन ईन करे",
"signInWithTestAccountInfoText": "(पुराना खाते का प्ररूप; आगे के लिए यंत्र खाते का प्रयोग करें)",
"signInWithTestAccountText": "परीक्षण के खाते से साइन इन करें",
"signInWithText": "${service} के साथ साइन इन करें",
"signInWithV2InfoText": "(एक खाता जो सभी प्लेटफार्मों पर काम करता है)",
"signInWithV2Text": "BombSquad खाते से साइन इन करें",
"signOutText": "साइन आउट",
@ -337,6 +338,8 @@
"getMoreGamesText": "और गेम्स कि जानकारी पायें",
"titleText": "गेम जोड़ें"
},
"addToFavoritesText": "पसंदीदा में जोड़े",
"addedToFavoritesText": "\"${NAME}\"को पसंदीदा में जोड़ा गया।",
"allText": "सभी",
"allowText": "अनुमति दें",
"alreadySignedInText": "आपका खाता किसी अन्य डिवाइस से साइन किया गया है; \nकृपया खातों को स्विच करें या अपने गेम को अन्य डिवाइस \nपर बंद करें और फिर से प्रयास करें",
@ -370,6 +373,7 @@
"chatMutedText": "बातचीत मौन हो गई है",
"chatUnMuteText": "बातचीत दोबारा शुरू करें",
"choosingPlayerText": "<खिलाड़ी चुना जा रहा है>",
"codesExplainText": "डेवलपर द्वारा कोड प्रदान किए जाते हैं\nखाता समस्याओं का निदान और सुधार करें।",
"completeThisLevelToProceedText": "आपको यह पड़ाव पार करना पड़ेगा आगे बढ़ने के लिए !",
"completionBonusText": "पूर्णता पुरस्कार",
"configControllersWindow": {
@ -450,6 +454,7 @@
"swipeText": "स्वाइप",
"titleText": "टच स्क्रीन को कॉन्फ़िगर करें"
},
"configureDeviceInSystemSettingsText": "${DEVICE}को सिस्टम सेटिंग्स ऐप में कॉन्फ़िगर किया जा सकता है।",
"configureItNowText": "अभी कॉन्फ़िगर करें ?",
"configureText": "कॉन्फ़िगर",
"connectMobileDevicesWindow": {
@ -563,6 +568,8 @@
"disableXInputDescriptionText": "4 नियंत्रकों से अधिक की अनुमति देता है लेकिन साथ ही साथ काम नहीं कर सकते",
"disableXInputText": "Xinput अक्षम करें",
"disabledText": "डिसेबल्ड",
"discordFriendsText": "क्या आप खेलने के लिए नए लोगों की तलाश करना चाहते हैं?\nहमारे डिस्कोर्ड में शामिल हों और नए दोस्त खोजें!",
"discordJoinText": "डिस्कोर्ड में शामिल हों।",
"doneText": "हो गया",
"drawText": "बराबर",
"duplicateText": "प्रतिलिपि",
@ -750,6 +757,7 @@
"manualYourLocalAddressText": "आपका स्थानीय पता:",
"nearbyText": "आस/पास",
"noConnectionText": "<कोई कनेक्शन नहीं है>",
"noPartiesAddedText": "कोई दल नहीं जोड़ा गया",
"otherVersionsText": "(कोई और संस्करण)",
"partyCodeText": "दल कोड",
"partyInviteAcceptText": "स्वीकार करें",
@ -1067,6 +1075,7 @@
"noContinuesText": "(कोई जारी रखना नहीं)",
"noExternalStorageErrorText": "कोई बाहरी संचयन करने कि जगह नहीं मिली",
"noGameCircleText": "त्रुटी: गेम-सर्किल में लॉग-इन नहीं हैं |",
"noPluginsInstalledText": "कोई प्लगइन्स इंस्टॉल नहीं है",
"noProfilesErrorText": "आपकी कोई खिलाड़ी पार्श्वचित्र नहीं है, इसलिए आप '${NAME}' नाम के साथ फंसे हैं |\nसेटिंग -> खिलाड़ी पार्श्वचित्र में जाके अपने लिए पार्श्वचित्र बनायें |",
"noScoresYetText": "अभी तक कोई स्कोर नहीं है |",
"noServersFoundText": "कोई सरवर्स नहीं मिलें।",
@ -1280,6 +1289,7 @@
"netTestingText": "नेटवर्क पर परीक्षण",
"resetText": "रीसेट",
"showBombTrajectoriesText": "बोम्ब का पथ दिखाएँ",
"showDemosWhenIdleText": "निष्क्रिय होने पर डेमो दिखाएं",
"showDevConsoleButtonText": "डेव कंसोल बटन दिखाएँ",
"showInGamePingText": "गेम पिंग में दिखाएं",
"showPlayerNamesText": "खिलाड़ी का नाम दिखाएँ",
@ -1373,6 +1383,7 @@
"submitText": "जमा करें",
"submittingPromoCodeText": "संहिता जमा कर रहा है ...",
"successText": "सफल!",
"supportEmailText": "यदि आप किसी भी समस्या का सामना कर रहे हैं\nऐप, कृपया ${EMAIL} को ईमेल करें",
"teamNamesColorText": "टीम के नाम / रंग ...",
"telnetAccessGrantedText": "टेलनेट एक्सेस सक्षम है",
"telnetAccessText": "टेलनेट पहुंच का पता चला; अनुमति देते हैं?",
@ -1826,6 +1837,7 @@
"unlockThisInTheStoreText": "यह स्टोर में अनलॉक होना चाहिए।",
"unlockThisProfilesText": "${NUM} प्रोफ़ाइल बनाने के लिए, आपको इसकी आवश्यकता है:",
"unlockThisText": "इसे अनलॉक करने के लिए, आपको इसकी आवश्यकता है:",
"unsupportedControllerText": "क्षमा करें, नियंत्रक \"${NAME}\" समर्थित नहीं है।",
"unsupportedHardwareText": "क्षमा करें, यह हार्डवेयर गेम के इस निर्माण द्वारा समर्थित नहीं है।",
"upFirstText": "सर्व प्रथम",
"upNextText": "गेम ${COUNT} में अगला:",
@ -1837,6 +1849,7 @@
"usingItunesText": "गाने के लिए संगीत ऐप का उपयोग कर रहे है ...",
"v2AccountLinkingInfoText": "V2 खातों को लिंक करने के लिए, 'खाता प्रबंधित करें' बटन का उपयोग करें।",
"validatingTestBuildText": "परीक्षण निर्माण मान्य ...",
"viaText": "के जरिए",
"victoryText": "विजय!",
"voteDelayText": "आप ${NUMBER} सेकंड के लिए एक और वोट शुरू नहीं कर सकते हैं",
"voteInProgressText": "एक वोट पहले ही प्रगति पर है।",

View file

@ -335,6 +335,8 @@
"getMoreGamesText": "Game Lain...",
"titleText": "Tambah Game"
},
"addToFavoritesText": "Tambahkan ke Favorit",
"addedToFavoritesText": "Tambah '${NAME}' ke dalam Favorit",
"allText": "Semua",
"allowText": "Izinkan",
"alreadySignedInText": "Akunmu telah masuk di perangkat lain;\nSilakan beralih akun atau menutup permainanmu \ndi perangkat lain dan coba lagi.",
@ -563,6 +565,8 @@
"disableXInputDescriptionText": "Izinkan lebih dari 4 pengontrol tapi mungkin agak lemot.",
"disableXInputText": "Blokir XInput",
"disabledText": "Dimatikan",
"discordFriendsText": "Ingin mencari teman baru untuk bermain?\nGabung ke Discord kami dan temukan teman baru kamu!",
"discordJoinText": "Gabung ke Discord",
"doneText": "Selesai",
"drawText": "Seri",
"duplicateText": "Duplikat",
@ -748,6 +752,7 @@
"manualYourLocalAddressText": "Alamat lokal Kamu:",
"nearbyText": "Dekat",
"noConnectionText": "<tidak ada koneksi>",
"noPartiesAddedText": "Tidak ada Acara yang Ditambahkan",
"otherVersionsText": "(Versi lain)",
"partyCodeText": "Kode Acara",
"partyInviteAcceptText": "Terima",
@ -1064,6 +1069,7 @@
"noContinuesText": "(tidak dapat melanjutkan)",
"noExternalStorageErrorText": "Tidak ada penyimpanan eksternal",
"noGameCircleText": "Kesalahan: tidak masuk ke LingkaranGame",
"noPluginsInstalledText": "Tidak ada plugin yang terinstal",
"noProfilesErrorText": "Kamu tidak punya profil pemain, jadi '${NAME}' dipakai. \nMasuk Pengaturan->Profil Pemain untuk membuat profil. ",
"noScoresYetText": "Belum ada skor.",
"noServersFoundText": "Server tidak ditemukan.",
@ -1277,6 +1283,7 @@
"netTestingText": "Tes Jaringan",
"resetText": "Atur ulang",
"showBombTrajectoriesText": "Lihat Lintasan Bom",
"showDemosWhenIdleText": "Tampilkan Demo saat tidak bergerak",
"showDevConsoleButtonText": "Tampilkan tombol Dev Console",
"showInGamePingText": "Tampilkan Ping dalam permainan",
"showPlayerNamesText": "Tunjukkan Nama Pemain",

View file

@ -1339,6 +1339,7 @@
"netTestingText": "Collaudo Rete",
"resetText": "Reset",
"showBombTrajectoriesText": "Mostra le traiettorie delle bombe",
"showDemosWhenIdleText": "Riproduci Demo All'Inattività",
"showDevConsoleButtonText": "mostra tasto console sviluppatore",
"showInGamePingText": "Mostra il Ping in gioco",
"showPlayerNamesText": "Mostra i nomi dei giocatori",

View file

@ -336,6 +336,8 @@
"getMoreGamesText": "다른 게임 보기...",
"titleText": "게임 추가"
},
"addToFavoritesText": "즐겨찾기에 추가하기",
"addedToFavoritesText": "즐겨찾기에 '${NAME}'을 추가했습니다.",
"allText": "모두",
"allowText": "허용",
"alreadySignedInText": "귀하의 계정은 다른 기기에서 로그인되었습니다. \n계정을 전환하거나 다른 기기에서 게임을 종료하고 \n다시 시도하십시오.",
@ -751,6 +753,7 @@
"manualYourLocalAddressText": "귀하의 로컬 주소:",
"nearbyText": "근처",
"noConnectionText": "<연결 없음>",
"noPartiesAddedText": "추가한 파티 없음",
"otherVersionsText": "(다른 버전)",
"partyCodeText": "파티 코드",
"partyInviteAcceptText": "수락",
@ -1065,6 +1068,7 @@
"noContinuesText": "(계속 없음)",
"noExternalStorageErrorText": "이 기기에서 외부 저장소를 찾지 못했습니다.",
"noGameCircleText": "오류: GameCircle에 로그인되지 않았습니다",
"noPluginsInstalledText": "설치된 플러그인 없음",
"noScoresYetText": "아직 점수 없음.",
"noServersFoundText": "서버를 찾을수 없음.",
"noThanksText": "아니요",
@ -1276,6 +1280,7 @@
"netTestingText": "네트워크 테스트",
"resetText": "재설정",
"showBombTrajectoriesText": "폭탄 궤적 표시",
"showDemosWhenIdleText": "유휴 상태일 때 데모 표시",
"showDevConsoleButtonText": "개발자 콘솔 버튼 보이기",
"showInGamePingText": "인게임 핑 보이기",
"showPlayerNamesText": "플레이어 이름 표시",

View file

@ -1281,6 +1281,7 @@
"netTestingText": "تست شبکه",
"resetText": "باز گرداندن",
"showBombTrajectoriesText": "نمایش خط سیر بمب",
"showDemosWhenIdleText": "نشان دادن دمو ها در وقت بیکاری",
"showDevConsoleButtonText": "نشان دادن دکمه توسعه دهنده کنسول",
"showInGamePingText": "نمایش پینگ در بازی",
"showPlayerNamesText": "نمایش نام بازیکنان",

View file

@ -340,6 +340,8 @@
"getMoreGamesText": "Mais jogos...",
"titleText": "Adicionar jogo"
},
"addToFavoritesText": "Adicionar aos Favoritos",
"addedToFavoritesText": "Adicionou '${NAME}' aos Favoritos.",
"allText": "Tudo",
"allowText": "Permitir",
"alreadySignedInText": "A conta tem sessão iniciada em outro dispositivo;\nMude de conta ou feche o jogo no seu\noutro dispositivo e tente novamente.",
@ -788,6 +790,7 @@
"manualYourLocalAddressText": "Seu endereço local:",
"nearbyText": "Próximo",
"noConnectionText": "<sem conexão>",
"noPartiesAddedText": "Nenhum grupo adicionado",
"otherVersionsText": "(outras versões)",
"partyCodeText": "Código da Sala",
"partyInviteAcceptText": "Aceitar",
@ -1134,6 +1137,7 @@
"noExternalStorageErrorText": "Armazenamento externo não encontrado",
"noGameCircleText": "Erro: não conectado no GameCircle",
"noJoinCoopMidwayText": "Jogos cooperativos não podem ser afiliados no meio do caminho.",
"noPluginsInstalledText": "Nenhum Plugin instalado",
"noProfilesErrorText": "Você não tem perfis de jogadores, então você está preso com '${NAME}'.\nVá para Configurações->Perfis de Jogador para criar um perfil.",
"noScoresYetText": "Ainda sem pontuação.",
"noServersFoundText": "Servidores não encontrados.",
@ -1363,6 +1367,7 @@
"netTestingText": "Teste de conexão",
"resetText": "Redefinir",
"showBombTrajectoriesText": "Mostrar trajetórias da bomba",
"showDemosWhenIdleText": "Mostrar demonstrações quando ocioso",
"showDevConsoleButtonText": "Mostrar console de desenvolvedor",
"showInGamePingText": "Mostrar latência no jogo",
"showPlayerNamesText": "Mostrar nomes dos jogadores",

View file

@ -341,6 +341,8 @@
"getMoreGamesText": "Еще игры",
"titleText": "Добавить игру"
},
"addToFavoritesText": "Добавить в избранное",
"addedToFavoritesText": "Добавлен '${NAME}' в избранное.",
"allText": "Все",
"allowText": "Разрешить",
"alreadySignedInText": "С вашего аккаунта играют на другом устройстве;\nпожалуйста зайдите с другого аккаунта или закройте\nигру на другом устройстве и попытайтесь снова.",
@ -783,6 +785,7 @@
"manualYourLocalAddressText": "Ваш локальный адрес:",
"nearbyText": "Поблизости",
"noConnectionText": "<нет соединения>",
"noPartiesAddedText": "Вечеринки не добавлены",
"otherVersionsText": "(другие версии)",
"partyCodeText": "Код лобби",
"partyInviteAcceptText": "Принять",
@ -1117,6 +1120,7 @@
"noExternalStorageErrorText": "На данном устройстве не найдено внешней памяти",
"noGameCircleText": "Ошибка: не вошли в GameCircle",
"noJoinCoopMidwayText": "К кооперативным играм нельзя присоединиться посреди игры.",
"noPluginsInstalledText": "Плагины не установлены",
"noProfilesErrorText": "У вас нет профиля игрока, так что вас будут звать '${NAME}'.\nСоздать профиль можно перейдя в 'Настройки' > 'Профили игроков'.",
"noScoresYetText": "Счета пока нет.",
"noServersFoundText": "Серверы не найдены.",
@ -1339,6 +1343,7 @@
"netTestingText": "Тестирование сети",
"resetText": "Сбросить",
"showBombTrajectoriesText": "Показывать траекторию бомбы",
"showDemosWhenIdleText": "Показывать демоверсии в режиме ожидания",
"showDevConsoleButtonText": "Показать кнопку консоли",
"showInGamePingText": "Показать Ping",
"showPlayerNamesText": "Показывать имена игроков",

View file

@ -128,7 +128,7 @@
},
"Mine Games": {
"description": "Mata a 3 chicos malos con minas terrestres",
"descriptionComplete": "Mató a 3 enemigos con minas terrestres",
"descriptionComplete": "Mató a 3 chicos malos con minas terrestres",
"descriptionFull": "Mata a 3 chicos malos con minas terrestres en ${LEVEL}",
"descriptionFullComplete": "Mató a 3 chicos con minas terrestres en ${LEVEL}",
"name": "Juegos De Minas"
@ -1107,7 +1107,7 @@
"modeClassicText": "Modo Clásico",
"modeDemoText": "Modo Demo",
"mostValuablePlayerText": "Jugador Más Valioso",
"mostViolatedPlayerText": "Jugador Más Violado",
"mostViolatedPlayerText": "Jugador Más Agredido",
"mostViolentPlayerText": "Jugador Más Violento",
"moveText": "Mover",
"multiKillText": "¡¡¡COMBO DE ${COUNT}!!!",
@ -1208,11 +1208,11 @@
"playlistsText": "Listas de juegos",
"pleaseRateText": "Si te gusta ${APP_NAME}, por favor tomate un momento\npara calificar o escribir una reseña. Esto proporcionará\ninformación útil y ayuda al soporte del futuro desarrollo del juego.\n\n¡gracias!\n-eric",
"pleaseWaitText": "Por favor, espera...",
"pluginClassLoadErrorText": "Error cargando la clase del complemento '${PLUGIN}': ${ERROR}",
"pluginClassLoadErrorText": "Error cargando el complemento '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Error iniciando complemento '${PLUGIN}': ${ERROR}",
"pluginSettingsText": "Ajustes De Complementos",
"pluginsAutoEnableNewText": "Auto Habilitar Nuevos Complementos",
"pluginsDetectedText": "Nuevo(s) complemento(s) detectado(s). Reinicie para activarlo(s), o configuralo(s) en ajustes.",
"pluginsDetectedText": "Complemento detectado(s). Reinicia el juego, o ve a ajustes para configurarlo.",
"pluginsDisableAllText": "Deshabilitar Todos Los Complementos",
"pluginsEnableAllText": "Habilitar Todos Los Complementos",
"pluginsRemovedText": "${NUM} complemento(s) ya no se encuentra(n).",
@ -1360,6 +1360,7 @@
"netTestingText": "Prueba De Red",
"resetText": "Reiniciar",
"showBombTrajectoriesText": "Mostrar Trayectorias De Las Bombas",
"showDemosWhenIdleText": "Mostrar demostraciones cuando esté inactivo",
"showDevConsoleButtonText": "Mostrar Botón de Consola de Desarrollador",
"showInGamePingText": "Mostrar Ping En El Juego",
"showPlayerNamesText": "Mostrar Nombres De Los Jugadores",

View file

@ -334,6 +334,8 @@
"getMoreGamesText": "รับเกมเพิ่มเติม",
"titleText": "เพิ่มเกม"
},
"addToFavoritesText": "เพื่มในรายการโปรด",
"addedToFavoritesText": "เพื่ม '${NAME}' ในรายการโปรดแล้ว",
"allText": "ทั้งหมด",
"allowText": "ยอมรับ",
"alreadySignedInText": "บัญชีของคุณลงชื่อเข้าใช้จากอุปกรณ์อื่น\nโปรดเปลี่ยนบัญชีหรือปิดเกมของคุณ\nอุปกรณ์อื่นและลองอีกครั้ง",

View file

@ -335,6 +335,8 @@
"getMoreGamesText": "Daha Çok Oyun...",
"titleText": "Oyun Ekle"
},
"addToFavoritesText": "Favorilere ekle",
"addedToFavoritesText": "'${NAME}' Favorilere eklendi.",
"allText": "Hepsi",
"allowText": "Kabul Et",
"alreadySignedInText": "Başka bir cihazda hesabına giriş yapılmış;\nlütfen hesapları değiştir ya da diğer cihazlardaki\noyunu kapat ve tekrar dene.",
@ -751,6 +753,7 @@
"manualYourLocalAddressText": "Yerel adresiniz:",
"nearbyText": "Yakında",
"noConnectionText": "<bağlantı yok>",
"noPartiesAddedText": "Parti Eklenmedi",
"otherVersionsText": "(diğer sürümler)",
"partyCodeText": "Parti kodu",
"partyInviteAcceptText": "Kabul Et",
@ -1065,6 +1068,7 @@
"noContinuesText": "(sürdürülemiyor)",
"noExternalStorageErrorText": "Bu cihazda harici depolama bulunamadı",
"noGameCircleText": "Hata: GameCircle girişi yapılamadı",
"noPluginsInstalledText": "Yüklü Eklenti Yok",
"noScoresYetText": "Henüz skor yok.",
"noServersFoundText": "Sunucu Bulunamadı.",
"noThanksText": "Hayır Teşekkürler",
@ -1276,6 +1280,7 @@
"netTestingText": "Ağ Testi",
"resetText": "Sıfırla",
"showBombTrajectoriesText": "Bomba Gidişatını Göster",
"showDemosWhenIdleText": "Boşta İken Demolar Göster",
"showDevConsoleButtonText": "Geliştirici panelini aç",
"showInGamePingText": "Oyun İçinde Gecikmeyi Göster",
"showPlayerNamesText": "Oyuncu Adlarını Göster",

View file

@ -337,6 +337,8 @@
"getMoreGamesText": "Ще ігри...",
"titleText": "Додати гру"
},
"addToFavoritesText": "Додати в обране",
"addedToFavoritesText": "Додано '${NAME}' до вибраного.",
"allText": "Все",
"allowText": "Дозволити",
"alreadySignedInText": "На вашому акаунті грають на іншому пристрої;\nбудь ласка зайдіть з іншого акаунта або закрийте гру на\nіншому пристрої та спробуйте знову.",
@ -370,6 +372,7 @@
"chatMutedText": "Чат приглушений",
"chatUnMuteText": "Включити чат",
"choosingPlayerText": "<вибір гравця>",
"codesExplainText": "Розробник надає коди для діагностики та\nусунення проблем з обліковим записом.",
"completeThisLevelToProceedText": "Щоб продовжити, потрібно\nпройти цей рівень!",
"completionBonusText": "Бонус за проходження",
"configControllersWindow": {
@ -564,6 +567,8 @@
"disableXInputDescriptionText": "Підключення більше 4 контролерів, але може не працювати.",
"disableXInputText": "Відключити XInput",
"disabledText": "Виключено",
"discordFriendsText": "Хочете шукати нових людей для гри?\nПриєднуйтесь до нашого Discord і знайдіть нових друзів!",
"discordJoinText": "Приєднуйтесь до Discord",
"doneText": "Готово",
"drawText": "Нічия",
"duplicateText": "Дублювати",
@ -749,6 +754,7 @@
"manualYourLocalAddressText": "Ваш локальний адрес:",
"nearbyText": "Поблизу",
"noConnectionText": "<немає з'єднання>",
"noPartiesAddedText": "Партії не додано",
"otherVersionsText": "(інші версії)",
"partyCodeText": "Код групи",
"partyInviteAcceptText": "Прийняти",
@ -1063,6 +1069,7 @@
"noContinuesText": "(без продовжень)",
"noExternalStorageErrorText": "На цьому пристрої не знайдено зовнішньої пам'яті",
"noGameCircleText": "Помилка: не ввійшли в GameCircle",
"noPluginsInstalledText": "Плагіни не встановлено",
"noScoresYetText": "Рахунка поки немає.",
"noServersFoundText": "Серверів не знайдено",
"noThanksText": "Ні дякую",
@ -1367,6 +1374,7 @@
"submitText": "Відправити",
"submittingPromoCodeText": "Активація коду...",
"successText": "Успішно!",
"supportEmailText": "Якщо у вас виникли проблеми з програмою,\nнадішліть електронний лист на ${EMAIL}.",
"teamNamesColorText": "імена/кольори команд",
"telnetAccessGrantedText": "Доступ Telnet включений.",
"telnetAccessText": "Виявлено доступ Telnet. Дозволити?",
@ -1833,6 +1841,7 @@
"usingItunesTurnRepeatAndShuffleOnText": "Будь ласка, переконайтеся, що випадковий порядок і повтор усіх пісень включений в Itunes.",
"v2AccountLinkingInfoText": "Щоб підключити V2-акаунти, використовуйте кнопку 'Керування акаунтом'.",
"validatingTestBuildText": "Перевірка тестової збірки...",
"viaText": "через",
"victoryText": "Перемога!",
"voteDelayText": "Ви зможете почати голосування через ${NUMBER} секунд",
"voteInProgressText": "Голосування ще триває.",

View file

@ -333,6 +333,8 @@
"getMoreGamesText": "Otien pì łevełi...",
"titleText": "Zonta zugo"
},
"addToFavoritesText": "Zonta so i prefarìi",
"addedToFavoritesText": "'${NAME}' zontà so i prefarìi.",
"allText": "Tuto",
"allowText": "Parmeti",
"alreadySignedInText": "El to account el ze in dòparo inte nantro\ndispozidivo: canbia account o sara sù el zugo\ninte chełaltro to dispozidivo e proa danovo.",
@ -748,6 +750,7 @@
"manualYourLocalAddressText": "El to ndariso łogałe:",
"nearbyText": "Łogałe",
"noConnectionText": "<gnauna conesion>",
"noPartiesAddedText": "Gnaun grupo zontà",
"otherVersionsText": "(par altre varsion)",
"partyCodeText": "Còdaze de'l grupo",
"partyInviteAcceptText": "Và ben",
@ -1062,6 +1065,7 @@
"noContinuesText": "(sensa continui)",
"noExternalStorageErrorText": "So sto dispozidivo no ze stà catada gnauna memoria esterna",
"noGameCircleText": "Eror: no te si miga conetesto co GameCircle",
"noPluginsInstalledText": "Gnauna estension instałada",
"noScoresYetText": "Gnancora gnaun puntejo.",
"noServersFoundText": "Gnaun server catà.",
"noThanksText": "Nò, grasie",

View file

@ -86,6 +86,7 @@ __all__ = [
'TYPE_CHECKING',
'Never',
'NoReturn',
'ReadOnly',
'Required',
'NotRequired',
@ -473,6 +474,7 @@ _EXCLUDED_ATTRS = {
"__orig_bases__", "__module__", "_MutableMapping__marker", "__doc__",
"__subclasshook__", "__orig_class__", "__init__", "__new__",
"__protocol_attrs__", "__callable_proto_members_only__",
"__match_args__",
}
if sys.version_info >= (3, 9):
@ -503,9 +505,9 @@ def _caller(depth=2):
return None
# The performance of runtime-checkable protocols is significantly improved on Python 3.12,
# so we backport the 3.12 version of Protocol to Python <=3.11
if sys.version_info >= (3, 12):
# `__match_args__` attribute was removed from protocol members in 3.13,
# we want to backport this change to older Python versions.
if sys.version_info >= (3, 13):
Protocol = typing.Protocol
else:
def _allow_reckless_class_checks(depth=3):
@ -569,8 +571,13 @@ else:
not cls.__callable_proto_members_only__
and cls.__dict__.get("__subclasshook__") is _proto_hook
):
non_method_attrs = sorted(
attr for attr in cls.__protocol_attrs__
if not callable(getattr(cls, attr, None))
)
raise TypeError(
"Protocols with non-method members don't support issubclass()"
"Protocols with non-method members don't support issubclass()."
f" Non-method members: {str(non_method_attrs)[1:-1]}."
)
if not getattr(cls, '_is_runtime_protocol', False):
raise TypeError(
@ -767,7 +774,7 @@ def _ensure_subclassable(mro_entries):
return inner
if sys.version_info >= (3, 13):
if hasattr(typing, "ReadOnly"):
# The standard library TypedDict in Python 3.8 does not store runtime information
# about which (if any) keys are optional. See https://bugs.python.org/issue38834
# The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
@ -778,6 +785,7 @@ if sys.version_info >= (3, 13):
# Aaaand on 3.12 we add __orig_bases__ to TypedDict
# to enable better runtime introspection.
# On 3.13 we deprecate some odd ways of creating TypedDicts.
# PEP 705 proposes adding the ReadOnly[] qualifier.
TypedDict = typing.TypedDict
_TypedDictMeta = typing._TypedDictMeta
is_typeddict = typing.is_typeddict
@ -785,8 +793,29 @@ else:
# 3.10.0 and later
_TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters
def _get_typeddict_qualifiers(annotation_type):
while True:
annotation_origin = get_origin(annotation_type)
if annotation_origin is Annotated:
annotation_args = get_args(annotation_type)
if annotation_args:
annotation_type = annotation_args[0]
else:
break
elif annotation_origin is Required:
yield Required
annotation_type, = get_args(annotation_type)
elif annotation_origin is NotRequired:
yield NotRequired
annotation_type, = get_args(annotation_type)
elif annotation_origin is ReadOnly:
yield ReadOnly
annotation_type, = get_args(annotation_type)
else:
break
class _TypedDictMeta(type):
def __new__(cls, name, bases, ns, total=True):
def __new__(cls, name, bases, ns, *, total=True):
"""Create new typed dict class object.
This method is called when TypedDict is subclassed,
@ -829,33 +858,46 @@ else:
}
required_keys = set()
optional_keys = set()
readonly_keys = set()
mutable_keys = set()
for base in bases:
annotations.update(base.__dict__.get('__annotations__', {}))
required_keys.update(base.__dict__.get('__required_keys__', ()))
optional_keys.update(base.__dict__.get('__optional_keys__', ()))
base_dict = base.__dict__
annotations.update(base_dict.get('__annotations__', {}))
required_keys.update(base_dict.get('__required_keys__', ()))
optional_keys.update(base_dict.get('__optional_keys__', ()))
readonly_keys.update(base_dict.get('__readonly_keys__', ()))
mutable_keys.update(base_dict.get('__mutable_keys__', ()))
annotations.update(own_annotations)
for annotation_key, annotation_type in own_annotations.items():
annotation_origin = get_origin(annotation_type)
if annotation_origin is Annotated:
annotation_args = get_args(annotation_type)
if annotation_args:
annotation_type = annotation_args[0]
annotation_origin = get_origin(annotation_type)
qualifiers = set(_get_typeddict_qualifiers(annotation_type))
if annotation_origin is Required:
if Required in qualifiers:
required_keys.add(annotation_key)
elif annotation_origin is NotRequired:
elif NotRequired in qualifiers:
optional_keys.add(annotation_key)
elif total:
required_keys.add(annotation_key)
else:
optional_keys.add(annotation_key)
if ReadOnly in qualifiers:
if annotation_key in mutable_keys:
raise TypeError(
f"Cannot override mutable key {annotation_key!r}"
" with read-only key"
)
readonly_keys.add(annotation_key)
else:
mutable_keys.add(annotation_key)
readonly_keys.discard(annotation_key)
tp_dict.__annotations__ = annotations
tp_dict.__required_keys__ = frozenset(required_keys)
tp_dict.__optional_keys__ = frozenset(optional_keys)
tp_dict.__readonly_keys__ = frozenset(readonly_keys)
tp_dict.__mutable_keys__ = frozenset(mutable_keys)
if not hasattr(tp_dict, '__total__'):
tp_dict.__total__ = total
return tp_dict
@ -936,6 +978,8 @@ else:
raise TypeError("TypedDict takes either a dict or keyword arguments,"
" but not both")
if kwargs:
if sys.version_info >= (3, 13):
raise TypeError("TypedDict takes no keyword arguments")
warnings.warn(
"The kwargs-based syntax for TypedDict definitions is deprecated "
"in Python 3.11, will be removed in Python 3.13, and may not be "
@ -1924,6 +1968,53 @@ else: # 3.8
""")
if hasattr(typing, 'ReadOnly'):
ReadOnly = typing.ReadOnly
elif sys.version_info[:2] >= (3, 9): # 3.9-3.12
@_ExtensionsSpecialForm
def ReadOnly(self, parameters):
"""A special typing construct to mark an item of a TypedDict as read-only.
For example:
class Movie(TypedDict):
title: ReadOnly[str]
year: int
def mutate_movie(m: Movie) -> None:
m["year"] = 1992 # allowed
m["title"] = "The Matrix" # typechecker error
There is no runtime checking for this property.
"""
item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
return typing._GenericAlias(self, (item,))
else: # 3.8
class _ReadOnlyForm(_ExtensionsSpecialForm, _root=True):
def __getitem__(self, parameters):
item = typing._type_check(parameters,
f'{self._name} accepts only a single type.')
return typing._GenericAlias(self, (item,))
ReadOnly = _ReadOnlyForm(
'ReadOnly',
doc="""A special typing construct to mark a key of a TypedDict as read-only.
For example:
class Movie(TypedDict):
title: ReadOnly[str]
year: int
def mutate_movie(m: Movie) -> None:
m["year"] = 1992 # allowed
m["title"] = "The Matrix" # typechecker error
There is no runtime checking for this propery.
""")
_UNPACK_DOC = """\
Type unpack operator.
@ -2251,7 +2342,7 @@ else: # <=3.11
Usage:
class Base:
def method(self) -> None: ...
def method(self) -> None:
pass
class Child(Base):
@ -2281,20 +2372,17 @@ else: # <=3.11
return arg
if hasattr(typing, "deprecated"):
deprecated = typing.deprecated
if hasattr(warnings, "deprecated"):
deprecated = warnings.deprecated
else:
_T = typing.TypeVar("_T")
def deprecated(
msg: str,
/,
*,
category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
stacklevel: int = 1,
) -> typing.Callable[[_T], _T]:
class deprecated:
"""Indicate that a class, function or overload is deprecated.
When this decorator is applied to an object, the type checker
will generate a diagnostic on usage of the deprecated object.
Usage:
@deprecated("Use B instead")
@ -2311,49 +2399,100 @@ else:
@overload
def g(x: str) -> int: ...
When this decorator is applied to an object, the type checker
will generate a diagnostic on usage of the deprecated object.
The warning specified by ``category`` will be emitted on use
of deprecated objects. For functions, that happens on calls;
for classes, on instantiation. If the ``category`` is ``None``,
no warning is emitted. The ``stacklevel`` determines where the
The warning specified by *category* will be emitted at runtime
on use of deprecated objects. For functions, that happens on calls;
for classes, on instantiation and on creation of subclasses.
If the *category* is ``None``, no warning is emitted at runtime.
The *stacklevel* determines where the
warning is emitted. If it is ``1`` (the default), the warning
is emitted at the direct caller of the deprecated object; if it
is higher, it is emitted further up the stack.
Static type checker behavior is not affected by the *category*
and *stacklevel* arguments.
The decorator sets the ``__deprecated__``
attribute on the decorated object to the deprecation message
passed to the decorator. If applied to an overload, the decorator
The deprecation message passed to the decorator is saved in the
``__deprecated__`` attribute on the decorated object.
If applied to an overload, the decorator
must be after the ``@overload`` decorator for the attribute to
exist on the overload as returned by ``get_overloads()``.
See PEP 702 for details.
"""
def decorator(arg: _T, /) -> _T:
def __init__(
self,
message: str,
/,
*,
category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
stacklevel: int = 1,
) -> None:
if not isinstance(message, str):
raise TypeError(
"Expected an object of type str for 'message', not "
f"{type(message).__name__!r}"
)
self.message = message
self.category = category
self.stacklevel = stacklevel
def __call__(self, arg: _T, /) -> _T:
# Make sure the inner functions created below don't
# retain a reference to self.
msg = self.message
category = self.category
stacklevel = self.stacklevel
if category is None:
arg.__deprecated__ = msg
return arg
elif isinstance(arg, type):
import functools
from types import MethodType
original_new = arg.__new__
has_init = arg.__init__ is not object.__init__
@functools.wraps(original_new)
def __new__(cls, *args, **kwargs):
warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
if cls is arg:
warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
if original_new is not object.__new__:
return original_new(cls, *args, **kwargs)
# Mirrors a similar check in object.__new__.
elif not has_init and (args or kwargs):
elif cls.__init__ is object.__init__ and (args or kwargs):
raise TypeError(f"{cls.__name__}() takes no arguments")
else:
return original_new(cls)
arg.__new__ = staticmethod(__new__)
original_init_subclass = arg.__init_subclass__
# We need slightly different behavior if __init_subclass__
# is a bound method (likely if it was implemented in Python)
if isinstance(original_init_subclass, MethodType):
original_init_subclass = original_init_subclass.__func__
@functools.wraps(original_init_subclass)
def __init_subclass__(*args, **kwargs):
warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
return original_init_subclass(*args, **kwargs)
arg.__init_subclass__ = classmethod(__init_subclass__)
# Or otherwise, which likely means it's a builtin such as
# object's implementation of __init_subclass__.
else:
@functools.wraps(original_init_subclass)
def __init_subclass__(*args, **kwargs):
warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
return original_init_subclass(*args, **kwargs)
arg.__init_subclass__ = __init_subclass__
arg.__deprecated__ = __new__.__deprecated__ = msg
__init_subclass__.__deprecated__ = msg
return arg
elif callable(arg):
import functools
@functools.wraps(arg)
def wrapper(*args, **kwargs):
warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
@ -2367,8 +2506,6 @@ else:
f"a class or callable, not {arg!r}"
)
return decorator
# We have to do some monkey patching to deal with the dual nature of
# Unpack/TypeVarTuple:
@ -2437,11 +2574,35 @@ else:
class_getitem = typing.Generic.__class_getitem__.__func__
nm_tpl.__class_getitem__ = classmethod(class_getitem)
# update from user namespace without overriding special namedtuple attributes
for key in ns:
for key, val in ns.items():
if key in _prohibited_namedtuple_fields:
raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
elif key not in _special_namedtuple_fields and key not in nm_tpl._fields:
setattr(nm_tpl, key, ns[key])
elif key not in _special_namedtuple_fields:
if key not in nm_tpl._fields:
setattr(nm_tpl, key, ns[key])
try:
set_name = type(val).__set_name__
except AttributeError:
pass
else:
try:
set_name(val, nm_tpl, key)
except BaseException as e:
msg = (
f"Error calling __set_name__ on {type(val).__name__!r} "
f"instance {key!r} in {typename!r}"
)
# BaseException.add_note() existed on py311,
# but the __set_name__ machinery didn't start
# using add_note() until py312.
# Making sure exceptions are raised in the same way
# as in "normal" classes seems most important here.
if sys.version_info >= (3, 12):
e.add_note(msg)
raise
else:
raise RuntimeError(msg) from e
if typing.Generic in bases:
nm_tpl.__init_subclass__()
return nm_tpl
@ -2600,7 +2761,7 @@ else:
num = UserId(5) + 1 # type: int
"""
def __call__(self, obj):
def __call__(self, obj, /):
return obj
def __init__(self, name, tp):

View file

@ -48,6 +48,7 @@ from _babase import (
fatal_error,
get_display_resolution,
get_immediate_return_code,
get_input_idle_time,
get_low_level_config_value,
get_max_graphics_quality,
get_replays_dir,
@ -60,6 +61,7 @@ from _babase import (
have_permission,
in_logic_thread,
increment_analytics_count,
invoke_main_menu,
is_os_playing_music,
is_xcode_build,
lock_all_input,
@ -116,7 +118,6 @@ from babase._apputils import (
get_remote_app_name,
AppHealthMonitor,
)
from babase._cloud import CloudSubsystem
from babase._devconsole import (
DevConsoleTab,
DevConsoleTabEntry,
@ -211,7 +212,6 @@ __all__ = [
'clipboard_has_text',
'clipboard_is_supported',
'clipboard_set_text',
'CloudSubsystem',
'commit_app_config',
'ContextCall',
'ContextError',
@ -235,6 +235,7 @@ __all__ = [
'garbage_collect',
'get_display_resolution',
'get_immediate_return_code',
'get_input_idle_time',
'get_ip_address_type',
'get_low_level_config_value',
'get_max_graphics_quality',
@ -254,6 +255,7 @@ __all__ = [
'increment_analytics_count',
'InputDeviceNotFoundError',
'InputType',
'invoke_main_menu',
'is_browser_likely_available',
'is_browser_likely_available',
'is_os_playing_music',

View file

@ -1,15 +1,17 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Functionality related to the high level state of the app."""
from __future__ import annotations
import os
import logging
from enum import Enum
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, TypeVar
from concurrent.futures import ThreadPoolExecutor
from functools import cached_property
from typing_extensions import override
from efro.call import tpartial
import _babase
@ -26,7 +28,7 @@ from babase._devconsole import DevConsoleSubsystem
if TYPE_CHECKING:
import asyncio
from typing import Any, Callable, Coroutine
from typing import Any, Callable, Coroutine, Generator, Awaitable
from concurrent.futures import Future
import babase
@ -42,6 +44,8 @@ if TYPE_CHECKING:
# __FEATURESET_APP_SUBSYSTEM_IMPORTS_END__
T = TypeVar('T')
class App:
"""A class for high level app functionality and state.
@ -124,6 +128,7 @@ class App:
statically in a spinoff project.
"""
@override
def app_mode_for_intent(
self, intent: AppIntent
) -> type[AppMode] | None:
@ -199,7 +204,8 @@ class App:
self._called_on_running = False
self._subsystem_registration_ended = False
self._pending_apply_app_config = False
self._aioloop: asyncio.AbstractEventLoop | None = None
self._asyncio_loop: asyncio.AbstractEventLoop | None = None
self._asyncio_tasks: set[asyncio.Task] = set()
self._asyncio_timer: babase.AppTimer | None = None
self._config: babase.AppConfig | None = None
self._pending_intent: AppIntent | None = None
@ -239,18 +245,68 @@ class App:
return _babase.app_is_active()
@property
def aioloop(self) -> asyncio.AbstractEventLoop:
def asyncio_loop(self) -> asyncio.AbstractEventLoop:
"""The logic thread's asyncio event loop.
This allow async tasks to be run in the logic thread.
Generally you should call App.create_async_task() to schedule
async code to run instead of using this directly. That will
handle retaining the task and logging errors automatically.
Only schedule tasks onto asyncio_loop yourself when you intend
to hold on to the returned task and await its results. Releasing
the task reference can lead to subtle bugs such as unreported
errors and garbage-collected tasks disappearing before their
work is done.
Note that, at this time, the asyncio loop is encapsulated
and explicitly stepped by the engine's logic thread loop and
thus things like asyncio.get_running_loop() will not return this
loop from most places in the logic thread; only from within a
task explicitly created in this loop.
thus things like asyncio.get_running_loop() will unintuitively
*not* return this loop from most places in the logic thread;
only from within a task explicitly created in this loop.
Hopefully this situation will be improved in the future with a
unified event loop.
"""
assert self._aioloop is not None
return self._aioloop
assert _babase.in_logic_thread()
assert self._asyncio_loop is not None
return self._asyncio_loop
def create_async_task(
self,
coro: Generator[Any, Any, T] | Coroutine[Any, Any, T],
*,
name: str | None = None,
) -> None:
"""Create a fully managed async task.
This will automatically retain and release a reference to the task
and log any exceptions that occur in it. If you need to await a task
or otherwise need more control, schedule a task directly using
App.asyncio_loop.
"""
assert _babase.in_logic_thread()
# Hold a strong reference to the task until it is done.
# Otherwise it is possible for it to be garbage collected and
# disappear midway if the caller does not hold on to the
# returned task, which seems like a great way to introduce
# hard-to-track bugs.
task = self.asyncio_loop.create_task(coro, name=name)
self._asyncio_tasks.add(task)
task.add_done_callback(self._on_task_done)
# return task
def _on_task_done(self, task: asyncio.Task) -> None:
# Report any errors that occurred.
try:
exc = task.exception()
if exc is not None:
logging.error(
"Error in async task '%s'.", task.get_name(), exc_info=exc
)
except Exception:
logging.exception('Error reporting async task error.')
self._asyncio_tasks.remove(task)
@property
def config(self) -> babase.AppConfig:
@ -437,6 +493,12 @@ class App:
self._native_shutdown_complete_called = True
self._update_state()
def on_native_active_changed(self) -> None:
"""Called by the native layer when the app active state changes."""
assert _babase.in_logic_thread()
if self._mode is not None:
self._mode.on_app_active_changed()
def read_config(self) -> None:
"""(internal)"""
from babase._appconfig import read_app_config
@ -588,7 +650,7 @@ class App:
_env.on_app_state_initing()
self._aioloop = _asyncio.setup_asyncio()
self._asyncio_loop = _asyncio.setup_asyncio()
self.health_monitor = AppHealthMonitor()
# __FEATURESET_APP_SUBSYSTEM_CREATE_BEGIN__
@ -868,8 +930,8 @@ class App:
)
# Now kick off any async shutdown task(s).
assert self._aioloop is not None
self._shutdown_task = self._aioloop.create_task(self._shutdown())
assert self._asyncio_loop is not None
self._shutdown_task = self._asyncio_loop.create_task(self._shutdown())
def _on_shutdown_complete(self) -> None:
"""(internal)"""

View file

@ -52,3 +52,10 @@ class AppMode:
def on_deactivate(self) -> None:
"""Called when the mode is being deactivated."""
def on_app_active_changed(self) -> None:
"""Called when babase.app.active changes.
The app-mode may want to take action such as pausing a running
game in such cases.
"""

View file

@ -40,16 +40,16 @@ class AppSubsystem:
"""Called when the app reaches the running state."""
def on_app_suspend(self) -> None:
"""Called when the app enters the paused state."""
"""Called when the app enters the suspended state."""
def on_app_unsuspend(self) -> None:
"""Called when the app exits the paused state."""
"""Called when the app exits the suspended state."""
def on_app_shutdown(self) -> None:
"""Called when the app is shutting down."""
"""Called when the app begins shutting down."""
def on_app_shutdown_complete(self) -> None:
"""Called when the app is done shutting down."""
"""Called when the app completes shutting down."""
def do_apply_app_config(self) -> None:
"""Called when the app config should be applied."""

View file

@ -10,9 +10,11 @@ from threading import Thread
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
from efro.call import tpartial
from efro.log import LogLevel
from efro.dataclassio import ioprepped, dataclass_to_json, dataclass_from_json
import _babase
from babase._appsubsystem import AppSubsystem
@ -386,6 +388,7 @@ class AppHealthMonitor(AppSubsystem):
self._response = False
self._first_check = True
@override
def on_app_loading(self) -> None:
# If any traceback dumps happened last run, log and clear them.
log_dumped_app_state(from_previous_run=True)
@ -449,10 +452,12 @@ class AppHealthMonitor(AppSubsystem):
self._first_check = False
@override
def on_app_suspend(self) -> None:
assert _babase.in_logic_thread()
self._running = False
@override
def on_app_unsuspend(self) -> None:
assert _babase.in_logic_thread()
self._running = True

View file

@ -8,6 +8,8 @@ from typing import TYPE_CHECKING
from dataclasses import dataclass
import logging
from typing_extensions import override
import _babase
if TYPE_CHECKING:
@ -96,6 +98,7 @@ class DevConsoleTab:
class DevConsoleTabPython(DevConsoleTab):
"""The Python dev-console tab."""
@override
def refresh(self) -> None:
self.python_terminal()
@ -103,6 +106,7 @@ class DevConsoleTabPython(DevConsoleTab):
class DevConsoleTabTest(DevConsoleTab):
"""Test dev-console tab."""
@override
def refresh(self) -> None:
import random

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bacommon.app import AppExperience
import _babase
@ -18,15 +19,18 @@ if TYPE_CHECKING:
class EmptyAppMode(AppMode):
"""An empty app mode that can be used as a fallback/etc."""
@override
@classmethod
def get_app_experience(cls) -> AppExperience:
return AppExperience.EMPTY
@override
@classmethod
def _supports_intent(cls, intent: AppIntent) -> bool:
# We support default and exec intents currently.
return isinstance(intent, AppIntentExec | AppIntentDefault)
@override
def handle_intent(self, intent: AppIntent) -> None:
if isinstance(intent, AppIntentExec):
_babase.empty_app_mode_handle_intent_exec(intent.code)
@ -34,10 +38,12 @@ class EmptyAppMode(AppMode):
assert isinstance(intent, AppIntentDefault)
_babase.empty_app_mode_handle_intent_default()
@override
def on_activate(self) -> None:
# Let the native layer do its thing.
_babase.on_empty_app_mode_activate()
@override
def on_deactivate(self) -> None:
# Let the native layer do its thing.
_babase.on_empty_app_mode_deactivate()

View file

@ -9,6 +9,7 @@ import logging
import warnings
from typing import TYPE_CHECKING
from typing_extensions import override
from efro.log import LogLevel
if TYPE_CHECKING:
@ -216,6 +217,7 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None:
class _CustomHelper:
"""Replacement 'help' that behaves better for our setup."""
@override
def __repr__(self) -> str:
return 'Type help(object) for help about object.'

View file

@ -10,6 +10,7 @@ import logging
import inspect
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType
from typing_extensions import override
from efro.terminal import Clr
import _babase
@ -178,6 +179,7 @@ class _WeakCall:
def __call__(self, *args_extra: Any) -> Any:
return self._call(*self._args + args_extra, **self._keywds)
@override
def __str__(self) -> str:
return (
'<ba.WeakCall object; _call='
@ -224,6 +226,7 @@ class _Call:
def __call__(self, *args_extra: Any) -> Any:
return self._call(*self._args + args_extra, **self._keywds)
@override
def __str__(self) -> str:
return (
'<ba.Call object; _call='
@ -268,6 +271,7 @@ class WeakMethod:
return None
return self._func(*((obj,) + args), **keywds)
@override
def __str__(self) -> str:
return '<ba.WeakMethod object; call=' + str(self._func) + '>'

View file

@ -8,6 +8,8 @@ import json
import logging
from typing import TYPE_CHECKING, overload
from typing_extensions import override
import _babase
from babase._appsubsystem import AppSubsystem
@ -217,6 +219,7 @@ class LanguageSubsystem(AppSubsystem):
color=(0, 1, 0),
)
@override
def do_apply_app_config(self) -> None:
assert _babase.in_logic_thread()
assert isinstance(_babase.app.config, dict)
@ -598,9 +601,11 @@ class Lstr:
_error.print_exception('_get_json failed for', self.args)
return 'JSON_ERR'
@override
def __str__(self) -> str:
return '<ba.Lstr: ' + self._get_json() + '>'
@override
def __repr__(self) -> str:
return '<ba.Lstr: ' + self._get_json() + '>'
@ -648,5 +653,6 @@ class AttrDict(dict):
assert not isinstance(val, bytes)
return val
@override
def __setattr__(self, attr: str, value: Any) -> None:
raise AttributeError()

View file

@ -9,6 +9,7 @@ import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, final
from typing_extensions import override
from bacommon.login import LoginType
import _babase
@ -353,6 +354,7 @@ class LoginAdapterNative(LoginAdapter):
self._sign_in_attempt_num = 123
self._sign_in_attempts: dict[int, Callable[[str | None], None]] = {}
@override
def get_sign_in_token(
self, completion_cb: Callable[[str | None], None]
) -> None:
@ -363,6 +365,7 @@ class LoginAdapterNative(LoginAdapter):
self.login_type.value, attempt_id
)
@override
def on_back_end_active_change(self, active: bool) -> None:
_babase.login_adapter_back_end_active_change(
self.login_type.value, active

View file

@ -26,7 +26,7 @@ EXPORT_CLASS_NAME_SHORTCUTS: dict[str, str] = {
'plugin': 'babase.Plugin',
# DEPRECATED as of 12/2023. Currently am warning if finding these
# but should take this out eventually.
'keyboard': 'babase.Keyboard',
'keyboard': 'bauiv1.Keyboard',
}
T = TypeVar('T')

View file

@ -149,78 +149,84 @@ class SpecialChar(Enum):
PLAY_PAUSE_BUTTON = 13
FAST_FORWARD_BUTTON = 14
DPAD_CENTER_BUTTON = 15
OUYA_BUTTON_O = 16
OUYA_BUTTON_U = 17
OUYA_BUTTON_Y = 18
OUYA_BUTTON_A = 19
OUYA_LOGO = 20
LOGO = 21
TICKET = 22
GOOGLE_PLAY_GAMES_LOGO = 23
GAME_CENTER_LOGO = 24
DICE_BUTTON1 = 25
DICE_BUTTON2 = 26
DICE_BUTTON3 = 27
DICE_BUTTON4 = 28
GAME_CIRCLE_LOGO = 29
PARTY_ICON = 30
TEST_ACCOUNT = 31
TICKET_BACKING = 32
TROPHY1 = 33
TROPHY2 = 34
TROPHY3 = 35
TROPHY0A = 36
TROPHY0B = 37
TROPHY4 = 38
LOCAL_ACCOUNT = 39
EXPLODINARY_LOGO = 40
FLAG_UNITED_STATES = 41
FLAG_MEXICO = 42
FLAG_GERMANY = 43
FLAG_BRAZIL = 44
FLAG_RUSSIA = 45
FLAG_CHINA = 46
FLAG_UNITED_KINGDOM = 47
FLAG_CANADA = 48
FLAG_INDIA = 49
FLAG_JAPAN = 50
FLAG_FRANCE = 51
FLAG_INDONESIA = 52
FLAG_ITALY = 53
FLAG_SOUTH_KOREA = 54
FLAG_NETHERLANDS = 55
FEDORA = 56
HAL = 57
CROWN = 58
YIN_YANG = 59
EYE_BALL = 60
SKULL = 61
HEART = 62
DRAGON = 63
HELMET = 64
MUSHROOM = 65
NINJA_STAR = 66
VIKING_HELMET = 67
MOON = 68
SPIDER = 69
FIREBALL = 70
FLAG_UNITED_ARAB_EMIRATES = 71
FLAG_QATAR = 72
FLAG_EGYPT = 73
FLAG_KUWAIT = 74
FLAG_ALGERIA = 75
FLAG_SAUDI_ARABIA = 76
FLAG_MALAYSIA = 77
FLAG_CZECH_REPUBLIC = 78
FLAG_AUSTRALIA = 79
FLAG_SINGAPORE = 80
OCULUS_LOGO = 81
STEAM_LOGO = 82
NVIDIA_LOGO = 83
FLAG_IRAN = 84
FLAG_POLAND = 85
FLAG_ARGENTINA = 86
FLAG_PHILIPPINES = 87
FLAG_CHILE = 88
MIKIROG = 89
V2_LOGO = 90
PLAY_STATION_CROSS_BUTTON = 16
PLAY_STATION_CIRCLE_BUTTON = 17
PLAY_STATION_TRIANGLE_BUTTON = 18
PLAY_STATION_SQUARE_BUTTON = 19
PLAY_BUTTON = 20
PAUSE_BUTTON = 21
OUYA_BUTTON_O = 22
OUYA_BUTTON_U = 23
OUYA_BUTTON_Y = 24
OUYA_BUTTON_A = 25
OUYA_LOGO = 26
LOGO = 27
TICKET = 28
GOOGLE_PLAY_GAMES_LOGO = 29
GAME_CENTER_LOGO = 30
DICE_BUTTON1 = 31
DICE_BUTTON2 = 32
DICE_BUTTON3 = 33
DICE_BUTTON4 = 34
GAME_CIRCLE_LOGO = 35
PARTY_ICON = 36
TEST_ACCOUNT = 37
TICKET_BACKING = 38
TROPHY1 = 39
TROPHY2 = 40
TROPHY3 = 41
TROPHY0A = 42
TROPHY0B = 43
TROPHY4 = 44
LOCAL_ACCOUNT = 45
EXPLODINARY_LOGO = 46
FLAG_UNITED_STATES = 47
FLAG_MEXICO = 48
FLAG_GERMANY = 49
FLAG_BRAZIL = 50
FLAG_RUSSIA = 51
FLAG_CHINA = 52
FLAG_UNITED_KINGDOM = 53
FLAG_CANADA = 54
FLAG_INDIA = 55
FLAG_JAPAN = 56
FLAG_FRANCE = 57
FLAG_INDONESIA = 58
FLAG_ITALY = 59
FLAG_SOUTH_KOREA = 60
FLAG_NETHERLANDS = 61
FEDORA = 62
HAL = 63
CROWN = 64
YIN_YANG = 65
EYE_BALL = 66
SKULL = 67
HEART = 68
DRAGON = 69
HELMET = 70
MUSHROOM = 71
NINJA_STAR = 72
VIKING_HELMET = 73
MOON = 74
SPIDER = 75
FIREBALL = 76
FLAG_UNITED_ARAB_EMIRATES = 77
FLAG_QATAR = 78
FLAG_EGYPT = 79
FLAG_KUWAIT = 80
FLAG_ALGERIA = 81
FLAG_SAUDI_ARABIA = 82
FLAG_MALAYSIA = 83
FLAG_CZECH_REPUBLIC = 84
FLAG_AUSTRALIA = 85
FLAG_SINGAPORE = 86
OCULUS_LOGO = 87
STEAM_LOGO = 88
NVIDIA_LOGO = 89
FLAG_IRAN = 90
FLAG_POLAND = 91
FLAG_ARGENTINA = 92
FLAG_PHILIPPINES = 93
FLAG_CHILE = 94
MIKIROG = 95
V2_LOGO = 96

View file

@ -8,6 +8,8 @@ import logging
import importlib.util
from typing import TYPE_CHECKING
from typing_extensions import override
import _babase
from babase._appsubsystem import AppSubsystem
@ -158,6 +160,7 @@ class PluginSubsystem(AppSubsystem):
if config_changed:
_babase.app.config.commit()
@override
def on_app_running(self) -> None:
# Load up our plugins and go ahead and call their on_app_running
# calls.
@ -170,6 +173,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_running()')
@override
def on_app_suspend(self) -> None:
for plugin in self.active_plugins:
try:
@ -179,6 +183,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_suspend()')
@override
def on_app_unsuspend(self) -> None:
for plugin in self.active_plugins:
try:
@ -188,6 +193,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_unsuspend()')
@override
def on_app_shutdown(self) -> None:
for plugin in self.active_plugins:
try:
@ -197,6 +203,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_shutdown()')
@override
def on_app_shutdown_complete(self) -> None:
for plugin in self.active_plugins:
try:

View file

@ -5,6 +5,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from babase._stringedit import StringEditAdapter
import _babase
@ -24,9 +26,11 @@ class DevConsoleStringEditAdapter(StringEditAdapter):
description, initial_text, max_length, screen_space_center
)
@override
def _do_apply(self, new_text: str) -> None:
_babase.set_dev_console_input_text(new_text)
_babase.dev_console_input_adapter_finish()
@override
def _do_cancel(self) -> None:
_babase.dev_console_input_adapter_finish()

View file

@ -229,9 +229,7 @@ class AdsSubsystem:
await asyncio.sleep(1.0)
payload.run(fallback=True)
_fallback_task = babase.app.aioloop.create_task(
add_fallback_task()
)
babase.app.create_async_task(add_fallback_task())
self.show_ad('between_game', on_completion_call=payload.run)
else:
babase.pushcall(call) # Just run the callback without the ad.

View file

@ -4,8 +4,10 @@
from __future__ import annotations
import random
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import bascenev1
import _baclassic
@ -42,70 +44,81 @@ def run_cpu_benchmark() -> None:
cfg['Graphics Quality'] = self._old_quality
cfg.apply()
@override
def on_player_request(self, player: bascenev1.SessionPlayer) -> bool:
return False
bascenev1.new_host_session(BenchmarkSession, benchmark_type='cpu')
@dataclass
class _StressTestArgs:
playlist_type: str
playlist_name: str
player_count: int
round_duration: int
attract_mode: bool
def run_stress_test(
playlist_type: str = 'Random',
playlist_name: str = '__default__',
player_count: int = 8,
round_duration: int = 30,
attract_mode: bool = False,
) -> None:
"""Run a stress test."""
babase.screenmessage(
"Beginning stress test.. use 'End Test' to stop testing.",
color=(1, 1, 0),
)
with babase.ContextRef.empty():
start_stress_test(
{
'playlist_type': playlist_type,
'playlist_name': playlist_name,
'player_count': player_count,
'round_duration': round_duration,
}
if not attract_mode:
babase.screenmessage(
"Beginning stress test.. use 'End Test' to stop testing.",
color=(1, 1, 0),
)
_start_stress_test(
_StressTestArgs(
playlist_type=playlist_type,
playlist_name=playlist_name,
player_count=player_count,
round_duration=round_duration,
attract_mode=attract_mode,
)
)
def stop_stress_test() -> None:
"""End a running stress test."""
_baclassic.set_stress_testing(False, 0)
assert babase.app.classic is not None
try:
if babase.app.classic.stress_test_reset_timer is not None:
babase.screenmessage('Ending stress test...', color=(1, 1, 0))
except Exception:
pass
babase.app.classic.stress_test_reset_timer = None
_baclassic.set_stress_testing(False, 0, False)
babase.app.classic.stress_test_update_timer = None
babase.app.classic.stress_test_update_timer_2 = None
def start_stress_test(args: dict[str, Any]) -> None:
def _start_stress_test(args: _StressTestArgs) -> None:
"""(internal)"""
from bascenev1 import DualTeamSession, FreeForAllSession
assert babase.app.classic is not None
appconfig = babase.app.config
playlist_type = args['playlist_type']
playlist_type = args.playlist_type
if playlist_type == 'Random':
if random.random() < 0.5:
playlist_type = 'Teams'
else:
playlist_type = 'Free-For-All'
babase.screenmessage(
'Running Stress Test (listType="'
+ playlist_type
+ '", listName="'
+ args['playlist_name']
+ '")...'
)
if not args.attract_mode:
babase.screenmessage(
'Running Stress Test (listType="'
+ playlist_type
+ '", listName="'
+ args.playlist_name
+ '")...'
)
if playlist_type == 'Teams':
appconfig['Team Tournament Playlist Selection'] = args['playlist_name']
appconfig['Team Tournament Playlist Selection'] = args.playlist_name
appconfig['Team Tournament Playlist Randomize'] = 1
babase.apptimer(
1.0,
@ -115,7 +128,7 @@ def start_stress_test(args: dict[str, Any]) -> None:
),
)
else:
appconfig['Free-for-All Playlist Selection'] = args['playlist_name']
appconfig['Free-for-All Playlist Selection'] = args.playlist_name
appconfig['Free-for-All Playlist Randomize'] = 1
babase.apptimer(
1.0,
@ -124,19 +137,38 @@ def start_stress_test(args: dict[str, Any]) -> None:
babase.Call(bascenev1.new_host_session, FreeForAllSession),
),
)
_baclassic.set_stress_testing(True, args['player_count'])
babase.app.classic.stress_test_reset_timer = babase.AppTimer(
args['round_duration'], babase.Call(_reset_stress_test, args)
_baclassic.set_stress_testing(True, args.player_count, args.attract_mode)
babase.app.classic.stress_test_update_timer = babase.AppTimer(
args.round_duration, babase.Call(_reset_stress_test, args)
)
if args.attract_mode:
babase.app.classic.stress_test_update_timer_2 = babase.AppTimer(
0.48, babase.Call(_update_attract_mode_test, args), repeat=True
)
def _reset_stress_test(args: dict[str, Any]) -> None:
_baclassic.set_stress_testing(False, args['player_count'])
babase.screenmessage('Resetting stress test...')
def _update_attract_mode_test(args: _StressTestArgs) -> None:
if babase.get_input_idle_time() < 5.0:
_reset_stress_test(args)
def _reset_stress_test(args: _StressTestArgs) -> None:
_baclassic.set_stress_testing(False, args.player_count, False)
if not args.attract_mode:
babase.screenmessage('Resetting stress test...')
session = bascenev1.get_foreground_host_session()
assert session is not None
session.end()
babase.apptimer(1.0, babase.Call(start_stress_test, args))
assert babase.app.classic is not None
babase.app.classic.stress_test_update_timer = None
babase.app.classic.stress_test_update_timer_2 = None
# For regular stress tests we keep the party going. For attract-mode
# we just end back at the main menu. If things are idle there then
# we'll get sent back to a new stress test.
if not args.attract_mode:
babase.apptimer(1.0, babase.Call(_start_stress_test, args))
def run_gpu_benchmark() -> None:

View file

@ -9,6 +9,7 @@ import threading
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import bascenev1
@ -68,6 +69,7 @@ class MasterServerV1CallThread(threading.Thread):
with self._context:
self._callback(arg)
@override
def run(self) -> None:
# pylint: disable=consider-using-with
# pylint: disable=too-many-branches

View file

@ -8,6 +8,7 @@ import random
import logging
import weakref
from typing_extensions import override
from efro.dataclassio import dataclass_from_dict
import babase
import bauiv1
@ -69,7 +70,8 @@ class ClassicSubsystem(babase.AppSubsystem):
# Misc.
self.tips: list[str] = []
self.stress_test_reset_timer: babase.AppTimer | None = None
self.stress_test_update_timer: babase.AppTimer | None = None
self.stress_test_update_timer_2: babase.AppTimer | None = None
self.value_test_defaults: dict = {}
self.special_offer: dict | None = None
self.ping_thread_count = 0
@ -148,6 +150,7 @@ class ClassicSubsystem(babase.AppSubsystem):
assert isinstance(self._env['legacy_user_agent_string'], str)
return self._env['legacy_user_agent_string']
@override
def on_app_loading(self) -> None:
from bascenev1lib.actor import spazappearance
from bascenev1lib import maps as stdmaps
@ -229,13 +232,16 @@ class ClassicSubsystem(babase.AppSubsystem):
self.accounts.on_app_loading()
@override
def on_app_suspend(self) -> None:
self.accounts.on_app_suspend()
@override
def on_app_unsuspend(self) -> None:
self.accounts.on_app_unsuspend()
self.music.on_app_unsuspend()
@override
def on_app_shutdown(self) -> None:
self.music.on_app_shutdown()
@ -555,11 +561,18 @@ class ClassicSubsystem(babase.AppSubsystem):
playlist_name: str = '__default__',
player_count: int = 8,
round_duration: int = 30,
attract_mode: bool = False,
) -> None:
"""Run a stress test."""
from baclassic._benchmark import run_stress_test as run
run(playlist_type, playlist_name, player_count, round_duration)
run(
playlist_type=playlist_type,
playlist_name=playlist_name,
player_count=player_count,
round_duration=round_duration,
attract_mode=attract_mode,
)
def get_input_device_mapped_value(
self, device: bascenev1.InputDevice, name: str

View file

@ -8,6 +8,7 @@ import threading
from collections import deque
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
from baclassic._music import MusicPlayer
@ -27,6 +28,7 @@ class MacMusicAppMusicPlayer(MusicPlayer):
self._thread = _MacMusicAppThread()
self._thread.start()
@override
def on_select_entry(
self,
callback: Callable[[Any], None],
@ -40,6 +42,7 @@ class MacMusicAppMusicPlayer(MusicPlayer):
callback, current_entry, selection_target_name
)
@override
def on_set_volume(self, volume: float) -> None:
self._thread.set_volume(volume)
@ -47,6 +50,7 @@ class MacMusicAppMusicPlayer(MusicPlayer):
"""Asynchronously fetch the list of available iTunes playlists."""
self._thread.get_playlists(callback)
@override
def on_play(self, entry: Any) -> None:
assert babase.app.classic is not None
music = babase.app.classic.music
@ -59,9 +63,11 @@ class MacMusicAppMusicPlayer(MusicPlayer):
entry_type,
)
@override
def on_stop(self) -> None:
self._thread.play_playlist(None)
@override
def on_app_shutdown(self) -> None:
self._thread.shutdown()
@ -77,6 +83,7 @@ class _MacMusicAppThread(threading.Thread):
self._current_playlist: str | None = None
self._orig_volume: int | None = None
@override
def run(self) -> None:
"""Run the Music.app thread."""
babase.set_thread_name('BA_MacMusicAppThread')

View file

@ -9,6 +9,7 @@ import logging
import threading
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
from baclassic._music import MusicPlayer
@ -33,6 +34,7 @@ class OSMusicPlayer(MusicPlayer):
# FIXME: should ask the C++ layer for these; just hard-coding for now.
return ['mp3', 'ogg', 'm4a', 'wav', 'flac', 'mid']
@override
def on_select_entry(
self,
callback: Callable[[Any], None],
@ -48,9 +50,11 @@ class OSMusicPlayer(MusicPlayer):
callback, current_entry, selection_target_name
)
@override
def on_set_volume(self, volume: float) -> None:
babase.music_player_set_volume(volume)
@override
def on_play(self, entry: Any) -> None:
assert babase.app.classic is not None
music = babase.app.classic.music
@ -99,11 +103,13 @@ class OSMusicPlayer(MusicPlayer):
self._actually_playing = True
babase.music_player_play(result)
@override
def on_stop(self) -> None:
self._want_to_play = False
self._actually_playing = False
babase.music_player_stop()
@override
def on_app_shutdown(self) -> None:
babase.music_player_shutdown()
@ -120,6 +126,7 @@ class _PickFolderSongThread(threading.Thread):
self._callback = callback
self._path = path
@override
def run(self) -> None:
do_log_error = True
try:

View file

@ -21,7 +21,7 @@ class BuildInfoSet:
@dataclass
class Entry:
"""Info about a particular build."""
"""Info about a particular app build."""
filename: Annotated[str, IOAttrs('fname')]
size: Annotated[int, IOAttrs('size')]

View file

@ -7,6 +7,7 @@ from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Annotated
from enum import Enum
from typing_extensions import override
from efro.message import Message, Response
from efro.dataclassio import ioprepped, IOAttrs
from bacommon.transfer import DirectoryManifest
@ -21,6 +22,7 @@ if TYPE_CHECKING:
class LoginProxyRequestMessage(Message):
"""Request send to the cloud to ask for a login-proxy."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [LoginProxyRequestResponse]
@ -49,6 +51,7 @@ class LoginProxyStateQueryMessage(Message):
proxyid: Annotated[str, IOAttrs('p')]
proxykey: Annotated[str, IOAttrs('k')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [LoginProxyStateQueryResponse]
@ -85,6 +88,7 @@ class LoginProxyCompleteMessage(Message):
class PingMessage(Message):
"""Standard ping."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [PingResponse]
@ -103,6 +107,7 @@ class TestMessage(Message):
testfoo: Annotated[int, IOAttrs('f')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [TestResponse]
@ -116,6 +121,28 @@ class TestResponse(Response):
testfoo: Annotated[int, IOAttrs('f')]
@ioprepped
@dataclass
class PromoCodeMessage(Message):
"""User is entering a promo code"""
code: Annotated[str, IOAttrs('c')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [PromoCodeResponse]
@ioprepped
@dataclass
class PromoCodeResponse(Response):
"""Applied that promo code for ya, boss."""
valid: Annotated[bool, IOAttrs('v')]
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
@ioprepped
@dataclass
class WorkspaceFetchState:
@ -136,6 +163,7 @@ class WorkspaceFetchMessage(Message):
workspaceid: Annotated[str, IOAttrs('w')]
state: Annotated[WorkspaceFetchState, IOAttrs('s')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [WorkspaceFetchResponse]
@ -162,6 +190,7 @@ class WorkspaceFetchResponse(Response):
class MerchAvailabilityMessage(Message):
"""Can we show merch link?"""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [MerchAvailabilityResponse]
@ -187,6 +216,7 @@ class SignInMessage(Message):
description: Annotated[str, IOAttrs('d', soft_default='-')]
apptime: Annotated[float, IOAttrs('at', soft_default=-1.0)]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SignInResponse]
@ -205,6 +235,7 @@ class SignInResponse(Response):
class ManageAccountMessage(Message):
"""Message asking for a manage-account url."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ManageAccountResponse]

View file

@ -31,7 +31,7 @@ class DirectoryManifest:
files: Annotated[dict[str, DirectoryManifestFile], IOAttrs('f')]
_empty_hash: str | None = None
# _empty_hash: str | None = None
@classmethod
def create_from_disk(cls, path: Path) -> DirectoryManifest:
@ -92,12 +92,12 @@ class DirectoryManifest:
)
break # 1 error is enough for now.
@classmethod
def get_empty_hash(cls) -> str:
"""Return the hash for an empty file."""
if cls._empty_hash is None:
import hashlib
# @classmethod
# def get_empty_hash(cls) -> str:
# """Return the hash for an empty file."""
# if cls._empty_hash is None:
# import hashlib
sha = hashlib.sha256()
cls._empty_hash = sha.hexdigest()
return cls._empty_hash
# sha = hashlib.sha256()
# cls._empty_hash = sha.hexdigest()
# return cls._empty_hash

View file

@ -52,8 +52,8 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21739
TARGET_BALLISTICA_VERSION = '1.7.32'
TARGET_BALLISTICA_BUILD = 21762
TARGET_BALLISTICA_VERSION = '1.7.33'
@dataclass
@ -350,9 +350,15 @@ def _setup_paths(
# platforms where there is no write access to said built-in
# stuff.
check_dir = Path(user_python_dir, 'sys', TARGET_BALLISTICA_VERSION)
if check_dir.is_dir():
app_python_dir = str(check_dir)
is_user_app_python_dir = True
try:
if check_dir.is_dir():
app_python_dir = str(check_dir)
is_user_app_python_dir = True
except PermissionError:
logging.warning(
"PermissionError checking user-app-python-dir path '%s'.",
check_dir,
)
# Ok, now apply these to sys.path.

View file

@ -16,9 +16,11 @@ from __future__ import annotations
import logging
from baplus._cloud import CloudSubsystem
from baplus._subsystem import PlusSubsystem
__all__ = [
'CloudSubsystem',
'PlusSubsystem',
]

214
dist/ba_data/python/baplus/_cloud.py vendored Normal file
View file

@ -0,0 +1,214 @@
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to the cloud."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, overload
import babase
if TYPE_CHECKING:
from typing import Callable, Any
from efro.message import Message, Response
import bacommon.cloud
DEBUG_LOG = False
# TODO: Should make it possible to define a protocol in bacommon.cloud and
# autogenerate this. That would give us type safety between this and
# internal protocols.
class CloudSubsystem(babase.AppSubsystem):
"""Manages communication with cloud components."""
@property
def connected(self) -> bool:
"""Property equivalent of CloudSubsystem.is_connected()."""
return self.is_connected()
def is_connected(self) -> bool:
"""Return whether a connection to the cloud is present.
This is a good indicator (though not for certain) that sending
messages will succeed.
"""
return False # Needs to be overridden
def on_connectivity_changed(self, connected: bool) -> None:
"""Called when cloud connectivity state changes."""
if DEBUG_LOG:
logging.debug('CloudSubsystem: Connectivity is now %s.', connected)
plus = babase.app.plus
assert plus is not None
# Inform things that use this.
# (TODO: should generalize this into some sort of registration system)
plus.accounts.on_cloud_connectivity_changed(connected)
@overload
def send_message_cb(
self,
msg: bacommon.cloud.LoginProxyRequestMessage,
on_response: Callable[
[bacommon.cloud.LoginProxyRequestResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.LoginProxyStateQueryMessage,
on_response: Callable[
[bacommon.cloud.LoginProxyStateQueryResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.LoginProxyCompleteMessage,
on_response: Callable[[None | Exception], None],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.PingMessage,
on_response: Callable[[bacommon.cloud.PingResponse | Exception], None],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.SignInMessage,
on_response: Callable[
[bacommon.cloud.SignInResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.ManageAccountMessage,
on_response: Callable[
[bacommon.cloud.ManageAccountResponse | Exception], None
],
) -> None:
...
def send_message_cb(
self,
msg: Message,
on_response: Callable[[Any], None],
) -> None:
"""Asynchronously send a message to the cloud from the logic thread.
The provided on_response call will be run in the logic thread
and passed either the response or the error that occurred.
"""
del msg # Unused.
babase.pushcall(
babase.Call(
on_response,
RuntimeError('Cloud functionality is not available.'),
)
)
@overload
def send_message(
self, msg: bacommon.cloud.WorkspaceFetchMessage
) -> bacommon.cloud.WorkspaceFetchResponse:
...
@overload
def send_message(
self, msg: bacommon.cloud.MerchAvailabilityMessage
) -> bacommon.cloud.MerchAvailabilityResponse:
...
@overload
def send_message(
self, msg: bacommon.cloud.TestMessage
) -> bacommon.cloud.TestResponse:
...
def send_message(self, msg: Message) -> Response | None:
"""Synchronously send a message to the cloud.
Must be called from a background thread.
"""
raise RuntimeError('Cloud functionality is not available.')
@overload
async def send_message_async(
self, msg: bacommon.cloud.PromoCodeMessage
) -> bacommon.cloud.PromoCodeResponse:
...
@overload
async def send_message_async(
self, msg: bacommon.cloud.TestMessage
) -> bacommon.cloud.TestResponse:
...
async def send_message_async(self, msg: Message) -> Response | None:
"""Synchronously send a message to the cloud.
Must be called from the logic thread.
"""
raise RuntimeError('Cloud functionality is not available.')
def cloud_console_exec(code: str) -> None:
"""Called by the cloud console to run code in the logic thread."""
import sys
import __main__
try:
# First try it as eval.
try:
evalcode = compile(code, '<console>', 'eval')
except SyntaxError:
evalcode = None
except Exception:
# hmm; when we can't compile it as eval will we always get
# syntax error?
logging.exception(
'unexpected error compiling code for cloud-console eval.'
)
evalcode = None
if evalcode is not None:
# pylint: disable=eval-used
value = eval(evalcode, vars(__main__), vars(__main__))
# For eval-able statements, print the resulting value if
# it is not None (just like standard Python interpreter).
if value is not None:
print(repr(value), file=sys.stderr)
# Fall back to exec if we couldn't compile it as eval.
else:
execcode = compile(code, '<console>', 'exec')
# pylint: disable=exec-used
exec(execcode, vars(__main__), vars(__main__))
except Exception:
import traceback
apptime = babase.apptime()
print(f'Exec error at time {apptime:.2f}.', file=sys.stderr)
traceback.print_exc()
# This helps the logging system ship stderr back to the
# cloud promptly.
sys.stderr.flush()

View file

@ -5,13 +5,17 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import _baplus
from typing_extensions import override
from babase import AppSubsystem
import _baplus
if TYPE_CHECKING:
from typing import Callable, Any
from babase import CloudSubsystem, AccountV2Subsystem
from babase import AccountV2Subsystem
from baplus._cloud import CloudSubsystem
class PlusSubsystem(AppSubsystem):
@ -32,6 +36,7 @@ class PlusSubsystem(AppSubsystem):
accounts: AccountV2Subsystem
cloud: CloudSubsystem
@override
def on_app_loading(self) -> None:
_baplus.on_app_loading()
self.accounts.on_app_loading()

View file

@ -103,6 +103,7 @@ from _bascenev1 import (
host_scan_cycle,
InputDevice,
is_in_replay,
is_replay_paused,
ls_input_devices,
ls_objects,
Material,
@ -112,11 +113,13 @@ from _bascenev1 import (
newactivity,
newnode,
Node,
pause_replay,
printnodes,
protocol_version,
release_gamepad_input,
release_keyboard_input,
reset_random_player_names,
resume_replay,
broadcastmessage,
SessionData,
SessionPlayer,
@ -352,6 +355,7 @@ __all__ = [
'IntSetting',
'is_in_replay',
'is_point_in_box',
'is_replay_paused',
'JoinActivity',
'Level',
'Lobby',
@ -374,6 +378,7 @@ __all__ = [
'normalized_color',
'NotFoundError',
'OutOfBoundsMessage',
'pause_replay',
'PickedUpMessage',
'PickUpMessage',
'Player',
@ -394,6 +399,7 @@ __all__ = [
'release_gamepad_input',
'release_keyboard_input',
'reset_random_player_names',
'resume_replay',
'safecolor',
'screenmessage',
'SceneV1AppMode',

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -34,11 +35,13 @@ class EndSessionActivity(Activity[EmptyPlayer, EmptyTeam]):
self.inherits_vr_camera_offset = True
self.inherits_vr_overlay_center = True
@override
def on_transition_in(self) -> None:
super().on_transition_in()
babase.fade_screen(False)
babase.lock_all_input()
@override
def on_begin(self) -> None:
# pylint: disable=cyclic-import
@ -77,6 +80,7 @@ class JoinActivity(Activity[EmptyPlayer, EmptyTeam]):
self._tips_text: bascenev1.Actor | None = None
self._join_info: JoinInfo | None = None
@override
def on_transition_in(self) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.actor.tipstext import TipsText
@ -110,6 +114,7 @@ class TransitionActivity(Activity[EmptyPlayer, EmptyTeam]):
super().__init__(settings)
self._background: bascenev1.Actor | None = None
@override
def on_transition_in(self) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.actor.background import Background
@ -119,6 +124,7 @@ class TransitionActivity(Activity[EmptyPlayer, EmptyTeam]):
fade_time=0.5, start_faded=False, show_logo=False
)
@override
def on_begin(self) -> None:
super().on_begin()
@ -152,6 +158,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
self._custom_continue_message: babase.Lstr | None = None
self._server_transitioning: bool | None = None
@override
def on_player_join(self, player: EmptyPlayer) -> None:
super().on_player_join(player)
time_till_assign = max(
@ -164,6 +171,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
time_till_assign, babase.WeakCall(self._safe_assign, player)
)
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor.tipstext import TipsText
from bascenev1lib.actor.background import Background
@ -176,6 +184,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
self._tips_text = TipsText()
setmusic(self.default_music)
@override
def on_begin(self) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.actor.text import Text

View file

@ -5,8 +5,15 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bacommon.app import AppExperience
from babase import AppMode, AppIntentExec, AppIntentDefault
from babase import (
app,
AppMode,
AppIntentExec,
AppIntentDefault,
invoke_main_menu,
)
import _bascenev1
@ -17,15 +24,18 @@ if TYPE_CHECKING:
class SceneV1AppMode(AppMode):
"""Our app-mode."""
@override
@classmethod
def get_app_experience(cls) -> AppExperience:
return AppExperience.MELEE
@override
@classmethod
def _supports_intent(cls, intent: AppIntent) -> bool:
# We support default and exec intents currently.
return isinstance(intent, AppIntentExec | AppIntentDefault)
@override
def handle_intent(self, intent: AppIntent) -> None:
if isinstance(intent, AppIntentExec):
_bascenev1.handle_app_intent_exec(intent.code)
@ -33,10 +43,19 @@ class SceneV1AppMode(AppMode):
assert isinstance(intent, AppIntentDefault)
_bascenev1.handle_app_intent_default()
@override
def on_activate(self) -> None:
# Let the native layer do its thing.
_bascenev1.on_app_mode_activate()
@override
def on_deactivate(self) -> None:
# Let the native layer do its thing.
_bascenev1.on_app_mode_deactivate()
@override
def on_app_active_changed(self) -> None:
# If we've gone inactive, bring up the main menu, which has the
# side effect of pausing the action (when possible).
if not app.active:
invoke_main_menu()

View file

@ -6,6 +6,7 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import babase
import _bascenev1
@ -31,6 +32,7 @@ class CoopGameActivity(GameActivity[PlayerT, TeamT]):
# We can assume our session is a CoopSession.
session: bascenev1.CoopSession
@override
@classmethod
def supports_session_type(
cls, sessiontype: type[bascenev1.Session]
@ -49,6 +51,7 @@ class CoopGameActivity(GameActivity[PlayerT, TeamT]):
self._life_warning_beep_timer: bascenev1.Timer | None = None
self._warn_beeps_sound = _bascenev1.getsound('warnBeeps')
@override
def on_begin(self) -> None:
super().on_begin()
@ -139,6 +142,7 @@ class CoopGameActivity(GameActivity[PlayerT, TeamT]):
)
vval -= 55
@override
def spawn_player_spaz(
self,
player: PlayerT,

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -97,6 +98,7 @@ class CoopSession(Session):
"""Get the game instance currently being played."""
return self._current_game_instance
@override
def should_allow_mid_activity_joins(
self, activity: bascenev1.Activity
) -> bool:
@ -174,9 +176,11 @@ class CoopSession(Session):
self._tutorial_activity = _bascenev1.newactivity(TutorialActivity)
@override
def get_custom_menu_entries(self) -> list[dict[str, Any]]:
return self._custom_menu_ui
@override
def on_player_leave(self, sessionplayer: bascenev1.SessionPlayer) -> None:
super().on_player_leave(sessionplayer)
@ -256,6 +260,7 @@ class CoopSession(Session):
activity.end(results={'outcome': 'restart'}, force=True)
# noinspection PyUnresolvedReferences
@override
def on_activity_end(
self, activity: bascenev1.Activity, results: Any
) -> None:

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import weakref
from typing import Generic, TypeVar, TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -313,6 +314,7 @@ class AssetPackage(DependencyComponent):
self.package_id = entry.config
print(f'LOADING ASSET PACKAGE {self.package_id}')
@override
@classmethod
def dep_is_present(cls, config: Any = None) -> bool:
assert isinstance(config, str)

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -32,6 +33,7 @@ class DualTeamSession(MultiTeamSession):
babase.increment_analytics_count('Teams session start')
super().__init__()
@override
def _switch_to_score_screen(self, results: bascenev1.GameResults) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.activity.multiteamvictory import (

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -53,6 +54,7 @@ class FreeForAllSession(MultiTeamSession):
babase.increment_analytics_count('Free-for-all session start')
super().__init__()
@override
def _switch_to_score_screen(self, results: bascenev1.GameResults) -> None:
# pylint: disable=cyclic-import
from efro.util import asserttype

View file

@ -9,6 +9,7 @@ import random
import logging
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import babase
import _bascenev1
@ -377,6 +378,7 @@ class GameActivity(Activity[PlayerT, TeamT]):
"""
return ''
@override
def on_transition_in(self) -> None:
super().on_transition_in()
@ -488,6 +490,7 @@ class GameActivity(Activity[PlayerT, TeamT]):
self.end_game()
@override
def on_begin(self) -> None:
super().on_begin()
@ -536,12 +539,14 @@ class GameActivity(Activity[PlayerT, TeamT]):
max(5, data_t[0]['timeRemaining'])
)
@override
def on_player_join(self, player: PlayerT) -> None:
super().on_player_join(player)
# By default, just spawn a dude.
self.spawn_player(player)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, PlayerDiedMessage):
# pylint: disable=cyclic-import
@ -835,6 +840,7 @@ class GameActivity(Activity[PlayerT, TeamT]):
animate(combine, 'input3', {0: 0, 1.0: 1, 4.0: 1, 5.0: 0})
_bascenev1.timer(5.0, tnode.delete)
@override
def end(
self, results: Any = None, delay: float = 0.0, force: bool = False
) -> None:

View file

@ -7,6 +7,7 @@ import copy
import weakref
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
if TYPE_CHECKING:
@ -38,6 +39,7 @@ class Level:
self._index: int | None = None
self._score_version_string: str | None = None
@override
def __repr__(self) -> str:
cls = type(self)
return f"<{cls.__module__}.{cls.__name__} '{self._name}'>"

View file

@ -441,7 +441,7 @@ class Chooser:
# list might have changed.
input_device = self._sessionplayer.inputdevice
is_remote = input_device.is_remote_client
is_test_input = input_device.name.startswith('TestInput')
is_test_input = input_device.is_test_input
# Pull this player's list of unlocked characters.
if is_remote:

View file

@ -6,6 +6,7 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -353,9 +354,11 @@ class Map(Actor):
return self.flag_points_default[:3]
return self.flag_points[team_index % len(self.flag_points)][:3]
@override
def exists(self) -> bool:
return bool(self.node)
@override
def handlemessage(self, msg: Any) -> Any:
from bascenev1 import _messages

View file

@ -8,7 +8,9 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
from bascenev1._session import Session
@ -70,6 +72,10 @@ class MultiTeamSession(Session):
show_tutorial = cfg.get('Show Tutorial', True)
# Special case: don't show tutorial while stress testing.
if classic.stress_test_update_timer is not None:
show_tutorial = False
self._tutorial_activity_instance: bascenev1.Activity | None
if show_tutorial:
from bascenev1lib.tutorial import TutorialActivity
@ -164,6 +170,7 @@ class MultiTeamSession(Session):
"""Returns which game in the series is currently being played."""
return self._game_number
@override
def on_team_join(self, team: bascenev1.SessionTeam) -> None:
team.customdata['previous_score'] = team.customdata['score'] = 0
@ -182,6 +189,7 @@ class MultiTeamSession(Session):
self._next_game_spec['settings'],
)
@override
def on_activity_end(
self, activity: bascenev1.Activity, results: Any
) -> None:

View file

@ -6,6 +6,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bascenev1._messages import DieMessage
from bascenev1._actor import Actor
@ -28,6 +30,7 @@ class NodeActor(Actor):
super().__init__()
self.node = node
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, DieMessage):
if self.node:
@ -35,5 +38,6 @@ class NodeActor(Actor):
return None
return super().handlemessage(msg)
@override
def exists(self) -> bool:
return bool(self.node)

View file

@ -253,7 +253,7 @@ class Session:
# Limit player counts *unless* we're in a stress test.
if (
babase.app.classic is not None
and babase.app.classic.stress_test_reset_timer is None
and babase.app.classic.stress_test_update_timer is None
):
if len(self.sessionplayers) >= self.max_players:
# Print a rejection message *only* to the client trying to

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import babase
import _bascenev1
@ -35,6 +36,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
bascenev1.Player has their own bascenev1.Team)
"""
@override
@classmethod
def supports_session_type(
cls, sessiontype: type[bascenev1.Session]
@ -57,6 +59,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
if isinstance(self.session, FreeForAllSession):
self.show_kill_points = False
@override
def on_transition_in(self) -> None:
# pylint: disable=cyclic-import
from bascenev1._coopsession import CoopSession
@ -67,7 +70,9 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
# On the first game, show the controls UI momentarily.
# (unless we're being run in co-op mode, in which case we leave
# it up to them)
if not isinstance(self.session, CoopSession):
if not isinstance(self.session, CoopSession) and getattr(
self, 'show_controls_guide', True
):
attrname = '_have_shown_ctrl_help_overlay'
if not getattr(self.session, attrname, False):
delay = 4.0
@ -83,6 +88,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
).autoretain()
setattr(self.session, attrname, True)
@override
def on_begin(self) -> None:
super().on_begin()
try:
@ -102,6 +108,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
except Exception:
logging.exception('Error in on_begin.')
@override
def spawn_player_spaz(
self,
player: PlayerT,

View file

@ -4,6 +4,7 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
@ -18,6 +19,7 @@ class CoopJoinActivity(bs.JoinActivity):
session = self.session
assert isinstance(session, bs.CoopSession)
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor.controlsguide import ControlsGuide
from bascenev1lib.actor.text import Text

View file

@ -9,6 +9,7 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
from bacommon.login import LoginType
import bascenev1 as bs
import bauiv1 as bui
@ -186,6 +187,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
self._victory: bool = settings['outcome'] == 'victory'
@override
def __del__(self) -> None:
super().__del__()
@ -194,6 +196,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
with bui.ContextRef.empty():
bui.containerwidget(edit=self._root_ui, transition='out_left')
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor import background # FIXME NO BSSTD
@ -574,6 +577,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
self._player_press,
)
@override
def on_player_join(self, player: bs.Player) -> None:
super().on_player_join(player)
@ -585,6 +589,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
bs.timer(time_till_assign, bs.WeakCall(self._safe_assign, player))
@override
def on_begin(self) -> None:
# FIXME: Clean this up.
# pylint: disable=too-many-statements

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText
@ -14,6 +16,7 @@ class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
default_music = None # Awkward silence...
@override
def on_begin(self) -> None:
bs.set_analytics_screen('Draw Score Screen')
super().on_begin()

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText
@ -17,6 +19,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self._winner: bs.SessionTeam = settings['winner']
assert isinstance(self._winner, bs.SessionTeam)
@override
def on_begin(self) -> None:
bs.set_analytics_screen('Teams Score Screen')
super().on_begin()

View file

@ -6,9 +6,11 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
if TYPE_CHECKING:
from typing import Any
@ -23,6 +25,7 @@ class FreeForAllVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self.transition_time = 0.5
self._cymbal_sound = bs.getsound('cymbal')
@override
def on_begin(self) -> None:
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.text import Text
@ -15,6 +17,7 @@ class MultiTeamJoinActivity(bs.JoinActivity):
super().__init__(settings)
self._next_up_text: Text | None = None
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor.controlsguide import ControlsGuide

View file

@ -3,7 +3,9 @@
"""Functionality related to teams mode score screen."""
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image
@ -18,6 +20,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
self._show_up_next: bool = True
@override
def on_begin(self) -> None:
super().on_begin()
session = self.session

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
@ -22,6 +24,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self._tips_text = None
self._default_show_tips = False
@override
def on_begin(self) -> None:
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals

View file

@ -9,6 +9,7 @@ import weakref
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -104,6 +105,7 @@ class Background(bs.Actor):
timeval += random.random() * 0.1
bs.animate(cmb, 'input1', keys, loop=True)
@override
def __del__(self) -> None:
# Normal actors don't get sent DieMessages when their
# activity is shutting down, but we still need to do so
@ -138,6 +140,7 @@ class Background(bs.Actor):
)
bs.timer(self.fade_time + 0.1, self.node.delete)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -10,7 +10,9 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
@ -661,6 +663,7 @@ class Blast(bs.Actor):
bs.timer(0.4, _extra_debris_sound)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
@ -935,6 +938,7 @@ class Bomb(bs.Actor):
else None
)
@override
def on_expire(self) -> None:
super().on_expire()
@ -1140,19 +1144,18 @@ class Bomb(bs.Actor):
if msg.srcnode:
pass
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, ExplodeMessage):
self.explode()
elif isinstance(msg, ImpactMessage):
self._handle_impact()
# Ok the logic below looks like it was backwards to me.
# Disabling for now; can bring back if need be.
# elif isinstance(msg, bs.PickedUpMessage):
# # Change our source to whoever just picked us up *only* if it
# # is None. This way we can get points for killing bots with their
# # own bombs. Hmm would there be a downside to this?
# if self._source_player is not None:
# self._source_player = msg.node.source_player
elif isinstance(msg, bs.PickedUpMessage):
# Change our source to whoever just picked us up *only* if it
# is None. This way we can get points for killing bots with their
# own bombs. Hmm would there be a downside to this?
if self._source_player is None:
self._source_player = msg.node.source_player
elif isinstance(msg, SplatMessage):
self._handle_splat()
elif isinstance(msg, bs.DroppedMessage):

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -547,9 +548,11 @@ class ControlsGuide(bs.Actor):
self._update_timer = None
self._dead = True
@override
def exists(self) -> bool:
return not self._dead
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -7,9 +7,11 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from bascenev1lib.gameutils import SharedObjects
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence
@ -328,6 +330,7 @@ class Flag(bs.Actor):
1.0, bs.WeakCall(self._hide_score_text)
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -7,6 +7,7 @@ from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -165,6 +166,7 @@ class Image(bs.Actor):
bs.WeakCall(self.handlemessage, bs.DieMessage()),
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -72,6 +73,7 @@ class OnScreenCountdown(bs.Actor):
)
self._timer = bs.Timer(1.0, self._update, repeat=True)
@override
def on_expire(self) -> None:
super().on_expire()

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import logging
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -93,6 +94,7 @@ class OnScreenTimer(bs.Actor):
"""Shortcut for start time in seconds."""
return self.getstarttime()
@override
def handlemessage(self, msg: Any) -> Any:
# if we're asked to die, just kill our node/timer
if isinstance(msg, bs.DieMessage):

View file

@ -6,7 +6,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING, TypeVar, overload
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz
if TYPE_CHECKING:
@ -183,6 +185,7 @@ class PlayerSpaz(Spaz):
' non-connected player'
)
@override
def handlemessage(self, msg: Any) -> Any:
# FIXME: Tidy this up.
# pylint: disable=too-many-branches

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -118,6 +119,7 @@ class PopupText(bs.Actor):
lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -7,7 +7,9 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
@ -278,6 +280,7 @@ class PowerupBox(bs.Actor):
if self.node:
self.node.flashing = True
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired

View file

@ -9,11 +9,13 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb, Blast
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence, Callable
@ -228,9 +230,11 @@ class Spaz(bs.Actor):
self.punch_callback: Callable[[Spaz], Any] | None = None
self.pick_up_powerup_callback: Callable[[Spaz], Any] | None = None
@override
def exists(self) -> bool:
return bool(self.node)
@override
def on_expire(self) -> None:
super().on_expire()
@ -249,6 +253,7 @@ class Spaz(bs.Actor):
assert not self.expired
self._dropped_bomb_callbacks.append(call)
@override
def is_alive(self) -> bool:
"""
Method override; returns whether ol' spaz is still kickin'.
@ -694,6 +699,7 @@ class Spaz(bs.Actor):
else:
self.shield_decay_timer = None
@override
def handlemessage(self, msg: Any) -> Any:
# pylint: disable=too-many-return-statements
# pylint: disable=too-many-statements

View file

@ -10,6 +10,7 @@ import weakref
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz
@ -489,6 +490,7 @@ class SpazBot(Spaz):
self.on_punch_press()
self.on_punch_release()
@override
def on_punched(self, damage: int) -> None:
"""
Method override; sends bs.SpazBotPunchedMessage
@ -496,6 +498,7 @@ class SpazBot(Spaz):
"""
bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage))
@override
def on_expire(self) -> None:
super().on_expire()
@ -503,6 +506,7 @@ class SpazBot(Spaz):
# no chance of them keeping activities or other things alive.
self.update_callback = None
@override
def handlemessage(self, msg: Any) -> Any:
# pylint: disable=too-many-branches
assert not self.expired

View file

@ -7,6 +7,7 @@ from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -221,6 +222,7 @@ class Text(bs.Actor):
bs.WeakCall(self.handlemessage, bs.DieMessage()),
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -95,6 +96,7 @@ class TipsText(bs.Actor):
)
self.node.text = next_tip
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -8,6 +8,7 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -158,6 +159,7 @@ class ZoomText(bs.Actor):
if lifespan is not None:
bs.timer(lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage()))
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -10,11 +10,13 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -71,10 +73,12 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -96,16 +100,19 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FORWARD_MARCH
)
@override
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:
return 'Touch the enemy flag.'
return 'Touch the enemy flag ${ARG1} times.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
if self._score_to_win == 1:
return 'touch 1 flag'
return 'touch ${ARG1} flags', self._score_to_win
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
shared = SharedObjects.get()
base_pos = self.map.get_flag_position(sessionteam.id)
@ -151,16 +158,19 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
return team
@override
def on_team_join(self, team: Team) -> None:
# Can't do this in create_team because the team's color/etc. have
# not been wired up yet at that point.
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard.
@ -249,6 +259,7 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
if player_team.score >= self._score_to_win:
self.end_game()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:

View file

@ -10,6 +10,9 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.flag import (
@ -19,7 +22,6 @@ from bascenev1lib.actor.flag import (
FlagDroppedMessage,
FlagDiedMessage,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -141,10 +143,12 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -173,16 +177,19 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FLAG_CATCHER
)
@override
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:
return 'Steal the enemy flag.'
return 'Steal the enemy flag ${ARG1} times.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
if self._score_to_win == 1:
return 'return 1 flag'
return 'return ${ARG1} flags', self._score_to_win
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
# Create our team instance and its initial values.
@ -272,12 +279,14 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
return team
@override
def on_team_join(self, team: Team) -> None:
# Can't do this in create_team because the team's color/etc. have
# not been wired up yet at that point.
self._spawn_flag_for_team(team)
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -406,6 +415,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
if team.score >= self._score_to_win:
self.end_game()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -532,6 +542,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
bs.animate(light, 'intensity', {0.0: 0, 0.25: 2.0, 0.5: 0}, loop=True)
bs.timer(length, light.delete)
@override
def spawn_player_spaz(
self,
player: Player,
@ -576,6 +587,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
team, team.score, self._score_to_win
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard behavior.

View file

@ -10,11 +10,13 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -83,6 +85,7 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
]
scoreconfig = bs.ScoreConfig(label='Time Held')
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -121,20 +124,25 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.CHOSEN_ONE
)
@override
def get_instance_description(self) -> str | Sequence:
return 'There can be only one.'
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
return Team(time_remaining=self._chosen_one_time)
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@override
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
if self._get_chosen_one_player() is player:
self._set_chosen_one_player(None)
@override
def on_begin(self) -> None:
super().on_begin()
shared = SharedObjects.get()
@ -251,6 +259,7 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
logging.error('got nonexistent player as chosen one in _tick')
self._set_chosen_one_player(None)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -335,6 +344,7 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
'position', light.node, 'position'
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.

View file

@ -10,12 +10,14 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.respawnicon import RespawnIcon
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -108,10 +110,12 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -143,16 +147,20 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
),
)
@override
def get_instance_description(self) -> str | Sequence:
return 'Secure all ${ARG1} flags.', len(self.map.flag_points)
@override
def get_instance_description_short(self) -> str | Sequence:
return 'secure all ${ARG1} flags', len(self.map.flag_points)
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scores()
@override
def on_player_join(self, player: Player) -> None:
player.respawn_timer = None
@ -160,6 +168,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
if player.team.flags_held > 0:
self.spawn_player(player)
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -221,6 +230,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
team, team.flags_held, len(self._flags)
)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -272,6 +282,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
):
self.spawn_player(otherplayer)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -287,6 +298,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
else:
super().handlemessage(msg)
@override
def spawn_player(self, player: Player) -> bs.Actor:
# We spawn players at different places based on what flags are held.
return self.spawn_player_spaz(

View file

@ -9,9 +9,11 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -38,6 +40,7 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
# Print messages when players die since it matters here.
announce_player_deaths = True
@override
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]
@ -87,12 +90,14 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
return settings
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
sessiontype, bs.FreeForAllSession
)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -116,16 +121,20 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.TO_THE_DEATH
)
@override
def get_instance_description(self) -> str | Sequence:
return 'Crush ${ARG1} of your enemies.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
return 'kill ${ARG1} enemies', self._score_to_win
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -137,6 +146,7 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
)
self._update_scoreboard()
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -197,6 +207,7 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
team, team.score, self._score_to_win
)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:

View file

@ -10,6 +10,9 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.spazbot import SpazBotSet, BouncyBot, SpazBotDiedMessage
@ -17,7 +20,6 @@ from bascenev1lib.actor.onscreencountdown import OnScreenCountdown
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.respawnicon import RespawnIcon
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any
@ -51,11 +53,13 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
scoreconfig = bs.ScoreConfig(label='Score', scoretype=bs.ScoreType.POINTS)
# We're currently hard-coded for one map.
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ['Tower D']
# We support teams, free-for-all, and co-op sessions.
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (
@ -93,11 +97,13 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FORWARD_MARCH
)
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
# Called when our game actually starts.
@override
def on_begin(self) -> None:
from bascenev1lib.maps import TowerD
@ -118,6 +124,7 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
self._spawn_evil_bunny()
# Overriding the default character spawning.
@override
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(player)
spaz.connect_controls_to_player()
@ -191,6 +198,7 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
self._eggs.append(Egg(position=(xpos, ypos, zpos)))
# Various high-level game events come through this method.
@override
def handlemessage(self, msg: Any) -> Any:
# Respawn dead players.
if isinstance(msg, bs.PlayerDiedMessage):
@ -231,6 +239,7 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
for team in self.teams:
self._scoreboard.set_team_value(team, team.score)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -271,9 +280,11 @@ class Egg(bs.Actor):
},
)
@override
def exists(self) -> bool:
return bool(self.node)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:

View file

@ -10,9 +10,11 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.actor.scoreboard import Scoreboard
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -157,6 +159,7 @@ class Icon(bs.Actor):
if lives == 0:
bs.timer(0.6, self.update_for_lives)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
self.node.delete()
@ -194,6 +197,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
allow_mid_activity_joins = False
@override
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]
@ -238,12 +242,14 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
)
return settings
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
sessiontype, bs.FreeForAllSession
)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -269,6 +275,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SURVIVAL
)
@override
def get_instance_description(self) -> str | Sequence:
return (
'Last team standing wins.'
@ -276,6 +283,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
else 'Last one standing wins.'
)
@override
def get_instance_description_short(self) -> str | Sequence:
return (
'last team standing wins'
@ -283,6 +291,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
else 'last one standing wins'
)
@override
def on_player_join(self, player: Player) -> None:
player.lives = self._lives_per_player
@ -299,6 +308,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
if self.has_begun():
self._update_icons()
@override
def on_begin(self) -> None:
super().on_begin()
self._start_time = bs.time()
@ -473,6 +483,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
return points[-1][1]
return None
@override
def spawn_player(self, player: Player) -> bs.Actor:
actor = self.spawn_player_spaz(player, self._get_spawn_point(player))
if not self._solo_mode:
@ -499,6 +510,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
position=player.node.position,
).autoretain()
@override
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
player.icons = []
@ -522,6 +534,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
def _get_total_team_lives(self, team: Team) -> int:
return sum(player.lives for player in team.players)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -592,6 +605,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
and any(player.lives > 0 for player in team.players)
]
@override
def end_game(self) -> None:
if self.has_ended():
return

View file

@ -1,5 +1,6 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Implements football games (both co-op and teams varieties)."""
# ba_meta require api 8
@ -12,6 +13,9 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import TNTSpawner
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
@ -39,7 +43,6 @@ from bascenev1lib.actor.spazbot import (
StickyBot,
ExplodeyBot,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -128,11 +131,13 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
# We only support two-team play.
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -170,6 +175,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FOOTBALL
)
@override
def get_instance_description(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
@ -181,6 +187,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
return 'Score ${ARG1} touchdowns.', touchdowns
return 'Score a touchdown.'
@override
def get_instance_description_short(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
@ -188,6 +195,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
return 'score ${ARG1} touchdowns', touchdowns
return 'score a touchdown'
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -224,6 +232,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
self._update_scoreboard()
self._chant_sound.play()
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@ -285,6 +294,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
bs.cameraflash(duration=10.0)
self._update_scoreboard()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -298,6 +308,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
team, team.score, self._score_to_win
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, FlagPickedUpMessage):
assert isinstance(msg.flag, FootballFlag)
@ -379,9 +390,11 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
default_music = bs.MusicType.FOOTBALL
# FIXME: Need to update co-op games to use getscoreconfig.
@override
def get_score_type(self) -> str:
return 'time'
@override
def get_instance_description(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
@ -389,6 +402,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
return 'Score ${ARG1} touchdowns.', touchdowns
return 'Score a touchdown.'
@override
def get_instance_description_short(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
@ -444,6 +458,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
self._flag_respawn_light: bs.Actor | None = None
self._flag: FootballFlag | None = None
@override
def on_transition_in(self) -> None:
super().on_transition_in()
self._scoreboard = Scoreboard()
@ -480,6 +495,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
)
self._chant_sound.play()
@override
def on_begin(self) -> None:
# FIXME: Split this up a bit.
# pylint: disable=too-many-statements
@ -795,11 +811,13 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
if i == 0:
bs.cameraflash(duration=10.0)
@override
def end_game(self) -> None:
bs.setmusic(None)
self._bots.final_celebrate()
bs.timer(0.001, bs.Call(self.do_end, 'defeat'))
@override
def on_continue(self) -> None:
# Subtract one touchdown from the bots and get them moving again.
assert self._bot_team is not None
@ -897,6 +915,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
},
)
@override
def handlemessage(self, msg: Any) -> Any:
"""handle high-level game messages"""
if isinstance(msg, bs.PlayerDiedMessage):
@ -959,6 +978,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
del player # Unused.
self._player_has_punched = True
@override
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(
player, position=self.map.get_start_position(player.team.id)

View file

@ -9,11 +9,13 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -58,6 +60,7 @@ class Puck(bs.Actor):
)
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 1.3, 0.26: 1})
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:
@ -152,10 +155,12 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -231,16 +236,19 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.HOCKEY
)
@override
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:
return 'Score a goal.'
return 'Score ${ARG1} goals.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
if self._score_to_win == 1:
return 'score a goal'
return 'score ${ARG1} goals', self._score_to_win
@override
def on_begin(self) -> None:
super().on_begin()
@ -281,6 +289,7 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
self._update_scoreboard()
self._chant_sound.play()
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@ -364,6 +373,7 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
bs.cameraflash(duration=10.0)
self._update_scoreboard()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -375,6 +385,7 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
for team in self.teams:
self._scoreboard.set_team_value(team, team.score, winscore)
@override
def handlemessage(self, msg: Any) -> Any:
# Respawn dead players if they're still in the game.
if isinstance(msg, bs.PlayerDiedMessage):

Some files were not shown because too many files have changed in this diff Show more