Merge pull request #63 from vishal332008/public-server

Updating mods to 1.7.9
This commit is contained in:
Ayush Saini 2022-09-25 18:59:14 +05:30 committed by GitHub
commit 1e503b9c50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
209 changed files with 17250 additions and 2945 deletions

View file

@ -1,13 +1,13 @@
# Bombsquad-Ballistica-Modded-Server
Modder server scripts to host ballistica (Bombsquad).Running on BS1.6.6.
Modder server scripts to host ballistica (Bombsquad).Running on BS1.7.2.
## Requirements
- Ubuntu 20
- python3.9
- python3.10
## Getting Started
- `sudo apt update; sudo apt install python3.9-dev`
- `sudo apt update; sudo apt install python3-pip python3.10-dev python3.10-venv` , or [Python3.10 on Ubuntu 20.04](https://computingforgeeks.com/how-to-install-python-on-ubuntu-linux-system/)
- `tmux new -s 43210`
- `git clone https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server`
- `cd Bombsquad-Ballistica-Modded-Server`
@ -39,9 +39,22 @@ Here you can ban player , mute them , disable their kick votes
- Restrict some player to start kick vote.
- Auto night mode .
- Transparent Kickvote , can see who started kick vote for whom.
- Kickvote msg to chat/screen , can choose to show kickvote start msg either as screen message or chat message
- Kickvote msg to chat/screen , can choose to show kickvote start msg either as screen message or chat message.
- Players IP Address and Device UUID tracking and banning.
- Team Chat, send msg starting with (,) comma to deliver it to team mates only.
- In game popup chat , send msg starting with (.) Dot to send in game popup msg.
- End Vote System , type `end` in chat to start end vote.
- support for [Ballisitca-web-stats](https://github.com/imayushsaini/ballistica-web-stats).
- Integrated Discord bot to sync live stats(current players, chats , all logs) to discord.
- Execute chat command remotely from discord.
- Many New mini games and maps.
- Colourfull bomb explosion.
- Floater
- Auto stats reset after configured days .
- Auto remove afk/idle players.
- Auto check server updates.
- All settings at one place ,no coding exp. required just edit settings.json
- Configurable Server Host name
- Configurable Server Host name.
- Character chooser , players can choose any character while joining .
- Restrict New accounts to join or chat in server.
- Custom characters , easy to load and use characters made by character maker.
@ -49,5 +62,6 @@ Here you can ban player , mute them , disable their kick votes
- Integrated ElPatronPowerups.
- Auto switch to coop mode when players are less then threshold.
- Change playlist on fly with playlist code or name , i.e /playlist teams , /playlist coop , /playlist 34532
- New Splitted Team score screen.
- New Splitted Team in game score screen.
- New final score screen , StumbledScoreScreen.
- other small small feature improvement here there find yourself.

View file

@ -48,7 +48,7 @@ max_party_size: 6
# playlist editor in the regular version of the game.
# This will give you a numeric code you can enter here to host that
# playlist.
playlist_code: 347990
playlist_code: 374313
# Alternately, you can embed playlist data here instead of using codes.
# Make sure to set session_type to the correct type for the data here.

View file

@ -50,6 +50,7 @@
"5PH3X",
"99",
"@sametsunal",
"_DraXX",
"_Fami",
"Omar a",
"Bruno A.",
@ -96,6 +97,7 @@
"ahmed",
"ahmedzabara",
"Collin Ainge",
"Akash",
"Akbar",
"Bekir Akdemir",
"Akhanyile",
@ -193,6 +195,7 @@
"Arin",
"Arroz",
"ARSHAD",
"ArshiyDLn",
"Artem",
"Valentino Artizzu",
"Arxyma",
@ -236,6 +239,7 @@
"Bank",
"Ibrahim Baraka",
"Kamil Barański",
"Leonan Barcelos",
"Bardiaghasedipour",
"William Barnak",
"William Barnakk",
@ -247,6 +251,7 @@
"Bashkot",
"Matthias Bastron",
"Ralph Bathmann",
"Bato",
"Florian Bauernfeind",
"David BAUMANN",
"bayanus",
@ -272,6 +277,7 @@
"Anton Bang Berner",
"Felix Bernhard",
"Davide Bigotto",
"bilibili@Medic药",
"Bima",
"Biytremni",
"Blackcat2960",
@ -306,6 +312,7 @@
"Brojas",
"Brojasko",
"BrotheRuzz11",
"Brunoazocar",
"bsam",
"Bsamhero",
"BSODPK",
@ -314,6 +321,7 @@
"Buskebam",
"Buto11",
"ByAdrianYT",
"Semih Byzts",
"Mohamad Hossein BZ",
"Christoffer Bünner",
"mvp aka cactus",
@ -374,11 +382,13 @@
"Cpt crook",
"Prashanth CrossFire",
"Cryfter",
"cuddles98",
"cukomus",
"CYCL0YT",
"D",
"Dada",
"Daivaras",
"Dajo6596YT",
"Dakkat",
"Mikkel Damgaard",
"Danco",
@ -395,6 +405,7 @@
"Dasto",
"David",
"Davide",
"DavidPlayzLol",
"DavidPlayzloll",
"DaymanLP",
"Ddávid",
@ -408,6 +419,7 @@
"Dennys",
"Alex Derbenew",
"df",
"Santanu Dhar",
"Guilherme Dias",
"Diase7en",
"ferbie Dicen",
@ -416,6 +428,7 @@
"dikivan2000",
"Dimitriy",
"Martin Dimitrov",
"DinoWattz",
"Diprone",
"djaber djafer",
"Fadhil djibran",
@ -439,6 +452,8 @@
"Dudow",
"Dustin",
"Paul Duvernay",
"Emir İslam Dündar",
"E.R.A.L",
"Ebutahapro07tr",
"Edson",
"Glen Edwards",
@ -452,6 +467,7 @@
"Rezk ElAdawy",
"ElCatCaesar",
"ElderLink",
"elfree",
"Elian",
"Elsans320_YT",
"Elskoser",
@ -460,6 +476,7 @@
"Emil",
"Kürti Emil",
"Muhammed emir",
"Muhammet Emir",
"EmirSametEr",
"emm",
"EnderDust123",
@ -487,6 +504,7 @@
"Jakub Fafek",
"Syed Fahrin (Mr.Lemoyne)",
"faizal.faiz.ms@gmail.com",
"Fakih",
"FanDolz.",
"Faqih",
"Muhammad Faqih ''None''",
@ -681,6 +699,7 @@
"Itamar",
"Ivan",
"iViietZ",
"JaaJ",
"Al jabbar",
"Jacek",
"Jack556",
@ -699,6 +718,7 @@
"Jembhut",
"CrackerKSR (Kishor Jena)",
"CrackerKSR (Kishor Jena))",
"Jenqa",
"Jeroen",
"jesus",
"Jetty",
@ -721,6 +741,7 @@
"joke",
"Jonatas",
"Jop",
"JoseANG3L",
"Joseetion",
"Joseph",
"Joshep",
@ -759,9 +780,11 @@
"Kaushik",
"KawaiiON",
"KD",
"Kejuxs",
"Mani kelidari",
"Kelmine",
"Kenjie",
"Kerfix",
"Kerim",
"Khaild1717",
"muh khairul",
@ -790,6 +813,7 @@
"Nikolay Korolyov",
"Kostas",
"Viktor Kostohryz",
"Kozmo909",
"Mikhail Krasovsky",
"kripanshu",
"kris",
@ -842,7 +866,10 @@
"Linux44313",
"LiteBalt",
"LittleNyanCat",
"Lizz",
"Lizzetc",
"Lkham",
"Lobinhofs",
"Loex",
"Loko",
"Longkencok",
@ -860,6 +887,7 @@
"Ludicrouswizard",
"satrio ludji",
"Ludovico",
"Luis (GalaxyM4)",
"Jose Luis",
"luislinares",
"luispro25",
@ -922,6 +950,7 @@
"Ihsan Maulana ( @ihsanm27)",
"Federico Mazzone",
"Andrea Mazzucchelli",
"Medic",
"Medic别闹我有药",
"German Medin",
"Martin Medina",
@ -933,6 +962,7 @@
"Meryu07",
"Meysam",
"MGH",
"Davis Michelle",
"Mick",
"Miguel",
"Miguelterrazas123",
@ -1092,12 +1122,14 @@
"pett",
"Petulakulina",
"Pez",
"PGIGM",
"Đào Xuân Phi",
"Philip",
"Philipp",
"piga",
"Stefano Pigozzi",
"Mario Donato Pilla",
"Pinchidino",
"Danilo \"Logan\" Pirrone",
"PivotStickfigure12",
"Pixelcube",
@ -1254,6 +1286,7 @@
"ShadowQ",
"shafay",
"Manan Shah",
"shakesm",
"Sharvesh",
"Nalam Shashwath",
"Haige Shi",
@ -1271,6 +1304,7 @@
"Igor Slobodchuk",
"Rasim Smaili",
"Nicola Smaniotto",
"smertfhg",
"Nico Smit",
"Snack",
"Mahteus Soares",
@ -1307,6 +1341,7 @@
"sun.4810",
"Samet Sunal",
"sundar",
"Suprcat",
"Indo sus",
"Sven",
"Shannon Sy",
@ -1315,6 +1350,7 @@
"Sz™",
"Jorge Luis Sánchez",
"Daniel Sýkora",
"Aleksandar Tadic",
"Arung Taftazani",
"taha",
"Rasim Eren TAHMAZ",
@ -1380,6 +1416,7 @@
"Kontantin Tsvetkov",
"Tudikk",
"Jan Tymll",
"Zacker Tz",
"Zoltán Tóth",
"uDinnoo",
"Cristian Ugalde",
@ -1549,6 +1586,7 @@
"مُحمَّد الأول",
"البطل",
"بسام البطل",
"رفيق العشي",
"ابو العواصف2020",
"عبدالرحمن النجم",
"امیرعلی",
@ -1585,6 +1623,7 @@
"حسین وفایی‌فرد",
"انا يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا عمر انا بران يا",
"١٢٣٤٥",
"٦٤٦٦٤٦٤٦",
"علیرضا پودینه",
"वेदाँश त्यागी",
"അർഷഖ് ഹസ്സൻ",
@ -1618,6 +1657,7 @@
"神仙",
"药药Medic",
"蔚蓝枫叶",
"鲨鱼服·Medic",
"鲲鹏元帅",
"꧁ephyro꧂",
"공팔이",

View file

@ -56,8 +56,8 @@
"Boxer": {
"description": "فز بدون استخدامك للقنابل",
"descriptionComplete": "لقد فزت بدون استخدام القنابل",
"descriptionFull": "قم بإكمال ${LEVEL} بدون استخدام أية قنابل",
"descriptionFullComplete": "اكمل ${LEVEL} بدون استخدام أية قنابل",
"descriptionFull": "بدون استخدام اي قنابل${LEVEL} قم بإكمال",
"descriptionFullComplete": "دون استخدام اي قنابل${LEVEL} اكمل",
"name": "مُلاكِمْ"
},
"Dual Wielding": {
@ -66,10 +66,10 @@
"name": "اللكمة المزدوجة"
},
"Flawless Victory": {
"description": "انتصر بدون تعرض للأذى",
"description": "انتصر بدون التعرض للأذى",
"descriptionComplete": "لقد فزت بدون تعرضك للأذى",
"descriptionFull": "انتصر في ${LEVEL} بدون تعرضك للأذى",
"descriptionFullComplete": "لقد فزت في ${LEVEL} بدون تعرضك للأذى",
"descriptionFull": "دون تعرضك للأذى${LEVEL} فُزْ",
"descriptionFullComplete": "دون تعرضك للأذى ${LEVEL} لقد فُزتَ",
"name": "الفوز المستحق"
},
"Free Loader": {
@ -89,7 +89,7 @@
"descriptionComplete": "لقد انتصرت بدون استخدامك للكمات او القنابل",
"descriptionFull": "بدون استخدام اللكمات أو القنابل ${LEVEL} فز في",
"descriptionFullComplete": "بدون استخدام اللكمات أو القنابل ${LEVEL} لقد ربحت في",
"name": "الأسلحة المخفية"
"name": "حصلت على الحركات"
},
"In Control": {
"descriptionFull": "(قم بتوصيل جهاز تحكم (جهاز أو تطبيق",
@ -136,7 +136,7 @@
"descriptionComplete": "!لقد سجلت 5000 نقطة",
"descriptionFull": "${LEVEL} سجِّل 5000 نقطة في",
"descriptionFullComplete": "${LEVEL} لقد سجَّلتَ 5000 نقطة في",
"name": "إله ${LEVEL}"
"name": "زعيم ${LEVEL}"
},
"Onslaught Master": {
"description": "سجل 500 نقطة",
@ -160,10 +160,10 @@
"name": "${LEVEL} ساحر"
},
"Precision Bombing": {
"description": "powerups فز بدون اي",
"descriptionComplete": "powerups لقد فزتَ بدون أي",
"descriptionFull": "power-ups بدون اي ${LEVEL} فز في",
"descriptionFullComplete": "power-ups بدون اي ${LEVEL} لقد فزتَ في",
"description": "فُزْ بدون اي قوى خارقة",
"descriptionComplete": "لقد فزتَ بدون أي قوى خارقة",
"descriptionFull": "بدون اي قوى خارقة${LEVEL} فز في",
"descriptionFullComplete": "بدون اي قوى خارقة${LEVEL} لقد فزتَ في",
"name": "دقة القصف"
},
"Pro Boxer": {
@ -218,7 +218,7 @@
"Rookie Onslaught Victory": {
"description": "هزيمة كل الجولات",
"descriptionComplete": "هزم كل الجولات",
"descriptionFull": "${LEVEL} اكسب كل الجولات في",
"descriptionFull": "${LEVEL} اهزم كل الجولات في",
"descriptionFullComplete": "${LEVEL} هزم كل الجولات في",
"name": "${LEVEL} لقد انتصرت"
},
@ -250,9 +250,9 @@
},
"Stayin' Alive": {
"description": "فز بدون أن تموت",
"descriptionComplete": "فاز بدون أن يموت",
"descriptionComplete": "لقد فُزت بدون ان تموت",
"descriptionFull": "بدون أن تموت ${LEVEL} فز",
"descriptionFullComplete": "بدون أن يموت ${LEVEL} فاز",
"descriptionFullComplete": "بدون أن يموت ${LEVEL} لقد فزت",
"name": "البقاء حيا"
},
"Super Mega Punch": {
@ -265,8 +265,8 @@
"Super Punch": {
"description": "إلحاق الضرر 50٪ بلكمة واحدة",
"descriptionComplete": "ألحق الضرر 50٪ بلكمة واحدة",
"descriptionFull": "${LEVEL} إلحاق الضرر 50٪ بلكمة واحدة",
"descriptionFullComplete": "${LEVEL} ألحق الضرر 50٪ بلكمة واحدة",
"descriptionFull": "${LEVEL} إلحاق الضرر 50٪ بلكمة واحدة في",
"descriptionFullComplete": "${LEVEL} ألحق الضرر 50٪ بلكمة واحدة في",
"name": "لكمة خارقة"
},
"TNT Terror": {
@ -327,6 +327,7 @@
"achievementsRemainingText": "الإنجازات المتبقية",
"achievementsText": "الإنجازات",
"achievementsUnavailableForOldSeasonsText": "المعذرة، الإنجازات للمواسم القديمة غير متوفرة",
"activatedText": "${THING} تم تفعيله.",
"addGameWindow": {
"getMoreGamesText": "الحصول على المزيد من الألعاب",
"titleText": "إضافة لعبة"
@ -625,7 +626,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} بحركة ملحمية بطيئة",
"epicNameFilterText": "الملحمي ${NAME}",
"errorAccessDeniedText": "تم الرفض",
"errorDeviceTimeIncorrectText": "توقف وقت جهازك بمقدار ${HOURS} ساعة.\nهذا قد يتسبب بمشاكل.\nمن فضلك قم بالتحقق من اعدادات الوقت.",
"errorOutOfDiskSpaceText": "انتهت مساحة التخزين",
"errorSecureConnectionFailText": "تعذر انشاء اتصال سحابي أمن; قد تفشل وظائف الشبكة.",
"errorText": "خطا",
"errorUnknownText": "خطا غير معروف",
"exitGameText": "هل تريد الخروج من ${APP_NAME}؟",
@ -788,7 +791,7 @@
"ticketPack4Text": "جمبو تذكرة حزمة",
"ticketPack5Text": "ماموث تذكرة حزمة",
"ticketPack6Text": "تذكرة حزمة هائلة",
"ticketsFromASponsorText": "احصل على تذاكر ${COUNT}\nمن مقدم مشروع القرار",
"ticketsFromASponsorText": "شاهد اعلانا\nمن التذاكر ${COUNT} للحصول على",
"ticketsText": "${COUNT} بطاقات",
"titleText": "أحصل على تذاكر",
"unavailableLinkAccountText": "عذرا، لا تتوفر عمليات الشراء على هذا النظام الأساسي.\nوكحل بديل، يمكنك ربط هذا الحساب بحساب في\nمنصة أخرى وجعل عمليات الشراء هناك.",
@ -799,6 +802,7 @@
"youHaveText": "لديك ${COUNT} تذاكر"
},
"googleMultiplayerDiscontinuedText": "عذرًا ، خدمة جوجل متعددة اللاعبين لم تعد متاحة.\n أنا أعمل على بديل بأسرع وقت ممكن.\n حتى ذلك الحين ، يرجى تجربة طريقة اتصال أخرى.\n -إريك",
"googlePlayPurchasesNotAvailableText": "عمليات شراء جوجل بلاي غير متوفرة.\nقد تحتاج لتحديث تطبيق المتجر.",
"googlePlayText": "جوجل بلاي",
"graphicsSettingsWindow": {
"alwaysText": "دائما",
@ -1109,7 +1113,10 @@
"playlistsText": "قائمة العب",
"pleaseRateText": "يرجى اتخاذ لحظة وتقييمه ${APP_NAME} اذا كنت تستمتع بلعبة\nاو كتابة مراجعة فهاذا يوفر معلومات مفيدة ويوفر التطوير\nفي المستقبل\n\n!شكرًا\nاريك—",
"pleaseWaitText": "الرجاء الانتظار . . .",
"pluginsDetectedText": "تم العتور على اضافات جديدة. فعلها او عدلها في الاعدادات",
"pluginClassLoadErrorText": "فشل في تشغيل الاضافة '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "خطأ في بدء الاضافة '${PLUGIN}: ${ERROR}",
"pluginsDetectedText": "تم العثور على اضافات جديدة. اعد التشغيل لتفعيلها، او قم بتخصيصها في الاعدادات.",
"pluginsRemovedText": "${NUM} لم تعد الاضافة موجودة.",
"pluginsText": "اضافات",
"practiceText": "تدريب",
"pressAnyButtonPlayAgainText": "اضغط اي زر للعب مجددا...",
@ -1296,8 +1303,8 @@
"comingSoonText": "قريبا...",
"extrasText": "إضافات",
"freeBombSquadProText": "فرقة القنبلة الآن أصبحت مجانية، لكن بما أنك اشتريتها\nبطاقات كشكر لك ${COUNT} ستتلقى فرقة القنبلة القنبلة للمحترفين و\n!استمتع بالميزات الجديدة، وشكرًا لدعمك\n-إيريك",
"holidaySpecialText": "عطلة خاصة",
"howToSwitchCharactersText": "(انتقل إلى \"${SETTINGS} -> ${PLAYER_PROFILES}\" لتعيين وتخصيص الأحرف)",
"holidaySpecialText": "خاص بالعطل",
"howToSwitchCharactersText": "(توجه الى \"${SETTINGS} -> ${PLAYER_PROFILES}\" لتخصيص الشخصيات)",
"howToUseIconsText": "(إنشاء ملفات تعريف لاعب العالمية (في إطار الحساب) لاستخدام هذه)",
"howToUseMapsText": "(استخدم هذه الخرائط في فرقك الخاصة / قوائم التشغيل المجانية للجميع)",
"iconsText": "رموز",
@ -1321,7 +1328,7 @@
"winterSpecialText": "عرض الشتاء",
"youOwnThisText": "- انت تملك هذا -"
},
"storeDescriptionText": "8 لاعب حفله لعبة الجنون!\n\nتفجير أصدقائك (أو الكمبيوتر) في البطولة من الألعاب المصغرة المتفجرة مثل القبض على العلم، منفذها هوكي، وملحمة بطيئة الحركة الموت الموت!\n\nضوابط بسيطة ودعم وحدة تحكم واسعة تجعل من السهل لمدة تصل إلى 8 أشخاص للحصول على في العمل. يمكنك حتى استخدام الأجهزة النقالة الخاصة بك عن طريق التحكم عن طريق الحرة 'بومبسكاد البعيد' التطبيق!\n\nالقنابل بعيدا!\n\nتحقق من www.froemling.net/bombsquad لمزيد من المعلومات.",
"storeDescriptionText": "لعبة لأكثر من 8 لاعبين!\n\nالعب مع اصدقائك (او الحاسوب) في بطولات من الميني جيمز المتفجرة كــإمساك بالعلم، الهوكي و المعركة البطيئة!\n\nتحكم بسيط و لعب باجهزة تحكم يجعلها سهلة لأكثر من 8 لاعبين ليدخلو المعركة; تستطيع ايضا استعمال اجهزة الهاتف كاجهزة تحكم من خلال التطبيق المجاني 'BombSquad Remote' !\n\nوقت رمي القنابل!\n\nتفقد www.froemling.net/bombsquad للمزيد من المعلومات.",
"storeDescriptions": {
"blowUpYourFriendsText": ".فجر أصدقائك",
"competeInMiniGamesText": "تنافس في الألعاب المصغرة بدءا من السباق للطيران.",
@ -1364,31 +1371,31 @@
"translations": {
"characterNames": {
"Agent Johnson": "العميل جونسون",
"B-9000": "الآلي الخارق",
"Bernard": "الدب القطبي",
"B-9000": "B-9000",
"Bernard": "بيرنارد",
"Bones": "هيكل عظمي",
"Butch": "بوتش",
"Easter Bunny": "أرنوب",
"Easter Bunny": "أرنب عيد الفصح",
"Flopsy": "فلوبسي",
"Frosty": "مُقاتل ثلجي",
"Frosty": "فروستي",
"Gretel": "جريتل",
"Grumbledorf": "المشعوذ",
"Jack Morgan": "خير الدين بارباروسا",
"Kronk": "عدنان",
"Grumbledorf": "الساحر",
"Jack Morgan": "جاك مورجان (قرصان)",
"Kronk": "كرونك",
"Lee": "لي",
"Lucky": "سعيد الحظ",
"Mel": "الطباخ",
"Mel": "ميل",
"Middle-Man": "الرجل المتوسط",
"Minimus": "أدنى لا",
"Pascal": "البطريق الكبير",
"Pixel": "حسناء",
"Pascal": "پاسكال",
"Pixel": "بيكسل",
"Sammy Slam": "سامي سلام",
"Santa Claus": "الشيخ",
"Snake Shadow": "مُحارب في الصحراء",
"Spaz": "Spaz",
"Taobao Mascot": "التميمه تاوباو",
"Santa Claus": "سانتا كلوس",
"Snake Shadow": "نينجا",
"Spaz": "سپاز",
"Taobao Mascot": "ماسكوت تاوباو",
"Todd McBurton": "تود بيرتون",
"Zoe": "ليلى",
"Zoe": "زوي",
"Zola": "زولا"
},
"coopLevelNames": {
@ -1843,6 +1850,8 @@
"winsPlayerText": "${NAME} !يفوز",
"winsTeamText": "${NAME} !يفوز فريق",
"winsText": "${NAME} !يفوز",
"workspaceSyncErrorText": "فشل في مزامنة ${WORKSPACE}. القي نظرة على السجل للتفاصيل.",
"workspaceSyncReuseText": "لا يمكن مزامنة ${WORKSPACE}. اعادة استخدام النسخة المتزامنة السابقة.",
"worldScoresUnavailableText": "(النتيجة العالمية غير متوفرة (اتصل بالانترنت",
"worldsBestScoresText": "افضل نتيجة للعالم",
"worldsBestTimesText": "افضل اوقات العالم",

View file

@ -330,6 +330,7 @@
"achievementsRemainingText": "未完成成就:",
"achievementsText": "成就",
"achievementsUnavailableForOldSeasonsText": "抱歉,往届的成就细节不可用。",
"activatedText": "${THING} 已激活",
"addGameWindow": {
"getMoreGamesText": "获取更多游戏模式…",
"titleText": "添加比赛"
@ -629,7 +630,9 @@
"epicDescriptionFilterText": "史诗级慢动作 ${DESCRIPTION}。",
"epicNameFilterText": "史诗级${NAME}",
"errorAccessDeniedText": "访问被拒绝",
"errorDeviceTimeIncorrectText": "您的系统时间与服务器时间相差了${HOURS}小时\n这可能会出现一些问题\n请检查您的系统时间或时区",
"errorOutOfDiskSpaceText": "磁盘空间不足",
"errorSecureConnectionFailText": "无法建立安全的云链接,网络可能会连接失败",
"errorText": "错误",
"errorUnknownText": "未知错误",
"exitGameText": "退出${APP_NAME}",
@ -795,7 +798,7 @@
"ticketPack4Text": "巨型点券包",
"ticketPack5Text": "猛犸象点券包",
"ticketPack6Text": "终极点券包",
"ticketsFromASponsorText": "从赞助商处\n获得${COUNT}点券",
"ticketsFromASponsorText": "观看广告\n白嫖${COUNT}个点券",
"ticketsText": "${COUNT}点券",
"titleText": "获得点券",
"unavailableLinkAccountText": "对不起,该平台上不可进行购买。\n您可将此帐户链接到另一个\n平台以进行购买。",
@ -806,6 +809,7 @@
"youHaveText": "你拥有${COUNT}点券"
},
"googleMultiplayerDiscontinuedText": "抱歉Google的多人游戏服务已不可用。\n我将尽快更换新的替代服务。\n在此之前请尝试其他连接方法。\n-Eric",
"googlePlayPurchasesNotAvailableText": "Google商店购买不可用!\n请安装谷歌框架或更新Google服务。",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "总是",
@ -1122,7 +1126,10 @@
"playlistsText": "列表",
"pleaseRateText": "如果你喜欢 ${APP_NAME},请考虑花一点时间\n来评价一下它或为它写一篇评论。这将为我们提供\n有用的反馈建议为游戏的未来开发给予支持。\n\n感谢您\n-eric",
"pleaseWaitText": "请稍等...",
"pluginsDetectedText": "新的插件已经加载.请在设置中启用它们.",
"pluginClassLoadErrorText": "加载'${PLUGIN}'插件时出错了耶: ${ERROR}",
"pluginInitErrorText": "初始化'${PLUGIN}'插件失败了啦: ${ERROR}",
"pluginsDetectedText": "新插件安装成功,请重启游戏或在设置中设置它们~",
"pluginsRemovedText": "有${NUM}个插件被删除了...",
"pluginsText": "插件",
"practiceText": "练习",
"pressAnyButtonPlayAgainText": "按任意按钮再玩一次...",
@ -1863,6 +1870,8 @@
"winsPlayerText": "${NAME}获胜!",
"winsTeamText": "${NAME}获胜!",
"winsText": "${NAME}获胜!",
"workspaceSyncErrorText": "同步${WORKSPACE}出错了啦,详情见日志文件",
"workspaceSyncReuseText": "同步${WORKSPACE}时出错,正在使用以前同步的数据....",
"worldScoresUnavailableText": "全球得分不可用。",
"worldsBestScoresText": "全球最高得分",
"worldsBestTimesText": "全球最佳时间",

View file

@ -324,6 +324,7 @@
"achievementsRemainingText": "未完成的成就",
"achievementsText": "成就",
"achievementsUnavailableForOldSeasonsText": "抱歉,往屆成就細節不可用",
"activatedText": "${THING}已激活",
"addGameWindow": {
"getMoreGamesText": "獲取更多比賽模式",
"titleText": "新增比賽"
@ -621,7 +622,9 @@
"epicDescriptionFilterText": "史詩級慢動作${DESCRIPTION}",
"epicNameFilterText": "史詩級${NAME}",
"errorAccessDeniedText": "訪問被拒絕",
"errorDeviceTimeIncorrectText": "您的系統時間與BS伺服器相差了${HOURS}小時\n這可能會出現一些問題\n請檢查你的系統時間或時區",
"errorOutOfDiskSpaceText": "磁盤空間不足",
"errorSecureConnectionFailText": "無法安全的連接到伺服器,網絡功能可能會失效",
"errorText": "錯誤",
"errorUnknownText": "未知錯誤",
"exitGameText": "退出${APP_NAME}?",
@ -1105,7 +1108,10 @@
"playlistsText": "遊戲列表",
"pleaseRateText": "如果你喜歡${APP_NAME},請考慮花一些時間\n為Bombsquad寫一篇評論。這將為我們\n提供一些有用的反饋建議為遊戲的未來開發給予支持\n\nThanks!\n-Eric",
"pleaseWaitText": "請稍等....",
"pluginsDetectedText": "檢測到新插件。 在設置中啟用/配置它們。",
"pluginClassLoadErrorText": "加載插件類'${PLUGIN}'時出錯:${ERROR}",
"pluginInitErrorText": "初始化插件'${PLUGIN}'時出錯:${ERROR}",
"pluginsDetectedText": "檢測到新插件。重新啓動以激活,或在設置中配置這些插件",
"pluginsRemovedText": "${NUM}個插件已丟失",
"pluginsText": "外掛程式",
"practiceText": "練習",
"pressAnyButtonPlayAgainText": "按任意鍵再玩一次...",
@ -1839,6 +1845,8 @@
"winsPlayerText": "${NAME}贏了!",
"winsTeamText": "${NAME}贏了!",
"winsText": "${NAME}贏了!",
"workspaceSyncErrorText": "同步${WORKSPACE}時出錯,詳情日志已輸出",
"workspaceSyncReuseText": "無法同步${WORKSPACE},重複使用以前的同步版本",
"worldScoresUnavailableText": "無法讀入全球分數",
"worldsBestScoresText": "全球最佳分數",
"worldsBestTimesText": "全球最佳時間",

View file

@ -329,6 +329,7 @@
"achievementsRemainingText": "Preostala Postignuća:",
"achievementsText": "Postignuća",
"achievementsUnavailableForOldSeasonsText": "Žao nam je,postignuća pojedinosti nisu dostupni za stare sezone.",
"activatedText": "${THING} uključeno.",
"addGameWindow": {
"getMoreGamesText": "Još igara...",
"titleText": "Dodaj igru"
@ -720,14 +721,15 @@
"internetText": "Internet",
"inviteAFriendText": "Prijatelji nemaju igru? Pozovi ih da je\nprobaju i oni će dobiti ${COUNT} besplatnih ulaznica.",
"inviteFriendsText": "Pozovi Prijatelje",
"joinPublicPartyDescriptionText": "Pridruži se javnoj partiji:",
"localNetworkDescriptionText": "Uključi se u partiju na tvojoj mreži:",
"joinPublicPartyDescriptionText": "Pridruži se javnoj partiji",
"localNetworkDescriptionText": "Uđi u blisku partiju (Lan, Bluetooth, itd.)",
"localNetworkText": "Lokalna mreža",
"makePartyPrivateText": "Postavi moju partiju privatnom",
"makePartyPublicText": "Postavi moju partiju javnom",
"manualAddressText": "Adresa",
"manualConnectText": "Spoji se",
"manualDescriptionText": "Uljuči se u partiju po adresi:",
"manualJoinSectionText": "Uđi s Adresom",
"manualJoinableFromInternetText": "Mogu li se drugi priključiti tebi s Interneta?:",
"manualJoinableNoWithAsteriskText": "NE*",
"manualJoinableYesText": "DA",
@ -735,14 +737,17 @@
"manualText": "Ručno",
"manualYourAddressFromInternetText": "Tvoja adresa s Interneta:",
"manualYourLocalAddressText": "Tvoja lokalna adresa:",
"nearbyText": "Blizu",
"noConnectionText": "<nema veze>",
"otherVersionsText": "(druge verzije)",
"partyCodeText": "Grupna šifra",
"partyInviteAcceptText": "Prihvati",
"partyInviteDeclineText": "Odbij",
"partyInviteGooglePlayExtraText": "(pogledaj 'Google Play' karticu u prozoru 'Sakupi')",
"partyInviteIgnoreText": "Odbaci",
"partyInviteText": "${NAME} te pozvao\nda se priključiš njegovoj partiji!",
"partyNameText": "Naziv igre",
"partyServerRunningText": "Tvoj grupni server radi.",
"partySizeText": "Veličina žurke",
"partyStatusCheckingText": "provjeravam status...",
"partyStatusJoinableText": "tvoja igra je spremna za povezivanje s interneta",
@ -751,11 +756,21 @@
"partyStatusNotPublicText": "tvoja igra nije javna",
"pingText": "ping",
"portText": "Port",
"privatePartyCloudDescriptionText": "Privatne grupe radu na posvećenim cloud serverima; Nikakvih ruter konfiguracija potrebno",
"privatePartyHostText": "Napravi privatnu grupu",
"privatePartyJoinText": "Uđi u privatnu grupu",
"privateText": "Privatno",
"publicHostRouterConfigText": "Za ovo možda bude trebalo konfiguriranje \"port-forwarding\"-a na vašem ruteru. Za lakšu opciju napravite privatnu grupu",
"publicText": "Javno",
"requestingAPromoCodeText": "Dohvaćam kod...",
"sendDirectInvitesText": "Pošalji izravni poziv",
"sendThisToAFriendText": "Posalji ovaj kod prijatelju:",
"shareThisCodeWithFriendsText": "Podijeli ovaj kod s prijateljima:",
"showMyAddressText": "Prikaži moju IP adresu",
"startHostingPaidText": "Napravi sada za ${COST}",
"startHostingText": "Napravi",
"startStopHostingMinutesText": "Možeš početi i zaustaviti server besplatno za slijedećih ${MINUTES} minuta.",
"stopHostingText": "Prestani server",
"titleText": "Okupljanje",
"wifiDirectDescriptionBottomText": "Ako svi uređaji imaju 'Wi-Fi Direct' opciju, trebali bi ga moći iskoristiti da pronađu jedan\ndrugoga i povežu se. Kad su svi uređaji povezani, možeš kreiratu igru\nkoristeći karticu 'Lokalna mreža', isto kao i kod obične Wi-Fi mreže. \n\nZa najbolje rezultate, domaćin Wi-Fi Directa također bi trebao biti domaćin ${APP_NAME} igre.",
"wifiDirectDescriptionTopText": "Wi-Fi Direct možete koristiti da povežete Android uređaje direktno bez \npotrebe za Wi-Fi mrežom. Ovo najbolje radi na Android verziji 4.2 ili novijoj. \n\nZa korištenje, otvori Wi-Fi postavke i potraži 'Wi-Fi Direct' u izborniku.",
@ -813,6 +828,7 @@
"bombInfoText": "- Bomba -\nJača od udaraca, ali može rezultirati \nsamoubojstvom. Za najbolje \nrezultate, baci je prema neprijatelju \nprije nego što fitilj potpuno izgori.",
"canHelpText": "${APP_NAME} može pomoći.",
"controllersInfoText": "Možeš igrati ${APP_NAME} s prijeteljima preko mreže, ili svi\nmožete igrati na istom uređaju ako imate dovoljno kontrolera. \n${APP_NAME} podržava razne kontrolere; čak možete koristiti telefone\nkao kontrolere pomoću besplatne '${REMOTE_APP_NAME}' aplikacije. \nZa više informacija pogledaj pod Postavke->Kontroleri.",
"controllersInfoTextRemoteOnly": "Možete igrati ${APP_NAME} sa prijateljima \nPreko wifi-a ili\npreko istog uređaju koristeći mobitele kao kontrolere pomoću besplatnom aplikacijom '${REMOTE_APP_NAME}'",
"controllersText": "Kontroleri",
"controlsSubtitleText": "Tvoj prijateljski ${APP_NAME} lik ima par osnovnih kretnji:",
"controlsText": "Kontrole",
@ -1051,6 +1067,7 @@
"offText": "Isključeno",
"okText": "U redu",
"onText": "Uključeno",
"oneMomentText": "Jedan trenutak...",
"onslaughtRespawnText": "${PLAYER} će se ponovno pojaviti u naletu ${WAVE}",
"orText": "${A} ili ${B}",
"otherText": "Drugo...",
@ -1097,10 +1114,14 @@
"playerText": "Igrač",
"playlistNoValidGamesErrorText": "Ova lista igara ne sadrži nijednu valjanu otključanu igru.",
"playlistNotFoundText": "lista igara nije pronađena",
"playlistText": "Playlista",
"playlistsText": "Liste igara",
"pleaseRateText": "Ako ti se sviđa ${APP_NAME}, molim te razmisli o tome\nda ga ocijeniš ili napišeš recenziju.\nOvako šalješ korisnu povratnu informaciju koja pomaže u daljnjem razvoju.\n\nHvala!\n-eric",
"pleaseWaitText": "Molimo sačekajte...",
"pluginsDetectedText": "Novi dodatak(ci) detektiraju. Osposobi/konfiguriraj ih u postavkama.",
"pluginClassLoadErrorText": "Pogreška učitavanja dodatka klase '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Pogreška iniciranja dodatka '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Novi dodatak(ci) detektiran(i) Resetiraj da ih ukljućiš ili ih konfiguriraj u postavkima",
"pluginsRemovedText": "${NUM} dodatak/ci više nisu pronađeni.",
"pluginsText": "Dodatci",
"practiceText": "Vježba",
"pressAnyButtonPlayAgainText": "Pritisni bilo koju tipku za ponovnu igru...",
@ -1496,6 +1517,7 @@
"Dutch": "Nizozemski",
"English": "Engleski",
"Esperanto": "Esperanto",
"Filipino": "Filipinski",
"Finnish": "Finski",
"French": "Francuski",
"German": "Njemački",
@ -1516,6 +1538,8 @@
"Slovak": "Slovački",
"Spanish": "Španjolski",
"Swedish": "Švedski",
"Tamil": "Tamil",
"Thai": "tajlandski",
"Turkish": "Turski",
"Ukrainian": "Ukrajinski",
"Venetian": "Venecijanski",
@ -1564,6 +1588,7 @@
"Account linking successful!": "Povezivanje računa uspješno!",
"Account unlinking successful!": "Odspajanje računa uspješno!",
"Accounts are already linked.": "Računi su već povezani.",
"Ad view could not be verified.\nPlease be sure you are running an official and up-to-date version of the game.": "Prikaz oglasa se nije mogao provjeriti.\nMolim vas budite sigurni da koristite službenu i suvremenu verziju ove igre",
"An error has occurred; (${ERROR})": "Greška se pojavila; (${ERROR})",
"An error has occurred; please contact support. (${ERROR})": "Greška se pojavila; molimo vas pozovite pomoć. (${ERROR})",
"An error has occurred; please contact support@froemling.net.": "Greška se pojavila; molim vas pozovite support@froemling.net.",
@ -1589,6 +1614,7 @@
"Max number of profiles reached.": "Max broj dosegao profila.",
"Maximum friend code rewards reached.": "Maksimalne nagrade od prijateljskih kodova dostignute.",
"Message is too long.": "Poruka je prevelika.",
"No servers are available. Please try again soon.": "Nema dostupnih servera. Pokušajte opet kasnije.",
"Profile \"${NAME}\" upgraded successfully.": "Profil \"${NAME}\" uspjesno upgrajdovan",
"Profile could not be upgraded.": "Profil se ne može ažurirati.",
"Purchase successful!": "Kupovina uspješna!",
@ -1598,10 +1624,12 @@
"Sorry, this code has already been used.": "Izvini, ovaj kod je već iskorišćen.",
"Sorry, this code has expired.": "Izvini, ovaj kod je istekao.",
"Sorry, this code only works for new accounts.": "Izvini, ovaj kod radi samo za nove igrače.",
"Still searching for nearby servers; please try again soon.": "Još pretražuješ za bliske servere; pokušajte opet kasnije",
"Temporarily unavailable; please try again later.": "Privremeno nedostupno; molimo vas pokušajte kasnije.",
"The tournament ended before you finished.": "Turnir je šio prije nego što si ti završio.",
"This account cannot be unlinked for ${NUM} days.": "Ovaj se račun ne može odvezati ${NUM} dana.",
"This code cannot be used on the account that created it.": "Ovaj kod se nemoze koristiti na akauntu koji je stvorio.",
"This is currently unavailable; please try again later.": "Ovo je trenutačno nedostupno; pokušajte opet kasnije.",
"This requires version ${VERSION} or newer.": "Ovo zahtijeva verziju ${VERSION} ili noviju.",
"Tournaments disabled due to rooted device.": "Turniri nedostupni zbog rootanog uređaja.",
"Tournaments require ${VERSION} or newer": "Turniri koriste ${VERSION} ili noviju",
@ -1834,6 +1862,8 @@
"winsPlayerText": "${NAME} pobjeđuje!",
"winsTeamText": "${NAME} pobjeđuju!",
"winsText": "${NAME} pobjeđuje!",
"workspaceSyncErrorText": "Pogreška sinkroniziranja ${WORKSPACE}. Pogledaj log za više detalja.",
"workspaceSyncReuseText": "Nemoguće sinkronizirati ${WORKSPACE}. Ponovno korištenje stare verzije",
"worldScoresUnavailableText": "Svjetski rezultati nedostupni.",
"worldsBestScoresText": "Najbolji svjetski rezultati",
"worldsBestTimesText": "Najbolja svjetska prolazna vremena",

View file

@ -331,6 +331,7 @@
"achievementsRemainingText": "Achievementů Zbývá:",
"achievementsText": "Achievementy",
"achievementsUnavailableForOldSeasonsText": "Promiňte, ale detaily úspěchu nejsou pro starší sezóny zpřístupněny.",
"activatedText": "${THING} aktivováno.",
"addGameWindow": {
"getMoreGamesText": "Získat Více Her...",
"titleText": "Přidat Hru"
@ -633,7 +634,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} V epickém slow motionu",
"epicNameFilterText": "Epické ${NAME}",
"errorAccessDeniedText": "přístup zamítnut",
"errorDeviceTimeIncorrectText": "Čas vašeho zařízení je mimo o ${HOURS} h.\nTo pravděpodobně způsobí problémy.\nProsím zkontrolujte nastavení času a časového pásma.",
"errorOutOfDiskSpaceText": "není místo na disku",
"errorSecureConnectionFailText": "Unable to establish secure cloud connection; network functionality may fail.",
"errorText": "Chyba",
"errorUnknownText": "neznámá chyba",
"exitGameText": "Ukončit ${APP_NAME} ???",
@ -1123,7 +1126,10 @@
"playlistsText": "Playlisty",
"pleaseRateText": "Jestliže Vás ${APP_NAME} baví, zvažte prosím, jestli si nechcete udělat\nchvilku na ohodnocení nebo napsání recenze. Poskytuje to užitečnou\nzpětnou vazbu a pomáhá podporovat budoucí vývoj.\n\nDíky!\n-eric",
"pleaseWaitText": "Prosím čekejte...",
"pluginsDetectedText": "Nové plugin(y) nalezeny. Zapněte/konfigurujte je v nastavení.",
"pluginClassLoadErrorText": "Chyba při načítání třídy pluginu '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Chyba při inicializaci pluginu '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Nové plugin(y) nalezeny. Pro aktivaci restartujte, nebo konfigurujte v nastavení.",
"pluginsRemovedText": "${NUM} plugin(y) nenalezeny.",
"pluginsText": "Pluginy",
"practiceText": "Cvičení",
"pressAnyButtonPlayAgainText": "Stiskněte libovolné tlačítko pro opakování hry...",
@ -1864,6 +1870,8 @@
"winsPlayerText": "${NAME} Vyhrál!",
"winsTeamText": "${NAME} Vyhrál!",
"winsText": "${NAME} Vyhrál!",
"workspaceSyncErrorText": "Chyba synchronizace ${WORKSPACE}. Podrobnosti v logu.",
"workspaceSyncReuseText": "Nelze synchronizovat ${WORKSPACE}. Opětovné použití předchozí synchronizované verze.",
"worldScoresUnavailableText": "Světové skóre nepřístupné.",
"worldsBestScoresText": "Světové nejlepší skóre",
"worldsBestTimesText": "Světové nejlepší časy",

View file

@ -324,6 +324,7 @@
"achievementsRemainingText": "Achievements Remaining:",
"achievementsText": "Achievements",
"achievementsUnavailableForOldSeasonsText": "Sorry, achievement specifics are not available for old seasons.",
"activatedText": "${THING} activated.",
"addGameWindow": {
"getMoreGamesText": "Get More Games...",
"titleText": "Add Game"
@ -623,7 +624,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} In epic slow motion.",
"epicNameFilterText": "Epic ${NAME}",
"errorAccessDeniedText": "access denied",
"errorDeviceTimeIncorrectText": "Your device's time is off by ${HOURS} hours.\nThis is likely to cause problems.\nPlease check your time and time-zone settings.",
"errorOutOfDiskSpaceText": "out of disk space",
"errorSecureConnectionFailText": "Unable to establish secure cloud connection; network functionality may fail.",
"errorText": "Error",
"errorUnknownText": "unknown error",
"exitGameText": "Exit ${APP_NAME}?",
@ -786,7 +789,7 @@
"ticketPack4Text": "Jumbo Ticket Pack",
"ticketPack5Text": "Mammoth Ticket Pack",
"ticketPack6Text": "Ultimate Ticket Pack",
"ticketsFromASponsorText": "Get ${COUNT} tickets\nfrom a sponsor",
"ticketsFromASponsorText": "Watch an ad\nfor ${COUNT} tickets",
"ticketsText": "${COUNT} Tickets",
"titleText": "Get Tickets",
"unavailableLinkAccountText": "Sorry, purchases are not available on this platform.\nAs a workaround, you can link this account to an account on\nanother platform and make purchases there.",
@ -797,6 +800,7 @@
"youHaveText": "you have ${COUNT} tickets"
},
"googleMultiplayerDiscontinuedText": "Sorry, Googles multiplayer service is no longer available.\nI am working on a replacement as fast as possible.\nUntil then, please try another connection method.\n-Eric",
"googlePlayPurchasesNotAvailableText": "Google Play purchases are not available.\nYou may need to update your store app.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Always",
@ -1115,7 +1119,10 @@
"playlistsText": "Playlists",
"pleaseRateText": "If you're enjoying ${APP_NAME}, please consider taking a\nmoment and rating it or writing a review. This provides\nuseful feedback and helps support future development.\n\nthanks!\n-eric",
"pleaseWaitText": "Please wait...",
"pluginsDetectedText": "New plugin(s) detected. Enable/configure them in settings.",
"pluginClassLoadErrorText": "Error loading plugin class '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Error initing plugin '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "New plugin(s) detected. Restart to activate them, or configure them in settings.",
"pluginsRemovedText": "${NUM} plugin(s) no longer found.",
"pluginsText": "Plugins",
"practiceText": "Practice",
"pressAnyButtonPlayAgainText": "Press any button to play again...",
@ -1851,6 +1858,8 @@
"winsPlayerText": "${NAME} Wins!",
"winsTeamText": "${NAME} Wins!",
"winsText": "${NAME} Wins!",
"workspaceSyncErrorText": "Error syncing ${WORKSPACE}. See log for details.",
"workspaceSyncReuseText": "Can't sync ${WORKSPACE}. Reusing previous synced version.",
"worldScoresUnavailableText": "World scores unavailable.",
"worldsBestScoresText": "World's Best Scores",
"worldsBestTimesText": "World's Best Times",

View file

@ -211,14 +211,14 @@
"descriptionComplete": "Naipanalo ang laro",
"descriptionFull": "Panalunin ang laro sa ${LEVEL}",
"descriptionFullComplete": "Pinanalo ang laro sa ${LEVEL}",
"name": "${LEVEL} Panalo"
"name": "Nanalo sa ${LEVEL}"
},
"Rookie Onslaught Victory": {
"description": "Italo lahat ang mga kaway",
"descriptionComplete": "Natalo lahat ang mga kaway",
"descriptionFull": "Italo lahat ang mga kaway sa ${LEVEL}",
"descriptionFullComplete": "Natalo lahat ng mga kaway sa ${LEVEL}",
"name": "${LEVEL} Panalo"
"name": "Nanalo sa ${LEVEL}"
},
"Runaround God": {
"description": "Maka-iskor ng 2000 na Puntos",
@ -312,7 +312,7 @@
"descriptionComplete": "Natalo ang lahat na mga kaway",
"descriptionFull": "Talunin ang lahat na mga kaway sa ${LEVEL}",
"descriptionFullComplete": "Natalo ang lahat na mga kaway sa ${LEVEL}",
"name": "${LEVEL} Panalo"
"name": "Nanalo sa ${LEVEL}"
},
"Uber Runaround Victory": {
"description": "Tapusin ang lahat na kaway",
@ -325,6 +325,7 @@
"achievementsRemainingText": "Ang Mga Natitirang Nakamtan:",
"achievementsText": "Achievements",
"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…",
"titleText": "Magdagdag Ng Laro"
@ -623,9 +624,11 @@
"epicDescriptionFilterText": "${DESCRIPTION} sa isang epic na slow motion.",
"epicNameFilterText": "Epikong ${NAME}",
"errorAccessDeniedText": "walang pahintulot",
"errorDeviceTimeIncorrectText": "Ang oras ng iyong device ay naka-off nang ${HOURS} na oras.\nIto ay malamang na magdulot ng mga ibat ibang problema.\nPakisuri ang iyong mga setting ng oras at time-zone.",
"errorOutOfDiskSpaceText": "Wala sa puwang ng disk",
"errorSecureConnectionFailText": "Hindi mapatunayan ng secure na “cloud connection”; maaaring mabigo ang pagpapagana ng network.",
"errorText": "Error",
"errorUnknownText": "hindi batid na error",
"errorUnknownText": "error na di malaman",
"exitGameText": "Exit mula sa ${APP_NAME}?",
"exportSuccessText": "Na-export ang '${NAME}'.",
"externalStorageText": "External na Storage",
@ -786,7 +789,7 @@
"ticketPack4Text": "Malaking Pakete Ng Tiket",
"ticketPack5Text": "Mas Malaking Pakete Ng Tiket",
"ticketPack6Text": "Pinakamalaking Pakete Ng Tiket",
"ticketsFromASponsorText": "Kumuha ng ${COUNT} na tiket\nmula sa isang sponsor",
"ticketsFromASponsorText": "Manood ng Ad\npara makuha ang ${COUNT} na tiket",
"ticketsText": "${COUNT} Tiket",
"titleText": "Kumuha Ng Tiket",
"unavailableLinkAccountText": "Pasensya na, hindi available ang pagbili sa platform na ito.\nBilang isang solusyon, maaari mong i-link ang account na ito sa isang account na nasa\nisa pang platform at bumili doon.",
@ -797,6 +800,7 @@
"youHaveText": "mayroon kang ${COUNT} na tiket"
},
"googleMultiplayerDiscontinuedText": "Pasensya na, hindi na available ang multiplayer na serbisyo ng Google.\nGumagawa ako ng kapalit sa lalong madaling panahon.\nHanggang doon, mangyaring sumubok ng ibang paraan ng koneksyon.\n-Eric",
"googlePlayPurchasesNotAvailableText": "Hindi puwede ang mga pagbili sa Google Play nito.\nMaaaring kailanganin mong i-update ang iyong store app.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Palagi",
@ -836,11 +840,11 @@
"pickUpInfoTextScale": 0.5,
"powerupBombDescriptionText": "Payagan kang maglabas ng tatlong bomba\nsa isang hilera sa halip na isa lamang.",
"powerupBombNameText": "Tatlong-Bomba",
"powerupCurseDescriptionText": "Malamang na gusto mong iwasan ang mga ito.\n ...o ikaw?",
"powerupCurseDescriptionText": "Malamang na gusto mong iwasan ang mga ganito.\n ...o sige lang?",
"powerupCurseNameText": "Sumpa",
"powerupHealthDescriptionText": "Ibangon ka sa buong kalusugan.\nHindi mo naisip nito.",
"powerupHealthNameText": "Medikal-Pakete",
"powerupIceBombsDescriptionText": "Mas mahina kaysa sa mga normal na bomba\nngunit nagyelo ang iyong mga kalaban\nat partikular na malutong.",
"powerupIceBombsDescriptionText": "Mas mahina kaysa sa mga normal na bomba\nngunit nagyelo ang iyong mga kalaban\nat partikular na mabasag.",
"powerupIceBombsNameText": "Bombang-Yelo",
"powerupImpactBombsDescriptionText": "Medyo mahina kaysa sa regular\nbomba, ngunit sumasabog ito kapag nabagsak.",
"powerupImpactBombsNameText": "Gatilyong-Bomba",
@ -1089,7 +1093,7 @@
"titleText": "Maglaro",
"twoToEightPlayersText": "2-8 na manlalaro"
},
"playerCountAbbreviatedText": "${COUNT}n.m",
"playerCountAbbreviatedText": "${COUNT}p",
"playerDelayedJoinText": "Papasok si ${PLAYER} sa simula ng susunod na round.",
"playerInfoText": "Impormasyon ng Manlalaro",
"playerLeftText": "Umalis si ${PLAYER} sa laro.",
@ -1110,7 +1114,10 @@
"playlistsText": "Mga Playlist",
"pleaseRateText": "Kung nae-enjoy mo ang ${APP_NAME}, mangyaring isaalang-alang ang \npagkuha na sandali lang at i-rate ito o pagsulat ng isang pagsusuri. Nagbibigay ito ngkapaki-pakinabang na feedback at \ntumutulong sa pagsuporta sa pag-unlad sa hinaharap.\n\nsalamat!\n-eric",
"pleaseWaitText": "Hintay lang…",
"pluginsDetectedText": "May nakitang bagong (mga) plugin. Paganahin/i-configure ang mga ito sa mga setting.",
"pluginClassLoadErrorText": "Error sa paglo-load ang '${PLUGIN}' na klaseng plugin : ${ERROR}",
"pluginInitErrorText": "Error sa pagsisimula ang '${PLUGIN}' na plugin: ${ERROR}",
"pluginsDetectedText": "May nakitang bagong (mga) plugin. I-restart para i-activate ang mga ito, o i-configure ang mga ito sa mga setting.",
"pluginsRemovedText": "Hindi na nahanapan ang ${NUM} ng (mga) plugin.",
"pluginsText": "Mga Plugin",
"practiceText": "Pagsasagawa",
"pressAnyButtonPlayAgainText": "Pindutin ang anumang button para maglaro muli...",
@ -1453,7 +1460,7 @@
"kill ${ARG1} enemies": "patayin ang ${ARG1} na mga kalaban.",
"last one standing wins": "kung sino ang huling nakatayo ang siyang mananalo",
"last team standing wins": "kung sino ang huling katayuan ng team ang siyang mananalo",
"return ${ARG1} flags": "ibalik ang ${ARG1} na mga bandera",
"return ${ARG1} flags": "Magnakaw ng ${ARG1} na mga bandera",
"return 1 flag": "ibalik ang 1 bandila",
"run ${ARG1} laps": "Tumakbo ng ${ARG1} ikot",
"run 1 lap": "tumakbo ng 1 ikot",
@ -1774,7 +1781,7 @@
"randomName3Text": "Stephen",
"randomName4Text": "Joshua",
"randomName5Text": "Villar",
"skipConfirmText": "Sure ka na i-skip ang tutorial? Tap o pindutin para ma i-confirm.",
"skipConfirmText": "Sure ka ba na i-skip ang tutorial? Tap o pindutin para ma i-confirm.",
"skipVoteCountText": "${COUNT}/${TOTAL} boto na gustong laktawan",
"skippingText": "Nilalaktawan ang tutorial",
"toSkipPressAnythingText": "(i-tap o pindutin ang anuman para laktawan ang tutorial)"
@ -1801,13 +1808,13 @@
"votedAlreadyText": "Nakaboto ka na",
"votesNeededText": "Kailangan ng ${NUMBER} (na) boto",
"vsText": "vs.",
"waitingForHostText": "(naghihintay para sa ${HOST} na magpatuloy)",
"waitingForHostText": "(naghihintay para kay ${HOST} na magpatuloy)",
"waitingForPlayersText": "naghihintay ng mga manlalaro na sumali...",
"waitingInLineText": "Naghihintay sa pila (puno ang party)...",
"watchAVideoText": "Manood ng Isang video",
"watchAnAdText": "Manood ng Ad",
"watchWindow": {
"deleteConfirmText": "Tangalin \"${REPLAY}\"?",
"deleteConfirmText": "Tangalin ang \"${REPLAY}\"?",
"deleteReplayButtonText": "Itanggal ang \nReplay",
"myReplaysText": "Ang Mga Replay Ko",
"noReplaySelectedErrorText": "Walang Napiling Replay",
@ -1825,7 +1832,7 @@
"watchReplayButtonText": "Ipanood ang\nReplay"
},
"waveText": "Kaway",
"wellSureText": "Oo syempre",
"wellSureText": "Oo Syempre!",
"wiimoteLicenseWindow": {
"titleText": "Copyright ni DarwiinRemote"
},
@ -1839,11 +1846,13 @@
"listenText": "Makinig",
"macInstructionsText": "Tiyaking naka-off ang iyong Wii at naka-enable ang Bluetooth\nsa iyong Mac, pagkatapos ay pindutin ang 'Makinig'. Maaari ang suporta ng Wiimote\nmaging medyo patumpik-tumpik, kaya maaaring kailanganin mong subukan ng ilang beses\nbago ka magkaroon ng koneksyon.\n\nDapat hawakan ng Bluetooth ang hanggang 7 konektadong device,\nkahit na ang iyong mileage ay maaaring mag-iba.\n\nSinusuportahan ng BombSquad ang orihinal na Wiimotes, Nunchuks,\nat ang Klasikong Controller.\nGumagana na rin ang mas bagong Wii Remote Plus\nngunit hindi sa mga kalakip.",
"thanksText": "Salamat sa DarwiinRemote team\nPara maging posible ito.",
"titleText": "Wiimote Setup"
"titleText": "Pag-setup ng Wiimote"
},
"winsPlayerText": "${NAME} Nanalo!",
"winsTeamText": "${NAME} Nanalo!",
"winsPlayerText": "Nanalo si ${NAME}!",
"winsTeamText": "Nanalo ang ${NAME}!",
"winsText": "${NAME} Nanalo!",
"workspaceSyncErrorText": "Error sa pag-sync ng ${WORKSPACE}. Tingnan ang log para sa mga detalye.",
"workspaceSyncReuseText": "Hindi ma-sync ang ${WORKSPACE}. Muling paggamit ng nakaraang naka-sync na bersyon.",
"worldScoresUnavailableText": "Ang scores sa buong mundo ay hindi pa handa",
"worldsBestScoresText": "Pinakamahusay na Iskor ng Mundo",
"worldsBestTimesText": "Pinakamahusay na Oras sa Mundo",

View file

@ -1191,7 +1191,7 @@
"playlistsText": "Playlists",
"pleaseRateText": "Wenn dir ${APP_NAME} Spaß macht, nimm dir kurz die Zeit\nund bewerte es oder schreib ein Review. Durch das Feedback\nwird zukünftige Arbeit an dem Spiel unterstützt.\n\nVielen Dank!\n-eric",
"pleaseWaitText": "Bitte warte...",
"pluginsDetectedText": "Neue Plugins erkannt. Aktivieren / konfigurieren Sie sie in den Einstellungen.",
"pluginsDetectedText": "Neue Plugins erkannt. Neustarten, um sie zu aktivieren oder in den Einstellungen konfigurieren.",
"pluginsText": "Plugins",
"practiceText": "Übung",
"pressAnyButtonPlayAgainText": "Drücke einen Knopf um nochmal zu spielen...",

View file

@ -333,6 +333,7 @@
"achievementsRemainingText": "Azhiévemúnts Rzmáinzng:",
"achievementsText": "Achéevúmentz",
"achievementsUnavailableForOldSeasonsText": "Srrrz, chi faow co wjefo iwefo wef;oiajwf asodvjoa sdfj odfjsodf.",
"activatedText": "${THING} cjwoeifjwer.",
"addGameWindow": {
"getMoreGamesText": "Gztz Mrrz Gmzz...",
"titleText": "Ádzd Gámzé",
@ -660,7 +661,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} Ín ípic slúw mztíon.",
"epicNameFilterText": "${NAME} Epícz",
"errorAccessDeniedText": "acczlr dnfflz",
"errorDeviceTimeIncorrectText": "Yowruc cowier oowefjwoefj ${HOURS} horewif.\noitwocweojowerjiowejjjfwoef.\nPefjwe wehapeocjjgwghwe w e weofwoefjwe.",
"errorOutOfDiskSpaceText": "orz of dkzk spzlfz",
"errorSecureConnectionFailText": "Uweorcjwoef ojcowe werryyeoi nowe; jcnwoeroidfowdffdj.dfsdf.",
"errorText": "Errórz",
"errorUnknownText": "unknznlz errzzz",
"exitGameText": "$Excej ${APP_NAME}",
@ -748,6 +751,7 @@
"getFriendInviteCodeText": "Gz Frjor Infivo Cdz",
"googlePlayDescriptionText": "Invlt Gglglz Plzlz plzlfer tz yzr prtaryz:",
"googlePlayInviteText": "Invtlzz",
"googlePlayPurchasesNotAvailableText": "JGowej eo owe weoirjweorjf\nYou we r wcowehpaw ecowjeoiwjrperpp.",
"googlePlayReInviteText": "Tnz arzs ${COUNT} Gflf Plzz Plalfjdsf oin weojf wfeoyowiufe\nwhiz ewofij woijdoisoic o asodf wfiawje;aoew aoiwfj.\nTIncowjf oweofjao wotonwei oiacoiw eotijweto tat.",
"googlePlaySeeInvitesText": "Szz Invtlfz",
"googlePlayText": "Gzzlg Plz",
@ -840,7 +844,7 @@
"ticketPack4Text": "Jmfpmf Tkckf CPkf",
"ticketPack5Text": "Mmthf Tckff Pckc",
"ticketPack6Text": "Ulzjtla Tkckdf Pck",
"ticketsFromASponsorText": "Gzt ${COUNT} tkckjtz\nfzn a sobfhrkz",
"ticketsFromASponsorText": "woejwoe c woeijweo\ncowjeo ${COUNT} weocjw",
"ticketsText": "${COUNT} Tckrtzz",
"titleText": "Gtz Tckrtz",
"unavailableLinkAccountText": "Srryrc, cpofj caodn oiwrj coi weo joweijwoief.\nIf wc woiejf w, cocwi eoiaoth aciw jeojfow iejoijwef\npcoj weo faona omowio wejfowijerwe.",
@ -851,6 +855,7 @@
"youHaveText": "yz hv ${COUNT} tickrrz"
},
"googleMultiplayerDiscontinuedText": "Sowoer Gojf wel wouwen weoioc long wf won.\nI wow oe wefwjr pif g wfpawouja c oeij fw ocjaoiejowr.\nUntil. cowier oa j fapefij cpoypt ao coonnec awoiery.\n-Ercff",
"googlePlayPurchasesNotAvailableText": "Weofiiwj woe woerd wieofjwe\ncome foe rj eofiaj cpwoe jrowjer..dd",
"googlePlayText": "Gzzggl Plzz",
"graphicsSettingsWindow": {
"alwaysText": "Azlwáys",
@ -1196,7 +1201,10 @@
"playlistsText": "Plzlntsfs",
"pleaseRateText": "If yóu're énjoyíng ${APP_NAME}, pléase cónsider táking z\nmúment ánd rúting zt ór wrúting í reváew. Thzs próvidzs\níseful fzédbíck ánd hélps súpport fútúre dévelópmént.\n\nthénkz!\n-eric",
"pleaseWaitText": "Poke focwoe fjowef.",
"pluginsDetectedText": "Nz pweo woe dfowocewr. Enfwoc/cowefe thzm incowrdss.",
"pluginClassLoadErrorText": "Erori cojflw cwoej woer erwe '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Error cowejwoer plugins ${PLUGIN}: ${ERROR}",
"pluginsDetectedText": "Now cow rwoejcw. Wnewoiow rwepwot ghowcoweior weorjdfdfs.",
"pluginsRemovedText": "${NUM} powefjwj no legate fcwdf.",
"pluginsText": "Plfzlfez",
"practiceText": "Pcoifjzzz",
"pressAnyButtonPlayAgainText": "Prézs ánz búttún tóz pláy agánz...",
@ -1990,6 +1998,8 @@
"winsSingularText": "${NAME} WinzfjlzSS!",
"winsTeamText": "${NAME} Wnttm!",
"winsText": "${NAME} Wínsz!",
"workspaceSyncErrorText": "Eeror ocijo itself ${WORKSPACE}. See zoo crew tcowdf.",
"workspaceSyncReuseText": "Cnoiwj zoo ${WORKSPACE}. ROI cw cwoerpwer housers.",
"worldScoresUnavailableText": "Wrlzld scrzzl unvlfldsjbzl.",
"worldsBestScoresText": "Wúrld's Bést Scórzs",
"worldsBestTimesText": "Wúrld's Bzst Tímés",

View file

@ -332,6 +332,7 @@
"achievementsRemainingText": "Trofei rimasti:",
"achievementsText": "Trofei",
"achievementsUnavailableForOldSeasonsText": "Mi dispiace, ma gli obbiettivi non sono disponibili per le stagioni vecchie.",
"activatedText": "${THING} attivato.",
"addGameWindow": {
"getMoreGamesText": "Ottieni Giochi...",
"titleText": "Aggiungi Partita"
@ -1162,7 +1163,10 @@
"playlistsText": "Scalette",
"pleaseRateText": "Se ti sta piacendo ${APP_NAME}, prenditi\nun momento per valutarlo o scriverci su una recensione.\nQuesto aiuterà a supportare futuri sviluppi.\n\nGrazie!\n-eric",
"pleaseWaitText": "Attendi...",
"pluginsDetectedText": "Nuovo/i Plugin rilevato/i. Abilitali/configurali nelle impostazioni.",
"pluginClassLoadErrorText": "Errore nel caricamento di plugin classe '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Errore inizializzazione plugin '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "nuovo/i plugin rilevato. Riavvia per attivarli, o configurali nelle impostazioni.",
"pluginsRemovedText": "${NUM} plugin non trovati.",
"pluginsText": "Plugin",
"practiceText": "Allenamento",
"pressAnyButtonPlayAgainText": "Premi un pulsante qualunque per rigiocare...",
@ -1943,6 +1947,8 @@
"winsPlayerText": "${NAME} Ha Vinto!",
"winsTeamText": "${NAME} Ha Vinto!",
"winsText": "${NAME} vince!",
"workspaceSyncErrorText": "Errore nella sincronizzazione di ${WORKSPACE}. Controlla il registro per dettagli.",
"workspaceSyncReuseText": "Impossibile sincronizzare ${WORKSPACE}. Riutilizzo della versione sincronizzata precedente.",
"worldScoresUnavailableText": "Punteggi mondiali non disponibili.",
"worldsBestScoresText": "I punteggi migliori del mondo",
"worldsBestTimesText": "I tempi migliori del mondo",

View file

@ -326,6 +326,7 @@
"achievementsRemainingText": "دستاورد های باقیمانده:",
"achievementsText": "دستاوردها",
"achievementsUnavailableForOldSeasonsText": "ببخشید، دستاوردهای مخصوص فصل گذشته در دسترس نیستند",
"activatedText": "${THING} فعال شد.",
"addGameWindow": {
"getMoreGamesText": "...بازی های بیشتر",
"titleText": "افزودن بازی"
@ -625,7 +626,9 @@
"epicDescriptionFilterText": "در حماسهٔ حرکت آهسته ${DESCRIPTION}",
"epicNameFilterText": "${NAME} حماسهٔ",
"errorAccessDeniedText": "دسترسی رد شد",
"errorDeviceTimeIncorrectText": "ساعت گوشی ${HOURS} ساعت خاموش بوده‌است.\nممکن است مشکل به‌وجود بیاید.\nلطفاً ساعت و منطقهٔ زمانی گوشی‌تان را بررسی کنید.",
"errorOutOfDiskSpaceText": "حافظه جا ندارد",
"errorSecureConnectionFailText": "قادر به ایجاد اتصال ابری امن نیست. عملکرد شبکه ممکن است خراب شود.",
"errorText": "خطا",
"errorUnknownText": "خطای ناشناخته",
"exitGameText": "؟${APP_NAME} خروج از",
@ -788,7 +791,7 @@
"ticketPack4Text": "پک خیلی بزرگ بلیط",
"ticketPack5Text": "پک ماموتی بلیط",
"ticketPack6Text": "پک نهایی بلیط",
"ticketsFromASponsorText": "بلیط از${COUNT}\nیه اسپانسر بگیرید",
"ticketsFromASponsorText": "در ازای ${COUNT} بلیت\nیک آگهی ببینید",
"ticketsText": "بلیط${COUNT}",
"titleText": "بلیط بگیرید",
"unavailableLinkAccountText": ".ببخشید،خرید به وسیله ی این دستگاه در دسترس نمیباشد\nمیتوانید حسابتان بر روی این دستگاه را به حسابی در دستگاهی\n.دیگر متصل کنید و آنجا خرید خود را انجام دهید",
@ -1109,7 +1112,10 @@
"playlistsText": "لیست بازی ها",
"pleaseRateText": "خوشتان آمده، لطفاً چند لحظه‌ای وقت بگذارید و ${APP_NAME} اگر از\nآن را رتبه‌بندی کنید یا مروری بر آن بنویسید. این کار بازخوردهای مفیدی\n.را به همراه دارد و به پشتیبانی از توسعه‌ها در آینده کمک خواهد کرد\n\n!با تشکر\nاریک—",
"pleaseWaitText": "…لطفاً صبر کنید",
"pluginsDetectedText": ".افزونه(ها)ی جدید شناسایی شد. آن‌ها را در تنظیمات فعال/پیکربندی کنید",
"pluginClassLoadErrorText": "${ERROR} :«${PLUGIN}» خطا در بارگیری دسته‌بندی افزونهٔ",
"pluginInitErrorText": "${ERROR} :«${PLUGIN}» خطا در راه‌اندازی افزونهٔ",
"pluginsDetectedText": "افزونه(ها)ی جدید شناسایی شد. آن‌ها را در تنظیمات فعال، یا پیکربندی کنید.",
"pluginsRemovedText": "${NUM} افزونه دیگر یافت نمی‌شود.",
"pluginsText": "افزونه‌ها",
"practiceText": "تمرین",
"pressAnyButtonPlayAgainText": "…فشردن هر دکمه‌ای برای بازی دوباره",
@ -1847,6 +1853,8 @@
"winsPlayerText": "${NAME} برنده شد",
"winsTeamText": "${NAME} برنده شد",
"winsText": "${NAME} برنده شد",
"workspaceSyncErrorText": "خطا در همگام‌سازی ${WORKSPACE}. برای جزئیات به لاگ مراجعه کنید.",
"workspaceSyncReuseText": "نمی‌توان ${WORKSPACE} را همگام‌سازی کرد. استفادهٔ مجدد از نسخهٔ همگام‌سازی‌شده قبلی.",
"worldScoresUnavailableText": "امتیاز های جهانی قابل دسترس نیستند.",
"worldsBestScoresText": "بهترین امتیازهای جهانی",
"worldsBestTimesText": "بهترین زمان های جهانی",

View file

@ -1161,7 +1161,7 @@
"playlistsText": "Listy gier",
"pleaseRateText": "Jeśli polubiłeś ${APP_NAME}, proszę o poddanie go ocenie\nlub napisanie krótkiej recenzji. Pozwoli to zebrać przydatne\ninformacje, które pomogą wesprzeć rozwój gry w przyszłości.\n\nDziękuję!\n-Eric",
"pleaseWaitText": "Czekaj chwilkę...",
"pluginsDetectedText": "Nowe pluginy wykryte. Włącz/skonfiguruj je w ustawieniach.",
"pluginsDetectedText": "Wykryto nowe pluginy. Uruchom ponownie grę, aby je aktywować, lub skonfiguruje je w ustawieniach.",
"pluginsText": "Pluginy",
"practiceText": "Praktyka",
"pressAnyButtonPlayAgainText": "Naciśnij dowolny przycisk aby zagrać ponownie...",

View file

@ -331,6 +331,7 @@
"achievementsRemainingText": "Conquistas restantes:",
"achievementsText": "Conquistas",
"achievementsUnavailableForOldSeasonsText": "Desculpe, algumas conquistas não estão disponíveis em temporadas antigas.",
"activatedText": "${THING} foi ativado.",
"addGameWindow": {
"getMoreGamesText": "Mais jogos...",
"titleText": "Adicionar jogo"
@ -657,7 +658,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} em câmera lenta épica.",
"epicNameFilterText": "${NAME} épico(a)",
"errorAccessDeniedText": "acesso negado",
"errorDeviceTimeIncorrectText": "A hora do seu dispositivo está atrasada/adiantada em ${HOURS} horas.\nIsso poderá causar problemas.\nPor favor cheque as suas configurações de hora e fuso horário.",
"errorOutOfDiskSpaceText": "pouco espaço em disco",
"errorSecureConnectionFailText": "Não foi possível estabelecer uma conexão segura à nuvem; a funcionalidade da rede pode falhar.",
"errorText": "Erro",
"errorUnknownText": "erro desconhecido",
"exitGameText": "Sair do ${APP_NAME}?",
@ -832,7 +835,7 @@
"ticketPack4Text": "Pacote jumbo de bilhetes",
"ticketPack5Text": "Pacote mamute de bilhete",
"ticketPack6Text": "Pacote ultimate de bilhetes",
"ticketsFromASponsorText": "Ganhe ${COUNT} bilhetes\nde um patrocinador",
"ticketsFromASponsorText": "Assista a um anúncio \ne ganhe ${COUNT} bilhetes",
"ticketsText": "${COUNT} bilhetes",
"titleText": "Obter bilhetes",
"unavailableLinkAccountText": "Desculpe, as compras não estão disponíveis nesta plataforma.\nComo solução alternativa, você pode vincular esta conta para\noutra conta de outra plataforma e fazer compras lá.",
@ -843,6 +846,7 @@
"youHaveText": "você possui ${COUNT} bilhetes"
},
"googleMultiplayerDiscontinuedText": "Desculpe, o serviço multijogador do Google não está mais disponível.\nEstou trabalhando em uma substituição o mais rápido possível.\nAté lá, tente outro método de conexão.\n-Eric",
"googlePlayPurchasesNotAvailableText": "Compras pela Google Play não estão disponíveis.\nTalvez seja necessário atualizar sua loja para isso.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Sempre",
@ -1183,7 +1187,10 @@
"playlistsText": "Playlists",
"pleaseRateText": "Se você está curtindo ${APP_NAME}, por favor, dê um tempinho\npara avaliar e comentar. Isso nos dá uma opinião útil\ne ajuda no desenvolvimento do jogo.\n\nobrigado!\n-eric",
"pleaseWaitText": "Por favor, aguarde...",
"pluginsDetectedText": "Novo(s) plugin(s) detected. Ative-os/configure-os em configurações.",
"pluginClassLoadErrorText": "Erro ao carregar a classe de um plugin '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Erro ao inicializar plugin '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Novo(s) plugin(s) detetados. Reinicie o jogo para ativá-los ou configure-os nas configurações.",
"pluginsRemovedText": "${NUM} plugin(s) não foram encontrados.",
"pluginsText": "Plugins",
"practiceText": "Praticar",
"pressAnyButtonPlayAgainText": "Aperte qualquer botão para jogar novamente...",
@ -1970,6 +1977,8 @@
"winsPlayerText": "${NAME} venceu!",
"winsTeamText": "${NAME} venceu!",
"winsText": "${NAME} ganhou!",
"workspaceSyncErrorText": "Erro ao sincronizar ${WORKSPACE}. Veja o log para mais detalhes.",
"workspaceSyncReuseText": "Não pôde sincronizar ${WORKSPACE}. Reusando uma versão anterior sincronizada.",
"worldScoresUnavailableText": "Pontuações mundiais indisponíveis.",
"worldsBestScoresText": "Melhores pontuações do mundo",
"worldsBestTimesText": "Melhores tempos do mundo",

File diff suppressed because it is too large Load diff

View file

@ -39,7 +39,7 @@
"testAccountWarningCardboardText": "Внимание: вы подписываете в с \"тест\" счета.\nЭти данные будут заменены со счетами Google сразу\nони становятся поддерживается в картонные приложений.\n\nСейчас вы будете зарабатывать все билеты в игре.\n(вы , тем не менее , получить Pro обновление BombSquad бесплатно )",
"testAccountWarningOculusText": "Внимание: вы входите под акканутом \"test\".\nОни будут заменены аккаунтами Oculus позже в этом\nгоду, что позволит совершать покупки билетов и многое другое.\n\nНа данный момент вам придется зарабатывать билеты в игре.\n(однако, вы получаете обновление BombSquad Pro бесплатно)",
"testAccountWarningText": "Внимание: вы заходите под аккаунтом \"test\".\nЭтот аккаунт привязан к конкретному устройству и может\nпериодически сбрасываться. (так что лучше особо\nне тратить время на сбор/разблокировку добра в нем)\n\nЧтобы использовать настоящий аккаунт (Game-Center,\nGoogle Plus и т.д.), запустите платную версию. Это также\nпозволит сохранять свой прогресс в облаке и делать его\nдоступным для разных устройств.",
"ticketsText": "Билетов: ${COUNT}",
"ticketsText": "Количество билетов: ${COUNT}",
"titleText": "Аккаунт",
"unlinkAccountsInstructionsText": "Выберите аккаунт, который хотите отвязать",
"unlinkAccountsText": "Отвязать аккаунты",
@ -49,14 +49,14 @@
"youAreSignedInAsText": "Вы вошли как:"
},
"achievementChallengesText": "Достижения",
"achievementText": "Медаль",
"achievementText": "Достижение",
"achievements": {
"Boom Goes the Dynamite": {
"description": "Убейте 3 плохих парней с помощью TNT",
"descriptionComplete": "С помощью TNT убито 3 плохих парней",
"descriptionFull": "Убейте 3 плохих парней с помощью TNT на уровне ${LEVEL}",
"descriptionFullComplete": "3 плохих парней убито с помощью TNT на уровне ${LEVEL}",
"name": "Сейчас бабахнет"
"name": "Динамит сейчас взорвётся!"
},
"Boxer": {
"description": "Победите без использования бомб",
@ -134,7 +134,7 @@
"descriptionComplete": "С карты сброшено 3 негодяя",
"descriptionFull": "Сбросьте 3 негодяев с карты на уровне ${LEVEL}",
"descriptionFullComplete": "3 негодяя сброшено с карты на уровне ${LEVEL}",
"name": "Давай отсюда"
"name": "Пора учится летать"
},
"Onslaught God": {
"description": "Наберите 5000 очков",
@ -180,7 +180,7 @@
},
"Pro Football Shutout": {
"description": "Победите в сухую",
"descriptionComplete": "Победил в сухую",
"descriptionComplete": "Уровень был пройден в сухую",
"descriptionFull": "Выиграйте матч ${LEVEL} в сухую",
"descriptionFullComplete": "Победа в матче ${LEVEL} в сухую",
"name": "${LEVEL} в сухую"
@ -194,9 +194,9 @@
},
"Pro Onslaught Victory": {
"description": "Победите все волны",
"descriptionComplete": обеждены все волны",
"descriptionComplete": ройдены все волны",
"descriptionFull": "Победите все волны на уровне ${LEVEL}",
"descriptionFullComplete": обеждены все волны на уровне ${LEVEL}",
"descriptionFullComplete": ройдены все волны на ${LEVEL}",
"name": "Победа на уровне ${LEVEL}"
},
"Pro Runaround Victory": {
@ -215,7 +215,7 @@
},
"Rookie Football Victory": {
"description": "Выиграйте матч",
"descriptionComplete": "Матч выигран",
"descriptionComplete": "Матч пройден в вашу пользу",
"descriptionFull": "Выиграйте матч ${LEVEL}",
"descriptionFullComplete": "Выигран матч ${LEVEL}",
"name": "Победа в матче ${LEVEL}"
@ -250,7 +250,7 @@
},
"Sharing is Caring": {
"descriptionFull": "Успешно поделиться игрой с другом",
"descriptionFullComplete": "Игра успешно поделена с другом",
"descriptionFullComplete": "Игра успешно передана другу",
"name": "Делиться - значит заботиться"
},
"Stayin' Alive": {
@ -309,7 +309,7 @@
},
"Uber Football Victory": {
"description": "Выиграйте матч",
"descriptionComplete": "Матч выигран",
"descriptionComplete": "Матч пройден в вашу пользу",
"descriptionFull": "Выиграйте матч ${LEVEL}",
"descriptionFullComplete": "Выигран матч ${LEVEL}",
"name": "Победа в матче ${LEVEL}"
@ -329,9 +329,10 @@
"name": "Победа на уровне ${LEVEL}"
}
},
"achievementsRemainingText": "Оставшиеся медали:",
"achievementsText": "Медали",
"achievementsRemainingText": "Осталось достижений:",
"achievementsText": "Достижения",
"achievementsUnavailableForOldSeasonsText": "К сожалению, подробности достижений не доступны для старых сезонов.",
"activatedText": "${THING} активировано.",
"addGameWindow": {
"getMoreGamesText": "Еще игр...",
"titleText": "Добавить игру"
@ -340,7 +341,7 @@
"alreadySignedInText": "На вашем аккаунте играют на другом устройстве;\nпожалуйста зайдите с другого аккаунта или закройте\nигру на другом устройстве и попытайтесь снова.",
"apiVersionErrorText": "Невозможно загрузить модуль ${NAME}; он предназначен для API версии ${VERSION_USED}; здесь требуется версия ${VERSION_REQUIRED}.",
"audioSettingsWindow": {
"headRelativeVRAudioInfoText": "(Режим \"Авто\" включает его только когда подключены наушники)",
"headRelativeVRAudioInfoText": "(Режим \"Авто\" активируется только при подключении наушников)",
"headRelativeVRAudioText": "Позиционно-зависимое ВР-аудио",
"musicVolumeText": "Громкость музыки",
"soundVolumeText": "Громкость звука",
@ -374,37 +375,37 @@
"completeThisLevelToProceedText": "Чтобы продолжить, нужно\nпройти этот уровень!",
"completionBonusText": "Бонус за прохождение",
"configControllersWindow": {
"configureControllersText": "Настройка контроллеров",
"configureControllersText": "Настройка геймпада",
"configureGamepadsText": "Настройка контроллеров",
"configureKeyboard2Text": "Настройка клавиатуры P2",
"configureKeyboardText": "Настройка клавиатуры",
"configureMobileText": "Мобильные устройства как контроллеры",
"configureMobileText": "Использовать мобильные устройства в качестве геймпадов",
"configureTouchText": "Настройка сенсорного экрана",
"ps3Text": "Контроллеры PS3",
"titleText": "Контроллеры",
"wiimotesText": "Контроллеры Wii",
"xbox360Text": "Контроллеры Xbox 360"
"ps3Text": "Геймпады PS3™",
"titleText": "Геймпады",
"wiimotesText": "Пульт Wii™",
"xbox360Text": "Геймпады Xbox 360™"
},
"configGamepadSelectWindow": {
"androidNoteText": "Внимание: поддержка контроллеров различается в зависимости от устройства и версии Android.",
"pressAnyButtonText": "Нажмите любую кнопку на контроллере,\n который хотите настроить...",
"titleText": "Настроить контроллер"
"androidNoteText": "Внимание: поддержка геймпада различается в зависимости от устройства и версии Android.",
"pressAnyButtonText": "Нажмите любую кнопку на геймпаде,\n которую хотите настроить...",
"titleText": "Настроить геймпад"
},
"configGamepadWindow": {
"advancedText": "Дополнительно",
"advancedTitleText": "Дополнительные настройки контроллера",
"advancedTitleText": "Дополнительные настройки геймпада",
"analogStickDeadZoneDescriptionText": "(Включите, если персонаж продолжает двигаться после того, как стик отпущен)",
"analogStickDeadZoneText": "Мертвая зона аналогового стика",
"appliesToAllText": "(относится ко всем контроллерам данного типа)",
"appliesToAllText": "(настроить ко всем геймпадам данного типа)",
"autoRecalibrateDescriptionText": "(включить, если персонаж не двигается на полной скорости)",
"autoRecalibrateText": "Авто-перекалибровка аналоговых стиков",
"axisText": "ось",
"clearText": "очистить",
"dpadText": "D-Pad",
"extraStartButtonText": "Дополнительная кнопка \"Старт",
"extraStartButtonText": "Настроить дополнительную кнопку \"Start\"",
"ifNothingHappensTryAnalogText": "Если ничего не происходит, попробуйте вместо этого присвоить аналоговому стику.",
"ifNothingHappensTryDpadText": "Если ничего не происходит, попробуйте вместо этого присвоить D-Pad.",
"ignoreCompletelyDescriptionText": "(не дайте этому контроллеру воздействовать на игру, или меню)",
"ignoreCompletelyDescriptionText": "(запретить геймпаду воздействовать на игру, или меню)",
"ignoreCompletelyText": "Игнорировать полностью",
"ignoredButton1Text": "Игнорируемая кнопка 1",
"ignoredButton2Text": "Игнорируемая кнопка 2",
@ -422,13 +423,13 @@
"runTrigger1Text": "Триггер для бега 1",
"runTrigger2Text": "Триггер для бега 2",
"runTriggerDescriptionText": "(аналоговые триггеры позволяют бегать с разной скоростью)",
"secondHalfText": "Используйте это для настройки второй половины\nустройства 'два контроллера в одном',\nкоторое показывается как один контроллер.",
"secondHalfText": "Используйте эту опцию для настройки второй\nполовины устройства \"два геймпада в одном\",\nдля использования в качестве одного геймпада.",
"secondaryEnableText": "Включить",
"secondaryText": "Вторичный контроллер",
"secondaryText": "Второй геймпад",
"startButtonActivatesDefaultDescriptionText": "(выключить, если ваша кнопка \"старт\" работает больше в качестве кнопки \"меню\")",
"startButtonActivatesDefaultText": "Кнопка Старт активирует стандартный виджет",
"titleText": "Настройка контроллера",
"twoInOneSetupText": "Настройка контроллера 2-в-1",
"titleText": "Настройка геймпада",
"twoInOneSetupText": "Настройка геймпада 2-в-1",
"uiOnlyDescriptionText": "(Запретить этому контроллеру присоединяться к игре)",
"uiOnlyText": "Ограничить только для меню",
"unassignedButtonsRunText": "Все неприсвоенные кнопки для бега",
@ -481,8 +482,8 @@
"challengesText": "Испытания",
"currentBestText": "Последний рекорд",
"customText": "Другое",
"entryFeeText": "Участие",
"forfeitConfirmText": "Так просто сдаться?",
"entryFeeText": "Участвовать",
"forfeitConfirmText": "Сдаться?",
"forfeitNotAllowedYetText": "Вы не можете покинуть это состязание.",
"forfeitText": "Сдаться",
"multipliersText": "Множители",
@ -651,14 +652,16 @@
"epicDescriptionFilterText": "${DESCRIPTION} в эпическом замедленном действии.",
"epicNameFilterText": "${NAME} в эпическом режиме",
"errorAccessDeniedText": "доступ запрещен",
"errorDeviceTimeIncorrectText": "Время на устройстве отстает на ${HOURS} часов.\nЭто вызывает проблемы.\nПожалуйста, проверьте настройки времени и часового пояса.",
"errorOutOfDiskSpaceText": "нет места на диске",
"errorSecureConnectionFailText": "Ошибка установки безопасного облачного соединения; сетевые функции могут дать сбой.",
"errorText": "Ошибка",
"errorUnknownText": "неизвестная ошибка",
"exitGameText": "Выйти из ${APP_NAME}?",
"exportSuccessText": "'${NAME}' экспортирован.",
"externalStorageText": "Внешняя память",
"failText": "Провал",
"fatalErrorText": "Ой, что-то потерялось или сломалось.\nПопытайтесь переустановить приложение или\nобратитесь к ${EMAIL} за помощью.",
"fatalErrorText": "Файлы игры повреждены или отсутствуют.\nПопробуйте переустановить приложение или\nобратитесь к ${EMAIL} за помощью.",
"fileSelectorWindow": {
"titleFileFolderText": "Выберите файл или папку",
"titleFileText": "Выберите файл",
@ -670,7 +673,7 @@
"finalScoresText": "Финальные очки",
"finalTimeText": "Финальное время",
"finishingInstallText": "Завершается установка, минутку...",
"fireTVRemoteWarningText": "* Для лучшего результата используйте\nигровые контроллеры или установите\nприложение '${REMOTE_APP_NAME}'\nна ваших телефонах и планшетах.",
"fireTVRemoteWarningText": "* Для лучшего результата используйте\nгеймпады или установите\nприложение '${REMOTE_APP_NAME}'\nна ваших телефонах и планшетах.",
"firstToFinalText": "Финал до ${COUNT} очков",
"firstToSeriesText": "Серия до ${COUNT} очков",
"fiveKillText": "ПЯТЕРЫХ ЗА РАЗ!!!",
@ -708,7 +711,7 @@
"addressFetchErrorText": "<ошибка запроса адресов>",
"appInviteInfoText": "Пригласите друзей поиграть в BombSquad и они\nполучат ${COUNT} бесплатных билетов. Ты получишь\n${YOU_COUNT} за каждого друга.",
"appInviteMessageText": "${NAME} отправил вам ${COUNT} билетов в ${APP_NAME}",
"appInviteSendACodeText": "Отправьте им код",
"appInviteSendACodeText": "Отправить им код",
"appInviteTitleText": "Приглашение в ${APP_NAME}",
"bluetoothAndroidSupportText": "(работает с любым устройством, поддерживающим Bluetooth)",
"bluetoothDescriptionText": "Создать/войти в лобби через Bluetooth:",
@ -829,7 +832,7 @@
"ticketPack4Text": "Огромная пачка билетов",
"ticketPack5Text": "Слоновая пачка билетов",
"ticketPack6Text": "Максимальная пачка билетов",
"ticketsFromASponsorText": "Получить ${COUNT} билетов\nот спонсора",
"ticketsFromASponsorText": "Посмотреть рекламу,\nчтобы получить ${COUNT} билетов",
"ticketsText": "Билетов: ${COUNT}",
"titleText": "Получить билеты",
"unavailableLinkAccountText": "Извините, но на этой платформе покупки недоступны.\nВ качестве решения, вы можете привязать этот аккаунт \nк аккаунту на другой платформе, и совершать покупки там.",
@ -840,6 +843,7 @@
"youHaveText": "У вас ${COUNT} билетов"
},
"googleMultiplayerDiscontinuedText": "Простите, сервис многопользовательской игры Google больше не поддерживается.\nЯ работаю над заменой так быстро, насколько это возможно.\nДо тех пор, пожалуйста выберете другой способ подключения.\n-Эрик",
"googlePlayPurchasesNotAvailableText": "Покупки в Google Play недоступны.\nВозможно, вам необходимо обновить приложение магазина.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Всегда",
@ -1166,7 +1170,10 @@
"playlistsText": "Плей-листы",
"pleaseRateText": "Если вам нравится игра ${APP_NAME}, пожалуйста, подумайте о том,\nчтобы оценить ее или написать рецензию. Это обеспечивает полезную\nобратную связь и помогает поддержать дальнейшую разработку.\n\nСпасибо!\n- Эрик",
"pleaseWaitText": "Пожалуйста, подождите...",
"pluginsDetectedText": "Обнаружены новые плагины. Включите/настройте их в настройках.",
"pluginClassLoadErrorText": "Ошибка при попытке загрузить класс плагина '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Ошибка при инициализации плагина '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Обнаружены новые плагины! Перезапустите игру, чтобы активировать их, или настройте их в настройках.",
"pluginsRemovedText": "${NUM} плагин(ов) больше не найдены.",
"pluginsText": "Плагины",
"practiceText": "Тренировка",
"pressAnyButtonPlayAgainText": "Нажмите любую кнопку чтобы играть снова...",
@ -1188,10 +1195,10 @@
},
"promoSubmitErrorText": "Ошибка отправки кода, проверьте своё интернете соединение",
"ps3ControllersWindow": {
"macInstructionsText": "Выключите питание на задней панели PS3, убедитесь, что Bluetooth\nвключен на вашем компьютере, а затем подключите контроллер к Маку\nс помощью кабеля USB для синхронизации. Теперь можно использовать\nкнопку контроллера 'PS' чтобы подключить его к Маку\nв проводном (USB) или беспроводном (Bluetooth) режиме.\n\nНа некоторых Маках при синхронизации может потребоваться код доступа.\nВ этом случае обратитесь к следующей инструкции или к гуглу.\n\n\n\n\nКонтроллеры PS3, связанные по беспроводной сети, должны появиться\nв списке устройств в Настройках системы -> Bluetooth. Возможно, вам придется\nудалить их из этого списка, если вы хотите снова использовать их с PS3.\n\nТакже всегда отключайте их от Bluetooth, когда он не используется,\nиначе будут садиться батарейки.\n\nBluetooth должен обрабатывать до 7 подключенных устройств,\nхотя у вас может получиться по-другому.",
"ouyaInstructionsText": "Чтобы использовать контроллер PS3 с OUYA, просто подключите его один раз\nс помощью кабеля USB для синхронизации. Это может отключить другие\nконтроллеры, тогда нужно перезагрузить OUYA и отсоединить кабель USB.\n\nПосле этого можно использовать кнопку 'PS' контроллера для беспроводного\nподключения. После игры нажмите и удерживайте кнопку 'PS' в течение\n10 секунд чтобы выключить контроллер, в противном случае он может\nостаться включенным и разрядит батарейки.",
"macInstructionsText": "Выключите питание на задней панели PS3, убедитесь, что Bluetooth\nвключен на вашем компьютере, а затем подключите геймпад к Mac\nс помощью кабеля USB для синхронизации. Теперь можно использовать\nкнопку геймпад 'PS' чтобы подключить его к Mac\nв проводном (USB) или беспроводном (Bluetooth) режиме.\n\nНа некоторых системах Mac при синхронизации может потребоватьсякод доступа.\nВ этом случае обратитесь к следующей инструкции или к гуглу.\n\n\n\n\nГеймпады PS3, связанные по беспроводной сети, должны появиться\nв списке устройств в Настройках системы -> Bluetooth. Возможно, вам придется\nудалить их из этого списка, если вы хотите снова использовать их с PS3.\n\nТакже всегда отключайте их от Bluetooth, когда он не используется,\nиначе будут садиться батарейки.\n\nBluetooth должен обрабатывать до 7 подключенных устройств,\nхотя у вас может получиться по-другому.",
"ouyaInstructionsText": "Чтобы использовать геймпад PS3 с OUYA, просто подключите его один раз\nс помощью кабеля USB для синхронизации. Это может отключить другие\nгеймпады, тогда нужно перезагрузить OUYA и отсоединить кабель USB.\n\nПосле этого можно использовать кнопку 'PS' геймпада для беспроводного\nподключения. После игры нажмите и удерживайте кнопку 'PS' в течение\n10 секунд чтобы выключить геймпад, в противном случае он может\nостаться включенным и разрядит батарейки.",
"pairingTutorialText": "видео-тьюториал по синхронизации",
"titleText": "Использование контроллеров PS3 с ${APP_NAME}:"
"titleText": "Использование геймпада PS3 с ${APP_NAME}:"
},
"publicBetaText": "ОТКРЫТАЯ БЕТА-ВЕРСИЯ",
"punchBoldText": "УДАР",
@ -1201,7 +1208,7 @@
"purchasingText": "Покупка...",
"quitGameText": "Выйти из ${APP_NAME}?",
"quittingIn5SecondsText": "Выход через 5 секунд...",
"randomPlayerNamesText": "Дима, Кузя, Вован, Маха, Русский, Какуля, Бибер, Борька, Няшка, Толян, Ержан, Дибисяра, Вася, Морген, Серёга, Ваня, Кеша, Жорик, Стёпа, Эдгар, Циган",
"randomPlayerNamesText": "Дима, Кузя, Вован, Маха, Русский, Какуля, Бибер, Борька, Няшка, Толян, Ержан, Дибисяра, Вася, Морген, Серёга, Ваня, Кеша, Жорик, Стёпа, Эдгар, Цыган, Олег, Егор, Ёршик",
"randomText": "Случайный",
"rankText": "Ранг",
"ratingText": "Рейтинг",
@ -1508,9 +1515,9 @@
"Get the flag to the enemy end zone.": "Отнесите флаг в зону защиты противника.",
"How fast can you defeat the ninjas?": "Как быстро вы сможете победить ниндзя?",
"Kill a set number of enemies to win.": "Убейте заданное число врагов, чтобы выиграть.",
"Last one standing wins.": "Побеждает последний стоящий на ногах.",
"Last remaining alive wins.": "Побеждает последний в живых.",
"Last team standing wins.": "Побеждает последняя стоящая на ногах команда.",
"Last one standing wins.": "Побеждает последний живой игрок.",
"Last remaining alive wins.": "Побеждает последний живой игрок.",
"Last team standing wins.": "Побеждает последняя живая команда.",
"Prevent enemies from reaching the exit.": "Не дайте врагам дойти до выхода.",
"Reach the enemy flag to score.": "Доберитесь до вражеского флага чтобы набрать очки.",
"Return the enemy flag to score.": "Принесите вражеский флаг на базу, чтобы набрать очки.",
@ -1536,8 +1543,8 @@
"Touch the enemy flag.": "Коснитесь вражеского флага.",
"carry the flag for ${ARG1} seconds": "пронесите флаг в течение ${ARG1} секунд",
"kill ${ARG1} enemies": "убейте ${ARG1} врагов",
"last one standing wins": "побеждает последний стоящий на ногах",
"last team standing wins": "побеждает последняя стоящая на ногах команда",
"last one standing wins": "побеждает последний живой игрок",
"last team standing wins": "побеждает последняя живая команда",
"return ${ARG1} flags": "принесите ${ARG1} флагов на базу",
"return 1 flag": "принесите 1 флаг на базу",
"run ${ARG1} laps": "пробегите ${ARG1} кругов",
@ -1874,7 +1881,7 @@
},
"twoKillText": "ДВОИХ ЗА РАЗ!",
"unavailableText": "недоступно",
"unconfiguredControllerDetectedText": "Обнаружен ненастроенный контроллер:",
"unconfiguredControllerDetectedText": "Обнаружен ненастроенный геймпад:",
"unlockThisInTheStoreText": "Это должно быть разблокировано в магазине.",
"unlockThisProfilesText": "Чтобы создать более ${NUM} профиль, Вам необходимо:",
"unlockThisText": "Чтобы разблокировать это, вам нужно:",
@ -1940,13 +1947,15 @@
"winsPlayerText": "Победил ${NAME}!",
"winsTeamText": "Победили ${NAME}!",
"winsText": "${NAME} выиграл!",
"workspaceSyncErrorText": "Ошибка при попытке синхронизации ${WORKSPACE}. Посмотрите лог для информации.",
"workspaceSyncReuseText": "Не может синронизировать ${WORKSPACE}. Будет использоватся прошлая синхронизация.",
"worldScoresUnavailableText": "Мировые результаты недоступны.",
"worldsBestScoresText": "Лучшие в мире очки",
"worldsBestTimesText": "Лучшее в мире время",
"xbox360ControllersWindow": {
"getDriverText": "Скачать драйвер",
"macInstructions2Text": "Для использования контроллеров по беспроводной связи, вам также\nпотребуется ресивер, который поставляется с \"беспроводным контроллером\nXbox 360 для Windows\". Один ресивер позволяет подключить до 4 контроллеров.\n\nВнимание: ресиверы сторонних производителей не будут работать с этим драйвером,\nубедитесь, что на вашем ресивере написано \"Microsoft\", а не \"XBOX 360\".\nMicrosoft больше не продает их отдельно, так что вам нужно будет найти\nресивер в комплекте с контроллером, либо искать на ebay.\n\nЕсли вы считаете это полезным, можете отправить денег разработчику\nдрайвера на его сайте.",
"macInstructionsText": "Для использования контроллеров Xbox 360 необходимо\nустановить драйвер Mac, доступный по ссылке ниже.\nОн работает и с проводными и беспроводными контроллерами.",
"macInstructions2Text": "Для использования контроллеров по беспроводной связи, вам также\nпотребуется ресивер, который поставляется с \"беспроводным геймпадом\nXbox 360 для Windows\". Один ресивер позволяет подключить до 4 контроллеров.\n\nВнимание: ресиверы сторонних производителей не будут работать с этим драйвером,\nубедитесь, что на вашем ресивере написано \"Microsoft\", а не \"XBOX 360\".\nMicrosoft больше не продает их отдельно, так что вам нужно будет найти\nресивер в комплекте с геймпадом, либо искать на ebay.\n\nЕсли вы считаете это полезным, можете отправить денег разработчику\nдрайвера на его сайте.",
"macInstructionsText": "Для использования геймпада Xbox 360 необходимо\nустановить драйвер Mac, доступный по ссылке ниже.\nОн работает и с проводными и беспроводными геймпадами.",
"macInstructionsTextScale": 0.8,
"ouyaInstructionsText": "Для использования проводных контроллеров Xbox 360 в BombSquad,\nпросто подключите их к USB-порту вашего устройства. Для нескольких\nконтроллеров можно использовать концентратор USB.\n\nДля использования беспроводных контроллеров вам понадобится беспроводной\nресивер который поставляется в наборе \"беспроводного геймпада Xbox 360\nдля Windows\" или продается отдельно. Каждый ресивер подключается\nк порту USB и позволяет подключать до 4 беспроводных контроллеров.",
"titleText": "Использование контроллеров Xbox 360 в ${APP_NAME}:"

View file

@ -328,6 +328,7 @@
"achievementsRemainingText": "Неизвршена достигнућа:",
"achievementsText": "Достигнућа",
"achievementsUnavailableForOldSeasonsText": "Извини, информације нису доступне за старије сезоне.",
"activatedText": "${THING} Активиран",
"addGameWindow": {
"getMoreGamesText": "Додај више игри...",
"titleText": "Додај игру"
@ -627,7 +628,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} у епско успореној игри.",
"epicNameFilterText": "Епски ${NAME}",
"errorAccessDeniedText": "приступ одбијен",
"errorDeviceTimeIncorrectText": "Време на твом уређају је нетачно за ${HOURS} сати.\nОво може изазвати проблеме.\nМолимо вас проверите подешавања времена и временску зону.",
"errorOutOfDiskSpaceText": "нема места на диску",
"errorSecureConnectionFailText": "Немогуће успоставити везу са клаудом; функција мреже може престати.",
"errorText": "Грешка",
"errorUnknownText": "непозната грешка",
"exitGameText": "Изађи из \"${APP_NAME}\"?",
@ -1119,7 +1122,10 @@
"playlistsText": "Листе игара",
"pleaseRateText": "Ако уживате у \"${APP_NAME}\" игри, молимо вас да издвојите\nмало времена да оцените апликацију или напишете коментар.\nОво нам даје корисне информације и помаже у будућем развоју.\n\nХвала!\n-Ерик",
"pleaseWaitText": "Молимо вас сачекајте...",
"pluginsDetectedText": "Нови додатак/ци пронађен/и. Укључи/подеси их у подешавањима.",
"pluginClassLoadErrorText": "Грешка учитавања плугина класе '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Грешка иницијације плугина '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Нови додатак/ци пронађени. Рестартуј да их активираш,или конфигуришеш у подешавањима.",
"pluginsRemovedText": "${NUM} додатак/ више не постоје.",
"pluginsText": "Додаци",
"practiceText": "Вежбање",
"pressAnyButtonPlayAgainText": "Притисни било које дугме да одиграш опет...",
@ -1855,6 +1861,8 @@
"winsPlayerText": "${NAME} је победио!",
"winsTeamText": "${NAME} је победио!",
"winsText": "${NAME} је победио!",
"workspaceSyncErrorText": "Грешка синхронизовања ${WORKSPACE}. Погледајте лог за више детаља.",
"workspaceSyncReuseText": "Синхронизовање ${WORKSPACE} неуспешно. Коришћење претходно синхронизоване верзије.",
"worldScoresUnavailableText": "Светски резултати недоступни.",
"worldsBestScoresText": "Најбољи резултати на свету",
"worldsBestTimesText": "Најбоља времена на свету",

View file

@ -32,7 +32,7 @@
"signInWithTestAccountInfoText": "(Cuenta de prueba, usa la cuenta del dispositivo para avanzar)",
"signInWithTestAccountText": "Registrarse con una Cuenta de Prueba",
"signInWithV2InfoText": "(una cuenta que funciona en todas las plataformas)",
"signInWithV2Text": "Inicie cesión con una cuenta de bosquad",
"signInWithV2Text": "Inicie sesión con tú cuenta de BombSquad",
"signOutText": "Cerrar Sesión",
"signingInText": "Iniciando sesión...",
"signingOutText": "Cerrando sesión...",
@ -332,6 +332,7 @@
"achievementsRemainingText": "Logros pendientes:",
"achievementsText": "Logros",
"achievementsUnavailableForOldSeasonsText": "Lo sentimos, los logros específicos no están disponibles para temporadas anteriores.",
"activatedText": "${THING} activado.",
"addGameWindow": {
"getMoreGamesText": "Más Juegos...",
"titleText": "Agregar Juego"
@ -651,7 +652,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} En cámara lenta épica.",
"epicNameFilterText": "${NAME} - Modo épico",
"errorAccessDeniedText": "acceso negado",
"errorDeviceTimeIncorrectText": "La hora actual de tu dispositivo es ${HOURS} horas.\nEsto podría causar problemas.\nPorfavor verifica la hora y zona horaria en ajustes.",
"errorOutOfDiskSpaceText": "insuficiente espacio en disco",
"errorSecureConnectionFailText": "No se puede establecer una conexión segura en la nube; La red podría estar fallando",
"errorText": "Error",
"errorUnknownText": "error desconocido",
"exitGameText": "¿Salir de ${APP_NAME}?",
@ -838,6 +841,7 @@
"youHaveText": "tienes ${COUNT} boletos"
},
"googleMultiplayerDiscontinuedText": "Lo siento, Google's multijugador servicio ya no esta mas disponible.\nEstoy trabajando en un reemplazo lo mas rapido posible.\nHasta entonces, por favor intente con otro metodo de conexión.\n-Eric",
"googlePlayPurchasesNotAvailableText": "Las compras de Google Play no están disponibles.\nEs posible que deba actualizar la aplicación de su tienda.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Siempre",
@ -1079,8 +1083,8 @@
"modeArcadeText": "Modo Arcade",
"modeClassicText": "Modo Clásico",
"modeDemoText": "Modo De Demostración",
"mostValuablePlayerText": "Jugador más valorado",
"mostViolatedPlayerText": "Jugador más agredido",
"mostValuablePlayerText": "Jugador más Valorado",
"mostViolatedPlayerText": "Jugador más Violado",
"mostViolentPlayerText": "Jugador más Violento",
"moveText": "Mover",
"multiKillText": "¡¡¡${COUNT}-COMBO!!!",
@ -1178,7 +1182,10 @@
"playlistsText": "Listas de Juego",
"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...",
"pluginsDetectedText": "Nuevos plugin(s) detectados. Activar/configurarlos en los ajustes.",
"pluginClassLoadErrorText": "Error al cargar la clase del plugin '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Error al iniciar el plugin '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Nuevos complemento(s) detectados. Reinicie para activarlos, o configúrelos en la configuración",
"pluginsRemovedText": "${NUM} plugin(s) ya no se encuentran.",
"pluginsText": "Plugins",
"practiceText": "Práctica",
"pressAnyButtonPlayAgainText": "Oprime cualquier botón jugar de nuevo...",
@ -1960,6 +1967,8 @@
"winsPlayerText": "¡${NAME} Gana!",
"winsTeamText": "¡${NAME} Gana!!",
"winsText": "¡${NAME} Gana!",
"workspaceSyncErrorText": "Error al sincronizar ${WORKSPACE}. Mira el registro para mas detalles.",
"workspaceSyncReuseText": "No se puede sincronizar ${WORKSPACE}. Reusando la versión sincronizada anterior.",
"worldScoresUnavailableText": "Puntuaciones globales no disponibles.",
"worldsBestScoresText": "Mejores puntuaciones Mundiales",
"worldsBestTimesText": "Mejores tiempos Mundiales",

View file

@ -324,6 +324,7 @@
"achievementsRemainingText": "மீதமுள்ள சாதனைகள்:",
"achievementsText": "சாதனைகள்",
"achievementsUnavailableForOldSeasonsText": "மன்னிக்கவும், சாதனை விவரங்கள் பழைய பருவங்களுக்கு கிடைக்கவில்லை.",
"activatedText": "${THING} செயல்படுத்தப்பட்டது.",
"addGameWindow": {
"getMoreGamesText": "மேலும் விளையாட்டுகளைப் பெறுங்கள்...",
"titleText": "விளையாட்டைச் சேர்"
@ -623,7 +624,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} காவிய மெதுவான இயக்கத்தில்.",
"epicNameFilterText": "காவியம் ${NAME}",
"errorAccessDeniedText": "அணுகல் மறுக்கப்பட்டது",
"errorDeviceTimeIncorrectText": "உங்கள் சாதனத்தின் நேரம் ${HOURS} மணிநேரம் உள்ளது.\nஇதனால் பிரச்னைகள் ஏற்பட வாய்ப்புள்ளது.\nஉங்கள் நேரம் மற்றும் நேர மண்டல அமைப்புகளைச் சரிபார்க்கவும்.",
"errorOutOfDiskSpaceText": "வட்டு இடத்திற்கு வெளியே",
"errorSecureConnectionFailText": "பாதுகாப்பான கிளவுட் இணைப்பை நிறுவ முடியவில்லை; பிணைய செயல்பாடு தோல்வியடையலாம்.",
"errorText": "பிழை",
"errorUnknownText": "அறியப்படாத பிழை",
"exitGameText": "${APP_NAME} இலிருந்து வெளியேறவா?",
@ -786,7 +789,7 @@
"ticketPack4Text": "ஜம்போ டிக்கெட் பேக்",
"ticketPack5Text": "மம்மத் டிக்கெட் பேக்",
"ticketPack6Text": "அல்டிமேட் டிக்கெட் பேக்",
"ticketsFromASponsorText": "${COUNT} டிக்கெட்டுகளைப் பெறுங்கள்\nஒரு ஸ்பான்சரிடமிருந்து",
"ticketsFromASponsorText": "ஒரு விளம்பரத்தைப் பார்த்து\n${COUNT} டிக்கெட்டுகளை பெறுங்கள்",
"ticketsText": "${COUNT} டிக்கெட்டுகள்",
"titleText": "டிக்கெட்டுகளைப் பெறுங்கள்",
"unavailableLinkAccountText": "மன்னிக்கவும், இந்த தளத்தில் கொள்முதல் கிடைக்கவில்லை.\nஒரு தீர்வாக, இந்தக் கணக்கை ஒரு கணக்குடன் இணைக்கலாம்\nமற்றொரு தளம் மற்றும் அங்கு கொள்முதல் செய்யுங்கள்.",
@ -797,6 +800,7 @@
"youHaveText": "உங்களிடம் ${COUNT} டிக்கெட்டுகள் உள்ளன"
},
"googleMultiplayerDiscontinuedText": "மன்னிக்கவும், கூகுளின் மல்டிபிளேயர் சேவை இனி கிடைக்காது.\nநான் முடிந்தவரை விரைவாக மாற்றுவதற்கு வேலை செய்கிறேன்.\nஅதுவரை, வேறு இணைப்பு முறையை முயற்சிக்கவும்.\n-எரிக்",
"googlePlayPurchasesNotAvailableText": "Google Play வாங்குதல்கள் கிடைக்கவில்லை.\nஉங்கள் ஸ்டோர் பயன்பாட்டைப் புதுப்பிக்க வேண்டியிருக்கலாம்.",
"googlePlayText": "கூகுள் பிளே",
"graphicsSettingsWindow": {
"alwaysText": "எப்போதும்",
@ -1043,7 +1047,7 @@
"noExternalStorageErrorText": "இந்தச் சாதனத்தில் வெளிப்புறச் சேமிப்பு இல்லை",
"noGameCircleText": "பிழை: GameCircle இல் உள்நுழையவில்லை",
"noScoresYetText": "இன்னும் மதிப்பெண்கள் இல்லை.",
"noThanksText": "இல்லை நன்றி",
"noThanksText": "இல்லை, நன்றி!",
"noTournamentsInTestBuildText": "எச்சரிக்கை: இந்த சோதனை உருவாக்கத்தில் இருந்து போட்டியின் மதிப்பெண்கள் புறக்கணிக்கப்படும்.",
"noValidMapsErrorText": "இந்த விளையாட்டு வகைக்கு சரியான வரைபடங்கள் இல்லை.",
"notEnoughPlayersRemainingText": "போதுமான வீரர்கள் மீதமில்லை; வெளியேறி ஒரு புதிய விளையாட்டைத் தொடங்கவும்.",
@ -1108,7 +1112,10 @@
"playlistsText": "பிளேலிஸ்ட்கள்",
"pleaseRateText": "நீங்கள் ${APP_NAME} ஐ அனுபவிக்கிறீர்கள் என்றால், தயவுசெய்து எடுப்பதைக் கவனியுங்கள்\nதருணம் மற்றும் மதிப்பிடுதல் அல்லது விமர்சனம் எழுதுதல். இது வழங்குகிறது\nபயனுள்ள கருத்து மற்றும் எதிர்கால வளர்ச்சிக்கு உதவுகிறது.\n\nநன்றி!\n-எரிக்",
"pleaseWaitText": "தயவுசெய்து காத்திருங்கள்...",
"pluginsDetectedText": "புதிய செருகுநிரல் (கள்) கண்டறியப்பட்டது. அமைப்புகளில் அவற்றை இயக்கவும்/கட்டமைக்கவும்.",
"pluginClassLoadErrorText": "செருகுநிரல் வகுப்பை ஏற்றுவதில் பிழை '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "'${PLUGIN}' செருகுநிரலைத் தொடங்குவதில் பிழை: ${ERROR}",
"pluginsDetectedText": "புதிய செருகுநிரல்(கள்) கண்டறியப்பட்டது. அவற்றைச் செயல்படுத்த மீண்டும் தொடங்கவும் அல்லது அமைப்புகளில் உள்ளமைக்கவும்.",
"pluginsRemovedText": "${NUM} செருகுநிரல்(கள்) இனி காணப்படவில்லை.",
"pluginsText": "செருகுநிரல்கள்",
"practiceText": "பயிற்சி",
"pressAnyButtonPlayAgainText": "மீண்டும் விளையாட எந்த பட்டனையும் அழுத்தவும்...",
@ -1304,7 +1311,7 @@
"iconsText": "சின்னங்கள்",
"loadErrorText": "பக்கத்தை ஏற்ற முடியவில்லை.\nஉங்கள் இணைய இணைப்பைச் சரிபார்க்கவும்.",
"loadingText": "ஏற்றுகிறது",
"mapsText": "Maps",
"mapsText": "வரைபடங்கள்",
"miniGamesText": "மினிகேம்ஸ்",
"oneTimeOnlyText": "(ஒரு முறை மட்டும்)",
"purchaseAlreadyInProgressText": "இந்த பொருளை வாங்குவது ஏற்கனவே நடந்து கொண்டிருக்கிறது.",
@ -1361,11 +1368,11 @@
"tournamentStandingsText": "போட்டி நிலைகள்",
"tournamentText": "போட்டி",
"tournamentTimeExpiredText": "போட்டி நேரம் காலாவதியானது",
"tournamentsText": "Tournaments",
"tournamentsText": "போட்டிகள்",
"translations": {
"characterNames": {
"Agent Johnson": "ஏஜெண்ட் ஜான்சன்",
"B-9000": "B-9000",
"B-9000": "பி-9000",
"Bernard": "பர்னார்ட்",
"Bones": "போன்ஸ்",
"Butch": "பட்ச்",
@ -1526,7 +1533,7 @@
"Thai": "தாய்",
"Turkish": "டர்கிஷ்",
"Ukrainian": "உக்ரைனியன்",
"Venetian": "வெநெடியன்",
"Venetian": "வெனிசியன்",
"Vietnamese": "வியெட்னமீஸ்"
},
"leagueNames": {
@ -1536,7 +1543,7 @@
"Silver": "வெள்ளி"
},
"mapsNames": {
"Big G": "Big G",
"Big G": "பிக் சி",
"Bridgit": "பிரிட்சிட்",
"Courtyard": "கோர்ட்யார்ட்",
"Crag Castle": "கிரக் கோட்டை",
@ -1689,7 +1696,7 @@
},
"tips": {
"A perfectly timed running-jumping-spin-punch can kill in a single hit\nand earn you lifelong respect from your friends.": "ஒரு சரியான நேர ஓட்டம்-ஜம்பிங்-ஸ்பின்-பஞ்ச் ஒரே வெற்றியில் கொல்லலாம்\nமற்றும் உங்கள் நண்பர்களிடமிருந்து உங்களுக்கு வாழ்நாள் முழுவதும் மரியாதை கிடைக்கும்.",
"Always remember to floss.": "எப்போதும் floss செய்ய நினைவில் கொள்ளுங்கள்.",
"Always remember to floss.": "எப்போதும் ஃப்ளோஸ் செய்ய நினைவில் கொள்ளுங்கள்.",
"Create player profiles for yourself and your friends with\nyour preferred names and appearances instead of using random ones.": "உங்களுக்கும் உங்கள் நண்பர்களுக்கும் பிளேயர் சுயவிவரங்களை உருவாக்கவும்\nசீரற்றவற்றைப் பயன்படுத்துவதற்குப் பதிலாக உங்கள் விருப்பமான பெயர்கள் மற்றும் தோற்றங்கள்.",
"Curse boxes turn you into a ticking time bomb.\nThe only cure is to quickly grab a health-pack.": "சாபப் பெட்டிகள் உங்களை ஒரு டிக்கிங் டைம் பாம்டாக மாற்றும்.\nஒரே ஒரு ஹெல்த் பேக் சீக்கிரம் பிடிப்பதுதான்.",
"Despite their looks, all characters' abilities are identical,\nso just pick whichever one you most closely resemble.": "அவற்றின் தோற்றம் இருந்தபோதிலும், அனைத்து கதாபாத்திரங்களின் திறன்களும் ஒரே மாதிரியானவை,\nஎனவே நீங்கள் மிகவும் நெருக்கமாக ஒத்திருப்பதைத் தேர்ந்தெடுக்கவும்.",
@ -1750,7 +1757,7 @@
"phrase08Text": "இப்போது அதிக வேகத்தைப் பெற குதித்து சுழலலாம்.",
"phrase09Text": "ஆ, அது சிறந்தது.",
"phrase10Text": "ஓடுவதும் உதவுகிறது.",
"phrase11Text": "இயக்க எந்த பொத்தானையும் அழுத்திப் பிடிக்கவும்.",
"phrase11Text": "இயக்க எந்த பட்டனையும் அழுத்திப் பிடிக்கவும்.",
"phrase12Text": "கூடுதல் அற்புதமான குத்துகளுக்கு, ஓடவும் சுழலவும் முயற்சிக்கவும்.",
"phrase13Text": "அச்சச்சோ; ${NAME} பற்றி மன்னிக்கவும்.",
"phrase14Text": "கொடிகள் .. அல்லது ${NAME} போன்றவற்றை எடுத்து எறியலாம்.",
@ -1761,11 +1768,11 @@
"phrase19Text": "குதிப்பது உயரத்தை எறிய உதவுகிறது.",
"phrase20Text": "உங்கள் குண்டுகளை இன்னும் நீண்ட தூரத்திற்கு \"சவுக்கடி\".",
"phrase21Text": "உங்கள் குண்டுகளை டைமிங் செய்வது தந்திரமானதாக இருக்கலாம்.",
"phrase22Text": "Dang.",
"phrase22Text": "அச்சச்சோ.",
"phrase23Text": "ஒன்றிரண்டு அல்லது இரண்டு நிமிடங்களுக்கு உருகி \"Cooking off\" முயற்சிக்கவும்.",
"phrase24Text": "ஹூரே! நன்றாக போடப்பட்டது",
"phrase25Text": "சரி, அது பற்றி தான்.",
"phrase26Text": "இப்போது அவர்களைப் போய் புலி!",
"phrase26Text": "இப்போது அவர்களைப் போய் புடி புலியே!",
"phrase27Text": "உங்கள் பயிற்சியை நினைவில் கொள்ளுங்கள், நீங்கள் உயிருடன் திரும்பி வருவீர்கள்!",
"phrase28Text": "...நன்று,இருக்கலாம்...",
"phrase29Text": "நல்ல அதிர்ஷ்டம்!",
@ -1844,6 +1851,8 @@
"winsPlayerText": "${NAME} வெற்றி!",
"winsTeamText": "${NAME} வெற்றி!",
"winsText": "${NAME} வெற்றி!",
"workspaceSyncErrorText": "${WORKSPACE} ஐ ஒத்திசைப்பதில் பிழை. விவரங்களுக்கு பதிவைப் பார்க்கவும்.",
"workspaceSyncReuseText": "${WORKSPACE} ஐ ஒத்திசைக்க முடியவில்லை. முந்தைய ஒத்திசைக்கப்பட்ட பதிப்பை மீண்டும் பயன்படுத்துகிறது.",
"worldScoresUnavailableText": "உலக ஸ்கோர் கிடைக்கவில்லை.",
"worldsBestScoresText": "உலகின் சிறந்த மதிப்பெண்கள்",
"worldsBestTimesText": "உலகின் சிறந்த நேரங்கள்",

View file

@ -216,7 +216,7 @@
},
"Rookie Onslaught Victory": {
"description": "Tüm dalgaları kazan",
"descriptionComplete": "Tüm dalgalar kazanıldı",
"descriptionComplete": "Tüm dalgaları kazan",
"descriptionFull": "${LEVEL} da tüm dalgaları kazan",
"descriptionFullComplete": "${LEVEL} da tüm dalgalar kazanıldı",
"name": "${LEVEL} Galibiyeti"
@ -226,7 +226,7 @@
"descriptionComplete": "2000 puan kazanıldı",
"descriptionFull": "${LEVEL} da 2000 puan kazan",
"descriptionFullComplete": "${LEVEL} da 2000 puan kazanıldı",
"name": "${LEVEL} Tanrısı"
"name": "${LEVEL} Tanrı"
},
"Runaround Master": {
"description": "500 puan kazan",
@ -237,7 +237,7 @@
},
"Runaround Wizard": {
"description": "1000 puan kazan",
"descriptionComplete": "1000 puan kazanıldı",
"descriptionComplete": "1000 puan kazan",
"descriptionFull": "${LEVEL} da 1000 puan kazan",
"descriptionFullComplete": "${LEVEL} da 1000 puan kazanıldı",
"name": "${LEVEL} Büyücüsü"
@ -249,7 +249,7 @@
},
"Stayin' Alive": {
"description": "Hiç ölmeden Kazan",
"descriptionComplete": "Hiç ölmeden kazanıldı",
"descriptionComplete": "Hiç ölmeden kazan",
"descriptionFull": "Hiç ölmeden ${LEVEL} da kazan",
"descriptionFullComplete": "Hiç ölmeden ${LEVEL} da kazanıldı",
"name": "Hayatta Kal"
@ -259,7 +259,7 @@
"descriptionComplete": "Tek yumruk ile %100 hasar verildi",
"descriptionFull": "${LEVEL} da tek yumruk ile %100 hasar ver",
"descriptionFullComplete": "${LEVEL} da tek yumruk ile %100 hasar verildi",
"name": "Super Mega Yumruk"
"name": "Süper Mega Yumruk"
},
"Super Punch": {
"description": "Tek yumruk ile %50 hasar ver",
@ -277,19 +277,19 @@
},
"Team Player": {
"descriptionFull": "4+ oyuncu ile bir Takımlar oyunu başlat",
"descriptionFullComplete": "4+ oyuncu ile bir Takımlar oyunu başlatıldı",
"descriptionFullComplete": "4+ oyuncu ile bir Takım oyunu başlat",
"name": "Takım Oyuncusu"
},
"The Great Wall": {
"description": "Bütün haylazları durdur",
"descriptionComplete": "Bütün haylazlar durduruldu",
"descriptionComplete": "Bütün haylazları durdur",
"descriptionFull": "${LEVEL} da bütün haylazları durdur",
"descriptionFullComplete": "${LEVEL} da bütün haylazlar durduruldu",
"name": "Muhteşem Bariyer"
},
"The Wall": {
"description": "Bütün haylazları durdur",
"descriptionComplete": "Bütün haylazlar durduruldu",
"descriptionComplete": "Bütün haylazları durdur",
"descriptionFull": "${LEVEL} da bütün haylazları durdur",
"descriptionFullComplete": "${LEVEL} da bütün haylazlar durduruldu",
"name": "Bariyer"
@ -320,12 +320,13 @@
"descriptionComplete": "Tüm dalgalar tamamlandı",
"descriptionFull": "${LEVEL} da tüm dalgaları tamamla",
"descriptionFullComplete": "${LEVEL} da tüm dalgalar tamamlandı",
"name": "${LEVEL} Galibiyeti"
"name": "${LEVEL} Galibiyet"
}
},
"achievementsRemainingText": "Kalan Başarılar;",
"achievementsText": "Başarı",
"achievementsUnavailableForOldSeasonsText": "Üzgünüz, başarı detayları eski sezonlar için kullanılamaz.",
"activatedText": "${THING} aktifleştirildi.",
"addGameWindow": {
"getMoreGamesText": "Daha Çok Oyun...",
"titleText": "Oyun Ekle"
@ -353,14 +354,14 @@
"bombBoldText": "BOMBA",
"bombText": "Bomba",
"boostText": "Öne Geç",
"bsRemoteConfigureInAppText": "${REMOTE_APP_NAME} Uygulaması kendisini yapılandırdı",
"bsRemoteConfigureInAppText": "${REMOTE_APP_NAME} Uygulamayı kendisine yapılandır",
"buttonText": "buton",
"canWeDebugText": "Oyun otomatik olarak hataları, çökmeleri ve basit \nkullanım istatistiklerini geliştiriciye gondersin mi?\n\nBu veri içeriği kişisel bilgilerinizi kullanmaz\noyunun daha iyi çalışmasına yardımcı olur.",
"cancelText": "İptal",
"cantConfigureDeviceText": "Üzgünüz, ${DEVICE} ayarlanabilir değil.",
"challengeEndedText": "Bu mücadele sona erdi.",
"chatMuteText": "Konuşmayı sustur",
"chatMutedText": "Konuşma Susturuldu",
"chatMutedText": "Sohbet Susturuldu",
"chatUnMuteText": "Konuşmayı aç",
"choosingPlayerText": "<oyuncu seçme>",
"completeThisLevelToProceedText": "İlerlemek için bu \nseviyeyi tamamlamalısınız!",
@ -438,7 +439,7 @@
"movementControlScaleText": "Hareket Kontrolü Ölçeği",
"movementText": "Hareket",
"resetText": "Sıfırla",
"swipeControlsHiddenText": "Sürme Simgelerini Gizle",
"swipeControlsHiddenText": "Joystick'i gizle",
"swipeInfoText": "\"Sürme\" kontrol stilleri kullanışlıdır fakat\nkontrollere bakmadan oynamak daha kolaydır.",
"swipeText": "sürme",
"titleText": "Dokunmatikleri Yapılandır"
@ -624,7 +625,9 @@
"epicDescriptionFilterText": "${DESCRIPTION} epik ağırçekim.",
"epicNameFilterText": "Epik ${NAME}",
"errorAccessDeniedText": "erişim reddedildi",
"errorDeviceTimeIncorrectText": "Eyvah! Cihazın ile sunucu arasındaki zaman farkı ${HOURS} saat.\nBu bazı sorunlara yol açabilir.\nLütfen saat ve saat dilimi ayarlarını kontrol et.",
"errorOutOfDiskSpaceText": "disk alanı doldu",
"errorSecureConnectionFailText": "Güvenli bulut bağlantısı kurulamadı; ağ işlevi başarısız olabilir.",
"errorText": "Hata",
"errorUnknownText": "bilinmeyen hata",
"exitGameText": ık ${APP_NAME}?",
@ -787,7 +790,7 @@
"ticketPack4Text": "Dev Bilet Paketi",
"ticketPack5Text": "Muazzam Bilet Paketi",
"ticketPack6Text": "En Üst Düzey Bilet Paketi",
"ticketsFromASponsorText": "Bir sponsordan\n${COUNT} Bilet Al",
"ticketsFromASponsorText": "${COUNT} Bilet için\nbir reklam izle",
"ticketsText": "${COUNT} Bilet",
"titleText": "Bilet Al",
"unavailableLinkAccountText": "Üzgünüz, Satın almalar bu patformda kullanılamaz.\nBu geçici oldugu gibi, bu hesabını diğer platformlardaki\nhesablara bağlayabilir oradan satın alma işlemi yapbilirsin.",
@ -798,6 +801,7 @@
"youHaveText": "${COUNT} biletin var"
},
"googleMultiplayerDiscontinuedText": "Üzgünüz, Google'ın çok oyunculu servisi şu anda çalışmıyor.\nBir yer değişimi için olabildiğince hızlı çalışıyorum.\nO zamana kadar, lütfen başka bir bağlantı yöntemi deneyin.\n-Eric",
"googlePlayPurchasesNotAvailableText": "Google Play satın alma işlemleri mevcut değildir.\nMağaza uygulamanızı güncellemeniz gerekebilir.",
"googlePlayText": "Google Play",
"graphicsSettingsWindow": {
"alwaysText": "Her Zaman",
@ -1108,7 +1112,10 @@
"playlistsText": "ÇalmaListesi",
"pleaseRateText": "Eğer ${APP_NAME}'dan zevk aldıysan, lütfen oyunu değerlerdir\nve yorumunu yaz. Bu oyunun daha fazla geliştirilmesine\nyardımcı olur.\n\nteşekkürler!\n-eric",
"pleaseWaitText": "Lütfen bekle...",
"pluginsDetectedText": "Yeni eklenti(ler) tespit edildi. Bunları ayarlardan etkinleştirin / yapılandırın.",
"pluginClassLoadErrorText": "'${PLUGIN}' eklenti sınıfı yüklenirken hata oluştu: ${ERROR}",
"pluginInitErrorText": "'${PLUGIN}' eklentisi başlatılırken hata oluştu: ${ERROR}",
"pluginsDetectedText": "Yeni eklentiler tespit edildi. Onları etkinleştirmek veya ayarlarda yapılandırmak için oyunu yeniden başlatın.",
"pluginsRemovedText": "${NUM} eklenti(ler) artık bulunmuyor.",
"pluginsText": "Eklentiler",
"practiceText": "Alıştırma",
"pressAnyButtonPlayAgainText": "Tekrar oynamak için bir tuşa basın...",
@ -1844,6 +1851,8 @@
"winsPlayerText": "${NAME} Kazandı!",
"winsTeamText": "${NAME} Kazandı!",
"winsText": "${NAME} Kazandı!",
"workspaceSyncErrorText": "${WORKSPACE} eşitlemesinde hata oluştu. Detaylar için günlüğü inceleyin.",
"workspaceSyncReuseText": "${WORKSPACE} eşitlenemiyor. Önceden eşitlenmiş sürüm kullanılıyor.",
"worldScoresUnavailableText": "Global Skorlar Mevcut Değil.",
"worldsBestScoresText": "Global En-İyi Skorlar",
"worldsBestTimesText": "Global En-İyi Süreler",

View file

@ -5,27 +5,27 @@
"achievementProgressText": "Obietivi: ${COUNT} de ${TOTAL}",
"campaignProgressText": "Progreso canpagna [Defìsiłe]: ${PROGRESS}",
"changeOncePerSeason": "A te połi canbiar sto dato soło na volta par stajon.",
"changeOncePerSeasonError": "Par modifegar (${NUM} days) A te ghè da spetar ła stajon pròsema.",
"changeOncePerSeasonError": "Par canbiarlo te ghè da spetar ła pròsema stajon (${NUM} days).",
"customName": "Nome parsonałizà",
"linkAccountsEnterCodeText": "Insarisi còdaze",
"linkAccountsGenerateCodeText": "Jènara còdaze",
"linkAccountsInfoText": "(sparpagna progresi infrà dispozitivi defarenti)",
"linkAccountsInstructionsNewText": "Par cołegar do account, jènara un còdaze inte'l primo\ndispozitivo e insarìseło intel segondo. I dati del\nsegondo account i vegnarà sparpagnài so tuti do i account\n(i dati del primo account i ndarà perdesti).\n\nA te połi cołegar fin a ${COUNT} account.\n\nINPORTANTE: cołega soło i account de to propiedà.\nSe A te cołeghi account de calche to amigo, A no sarè\npì boni de zugar online intel mèdemo momento.",
"linkAccountsInfoText": "(sparpagna progresi infrà dispozidivi defarenti)",
"linkAccountsInstructionsNewText": "Par cołegar do account, jènara un còdaze inte'l primo\ndispozidivo e insarìseło intel segondo. I dati del\nsegondo account i vegnarà sparpagnài so tuti do i account\n(i dati del primo account i ndarà perdesti).\n\nTe połi cołegar fin a ${COUNT} account.\n\nINPORTANTE: cołega soło i account de to propiedà.\nSe te cołeghi account de calche to amigo, no sarè\npì boni de zugar online intel mèdemo momento.",
"linkAccountsText": "Cołega account",
"linkedAccountsText": "Account cołegài:",
"nameChangeConfirm": "Vutu canbiar el nome del to account co ${NAME}?",
"resetProgressConfirmNoAchievementsText": "A te si drio ełimenar i to progresi so ła modałidà\ncooparadiva e i to punteji łogałi (ma miga i to biłieti).\nSta asion no ła połe mìa èsar anułada. Vutu ndar vanti?",
"resetProgressConfirmText": "A te si drio ełimenar i to progresi so ła\nmodałidà cooparadiva, i to obietivi e i to punteji\nłogałi (ma miga i to biłieti). Sta asion\nno ła połe mìa èsar anułada. Vutu ndar vanti?",
"resetProgressConfirmNoAchievementsText": "Te si drio ełimenar i to progresi so ła modałidà\ncooparadiva e i to punteji łogałi (ma miga i to biłieti).\nSta asion no ła połe èsar anułada. Vutu ndar vanti?",
"resetProgressConfirmText": "Te si drio ełimenar i to progresi so ła\nmodałidà cooparadiva, i to obietivi e i to punteji\nłogałi (ma miga i to biłieti). Sta asion\nno ła połe èsar anułada. Vutu ndar vanti?",
"resetProgressText": "Ełìmena progresi",
"setAccountName": "Inposta un nome utente",
"setAccountNameDesc": "Sełesiona el nome da vizuałizar sol to account.\nA te połi doparar el nome da uno de i to account\ncołegài o crear un nome parsonałizà ma ùnivogo.",
"signInInfoText": "Conétate par tirar sù biłieti, batajar online e\nsparpagnar i to progresi infrà dispozitivi defarenti.",
"signInInfoText": "Conétate par tirar sù biłieti, batajar online e\nsparpagnar i to progresi infrà dispozidivi defarenti.",
"signInText": "Conétate",
"signInWithDeviceInfoText": "(par 'sto dispozitivo A ze disponìbiłe un soło account automàtego)",
"signInWithDeviceText": "Conétate co l'account del dispozitivo",
"signInWithDeviceInfoText": "(par 'sto dispozidivo ze disponìbiłe un soło account automàtego)",
"signInWithDeviceText": "Conétate co l'account del dispozidivo",
"signInWithGameCircleText": "Conétate co Game Circle",
"signInWithGooglePlayText": "Conétate co Google Play",
"signInWithTestAccountInfoText": "(account de proa vecio: in fuduro dòpara i account del dispozitivo)",
"signInWithTestAccountInfoText": "(account de proa vecio: in fuduro dòpara i account del dispozidivo)",
"signInWithTestAccountText": "Conétate co un account de proa",
"signInWithV2InfoText": "(un account che fusiona so tute łe piataforme)",
"signInWithV2Text": "Acedi co un account BombSquad",
@ -38,7 +38,7 @@
"unlinkAccountsText": "Descołega account",
"v2LinkInstructionsText": "Acedi o dòpara 'sto link par crear un account.",
"viaAccount": "(doparando ${NAME})",
"youAreSignedInAsText": "A te zugarè cofà:"
"youAreSignedInAsText": "Te zugarè cofà:"
},
"achievementChallengesText": "Obietivi sfide",
"achievementText": "Obietivo",
@ -323,14 +323,15 @@
},
"achievementsRemainingText": "Obietivi restanti:",
"achievementsText": "Obietivi",
"achievementsUnavailableForOldSeasonsText": "Ne despiaze, i obietivi de łe stajon pasàe no i ze mìa disponìbiłe.",
"achievementsUnavailableForOldSeasonsText": "Ne despiaze, i obietivi de łe stajon pasàe no i ze pì disponìbiłi.",
"activatedText": "${THING} ativà.",
"addGameWindow": {
"getMoreGamesText": "Otien pì łevełi...",
"titleText": "Zonta zugo"
},
"allowText": "Parmeti",
"alreadySignedInText": "El to account el ze in dòparo inte nantro\ndispozitivo: canbia account o sara sù el zugo\ninte chełaltro to dispozitivo e proa danovo.",
"apiVersionErrorText": "A no ze mìa posìbiłe cargar el mòduło ${NAME}, el se refarise a ła varsion ${VERSION_USED}. A serve invese ła ${VERSION_REQUIRED}.",
"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.",
"apiVersionErrorText": "Inposìbiłe cargar el mòduło ${NAME}, el se refarise a ła varsion ${VERSION_USED}. Serve invese ła ${VERSION_REQUIRED}.",
"audioSettingsWindow": {
"headRelativeVRAudioInfoText": "(Ativa \"Auto\" soło co A te tachi sù łe fonarołe par ła realtà virtuałe)",
"headRelativeVRAudioText": "Àudio par fonarołe VR",
@ -354,19 +355,19 @@
"buttonText": "boton",
"canWeDebugText": "Ghetu caro che BombSquad el reporte in automàtego bai,\nblochi e informasion baze só'l so dòparo a'l dezviłupador?\n\n'Sti dati no i contien miga informasion parsonałi ma i ło\njuta a far ndar ben el zugo sensa blochi o bai.",
"cancelText": "Anuła",
"cantConfigureDeviceText": "Ne despiaze, ${DEVICE} no'l ze mìa configuràbiłe.",
"cantConfigureDeviceText": "Ne despiaze, ${DEVICE} no'l ze miga configuràbiłe.",
"challengeEndedText": "'Sta sfida ła ze fenìa.",
"chatMuteText": "Siłensia ciacołada",
"chatMutedText": "Ciacołada siłensiada",
"chatUnMuteText": "Reativa son",
"choosingPlayerText": "<sernisi zugador>",
"completeThisLevelToProceedText": "A te ghè da conpletar 'sto\nłeveło par ndar vanti!",
"completeThisLevelToProceedText": "Par ndar vanti te ghè\nda conpletar 'sto łeveło!",
"completionBonusText": "Premio de concruzion",
"configControllersWindow": {
"configureControllersText": "Configura controładori",
"configureKeyboard2Text": "Configura botonera P2",
"configureKeyboardText": "Configura botonera",
"configureMobileText": "Dòpara dispozitivi cofà controładori",
"configureMobileText": "Dòpara dispozidivi cofà controładori",
"configureTouchText": "Configura schermo tàtiłe",
"ps3Text": "Controładori PS3",
"titleText": "Controładori",
@ -374,7 +375,7 @@
"xbox360Text": "Controładori Xbox 360"
},
"configGamepadSelectWindow": {
"androidNoteText": "Nota: ła conpatibiłidà par i controładori ła muda drio dispozitivo e varsion de Android.",
"androidNoteText": "Nota: ła conpatibiłidà par i controładori ła muda drio dispozidivo e varsion de Android.",
"pressAnyButtonText": "Struca un boton calsìase de'l controłador\nche A te vołi configurar...",
"titleText": "Configura controładori"
},
@ -418,13 +419,13 @@
"twoInOneSetupText": "Configurasion controłador 2 so 1",
"uiOnlyDescriptionText": "(par evitar che 'sto controłador el posa zontarse inte na partìa)",
"uiOnlyText": "Łìmida el dòparo de'l menù",
"unassignedButtonsRunText": "Tuti i botoni mìa defenìi i fà córar",
"unsetText": "<mìa defenìo>",
"unassignedButtonsRunText": "Tuti i botoni miga defenìi i fà córar",
"unsetText": "<miga defenìo>",
"vrReorientButtonText": "Boton de repozision VR"
},
"configKeyboardWindow": {
"configuringText": "Configurasion ${DEVICE}",
"keyboard2NoteText": "Nota: ła parte pì granda de łe botonere łe połe rejistrar\nsoło un fià de comandi a'l colpo. Donca, par zugar in du,\nA se ndarìa mejo doparar do botonere defarenti. Tien daconto\nche A te gavarè da defenir in tuti i cazi botoni unìvoghi\npar i tuti do i zugadori."
"keyboard2NoteText": "Nota: ła parte pì granda de łe botonere łe połe rejistrar\nsoło un fià de comandi a'l colpo. Donca, par zugar in du,\nse ndarìa mejo doparar do botonere defarenti. Tien daconto\nche te gavarè da defenir in tuti i cazi botoni unìvoghi\npar i tuti do i zugadori."
},
"configTouchscreenWindow": {
"actionControlScaleText": "Grandesa controło",
@ -445,13 +446,13 @@
"connectMobileDevicesWindow": {
"amazonText": "Amazon Store",
"appStoreText": "App Store",
"bestResultsText": "Par zugar co rezultài pì boni A te serve na rede wifi ràpida.\nA te połi redùzar el retardo (lag) de ła rede zugando visin al\ntrazmetidor del segnałe, desconetendo altri dispozitivi sensa\nfiło o conetendo lospitador del zugo co un cavo ethernet.",
"explanationText": "Par doparar un tełèfono o un tołeto cofà un controłador sensa fiło,\ninstàłaghe rento lapl \"${REMOTE_APP_NAME}\". Par zugar a ${APP_NAME}\nA te połi conétar col wifi tuti i dispozitivi che A te vołi! A ze gratis!",
"bestResultsText": "Par zugar co rezultài pì boni te serve na rede wifi ràpida.\nTe połi redùzar el retardo (lag) de ła rede zugando visin al\ntrazmetidor del segnałe, desconetendo altri dispozidivi sensa\nfiło o conetendo lospitador del zugo co un cavo ethernet.",
"explanationText": "Par doparar un tełèfono o un tołeto cofà un controłador sensa fiło,\ninstàłaghe rento lapl \"${REMOTE_APP_NAME}\". Par zugar a ${APP_NAME}\nte połi conétar col wifi tuti i dispozidivi che te vołi! Ze agratis!",
"forAndroidText": "par Android:",
"forIOSText": "par iOS:",
"getItForText": "Descarga ${REMOTE_APP_NAME} par iOS so l'AppStore de\nApple o par Android so'l Store de Google Play o l'Amazon Store",
"googlePlayText": "Google Play",
"titleText": "Doparar dispozitivi mòbiłi cofà controładori:"
"titleText": "Doparar dispozidivi mòbiłi cofà controładori:"
},
"continuePurchaseText": "Vutu ndar vanti par ${PRICE}?",
"continueText": "Sì!",
@ -466,7 +467,7 @@
"currentBestText": "El mejo par deso",
"customText": "E in pì...",
"entryFeeText": "Costo",
"forfeitConfirmText": "Vutu dalvero dàrgheła vinta?",
"forfeitConfirmText": "Vutu dabon dàrgheła vinta?",
"forfeitNotAllowedYetText": "Deso A no te połi miga dàrgheła vinta suito.",
"forfeitText": "Dàgheła vinta",
"multipliersText": "Moltiplegadori",
@ -533,7 +534,7 @@
"stressTestRoundDurationText": "Durada turno",
"stressTestTitleText": "Proa soto sforso",
"titleText": "Prestasion e Proe soto sforso",
"totalReloadTimeText": "Tenpo totałe recargamento: ${TIME} (par i detaji varda el rejistro)"
"totalReloadTimeText": "Tenpo totałe recargamento: ${TIME} (par i detaji varda el rejistro eventi)"
},
"defaultGameListNameText": "Łista de zugo \"${PLAYMODE}\" predefenìa",
"defaultNewGameListNameText": "Ła me łista de zugo ${PLAYMODE}",
@ -623,7 +624,7 @@
"errorAccessDeniedText": "aceso refudà",
"errorOutOfDiskSpaceText": "spasio so'l disco fenìo",
"errorText": "Eror",
"errorUnknownText": "eror mìa conosesto",
"errorUnknownText": "eror miga conosesto",
"exitGameText": "Vutu ndar fora da ${APP_NAME}?",
"exportSuccessText": "'${NAME}' esportà.",
"externalStorageText": "Memoria esterna",
@ -646,7 +647,7 @@
"fiveKillText": "BEN 5 SASINÀI!!!!",
"flawlessWaveText": "Ondada parfeta!",
"fourKillText": "EŁIMENÀI: 4!!!",
"friendScoresUnavailableText": "Punteji de i amighi mìa disponìbiłi.",
"friendScoresUnavailableText": "Punteji de i amighi miga disponìbiłi.",
"gameCenterText": "GameCenter",
"gameCircleText": "GameCircle",
"gameLeadersText": "Clasìfega de'l łeveło ${COUNT}",
@ -668,14 +669,14 @@
},
"gamesToText": "${WINCOUNT} a ${LOSECOUNT}",
"gatherWindow": {
"aboutDescriptionLocalMultiplayerExtraText": "Recòrdate: se A te ghè bastansa controładori, caun\ndispozitivo de un grupo el połe ospitar pì zugadori.",
"aboutDescriptionText": "Dòpara ste sesion par far sù un grupo.\n\nI grupi i te parmete de zugar partìe e tornèi\nco i to amighi infrà i vari dispozitivi.\n\nDòpara el boton ${PARTY} insima a drita par\nciacołar e interajir col to grupo.\n(so un controłador, struca ${BUTTON} co A te si inte un menù)",
"aboutDescriptionLocalMultiplayerExtraText": "Recòrdate: se te ghè bastansa controładori, tuti i\ndispozidivi de un grupo i połe ospitar pì zugadori.",
"aboutDescriptionText": "Dòpara 'ste sesion par far sù un grupo.\n\nI grupi i te parmete de zugar partìe e tornèi\nco i to amighi infrà i vari dispozidivi.\n\nDòpara el boton ${PARTY} insima a drita par\nciacołar e interajir col to grupo.\n(so un controłador, struca ${BUTTON} co te si inte un menù)",
"aboutText": "Info",
"addressFetchErrorText": "<eror rancurando sù i ndarisi>",
"appInviteMessageText": "L'utente ${NAME} el te ga mandà ${COUNT} biłieti so ${APP_NAME}",
"appInviteSendACodeText": "Màndaghe un còdaze",
"appInviteTitleText": "Invido a proar ${APP_NAME}",
"bluetoothAndroidSupportText": "(el funsiona so tuti i dispozitivi Android co el bluetooth)",
"bluetoothAndroidSupportText": "(el funsiona so tuti i dispozidivi Android co el bluetooth)",
"bluetoothDescriptionText": "Òspita/zóntate so un grupo co'l bluetooth:",
"bluetoothHostText": "Òspita",
"bluetoothJoinText": "Zóntate",
@ -708,8 +709,8 @@
"googlePlayText": "Google Play",
"googlePlayVersionOnlyText": "(soło inte ła varsion Android / Google Play)",
"hostPublicPartyDescriptionText": "Òspita un grupo pùblego",
"hostingUnavailableText": "Ospitamento mìa disponìbiłe",
"inDevelopmentWarningText": "Nota:\n\nEl zugo in rede ła ze na funsion nova e in dezviłupo.\nPar deso, A ze dalvero racomandà che tuti i\nzugadori i sie tuti so ła mèdema rede wifi.",
"hostingUnavailableText": "Ospitamento miga disponìbiłe",
"inDevelopmentWarningText": "Nota:\n\nEl zugo in rede ła ze na funsion nova e in dezviłupo.\nPar deso, ze dabon racomandà che tuti i\nzugadori i sipie tuti so ła mèdema rede wifi.",
"internetText": "Internet",
"inviteAFriendText": "I to amighi zełi sensa zugo? Invìdełi a\nproarlo e i resevarà ${COUNT} biłieti gratùidi.",
"inviteFriendsText": "Invida amighi",
@ -734,7 +735,7 @@
"otherVersionsText": "(par altre varsion)",
"partyCodeText": "Còdaze de'l grupo",
"partyInviteAcceptText": "Và ben",
"partyInviteDeclineText": "Mìa deso",
"partyInviteDeclineText": "Miga deso",
"partyInviteGooglePlayExtraText": "(varda el paneło 'Google Play' inte ła fenestra 'Crea grupo')",
"partyInviteIgnoreText": "Ignora",
"partyInviteText": "A te ze rivà un invido de ${NAME}\npar zontarte inte'l só grupo!",
@ -744,7 +745,7 @@
"partyStatusCheckingText": "Varìfega condision...",
"partyStatusJoinableText": "deso el to grupo el połe èsar catà da internet",
"partyStatusNoConnectionText": "A no ze miga posìbiłe conétarse a'l server",
"partyStatusNotJoinableText": "el to grupo no'l połe mìa èsar catà da internet",
"partyStatusNotJoinableText": "el to grupo no'l połe miga èsar catà da internet",
"partyStatusNotPublicText": "el to grupo no'l ze miga pùblego",
"pingText": "łatensa",
"portText": "Porta",
@ -763,12 +764,12 @@
"startStopHostingMinutesText": "Te połi tacar o fermar de ospitar grupi łìbaramente par n'antri ${MINUTES} menuti.",
"stopHostingText": "Ferma ospitamento",
"titleText": "Crea grupo",
"wifiDirectDescriptionBottomText": "Se tuti i dispozitivi i gà ła sesion 'Wifi direto', i sarà boni de dopararlo par\ncatarse e conétarse infrà de łori. Na volta che tuti i dispozitivi i sarà conetesti,\nA te podarè crear grupi inte ła sesion 'Rede łogałe', cofà ła fuse na rede wifi normałe.\n\nPar rezultài pì boni, mejo che lospitador del wifi direto el sipie lòspite anca del grupo so ${APP_NAME}.",
"wifiDirectDescriptionTopText": "El Wifi direto el połe èsar doparà par conétar diretamente dispozitivi Android\nsensa pasar par ła rede wifi. El funsiona mejo so varsion Android 4.2 o pì resenti.\n\nPar dopararlo, verzi łe inpostasion wifi e controła ła funsion 'Wifi direto'.",
"wifiDirectDescriptionBottomText": "Se tuti i dispozidivi i gà ła sesion 'Wifi direto', i sarà boni de dopararlo par\ncatarse e conétarse infrà de łori. Na volta che tuti i dispozidivi i sarà conetesti,\nte podarè crear grupi inte ła sesion 'Rede łogałe', cofà ła fuse na rede wifi normałe.\n\nPar rezultài pì boni, mejo che lospitador del wifi direto el sipie lòspite anca del grupo so ${APP_NAME}.",
"wifiDirectDescriptionTopText": "El Wifi direto el połe èsar doparà par conétar diretamente dispozidivi Android\nsensa pasar par ła rede wifi. El funsiona mejo so varsion Android 4.2 o pì resenti.\n\nPar dopararlo, verzi łe inpostasion wifi e controła ła funsion 'Wifi direto'.",
"wifiDirectOpenWiFiSettingsText": "Verzi inpostasion wifi",
"wifiDirectText": "Wifi direto",
"worksBetweenAllPlatformsText": "(el funsiona intrà tute łe piataforme)",
"worksWithGooglePlayDevicesText": "(el funsiona co tuti i dispozitivi co ła varsion del zugo de Google Play par Android)",
"worksWithGooglePlayDevicesText": "(el funsiona co tuti i dispozidivi co ła varsion del zugo de Google Play par Android)",
"youHaveBeenSentAPromoCodeText": "A te ze stà mandà un còdaze promosionałe de ${APP_NAME}:"
},
"getTicketsWindow": {
@ -788,8 +789,8 @@
"ticketsText": "${COUNT} biłieti",
"titleText": "Otien biłieti",
"unavailableLinkAccountText": "Ne despiaze, A no se połe miga cronpar so 'sta piataforma.\nVołendo, cofà sołusion, A te połi cołegar 'sto account co\nuno inte n'antra piataforma e cronpar calcosa da łà.",
"unavailableTemporarilyText": "'Sta funsion no ła ze mìa disponìbiłe par deso: proa danovo pì tardi.",
"unavailableText": "Ne despiaze, 'sta funsion no ła ze mìa disponìbiłe.",
"unavailableTemporarilyText": "'Sta funsion no ła ze miga disponìbiłe par deso: proa danovo pì tardi.",
"unavailableText": "Ne despiaze, 'sta funsion no ła ze miga disponìbiłe.",
"versionTooOldText": "Ne despiaze, 'sta varsion ła ze masa vecia: ajorna el zugo co cheła nova.",
"youHaveShortText": "A te ghè ${COUNT}",
"youHaveText": "A te ghè ${COUNT} biłieti"
@ -817,13 +818,13 @@
"helpWindow": {
"bombInfoText": "- Bonbe -\nPì forti de i crogni, ma łe połe\nfarte małe anca a ti. Dopàrełe\nben tràndoghełe doso a i nemighi\nprima che łe salte par aria.",
"canHelpText": "${APP_NAME} el połe jutarte!",
"controllersInfoText": "A te połi zugar a ${APP_NAME} co i amighi co na rede o, se gavì\ncontroładori che basta, połì zugar tuti insenbre so el mèdemo\ndispozitivo: ${APP_NAME} el ghin suporta racuanti. Połì senpre doparar\ni tełèfoni cofà controładori co lapl gratùida '${REMOTE_APP_NAME}'.\nPar info in pì varda so Inpostasion > Controładori.",
"controllersInfoTextRemoteOnly": "Te połi zugar a ${APP_NAME} co i to amighi doparando na rede,\no zugar tuti so'l mèdemo dispozitivo doparando i tełèfoni cofà\ncontroładori co l'apl gratùida '${REMOTE_APP_NAME}'.",
"controllersInfoText": "Te połi zugar a ${APP_NAME} co i amighi co na rede o, se gavì\ncontroładori che basta, połì zugar tuti insenbre so el mèdemo\ndispozidivo: ${APP_NAME} el ghin suporta racuanti. Połì senpre doparar\ni tełèfoni cofà controładori co lapl gratùida '${REMOTE_APP_NAME}'.\nPar info in pì varda so Inpostasion > Controładori.",
"controllersInfoTextRemoteOnly": "Te połi zugar a ${APP_NAME} co i to amighi doparando na rede,\no zugar tuti so'l mèdemo dispozidivo doparando i tełèfoni cofà\ncontroładori co l'apl gratùida '${REMOTE_APP_NAME}'.",
"controllersText": "Controładori",
"controlsSubtitleText": "El to amighévołe parsonajo de ${APP_NAME} el gà un fià de funsion de baze:",
"controlsText": "Controłi",
"devicesInfoText": "Ła varsion VR de ${APP_NAME} ła połe èsar zugada so na rede anca\nco ła varsion normałe de l'app, donca tira fora tełèfoni, tołeti\ne computers e taca a zugar! Podarìa èsar ùtiłe conétar na varsion\nnormałe de'l zugo a cheła VR anca soło par parmétarghe a łe parsone\nde poder ndarghe drio a'l zugo da fora.",
"devicesText": "Dispozitivi",
"devicesText": "Dispozidivi",
"friendsGoodText": "A ze senpre bona roba vèrghene. Se se ła gode de pì co pì zugadori\ne ${APP_NAME} el ghin suporta fin a 8, e 'sta roba ła ne mena a:",
"friendsText": "Amighi",
"jumpInfoText": "- Salto -\nSalta par traversar buzi cełi,\npar trar robe pì alte, o par\nfar védar tuta ła to ałegresa.",
@ -858,18 +859,18 @@
},
"holdAnyButtonText": "<tien strucà un boton calsìase>",
"holdAnyKeyText": "<tien strucà un boton calsìase>",
"hostIsNavigatingMenusText": "- ${HOST} l'è drio navegar intrà i menù a zbregabałon! -",
"hostIsNavigatingMenusText": "- ${HOST} ze drio navegar intrà i menù a zbregabałon! -",
"importPlaylistCodeInstructionsText": "Dòpara 'sto còdaze par inportar 'sta łista de zugo ndove che te vołi:",
"importPlaylistSuccessText": "Łista de zugo '${NAME}' a ${TYPE} inportada",
"importText": "Inporta",
"importingText": "Inportasion...",
"inGameClippedNameText": "rento el zugo:\n\"${NAME}\"",
"installDiskSpaceErrorText": "EROR: A no ze mìa posìbiłe fenir linstałasion.\nEl to dispozitivo el podarìa èsar sensa spasio.\nŁìbara un fià de memoria e proa danovo.",
"installDiskSpaceErrorText": "EROR: inposìbiłe fenir linstałasion.\nEl to dispozidivo el podarìa èsar sensa spasio.\nŁìbara un fià de memoria e proa danovo.",
"internal": {
"arrowsToExitListText": "struca ${LEFT} o ${RIGHT} par ndar fora da ła serie",
"buttonText": "boton",
"cantKickHostError": "A no te połi miga parar vìa l'ospitador.",
"chatBlockedText": "${NAME} l'è stà tajà fora da ła chat par ${TIME} segondi.",
"chatBlockedText": "${NAME} ze stà tajà fora da ła chat par ${TIME} segondi.",
"connectedToGameText": "A te te si zontà so '${NAME}'",
"connectedToPartyText": "A te te si zontà so'l grupo de ${NAME}!",
"connectingToPartyText": "Conesion...",
@ -890,10 +891,10 @@
"corruptFileText": "A ze stà catài fora file coronpesti. Proa reinstałar el zugo o manda na mail a: ${EMAIL}",
"errorPlayingMusicText": "Eror de reprodusion muzegałe: ${MUSIC}",
"errorResettingAchievementsText": "A no ze miga posìbiłe reinpostar i obietivi in łinea. Proa danovo pì tardi.",
"hasMenuControlText": "${NAME} l'à el controło de'l menù",
"hasMenuControlText": "${NAME} gà el controło de'l menù",
"incompatibleNewerVersionHostText": "L'ospitador el gà na varsion de'l zugo pì resente.\nAjórneło anca ti a ła varsion ùltema e proa danovo.",
"incompatibleVersionHostText": "L'ospitador el gà na varsion de'l zugo defarente.\nSegùreve de ver tuti do l'apl ajornada e provè danovo.",
"incompatibleVersionPlayerText": "${NAME} l'à na varsion de'l zugo defarente.\nSegùreve de ver tuti do l'apl ajornada e provè danovo.",
"incompatibleVersionPlayerText": "${NAME} gà na varsion de'l zugo defarente.\nSegùreve de ver tuti do l'apl ajornada e provè danovo.",
"invalidAddressErrorText": "Eror: ndariso miga vàłido.",
"invalidNameErrorText": "Eror: nome miga vàłido.",
"invalidPortErrorText": "Eror: porta miga vàłida.",
@ -901,20 +902,20 @@
"invitationsSentText": "Mandài ${COUNT} invidi.",
"joinedPartyInstructionsText": "Calchedun el se gà zontà inte'l to grupo.\nVà so \"Zuga\" par tacar na partìa.",
"keyboardText": "Botonera",
"kickIdlePlayersKickedText": "A ze stà parà vìa ${NAME} par masa sonera.",
"kickIdlePlayersWarning1Text": "Se ła só sonera ła sèvita, ${NAME} l'vegnarà parà vìa tenpo ${COUNT} segondi.",
"kickIdlePlayersKickedText": "Ze stà parà fora ${NAME} par masa sonera.",
"kickIdlePlayersWarning1Text": "Se ła só sonera ła sèvita, ${NAME} vegnarà parà fora tenpo ${COUNT} segondi.",
"kickIdlePlayersWarning2Text": "(A te połi dezativarlo so Inpostasion > Avansàe)",
"leftGameText": "A te si ndà fora da '${NAME}'.",
"leftPartyText": "A te si ndà fora da'l grupo de ${NAME}.",
"noMusicFilesInFolderText": "Ła carteła no ła gà rento gnaun file muzegałe.",
"playerJoinedPartyText": "${NAME} l'se gà zontà inte'l grupo!",
"playerLeftPartyText": "${NAME} l'è ndà fora da'l grupo!",
"playerLeftPartyText": "${NAME} gà mołà el grupo!",
"rejectingInviteAlreadyInPartyText": "Invido refudà (dezà inte un grupo).",
"serverRestartingText": "El server el ze drio retacarse. Reconétate infrà na scianta...",
"serverShuttingDownText": "El server el ze drio stuarse...",
"signInErrorText": "Eror in entrada.",
"signInNoConnectionText": "A no ze mìa posìbiłe ndar rento. (gnauna conesion a internet?)",
"telnetAccessDeniedText": "EROR: l'utente no'l gà mìa parmeso l'aceso co telnet.",
"signInNoConnectionText": "Inposìbiłe acédar. (sensa conesion internet?)",
"telnetAccessDeniedText": "EROR: l'utente no'l gà miga parmeso l'aceso co telnet.",
"timeOutText": "(tocarà a ti tenpo ${TIME} segondi)",
"touchScreenJoinWarningText": "A te te si zontà co'l touchscreen.\nSe ła ze stà na capeła, struca 'Menù > Moła łeveło'.",
"touchScreenText": "TouchScreen",
@ -931,9 +932,9 @@
"keyboardChangeInstructionsText": "Struca spasio do 'olte par mudar botonera.",
"keyboardNoOthersAvailableText": "A no ghe ze miga altre botonere disponìbiłi.",
"keyboardSwitchText": "Pasajo a ła botonera \"${NAME}\".",
"kickOccurredText": "${NAME} l'è stà parà vìa.",
"kickOccurredText": "${NAME} ze stà parà fora.",
"kickQuestionText": "Vutu parar vìa ${NAME}?",
"kickText": "Para a",
"kickText": "Para fora",
"kickVoteCantKickAdminsText": "I aministradori no i połe mìa èsar parài vìa.",
"kickVoteCantKickSelfText": "A no te połi mìa pararte vìa da soło.",
"kickVoteFailedNotEnoughVotersText": "A ghe ze masa pochi zugadori par na votasion.",
@ -996,8 +997,8 @@
"howToPlayText": "Come zugar",
"justPlayerText": "(Soło par ${NAME})",
"leaveGameText": "Moła łeveło",
"leavePartyConfirmText": "Vutu dalvero ndar fora da'l grupo?",
"leavePartyText": "Va fora da'l grupo",
"leavePartyConfirmText": "Vutu dabon ndar fora da'l grupo?",
"leavePartyText": "Moła grupo",
"quitText": "Sortisi",
"resumeText": "Continua",
"settingsText": "Inpostasion"
@ -1020,9 +1021,9 @@
"multiKillText": "CAENA DE ${COUNT} FATI FORA!!!!!",
"multiPlayerCountText": "${COUNT} zugadori",
"mustInviteFriendsText": "Nota: A te ghè da invidar i amighi\nso'l paneło \"${GATHER}\" o picar pì\ncontroładori par zugar in multizugador.",
"nameBetrayedText": "${NAME} l'à tradìo ${VICTIM}.",
"nameDiedText": "${NAME} l'è crepà.",
"nameKilledText": "${NAME} l'à copà ${VICTIM}.",
"nameBetrayedText": "${NAME} gà tradìo ${VICTIM}.",
"nameDiedText": "${NAME} ze crepà.",
"nameKilledText": "${NAME} gà copà ${VICTIM}.",
"nameNotEmptyText": "El nome no'l połe miga star vodo!",
"nameScoresText": "Un ponto par ${NAME}!",
"nameSuicideKidFriendlyText": "${NAME} par zbałio L ze crepà.",
@ -1037,8 +1038,8 @@
"nextLevelText": "Łeveło seguente",
"noAchievementsRemainingText": "- gnaun",
"noContinuesText": "(sensa continui)",
"noExternalStorageErrorText": "Inte sto dispozitivo A no ze stà catada gnauna memoria esterna",
"noGameCircleText": "Eror: A no te si miga conetesto co GameCircle",
"noExternalStorageErrorText": "So sto dispozidivo no ze stà catada gnauna memoria esterna",
"noGameCircleText": "Eror: no te si miga conetesto co GameCircle",
"noScoresYetText": "Gnancora gnaun puntejo.",
"noThanksText": "Nò, grasie",
"noTournamentsInTestBuildText": "AVERTENSA: i punteji de'l tornèo de 'sta varsion de proa i vegnarà ignorài.",
@ -1087,7 +1088,7 @@
"playerCountAbbreviatedText": "${COUNT}z",
"playerDelayedJoinText": "Co tacarà el turno che'l vien A se zontarà anca ${PLAYER}.",
"playerInfoText": "Informasion zugador",
"playerLeftText": "${PLAYER} l'à mołà el łeveło.",
"playerLeftText": "${PLAYER} gà mołà el łeveło.",
"playerLimitReachedText": "Nùmaro màsemo de ${COUNT} zugadori: A no połe zontarse pì nesun.",
"playerProfilesWindow": {
"cantDeleteAccountProfileText": "A no te połi miga ełimenar el profiło prinsipałe de'l to account.",
@ -1105,7 +1106,10 @@
"playlistsText": "Łiste de zugo",
"pleaseRateText": "Se ${APP_NAME} el ze drio piazerte, tote un àtemo par\nłasarghe zó na vałudasion o scrìvarghe zó un comento. 'Ste\nopinion łe tornarà còmode par dezviłupi fuduri de'l zugo.\n\ngrasie!\n-eric",
"pleaseWaitText": "Speta n'àtemo...",
"pluginsDetectedText": "Estension nove rełevàe. Atìvełe/configùrełe inte łe inpostasion.",
"pluginClassLoadErrorText": "Eror de cargamento de ła clase de estension '${PLUGIN}': ${ERROR}",
"pluginInitErrorText": "Eror de inisiałizasion de l'estension '${PLUGIN}': ${ERROR}",
"pluginsDetectedText": "Estension nove rełevàe. Retaca el zugo par ativarle, o configùrełe so łe inpostasion.",
"pluginsRemovedText": "Estension miga catàe: ${NUM}",
"pluginsText": "Estension",
"practiceText": "Pràtega",
"pressAnyButtonPlayAgainText": "Struca un boton calsìase par zugar danovo...",
@ -1125,7 +1129,7 @@
},
"promoSubmitErrorText": "Eror de trazmision de'l còdaze: controła ła to conesion internet",
"ps3ControllersWindow": {
"macInstructionsText": "Stua ła corente so'l dadrìo de ła to PS3, segùrate che'l\nbluetooth de'l to Mac el sipie ativà, daspò coneti el to controłador\na'l to Mac co un cavo USB par cubiarli. Da deso in vanti A te\npołi doparar el boton \"cao\" de'l controłador par conétarlo co'l to\nMac in modałidà USB (co'l fiło) o bluetooth (sensa fiło).\n\nPar el cubiamento calche Mac el podarìa dimandarte un còdaze.\nSe càpida, varda ła demostrasion seguente o serca juto so Google.\n\n\n\n\nI controładori PS3 conetesti sensa fiło i gavarìa da védarse inte ła łista\nde'l dispozitivo so Prefarense de sistema > Bluetooth. Co te vorè\ndopararli danovo so ła to PS3, te podarisi ver da cavarli vìa da 'sta łista.\n\nSegùrate anca de desconétarli da'l bluetooth co A no te łi\ndòpari o łe só batarìe łe sevitarà a sconsumarse.\n\nEl bluetooth el gavarìa da suportar fin a 7 dispozitivi conetesti,\nanca se ła capasidà ła podarìa variar.",
"macInstructionsText": "Stua ła corente so'l dadrìo de ła to PS3, segùrate che'l\nbluetooth de'l to Mac el sipie ativà, daspò coneti el to controłador\na'l to Mac co un cavo USB par cubiarli. Da deso in vanti te\npołi doparar el boton \"cao\" de'l controłador par conétarlo co'l to\nMac in modałidà USB (co'l fiło) o bluetooth (sensa fiło).\n\nPar el cubiamento calche Mac el podarìa dimandarte un còdaze.\nSe càpida, varda ła demostrasion seguente o serca juto so Google.\n\n\n\n\nI controładori PS3 conetesti sensa fiło i gavarìa da védarse inte ła łista\nde'l dispozidivo so Prefarense de sistema > Bluetooth. Co te vorè\ndopararli danovo so ła to PS3, te podarisi ver da cavarli vìa da 'sta łista.\n\nSegùrate anca de desconétarli da'l bluetooth co no te łi\ndòpari o łe só batarìe łe sevitarà a sconsumarse.\n\nEl bluetooth el gavarìa da suportar fin a 7 dispozidivi conetesti,\nanca se ła capasidà ła podarìa variar.",
"ouyaInstructionsText": "Par doparar un controłador PS3 co ła to OUYA, conéteło co un cavo USB par\ncubiarlo. 'Sta asion ła podarìa desconétar cheł'altri to controładori, donca\nA podarìa servirte retacar ła to OUYA e destacar el cavo USB.\n\nDa deso in vanti A te dovarisi èsar bon de doparar el boton \"cao\" de'l\ncontrołador par conétarlo sensa fiło. Co A te ghè fenìo de zugar, tien\nstrucà el boton \"cao\" par 10 segondi par stuar el controłador, el\npodarìa restar inpisà e stracar ła batarìa.",
"pairingTutorialText": "demostrasion video pa'l cubiamento",
"titleText": "Doparar un controłador PS3 co ${APP_NAME}:"
@ -1153,7 +1157,7 @@
"cant_resolve_host": "A no ze mìa posìbiłe catar fora l'ospitador.",
"capturing": "Drio spetar i zugadori…",
"connected": "Conetesto.",
"description": "Dòpara el to tełèfono o tołeto cofà controłador par BombSquad.\nA połe conétarse so un schermo ùgnoło fin a 8 dispozitivi insenbre, par un feston bueło multizugador!",
"description": "Dòpara el to tełèfono o tołeto cofà controłador par BombSquad.\nPołe conétarse so un schermo ùgnoło fin a 8 dispozidivi insenbre, par un feston bueło multizugador!",
"disconnected": "Desconetesto da'l server.",
"dpad_fixed": "fiso",
"dpad_floating": "mòbiłe",
@ -1206,9 +1210,9 @@
},
"scoreWasText": "(prima ${COUNT})",
"selectText": "Sełesiona",
"seriesWinLine1PlayerText": "L'À VINTO ŁA",
"seriesWinLine1TeamText": "L'À VINTO ŁA",
"seriesWinLine1Text": "L'À VINTO ŁA",
"seriesWinLine1PlayerText": "GÀ VINTO ŁA",
"seriesWinLine1TeamText": "GÀ VINTO ŁA",
"seriesWinLine1Text": "GÀ VINTO ŁA",
"seriesWinLine2Text": "DESFIDA!",
"settingsWindow": {
"accountText": "Account",
@ -1231,7 +1235,7 @@
"enterPromoCodeText": "Insarisi còdaze",
"forTestingText": "Nota: vałori vàłidi soło par proe. Sortendo da l'apl łi vegnarà perdesti.",
"helpTranslateText": "Łe tradusion de ${APP_NAME} łe ze curàe da vołontari.\nSe A te vol darghe na ociada a cheła veneta, struca so'l boton\ncuà soto. Curada da Còdaze Veneto: codazeveneto@gmail.com",
"kickIdlePlayersText": "Para a zugadori in sonera",
"kickIdlePlayersText": "Para fora zugadori in sonera",
"kidFriendlyModeText": "Modałidà bocia (viołensa reduzesta, evc)",
"languageText": "Łengua",
"moddingGuideText": "Guida par modifegasion",
@ -1317,7 +1321,7 @@
"winterSpecialText": "Spesiałe inverno",
"youOwnThisText": "- dezà tuo -"
},
"storeDescriptionText": "Feston bueło a 8 zugadori!\n\nFà saltar par aria i to amighi (o el computer) inte un tornèo de minizughi cofà 'Brinca ła bandiera', 'Scravaso de stełe' o 'Scontri mortałi' in movensa camoma!\n\nFin a 8 parsone łe sarà bone de batajar insenbre, grasie a comandi senpi e a ła conpatibiłidà co tanti controładori. Co l'apl gratùida 'BombSquad Remote' A te połi parfin doparar i to dispozitivi mòbiłi cofà controładori!\n\nE deso... tira fora łe bonbe!\n\nPar informasion in pì daghe na ociada so www.froemling.net/bombsquad.",
"storeDescriptionText": "Feston bueło a 8 zugadori!\n\nFà saltar par aria i to amighi (o el computer) inte un tornèo de minizughi cofà 'Brinca ła bandiera', 'Scravaso de stełe' o 'Scontri mortałi' in movensa camoma!\n\nFin a 8 parsone łe sarà bone de batajar insenbre, grasie a comandi senpi e a ła conpatibiłidà co tanti controładori. Co l'apl gratùida 'BombSquad Remote' te połi parfin doparar i to dispozidivi mòbiłi cofà controładori!\n\nE deso... tira fora łe bonbe!\n\nPar informasion in pì daghe na ociada so www.froemling.net/bombsquad.",
"storeDescriptions": {
"blowUpYourFriendsText": "Fà sciopar par aria i to amighi.",
"competeInMiniGamesText": "Zuga co tanti minizughi: da corse a zvołi.",
@ -1572,7 +1576,7 @@
"An error has occurred; please contact support. (${ERROR})": "A se gà verifegà un eror: contata l'asistensa. (${ERROR})",
"An error has occurred; please contact support@froemling.net.": "A se gà verifegà un eror: contata support@froemling.net.",
"An error has occurred; please try again later.": "A se gà verifegà un eror: proa danovo pì tardi.",
"Are you sure you want to link these accounts?\n\n${ACCOUNT1}\n${ACCOUNT2}\n\nThis cannot be undone!": "Vutu dalvero cołegar 'sti profiłi?\n\n${ACCOUNT1}\n${ACCOUNT2}\n\n'Sta asion no ła połe mìa èsar anułada!",
"Are you sure you want to link these accounts?\n\n${ACCOUNT1}\n${ACCOUNT2}\n\nThis cannot be undone!": "Vutu dabon cołegar 'sti profiłi?\n\n${ACCOUNT1}\n${ACCOUNT2}\n\n'Sta asion no ła połe pì èsar anułada!",
"BombSquad Pro unlocked!": "BombSquad Pro dezblocà!",
"Can't link 2 accounts of this type.": "A no se połe mìa cołegar 2 profiłi de 'sto tipo.",
"Can't link 2 diamond league accounts.": "A no se połe mìa cołegar 2 profiłi de ła łega de damante.",
@ -1588,7 +1592,7 @@
"Invalid tournament entry; score will be ignored.": "Entrada inte'l tornèo mìa vàłida: el puntejo el vegnarà ignorà.",
"Item unlocked!": "Ojeto dezblocà!",
"LINKING DENIED. ${ACCOUNT} contains\nsignificant data that would ALL BE LOST.\nYou can link in the opposite order if you'd like\n(and lose THIS account's data instead)": "COŁEGAMENTO REFUDÀ. ${ACCOUNT} el contien dati\ninportanti che i ndarà PERDESTI.\nSe te vołi A te połi cołegarli in òrdane raverso\n(e pèrdar a'l só posto i dati de 'STO account cuà).",
"Link account ${ACCOUNT} to this account?\nAll existing data on ${ACCOUNT} will be lost.\nThis can not be undone. Are you sure?": "Vutu dalvero cołegar l'account ${ACCOUNT} a 'sto account?\nTuti i dati so ${ACCOUNT} i ndarà perdesti.\n'Sta asion no ła połe mìa èsar anułada: situ seguro?",
"Link account ${ACCOUNT} to this account?\nAll existing data on ${ACCOUNT} will be lost.\nThis can not be undone. Are you sure?": "Vutu dabon cołegar l'account ${ACCOUNT} a 'sto account?\nTuti i dati so ${ACCOUNT} i ndarà perdesti.\n'Sta asion no ła połe pì èsar anułada: vutu ndar vanti?",
"Max number of playlists reached.": "Nùmaro màsemo de łiste de scolto pasà.",
"Max number of profiles reached.": "Nùmaro màsemo de profiłi pasà.",
"Maximum friend code rewards reached.": "Brincà el nùmaro màsemo de premi da'l còdaze amigo.",
@ -1610,11 +1614,11 @@
"This code cannot be used on the account that created it.": "'Sto còdaze no'l połe mìa èsar doparà inte l'account che'l ło gà creà.",
"This is currently unavailable; please try again later.": "Par deso, funsion miga disponìbiłe. Proa danovo pì tardi.",
"This requires version ${VERSION} or newer.": "A ghe serve ła varsion ${VERSION} o una pì nova.",
"Tournaments disabled due to rooted device.": "Tornèi dezativài parvìa de'l dispozitivo co'l root.",
"Tournaments disabled due to rooted device.": "Tornèi dezativài parvìa de'l dispozidivo co'l root.",
"Tournaments require ${VERSION} or newer": "Par i tornèi A serve ła varsion ${VERSION} o una pì resente",
"Unlink ${ACCOUNT} from this account?\nAll data on ${ACCOUNT} will be reset.\n(except for achievements in some cases)": "Vutu descołegar l'account ${ACCOUNT} da 'sto account?\nTuti i dati de ${ACCOUNT} i vegnarà ełimenài.\n(obietivi a parte in calche cazo)",
"WARNING: complaints of hacking have been issued against your account.\nAccounts found to be hacking will be banned. Please play fair.": "AVERTENSA: el to account el ze stà segnałà par el dòparo de truchi.\nI zugaduri catài a doparar truchi i vegnarà blocài. Zuga da gałantomo.",
"Would you like to link your device account to this one?\n\nYour device account is ${ACCOUNT1}\nThis account is ${ACCOUNT2}\n\nThis will allow you to keep your existing progress.\nWarning: this cannot be undone!\n": "Ghetu caro cołegar l'account de'l to dispozitivo co 'sto cuà?\n\nL'account de'l to dispozitivo el ze ${ACCOUNT1}\n'Sto account el ze ${ACCOUNT2}\n\n'Sta oparasion ła te parmetarà de mantegner i to progresi ezistenti.\nAvertensa: 'sta asion no ła połe mìa èsar anułada!",
"Would you like to link your device account to this one?\n\nYour device account is ${ACCOUNT1}\nThis account is ${ACCOUNT2}\n\nThis will allow you to keep your existing progress.\nWarning: this cannot be undone!\n": "Ghetu caro cołegar l'account de'l to dispozidivo co 'sto cuà?\n\nL'account de'l to dispozidivo el ze ${ACCOUNT1}\n'Sto account el ze ${ACCOUNT2}\n\n'Sta oparasion ła te parmetarà de mantegner i to progresi ezistenti.\nAvertensa: 'sta asion no ła połe èsar anułada!",
"You already own this!": "Dezà cronpà!",
"You can join in ${COUNT} seconds.": "A te połi zontarte tenpo ${COUNT} segondi.",
"You don't have enough tickets for this!": "A no te ghè miga biłieti che basta par cronparlo!",
@ -1672,7 +1676,7 @@
"Time Limit": "Łìmide de tenpo"
},
"statements": {
"${TEAM} is disqualified because ${PLAYER} left": "Ła scuadra ${TEAM} ła ze scuałifegada parché ${PLAYER} l'à mołà.",
"${TEAM} is disqualified because ${PLAYER} left": "Ła scuadra ${TEAM} ła ze scuałifegada parché ${PLAYER} gà mołà.",
"Killing ${NAME} for skipping part of the track!": "Copà ${NAME} par ver saltà un toco de'l parcorso!",
"Warning to ${NAME}: turbo / button-spamming knocks you out.": "Avertensa par ${NAME}: el turbo / spam co i botoni el te gà fato trar fora."
},
@ -1689,19 +1693,19 @@
"Curse boxes turn you into a ticking time bomb.\nThe only cure is to quickly grab a health-pack.": "Łe case małedision łe te transforma inte na bonba a tenpo.\nA te połi curarte soło tołendo in presa un pacheto sałude.",
"Despite their looks, all characters' abilities are identical,\nso just pick whichever one you most closely resemble.": "Anca se drio ła siera A no par, łe abiłidà de tuti i parsonaji\nłe ze conpagne, donca tote sù cheło che'l te połe somejar de pì.",
"Don't get too cocky with that energy shield; you can still get yourself thrown off a cliff.": "Mai far masa i gałeti co'l scudo nerjètego: A te połi uncora farte trar baso da na croda.",
"Don't run all the time. Really. You will fall off cliffs.": "No stà córar tuto el tenpo. Dalvero. A te fenirè zó par na croda.",
"Don't run all the time. Really. You will fall off cliffs.": "No stà córar tuto el tenpo. Dabon. Te fenirè zó par na croda.",
"Don't spin for too long; you'll become dizzy and fall.": "No stà ndar torno par masa tenpo: te vegnarà na storniroła e te cascarè.",
"Hold any button to run. (Trigger buttons work well if you have them)": "Tien strucà un boton calsìase par córar. (I griłeti nałòzeghi i ze i pì adati, se A te łi ghè)",
"Hold down any button to run. You'll get places faster\nbut won't turn very well, so watch out for cliffs.": "Tien strucà un boton calsìase par córar. A te te movarè pì in\npresa ma no te voltarè miga masa ben, donca ocio a łe crode.",
"Ice bombs are not very powerful, but they freeze\nwhoever they hit, leaving them vulnerable to shattering.": "Łe bonbe jaso no łe ze miga tanto potenti, ma łe injasa tuto cheło\nche łe brinca, e cheło che'l ze injasà el ze anca fàsiłe da fracasar.",
"If someone picks you up, punch them and they'll let go.\nThis works in real life too.": "Se calchedun el te łeva sù, daghe un crogno che'l te\nmołe zó. 'Sta roba ła funsiona anca inte ła vida vera.",
"If you are short on controllers, install the '${REMOTE_APP_NAME}' app\non your mobile devices to use them as controllers.": "Se A te si a curto de controładori, instała l'apl '${REMOTE_APP_NAME}'\nso i to dispozitivi mòbiłi e dopàrełi cofà controładori.",
"If you are short on controllers, install the '${REMOTE_APP_NAME}' app\non your mobile devices to use them as controllers.": "Se te si a curto de controładori, instała l'apl '${REMOTE_APP_NAME}'\nso i to dispozidivi mòbiłi e dopàrełi cofà controładori.",
"If you get a sticky-bomb stuck to you, jump around and spin in circles. You might\nshake the bomb off, or if nothing else your last moments will be entertaining.": "Se na bonba petaisa ła te se taca doso, salta in volta e và torno: ła gavarìa da cavarse.\nE se no funsionase, i to momenti ùltemi de vida i gavarà almanco dà spetàgoło!",
"If you kill an enemy in one hit you get double points for it.": "Se A te copi un nemigo co un colpo soło A te ciapi el dupio de i punti.",
"If you pick up a curse, your only hope for survival is to\nfind a health powerup in the next few seconds.": "Se A te ciapi na małedision, A te ghè soło na sparansa de salvesa:\ncatar fora un pacheto sałude inte i puchi segondi che A te resta.",
"If you stay in one place, you're toast. Run and dodge to survive..": "Se A se stà fermi inte un posto, A se ze friti. Cori e schiva par soravìvar...",
"If you've got lots of players coming and going, turn on 'auto-kick-idle-players'\nunder settings in case anyone forgets to leave the game.": "Se A te te cati tanti zugadori che i và e vien, e inte'l cazo che calchedun el se dezménteghe de\nmołar el zugo, ativa ła funsion automàtega 'Para a zugadori in sonera' inte łe inpostasion.",
"If your device gets too warm or you'd like to conserve battery power,\nturn down \"Visuals\" or \"Resolution\" in Settings->Graphics": "Se'l to dispozitivo el taca scotar o se A te ghè caro sparagnar batarìa,\ncała ła \"Prospetiva\" o ła \"Resołusion\" so Inpostasion > Gràfega",
"If you've got lots of players coming and going, turn on 'auto-kick-idle-players'\nunder settings in case anyone forgets to leave the game.": "Se te te cati tanti zugadori che và e vien, e inte'l cazo che calchedun el se dezménteghe de\nmołar el zugo, ativa ła funsion automàtega 'Para fora zugadori in sonera' inte łe inpostasion.",
"If your device gets too warm or you'd like to conserve battery power,\nturn down \"Visuals\" or \"Resolution\" in Settings->Graphics": "Se'l to dispozidivo el taca scotar o se te ghè caro sparagnar batarìa,\ncała ła \"Prospetiva\" o ła \"Resołusion\" so Inpostasion > Gràfega",
"If your framerate is choppy, try turning down resolution\nor visuals in the game's graphics settings.": "Se el zugo el và a scati, proa całar ła resołusion\no ła prospetiva inte l'inpostasion gràfega.",
"In Capture-the-Flag, your own flag must be at your base to score, If the other\nteam is about to score, stealing their flag can be a good way to stop them.": "Par far ponto so 'Brinca ła bandiera', A te ghè da portar ła bandiera fin so ła to\nbaze. Se cheł'altra scuadra ła ze drio far ponto, A te połi fermarla anca robàndogheła.",
"In hockey, you'll maintain more speed if you turn gradually.": "Inte l'hockey, voltando gradualmente A te mantien alta ła vełosidà.",
@ -1769,7 +1773,7 @@
"randomName3Text": "Teo",
"randomName4Text": "Cesco",
"randomName5Text": "Stè",
"skipConfirmText": "Vutu dalvero saltar ła demostrasion? Toca o struca par confermar.",
"skipConfirmText": "Vutu dabon saltar ła demostrasion? Toca o struca par confermar.",
"skipVoteCountText": "Voti par saltar ła demostrasion: ${COUNT}/${TOTAL}",
"skippingText": "demostrasion saltada...",
"toSkipPressAnythingText": "(toca o struca un boton par saltar ła demostrasion)"
@ -1787,7 +1791,7 @@
"upgradeText": "Mejora",
"upgradeToPlayText": "Par zugarghe, dezbloca \"${PRO}\" inte ła botega.",
"useDefaultText": "Reinposta",
"usesExternalControllerText": "'Sto zugo el dòpara un controłador esterno cofà dispozitivo de entrada.",
"usesExternalControllerText": "'Sto zugo el dòpara un controłador esterno cofà dispozidivo de entrada.",
"usingItunesText": "Doparar l'apl de mùzega par el son de fondo...",
"validatingTestBuildText": "Confermasion varsion de proa...",
"victoryText": "Vitoria!",
@ -1832,21 +1836,23 @@
"wiimoteSetupWindow": {
"copyrightText": "Deriti d'autor DarwiinRemote",
"listenText": "Scolta",
"macInstructionsText": "Segùrate che ła to Wii ła sipie stuada e el bluetooth de'l\nto Mac ativà, donca stuca so 'Scolta'. Ła conpatibiłidà co'l\nWiimote ła podarìa èsar un fià inserta, donca te podarisi\ncatarte a proar pì 'olte prima de otegner na conesion.\n\nEl bluetooth el gavarìa da suportar fin a 7 dispozitivi conetesti,\nanca se ła capasidà ła podarìa variar.\n\nBombSquad el suporta el controłador Clàsego e i controładori\norijinałi Wiimotes, Nunchuks.\nAnca el novo Wii Remote Plus el funsiona\nma soło sensa acesori.",
"macInstructionsText": "Segùrate che ła to Wii ła sipie stuada e el bluetooth de'l\nto Mac ativà, donca stuca so 'Scolta'. Ła conpatibiłidà co'l\nWiimote ła podarìa èsar un fià inserta, donca te podarisi\ncatarte a proar pì 'olte prima de otegner na conesion.\n\nEl bluetooth el gavarìa da suportar fin a 7 dispozidivi conetesti,\nanca se ła capasidà ła podarìa variar.\n\nBombSquad el suporta el controłador Clàsego e i controładori\norijinałi Wiimotes, Nunchuks.\nAnca el novo Wii Remote Plus el funsiona\nma soło sensa acesori.",
"thanksText": "Grasie a ła scuadra DarwiinRemote\npar verlo rendesto posìbiłe.",
"titleText": "Configurasion Wiimote"
},
"winsPlayerText": "Vitoria de ${NAME}!",
"winsTeamText": "Vitoria de i ${NAME}!",
"winsText": "Vitoria de ${NAME}!",
"workspaceSyncErrorText": "Eror de sincronizasion de ${WORKSPACE}. Varda el rejistro eventi par i detaji.",
"workspaceSyncReuseText": "Inposìbiłe sinconizar ${WORKSPACE}. Dòparo de ła varsion sincronizada presedente.",
"worldScoresUnavailableText": "Punteji mondiałi miga disponìbiłi.",
"worldsBestScoresText": "I punteji mejo de'l mondo",
"worldsBestTimesText": "I tenpi mejo de'l mondo",
"xbox360ControllersWindow": {
"getDriverText": "Descarga el driver",
"macInstructions2Text": "Par doparar sensa fiło i controładori, A te serve anca el resevidor\nche'l riva co l''Xbox 360 Wireless Controller par Windows'.\nUn resevidor el te parmete de conétar fin a 4 controładori.\n\nInportante: i resevidori de terse parti no i funsionarà mìa co 'sto driver;\nsegùrate che'l to resevidor el sipia 'Microsoft' e miga 'XBOX 360'.\nŁa Microsoft no łi vende pì destacài, donca A te servirà par forsa cheło\nvendesto insebre co'l controłador, o senò, proa sercar so Ebay.\n\nSe A te cati ùtiłe el driver, ciapa in considerasion de farghe na\ndonasion a'l só dezviłupador so 'sto sito.",
"macInstructions2Text": "Par doparar sensa fiło i controładori, A te serve anca el resevidor\nche'l riva co l''Xbox 360 Wireless Controller par Windows'.\nUn resevidor el te parmete de conétar fin a 4 controładori.\n\nInportante: i resevidori de terse parti no i funsionarà miga co 'sto driver;\nsegùrate che'l to resevidor el sipia 'Microsoft' e miga 'XBOX 360'.\nŁa Microsoft no łi vende pì destacài, donca te servirà par forsa cheło\nvendesto insebre co'l controłador, o senò, proa sercar so Ebay.\n\nSe te cati ùtiłe el driver, ciapa in considerasion de farghe na\ndonasion a'l só dezviłupador so 'sto sito.",
"macInstructionsText": "Per doparar i controładori co'l fiło de ła Xbox 360, A te ghè\nda instałar el driver Mac disponìbiłe so'l link cuà soto.\nEl funsiona co anbo i controładori, co'l fiło o sensa.",
"ouyaInstructionsText": "Par doparar so Bombsquad un controłador de l'Xbox 360 co'l fiło,\ntàcheło sù inte ła porta USB del to dispozitivo. A te połi anca\ntacar sù pì controładori insenbre doparando un hub USB.\n\nPar doparar i controładori sensa fiło invese, A te serve un resevidor\nde segnałe. A te połi catarlo, o rento ła scàtoła \"Controładori sensa fiło\nXbox 360 par Windows\", o vendesto a parte. Caun resevidor el và tacà so\nna porta USB e el te parmete de conétar fin a 4 controładori.",
"ouyaInstructionsText": "Par doparar so Bombsquad un controłador de l'Xbox 360 co'l fiło,\ntàcheło sù inte ła porta USB del to dispozidivo. Te połi anca\ntacar sù pì controładori insenbre doparando un hub USB.\n\nPar doparar i controładori sensa fiło invese, te serve un resevidor\nde segnałe. Te połi catarlo, o rento ła scàtoła \"Controładori sensa fiło\nXbox 360 par Windows\", o vendesto a parte. Caun resevidor el và tacà so\nna porta USB e el te parmete de conétar fin a 4 controładori.",
"titleText": "Doparar un controłador Xbox 360 co ${APP_NAME}:"
},
"yesAllowText": "Sì, parmeti!",

View file

@ -1,4 +1,4 @@
from .core import contents, where
__all__ = ["contents", "where"]
__version__ = "2022.05.18.1"
__version__ = "2022.06.15"

View file

@ -1362,39 +1362,6 @@ Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl
nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI=
-----END CERTIFICATE-----
# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
# Label: "Hellenic Academic and Research Institutions RootCA 2011"
# Serial: 0
# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9
# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d
# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
l7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----
# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
# Label: "Actalis Authentication Root CA"
@ -4528,3 +4495,191 @@ PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA
y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb
gfM0agPnIjhQW+0ZT0MW
-----END CERTIFICATE-----
# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc.
# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc.
# Label: "DigiCert TLS ECC P384 Root G5"
# Serial: 13129116028163249804115411775095713523
# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed
# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee
# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05
-----BEGIN CERTIFICATE-----
MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
DXZDjC5Ty3zfDBeWUA==
-----END CERTIFICATE-----
# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc.
# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc.
# Label: "DigiCert TLS RSA4096 Root G5"
# Serial: 11930366277458970227240571539258396554
# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1
# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35
# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT
HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN
NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+
ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0
2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp
wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM
pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD
nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po
sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx
Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd
Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX
KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe
XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL
tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv
TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw
GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H
PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF
O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ
REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik
AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv
/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+
p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw
MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF
qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK
ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+
-----END CERTIFICATE-----
# Issuer: CN=Certainly Root R1 O=Certainly
# Subject: CN=Certainly Root R1 O=Certainly
# Label: "Certainly Root R1"
# Serial: 188833316161142517227353805653483829216
# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12
# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af
# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0
-----BEGIN CERTIFICATE-----
MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw
PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy
dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0
YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2
1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT
vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed
aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0
1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5
r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5
cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ
wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ
6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA
2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH
Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR
eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB
/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u
d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr
PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d
8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi
1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd
rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di
taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7
lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj
yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn
Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy
yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n
wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6
OV+KmalBWQewLK8=
-----END CERTIFICATE-----
# Issuer: CN=Certainly Root E1 O=Certainly
# Subject: CN=Certainly Root E1 O=Certainly
# Label: "Certainly Root E1"
# Serial: 8168531406727139161245376702891150584
# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9
# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b
# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2
-----BEGIN CERTIFICATE-----
MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw
CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu
bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ
BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s
eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK
+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2
QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4
hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm
ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG
BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
-----END CERTIFICATE-----
# Issuer: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
# Subject: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
# Label: "E-Tugra Global Root CA RSA v3"
# Serial: 75951268308633135324246244059508261641472512052
# MD5 Fingerprint: 22:be:10:f6:c2:f8:03:88:73:5f:33:29:47:28:47:a4
# SHA1 Fingerprint: e9:a8:5d:22:14:52:1c:5b:aa:0a:b4:be:24:6a:23:8a:c9:ba:e2:a9
# SHA256 Fingerprint: ef:66:b0:b1:0a:3c:db:9f:2e:36:48:c7:6b:d2:af:18:ea:d2:bf:e6:f1:17:65:5e:28:c4:06:0d:a1:a3:f4:c2
-----BEGIN CERTIFICATE-----
MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQEL
BQAwgYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUt
VHVncmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYw
JAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIFJTQSB2MzAeFw0yMDAzMTgw
OTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMG
QW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1
Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBD
QSBSU0EgdjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J7
7gnJY9LTQ91ew6aEOErxjYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscx
uj7X/iWpKo429NEvx7epXTPcMHD4QGxLsqYxYdE0PD0xesevxKenhOGXpOhL9hd8
7jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF/YP9f4RtNGx/ardLAQO/
rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8qQedmCeFL
l+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bG
wzrwbMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4
znKS4iicvObpCdg604nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBO
M/J+JjKsBY04pOZ2PJ8QaQ5tndLBeSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK
5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiMbIedBi3x7+PmBvrFZhNb/FAH
nnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbgh3cXTJ2w2Amo
DVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD
AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSy
tK7mLfcm1ap1LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEL
BQADggIBAImocn+M684uGMQQgC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ
6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN438o2Fi+CiJ+8EUdPdk3ILY7r3y18
Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/qln0F7psTpURs+APQ
3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3sSdPk
vmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn9
9t2HVhjYsCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQ
mhty3QUBjYZgv6Rn7rWlDdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YA
VSgU7NbHEqIbZULpkejLPoeJVF3Zr52XnGnnCv8PWniLYypMfUeUP95L6VPQMPHF
9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFHIK+WEj5jlB0E5y67hscM
moi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiXYY60MGo8
bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ
-----END CERTIFICATE-----
# Issuer: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
# Subject: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
# Label: "E-Tugra Global Root CA ECC v3"
# Serial: 218504919822255052842371958738296604628416471745
# MD5 Fingerprint: 46:bc:81:bb:f1:b5:1e:f7:4b:96:bc:14:e2:e7:27:64
# SHA1 Fingerprint: 8a:2f:af:57:53:b1:b0:e6:a1:04:ec:5b:6a:69:71:6d:f6:1c:e2:84
# SHA256 Fingerprint: 87:3f:46:85:fa:7f:56:36:25:25:2e:6d:36:bc:d7:f1:6f:c2:49:51:f2:64:e4:7e:1b:95:4f:49:08:cd:ca:13
-----BEGIN CERTIFICATE-----
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw
gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn
cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD
VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2
NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r
YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh
IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF
Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ
KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK
fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB
Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C
MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp
ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6
7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx
vmjkI6TZraE3
-----END CERTIFICATE-----

View file

@ -37,6 +37,7 @@ __all__ = [
'Counter',
'Deque',
'DefaultDict',
'NamedTuple',
'OrderedDict',
'TypedDict',
@ -380,6 +381,46 @@ def _is_callable_members_only(cls):
return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
def _maybe_adjust_parameters(cls):
"""Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__.
The contents of this function are very similar
to logic found in typing.Generic.__init_subclass__
on the CPython main branch.
"""
tvars = []
if '__orig_bases__' in cls.__dict__:
tvars = typing._collect_type_vars(cls.__orig_bases__)
# Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
# If found, tvars must be a subset of it.
# If not found, tvars is it.
# Also check for and reject plain Generic,
# and reject multiple Generic[...] and/or Protocol[...].
gvars = None
for base in cls.__orig_bases__:
if (isinstance(base, typing._GenericAlias) and
base.__origin__ in (typing.Generic, Protocol)):
# for error messages
the_base = base.__origin__.__name__
if gvars is not None:
raise TypeError(
"Cannot inherit from Generic[...]"
" and/or Protocol[...] multiple types.")
gvars = base.__parameters__
if gvars is None:
gvars = tvars
else:
tvarset = set(tvars)
gvarset = set(gvars)
if not tvarset <= gvarset:
s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
s_args = ', '.join(str(g) for g in gvars)
raise TypeError(f"Some type variables ({s_vars}) are"
f" not listed in {the_base}[{s_args}]")
tvars = gvars
cls.__parameters__ = tuple(tvars)
# 3.8+
if hasattr(typing, 'Protocol'):
Protocol = typing.Protocol
@ -476,43 +517,13 @@ else:
return typing._GenericAlias(cls, params)
def __init_subclass__(cls, *args, **kwargs):
tvars = []
if '__orig_bases__' in cls.__dict__:
error = typing.Generic in cls.__orig_bases__
else:
error = typing.Generic in cls.__bases__
if error:
raise TypeError("Cannot inherit from plain Generic")
if '__orig_bases__' in cls.__dict__:
tvars = typing._collect_type_vars(cls.__orig_bases__)
# Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
# If found, tvars must be a subset of it.
# If not found, tvars is it.
# Also check for and reject plain Generic,
# and reject multiple Generic[...] and/or Protocol[...].
gvars = None
for base in cls.__orig_bases__:
if (isinstance(base, typing._GenericAlias) and
base.__origin__ in (typing.Generic, Protocol)):
# for error messages
the_base = base.__origin__.__name__
if gvars is not None:
raise TypeError(
"Cannot inherit from Generic[...]"
" and/or Protocol[...] multiple types.")
gvars = base.__parameters__
if gvars is None:
gvars = tvars
else:
tvarset = set(tvars)
gvarset = set(gvars)
if not tvarset <= gvarset:
s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
s_args = ', '.join(str(g) for g in gvars)
raise TypeError(f"Some type variables ({s_vars}) are"
f" not listed in {the_base}[{s_args}]")
tvars = gvars
cls.__parameters__ = tuple(tvars)
_maybe_adjust_parameters(cls)
# Determine if this is a protocol or a concrete subclass.
if not cls.__dict__.get('_is_protocol', None):
@ -613,6 +624,7 @@ if hasattr(typing, "Required"):
# keyword with old-style TypedDict(). See https://bugs.python.org/issue42059
# The standard library TypedDict below Python 3.11 does not store runtime
# information about optional and required keys when using Required or NotRequired.
# Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
TypedDict = typing.TypedDict
_TypedDictMeta = typing._TypedDictMeta
is_typeddict = typing.is_typeddict
@ -695,8 +707,16 @@ else:
# Subclasses and instances of TypedDict return actual dictionaries
# via _dict_new.
ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
# Don't insert typing.Generic into __bases__ here,
# or Generic.__init_subclass__ will raise TypeError
# in the super().__new__() call.
# Instead, monkey-patch __bases__ onto the class after it's been created.
tp_dict = super().__new__(cls, name, (dict,), ns)
if any(issubclass(base, typing.Generic) for base in bases):
tp_dict.__bases__ = (typing.Generic, dict)
_maybe_adjust_parameters(tp_dict)
annotations = {}
own_annotations = ns.get('__annotations__', {})
msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
@ -1958,3 +1978,92 @@ else:
if not hasattr(typing, "TypeVarTuple"):
typing._collect_type_vars = _collect_type_vars
typing._check_generic = _check_generic
# Backport typing.NamedTuple as it exists in Python 3.11.
# In 3.11, the ability to define generic `NamedTuple`s was supported.
# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
if sys.version_info >= (3, 11):
NamedTuple = typing.NamedTuple
else:
def _caller():
try:
return sys._getframe(2).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError): # For platforms without _getframe()
return None
def _make_nmtuple(name, types, module, defaults=()):
fields = [n for n, t in types]
annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
for n, t in types}
nm_tpl = collections.namedtuple(name, fields,
defaults=defaults, module=module)
nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
# The `_field_types` attribute was removed in 3.9;
# in earlier versions, it is the same as the `__annotations__` attribute
if sys.version_info < (3, 9):
nm_tpl._field_types = annotations
return nm_tpl
_prohibited_namedtuple_fields = typing._prohibited
_special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
class _NamedTupleMeta(type):
def __new__(cls, typename, bases, ns):
assert _NamedTuple in bases
for base in bases:
if base is not _NamedTuple and base is not typing.Generic:
raise TypeError(
'can only inherit from a NamedTuple type and Generic')
bases = tuple(tuple if base is _NamedTuple else base for base in bases)
types = ns.get('__annotations__', {})
default_names = []
for field_name in types:
if field_name in ns:
default_names.append(field_name)
elif default_names:
raise TypeError(f"Non-default namedtuple field {field_name} "
f"cannot follow default field"
f"{'s' if len(default_names) > 1 else ''} "
f"{', '.join(default_names)}")
nm_tpl = _make_nmtuple(
typename, types.items(),
defaults=[ns[n] for n in default_names],
module=ns['__module__']
)
nm_tpl.__bases__ = bases
if typing.Generic in bases:
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:
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])
if typing.Generic in bases:
nm_tpl.__init_subclass__()
return nm_tpl
def NamedTuple(__typename, __fields=None, **kwargs):
if __fields is None:
__fields = kwargs.items()
elif kwargs:
raise TypeError("Either list of fields or keywords"
" can be provided to NamedTuple, not both")
return _make_nmtuple(__typename, __fields, module=_caller())
NamedTuple.__doc__ = typing.NamedTuple.__doc__
_NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
# On 3.8+, alter the signature so that it matches typing.NamedTuple.
# The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7,
# so just leave the signature as it is on 3.7.
if sys.version_info >= (3, 8):
NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)'
def _namedtuple_mro_entries(bases):
assert NamedTuple in bases
return (_NamedTuple,)
NamedTuple.__mro_entries__ = _namedtuple_mro_entries

View file

@ -74,7 +74,7 @@ class AccountV1Subsystem:
def get_league_rank_points(self,
data: dict[str, Any] | None,
subset: str = None) -> int:
subset: str | None = None) -> int:
"""(internal)"""
if data is None:
return 0

View file

@ -610,8 +610,8 @@ class Achievement:
x: float,
y: float,
delay: float,
outdelay: float = None,
color: Sequence[float] = None,
outdelay: float | None = None,
color: Sequence[float] | None = None,
style: str = 'post_game') -> list[ba.Actor]:
"""Create a display for the Achievement.

View file

@ -53,14 +53,15 @@ class AdsSubsystem:
def show_ad(self,
purpose: str,
on_completion_call: Callable[[], Any] = None) -> None:
on_completion_call: Callable[[], Any] | None = None) -> None:
"""(internal)"""
self.last_ad_purpose = purpose
_ba.show_ad(purpose, on_completion_call)
def show_ad_2(self,
purpose: str,
on_completion_call: Callable[[bool], Any] = None) -> None:
def show_ad_2(
self,
purpose: str,
on_completion_call: Callable[[bool], Any] | None = None) -> None:
"""(internal)"""
self.last_ad_purpose = purpose
_ba.show_ad_2(purpose, on_completion_call)

View file

@ -29,6 +29,7 @@ if TYPE_CHECKING:
from ba._cloud import CloudSubsystem
from bastd.actor import spazappearance
from ba._accountv2 import AccountV2Subsystem
from ba._level import Level
class App:
@ -274,6 +275,7 @@ class App:
# Co-op Campaigns.
self.campaigns: dict[str, ba.Campaign] = {}
self.custom_coop_practice_games: list[str] = []
# Server Mode.
self.server: ba.ServerController | None = None
@ -341,7 +343,7 @@ class App:
from bastd import maps as stdmaps
from bastd.actor import spazappearance
from ba._generated.enums import TimeType
self._aioloop = _asyncio.setup_asyncio()
@ -430,13 +432,9 @@ class App:
self.meta.on_app_running()
self.plugins.on_app_running()
import custom_hooks
custom_hooks.on_app_running()
# from ba._dependency import test_depset
# test_depset()
if bool(False):
self._test_https()
def _update_state(self) -> None:
if self._app_paused:
@ -530,6 +528,15 @@ class App:
# FIXME: This should not be an actor attr.
activity.paused_text = None
def add_coop_practice_level(self, level: Level) -> None:
"""Adds an individual level to the 'practice' section in Co-op."""
# Assign this level to our catch-all campaign.
self.campaigns['Challenges'].addlevel(level)
# Make note to add it to our challenges UI.
self.custom_coop_practice_games.append(f'Challenges:{level.name}')
def return_to_main_menu_session_gracefully(self,
reset_ui: bool = True) -> None:
"""Attempt to cleanly get back to the main menu."""
@ -580,7 +587,7 @@ class App:
def launch_coop_game(self,
game: str,
force: bool = False,
args: dict = None) -> bool:
args: dict | None = None) -> bool:
"""High level way to launch a local co-op session."""
# pylint: disable=cyclic-import
from ba._campaign import getcampaign
@ -646,19 +653,3 @@ class App:
"""
self._initial_login_completed = True
self._update_state()
def _test_https(self) -> None:
"""Testing https support.
(would be nice to get this working on our custom Python builds; need
to wrangle certificates somehow).
"""
import urllib.request
try:
with urllib.request.urlopen('https://example.com') as url:
val = url.read()
_ba.screenmessage('HTTPS SUCCESS!')
print('HTTPS TEST SUCCESS', len(val))
except Exception as exc:
_ba.screenmessage('HTTPS FAIL.')
print('HTTPS TEST FAIL:', exc)

View file

@ -177,9 +177,10 @@ def garbage_collect() -> None:
gc.collect()
def print_live_object_warnings(when: Any,
ignore_session: ba.Session = None,
ignore_activity: ba.Activity = None) -> None:
def print_live_object_warnings(
when: Any,
ignore_session: ba.Session | None = None,
ignore_activity: ba.Activity | None = None) -> None:
"""Print warnings for remaining objects in the current context."""
# pylint: disable=cyclic-import
from ba._session import Session

View file

@ -17,6 +17,7 @@ import sys
from efro.dataclassio import (ioprepped, IOAttrs, dataclass_from_json,
dataclass_to_json)
import _ba
if TYPE_CHECKING:
from bacommon.assets import AssetPackageFlavor
@ -165,7 +166,6 @@ def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None:
"""
# pylint: disable=consider-using-with
import socket
# We don't want to keep the provided AssetGather alive, but we want
@ -175,7 +175,9 @@ def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None:
# Pass a very short timeout to urllib so we have opportunities
# to cancel even with network blockage.
req = urllib.request.urlopen(url, timeout=1)
req = urllib.request.urlopen(url,
context=_ba.app.net.sslcontext,
timeout=1)
file_size = int(req.headers['Content-Length'])
print(f'\nDownloading: {filename} Bytes: {file_size:,}')

View file

@ -11,6 +11,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import asyncio
import logging
import time
import os
if TYPE_CHECKING:
import ba
@ -19,6 +22,8 @@ if TYPE_CHECKING:
_asyncio_timer: ba.Timer | None = None
_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
DEBUG_TIMING = os.environ.get('BA_DEBUG_TIMING') == '1'
def setup_asyncio() -> asyncio.AbstractEventLoop:
"""Setup asyncio functionality for the logic thread."""
@ -53,7 +58,18 @@ def setup_asyncio() -> asyncio.AbstractEventLoop:
def run_cycle() -> None:
assert _asyncio_event_loop is not None
_asyncio_event_loop.call_soon(_asyncio_event_loop.stop)
starttime = time.monotonic() if DEBUG_TIMING else 0
_asyncio_event_loop.run_forever()
endtime = time.monotonic() if DEBUG_TIMING else 0
# Let's aim to have nothing take longer than 1/120 of a second.
if DEBUG_TIMING:
warn_time = 1.0 / 120
duration = endtime - starttime
if duration > warn_time:
logging.warning(
'Asyncio loop step took %.4fs; ideal max is %.4f',
duration, warn_time)
global _asyncio_timer # pylint: disable=invalid-name
_asyncio_timer = _ba.Timer(1.0 / 30.0,

View file

@ -28,10 +28,16 @@ class Campaign:
Category: **App Classes**
"""
def __init__(self, name: str, sequential: bool = True):
def __init__(self,
name: str,
sequential: bool = True,
levels: list[ba.Level] | None = None):
self._name = name
self._levels: list[ba.Level] = []
self._sequential = sequential
self._levels: list[ba.Level] = []
if levels is not None:
for level in levels:
self.addlevel(level)
@property
def name(self) -> str:
@ -43,12 +49,15 @@ class Campaign:
"""Whether this Campaign's levels must be played in sequence."""
return self._sequential
def addlevel(self, level: ba.Level) -> None:
def addlevel(self, level: ba.Level, index: int | None = None) -> None:
"""Adds a ba.Level to the Campaign."""
if level.campaign is not None:
raise RuntimeError('Level already belongs to a campaign.')
level.set_campaign(self, len(self._levels))
self._levels.append(level)
if index is None:
self._levels.append(level)
else:
self._levels.insert(index, level)
@property
def levels(self) -> list[ba.Level]:
@ -91,9 +100,8 @@ class Campaign:
def init_campaigns() -> None:
"""Fill out initial default Campaigns."""
# pylint: disable=too-many-statements
# pylint: disable=cyclic-import
from ba import _level
from ba._level import Level
from bastd.game.onslaught import OnslaughtGame
from bastd.game.football import FootballCoopGame
from bastd.game.runaround import RunaroundGame
@ -109,244 +117,218 @@ def init_campaigns() -> None:
# FIXME: Once translations catch up, we can convert these to use the
# generic display-name '${GAME} Training' type stuff.
campaign = Campaign('Easy')
campaign.addlevel(
_level.Level('Onslaught Training',
gametype=OnslaughtGame,
settings={'preset': 'training_easy'},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Rookie Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'rookie_easy'},
preview_texture_name='courtyardPreview'))
campaign.addlevel(
_level.Level('Rookie Football',
gametype=FootballCoopGame,
settings={'preset': 'rookie_easy'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Pro Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'pro_easy'},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Pro Football',
gametype=FootballCoopGame,
settings={'preset': 'pro_easy'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Pro Runaround',
gametype=RunaroundGame,
settings={'preset': 'pro_easy'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('Uber Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'uber_easy'},
preview_texture_name='courtyardPreview'))
campaign.addlevel(
_level.Level('Uber Football',
gametype=FootballCoopGame,
settings={'preset': 'uber_easy'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Uber Runaround',
gametype=RunaroundGame,
settings={'preset': 'uber_easy'},
preview_texture_name='towerDPreview'))
register_campaign(campaign)
register_campaign(
Campaign(
'Easy',
levels=[
Level('Onslaught Training',
gametype=OnslaughtGame,
settings={'preset': 'training_easy'},
preview_texture_name='doomShroomPreview'),
Level('Rookie Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'rookie_easy'},
preview_texture_name='courtyardPreview'),
Level('Rookie Football',
gametype=FootballCoopGame,
settings={'preset': 'rookie_easy'},
preview_texture_name='footballStadiumPreview'),
Level('Pro Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'pro_easy'},
preview_texture_name='doomShroomPreview'),
Level('Pro Football',
gametype=FootballCoopGame,
settings={'preset': 'pro_easy'},
preview_texture_name='footballStadiumPreview'),
Level('Pro Runaround',
gametype=RunaroundGame,
settings={'preset': 'pro_easy'},
preview_texture_name='towerDPreview'),
Level('Uber Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'uber_easy'},
preview_texture_name='courtyardPreview'),
Level('Uber Football',
gametype=FootballCoopGame,
settings={'preset': 'uber_easy'},
preview_texture_name='footballStadiumPreview'),
Level('Uber Runaround',
gametype=RunaroundGame,
settings={'preset': 'uber_easy'},
preview_texture_name='towerDPreview')
],
))
# "hard" mode
campaign = Campaign('Default')
campaign.addlevel(
_level.Level('Onslaught Training',
gametype=OnslaughtGame,
settings={'preset': 'training'},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Rookie Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'rookie'},
preview_texture_name='courtyardPreview'))
campaign.addlevel(
_level.Level('Rookie Football',
gametype=FootballCoopGame,
settings={'preset': 'rookie'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Pro Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'pro'},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Pro Football',
gametype=FootballCoopGame,
settings={'preset': 'pro'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Pro Runaround',
gametype=RunaroundGame,
settings={'preset': 'pro'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('Uber Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'uber'},
preview_texture_name='courtyardPreview'))
campaign.addlevel(
_level.Level('Uber Football',
gametype=FootballCoopGame,
settings={'preset': 'uber'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Uber Runaround',
gametype=RunaroundGame,
settings={'preset': 'uber'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('The Last Stand',
gametype=TheLastStandGame,
settings={},
preview_texture_name='rampagePreview'))
register_campaign(campaign)
register_campaign(
Campaign(
'Default',
levels=[
Level('Onslaught Training',
gametype=OnslaughtGame,
settings={'preset': 'training'},
preview_texture_name='doomShroomPreview'),
Level('Rookie Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'rookie'},
preview_texture_name='courtyardPreview'),
Level('Rookie Football',
gametype=FootballCoopGame,
settings={'preset': 'rookie'},
preview_texture_name='footballStadiumPreview'),
Level('Pro Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'pro'},
preview_texture_name='doomShroomPreview'),
Level('Pro Football',
gametype=FootballCoopGame,
settings={'preset': 'pro'},
preview_texture_name='footballStadiumPreview'),
Level('Pro Runaround',
gametype=RunaroundGame,
settings={'preset': 'pro'},
preview_texture_name='towerDPreview'),
Level('Uber Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'uber'},
preview_texture_name='courtyardPreview'),
Level('Uber Football',
gametype=FootballCoopGame,
settings={'preset': 'uber'},
preview_texture_name='footballStadiumPreview'),
Level('Uber Runaround',
gametype=RunaroundGame,
settings={'preset': 'uber'},
preview_texture_name='towerDPreview'),
Level('The Last Stand',
gametype=TheLastStandGame,
settings={},
preview_texture_name='rampagePreview')
],
))
# challenges: our 'official' random extra co-op levels
campaign = Campaign('Challenges', sequential=False)
campaign.addlevel(
_level.Level('Infinite Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'endless'},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Infinite Runaround',
gametype=RunaroundGame,
settings={'preset': 'endless'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('Race',
displayname='${GAME}',
gametype=RaceGame,
settings={
'map': 'Big G',
'Laps': 3,
'Bomb Spawning': 0
},
preview_texture_name='bigGPreview'))
campaign.addlevel(
_level.Level('Pro Race',
displayname='Pro ${GAME}',
gametype=RaceGame,
settings={
'map': 'Big G',
'Laps': 3,
'Bomb Spawning': 1000
},
preview_texture_name='bigGPreview'))
campaign.addlevel(
_level.Level('Lake Frigid Race',
displayname='${GAME}',
gametype=RaceGame,
settings={
'map': 'Lake Frigid',
'Laps': 6,
'Mine Spawning': 2000,
'Bomb Spawning': 0
},
preview_texture_name='lakeFrigidPreview'))
campaign.addlevel(
_level.Level('Football',
displayname='${GAME}',
gametype=FootballCoopGame,
settings={'preset': 'tournament'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Pro Football',
displayname='Pro ${GAME}',
gametype=FootballCoopGame,
settings={'preset': 'tournament_pro'},
preview_texture_name='footballStadiumPreview'))
campaign.addlevel(
_level.Level('Runaround',
displayname='${GAME}',
gametype=RunaroundGame,
settings={'preset': 'tournament'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('Uber Runaround',
displayname='Uber ${GAME}',
gametype=RunaroundGame,
settings={'preset': 'tournament_uber'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('The Last Stand',
displayname='${GAME}',
gametype=TheLastStandGame,
settings={'preset': 'tournament'},
preview_texture_name='rampagePreview'))
campaign.addlevel(
_level.Level('Tournament Infinite Onslaught',
displayname='Infinite Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'endless_tournament'},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Tournament Infinite Runaround',
displayname='Infinite Runaround',
gametype=RunaroundGame,
settings={'preset': 'endless_tournament'},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('Target Practice',
displayname='Pro ${GAME}',
gametype=TargetPracticeGame,
settings={},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Target Practice B',
displayname='${GAME}',
gametype=TargetPracticeGame,
settings={
'Target Count': 2,
'Enable Impact Bombs': False,
'Enable Triple Bombs': False
},
preview_texture_name='doomShroomPreview'))
campaign.addlevel(
_level.Level('Meteor Shower',
displayname='${GAME}',
gametype=MeteorShowerGame,
settings={},
preview_texture_name='rampagePreview'))
campaign.addlevel(
_level.Level('Epic Meteor Shower',
displayname='${GAME}',
gametype=MeteorShowerGame,
settings={'Epic Mode': True},
preview_texture_name='rampagePreview'))
campaign.addlevel(
_level.Level('Easter Egg Hunt',
displayname='${GAME}',
gametype=EasterEggHuntGame,
settings={},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level('Pro Easter Egg Hunt',
displayname='Pro ${GAME}',
gametype=EasterEggHuntGame,
settings={'Pro Mode': True},
preview_texture_name='towerDPreview'))
campaign.addlevel(
_level.Level(
name='Ninja Fight', # (unique id not seen by player)
displayname='${GAME}', # (readable name seen by player)
gametype=NinjaFightGame,
settings={'preset': 'regular'},
preview_texture_name='courtyardPreview'))
campaign.addlevel(
_level.Level(name='Pro Ninja Fight',
displayname='Pro ${GAME}',
gametype=NinjaFightGame,
settings={'preset': 'pro'},
preview_texture_name='courtyardPreview'))
register_campaign(campaign)
register_campaign(
Campaign(
'Challenges',
sequential=False,
levels=[
Level('Infinite Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'endless'},
preview_texture_name='doomShroomPreview'),
Level('Infinite Runaround',
gametype=RunaroundGame,
settings={'preset': 'endless'},
preview_texture_name='towerDPreview'),
Level('Race',
displayname='${GAME}',
gametype=RaceGame,
settings={
'map': 'Big G',
'Laps': 3,
'Bomb Spawning': 0
},
preview_texture_name='bigGPreview'),
Level('Pro Race',
displayname='Pro ${GAME}',
gametype=RaceGame,
settings={
'map': 'Big G',
'Laps': 3,
'Bomb Spawning': 1000
},
preview_texture_name='bigGPreview'),
Level('Lake Frigid Race',
displayname='${GAME}',
gametype=RaceGame,
settings={
'map': 'Lake Frigid',
'Laps': 6,
'Mine Spawning': 2000,
'Bomb Spawning': 0
},
preview_texture_name='lakeFrigidPreview'),
Level('Football',
displayname='${GAME}',
gametype=FootballCoopGame,
settings={'preset': 'tournament'},
preview_texture_name='footballStadiumPreview'),
Level('Pro Football',
displayname='Pro ${GAME}',
gametype=FootballCoopGame,
settings={'preset': 'tournament_pro'},
preview_texture_name='footballStadiumPreview'),
Level('Runaround',
displayname='${GAME}',
gametype=RunaroundGame,
settings={'preset': 'tournament'},
preview_texture_name='towerDPreview'),
Level('Uber Runaround',
displayname='Uber ${GAME}',
gametype=RunaroundGame,
settings={'preset': 'tournament_uber'},
preview_texture_name='towerDPreview'),
Level('The Last Stand',
displayname='${GAME}',
gametype=TheLastStandGame,
settings={'preset': 'tournament'},
preview_texture_name='rampagePreview'),
Level('Tournament Infinite Onslaught',
displayname='Infinite Onslaught',
gametype=OnslaughtGame,
settings={'preset': 'endless_tournament'},
preview_texture_name='doomShroomPreview'),
Level('Tournament Infinite Runaround',
displayname='Infinite Runaround',
gametype=RunaroundGame,
settings={'preset': 'endless_tournament'},
preview_texture_name='towerDPreview'),
Level('Target Practice',
displayname='Pro ${GAME}',
gametype=TargetPracticeGame,
settings={},
preview_texture_name='doomShroomPreview'),
Level('Target Practice B',
displayname='${GAME}',
gametype=TargetPracticeGame,
settings={
'Target Count': 2,
'Enable Impact Bombs': False,
'Enable Triple Bombs': False
},
preview_texture_name='doomShroomPreview'),
Level('Meteor Shower',
displayname='${GAME}',
gametype=MeteorShowerGame,
settings={},
preview_texture_name='rampagePreview'),
Level('Epic Meteor Shower',
displayname='${GAME}',
gametype=MeteorShowerGame,
settings={'Epic Mode': True},
preview_texture_name='rampagePreview'),
Level('Easter Egg Hunt',
displayname='${GAME}',
gametype=EasterEggHuntGame,
settings={},
preview_texture_name='towerDPreview'),
Level('Pro Easter Egg Hunt',
displayname='Pro ${GAME}',
gametype=EasterEggHuntGame,
settings={'Pro Mode': True},
preview_texture_name='towerDPreview'),
Level(
name='Ninja Fight', # (unique id not seen by player)
displayname='${GAME}', # (readable name seen by player)
gametype=NinjaFightGame,
settings={'preset': 'regular'},
preview_texture_name='courtyardPreview'),
Level(name='Pro Ninja Fight',
displayname='Pro ${GAME}',
gametype=NinjaFightGame,
settings={'preset': 'pro'},
preview_texture_name='courtyardPreview')
],
))

View file

@ -56,6 +56,14 @@ class CloudSubsystem:
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.PingMessage,
on_response: Callable[[bacommon.cloud.PingResponse | Exception], None],
) -> None:
...
def send_message_cb(
self,
msg: Message,

View file

@ -177,7 +177,7 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]):
def spawn_player_spaz(self,
player: PlayerType,
position: Sequence[float] = (0.0, 0.0, 0.0),
angle: float = None) -> PlayerSpaz:
angle: float | None = None) -> PlayerSpaz:
"""Spawn and wire up a standard player spaz."""
spaz = super().spawn_player_spaz(player, position, angle)

View file

@ -841,7 +841,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
def spawn_player_spaz(self,
player: PlayerType,
position: Sequence[float] = (0, 0, 0),
angle: float = None) -> PlayerSpaz:
angle: float | None = None) -> PlayerSpaz:
"""Create and wire up a ba.PlayerSpaz for the provided ba.Player."""
# pylint: disable=too-many-locals
# pylint: disable=cyclic-import

View file

@ -360,7 +360,7 @@ def _verify_object_death(wref: weakref.ref) -> None:
print_active_refs(obj)
def storagename(suffix: str = None) -> str:
def storagename(suffix: str | None = None) -> str:
"""Generate a unique name for storing class data in shared places.
Category: **General Utility Functions**

View file

@ -204,6 +204,12 @@ def no_game_circle_message() -> None:
_ba.screenmessage(Lstr(resource='noGameCircleText'), color=(1, 0, 0))
def google_play_purchases_not_available_message() -> None:
from ba._language import Lstr
_ba.screenmessage(Lstr(resource='googlePlayPurchasesNotAvailableText'),
color=(1, 0, 0))
def empty_call() -> None:
pass

View file

@ -240,7 +240,7 @@ class LanguageSubsystem:
def get_resource(self,
resource: str,
fallback_resource: str = None,
fallback_resource: str | None = None,
fallback_value: Any = None) -> Any:
"""Return a translation resource by name.

View file

@ -25,7 +25,7 @@ class Level:
gametype: type[ba.GameActivity],
settings: dict,
preview_texture_name: str,
displayname: str = None):
displayname: str | None = None):
self._name = name
self._gametype = gametype
self._settings = settings

View file

@ -388,7 +388,8 @@ class Map(Actor):
assert farthestpt is not None
return tuple(farthestpt)
def get_flag_position(self, team_index: int = None) -> Sequence[float]:
def get_flag_position(self,
team_index: int | None = None) -> Sequence[float]:
"""Return a flag position on the map for the given team index.
Pass None to get the default flag point.

View file

@ -236,17 +236,17 @@ class HitMessage:
"""
def __init__(self,
srcnode: ba.Node = None,
pos: Sequence[float] = None,
velocity: Sequence[float] = None,
srcnode: ba.Node | None = None,
pos: Sequence[float] | None = None,
velocity: Sequence[float] | None = None,
magnitude: float = 1.0,
velocity_magnitude: float = 0.0,
radius: float = 1.0,
source_player: ba.Player = None,
source_player: ba.Player | None = None,
kick_back: float = 1.0,
flat_damage: float = None,
flat_damage: float | None = None,
hit_type: str = 'generic',
force_direction: Sequence[float] = None,
force_direction: Sequence[float] | None = None,
hit_subtype: str = 'default'):
"""Instantiate a message with given values."""

View file

@ -44,7 +44,7 @@ class MetadataSubsystem:
"""
def __init__(self) -> None:
self.metascan: ScanResults | None = None
self.scanresults: ScanResults | None = None
self.extra_scan_dirs: list[str] = []
def on_app_running(self) -> None:
@ -58,7 +58,7 @@ class MetadataSubsystem:
Should be called only once at launch."""
app = _ba.app
if self.metascan is not None:
if self.scanresults is not None:
print('WARNING: meta scan run more than once.')
pythondirs = ([app.python_directory_app, app.python_directory_user] +
self.extra_scan_dirs)
@ -133,7 +133,7 @@ class MetadataSubsystem:
def get_scan_results(self) -> ScanResults:
"""Return meta scan results; block if the scan is not yet complete."""
if self.metascan is None:
if self.scanresults is None:
print('WARNING: ba.meta.get_scan_results()'
' called before scan completed.'
' This can cause hitches.')
@ -141,12 +141,12 @@ class MetadataSubsystem:
# Now wait a bit for the scan to complete.
# Eventually error though if it doesn't.
starttime = time.time()
while self.metascan is None:
while self.scanresults is None:
time.sleep(0.05)
if time.time() - starttime > 10.0:
raise TimeoutError(
'timeout waiting for meta scan to complete.')
return self.metascan
return self.scanresults
def get_game_types(self) -> list[type[ba.GameActivity]]:
"""Return available game types."""
@ -206,7 +206,7 @@ class ScanThread(threading.Thread):
# We also, however, immediately make results available.
# This is because the game thread may be blocked waiting
# for them so we can't push a call or we'd get deadlock.
_ba.app.meta.metascan = results
_ba.app.meta.scanresults = results
class DirectoryScan:
@ -288,7 +288,7 @@ class DirectoryScan:
# If we find a module requiring a different api version, warn
# and ignore.
if required_api is not None and required_api <= CURRENT_API_VERSION:
if required_api is not None and required_api < CURRENT_API_VERSION:
self.results.warnings += (
f'Warning: {subpath} requires api {required_api} but'
f' we are running {CURRENT_API_VERSION}; ignoring module.\n')
@ -403,13 +403,13 @@ class DirectoryScan:
if len(lines) > 1:
self.results.warnings += (
'Warning: ' + str(subpath) +
': multiple "# ba_meta api require <NUM>" lines found;'
': multiple "# ba_meta require api <NUM>" lines found;'
' ignoring module.\n')
elif not lines and toplevel and meta_lines:
# If we're a top-level module containing meta lines but
# no valid api require, complain.
# no valid "require api" line found, complain.
self.results.warnings += (
'Warning: ' + str(subpath) +
': no valid "# ba_meta api require <NUM>" line found;'
': no valid "# ba_meta require api <NUM>" line found;'
' ignoring module.\n')
return None

View file

@ -211,7 +211,7 @@ class MusicSubsystem:
return 'Mac' in uas
if entry_type in ('musicFile', 'musicFolder'):
return ('android' in uas
and _ba.android_get_external_storage_path() is not None)
and _ba.android_get_external_files_dir() is not None)
if entry_type == 'default':
return True
return False
@ -273,7 +273,7 @@ class MusicSubsystem:
musictype: MusicType | str | None,
continuous: bool = False,
mode: MusicPlayMode = MusicPlayMode.REGULAR,
testsoundtrack: dict[str, Any] = None) -> None:
testsoundtrack: dict[str, Any] | None = None) -> None:
"""Plays the requested music type/mode.
For most cases, setmusic() is the proper call to use, which itself

View file

@ -3,6 +3,7 @@
"""Networking related functionality."""
from __future__ import annotations
import ssl
import copy
import threading
import weakref
@ -29,14 +30,32 @@ class NetworkSubsystem:
# as it is updated by a background thread.
self.zone_pings_lock = threading.Lock()
# Region IDs mapped to average pings. This will remain empty
# Zone IDs mapped to average pings. This will remain empty
# until enough pings have been run to be reasonably certain
# that a nearby server has been pinged.
self.zone_pings: dict[str, float] = {}
self._sslcontext: ssl.SSLContext | None = None
# For debugging.
self.v1_test_log: str = ''
self.v1_ctest_results: dict[int, str] = {}
self.server_time_offset_hours: float | None = None
@property
def sslcontext(self) -> ssl.SSLContext:
"""Create/return our shared SSLContext.
This can be reused for all standard urllib requests/etc.
"""
# Note: I've run into older Android devices taking upwards of 1 second
# to put together a default SSLContext, so recycling one can definitely
# be a worthwhile optimization. This was suggested to me in this
# thread by one of Python's SSL maintainers:
# https://github.com/python/cpython/issues/94637
if self._sslcontext is None:
self._sslcontext = ssl.create_default_context()
return self._sslcontext
def get_ip_address_type(addr: str) -> socket.AddressFamily:
@ -108,30 +127,36 @@ class MasterServerCallThread(threading.Thread):
self._callback(arg)
def run(self) -> None:
# pylint: disable=too-many-branches, consider-using-with
# pylint: disable=consider-using-with
import urllib.request
import urllib.parse
import urllib.error
import json
from efro.error import is_urllib_network_error
from efro.error import is_urllib_communication_error
from ba import _general
response_data: Any = None
url: str | None = None
try:
self._data = _general.utf8_all(self._data)
_ba.set_thread_name('BA_ServerCallThread')
if self._request_type == 'get':
url = (_ba.get_master_server_address() + '/' + self._request +
'?' + urllib.parse.urlencode(self._data))
response = urllib.request.urlopen(
urllib.request.Request(
(_ba.get_master_server_address() + '/' +
self._request + '?' +
urllib.parse.urlencode(self._data)), None,
{'User-Agent': _ba.app.user_agent_string}),
url, None, {'User-Agent': _ba.app.user_agent_string}),
context=_ba.app.net.sslcontext,
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
elif self._request_type == 'post':
url = _ba.get_master_server_address() + '/' + self._request
response = urllib.request.urlopen(
urllib.request.Request(
_ba.get_master_server_address() + '/' + self._request,
url,
urllib.parse.urlencode(self._data).encode(),
{'User-Agent': _ba.app.user_agent_string}),
context=_ba.app.net.sslcontext,
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
else:
raise TypeError('Invalid request_type: ' + self._request_type)
@ -151,29 +176,18 @@ class MasterServerCallThread(threading.Thread):
raise TypeError(f'invalid responsetype: {self._response_type}')
except Exception as exc:
do_print = False
response_data = None
# Ignore common network errors; note unexpected ones.
if is_urllib_network_error(exc):
pass
elif (self._response_type == MasterServerResponseType.JSON
and isinstance(exc, json.decoder.JSONDecodeError)):
# FIXME: should handle this better; could mean either the
# server sent us bad data or it got corrupted along the way.
pass
else:
do_print = True
if do_print:
# Any other error here is unexpected,
# so let's make a note of it,
if not is_urllib_communication_error(exc, url=url):
print(f'Error in MasterServerCallThread'
f' (response-type={self._response_type},'
f' (url={url},'
f' response-type={self._response_type},'
f' response-data={response_data}):')
import traceback
traceback.print_exc()
response_data = None
if self._callback is not None:
_ba.pushcall(_general.Call(self._run_callback, response_data),
from_other_thread=True)

View file

@ -66,6 +66,7 @@ class PluginSubsystem:
def load_plugins(self) -> None:
"""(internal)"""
from ba._general import getclass
from ba._language import Lstr
# Note: the plugins we load is purely based on what's enabled
# in the app config. Our meta-scan gives us a list of available
@ -76,15 +77,21 @@ class PluginSubsystem:
assert isinstance(plugstates, dict)
plugkeys: list[str] = sorted(key for key, val in plugstates.items()
if val.get('enabled', False))
disappeared_plugs: set[str] = set()
for plugkey in plugkeys:
try:
cls = getclass(plugkey, Plugin)
except ModuleNotFoundError:
disappeared_plugs.add(plugkey)
continue
except Exception as exc:
_ba.playsound(_ba.getsound('error'))
# TODO: Lstr.
errstr = f"Error loading plugin class '{plugkey}': {exc}"
_ba.screenmessage(errstr, color=(1, 0, 0))
_ba.log(errstr, to_server=False)
_ba.screenmessage(Lstr(resource='pluginClassLoadErrorText',
subs=[('${PLUGIN}', plugkey),
('${ERROR}', str(exc))]),
color=(1, 0, 0))
_ba.log(f"Error loading plugin class '{plugkey}': {exc}",
to_server=False)
continue
try:
plugin = cls()
@ -93,10 +100,31 @@ class PluginSubsystem:
except Exception as exc:
from ba import _error
_ba.playsound(_ba.getsound('error'))
# TODO: Lstr.
_ba.screenmessage(f"Error loading plugin: '{plugkey}': {exc}",
_ba.screenmessage(Lstr(resource='pluginInitErrorText',
subs=[('${PLUGIN}', plugkey),
('${ERROR}', str(exc))]),
color=(1, 0, 0))
_error.print_exception(f"Error loading plugin: '{plugkey}'.")
_error.print_exception(f"Error initing plugin: '{plugkey}'.")
# If plugins disappeared, let the user know gently and remove them
# from the config so we'll again let the user know if they later
# reappear. This makes it much smoother to switch between users
# or workspaces.
if disappeared_plugs:
_ba.playsound(_ba.getsound('shieldDown'))
_ba.screenmessage(
Lstr(resource='pluginsRemovedText',
subs=[('${NUM}', str(len(disappeared_plugs)))]),
color=(1, 1, 0),
)
plugnames = ', '.join(disappeared_plugs)
_ba.log(
f'{len(disappeared_plugs)} plugin(s) no longer found:'
f' {plugnames}.',
to_server=False)
for goneplug in disappeared_plugs:
del _ba.app.config['Plugins'][goneplug]
_ba.app.config.commit()
@dataclass

View file

@ -50,7 +50,7 @@ def get_player_profile_icon(profilename: str) -> str:
def get_player_profile_colors(
profilename: str | None,
profiles: dict[str, dict[str, Any]] = None
profiles: dict[str, dict[str, Any]] | None = None
) -> tuple[tuple[float, float, float], tuple[float, float, float]]:
"""Given a profile, return colors for them."""
appconfig = _ba.app.config

View file

@ -72,8 +72,8 @@ class Session:
def __init__(self,
depsets: Sequence[ba.DependencySet],
team_names: Sequence[str] = None,
team_colors: Sequence[Sequence[float]] = None,
team_names: Sequence[str] | None = None,
team_colors: Sequence[Sequence[float]] | None = None,
min_players: int = 1,
max_players: int = 8):
"""Instantiate a session.

View file

@ -314,11 +314,11 @@ class Stats:
def player_scored(self,
player: ba.Player,
base_points: int = 1,
target: Sequence[float] = None,
target: Sequence[float] | None = None,
kill: bool = False,
victim_player: ba.Player = None,
victim_player: ba.Player | None = None,
scale: float = 1.0,
color: Sequence[float] = None,
color: Sequence[float] | None = None,
title: str | ba.Lstr | None = None,
screenmessage: bool = True,
display: bool = True,
@ -422,7 +422,7 @@ class Stats:
def player_was_killed(self,
player: ba.Player,
killed: bool = False,
killer: ba.Player = None) -> None:
killer: ba.Player | None = None) -> None:
"""Should be called when a player is killed."""
from ba._language import Lstr
name = player.getname()

View file

@ -398,7 +398,7 @@ def get_clean_price(price_string: str) -> str:
return psubs.get(price_string, price_string)
def get_available_purchase_count(tab: str = None) -> int:
def get_available_purchase_count(tab: str | None = None) -> int:
"""(internal)"""
try:
if _ba.get_v1_account_state() != 'signed_in':

View file

@ -91,8 +91,8 @@ class TeamGameActivity(GameActivity[PlayerType, TeamType]):
def spawn_player_spaz(self,
player: PlayerType,
position: Sequence[float] = None,
angle: float = None) -> PlayerSpaz:
position: Sequence[float] | None = None,
angle: float | None = None) -> PlayerSpaz:
"""
Method override; spawns and wires up a standard ba.PlayerSpaz for
a ba.Player.

View file

@ -144,7 +144,7 @@ class UISubsystem:
_ba.timer(1.0, _delay_kill, timetype=TimeType.REAL)
self._main_menu_window = window
def clear_main_menu_window(self, transition: str = None) -> None:
def clear_main_menu_window(self, transition: str | None = None) -> None:
"""Clear any existing 'main' window with the provided transition."""
if self._main_menu_window:
if transition is not None:

View file

@ -52,17 +52,17 @@ class WorkspaceSubsystem:
daemon=True,
).start()
def _errmsg(self, msg: str | ba.Lstr) -> None:
def _errmsg(self, msg: ba.Lstr) -> None:
_ba.screenmessage(msg, color=(1, 0, 0))
_ba.playsound(_ba.getsound('error'))
def _successmsg(self, msg: str | ba.Lstr) -> None:
def _successmsg(self, msg: ba.Lstr) -> None:
_ba.screenmessage(msg, color=(0, 1, 0))
_ba.playsound(_ba.getsound('gunCocking'))
def _set_active_workspace_bg(self, workspaceid: str, workspacename: str,
on_completed: Callable[[], None]) -> None:
# pylint: disable=too-many-branches
from ba._language import Lstr
class _SkipSyncError(RuntimeError):
pass
@ -101,45 +101,42 @@ class WorkspaceSubsystem:
break
state.iteration += 1
extras: list[str] = []
# Hmm; let's not show deletes for now since currently lots of
# .pyc files get deleted.
if bool(False):
if state.total_deletes:
extras.append(f'{state.total_deletes} files deleted')
if state.total_downloads:
extras.append(f'{state.total_downloads} files downloaded')
if state.total_up_to_date:
extras.append(f'{state.total_up_to_date} files up-to-date')
# Actually let's try with none of this; seems a bit excessive.
if bool(False) and extras:
extras_s = '\n' + ', '.join(extras) + '.'
else:
extras_s = ''
_ba.pushcall(tpartial(self._successmsg,
f'{workspacename} activated.{extras_s}'),
from_other_thread=True)
_ba.pushcall(
tpartial(
self._successmsg,
Lstr(resource='activatedText',
subs=[('${THING}', workspacename)]),
),
from_other_thread=True,
)
except _SkipSyncError:
_ba.pushcall(tpartial(
self._errmsg, f'Can\'t sync {workspacename}'
f'. Reusing previous synced version.'),
from_other_thread=True)
_ba.pushcall(
tpartial(
self._errmsg,
Lstr(resource='workspaceSyncReuseText',
subs=[('${WORKSPACE}', workspacename)])),
from_other_thread=True,
)
except CleanError as exc:
# Avoid reusing existing if we fail in the middle; could
# be in wonky state.
set_path = False
_ba.pushcall(tpartial(self._errmsg, str(exc)),
_ba.pushcall(tpartial(self._errmsg, Lstr(value=str(exc))),
from_other_thread=True)
except Exception:
# Ditto.
set_path = False
logging.exception('Error syncing workspace.')
# TODO: Lstr.
_ba.pushcall(tpartial(
self._errmsg, 'Error syncing workspace. See log for details.'),
from_other_thread=True)
logging.exception("Error syncing workspace '%s'.", workspacename)
_ba.pushcall(
tpartial(
self._errmsg,
Lstr(resource='workspaceSyncErrorText',
subs=[('${WORKSPACE}', workspacename)]),
),
from_other_thread=True,
)
if set_path and wspath.is_dir():
# Add to Python paths and also to list of stuff to be scanned

View file

@ -17,23 +17,29 @@ def get_human_readable_user_scripts_path() -> str:
This is NOT a valid filesystem path; may be something like "(SD Card)".
"""
from ba import _language
app = _ba.app
path: str | None = app.python_directory_user
if path is None:
return '<Not Available>'
# On newer versions of android, the user's external storage dir is probably
# only visible to the user's processes and thus not really useful printed
# in its entirety; lets print it as <External Storage>/myfilepath.
# These days, on Android, we use getExternalFilesDir() as the base of our
# app's user-scripts dir, which gives us paths like:
# /storage/emulated/0/Android/data/net.froemling.bombsquad/files
# Userspace apps tend to show that as:
# Android/data/net.froemling.bombsquad/files
# We'd like to display it that way, but I'm not sure if there's a clean
# way to get the root of the external storage area (/storage/emulated/0)
# so that we could strip it off. There is
# Environment.getExternalStorageDirectory() but that is deprecated.
# So for now let's just be conservative and trim off recognized prefixes
# and show the whole ugly path as a fallback.
# Note that we used to use externalStorageText resource but gonna try
# without it for now. (simply 'foo' instead of <External Storage>/foo).
if app.platform == 'android':
ext_storage_path: str | None = (
_ba.android_get_external_storage_path())
if (ext_storage_path is not None
and app.python_directory_user.startswith(ext_storage_path)):
path = ('<' +
_language.Lstr(resource='externalStorageText').evaluate() +
'>' + app.python_directory_user[len(ext_storage_path):])
for pre in ['/storage/emulated/0/']:
if path.startswith(pre):
path = path.removeprefix(pre)
break
return path

View file

@ -14,7 +14,7 @@ if TYPE_CHECKING:
# Version is sent to the master-server with all commands. Can be incremented
# if we need to change behavior server-side to go along with client changes.
BACLOUD_VERSION = 6
BACLOUD_VERSION = 7
@ioprepped

View file

@ -76,6 +76,22 @@ class LoginProxyCompleteMessage(Message):
proxyid: Annotated[str, IOAttrs('p')]
@ioprepped
@dataclass
class PingMessage(Message):
"""Standard ping."""
@classmethod
def get_response_types(cls) -> list[type[Response]]:
return [PingResponse]
@ioprepped
@dataclass
class PingResponse(Response):
"""pong."""
@ioprepped
@dataclass
class TestMessage(Message):

View file

@ -4,6 +4,7 @@
from __future__ import annotations
import datetime
from typing import TYPE_CHECKING, Any, Annotated
from dataclasses import dataclass, field
@ -27,6 +28,9 @@ class ServerNodeEntry:
class ServerNodeQueryResponse:
"""A response to a query about server-nodes."""
# The current utc time on the master server.
time: Annotated[datetime.datetime, IOAttrs('t')]
# If present, something went wrong, and this describes it.
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None

View file

@ -5,13 +5,14 @@
from __future__ import annotations
import os
from pathlib import Path
from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated
from efro.dataclassio import ioprepped, IOAttrs
if TYPE_CHECKING:
from pathlib import Path
pass
@ioprepped
@ -40,15 +41,17 @@ class DirectoryManifest:
paths: list[str] = []
if path.is_dir():
# Build the full list of package-relative paths.
# Build the full list of relative paths.
for basename, _dirnames, filenames in os.walk(path):
for filename in filenames:
fullname = os.path.join(basename, filename)
assert fullname.startswith(pathstr)
paths.append(fullname[len(pathstr) + 1:])
# Make sure we end up with forward slashes no matter
# what the os.* stuff above here was using.
paths.append(Path(fullname[len(pathstr) + 1:]).as_posix())
elif path.exists():
# Just return a single file entry if path is not a dir.
paths.append(pathstr)
paths.append(path.as_posix())
def _get_file_info(filepath: str) -> tuple[str, DirectoryManifestFile]:
sha = hashlib.sha256()
@ -70,6 +73,18 @@ class DirectoryManifest:
with ThreadPoolExecutor(max_workers=cpus) as executor:
return cls(files=dict(executor.map(_get_file_info, paths)))
def validate(self) -> None:
"""Log any odd data in the manifest; for debugging."""
import logging
for fpath, _fentry in self.files.items():
# We want to be dealing in only forward slashes; make sure
# that's the case (wondering if we'll ever see backslashes
# for escape purposes).
if '\\' in fpath:
logging.exception("Found unusual path in manifest: '%s'.",
fpath)
break # 1 error is enough for now.
@classmethod
def get_empty_hash(cls) -> str:
"""Return the hash for an empty file."""

View file

@ -327,7 +327,7 @@ class Blast(ba.Actor):
velocity: Sequence[float] = (0.0, 0.0, 0.0),
blast_radius: float = 2.0,
blast_type: str = 'normal',
source_player: ba.Player = None,
source_player: ba.Player | None = None,
hit_type: str = 'explosion',
hit_subtype: str = 'normal'):
"""Instantiate with given values."""
@ -657,8 +657,8 @@ class Bomb(ba.Actor):
bomb_type: str = 'normal',
blast_radius: float = 2.0,
bomb_scale: float = 1.0,
source_player: ba.Player = None,
owner: ba.Node = None):
source_player: ba.Player | None = None,
owner: ba.Node | None = None):
"""Create a new Bomb.
bomb_type can be 'ice','impact','land_mine','normal','sticky', or

View file

@ -27,7 +27,7 @@ class ControlsGuide(ba.Actor):
position: tuple[float, float] = (390.0, 120.0),
scale: float = 1.0,
delay: float = 0.0,
lifespan: float = None,
lifespan: float | None = None,
bright: bool = False):
"""Instantiate an overlay.

View file

@ -167,9 +167,9 @@ class Flag(ba.Actor):
def __init__(self,
position: Sequence[float] = (0.0, 1.0, 0.0),
color: Sequence[float] = (1.0, 1.0, 1.0),
materials: Sequence[ba.Material] = None,
materials: Sequence[ba.Material] | None = None,
touchable: bool = True,
dropped_timeout: int = None):
dropped_timeout: int | None = None):
"""Instantiate a flag.
If 'touchable' is False, the flag will only touch terrain;

View file

@ -40,9 +40,9 @@ class Image(ba.Actor):
attach: Attach = Attach.CENTER,
color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
scale: tuple[float, float] = (100.0, 100.0),
transition_out_delay: float = None,
model_opaque: ba.Model = None,
model_transparent: ba.Model = None,
transition_out_delay: float | None = None,
model_opaque: ba.Model | None = None,
model_transparent: ba.Model | None = None,
vr_depth: float = 0.0,
host_only: bool = False,
front: bool = False):

View file

@ -20,7 +20,9 @@ class OnScreenCountdown(ba.Actor):
Useful for time-based games that count down to zero.
"""
def __init__(self, duration: int, endcall: Callable[[], Any] = None):
def __init__(self,
duration: int,
endcall: Callable[[], Any] | None = None):
"""Duration is provided in seconds."""
super().__init__()
self._timeremaining = duration
@ -73,7 +75,7 @@ class OnScreenCountdown(ba.Actor):
# Release callbacks/refs.
self._endcall = None
def _update(self, forcevalue: int = None) -> None:
def _update(self, forcevalue: int | None = None) -> None:
if forcevalue is not None:
tval = forcevalue
else:

View file

@ -137,8 +137,8 @@ class PowerupBoxFactory:
self._powerupdist.append(powerup)
def get_random_powerup_type(self,
forcetype: str = None,
excludetypes: list[str] = None) -> str:
forcetype: str | None = None,
excludetypes: list[str] | None = None) -> str:
"""Returns a random powerup type (string).
See ba.Powerup.poweruptype for available type values.

View file

@ -241,7 +241,7 @@ class _Entry:
def set_value(self,
score: float,
max_score: float = None,
max_score: float | None = None,
countdown: bool = False,
flash: bool = True,
show_value: bool = True) -> None:
@ -327,7 +327,7 @@ class Scoreboard:
_ENTRYSTORENAME = ba.storagename('entry')
def __init__(self, label: ba.Lstr = None, score_split: float = 0.7):
def __init__(self, label: ba.Lstr | None = None, score_split: float = 0.7):
"""Instantiate a scoreboard.
Label can be something like 'points' and will
@ -356,7 +356,7 @@ class Scoreboard:
def set_team_value(self,
team: ba.Team,
score: float,
max_score: float = None,
max_score: float | None = None,
countdown: bool = False,
flash: bool = True,
show_value: bool = True) -> None:

View file

@ -54,7 +54,7 @@ class Spawner:
pt: Sequence[float] = (0, 0, 0), # pylint: disable=invalid-name
spawn_time: float = 1.0,
send_spawn_message: bool = True,
spawn_callback: Callable[[], Any] = None):
spawn_callback: Callable[[], Any] | None = None):
"""Instantiate a Spawner.
Requires some custom data, a position,

View file

@ -67,7 +67,7 @@ class Spaz(ba.Actor):
color: Sequence[float] = (1.0, 1.0, 1.0),
highlight: Sequence[float] = (0.5, 0.5, 0.5),
character: str = 'Spaz',
source_player: ba.Player = None,
source_player: ba.Player | None = None,
start_invincible: bool = True,
can_accept_powerups: bool = True,
powerups_expire: bool = False,
@ -177,6 +177,7 @@ class Spaz(ba.Actor):
self._bomb_wear_off_timer: ba.Timer | None = None
self._bomb_wear_off_flash_timer: ba.Timer | None = None
self._multi_bomb_wear_off_timer: ba.Timer | None = None
self._multi_bomb_wear_off_flash_timer: ba.Timer | None = None
self.bomb_count = self.default_bomb_count
self._max_bomb_count = self.default_bomb_count
self.bomb_type_default = self.default_bomb_type
@ -605,6 +606,7 @@ class Spaz(ba.Actor):
"""
assert self.node
self.node.boxing_gloves = True
self._has_boxing_gloves = True
if self._demo_mode: # Preserve old behavior.
self._punch_power_scale = 1.7
self._punch_cooldown = 300
@ -704,7 +706,7 @@ class Spaz(ba.Actor):
self.node.mini_billboard_1_start_time = t_ms
self.node.mini_billboard_1_end_time = (
t_ms + POWERUP_WEAR_OFF_TIME)
self._multi_bomb_wear_off_timer = (ba.Timer(
self._multi_bomb_wear_off_flash_timer = (ba.Timer(
(POWERUP_WEAR_OFF_TIME - 2000),
ba.WeakCall(self._multi_bomb_wear_off_flash),
timeformat=ba.TimeFormat.MILLISECONDS))
@ -753,7 +755,6 @@ class Spaz(ba.Actor):
ba.WeakCall(self._bomb_wear_off),
timeformat=ba.TimeFormat.MILLISECONDS))
elif msg.poweruptype == 'punch':
self._has_boxing_gloves = True
tex = PowerupBoxFactory.get().tex_punch
self._flash_billboard(tex)
self.equip_boxing_gloves()
@ -1266,7 +1267,7 @@ class Spaz(ba.Actor):
else:
self.node.counter_text = ''
def curse_explode(self, source_player: ba.Player = None) -> None:
def curse_explode(self, source_player: ba.Player | None = None) -> None:
"""Explode the poor spaz spectacularly."""
if self._cursed and self.node:
self.shatter(extreme=True)

View file

@ -888,11 +888,12 @@ class SpazBotSet:
def __del__(self) -> None:
self.clear()
def spawn_bot(self,
bot_type: type[SpazBot],
pos: Sequence[float],
spawn_time: float = 3.0,
on_spawn_call: Callable[[SpazBot], Any] = None) -> None:
def spawn_bot(
self,
bot_type: type[SpazBot],
pos: Sequence[float],
spawn_time: float = 3.0,
on_spawn_call: Callable[[SpazBot], Any] | None = None) -> None:
"""Spawn a bot from this set."""
from bastd.actor import spawner
spawner.Spawner(pt=pos,

View file

@ -60,8 +60,8 @@ class Text(ba.Actor):
v_attach: VAttach = VAttach.CENTER,
h_attach: HAttach = HAttach.CENTER,
scale: float = 1.0,
transition_out_delay: float = None,
maxwidth: float = None,
transition_out_delay: float | None = None,
maxwidth: float | None = None,
shadow: float = 0.5,
flatness: float = 0.0,
vr_depth: float = 0.0,

View file

@ -24,9 +24,9 @@ class ZoomText(ba.Actor):
def __init__(self,
text: str | ba.Lstr,
position: tuple[float, float] = (0.0, 0.0),
shiftposition: tuple[float, float] = None,
shiftdelay: float = None,
lifespan: float = None,
shiftposition: tuple[float, float] | None = None,
shiftdelay: float | None = None,
lifespan: float | None = None,
flash: bool = True,
trail: bool = True,
h_align: str = 'center',
@ -36,7 +36,7 @@ class ZoomText(ba.Actor):
scale: float = 1.0,
project_scale: float = 1.0,
tilt_translate: float = 0.0,
maxwidth: float = None):
maxwidth: float | None = None):
# pylint: disable=too-many-locals
super().__init__()
self._dying = False

View file

@ -492,8 +492,8 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
def spawn_player_spaz(self,
player: Player,
position: Sequence[float] = None,
angle: float = None) -> PlayerSpaz:
position: Sequence[float] | None = None,
angle: float | None = None) -> PlayerSpaz:
"""Intercept new spazzes and add our team material for them."""
spaz = super().spawn_player_spaz(player, position, angle)
player = spaz.getplayer(Player, True)

View file

@ -137,9 +137,6 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
self.setup_standard_powerup_drops()
self._flag_spawn_pos = self.map.get_flag_position(None)
Flag.project_stand(self._flag_spawn_pos)
self._set_chosen_one_player(None)
pos = self._flag_spawn_pos
ba.timer(1.0, call=self._tick, repeat=True)
mat = self._reset_region_material = ba.Material()
@ -156,14 +153,20 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
),
)
self._reset_region = ba.newnode('region',
attrs={
'position': (pos[0], pos[1] + 0.75,
pos[2]),
'scale': (0.5, 0.5, 0.5),
'type': 'sphere',
'materials': [mat]
})
self._set_chosen_one_player(None)
def _create_reset_region(self) -> None:
assert self._reset_region_material is not None
assert self._flag_spawn_pos is not None
pos = self._flag_spawn_pos
self._reset_region = ba.newnode(
'region',
attrs={
'position': (pos[0], pos[1] + 0.75, pos[2]),
'scale': (0.5, 0.5, 0.5),
'type': 'sphere',
'materials': [self._reset_region_material]
})
def _get_chosen_one_player(self) -> Player | None:
# Should never return invalid references; return None in that case.
@ -176,14 +179,14 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
if self._get_chosen_one_player() is not None:
return
# Attempt to get a Player controlling a Spaz that we hit.
# Attempt to get a Actor that we hit.
try:
player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player, True)
spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True)
player = spaz.getplayer(Player, True)
except ba.NotFoundError:
return
if player.is_alive():
if spaz.is_alive():
self._set_chosen_one_player(player)
def _flash_flag_spawn(self) -> None:
@ -278,6 +281,10 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
# Also an extra momentary flash.
self._flash_flag_spawn()
# Re-create our flag region in case if someone is waiting for
# flag right there:
self._create_reset_region()
else:
if player.actor:
self._flag = None

View file

@ -596,7 +596,9 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
if closest_bot is not None:
closest_bot.target_flag = self._flag
def _drop_powerup(self, index: int, poweruptype: str = None) -> None:
def _drop_powerup(self,
index: int,
poweruptype: str | None = None) -> None:
if poweruptype is None:
poweruptype = (PowerupBoxFactory.get().get_random_powerup_type(
excludetypes=self._exclude_powerups))
@ -610,7 +612,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
def _drop_powerups(self,
standard_points: bool = False,
poweruptype: str = None) -> None:
poweruptype: str | None = None) -> None:
"""Generic powerup drop."""
if standard_points:
spawnpoints = self.map.powerup_spawn_points

View file

@ -5,6 +5,9 @@
# Yes this is a long one..
# pylint: disable=too-many-lines
# ba_meta require api 7
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import math
@ -690,7 +693,9 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
del player, bomb # Unused.
self._player_has_dropped_bomb = True
def _drop_powerup(self, index: int, poweruptype: str = None) -> None:
def _drop_powerup(self,
index: int,
poweruptype: str | None = None) -> None:
poweruptype = (PowerupBoxFactory.get().get_random_powerup_type(
forcetype=poweruptype, excludetypes=self._excluded_powerups))
PowerupBox(position=self.map.powerup_spawn_points[index],
@ -703,7 +708,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
def _drop_powerups(self,
standard_points: bool = False,
poweruptype: str = None) -> None:
poweruptype: str | None = None) -> None:
"""Generic powerup drop."""
if standard_points:
points = self.map.powerup_spawn_points

View file

@ -5,6 +5,9 @@
# We wear the cone of shame.
# pylint: disable=too-many-lines
# ba_meta require api 7
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import random
@ -497,7 +500,9 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
del player # Unused.
self._player_has_picked_up_powerup = True
def _drop_powerup(self, index: int, poweruptype: str = None) -> None:
def _drop_powerup(self,
index: int,
poweruptype: str | None = None) -> None:
if poweruptype is None:
poweruptype = (PowerupBoxFactory.get().get_random_powerup_type(
excludetypes=self._exclude_powerups))
@ -509,7 +514,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
def _drop_powerups(self,
standard_points: bool = False,
force_first: str = None) -> None:
force_first: str | None = None) -> None:
"""Generic powerup drop."""
# If its been a minute since our last wave finished emerging, stop

View file

@ -132,7 +132,9 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
self._bot_update_timer = ba.Timer(self._bot_update_interval,
ba.WeakCall(self._update_bots))
def _drop_powerup(self, index: int, poweruptype: str = None) -> None:
def _drop_powerup(self,
index: int,
poweruptype: str | None = None) -> None:
if poweruptype is None:
poweruptype = (PowerupBoxFactory.get().get_random_powerup_type(
excludetypes=self._excludepowerups))
@ -146,7 +148,7 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
def _drop_powerups(self,
standard_points: bool = False,
force_first: str = None) -> None:
force_first: str | None = None) -> None:
"""Generic powerup drop."""
from bastd.actor import powerupbox
if standard_points:

View file

@ -767,7 +767,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
y: float,
scale: float,
delay: float,
custom_texture: str = None,
custom_texture: str | None = None,
jitter_scale: float = 1.0,
rotate: float = 0.0,
vr_depth_offset: float = 0.0) -> None:

View file

@ -178,7 +178,7 @@ class Team(ba.Team[Player]):
class TutorialActivity(ba.Activity[Player, Team]):
def __init__(self, settings: dict = None):
def __init__(self, settings: dict | None = None):
from bastd.maps import Rampage
if settings is None:
settings = {}
@ -492,7 +492,7 @@ class TutorialActivity(ba.Activity[Player, Team]):
position: Sequence[float],
color: Sequence[float] = (1.0, 1.0, 1.0),
make_current: bool = False,
relative_to: int = None,
relative_to: int | None = None,
name: str | ba.Lstr = '',
flash: bool = True,
angle: float = 0.0):
@ -550,7 +550,7 @@ class TutorialActivity(ba.Activity[Player, Team]):
position: Sequence[float],
color: Sequence[float] = (1.0, 1.0, 1.0),
make_current: bool = False,
relative_to: int = None):
relative_to: int | None = None):
self._position = position
self._relative_to = relative_to
@ -761,7 +761,7 @@ class TutorialActivity(ba.Activity[Player, Team]):
class PrintPos:
def __init__(self, spaz_num: int = None):
def __init__(self, spaz_num: int | None = None):
self._spaz_num = spaz_num
def run(self, a: TutorialActivity) -> None:
@ -787,7 +787,7 @@ class TutorialActivity(ba.Activity[Player, Team]):
def __init__(self,
celebrate_type: str = 'both',
spaz_num: int = None,
spaz_num: int | None = None,
duration: int = 1000):
self._spaz_num = spaz_num
self._celebrate_type = celebrate_type

View file

@ -8,7 +8,7 @@ import _ba
import ba
def show_sign_in_prompt(account_type: str = None) -> None:
def show_sign_in_prompt(account_type: str | None = None) -> None:
"""Bring up a prompt telling the user they must sign in."""
from bastd.ui.confirm import ConfirmWindow
from bastd.ui.account import settings

View file

@ -18,7 +18,7 @@ if TYPE_CHECKING:
class AccountLinkWindow(ba.Window):
"""Window for linking accounts."""
def __init__(self, origin_widget: ba.Widget = None):
def __init__(self, origin_widget: ba.Widget | None = None):
scale_origin: tuple[float, float] | None
if origin_widget is not None:
self._transition_out = 'out_scale'

View file

@ -21,7 +21,7 @@ class AccountSettingsWindow(ba.Window):
def __init__(self,
transition: str = 'in_right',
modal: bool = False,
origin_widget: ba.Widget = None,
origin_widget: ba.Widget | None = None,
close_once_signed_in: bool = False):
# pylint: disable=too-many-statements

View file

@ -17,7 +17,7 @@ if TYPE_CHECKING:
class AccountUnlinkWindow(ba.Window):
"""A window to kick off account unlinks."""
def __init__(self, origin_widget: ba.Widget = None):
def __init__(self, origin_widget: ba.Widget | None = None):
scale_origin: tuple[float, float] | None
if origin_widget is not None:
self._transition_out = 'out_scale'

View file

@ -19,9 +19,9 @@ class AccountViewerWindow(popup.PopupWindow):
def __init__(self,
account_id: str,
profile_id: str = None,
profile_id: str | None = None,
position: tuple[float, float] = (0.0, 0.0),
scale: float = None,
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0)):
from ba.internal import is_browser_likely_available, master_server_get

View file

@ -16,7 +16,9 @@ if TYPE_CHECKING:
class AchievementsWindow(popup.PopupWindow):
"""Popup window to view achievements."""
def __init__(self, position: tuple[float, float], scale: float = None):
def __init__(self,
position: tuple[float, float],
scale: float | None = None):
# pylint: disable=too-many-locals
uiscale = ba.app.ui.uiscale
if scale is None:

View file

@ -22,11 +22,11 @@ class CharacterPicker(popup.PopupWindow):
parent: ba.Widget,
position: tuple[float, float] = (0.0, 0.0),
delegate: Any = None,
scale: float = None,
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0),
tint_color: Sequence[float] = (1.0, 1.0, 1.0),
tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
selected_character: str = None):
selected_character: str | None = None):
# pylint: disable=too-many-locals
from bastd.actor import spazappearance
del parent # unused here

View file

@ -24,7 +24,7 @@ class ColorPicker(PopupWindow):
position: tuple[float, float],
initial_color: Sequence[float] = (1.0, 1.0, 1.0),
delegate: Any = None,
scale: float = None,
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0),
tag: Any = ''):
# pylint: disable=too-many-locals
@ -162,7 +162,7 @@ class ColorPickerExact(PopupWindow):
position: tuple[float, float],
initial_color: Sequence[float] = (1.0, 1.0, 1.0),
delegate: Any = None,
scale: float = None,
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0),
tag: Any = ''):
# pylint: disable=too-many-locals

View file

@ -28,10 +28,10 @@ class ConfigCheckBox:
position: tuple[float, float],
size: tuple[float, float],
displayname: str | ba.Lstr | None = None,
scale: float = None,
maxwidth: float = None,
scale: float | None = None,
maxwidth: float | None = None,
autoselect: bool = True,
value_change_call: Callable[[Any], Any] = None):
value_change_call: Callable[[Any], Any] | None = None):
if displayname is None:
displayname = configkey
self._value_change_call = value_change_call
@ -84,7 +84,7 @@ class ConfigNumberEdit:
minval: float = 0.0,
maxval: float = 100.0,
increment: float = 1.0,
callback: Callable[[float], Any] = None,
callback: Callable[[float], Any] | None = None,
xoffset: float = 0.0,
displayname: str | ba.Lstr | None = None,
changesound: bool = True,

View file

@ -18,7 +18,7 @@ class ConfirmWindow:
def __init__(self,
text: str | ba.Lstr = 'Are you sure?',
action: Callable[[], Any] = None,
action: Callable[[], Any] | None = None,
width: float = 360.0,
height: float = 100.0,
cancel_button: bool = True,
@ -27,7 +27,7 @@ class ConfirmWindow:
text_scale: float = 1.0,
ok_text: str | ba.Lstr | None = None,
cancel_text: str | ba.Lstr | None = None,
origin_widget: ba.Widget = None):
origin_widget: ba.Widget | None = None):
# pylint: disable=too-many-locals
if ok_text is None:
ok_text = ba.Lstr(resource='okText')
@ -127,7 +127,7 @@ class QuitWindow:
def __init__(self,
swish: bool = False,
back: bool = False,
origin_widget: ba.Widget = None):
origin_widget: ba.Widget | None = None):
ui = ba.app.ui
app = ba.app
self._back = back

View file

@ -6,7 +6,6 @@
from __future__ import annotations
import copy
from typing import TYPE_CHECKING
import _ba
@ -18,6 +17,8 @@ from bastd.ui.store.browser import StoreBrowserWindow
if TYPE_CHECKING:
from typing import Any
from bastd.ui.coop.tournamentbutton import TournamentButton
class CoopBrowserWindow(ba.Window):
"""Window for browsing co-op levels/games/etc."""
@ -37,7 +38,7 @@ class CoopBrowserWindow(ba.Window):
def __init__(self,
transition: str | None = 'in_right',
origin_widget: ba.Widget = None):
origin_widget: ba.Widget | None = None):
# pylint: disable=too-many-statements
# pylint: disable=cyclic-import
import threading
@ -175,8 +176,6 @@ class CoopBrowserWindow(ba.Window):
'Selected Coop Campaign Level', None))
self._selected_custom_level = (cfg.get('Selected Coop Custom Level',
None))
self._selected_challenge_level = (cfg.get(
'Selected Coop Challenge Level', None))
# Don't want initial construction affecting our last-selected.
self._do_selection_callbacks = False
@ -283,6 +282,7 @@ class CoopBrowserWindow(ba.Window):
import bastd.ui.tournamentscores as _unused8
import bastd.ui.tournamententry as _unused9
import bastd.ui.play as _unused10
import bastd.ui.coop.tournamentbutton as _unused11
def _update(self) -> None:
# Do nothing if we've somehow outlived our actual UI.
@ -335,21 +335,21 @@ class CoopBrowserWindow(ba.Window):
# Decrement time on our tournament buttons.
ads_enabled = _ba.have_incentivized_ad()
for tbtn in self._tournament_buttons:
tbtn['time_remaining'] = max(0, tbtn['time_remaining'] - 1)
if tbtn['time_remaining_value_text'] is not None:
tbtn.time_remaining = max(0, tbtn.time_remaining - 1)
if tbtn.time_remaining_value_text is not None:
ba.textwidget(
edit=tbtn['time_remaining_value_text'],
text=ba.timestring(tbtn['time_remaining'],
edit=tbtn.time_remaining_value_text,
text=ba.timestring(tbtn.time_remaining,
centi=False,
suppress_format_warning=True) if
(tbtn['has_time_remaining']
(tbtn.has_time_remaining
and self._tourney_data_up_to_date) else '-')
# Also adjust the ad icon visibility.
if tbtn.get('allow_ads', False) and _ba.has_video_ads():
ba.imagewidget(edit=tbtn['entry_fee_ad_image'],
if tbtn.allow_ads and _ba.has_video_ads():
ba.imagewidget(edit=tbtn.entry_fee_ad_image,
opacity=1.0 if ads_enabled else 0.25)
ba.textwidget(edit=tbtn['entry_fee_text_remaining'],
ba.textwidget(edit=tbtn.entry_fee_text_remaining,
color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2))
self._update_hard_mode_lock_image()
@ -363,232 +363,21 @@ class CoopBrowserWindow(ba.Window):
ba.print_exception('Error updating campaign lock.')
def _update_for_data(self, data: list[dict[str, Any]] | None) -> None:
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
from ba.internal import getcampaign, get_tournament_prize_strings
# If the number of tournaments or challenges in the data differs from
# our current arrangement, refresh with the new number.
if ((data is None and self._tournament_button_count != 0)
or (data is not None and
(len(data) != self._tournament_button_count))):
self._tournament_button_count = len(
data) if data is not None else 0
self._tournament_button_count = (len(data)
if data is not None else 0)
ba.app.config['Tournament Rows'] = self._tournament_button_count
self._refresh()
# Update all of our tourney buttons based on whats in data.
for i, tbtn in enumerate(self._tournament_buttons):
assert data is not None
entry: dict[str, Any] = data[i]
prize_y_offs = (34 if 'prizeRange3' in entry else
20 if 'prizeRange2' in entry else 12)
x_offs = 90
# This seems to be a false alarm.
# pylint: disable=unbalanced-tuple-unpacking
pr1, pv1, pr2, pv2, pr3, pv3 = (
get_tournament_prize_strings(entry))
# pylint: enable=unbalanced-tuple-unpacking
enabled = 'requiredLeague' not in entry
ba.buttonwidget(edit=tbtn['button'],
color=(0.5, 0.7, 0.2) if enabled else
(0.5, 0.5, 0.5))
ba.imagewidget(edit=tbtn['lock_image'],
opacity=0.0 if enabled else 1.0)
ba.textwidget(edit=tbtn['prize_range_1_text'],
text='-' if pr1 == '' else pr1,
position=(tbtn['button_x'] + 365 + x_offs,
tbtn['button_y'] + tbtn['button_scale_y'] -
93 + prize_y_offs))
# We want to draw values containing tickets a bit smaller
# (scratch that; we now draw medals a bit bigger).
ticket_char = ba.charstr(ba.SpecialChar.TICKET_BACKING)
prize_value_scale_large = 1.0
prize_value_scale_small = 1.0
ba.textwidget(edit=tbtn['prize_value_1_text'],
text='-' if pv1 == '' else pv1,
scale=prize_value_scale_large if ticket_char
not in pv1 else prize_value_scale_small,
position=(tbtn['button_x'] + 380 + x_offs,
tbtn['button_y'] + tbtn['button_scale_y'] -
93 + prize_y_offs))
ba.textwidget(edit=tbtn['prize_range_2_text'],
text=pr2,
position=(tbtn['button_x'] + 365 + x_offs,
tbtn['button_y'] + tbtn['button_scale_y'] -
93 - 45 + prize_y_offs))
ba.textwidget(edit=tbtn['prize_value_2_text'],
text=pv2,
scale=prize_value_scale_large if ticket_char
not in pv2 else prize_value_scale_small,
position=(tbtn['button_x'] + 380 + x_offs,
tbtn['button_y'] + tbtn['button_scale_y'] -
93 - 45 + prize_y_offs))
ba.textwidget(edit=tbtn['prize_range_3_text'],
text=pr3,
position=(tbtn['button_x'] + 365 + x_offs,
tbtn['button_y'] + tbtn['button_scale_y'] -
93 - 90 + prize_y_offs))
ba.textwidget(edit=tbtn['prize_value_3_text'],
text=pv3,
scale=prize_value_scale_large if ticket_char
not in pv3 else prize_value_scale_small,
position=(tbtn['button_x'] + 380 + x_offs,
tbtn['button_y'] + tbtn['button_scale_y'] -
93 - 90 + prize_y_offs))
leader_name = '-'
leader_score: str | ba.Lstr = '-'
if entry['scores']:
score = tbtn['leader'] = copy.deepcopy(entry['scores'][0])
leader_name = score[1]
leader_score = (ba.timestring(
score[0] * 10,
centi=True,
timeformat=ba.TimeFormat.MILLISECONDS,
suppress_format_warning=True) if entry['scoreType']
== 'time' else str(score[0]))
else:
tbtn['leader'] = None
ba.textwidget(edit=tbtn['current_leader_name_text'],
text=ba.Lstr(value=leader_name))
self._tournament_leader_score_type = (entry['scoreType'])
ba.textwidget(edit=tbtn['current_leader_score_text'],
text=leader_score)
ba.buttonwidget(edit=tbtn['more_scores_button'],
label=ba.Lstr(resource=self._r + '.seeMoreText'))
out_of_time_text: str | ba.Lstr = (
'-' if 'totalTime' not in entry else ba.Lstr(
resource=self._r + '.ofTotalTimeText',
subs=[('${TOTAL}',
ba.timestring(entry['totalTime'],
centi=False,
suppress_format_warning=True))]))
ba.textwidget(edit=tbtn['time_remaining_out_of_text'],
text=out_of_time_text)
tbtn['time_remaining'] = entry['timeRemaining']
tbtn['has_time_remaining'] = entry is not None
tbtn['tournament_id'] = entry['tournamentID']
tbtn['required_league'] = (None if 'requiredLeague' not in entry
else entry['requiredLeague'])
game = ba.app.accounts_v1.tournament_info[
tbtn['tournament_id']]['game']
if game is None:
ba.textwidget(edit=tbtn['button_text'], text='-')
ba.imagewidget(edit=tbtn['image'],
texture=ba.gettexture('black'),
opacity=0.2)
else:
campaignname, levelname = game.split(':')
campaign = getcampaign(campaignname)
max_players = ba.app.accounts_v1.tournament_info[
tbtn['tournament_id']]['maxPlayers']
txt = ba.Lstr(
value='${A} ${B}',
subs=[('${A}', campaign.getlevel(levelname).displayname),
('${B}',
ba.Lstr(resource='playerCountAbbreviatedText',
subs=[('${COUNT}', str(max_players))]))])
ba.textwidget(edit=tbtn['button_text'], text=txt)
ba.imagewidget(
edit=tbtn['image'],
texture=campaign.getlevel(levelname).get_preview_texture(),
opacity=1.0 if enabled else 0.5)
fee = entry['fee']
if fee is None:
fee_var = None
elif fee == 4:
fee_var = 'price.tournament_entry_4'
elif fee == 3:
fee_var = 'price.tournament_entry_3'
elif fee == 2:
fee_var = 'price.tournament_entry_2'
elif fee == 1:
fee_var = 'price.tournament_entry_1'
else:
if fee != 0:
print('Unknown fee value:', fee)
fee_var = 'price.tournament_entry_0'
tbtn['allow_ads'] = allow_ads = entry['allowAds']
final_fee: int | None = (None if fee_var is None else
_ba.get_v1_account_misc_read_val(
fee_var, '?'))
final_fee_str: str | ba.Lstr
if fee_var is None:
final_fee_str = ''
else:
if final_fee == 0:
final_fee_str = ba.Lstr(
resource='getTicketsWindow.freeText')
else:
final_fee_str = (
ba.charstr(ba.SpecialChar.TICKET_BACKING) +
str(final_fee))
ad_tries_remaining = ba.app.accounts_v1.tournament_info[
tbtn['tournament_id']]['adTriesRemaining']
free_tries_remaining = ba.app.accounts_v1.tournament_info[
tbtn['tournament_id']]['freeTriesRemaining']
# Now, if this fee allows ads and we support video ads, show
# the 'or ad' version.
if allow_ads and _ba.has_video_ads():
ads_enabled = _ba.have_incentivized_ad()
ba.imagewidget(edit=tbtn['entry_fee_ad_image'],
opacity=1.0 if ads_enabled else 0.25)
or_text = ba.Lstr(resource='orText',
subs=[('${A}', ''),
('${B}', '')]).evaluate().strip()
ba.textwidget(edit=tbtn['entry_fee_text_or'], text=or_text)
ba.textwidget(
edit=tbtn['entry_fee_text_top'],
position=(tbtn['button_x'] + 360,
tbtn['button_y'] + tbtn['button_scale_y'] - 60),
scale=1.3,
text=final_fee_str)
# Possibly show number of ad-plays remaining.
ba.textwidget(
edit=tbtn['entry_fee_text_remaining'],
position=(tbtn['button_x'] + 360,
tbtn['button_y'] + tbtn['button_scale_y'] - 146),
text='' if ad_tries_remaining in [None, 0] else
('' + str(ad_tries_remaining)),
color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2))
else:
ba.imagewidget(edit=tbtn['entry_fee_ad_image'], opacity=0.0)
ba.textwidget(edit=tbtn['entry_fee_text_or'], text='')
ba.textwidget(
edit=tbtn['entry_fee_text_top'],
position=(tbtn['button_x'] + 360,
tbtn['button_y'] + tbtn['button_scale_y'] - 80),
scale=1.3,
text=final_fee_str)
# Possibly show number of free-plays remaining.
ba.textwidget(
edit=tbtn['entry_fee_text_remaining'],
position=(tbtn['button_x'] + 360,
tbtn['button_y'] + tbtn['button_scale_y'] - 100),
text=('' if (free_tries_remaining in [None, 0]
or final_fee != 0) else
('' + str(free_tries_remaining))),
color=(0.6, 0.6, 0.6, 1))
tbtn.update_for_data(data[i])
def _on_tournament_query_response(self,
data: dict[str, Any] | None) -> None:
@ -715,10 +504,13 @@ class CoopBrowserWindow(ba.Window):
items = [
campaignname + ':Onslaught Training',
campaignname + ':Rookie Onslaught',
campaignname + ':Rookie Football', campaignname + ':Pro Onslaught',
campaignname + ':Pro Football', campaignname + ':Pro Runaround',
campaignname + ':Uber Onslaught', campaignname + ':Uber Football',
campaignname + ':Uber Runaround'
campaignname + ':Rookie Football',
campaignname + ':Pro Onslaught',
campaignname + ':Pro Football',
campaignname + ':Pro Runaround',
campaignname + ':Uber Onslaught',
campaignname + ':Uber Football',
campaignname + ':Uber Runaround',
]
items += [campaignname + ':The Last Stand']
if self._selected_campaign_level is None:
@ -772,6 +564,7 @@ class CoopBrowserWindow(ba.Window):
# pylint: disable=too-many-locals
# pylint: disable=cyclic-import
from bastd.ui.coop.gamebutton import GameButton
from bastd.ui.coop.tournamentbutton import TournamentButton
# (Re)create the sub-container if need be.
if self._subcontainer is not None:
@ -839,7 +632,7 @@ class CoopBrowserWindow(ba.Window):
# Tournaments
self._tournament_buttons: list[dict[str, Any]] = []
self._tournament_buttons: list[TournamentButton] = []
v -= 53
# FIXME shouldn't use hard-coded strings here.
@ -919,10 +712,15 @@ class CoopBrowserWindow(ba.Window):
v2 = -2
is_last_sel = True
self._tournament_buttons.append(
self._tournament_button(sc2, h, v2, is_last_sel))
TournamentButton(sc2,
h,
v2,
is_last_sel,
on_pressed=ba.WeakCall(
self.run_tournament)))
v -= 200
# Custom Games.
# Custom Games. (called 'Practice' in UI these days).
v -= 50
ba.textwidget(parent=w_parent,
position=(h_base + 27, v + 30 + 198),
@ -949,14 +747,13 @@ class CoopBrowserWindow(ba.Window):
if _ba.get_v1_account_misc_read_val(
'easter', False) or _ba.get_purchased('games.easter_egg_hunt'):
items = [
'Challenges:Easter Egg Hunt', 'Challenges:Pro Easter Egg Hunt'
'Challenges:Easter Egg Hunt',
'Challenges:Pro Easter Egg Hunt',
] + items
# add all custom user levels here..
# items += [
# 'User:' + l.getname()
# for l in getcampaign('User').getlevels()
# ]
# If we've defined custom games, put them at the beginning.
if ba.app.custom_coop_practice_games:
items = ba.app.custom_coop_practice_games + items
self._custom_h_scroll = custom_h_scroll = h_scroll = ba.hscrollwidget(
parent=w_parent,
@ -995,19 +792,19 @@ class CoopBrowserWindow(ba.Window):
for i, tbutton in enumerate(self._tournament_buttons):
ba.widget(
edit=tbutton['button'],
edit=tbutton.button,
up_widget=self._tournament_info_button
if i == 0 else self._tournament_buttons[i - 1]['button'],
down_widget=self._tournament_buttons[(i + 1)]['button']
if i == 0 else self._tournament_buttons[i - 1].button,
down_widget=self._tournament_buttons[(i + 1)].button
if i + 1 < len(self._tournament_buttons) else custom_h_scroll)
ba.widget(
edit=tbutton['more_scores_button'],
edit=tbutton.more_scores_button,
down_widget=self._tournament_buttons[(
i + 1)]['current_leader_name_text']
i + 1)].current_leader_name_text
if i + 1 < len(self._tournament_buttons) else custom_h_scroll)
ba.widget(edit=tbutton['current_leader_name_text'],
ba.widget(edit=tbutton.current_leader_name_text,
up_widget=self._tournament_info_button if i == 0 else
self._tournament_buttons[i - 1]['more_scores_button'])
self._tournament_buttons[i - 1].more_scores_button)
for btn in self._custom_buttons:
try:
@ -1037,314 +834,6 @@ class CoopBrowserWindow(ba.Window):
def _enable_selectable_callback(self) -> None:
self._do_selection_callbacks = True
def _tournament_button(self, parent: ba.Widget, x: float, y: float,
select: bool) -> dict[str, Any]:
sclx = 300
scly = 195.0
data: dict[str, Any] = {
'tournament_id': None,
'time_remaining': 0,
'has_time_remaining': False,
'leader': None
}
data['button'] = btn = ba.buttonwidget(
parent=parent,
position=(x + 23, y + 4),
size=(sclx, scly),
label='',
button_type='square',
autoselect=True,
on_activate_call=lambda: self.run(None, tournament_button=data))
ba.widget(edit=btn,
show_buffer_bottom=50,
show_buffer_top=50,
show_buffer_left=400,
show_buffer_right=200)
if select:
ba.containerwidget(edit=parent,
selected_child=btn,
visible_child=btn)
image_width = sclx * 0.85 * 0.75
data['image'] = ba.imagewidget(
parent=parent,
draw_controller=btn,
position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 150),
size=(image_width, image_width * 0.5),
model_transparent=self.lsbt,
model_opaque=self.lsbo,
texture=ba.gettexture('black'),
opacity=0.2,
mask_texture=ba.gettexture('mapPreviewMask'))
data['lock_image'] = ba.imagewidget(
parent=parent,
draw_controller=btn,
position=(x + 21 + sclx * 0.5 - image_width * 0.25,
y + scly - 150),
size=(image_width * 0.5, image_width * 0.5),
texture=ba.gettexture('lock'),
opacity=0.0)
data['button_text'] = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 20 + sclx * 0.5,
y + scly - 35),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=sclx * 0.76,
scale=0.85,
color=(0.8, 1.0, 0.8, 1.0))
header_color = (0.43, 0.4, 0.5, 1)
value_color = (0.6, 0.6, 0.6, 1)
x_offs = 0
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.entryFeeText'),
v_align='center',
maxwidth=100,
scale=0.9,
color=header_color,
flatness=1.0)
data['entry_fee_text_top'] = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360,
y + scly - 60),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=60,
scale=1.3,
color=value_color,
flatness=1.0)
data['entry_fee_text_or'] = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360,
y + scly - 90),
size=(0, 0),
h_align='center',
text='',
v_align='center',
maxwidth=60,
scale=0.5,
color=value_color,
flatness=1.0)
data['entry_fee_text_remaining'] = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360, y +
scly - 90),
size=(0, 0),
h_align='center',
text='',
v_align='center',
maxwidth=60,
scale=0.5,
color=value_color,
flatness=1.0)
data['entry_fee_ad_image'] = ba.imagewidget(
parent=parent,
size=(40, 40),
draw_controller=btn,
position=(x + 360 - 20, y + scly - 140),
opacity=0.0,
texture=ba.gettexture('tv'))
x_offs += 50
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 447 + x_offs, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.prizesText'),
v_align='center',
maxwidth=130,
scale=0.9,
color=header_color,
flatness=1.0)
data['button_x'] = x
data['button_y'] = y
data['button_scale_y'] = scly
xo2 = 0
prize_value_scale = 1.5
data['prize_range_1_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 355 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='right',
v_align='center',
maxwidth=50,
text='-',
scale=0.8,
color=header_color,
flatness=1.0)
data['prize_value_1_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 380 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='left',
text='-',
v_align='center',
maxwidth=100,
scale=prize_value_scale,
color=value_color,
flatness=1.0)
data['prize_range_2_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 355 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='right',
v_align='center',
maxwidth=50,
scale=0.8,
color=header_color,
flatness=1.0)
data['prize_value_2_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 380 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='left',
text='',
v_align='center',
maxwidth=100,
scale=prize_value_scale,
color=value_color,
flatness=1.0)
data['prize_range_3_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 355 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='right',
v_align='center',
maxwidth=50,
scale=0.8,
color=header_color,
flatness=1.0)
data['prize_value_3_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 380 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='left',
text='',
v_align='center',
maxwidth=100,
scale=prize_value_scale,
color=value_color,
flatness=1.0)
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 620 + x_offs, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.currentBestText'),
v_align='center',
maxwidth=180,
scale=0.9,
color=header_color,
flatness=1.0)
data['current_leader_name_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 620 + x_offs - (170 / 1.4) * 0.5,
y + scly - 60 - 40 * 0.5),
selectable=True,
click_activate=True,
autoselect=True,
on_activate_call=lambda: self._show_leader(tournament_button=data),
size=(170 / 1.4, 40),
h_align='center',
text='-',
v_align='center',
maxwidth=170,
scale=1.4,
color=value_color,
flatness=1.0)
data['current_leader_score_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 620 + x_offs, y + scly - 113 + 10),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=170,
scale=1.8,
color=value_color,
flatness=1.0)
data['more_scores_button'] = ba.buttonwidget(
parent=parent,
position=(x + 620 + x_offs - 60, y + scly - 50 - 125),
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
label='-',
size=(120, 40),
autoselect=True,
up_widget=data['current_leader_name_text'],
text_scale=0.6,
on_activate_call=lambda: self._show_scores(tournament_button=data))
ba.widget(edit=data['current_leader_name_text'],
down_widget=data['more_scores_button'])
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 820 + x_offs, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.timeRemainingText'),
v_align='center',
maxwidth=180,
scale=0.9,
color=header_color,
flatness=1.0)
data['time_remaining_value_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 820 + x_offs, y + scly - 68),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=180,
scale=2.0,
color=value_color,
flatness=1.0)
data['time_remaining_out_of_text'] = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 820 + x_offs, y + scly - 110),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=120,
scale=0.72,
color=(0.4, 0.4, 0.5),
flatness=1.0)
return data
def _switch_to_league_rankings(self) -> None:
# pylint: disable=cyclic-import
from bastd.ui.account import show_sign_in_prompt
@ -1378,100 +867,20 @@ class CoopBrowserWindow(ba.Window):
show_tab=show_tab,
back_location='CoopBrowserWindow').get_root_widget())
def _show_leader(self, tournament_button: dict[str, Any]) -> None:
# pylint: disable=cyclic-import
from bastd.ui.account.viewer import AccountViewerWindow
tournament_id = tournament_button['tournament_id']
# FIXME: This assumes a single player entry in leader; should expand
# this to work with multiple.
if tournament_id is None or tournament_button['leader'] is None or len(
tournament_button['leader'][2]) != 1:
ba.playsound(ba.getsound('error'))
return
ba.playsound(ba.getsound('swish'))
AccountViewerWindow(
account_id=tournament_button['leader'][2][0].get('a', None),
profile_id=tournament_button['leader'][2][0].get('p', None),
position=tournament_button['current_leader_name_text'].
get_screen_space_center())
def _show_scores(self, tournament_button: dict[str, Any]) -> None:
# pylint: disable=cyclic-import
from bastd.ui.tournamentscores import TournamentScoresWindow
tournament_id = tournament_button['tournament_id']
if tournament_id is None:
ba.playsound(ba.getsound('error'))
return
TournamentScoresWindow(
tournament_id=tournament_id,
position=tournament_button['more_scores_button'].
get_screen_space_center())
def is_tourney_data_up_to_date(self) -> bool:
"""Return whether our tourney data is up to date."""
return self._tourney_data_up_to_date
def run(self,
game: str | None,
tournament_button: dict[str, Any] = None) -> None:
def run_game(self, game: str) -> None:
"""Run the provided game."""
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# pylint: disable=too-many-return-statements
# pylint: disable=cyclic-import
from bastd.ui.confirm import ConfirmWindow
from bastd.ui.tournamententry import TournamentEntryWindow
from bastd.ui.purchase import PurchaseWindow
from bastd.ui.account import show_sign_in_prompt
args: dict[str, Any] = {}
# Do a bit of pre-flight for tournament options.
if tournament_button is not None:
if _ba.get_v1_account_state() != 'signed_in':
show_sign_in_prompt()
return
if not self._tourney_data_up_to_date:
ba.screenmessage(
ba.Lstr(resource='tournamentCheckingStateText'),
color=(1, 1, 0))
ba.playsound(ba.getsound('error'))
return
if tournament_button['tournament_id'] is None:
ba.screenmessage(
ba.Lstr(resource='internal.unavailableNoConnectionText'),
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
if tournament_button['required_league'] is not None:
ba.screenmessage(ba.Lstr(
resource='league.tournamentLeagueText',
subs=[
('${NAME}',
ba.Lstr(
translate=('leagueNames',
tournament_button['required_league'])))
]),
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
if tournament_button['time_remaining'] <= 0:
ba.screenmessage(ba.Lstr(resource='tournamentEndedText'),
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
# Game is whatever the tournament tells us it is.
game = ba.app.accounts_v1.tournament_info[
tournament_button['tournament_id']]['game']
if tournament_button is None and game == 'Easy:The Last Stand':
if game == 'Easy:The Last Stand':
ConfirmWindow(ba.Lstr(resource='difficultyHardUnlockOnlyText',
fallback_resource='difficultyHardOnlyText'),
cancel_button=False,
@ -1479,12 +888,11 @@ class CoopBrowserWindow(ba.Window):
height=130)
return
# Infinite onslaught/runaround require pro; bring up a store link if
# need be.
if tournament_button is None and game in (
'Challenges:Infinite Runaround',
'Challenges:Infinite Onslaught'
) and not ba.app.accounts_v1.have_pro():
# Infinite onslaught/runaround require pro; bring up a store link
# if need be.
if game in ('Challenges:Infinite Runaround',
'Challenges:Infinite Onslaught'
) and not ba.app.accounts_v1.have_pro():
if _ba.get_v1_account_state() != 'signed_in':
show_sign_in_prompt()
else:
@ -1495,7 +903,8 @@ class CoopBrowserWindow(ba.Window):
if game in ['Challenges:Meteor Shower']:
required_purchase = 'games.meteor_shower'
elif game in [
'Challenges:Target Practice', 'Challenges:Target Practice B'
'Challenges:Target Practice',
'Challenges:Target Practice B',
]:
required_purchase = 'games.target_practice'
elif game in ['Challenges:Ninja Fight']:
@ -1503,13 +912,14 @@ class CoopBrowserWindow(ba.Window):
elif game in ['Challenges:Pro Ninja Fight']:
required_purchase = 'games.ninja_fight'
elif game in [
'Challenges:Easter Egg Hunt', 'Challenges:Pro Easter Egg Hunt'
'Challenges:Easter Egg Hunt',
'Challenges:Pro Easter Egg Hunt',
]:
required_purchase = 'games.easter_egg_hunt'
else:
required_purchase = None
if (tournament_button is None and required_purchase is not None
if (required_purchase is not None
and not _ba.get_purchased(required_purchase)):
if _ba.get_v1_account_state() != 'signed_in':
show_sign_in_prompt()
@ -1519,17 +929,57 @@ class CoopBrowserWindow(ba.Window):
self._save_state()
# For tournaments, we pop up the entry window.
if tournament_button is not None:
TournamentEntryWindow(
tournament_id=tournament_button['tournament_id'],
position=tournament_button['button'].get_screen_space_center())
else:
# Otherwise just dive right in.
assert game is not None
if ba.app.launch_coop_game(game, args=args):
ba.containerwidget(edit=self._root_widget,
transition='out_left')
if ba.app.launch_coop_game(game, args=args):
ba.containerwidget(edit=self._root_widget, transition='out_left')
def run_tournament(self, tournament_button: TournamentButton) -> None:
"""Run the provided tournament game."""
from bastd.ui.account import show_sign_in_prompt
from bastd.ui.tournamententry import TournamentEntryWindow
if _ba.get_v1_account_state() != 'signed_in':
show_sign_in_prompt()
return
if not self._tourney_data_up_to_date:
ba.screenmessage(ba.Lstr(resource='tournamentCheckingStateText'),
color=(1, 1, 0))
ba.playsound(ba.getsound('error'))
return
if tournament_button.tournament_id is None:
ba.screenmessage(
ba.Lstr(resource='internal.unavailableNoConnectionText'),
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
if tournament_button.required_league is not None:
ba.screenmessage(
ba.Lstr(
resource='league.tournamentLeagueText',
subs=[('${NAME}',
ba.Lstr(
translate=('leagueNames',
tournament_button.required_league)))
]),
color=(1, 0, 0),
)
ba.playsound(ba.getsound('error'))
return
if tournament_button.time_remaining <= 0:
ba.screenmessage(ba.Lstr(resource='tournamentEndedText'),
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
self._save_state()
assert tournament_button.tournament_id is not None
TournamentEntryWindow(
tournament_id=tournament_button.tournament_id,
position=tournament_button.button.get_screen_space_center())
def _back(self) -> None:
# pylint: disable=cyclic-import
@ -1542,24 +992,6 @@ class CoopBrowserWindow(ba.Window):
ba.app.ui.set_main_menu_window(
PlayWindow(transition='in_left').get_root_widget())
def _restore_state(self) -> None:
try:
sel_name = ba.app.ui.window_states.get(type(self),
{}).get('sel_name')
if sel_name == 'Back':
sel = self._back_button
elif sel_name == 'Scroll':
sel = self._scrollwidget
elif sel_name == 'PowerRanking':
sel = self._league_rank_button_widget
elif sel_name == 'Store':
sel = self._store_button_widget
else:
sel = self._scrollwidget
ba.containerwidget(edit=self._root_widget, selected_child=sel)
except Exception:
ba.print_exception(f'Error restoring state for {self}.')
def _save_state(self) -> None:
cfg = ba.app.config
try:
@ -1580,16 +1012,31 @@ class CoopBrowserWindow(ba.Window):
cfg['Selected Coop Row'] = self._selected_row
cfg['Selected Coop Custom Level'] = self._selected_custom_level
cfg['Selected Coop Challenge Level'] = self._selected_challenge_level
cfg['Selected Coop Campaign Level'] = self._selected_campaign_level
cfg.commit()
def _restore_state(self) -> None:
try:
sel_name = ba.app.ui.window_states.get(type(self),
{}).get('sel_name')
if sel_name == 'Back':
sel = self._back_button
elif sel_name == 'Scroll':
sel = self._scrollwidget
elif sel_name == 'PowerRanking':
sel = self._league_rank_button_widget
elif sel_name == 'Store':
sel = self._store_button_widget
else:
sel = self._scrollwidget
ba.containerwidget(edit=self._root_widget, selected_child=sel)
except Exception:
ba.print_exception(f'Error restoring state for {self}.')
def sel_change(self, row: str, game: str) -> None:
"""(internal)"""
if self._do_selection_callbacks:
if row == 'custom':
self._selected_custom_level = game
if row == 'challenges':
self._selected_challenge_level = game
elif row == 'campaign':
self._selected_campaign_level = game

View file

@ -55,7 +55,7 @@ class GameButton:
position=(x + 23, y + 4),
size=(sclx, scly),
label='',
on_activate_call=ba.Call(window.run, game),
on_activate_call=ba.Call(window.run_game, game),
button_type='square',
autoselect=True,
on_select_call=ba.Call(window.sel_change, row, game))

View file

@ -0,0 +1,561 @@
# Released under the MIT License. See LICENSE for details.
#
"""Defines button for co-op games."""
from __future__ import annotations
from typing import TYPE_CHECKING
import copy
import ba
import _ba
if TYPE_CHECKING:
from typing import Any, Callable
class TournamentButton:
"""Button showing a tournament in coop window."""
def __init__(self, parent: ba.Widget, x: float, y: float, select: bool,
on_pressed: Callable[[TournamentButton], None]) -> None:
self._r = 'coopSelectWindow'
sclx = 300
scly = 195.0
self.on_pressed = on_pressed
self.lsbt = ba.getmodel('level_select_button_transparent')
self.lsbo = ba.getmodel('level_select_button_opaque')
self.allow_ads = False
self.tournament_id: str | None = None
self.time_remaining: int = 0
self.has_time_remaining: bool = False
self.leader: Any = None
self.required_league: str | None = None
self.button = btn = ba.buttonwidget(
parent=parent,
position=(x + 23, y + 4),
size=(sclx, scly),
label='',
button_type='square',
autoselect=True,
# on_activate_call=lambda: self.run(None, tournament_button=data)
on_activate_call=ba.WeakCall(self._pressed))
ba.widget(edit=btn,
show_buffer_bottom=50,
show_buffer_top=50,
show_buffer_left=400,
show_buffer_right=200)
if select:
ba.containerwidget(edit=parent,
selected_child=btn,
visible_child=btn)
image_width = sclx * 0.85 * 0.75
self.image = ba.imagewidget(
parent=parent,
draw_controller=btn,
position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 150),
size=(image_width, image_width * 0.5),
model_transparent=self.lsbt,
model_opaque=self.lsbo,
texture=ba.gettexture('black'),
opacity=0.2,
mask_texture=ba.gettexture('mapPreviewMask'))
self.lock_image = ba.imagewidget(
parent=parent,
draw_controller=btn,
position=(x + 21 + sclx * 0.5 - image_width * 0.25,
y + scly - 150),
size=(image_width * 0.5, image_width * 0.5),
texture=ba.gettexture('lock'),
opacity=0.0)
self.button_text = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 20 + sclx * 0.5,
y + scly - 35),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=sclx * 0.76,
scale=0.85,
color=(0.8, 1.0, 0.8, 1.0))
header_color = (0.43, 0.4, 0.5, 1)
value_color = (0.6, 0.6, 0.6, 1)
x_offs = 0
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.entryFeeText'),
v_align='center',
maxwidth=100,
scale=0.9,
color=header_color,
flatness=1.0)
self.entry_fee_text_top = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360,
y + scly - 60),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=60,
scale=1.3,
color=value_color,
flatness=1.0)
self.entry_fee_text_or = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360,
y + scly - 90),
size=(0, 0),
h_align='center',
text='',
v_align='center',
maxwidth=60,
scale=0.5,
color=value_color,
flatness=1.0)
self.entry_fee_text_remaining = ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 360,
y + scly - 90),
size=(0, 0),
h_align='center',
text='',
v_align='center',
maxwidth=60,
scale=0.5,
color=value_color,
flatness=1.0)
self.entry_fee_ad_image = ba.imagewidget(parent=parent,
size=(40, 40),
draw_controller=btn,
position=(x + 360 - 20,
y + scly - 140),
opacity=0.0,
texture=ba.gettexture('tv'))
x_offs += 50
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 447 + x_offs, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.prizesText'),
v_align='center',
maxwidth=130,
scale=0.9,
color=header_color,
flatness=1.0)
self.button_x = x
self.button_y = y
self.button_scale_y = scly
xo2 = 0
prize_value_scale = 1.5
self.prize_range_1_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 355 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='right',
v_align='center',
maxwidth=50,
text='-',
scale=0.8,
color=header_color,
flatness=1.0)
self.prize_value_1_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 380 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='left',
text='-',
v_align='center',
maxwidth=100,
scale=prize_value_scale,
color=value_color,
flatness=1.0)
self.prize_range_2_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 355 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='right',
v_align='center',
maxwidth=50,
scale=0.8,
color=header_color,
flatness=1.0)
self.prize_value_2_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 380 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='left',
text='',
v_align='center',
maxwidth=100,
scale=prize_value_scale,
color=value_color,
flatness=1.0)
self.prize_range_3_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 355 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='right',
v_align='center',
maxwidth=50,
scale=0.8,
color=header_color,
flatness=1.0)
self.prize_value_3_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 380 + xo2 + x_offs, y + scly - 93),
size=(0, 0),
h_align='left',
text='',
v_align='center',
maxwidth=100,
scale=prize_value_scale,
color=value_color,
flatness=1.0)
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 620 + x_offs, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.currentBestText'),
v_align='center',
maxwidth=180,
scale=0.9,
color=header_color,
flatness=1.0)
self.current_leader_name_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 620 + x_offs - (170 / 1.4) * 0.5,
y + scly - 60 - 40 * 0.5),
selectable=True,
click_activate=True,
autoselect=True,
on_activate_call=ba.WeakCall(self._show_leader),
size=(170 / 1.4, 40),
h_align='center',
text='-',
v_align='center',
maxwidth=170,
scale=1.4,
color=value_color,
flatness=1.0)
self.current_leader_score_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 620 + x_offs, y + scly - 113 + 10),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=170,
scale=1.8,
color=value_color,
flatness=1.0)
self.more_scores_button = ba.buttonwidget(
parent=parent,
position=(x + 620 + x_offs - 60, y + scly - 50 - 125),
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
label='-',
size=(120, 40),
autoselect=True,
up_widget=self.current_leader_name_text,
text_scale=0.6,
on_activate_call=ba.WeakCall(self._show_scores))
ba.widget(edit=self.current_leader_name_text,
down_widget=self.more_scores_button)
ba.textwidget(parent=parent,
draw_controller=btn,
position=(x + 820 + x_offs, y + scly - 20),
size=(0, 0),
h_align='center',
text=ba.Lstr(resource=self._r + '.timeRemainingText'),
v_align='center',
maxwidth=180,
scale=0.9,
color=header_color,
flatness=1.0)
self.time_remaining_value_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 820 + x_offs, y + scly - 68),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=180,
scale=2.0,
color=value_color,
flatness=1.0)
self.time_remaining_out_of_text = ba.textwidget(
parent=parent,
draw_controller=btn,
position=(x + 820 + x_offs, y + scly - 110),
size=(0, 0),
h_align='center',
text='-',
v_align='center',
maxwidth=120,
scale=0.72,
color=(0.4, 0.4, 0.5),
flatness=1.0)
def _pressed(self) -> None:
self.on_pressed(self)
def _show_leader(self) -> None:
# pylint: disable=cyclic-import
from bastd.ui.account.viewer import AccountViewerWindow
tournament_id = self.tournament_id
# FIXME: This assumes a single player entry in leader; should expand
# this to work with multiple.
if tournament_id is None or self.leader is None or len(
self.leader[2]) != 1:
ba.playsound(ba.getsound('error'))
return
ba.playsound(ba.getsound('swish'))
AccountViewerWindow(
account_id=self.leader[2][0].get('a', None),
profile_id=self.leader[2][0].get('p', None),
position=self.current_leader_name_text.get_screen_space_center())
def _show_scores(self) -> None:
# pylint: disable=cyclic-import
from bastd.ui.tournamentscores import TournamentScoresWindow
tournament_id = self.tournament_id
if tournament_id is None:
ba.playsound(ba.getsound('error'))
return
TournamentScoresWindow(
tournament_id=tournament_id,
position=self.more_scores_button.get_screen_space_center())
def update_for_data(self, entry: dict[str, Any]) -> None:
"""Update for new incoming data."""
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
from ba.internal import getcampaign, get_tournament_prize_strings
prize_y_offs = (34 if 'prizeRange3' in entry else
20 if 'prizeRange2' in entry else 12)
x_offs = 90
# This seems to be a false alarm.
# pylint: disable=unbalanced-tuple-unpacking
pr1, pv1, pr2, pv2, pr3, pv3 = (get_tournament_prize_strings(entry))
# pylint: enable=unbalanced-tuple-unpacking
enabled = 'requiredLeague' not in entry
ba.buttonwidget(edit=self.button,
color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5))
ba.imagewidget(edit=self.lock_image, opacity=0.0 if enabled else 1.0)
ba.textwidget(edit=self.prize_range_1_text,
text='-' if pr1 == '' else pr1,
position=(self.button_x + 365 + x_offs, self.button_y +
self.button_scale_y - 93 + prize_y_offs))
# We want to draw values containing tickets a bit smaller
# (scratch that; we now draw medals a bit bigger).
ticket_char = ba.charstr(ba.SpecialChar.TICKET_BACKING)
prize_value_scale_large = 1.0
prize_value_scale_small = 1.0
ba.textwidget(edit=self.prize_value_1_text,
text='-' if pv1 == '' else pv1,
scale=prize_value_scale_large
if ticket_char not in pv1 else prize_value_scale_small,
position=(self.button_x + 380 + x_offs, self.button_y +
self.button_scale_y - 93 + prize_y_offs))
ba.textwidget(edit=self.prize_range_2_text,
text=pr2,
position=(self.button_x + 365 + x_offs, self.button_y +
self.button_scale_y - 93 - 45 + prize_y_offs))
ba.textwidget(edit=self.prize_value_2_text,
text=pv2,
scale=prize_value_scale_large
if ticket_char not in pv2 else prize_value_scale_small,
position=(self.button_x + 380 + x_offs, self.button_y +
self.button_scale_y - 93 - 45 + prize_y_offs))
ba.textwidget(edit=self.prize_range_3_text,
text=pr3,
position=(self.button_x + 365 + x_offs, self.button_y +
self.button_scale_y - 93 - 90 + prize_y_offs))
ba.textwidget(edit=self.prize_value_3_text,
text=pv3,
scale=prize_value_scale_large
if ticket_char not in pv3 else prize_value_scale_small,
position=(self.button_x + 380 + x_offs, self.button_y +
self.button_scale_y - 93 - 90 + prize_y_offs))
leader_name = '-'
leader_score: str | ba.Lstr = '-'
if entry['scores']:
score = self.leader = copy.deepcopy(entry['scores'][0])
leader_name = score[1]
leader_score = (ba.timestring(
score[0] * 10,
centi=True,
timeformat=ba.TimeFormat.MILLISECONDS,
suppress_format_warning=True)
if entry['scoreType'] == 'time' else str(score[0]))
else:
self.leader = None
ba.textwidget(edit=self.current_leader_name_text,
text=ba.Lstr(value=leader_name))
ba.textwidget(edit=self.current_leader_score_text, text=leader_score)
ba.buttonwidget(edit=self.more_scores_button,
label=ba.Lstr(resource=self._r + '.seeMoreText'))
out_of_time_text: str | ba.Lstr = (
'-' if 'totalTime' not in entry else ba.Lstr(
resource=self._r + '.ofTotalTimeText',
subs=[('${TOTAL}',
ba.timestring(entry['totalTime'],
centi=False,
suppress_format_warning=True))]))
ba.textwidget(edit=self.time_remaining_out_of_text,
text=out_of_time_text)
self.time_remaining = entry['timeRemaining']
self.has_time_remaining = entry is not None
self.tournament_id = entry['tournamentID']
self.required_league = (None if 'requiredLeague' not in entry else
entry['requiredLeague'])
game = ba.app.accounts_v1.tournament_info[self.tournament_id]['game']
if game is None:
ba.textwidget(edit=self.button_text, text='-')
ba.imagewidget(edit=self.image,
texture=ba.gettexture('black'),
opacity=0.2)
else:
campaignname, levelname = game.split(':')
campaign = getcampaign(campaignname)
max_players = ba.app.accounts_v1.tournament_info[
self.tournament_id]['maxPlayers']
txt = ba.Lstr(value='${A} ${B}',
subs=[('${A}',
campaign.getlevel(levelname).displayname),
('${B}',
ba.Lstr(resource='playerCountAbbreviatedText',
subs=[('${COUNT}', str(max_players))
]))])
ba.textwidget(edit=self.button_text, text=txt)
ba.imagewidget(
edit=self.image,
texture=campaign.getlevel(levelname).get_preview_texture(),
opacity=1.0 if enabled else 0.5)
fee = entry['fee']
if fee is None:
fee_var = None
elif fee == 4:
fee_var = 'price.tournament_entry_4'
elif fee == 3:
fee_var = 'price.tournament_entry_3'
elif fee == 2:
fee_var = 'price.tournament_entry_2'
elif fee == 1:
fee_var = 'price.tournament_entry_1'
else:
if fee != 0:
print('Unknown fee value:', fee)
fee_var = 'price.tournament_entry_0'
self.allow_ads = allow_ads = entry['allowAds']
final_fee: int | None = (None if fee_var is None else
_ba.get_v1_account_misc_read_val(
fee_var, '?'))
final_fee_str: str | ba.Lstr
if fee_var is None:
final_fee_str = ''
else:
if final_fee == 0:
final_fee_str = ba.Lstr(resource='getTicketsWindow.freeText')
else:
final_fee_str = (ba.charstr(ba.SpecialChar.TICKET_BACKING) +
str(final_fee))
ad_tries_remaining = ba.app.accounts_v1.tournament_info[
self.tournament_id]['adTriesRemaining']
free_tries_remaining = ba.app.accounts_v1.tournament_info[
self.tournament_id]['freeTriesRemaining']
# Now, if this fee allows ads and we support video ads, show
# the 'or ad' version.
if allow_ads and _ba.has_video_ads():
ads_enabled = _ba.have_incentivized_ad()
ba.imagewidget(edit=self.entry_fee_ad_image,
opacity=1.0 if ads_enabled else 0.25)
or_text = ba.Lstr(resource='orText',
subs=[('${A}', ''),
('${B}', '')]).evaluate().strip()
ba.textwidget(edit=self.entry_fee_text_or, text=or_text)
ba.textwidget(edit=self.entry_fee_text_top,
position=(self.button_x + 360,
self.button_y + self.button_scale_y - 60),
scale=1.3,
text=final_fee_str)
# Possibly show number of ad-plays remaining.
ba.textwidget(edit=self.entry_fee_text_remaining,
position=(self.button_x + 360,
self.button_y + self.button_scale_y - 146),
text='' if ad_tries_remaining in [None, 0] else
('' + str(ad_tries_remaining)),
color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2))
else:
ba.imagewidget(edit=self.entry_fee_ad_image, opacity=0.0)
ba.textwidget(edit=self.entry_fee_text_or, text='')
ba.textwidget(edit=self.entry_fee_text_top,
position=(self.button_x + 360,
self.button_y + self.button_scale_y - 80),
scale=1.3,
text=final_fee_str)
# Possibly show number of free-plays remaining.
ba.textwidget(
edit=self.entry_fee_text_remaining,
position=(self.button_x + 360,
self.button_y + self.button_scale_y - 100),
text=('' if (free_tries_remaining in [None, 0]
or final_fee != 0) else
('' + str(free_tries_remaining))),
color=(0.6, 0.6, 0.6, 1),
)

View file

@ -16,7 +16,7 @@ if TYPE_CHECKING:
class CreditsListWindow(ba.Window):
"""Window for displaying game credits."""
def __init__(self, origin_widget: ba.Widget = None):
def __init__(self, origin_widget: ba.Widget | None = None):
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
import json
@ -207,10 +207,15 @@ class CreditsListWindow(ba.Window):
'\n' + '\n'.join(translation_names.splitlines()[:146]) +
'\n'.join(translation_names.splitlines()[146:]) + '\n'
'\n'
' Shout Out to Awesome Mods / Modders:\n\n'
' Shout Out to Awesome Mods / Modders / Contributors:\n\n'
' BombDash ModPack\n'
' TheMikirog & SoK - BombSquad Joyride Modpack\n'
' Mrmaxmeier - BombSquad-Community-Mod-Manager\n'
' Ritiek Malhotra \n'
' Dliwk\n'
' vishal332008\n'
' itsre3\n'
' Drooopyyy\n'
'\n'
' Holiday theme vector art designed by Freepik\n'
'\n'

View file

@ -21,9 +21,9 @@ class FileSelectorWindow(ba.Window):
def __init__(self,
path: str,
callback: Callable[[str | None], Any] = None,
callback: Callable[[str | None], Any] | None = None,
show_base_path: bool = True,
valid_file_extensions: Sequence[str] = None,
valid_file_extensions: Sequence[str] | None = None,
allow_folders: bool = False):
if valid_file_extensions is None:
valid_file_extensions = []

View file

@ -68,7 +68,7 @@ class GatherWindow(ba.Window):
def __init__(self,
transition: str | None = 'in_right',
origin_widget: ba.Widget = None):
origin_widget: ba.Widget | None = None):
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
# pylint: disable=cyclic-import

View file

@ -705,8 +705,8 @@ class ManualGatherTab(GatherTab):
from_other_thread=True,
)
except Exception as exc:
from efro.error import is_udp_network_error
if is_udp_network_error(exc):
from efro.error import is_udp_communication_error
if is_udp_communication_error(exc):
ba.pushcall(ba.Call(
_safe_set_text, self._checking_state_text,
ba.Lstr(resource='gatherWindow.'

View file

@ -219,9 +219,9 @@ class AddrFetchThread(threading.Thread):
sock.close()
ba.pushcall(ba.Call(self._call, val), from_other_thread=True)
except Exception as exc:
from efro.error import is_udp_network_error
from efro.error import is_udp_communication_error
# Ignore expected network errors; log others.
if is_udp_network_error(exc):
if is_udp_communication_error(exc):
pass
else:
ba.print_exception()
@ -271,8 +271,8 @@ class PingThread(threading.Thread):
ping if accessible else None),
from_other_thread=True)
except Exception as exc:
from efro.error import is_udp_network_error
if is_udp_network_error(exc):
from efro.error import is_udp_communication_error
if is_udp_communication_error(exc):
pass
else:
ba.print_exception('Error on gather ping', once=True)

View file

@ -20,8 +20,8 @@ class GetCurrencyWindow(ba.Window):
transition: str = 'in_right',
from_modal_store: bool = False,
modal: bool = False,
origin_widget: ba.Widget = None,
store_back_location: str = None):
origin_widget: ba.Widget | None = None,
store_back_location: str | None = None):
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
@ -105,8 +105,8 @@ class GetCurrencyWindow(ba.Window):
position: tuple[float, float],
size: tuple[float, float],
label: ba.Lstr,
price: str = None,
tex_name: str = None,
price: str | None = None,
tex_name: str | None = None,
tex_opacity: float = 1.0,
tex_scale: float = 1.0,
enabled: bool = True,

View file

@ -18,7 +18,7 @@ class HelpWindow(ba.Window):
def __init__(self,
main_menu: bool = False,
origin_widget: ba.Widget = None):
origin_widget: ba.Widget | None = None):
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
from ba.internal import get_remote_app_name

View file

@ -22,11 +22,11 @@ class IconPicker(popup.PopupWindow):
parent: ba.Widget,
position: tuple[float, float] = (0.0, 0.0),
delegate: Any = None,
scale: float = None,
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0),
tint_color: Sequence[float] = (1.0, 1.0, 1.0),
tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
selected_icon: str = None):
selected_icon: str | None = None):
# pylint: disable=too-many-locals
del parent # unused here
del tint_color # unused_here

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