Compare commits
	
		
			118 Commits
		
	
	
		
			d140e726b4
			...
			master_eve
		
	
	| Author | SHA256 | Date | |
|---|---|---|---|
| 05e219ad18 | |||
| 0d36f1dd52 | |||
| 13bb11eb8c | |||
| af7f0019ec | |||
| 804a3961c9 | |||
|   | cdb69699ab | ||
|   | 6c3fe6326f | ||
|   | 2550f0b262 | ||
|   | 3cc6966d86 | ||
| 62c7c7f2c2 | |||
|   | aa245700c6 | ||
|   | 7f36158c40 | ||
|   | 171b9fe787 | ||
|   | eb3b7da07a | ||
|   | 8ed1bd3c4f | ||
|   | 0379a3f935 | ||
|   | 18bccfded8 | ||
|   | c6c53caeee | ||
|   | a5831085ba | ||
|   | 9ad177b459 | ||
|   | f2c8acb80a | ||
|   | 47729ceeb9 | ||
| c30c46e20f | |||
|   | 51e3367a1c | ||
|   | e0dc558c27 | ||
|   | 29157e22d4 | ||
|   | 53189e3fd8 | ||
|   | 87c0cefb56 | ||
|   | 52cc55e611 | ||
|   | 56da6031f2 | ||
|   | 5002890540 | ||
|   | 9653965d2b | ||
|   | 37578437f6 | ||
|   | 9b503872e8 | ||
|   | e604743796 | ||
|   | 4dcb87723d | ||
|   | 5c3e349cb6 | ||
|   | 5e6816269b | ||
|   | a7fd66e9e4 | ||
|   | 172b387dcc | ||
|   | 4639d85f00 | ||
|   | 24f0f3e3e1 | ||
|   | 0bfa74c0e2 | ||
|   | c82f31a101 | ||
|   | b9161a052f | ||
|   | bd0601e115 | ||
|   | bee2e94f82 | ||
|   | da0368d6c5 | ||
|   | f8a3e12909 | ||
| 111e5e04df | |||
| 1a0422414c | |||
| af70d30279 | |||
| 308107fdc7 | |||
| 7c6e7fb7b4 | |||
| 876c55522d | |||
| 76a14d69ce | |||
| aa46acc812 | |||
| c1c8301e11 | |||
|   | 269724345d | ||
|   | a9cb58100f | ||
|   | 8f3d6dfaff | ||
|   | 460a6ccdec | ||
|   | 2bf65583b7 | ||
|   | 5e530e1af1 | ||
|   | ffbaa1c3b5 | ||
|   | 72ec279036 | ||
|   | f55afe5959 | ||
|   | 669701dbb7 | ||
|   | 9b538e7d2f | ||
|   | 4d3dd5c781 | ||
|   | a948db5af3 | ||
|   | 11e4f9b2dd | ||
|   | 56e5e35b53 | ||
| f55a6a2cdb | |||
|   | 4729a45c30 | ||
| 5a6810e948 | |||
| 31637dc845 | |||
|   | 079dcd6639 | ||
|   | 65688b9100 | ||
|   | 308c9e3602 | ||
|   | 871b3d2ab1 | ||
|   | 5f0d6ad3f5 | ||
|   | 551c2ef8ef | ||
|   | 4ce19d1a50 | ||
|   | 67804a394d | ||
|   | 415cdfc123 | ||
| 9f745bd451 | |||
|   | fe356864c8 | ||
|   | c5a6b886d6 | ||
|   | a7c6dd25df | ||
|   | 888f22ac1b | ||
|   | 0a6deafd32 | ||
|   | 100bb4fe53 | ||
|   | f360fe4200 | ||
|   | 0df960ea22 | ||
|   | 634c651cb5 | ||
|   | 07dd5611e5 | ||
|   | 09c892a786 | ||
|   | 3d2a0bcc70 | ||
|   | c10872a47e | ||
|   | e8f2dbbc29 | ||
|   | 62ac0e83a5 | ||
|   | 82be14e603 | ||
|   | b12db42c0b | ||
| 5986e0f880 | |||
|   | 0bb2c840f9 | ||
| 526668ed5d | |||
|   | 1c284bad97 | ||
|   | 761cf794b4 | ||
|   | f193464006 | ||
|   | c5a6f4ea36 | ||
|   | 5a672b336f | ||
|   | 05d1718775 | ||
| e66a6fbd9c | |||
|   | e038f711d5 | ||
|   | 82141d89d6 | ||
| 0098861e54 | |||
| db86f3dd06 | 
							
								
								
									
										63
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,3 +1,62 @@ | |||||||
| # battle_royale_sim | # Battle Royale Simulator (Hunger Games) | ||||||
|  |  | ||||||
| Hunger Games Simulator | Hunger Games Simulator for telegram. | ||||||
|  |  | ||||||
|  | You can find the Official Bot here: | ||||||
|  | @Brsimgen_Bot - https://t.me/Brsimgen_Bot | ||||||
|  |  | ||||||
|  | Feel free to fork the project and make your own Bot | ||||||
|  |  | ||||||
|  | This game is inspired to this Hunger Games Simulator: https://brantsteele.net/hungergames/reaping.php | ||||||
|  |  | ||||||
|  | # Bot Instructions (Telegram) | ||||||
|  |  | ||||||
|  | 1. start a chat with the bot: https://t.me/Brsimgen_Bot | ||||||
|  | 2. open the bot keyboard | ||||||
|  | 3. Press button "Init/Restart" to initialize the world | ||||||
|  | 4. now is time to add players (max players is 70): | ||||||
|  |   - you can manually add new players with button "Add Player" then you'll need to insert the player name (or multiple names comma separated) | ||||||
|  |   - test  | ||||||
|  | 5. Now you can start with the game simulation | ||||||
|  |   - press "Simulate Day" to make time elapse till the end of the day, and watch what's happend during the day | ||||||
|  |   - press "Run Periodically" to make the bot do everything, you only need to insert the periodicity of the scheduler: for example 30 | ||||||
|  |     now every day will automatically end in 30 seconds | ||||||
|  | 6. you can always check the game status with the buttons: | ||||||
|  |   - Get Players: (see all the players) | ||||||
|  |   - Get Alive Players: (list of alive players) | ||||||
|  |   - Get Death Players: (list of death players) | ||||||
|  |   - Get Ranking Players: (this is the leaderboard, based on number of kills) | ||||||
|  |   - Show Map UTF8 or Show Map Image (show Players/Items position on the map) | ||||||
|  |  | ||||||
|  | # Bot Instructions (CLI) | ||||||
|  |  | ||||||
|  | 1. open a python shell | ||||||
|  | 2. `import debug` | ||||||
|  | 3. `debug.init_debug_simulation()` | ||||||
|  |  | ||||||
|  | # Fork Instructions | ||||||
|  |  | ||||||
|  | if you want to fork this project | ||||||
|  | remember to create a file | ||||||
|  |  | ||||||
|  | `local_settings.py` | ||||||
|  |  | ||||||
|  | on the same folder of | ||||||
|  |  | ||||||
|  | `bot.py` | ||||||
|  |  | ||||||
|  | file | ||||||
|  |  | ||||||
|  | containing | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | TOKEN = 'your-bot-token' | ||||||
|  |  | ||||||
|  | BOT_PATH= '/the/path/of/the/project' | ||||||
|  |  | ||||||
|  | LOG_PATH= '/the/path/where/you/want/put/game/daily/logs' | ||||||
|  |  | ||||||
|  | BOT_EXEC_CMD= 'python3 bot.py' # or any other way you start the bot | ||||||
|  |  | ||||||
|  | SUPER_USERS= [ your_chat_id ] | ||||||
|  | ``` | ||||||
							
								
								
									
										27
									
								
								assets/events.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								assets/events.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | EVENTS = [ | ||||||
|  |   { | ||||||
|  |     'id'                      : 'ATTACK', | ||||||
|  |     'text'                    : '{Player1} ha attacato {player2}', | ||||||
|  |     'resolve_text'            : '{Player1} ha causato {effetto_collaterale}', | ||||||
|  |     'fail_text'               : '{Player1} ha fallito, {player2} è indenne', | ||||||
|  |     'success_percentage'      : 80, | ||||||
|  |     'fail_percentage'         : 19, | ||||||
|  |     'extreme_fail_percentage' : 1, | ||||||
|  |     'requirements'            : {}, | ||||||
|  |     'weight'                  : 1, | ||||||
|  |     'affected_players'        : 1, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     'id'                      : 'BOMB_EXPLOSION', | ||||||
|  |     'text'                    : '{Player1} ha attacato {player2}', | ||||||
|  |     'resolve_text'            : '{Player1} ha causato {effetto_collaterale}', | ||||||
|  |     'fail_text'               : '{Player1} ha fallito, {player2} è indenne', | ||||||
|  |     'success_percentage'      : 80, | ||||||
|  |     'fail_percentage'         : 19, | ||||||
|  |     'extreme_fail_percentage' : 1, | ||||||
|  |     'requirements'            : { | ||||||
|  |       'weapons' : ['BOMB'] | ||||||
|  |     }, | ||||||
|  |     'affected_players'       : [2,3,4], | ||||||
|  |   }, | ||||||
|  | ] | ||||||
							
								
								
									
										130
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								bot.py
									
									
									
									
									
								
							| @@ -1,64 +1,104 @@ | |||||||
| import asyncio | from telegram.ext import Application | ||||||
| import datetime | from telegram.ext import CommandHandler | ||||||
| from telegram import Update | from telegram.ext import MessageHandler | ||||||
| from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes | from telegram.ext import filters | ||||||
| import bot_syms as _botsyms | from telegram import ReplyKeyboardMarkup | ||||||
| import main as _brsim | from telegram import ReplyKeyboardRemove | ||||||
|  |  | ||||||
| async def loop_game(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | from utils import logs as _log | ||||||
|   chat_id = update.effective_chat.id |  | ||||||
|   if 'arena' in context.application.bot_data: |  | ||||||
|     pass |  | ||||||
|   else: |  | ||||||
|     print('Arena non trovata') |  | ||||||
|     await update.message.reply_text('Che e\' successo? un Guarino ha rubato l\'arena, avvia una nuova partita con /start') |  | ||||||
|  |  | ||||||
| async def bot_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | from entities import arena as _arena | ||||||
|  | from bot_libs import special_commands as _scmd | ||||||
|  | from bot_libs import syms as _botsyms | ||||||
|  | from bot_libs import commands_handling as _cmd | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def bot_start(update, context): | ||||||
|   await update.message.reply_text(_botsyms.START_MSG) |   await update.message.reply_text(_botsyms.START_MSG) | ||||||
|  |   if 'ask_name' in context.application.bot_data: | ||||||
|  |     del(context.application.bot_data['ask_name']) | ||||||
|  |   if 'ask_seconds' in context.application.bot_data: | ||||||
|  |     del(context.application.bot_data['ask_seconds']) | ||||||
|  |  | ||||||
|  |   keyboard = [ | ||||||
|  |       ['Init/Restart'], | ||||||
|  |       ['Add Player', 'Add random Players', 'Add random color Players'], | ||||||
|  |       ['Get Players', 'Get Alive Players', 'Get Death Players', 'Get Ranking Players',], | ||||||
|  |       ['Simulate Day', 'Run Periodically', 'Show Map UTF8', 'Show Map Image'] | ||||||
|  |   ] | ||||||
|  |   reply_markup= ReplyKeyboardMarkup(keyboard, one_time_keyboard=False, resize_keyboard=True) | ||||||
|  |  | ||||||
|   chat_id = update.effective_chat.id |   chat_id = update.effective_chat.id | ||||||
|   print(f'{chat_id}: Sto costruendo il mondo di gioco...') |   _log.log_debug(f'bot_start: {chat_id} - I\'m building the world\'s game...') | ||||||
|   Arena= _brsim.init_arena() |   Arena= _arena.BrSimArena() | ||||||
|   players= Arena.get_players() |  | ||||||
|   weapons= Arena.get_weapons() |  | ||||||
|   print(f'Ecco il mondo di gioco, questi sono i giocatori: {players}') |  | ||||||
|   print(f'Ecco le armi disponibili nel mondo: {weapons}') |  | ||||||
|  |  | ||||||
|   await update.message.reply_text('Ho creato il mondo di gioco') |   await update.message.reply_text('Ho creato il mondo di gioco', reply_markup=reply_markup) | ||||||
|   await update.message.reply_text(f'Ecco la lista degli sfortunati avventurieri:\n{players}') |  | ||||||
|   await update.message.reply_text(f'Queste le armi che avranno a disposizione nell\'arena:\n{weapons}') |  | ||||||
|   context.application.bot_data['arena'] = Arena |   context.application.bot_data['arena'] = Arena | ||||||
|  |  | ||||||
|   context.job_queue.run_daily( | async def bot_commands(update, context): | ||||||
|     loop_game, |   text= update.message.text | ||||||
|     time=datetime.time(hour=0, minute=0, second=5, tzinfo=datetime.timezone.utc), |   chat_id = update.effective_chat.id | ||||||
|     chat_id=chat_id, |   username= update.effective_chat.username | ||||||
|     name=str(chat_id) |   first_name= update.effective_chat.first_name | ||||||
|     ) |   last_name= update.effective_chat.last_name | ||||||
|   print(f'Job giornaliero creato per la chat {chat_id}') |   chat_type= update.effective_chat.type.name # PRIVAT|BOT|ETC.. | ||||||
|  |  | ||||||
| async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: |  | ||||||
|   testo_ricevuto = update.message.text |   _log.log_info(f'bot_command: \ | ||||||
|  | user id="{chat_id}" \ | ||||||
|  | username="{username}" \ | ||||||
|  | first_name="{first_name}" \ | ||||||
|  | last_name="{last_name}" \ | ||||||
|  | chat_type="{chat_type}"') | ||||||
|  |  | ||||||
|  |   # init or restart the game | ||||||
|  |   if text == 'Init/Restart': | ||||||
|  |     return await bot_start(update, context) | ||||||
|  |    | ||||||
|  |   # player game commands | ||||||
|  |   if text == 'Add Player': | ||||||
|  |     return await _cmd.cmd_add_player(context, update, chat_id) | ||||||
|  |   if text == 'Get Players': | ||||||
|  |     return await _cmd.cmd_get_players(context, update, chat_id) | ||||||
|  |   if text == 'Get Alive Players': | ||||||
|  |     return await _cmd.cmd_get_alive_players(context, update, chat_id) | ||||||
|  |   if text == 'Get Death Players': | ||||||
|  |     return await _cmd.cmd_get_death_players(context, update, chat_id) | ||||||
|  |   if text == 'Get Ranking Players': | ||||||
|  |     return await _cmd.cmd_get_ranking_players(context, update, chat_id) | ||||||
|  |   if text == 'Simulate Day': | ||||||
|  |     return await _cmd.cmd_simulate_day(context, update, chat_id) | ||||||
|  |   if text == 'Run Periodically': | ||||||
|  |     return await _cmd.cmd_simulate_day_cron(context, update, chat_id) | ||||||
|  |   if text == 'Add random Players': | ||||||
|  |     return await _cmd.cmd_add_random_players(context, update, chat_id) | ||||||
|  |   if text == 'Add random color Players': | ||||||
|  |     return await _cmd.cmd_add_random_color_players(context, update, chat_id) | ||||||
|  |   if text == 'Show Map UTF8': | ||||||
|  |     return await _cmd.cmd_show_game_map_unicode(context, update, chat_id) | ||||||
|  |   if text == 'Show Map Image': | ||||||
|  |     return await _cmd.cmd_show_game_map_image(context, update, chat_id) | ||||||
|  |    | ||||||
|  |   # special commands | ||||||
|  |   if text == 'upstart': return await _scmd.update_bot(update, context) | ||||||
|  |   if text == 'logs': return await _scmd.show_logs(update, context) | ||||||
|  |    | ||||||
|  |   # get user input | ||||||
|  |   if context.application.bot_data.get('ask_name'): | ||||||
|  |     return await _cmd.cmd_get_player_name(context, update, chat_id, text) | ||||||
|  |   if context.application.bot_data.get('ask_seconds'): | ||||||
|  |     return await _cmd.cmd_get_cron_time(context, update, chat_id, text) | ||||||
|  |  | ||||||
|  |   _log.log_debug(f'bot_command: {chat_id} - sent this text: {text}') | ||||||
|   await update.message.reply_text(_botsyms.WIP_MSG) |   await update.message.reply_text(_botsyms.WIP_MSG) | ||||||
|  |  | ||||||
| async def add_player(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: |  | ||||||
|   name= " ".join(context.args) |  | ||||||
|   print(f'sto aggiungendo il giocatore {name} all\'arena') |  | ||||||
|   _brsim.BrSimArena |  | ||||||
|   Arena= context.application.bot_data['arena'] |  | ||||||
|   Arena.add_player(name) |  | ||||||
|   print(f'Giocatori: {Arena.get_players()}') |  | ||||||
|   await update.message.reply_text(f'Ecco i giocatori presenti nel mondo do gioco: \n{Arena.get_players()}') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
|   application = Application.builder().token(_botsyms.TOKEN).build() |   application = Application.builder().token(_botsyms.TOKEN).build() | ||||||
|  |  | ||||||
|   application.add_handler(CommandHandler('start', bot_start)) |   application.add_handler(CommandHandler('start', bot_start)) | ||||||
|   application.add_handler(CommandHandler('addplayer', add_player)) |   application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, bot_commands)) | ||||||
|   application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo)) |  | ||||||
|  |  | ||||||
|   print('Bot in esecuzione...') |   _log.log_info('main: Bot is running...') | ||||||
|   application.run_polling() |   application.run_polling() | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								bot_libs/commands_handling.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								bot_libs/commands_handling.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | from utils import logs as _log | ||||||
|  | from bot_libs import syms as _bot_syms | ||||||
|  | from bot_libs import player_handling as _bot_player | ||||||
|  | from bot_libs import simulation as _bot_simulation | ||||||
|  | from bot_libs import repeating as _bot_repeat | ||||||
|  |  | ||||||
|  | ### parse user input | ||||||
|  |  | ||||||
|  | async def _cmd_list_of_players(context, update, chat_id): | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   players= [p.get_name_and_stats() for p in Arena.get_players()] | ||||||
|  |   players_str= '\n'.join(players) | ||||||
|  |   return await update.message.reply_text(f'Ecco i {len(players)} giocatori presenti nel mondo do gioco: \n{players_str}') | ||||||
|  |  | ||||||
|  | async def cmd_get_player_name(context, update, chat_id, text): | ||||||
|  |   _log.log_info(f'cmd_get_player_name: {chat_id} - Collected Player Name {text}') | ||||||
|  |   if not 'ask_name' in context.application.bot_data: return | ||||||
|  |  | ||||||
|  |   del(context.application.bot_data['ask_name']) | ||||||
|  |   players= text.split(',') | ||||||
|  |   for player in players: | ||||||
|  |     await _bot_player.add_player(update, context, chat_id, player.strip()) | ||||||
|  |   return await _cmd_list_of_players(context, update, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_get_cron_time(context, update, chat_id, text): | ||||||
|  |   _log.log_info(f'cmd_get_cron_time: {chat_id} - User Wants to auto-run the game every {text} seconds') | ||||||
|  |   try: text= int(text) | ||||||
|  |   except: return | ||||||
|  |   seconds= max(1, text) | ||||||
|  |   if 'ask_seconds' in context.application.bot_data: | ||||||
|  |     del(context.application.bot_data['ask_seconds']) | ||||||
|  |   return await _bot_repeat.start_loop_game(update, context, seconds) | ||||||
|  |  | ||||||
|  | ### basic commands handling | ||||||
|  |  | ||||||
|  | async def cmd_add_random_players(context, update, chat_id): | ||||||
|  |   await _bot_player.add_random_players(update, context, chat_id, colors_names= False) | ||||||
|  |   return await _cmd_list_of_players(context, update, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_add_random_color_players(context, update, chat_id): | ||||||
|  |   await _bot_player.add_random_players(update, context, chat_id, colors_names= True) | ||||||
|  |   return await _cmd_list_of_players(context, update, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_add_player(context, update, chat_id): | ||||||
|  |   context.application.bot_data['ask_name'] = 1 | ||||||
|  |   if 'ask_seconds' in context.application.bot_data: | ||||||
|  |     del(context.application.bot_data['ask_seconds']) | ||||||
|  |   return await update.message.reply_text('Inserisci il Nome del giocatore (o piu\' nomi separati da virgola)') | ||||||
|  |  | ||||||
|  | async def cmd_get_players(context, update, chat_id): | ||||||
|  |   return await _bot_player.get_players(update, context, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_get_alive_players(context, update, chat_id): | ||||||
|  |   return await _bot_player.get_alive_players(update, context, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_get_death_players(context, update, chat_id): | ||||||
|  |   return await _bot_player.get_death_players(update, context, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_get_ranking_players(context, update, chat_id): | ||||||
|  |   return await _bot_player.get_ranking_players(update, context, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_simulate_day(context, update, chat_id): | ||||||
|  |   return await _bot_simulation.simulate_day(context, chat_id) | ||||||
|  |  | ||||||
|  | async def cmd_show_game_map_unicode(context, update, chat_id): | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   Gamemap= Arena.get_map() | ||||||
|  |   rendered_map= Gamemap.get_renderized_map() | ||||||
|  |   await update.message.reply_text(rendered_map) | ||||||
|  |   return await update.message.reply_text(_bot_syms.MAP_UTF8_LEGEND, parse_mode='MarkdownV2') | ||||||
|  |  | ||||||
|  | async def cmd_show_game_map_image(context, update, chat_id): | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   Gamemap= Arena.get_map() | ||||||
|  |   image_map= Gamemap.get_image_map() | ||||||
|  |   bot= update.get_bot() | ||||||
|  |   await bot.send_photo(chat_id= chat_id, photo= image_map) | ||||||
|  |   return await update.message.reply_text(_bot_syms.MAP_IMAGE_LEGEND, parse_mode='MarkdownV2') | ||||||
|  |  | ||||||
|  | async def cmd_simulate_day_cron(context, update, chat_id): | ||||||
|  |   context.application.bot_data['ask_seconds'] = 1 | ||||||
|  |   if 'ask_name' in context.application.bot_data: | ||||||
|  |     del(context.application.bot_data['ask_name']) | ||||||
|  |   return await update.message.reply_text('Inserisci il numero di secondi, ad esempio \n(60 = 1 minuto)(600 = 10 minuti)\n(3600 = 1 ora)\n(86400 = 1 giorno)') | ||||||
							
								
								
									
										53
									
								
								bot_libs/player_handling.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								bot_libs/player_handling.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | import random as _rand | ||||||
|  | from utils import logs as _log | ||||||
|  | from bot_libs import syms as _bot_syms | ||||||
|  |  | ||||||
|  | async def add_player(update, context, chat_id, name): | ||||||
|  |   _log.log_info(f'add_player: {chat_id} - {name}') | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   #if len(Arena.get_players()) >= 70: return # prevent message too long error | ||||||
|  |   if len(Arena.get_players()) >= 20: return # maybe this should depend on the map dimension | ||||||
|  |   Arena.add_player(name) | ||||||
|  |  | ||||||
|  | async def add_random_players(update, context, chat_id, colors_names= False): | ||||||
|  |   if colors_names: names= _bot_syms.COLORS_NAMES | ||||||
|  |   else: names= _bot_syms.RANDOM_NAMES | ||||||
|  |  | ||||||
|  |   max_players= len(names) | ||||||
|  |   min_players= min(7, max_players) | ||||||
|  |  | ||||||
|  |   players_num= _rand.randint(min_players, max_players) | ||||||
|  |   _rand.shuffle(names) | ||||||
|  |   lucky_players= _rand.sample(names, players_num) | ||||||
|  |   _log.log_info(f'add_random_players: {chat_id} - extracting {players_num} random players for the game') | ||||||
|  |   for name in lucky_players: await add_player(update, context, chat_id, name) | ||||||
|  |  | ||||||
|  | async def get_players(update, context, chat_id): | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   players= [p.get_name_and_stats() for p in Arena.get_players()] | ||||||
|  |   players_str= '\n'.join(players) | ||||||
|  |   _log.log_info(f'get_players: {chat_id} - {players_str}') | ||||||
|  |   await update.message.reply_text(f'Ecco i giocatori presenti nel mondo do gioco: \n{players_str}') | ||||||
|  |  | ||||||
|  | async def get_alive_players(update, context, chat_id): | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   alive= [a.get_name_and_stats() for a in Arena.get_alive_players()] | ||||||
|  |   alive_str= '\n'.join(alive) | ||||||
|  |   _log.log_info(f'get_alive_players: {chat_id} - {alive_str}') | ||||||
|  |   await update.message.reply_text(f'Ecco i giocatori ancora vivi: \n{alive_str}') | ||||||
|  |  | ||||||
|  | async def get_death_players(update, context, chat_id): | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   death= [d.get_name_and_stats() for d in Arena.get_death_players()] | ||||||
|  |   death_str= '\n'.join(death) | ||||||
|  |   _log.log_info(f'get_death_players: {chat_id} - {death_str}') | ||||||
|  |   await update.message.reply_text(f'Ecco i giocatori morti x.x: \n{death_str}') | ||||||
|  |  | ||||||
|  | async def get_ranking_players(update, context, chat_id): | ||||||
|  |   Arena = context.application.bot_data['arena'] | ||||||
|  |   leaderboard_text = Arena.get_ranking() | ||||||
|  |   _log.log_info(f'get_ranking_players: {chat_id} - {leaderboard_text}') | ||||||
|  |   await update.message.reply_text( | ||||||
|  |       f'{leaderboard_text}', | ||||||
|  |       parse_mode='Markdown' | ||||||
|  |   ) | ||||||
							
								
								
									
										24
									
								
								bot_libs/repeating.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								bot_libs/repeating.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | from utils import logs as _log | ||||||
|  | from bot_libs import simulation as _bot_sim | ||||||
|  |  | ||||||
|  | async def _loop_game(context): | ||||||
|  |   chat_id = context.job.chat_id | ||||||
|  |   _log.log_info(f'_loop_game: {chat_id} - run game simulation day') | ||||||
|  |   return await _bot_sim.simulate_day(context, chat_id) | ||||||
|  |  | ||||||
|  | async def start_loop_game(update, context, seconds): | ||||||
|  |   await update.message.reply_text(f'Ok capo!! giochero\' per te ogni {seconds}s') | ||||||
|  |   chat_id = update.effective_chat.id | ||||||
|  |   if 'arena' not in context.application.bot_data: | ||||||
|  |     _log.log_info(f'start_loop_game: {chat_id} - Arena not found') | ||||||
|  |     await update.message.reply_text(f'Arena non trovata, avviare con /start') | ||||||
|  |     return | ||||||
|  |  | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   if len(Arena.get_players()) < 2: | ||||||
|  |     _log.log_info(f'start_loop_game: {chat_id} - Not enough player to start the match') | ||||||
|  |     await update.message.reply_text(f'Servono almeno 2 giocatori. Ecco i giocatori presenti nel mondo do gioco: \n{Arena.get_players()}') | ||||||
|  |     return | ||||||
|  |  | ||||||
|  |   context.job_queue.run_repeating(_loop_game, interval= seconds, first=1, chat_id= chat_id) | ||||||
|  |   _log.log_info(f'start_loop_game: {chat_id} - Cron job started') | ||||||
							
								
								
									
										41
									
								
								bot_libs/simulation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								bot_libs/simulation.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | import debug as _dbg | ||||||
|  | from utils import logs as _log | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_winner(context, Arena, chat_id): | ||||||
|  |   winner= Arena.get_alive_players()[0] | ||||||
|  |   try: | ||||||
|  |     context.job.schedule_removal() | ||||||
|  |     _log.log_info(f'simulate_day: {chat_id} Loop removed') | ||||||
|  |   except: pass | ||||||
|  |  | ||||||
|  |   msg= f'{winner.get_name_and_stats()} Vince la cruenta battaglia ' | ||||||
|  |   msg+= f'uccidendo {winner.get_kills()} giocatori ' | ||||||
|  |   msg+= f'e schivando {winner.get_dodges()} colpi nemici, e vive felice e ' | ||||||
|  |   if winner.player_gender_is_male(): | ||||||
|  |     msg+= 'contento con Guarino' | ||||||
|  |   elif winner.player_gender_is_female(): | ||||||
|  |     msg+= 'contenta con Guarino' | ||||||
|  |   else: | ||||||
|  |     msg+= 'content# con Guarino' | ||||||
|  |   return msg | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def simulate_day(context, chat_id): | ||||||
|  |   if 'arena' not in context.application.bot_data: | ||||||
|  |     _log.log_info('simulate_day: {chat_id} Arena not Found') | ||||||
|  |     await context.bot.send_message(chat_id, 'Che e\' successo? un Guarino ha rubato l\'arena, avvia una nuova partita con /start') | ||||||
|  |     return | ||||||
|  |  | ||||||
|  |   Arena= context.application.bot_data['arena'] | ||||||
|  |   if len(Arena.get_alive_players()) <= 1: return await context.bot.send_message(chat_id, 'Il gioco e\' finito, Grazie per aver giocato!') | ||||||
|  |  | ||||||
|  |   await context.bot.send_message(chat_id, f'Giorno #{Arena.day}') | ||||||
|  |   msg= _dbg.play_one_day_debug(Arena) | ||||||
|  |   await context.bot.send_message(chat_id, msg) | ||||||
|  |   #Print the ranking each day | ||||||
|  |   msg= Arena.get_ranking() | ||||||
|  |   await context.bot.send_message(chat_id, msg) | ||||||
|  |   if len(Arena.get_alive_players()) == 1: | ||||||
|  |     msg= get_winner(context, Arena, chat_id) | ||||||
|  |     return await context.bot.send_message(chat_id, msg) | ||||||
							
								
								
									
										50
									
								
								bot_libs/special_commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								bot_libs/special_commands.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | import os as _os | ||||||
|  | from utils import logs as _log | ||||||
|  | from bot_libs import syms as _botsyms | ||||||
|  |  | ||||||
|  | async def _pull_repo(update): | ||||||
|  |   err= _os.system(f'cd {_botsyms.BOT_PATH}; /usr/bin/git pull') | ||||||
|  |   if err: | ||||||
|  |     _log.log_error(f'update_bot: {chat_id} error {err} while trying to update the app') | ||||||
|  |     await update.message.reply_text('Errore durante l\'aggiornamento del Bot') | ||||||
|  |   return err | ||||||
|  |  | ||||||
|  | async def _upstart_service(update): | ||||||
|  |   err= _os.system(_botsyms.BOT_EXEC_CMD) | ||||||
|  |   # this error is fake, probably due to systemd restart that make the bot istance broke | ||||||
|  |   #if err: | ||||||
|  |     #_log.log_error(f'update_bot: {chat_id} error {err} while trying to upstart the app') | ||||||
|  |     #return await update.message.reply_text('Errore durante il riavvio del Bot') | ||||||
|  |  | ||||||
|  | async def update_bot(update, context): | ||||||
|  |   chat_id = update.effective_chat.id | ||||||
|  |  | ||||||
|  |   if update.message.chat.id not in _botsyms.SUPER_USERS: | ||||||
|  |     return _log.log_warning(f'update_bot: user {chat_id} not allowed') | ||||||
|  |  | ||||||
|  |   await update.message.reply_text('Sto aggiornando il Bot...') | ||||||
|  |   _log.log_info(f'update_bot: {chat_id} bot is updating...') | ||||||
|  |  | ||||||
|  |   err= await _pull_repo(update) | ||||||
|  |   if err: return | ||||||
|  |   err= await _upstart_service(update) | ||||||
|  |   if err: return | ||||||
|  |  | ||||||
|  |   _log.log_info(f'update_bot: {chat_id} bot successfully updated') | ||||||
|  |   await update.message.reply_text('Bot aggiornato e riavviato!') | ||||||
|  |  | ||||||
|  | async def show_logs(update, context): | ||||||
|  |   chat_id = update.effective_chat.id | ||||||
|  |   if update.message.chat.id not in _botsyms.SUPER_USERS: | ||||||
|  |     return _log.log_warning(f'open_logs: user {chat_id} not allowed') | ||||||
|  |  | ||||||
|  |   await update.message.reply_text('Sto provando ad aprire i log...') | ||||||
|  |   _log.log_debug(f'open_logs: {chat_id} trying opening logs...') | ||||||
|  |   try: | ||||||
|  |     game_log= _os.path.expanduser(f'{_log.get_log_name()}') | ||||||
|  |     with open(game_log, 'r') as _log_file: | ||||||
|  |       lines= _log_file.readlines()[-30:] | ||||||
|  |       log_content= '\n'.join(lines) | ||||||
|  |       await update.message.reply_text(f"Contenuto del log:\n{log_content}") | ||||||
|  |   except Exception as e: | ||||||
|  |     await update.message.reply_text(f"Errore nella lettura del file di log: {str(e)}") | ||||||
							
								
								
									
										115
									
								
								bot_libs/syms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								bot_libs/syms.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | import os as _os | ||||||
|  | from utils import logs as _logs | ||||||
|  |  | ||||||
|  | _MISSING_LOCAL_SETTINGS= """ | ||||||
|  | =============== ERROR =============== | ||||||
|  |  | ||||||
|  |   Missing local configurations: | ||||||
|  |   create: local_settings.py | ||||||
|  |   with these settings: | ||||||
|  |  | ||||||
|  |   TOKEN= '<this_is_your_token_id>' # created using https://t.me/BotFather | ||||||
|  |   BOT_PATH= '/path/of/your/repo/directory' # used for upstart command | ||||||
|  |   LOG_PATH= '/path/where/you/place/logs' # used to save daily logs | ||||||
|  |   BOT_EXEC_CMD= 'python /path/of/your/repo/directory/bot.py' # the command you use to launch the bot instance | ||||||
|  |   SUPER_USERS= [<chat_id>] # this is the superuser, allowed to use special commands (upstart and logs) | ||||||
|  |  | ||||||
|  | ===================================== | ||||||
|  | """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | try: | ||||||
|  |   from local_settings import TOKEN as _token | ||||||
|  |   TOKEN= _token | ||||||
|  | except: _logs.log_debug(_MISSING_LOCAL_SETTINGS) | ||||||
|  | try: | ||||||
|  |   from local_settings import BOT_PATH as _bot_path | ||||||
|  |   BOT_PATH= _os.path.expanduser(_bot_path) | ||||||
|  | except: BOT_PATH= '' | ||||||
|  | try: | ||||||
|  |   from local_settings import LOG_PATH as _log_path | ||||||
|  |   LOG_PATH= _os.path.expanduser(_log_path) | ||||||
|  | except: LOG_PATH= '/tmp' | ||||||
|  | try: | ||||||
|  |   from local_settings import BOT_EXEC_CMD as _bot_exec_cmd | ||||||
|  |   BOT_EXEC_CMD= _bot_exec_cmd | ||||||
|  | except: BOT_EXEC_CMD= None | ||||||
|  | try: | ||||||
|  |   from local_settings import SUPER_USERS as _superusers | ||||||
|  |   SUPER_USERS= _superusers | ||||||
|  | except: SUPER_USERS= [] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | START_MSG= """Benvenuto nel crudele mondo di Battle Royal Simulator, | ||||||
|  | La tua avventura e quella dei tuoi compagni inizia qui. | ||||||
|  | Questo Bot simula Hunger Games, | ||||||
|  | Tu e i tuoi compagni, sarete catapultati in questo mondo, | ||||||
|  | ma solo 1 di voi riuscira' a salvarsi. | ||||||
|  | Uccidi o sarai tu ad essere ucciso | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | WIP_MSG= "Ehi, mio padre mi sta ancora finendo di creare, abbi pazienza, che fretta hai di entrare in questo mondo per morire?" | ||||||
|  |  | ||||||
|  | RANDOM_NAMES = [ | ||||||
|  |     "Aeliana", "Thorne", "Kael", "Seraphine", "Jaxon", "Lyra", "Darius", "Elowen", | ||||||
|  |     "Zander", "Nyssa", "Orion", "Vesper", "Kieran", "Isolde", "Riven", "Calista", | ||||||
|  |     "Draven", "Mira", "Zephyr", "Selene", "Ashen", "Talia", "Finnian", "Aria", | ||||||
|  |     "Kaelan", "Liora", "Soren", "Elara", "Thalia", "Jett", "Cressida", "Lucian", | ||||||
|  |     "Freya", "Ronan", "Niamh", "Kellan", "Zara", "Dorian", "Amara", "Jace", | ||||||
|  |     "Elysia", "Caius", "Sable", "Alaric", "Veda", "Quinn", "Thorne", "Lirael", | ||||||
|  |     "Rhea", "Kade", "Isadora", "Ash", "Nyx", "Cassian", "Elowen", "Tamsin", | ||||||
|  |     "Rylan", "Faye", "Jorah", "Sienna", "Kieran", "Astra", "Zane", "Lyric", | ||||||
|  |     "Dax", "Ember", "Orion", "Selah", "Juno", "Kaia", "Thorne", "Vespera", | ||||||
|  |     "Riven", "Caden", "Liora", "Soren", "Elara", "Talia", "Jett", "Freya", | ||||||
|  |     "Ronan", "Niamh", "Kellan", "Zara", "Dorian", "Amara", "Jace", "Elysia", | ||||||
|  |     "Caius", "Sable", "Alaric", "Veda", "Quinn", "Thorne", "Lirael", "Rhea", | ||||||
|  |     "Kade", "Isadora", "Ash", "Nyx", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | COLORS_NAMES= [ | ||||||
|  |     "Yellow", "Grey", "Violet", "Black", "Lime", "Ruby", "Avocado", "Crystal", | ||||||
|  |     "Pink", "Maize", "Coral", "Jade", "Platinum", "Emerald", "Carmine", "Nickel", | ||||||
|  |     "Chocolate", "Slate", "Turquoise", "Silver", "Teal", "Jet", "Ivory", "Cobalt", | ||||||
|  |     "Vermillion", "Aero", "Orange", "Rhythm", "Amber", "Olive", "Sepia", "Cyan", | ||||||
|  |     "Green", "Ochre", "Denim", "Erin", "Fuchsia", "Aqua", "Iceberg", "Blue", | ||||||
|  |     "Canary", "Red", "Mint", "Scarlet", "Coffee", "Indigo", "Mystic", "Rose", | ||||||
|  |     "Pearl", "Pumpkin", "Navy", "Ultramarine", "Sapphire", "Desert", "Cherry", | ||||||
|  |     "Tulip", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | MAP_UTF8_FIELD= '🟩' | ||||||
|  | MAP_UTF8_MOUNTAIN= '⛰️' | ||||||
|  | MAP_UTF8_PLAYER_MALE= '👨' | ||||||
|  | MAP_UTF8_PLAYER_FEMALE= '👧🏻' | ||||||
|  | MAP_UTF8_PLAYER_NONBINARY= '⚧️' | ||||||
|  | MAP_UTF8_DEATH_PLAYER= '💀' | ||||||
|  | MAP_UTF8_ITEM= '📦' | ||||||
|  |  | ||||||
|  | MAP_UTF8_LEGEND= f"""*Legenda*: | ||||||
|  | - *{MAP_UTF8_FIELD}*: Cella *libera* per muoversi | ||||||
|  | - *{MAP_UTF8_MOUNTAIN}*: Bordo della mappa, *non raggiungibile* | ||||||
|  | - *{MAP_UTF8_PLAYER_MALE}*: Posizione di un *giocatore Maschio* | ||||||
|  | - *{MAP_UTF8_PLAYER_FEMALE}*: Posizione di una *giocatorice Femmina* | ||||||
|  | - *{MAP_UTF8_PLAYER_NONBINARY}*: Posizione di un *giocatore non binario* | ||||||
|  | - *{MAP_UTF8_DEATH_PLAYER}*: Posizione di un *giocatore morto* | ||||||
|  | - *{MAP_UTF8_ITEM}*: Posizione di un *oggetto* (non ancora implementato) | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | MAP_IMAGE_FIELD= (0, 255, 0) # green | ||||||
|  | MAP_IMAGE_MOUNTAIN= (0, 0, 0) # black | ||||||
|  | MAP_IMAGE_PLAYER_MALE= (0, 0, 255) # blue | ||||||
|  | MAP_IMAGE_PLAYER_FEMALE= (255, 0, 0) # red | ||||||
|  | MAP_IMAGE_PLAYER_NONBINARY= (255, 255, 0) # yellow | ||||||
|  | MAP_IMAGE_DEATH_PLAYER= (160, 160, 160) # grey | ||||||
|  | MAP_IMAGE_ITEM= (255, 255, 255) # white | ||||||
|  |  | ||||||
|  | MAP_IMAGE_LEGEND= r"""*Legenda*: | ||||||
|  | - *Verde*: Cella *libera* per muoversi | ||||||
|  | - *Nero*: Bordo della mappa, *non raggiungibile* | ||||||
|  | - *Blue*: Posizione di un *giocatore Maschio* | ||||||
|  | - *Rosso*: Posizione di una *giocatorice Femmina* | ||||||
|  | - *Giallo*: Posizione di un *giocatore non binario* | ||||||
|  | - *Grigio*: Posizione di un *giocatore morto* | ||||||
|  | - *Bianco*: Posizione di un *oggetto* (non ancora implementato) | ||||||
|  | """ | ||||||
							
								
								
									
										11
									
								
								bot_syms.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								bot_syms.py
									
									
									
									
									
								
							| @@ -1,11 +0,0 @@ | |||||||
| TOKEN = "7670066927:AAG8jI5n9NcyxPksYky7LPYqA08BThs07c4" |  | ||||||
|  |  | ||||||
| START_MSG= """Benvenuto nel crudele mondo di Battle Royal Simulator, |  | ||||||
| La tua avventura e quella dei tuoi compagni inizia qui. |  | ||||||
| Questo Bot simula Hunger Games, |  | ||||||
| Tu e i tuoi compagni, sarete catapultati in questo mondo, |  | ||||||
| ma solo 1 di voi riuscira' a salvarsi. |  | ||||||
| Uccidi o sarai tu ad essere ucciso |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| WIP_MSG= "Ehi, mio padre mi sta ancora finendo di creare, abbi pazienza, che fretta hai di entrare in questo mondo per morire?" |  | ||||||
							
								
								
									
										119
									
								
								debug.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								debug.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | |||||||
|  | import time as _time | ||||||
|  | import random as _rand | ||||||
|  | import main as _main | ||||||
|  | from utils import logs as _logs | ||||||
|  | from bot_libs import syms as _syms | ||||||
|  |  | ||||||
|  | def _debug_data(): | ||||||
|  |   players= _syms.COLORS_NAMES | ||||||
|  |   weapons= [] | ||||||
|  |  | ||||||
|  |   Arena= _main.init_arena() | ||||||
|  |   for player in players: | ||||||
|  |     Arena.add_player(player) | ||||||
|  |   return Arena | ||||||
|  |  | ||||||
|  | def _end_game_debug(alive_players, day): | ||||||
|  |   last_player= alive_players[0] | ||||||
|  |   msg= f'{last_player.get_name()} sopravvive e vince dopo {day} lunghi Giorni, conquistando l\'amore eterno di Guarino' | ||||||
|  |   _logs.log_debug(msg) | ||||||
|  |   return msg | ||||||
|  |  | ||||||
|  | def _random_action(Arena, Player_one): | ||||||
|  |   _RANDOM_ACTIONS= { | ||||||
|  |       1: 'attack', | ||||||
|  |       2: 'move', | ||||||
|  |       } | ||||||
|  |   Map= Arena.get_map() | ||||||
|  |   avail_actions= Map.get_player_available_actions(Player_one) | ||||||
|  |   _logs.log_debug(f'{Player_one.get_name()}:{Player_one.get_coordinates()}, avail_actions: {avail_actions}') | ||||||
|  |   msg= '' | ||||||
|  |   if 1 in avail_actions: | ||||||
|  |     # XXX maybe in future this action is available only if you are near to another player | ||||||
|  |     # so Player_two is no more random, but will be a random near player | ||||||
|  |     preys= avail_actions[1] | ||||||
|  |     Player_two= _rand.sample(preys, 1)[0] | ||||||
|  |     while Player_one.get_id() == Player_two.get_id() and not Player_two.is_alive(): | ||||||
|  |       Player_two= _rand.sample(preys, 1)[0] | ||||||
|  |     _dmg, msg= Player_one.attack(Player_two) | ||||||
|  |   elif 2 in avail_actions: | ||||||
|  |     Map= Arena.get_map() | ||||||
|  |     available_movements= Map.get_player_available_directions(Player_one) | ||||||
|  |     _logs.log_debug(f'{Player_one.get_name()}:{Player_one.get_coordinates()}, avail_movements: {available_movements}') | ||||||
|  |     if not available_movements: | ||||||
|  |       # XXX probably should skip this action and look for another action | ||||||
|  |       return f'{Player_one.get_name()} Pensa a Guarino tutto il giorno' | ||||||
|  |     _rand.shuffle(available_movements) | ||||||
|  |     x, y, direction= available_movements[0] | ||||||
|  |     Player_one.move(x, y) | ||||||
|  |     Map.init_map_matrix() | ||||||
|  |     _logs.log_debug(Map.get_renderized_map()) | ||||||
|  |     msg= f'{Player_one.get_name()} Si muove verso »»» {direction}' | ||||||
|  |  | ||||||
|  |   return msg | ||||||
|  |  | ||||||
|  | def play_one_day_debug(Arena): | ||||||
|  |   if not Arena.get_players(): return | ||||||
|  |   _logs.log_debug(f'Giorno #{Arena.day}') | ||||||
|  |   alive_players= Arena.get_alive_players() | ||||||
|  |   if len(alive_players) == 1: | ||||||
|  |     day= Arena.day | ||||||
|  |     return _end_game_debug(alive_players, day) | ||||||
|  |    | ||||||
|  |   daily_events= [] | ||||||
|  |   _rand.shuffle(alive_players) | ||||||
|  |   for Player_one in alive_players: | ||||||
|  |     if not Player_one.is_alive(): continue # he could be dead during this day cycle | ||||||
|  |     msg= _random_action(Arena, Player_one) | ||||||
|  |     #p_two= _rand.sample(Arena.get_alive_players(), 1)[0] | ||||||
|  |     #while Player_one.get_id() == p_two.get_id(): | ||||||
|  |       #p_two= _rand.sample(Arena.get_alive_players(), 1)[0] | ||||||
|  |     #_dmg, msg= Player_one.attack(p_two) | ||||||
|  |     daily_events.append(msg) | ||||||
|  |    | ||||||
|  |   Arena.next_day() | ||||||
|  |   res= '\n'.join(daily_events) | ||||||
|  |   return res | ||||||
|  |   #p_one, p_two= _rand.sample(alive_players, 2) | ||||||
|  |   #_dmg, msg= p_one.attack(p_two) | ||||||
|  |   #return msg | ||||||
|  |  | ||||||
|  | def init_debug_simulation(): | ||||||
|  |   Arena= _debug_data() | ||||||
|  |   while (len(Arena.get_alive_players()) > 1): | ||||||
|  |     events= play_one_day_debug(Arena) | ||||||
|  |     _logs.log_debug('#################') | ||||||
|  |     _logs.log_debug('#################') | ||||||
|  |     _logs.log_debug('#################') | ||||||
|  |     _logs.log_debug(events) | ||||||
|  |     _logs.log_debug('#################') | ||||||
|  |     _logs.log_debug('#################') | ||||||
|  |     _logs.log_debug('#################') | ||||||
|  |     _time.sleep(0.3) | ||||||
|  |  | ||||||
|  | def init_debug_loop(): | ||||||
|  |   Arena= _debug_data() | ||||||
|  |   while (len(Arena.get_alive_players()) > 1): | ||||||
|  |     alive_players= Arena.get_alive_players() | ||||||
|  |  | ||||||
|  |     p_one, p_two= _rand.sample(alive_players, 2) | ||||||
|  |     p_one.attack(p_two) | ||||||
|  |  | ||||||
|  |     #Start a day | ||||||
|  |     #At 23:59: | ||||||
|  |     Arena.next_day() | ||||||
|  |     _time.sleep(0.3) | ||||||
|  |     #End of day | ||||||
|  |  | ||||||
|  |   last_player= Arena.get_alive_players()[0] | ||||||
|  |   _logs.log_debug(f'{last_player.get_name()} sopravvive e vince dopo {Arena.day} lunghi Giorni, conquistando l\'amore eterno di Guarino') | ||||||
|  |  | ||||||
|  | def debug_random_map(): | ||||||
|  |   from entities import map as _map; | ||||||
|  |   from entities import player; | ||||||
|  |   M= _map.BrSimMap(players= [player.BrSimPlayer(i) for i in range(20)]); | ||||||
|  |   res= M.get_renderized_map() | ||||||
|  |   _logs.log_debug(res) | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |   init_debug() | ||||||
							
								
								
									
										134
									
								
								entities/arena.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								entities/arena.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | |||||||
|  | from entities import player as _player | ||||||
|  | from entities import event_picker as _events | ||||||
|  | from entities import gamemap as _map | ||||||
|  | from entities.items import weapons as _weapons | ||||||
|  | from utils import logs as _logs | ||||||
|  |  | ||||||
|  | class BrSimArena(): | ||||||
|  |  | ||||||
|  |   # players = [{'name': name, 'inventory': default_inventory, other_stats}] | ||||||
|  |   # weapons = [{WEAPON.KNIFE: quantity}, etc...] # this is the whole quantity of the items available on the world | ||||||
|  |  | ||||||
|  |   def __init__(self, players= None, weapons= None): | ||||||
|  |     self.day= 1 | ||||||
|  |     self.players= [] | ||||||
|  |     self.weapons= [] | ||||||
|  |     self.eventClass = _events.ArenaEventPicker(self.players) | ||||||
|  |     self.init_players(players) | ||||||
|  |     self.init_weapons(weapons) | ||||||
|  |     self.Map= _map.BrSimMap(self.players, self.weapons) | ||||||
|  |  | ||||||
|  |   def init_players(self, players): | ||||||
|  |     if not players: return | ||||||
|  |     for player in players: | ||||||
|  |       self.add_player(player['name'], player.get('inventory')) | ||||||
|  |  | ||||||
|  |   def init_weapons(self, weapons): | ||||||
|  |     if not weapons: return | ||||||
|  |     for weapon in weapons: | ||||||
|  |       for wtype, quantity in weapon.items(): | ||||||
|  |         for i in range(quantity): self.weapons.append(_weapons.BrSimWeapon(wtype)) | ||||||
|  |  | ||||||
|  |   def next_day(self): | ||||||
|  |     self.day+= 1 | ||||||
|  |     _logs.log_debug(f'Giorno: {self.day}') | ||||||
|  |     alive_players_str= ', '.join([p.get_name() for p in self.get_alive_players()]) | ||||||
|  |     _logs.log_debug(f'Giocatori vivi: {alive_players_str}') | ||||||
|  |     death_players= self.get_death_players() | ||||||
|  |     if (death_players): | ||||||
|  |       death_players_str= ', '.join([p.get_name() for p in death_players]) | ||||||
|  |       _logs.log_debug(f'Giocatori morti: {death_players_str}') | ||||||
|  |  | ||||||
|  |   def get_alive_players(self): | ||||||
|  |     res= [] | ||||||
|  |     for p in self.players: | ||||||
|  |       if not p.is_alive(): continue | ||||||
|  |       res.append(p) | ||||||
|  |     return res | ||||||
|  |  | ||||||
|  |   def get_death_players(self): | ||||||
|  |     res= [] | ||||||
|  |     for p in self.players: | ||||||
|  |       if p.is_alive(): continue | ||||||
|  |       res.append(p) | ||||||
|  |     return res | ||||||
|  |  | ||||||
|  |   def sort_players_by_kills_and_health(self, players): | ||||||
|  |     def player_sort_key(player): | ||||||
|  |         kills= player.get_kills() | ||||||
|  |         health= player.get_health() | ||||||
|  |         #Negative values to sort in decr mode | ||||||
|  |         return (-kills, -health)  | ||||||
|  |     return sorted(players, key=player_sort_key) | ||||||
|  |  | ||||||
|  |   def get_ranking(self): | ||||||
|  |     medals = ['🥇', '🥈', '🥉'] | ||||||
|  |     leaderboard = [] | ||||||
|  |     leaderboard.append('🏆 *Classifica attuale:* \n') | ||||||
|  |     alive_players= self.get_alive_players(); | ||||||
|  |     alive_sorted= self.sort_players_by_kills_and_health(alive_players) | ||||||
|  |     death_players= self.get_death_players() | ||||||
|  |     death_sorted= self.sort_players_by_kills_and_health(death_players) | ||||||
|  |  | ||||||
|  |     # Alive Players | ||||||
|  |     alive_players= self.get_alive_players(); | ||||||
|  |     alive_sorted= self.sort_players_by_kills_and_health(alive_players) | ||||||
|  |     for index, player in enumerate(alive_sorted): | ||||||
|  |       name = player.get_name() | ||||||
|  |       kills = player.get_kills() | ||||||
|  |       health = '♥️' * player.get_health() | ||||||
|  |       if index < 3: | ||||||
|  |         position = medals[index] | ||||||
|  |       else: | ||||||
|  |         position = f"{index + 1}." | ||||||
|  |  | ||||||
|  |       line = f"{position} {name} - {kills} uccision{'i' if kills != 1 else 'e'}, {health}" | ||||||
|  |       leaderboard.append(line) | ||||||
|  |  | ||||||
|  |     # Death players: | ||||||
|  |     death_players= self.get_death_players() | ||||||
|  |     death_sorted= self.sort_players_by_kills_and_health(death_players) | ||||||
|  |  | ||||||
|  |     if death_sorted: | ||||||
|  |       leaderboard.append("\n-- GIOCATORI ELIMINATI --") | ||||||
|  |       for player in death_sorted: | ||||||
|  |         name = player.get_name() | ||||||
|  |         kills = player.get_kills() | ||||||
|  |         health = player.get_health() | ||||||
|  |  | ||||||
|  |         line = f"💀 {name} - {kills} uccision{'i' if kills != 1 else 'e'}" | ||||||
|  |         leaderboard.append(line) | ||||||
|  |  | ||||||
|  |     return '\n'.join(leaderboard) | ||||||
|  |  | ||||||
|  |   def do_random_event(self): | ||||||
|  |     #XXX random player does random action according to his inventory health, wounds, available weapons on the world, etc... | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |   def supporter_donation(self): | ||||||
|  |     #XXX supporter donate a random item or weapon to a random player | ||||||
|  |     #TODO maybe in future a player can have charism stats that can influence the chance to get a donation | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |   def add_player(self, name, inventory= None): | ||||||
|  |     player= _player.BrSimPlayer(name, inventory) | ||||||
|  |     self.players.append(player) | ||||||
|  |     self.Map.add_player_to_map(player) | ||||||
|  |  | ||||||
|  |   def add_weapon(self, weapon_type): | ||||||
|  |     weapon= _weapons.BrSimWeapon(weapon_type) | ||||||
|  |     self.weapons.append(weapon) | ||||||
|  |     self.Map.add_item_to_map(item) | ||||||
|  |  | ||||||
|  |   def get_players(self): | ||||||
|  |     return self.players | ||||||
|  |  | ||||||
|  |   def get_weapons(self): | ||||||
|  |     res= [] | ||||||
|  |     for w in self.weapons: | ||||||
|  |       #XXX implement me | ||||||
|  |       res.append(w) | ||||||
|  |     return res | ||||||
|  |  | ||||||
|  |   def get_map(self): | ||||||
|  |     return self.Map | ||||||
							
								
								
									
										5
									
								
								entities/event.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								entities/event.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | class Event(): | ||||||
|  |   def __init__(self, event_settings, the_player, affected_players = []): | ||||||
|  |     # this will be the class that manage the event, so the result and what ahppens to the players | ||||||
|  |     # will do it later | ||||||
|  |     pass | ||||||
							
								
								
									
										81
									
								
								entities/event_picker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								entities/event_picker.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | from assets.events import EVENTS | ||||||
|  | import random as _random | ||||||
|  |  | ||||||
|  | class ArenaEventPicker(): | ||||||
|  |   def __init__(self, players): | ||||||
|  |     self.players = players | ||||||
|  |     self.event_list = EVENTS | ||||||
|  |     self.already_picked_players =  [] | ||||||
|  |  | ||||||
|  |   def pick_a_player(): | ||||||
|  |     the_player = _random.choice(self.players) | ||||||
|  |     the_player_id = the_player.get('id') | ||||||
|  |  | ||||||
|  |     self.players = list(filter(lambda x : x.get('id') != the_player_id)) | ||||||
|  |  | ||||||
|  |     self.already_picked_players.append(the_player) | ||||||
|  |      | ||||||
|  |     return the_player | ||||||
|  |  | ||||||
|  |   def pick_event(the_player): | ||||||
|  |  | ||||||
|  |     player_inventory  = the_player.get('inventory') or [] | ||||||
|  |     status            = the_player['health'] | ||||||
|  |     reputation        = the_player['reputation'] | ||||||
|  |  | ||||||
|  |     elegible_events = [] | ||||||
|  |     for event in EVENTS: | ||||||
|  |       requirements = event['requirements'] | ||||||
|  |  | ||||||
|  |       keys_to_check = ['item', 'weapon', 'status', 'reputation', 'affected_players'] | ||||||
|  |  | ||||||
|  |       for check in keys_to_check: | ||||||
|  |         if requirements.get(check) and check == 'item': | ||||||
|  |           needed_items = requirements.get('check') | ||||||
|  |           if needed_items in player_inventory: | ||||||
|  |             elegible_events.append(event) | ||||||
|  |  | ||||||
|  |         if requirements.get(check) and check == 'weapon': | ||||||
|  |           needed_weapons = requirements.get('check') | ||||||
|  |           if needed_items in player_inventory: | ||||||
|  |             elegible_events.append(event) | ||||||
|  |  | ||||||
|  |         if requirements.get(check) and check == 'status': | ||||||
|  |           needed_health = requirements.get('check') | ||||||
|  |           if '>' in requirements.get(check) and requirements.get(check) > needed_health: | ||||||
|  |             elegible_events.append(event) | ||||||
|  |           if '<' in requirements.get(check) and requirements.get(check) < needed_health: | ||||||
|  |             elegible_events.append(event) | ||||||
|  |  | ||||||
|  |         if requirements.get(check) and check == 'reputation': | ||||||
|  |           needed_reputation = requirements.get('reputation') | ||||||
|  |           if '>' in requirements.get(check) and requirements.get(check) > needed_reputation: | ||||||
|  |             elegible_events.append(event) | ||||||
|  |           if '<' in requirements.get(check) and requirements.get(check) < needed_reputation: | ||||||
|  |             elegible_events.append(event) | ||||||
|  |          | ||||||
|  |         if check == 'affected_players': | ||||||
|  |           needed_players = event.get('affected_players') | ||||||
|  |           if needed_players < len(self.players): | ||||||
|  |             elegible_events.append(event) | ||||||
|  |  | ||||||
|  |     the_event = _random.choice(elegible_events) | ||||||
|  |     return the_event | ||||||
|  |  | ||||||
|  |   def pick_targets(number_of_targets): | ||||||
|  |     random.shuffle(self.players) | ||||||
|  |     return list(self.players[0:number_of_targets]) | ||||||
|  |  | ||||||
|  |   def resolve_event(): | ||||||
|  |     playing_player = self.pick_a_player() | ||||||
|  |     assigned_event = self.pick_event(playing_player) | ||||||
|  |  | ||||||
|  |     affected_players = assigned_event['affected_players'] | ||||||
|  |     if isinstance(affected_players,list): | ||||||
|  |       affected_players = _random.choice(affected_players) | ||||||
|  |  | ||||||
|  |     targeted_players = self.pick_targets(affected_players) | ||||||
|  |  | ||||||
|  |     event_instance = _event.Event(assigned_event, playing_player) | ||||||
|  |      | ||||||
|  |  | ||||||
							
								
								
									
										235
									
								
								entities/gamemap.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								entities/gamemap.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | |||||||
|  | import random as _random | ||||||
|  | import copy as _copy | ||||||
|  | import io as _io | ||||||
|  | from PIL import Image as _Image | ||||||
|  | from PIL import ImageDraw as _ImageDraw | ||||||
|  | from utils import logs as _logs | ||||||
|  | from bot_libs import syms as _bot_syms | ||||||
|  | from entities import resource as _resource | ||||||
|  | from utils import logs as _logs | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BrSimMap(): | ||||||
|  |  | ||||||
|  |   def __init__(self, players= None, items= None): | ||||||
|  |     self.players= players or [] | ||||||
|  |     self.items= items or [] | ||||||
|  |     self.world_width= 10 #seems a reasonable width for smartphones larger maps would go on a new line | ||||||
|  |     self.world_height= 10 | ||||||
|  |     self.game_map= [] | ||||||
|  |     self.field_sym= _bot_syms.MAP_UTF8_FIELD | ||||||
|  |     self.player_male_sym= _bot_syms.MAP_UTF8_PLAYER_MALE | ||||||
|  |     self.player_female_sym= _bot_syms.MAP_UTF8_PLAYER_FEMALE | ||||||
|  |     self.player_nonbinary_sym= _bot_syms.MAP_UTF8_PLAYER_NONBINARY | ||||||
|  |     self.dead_player_sym= _bot_syms.MAP_UTF8_DEATH_PLAYER | ||||||
|  |     self.item_sym= _bot_syms.MAP_UTF8_ITEM | ||||||
|  |     self.mountain_sym = _bot_syms.MAP_UTF8_MOUNTAIN | ||||||
|  |     self.init_map_matrix() | ||||||
|  |     self.init_players_coordinates() | ||||||
|  |     self.init_items_coordinates() | ||||||
|  |     self.populate_map() | ||||||
|  |  | ||||||
|  |   def init_map_matrix(self): | ||||||
|  |     # show a matrix representing the game's map | ||||||
|  |     # 🟩 is and empty cell | ||||||
|  |     # (tomorrow we can choose different colors for different locations | ||||||
|  |     # 🟠 this is a player (we could use different colors for different genders) | ||||||
|  |     # 📦 this is an item (weapon or another item) | ||||||
|  |     # 💀 this is icon when the player is dead  | ||||||
|  |     # ⛰️ this is icon for the mountain (We can prevent players from passing through the mountains and thus use them for map boundaries.) | ||||||
|  |     self.game_map= []  | ||||||
|  |  | ||||||
|  |     width= [] | ||||||
|  |     #mon = [] | ||||||
|  |     #for i in range(self.world_width): | ||||||
|  |       #mon.append(self.mountain_sym) | ||||||
|  |     for i in range(self.world_width): | ||||||
|  |       #if i == 0 or i == self.world_width - 1: width.append(self.mountain_sym) | ||||||
|  |       #else: width.append(self.field_sym) | ||||||
|  |       width.append(None) | ||||||
|  |       #width.append(self.field_sym) | ||||||
|  |     for i in range(self.world_height): | ||||||
|  |       #if i == 0 or i == self.world_height - 1: self.game_map.append(mon) | ||||||
|  |       #else: self.game_map.append(_copy.deepcopy(width)) | ||||||
|  |       self.game_map.append(_copy.deepcopy(width)) | ||||||
|  |     _logs.log_debug(f'init_map_matrix: {self.game_map}') | ||||||
|  |  | ||||||
|  |   def populate_map(self): | ||||||
|  |     for player in self.players: | ||||||
|  |       p_coord_x, p_coord_y= player.get_coordinates() | ||||||
|  |       self.game_map[p_coord_y][p_coord_x]= player | ||||||
|  |       #if not player.is_alive(): self.game_map[p_coord_y][p_coord_x]= self.dead_player_sym | ||||||
|  |       #elif player.player_gender_is_male(): self.game_map[p_coord_y][p_coord_x]= self.player_male_sym | ||||||
|  |       #elif player.player_gender_is_female(): self.game_map[p_coord_y][p_coord_x]= self.player_female_sym | ||||||
|  |       #else: self.game_map[p_coord_y][p_coord_x]= self.player_nonbinary_sym | ||||||
|  |     for item in self.items: | ||||||
|  |       i_coord_x, i_coord_y= item.get_coordinates() | ||||||
|  |       self.game_map[p_coord_y][p_coord_x]= item | ||||||
|  |       #self.game_map[i_coord_y][i_coord_x]= self.item_sym | ||||||
|  |  | ||||||
|  |   def _put_resource_on_map(self, target): | ||||||
|  |       #x= _random.randint(1, self.world_width -2) # from 1 to width-2 because 1 cell is occupied by the mountain | ||||||
|  |       #y= _random.randint(1, self.world_height -2) | ||||||
|  |       x= _random.randint(0, self.world_width -1) | ||||||
|  |       y= _random.randint(0, self.world_height -1) | ||||||
|  |       resource= self.get_map_matrix()[y][x] | ||||||
|  |       while resource: | ||||||
|  |         #while self.get_map_matrix()[y][x] != self.field_sym: | ||||||
|  |         _logs.log_debug('_put_resource_on_map: collision, regenerate coordinates') | ||||||
|  |         x= _random.randint(0, self.world_width -1) | ||||||
|  |         y= _random.randint(0, self.world_height -1) | ||||||
|  |         resource= self.get_map_matrix()[y][x] | ||||||
|  |       _logs.log_debug(f'{target.get_name()} >>> ({x},{y})') | ||||||
|  |       target.set_coordinates(x, y) | ||||||
|  |       self.get_map_matrix()[y][x]= target | ||||||
|  |  | ||||||
|  |   def init_players_coordinates(self): | ||||||
|  |     for player in self.players: | ||||||
|  |       self._put_resource_on_map(player) | ||||||
|  |  | ||||||
|  |   def init_items_coordinates(self): | ||||||
|  |     for item in self.items: | ||||||
|  |       self._put_resource_on_map(item) | ||||||
|  |  | ||||||
|  |   def add_player_to_map(self, player): | ||||||
|  |     self.players.append(player) | ||||||
|  |     self._put_resource_on_map(player) | ||||||
|  |  | ||||||
|  |   def add_item_to_map(self, item): | ||||||
|  |     self.items.append(item) | ||||||
|  |     self._put_resource_on_map(item) | ||||||
|  |  | ||||||
|  |   def get_map_matrix(self): | ||||||
|  |     return self.game_map | ||||||
|  |    | ||||||
|  |   def get_player_available_directions(self, Player): | ||||||
|  |     coord_x, coord_y= Player.get_coordinates() | ||||||
|  |     avail_directions= [] | ||||||
|  |     #XXX for now move only on available cells, no over other players/items | ||||||
|  |     for shift in [-1, 1]: | ||||||
|  |       x= coord_x + shift | ||||||
|  |       if x < 0 or x > self.world_width -1: continue | ||||||
|  |       resource= self.get_map_matrix()[coord_y][x] | ||||||
|  |       direction= shift == -1 and 'sinistra' or 'destra' | ||||||
|  |       if not resource: avail_directions.append((shift, 0, direction)) | ||||||
|  |     for shift in [-1, 1]: | ||||||
|  |       y= coord_y + shift | ||||||
|  |       if y < 0 or y > self.world_height -1: continue | ||||||
|  |       resource= self.get_map_matrix()[y][coord_x] | ||||||
|  |       direction= shift == -1 and 'su' or 'giu\'' | ||||||
|  |       if not resource: avail_directions.append((0, shift, direction)) | ||||||
|  |     return avail_directions | ||||||
|  |  | ||||||
|  |   def check_near_players(self, Player): | ||||||
|  |     # TODO Implement me | ||||||
|  |     # 1. range weapons like arch can attack from distance | ||||||
|  |     # 2. knife, sword and punch can attack only on immediate near cell | ||||||
|  |  | ||||||
|  |     coord_x, coord_y= Player.get_coordinates() | ||||||
|  |     attackable_players= [] | ||||||
|  |     for shift in [-1, 1]: | ||||||
|  |       x= coord_x + shift | ||||||
|  |       if x < 0 or x >= self.world_width -1: continue | ||||||
|  |       resource= self.get_map_matrix()[coord_y][x] | ||||||
|  |       if resource and resource.is_player() and resource.is_alive(): attackable_players.append(resource) | ||||||
|  |     for shift in [-1, 1]: | ||||||
|  |       y= coord_y + shift | ||||||
|  |       if y < 0 or y >= self.world_height -1: continue | ||||||
|  |       resource= self.get_map_matrix()[y][coord_x] | ||||||
|  |       if resource and resource.is_player() and resource.is_alive(): attackable_players.append(resource) | ||||||
|  |  | ||||||
|  |     return attackable_players | ||||||
|  |  | ||||||
|  |   def check_near_items(self, Player): | ||||||
|  |     # TODO Implement me | ||||||
|  |     return [] | ||||||
|  |  | ||||||
|  |   def get_player_available_actions(self, Player): | ||||||
|  |     # TODO: define actions list | ||||||
|  |     coord_x, coord_y= Player.get_coordinates() | ||||||
|  |     avail_actions= {} | ||||||
|  |      | ||||||
|  |     attack= self.check_near_players(Player) | ||||||
|  |     if attack: | ||||||
|  |       _logs.log_debug(f'{Player.get_name()} can attack {[a.get_name() for a in attack]}') | ||||||
|  |       #avail_actions.append(1) #XXX replace with attack action (or maybe other actions on players) | ||||||
|  |       avail_actions[1]= attack #XXX replace with attack action (or maybe other actions on players) | ||||||
|  |     if self.get_player_available_directions(Player): | ||||||
|  |       avail_actions[2]= True #XXX replace with action move | ||||||
|  |     items= self.check_near_items(Player) | ||||||
|  |     if items: | ||||||
|  |       avail_actions[3]= items #XXX replace with get item action | ||||||
|  |  | ||||||
|  |     return avail_actions | ||||||
|  |  | ||||||
|  |   def get_renderized_map(self): | ||||||
|  |     res= '' | ||||||
|  |     self.populate_map() | ||||||
|  |     game_map= self.get_map_matrix() | ||||||
|  |     for y in game_map: | ||||||
|  |       for x in y: | ||||||
|  |         if not x: el= self.field_sym | ||||||
|  |         #XXX how to manage mountains? | ||||||
|  |         elif x.is_player(): | ||||||
|  |           if not x.is_alive(): el= self.dead_player_sym | ||||||
|  |           elif x.player_gender_is_male(): el= self.player_male_sym | ||||||
|  |           elif x.player_gender_is_female(): el= self.player_female_sym | ||||||
|  |           else: el= self.player_nonbinary_sym | ||||||
|  |         elif x.is_item(): | ||||||
|  |           el= self.item_sym | ||||||
|  |         res+= el | ||||||
|  |       res+= '\n' | ||||||
|  |     return res | ||||||
|  |  | ||||||
|  |   def get_image_map(self): | ||||||
|  |     self.populate_map() | ||||||
|  |     scale_x= 20 | ||||||
|  |     scale_y= 20 | ||||||
|  |     final_x= self.world_width * scale_x | ||||||
|  |     final_y= self.world_height * scale_y | ||||||
|  |     image = _Image.new('RGB', (final_x, final_y)) | ||||||
|  |     draw = _ImageDraw.Draw(image) | ||||||
|  |     outline= '#000000' | ||||||
|  |  | ||||||
|  |     for y in range(self.world_height): | ||||||
|  |       for x in range(self.world_width): | ||||||
|  |         resource= self.game_map[y][x] | ||||||
|  |         if not resource: | ||||||
|  |           pixel_color= _bot_syms.MAP_IMAGE_FIELD | ||||||
|  |         # XXX how to manage mountains? maybe another class? | ||||||
|  |         #elif resource == self.mountain_sym: | ||||||
|  |           #pixel_color= _bot_syms.MAP_IMAGE_MOUNTAIN | ||||||
|  |         elif resource.is_item(): | ||||||
|  |           pixel_color= _bot_syms.MAP_IMAGE_ITEM | ||||||
|  |         elif resource.is_player(): | ||||||
|  |           if not resource.is_alive(): | ||||||
|  |             pixel_color= _bot_syms.MAP_IMAGE_DEATH_PLAYER | ||||||
|  |           elif resource.player_gender_is_male(): | ||||||
|  |             pixel_color= _bot_syms.MAP_IMAGE_PLAYER_MALE | ||||||
|  |           elif resource.player_gender_is_female(): | ||||||
|  |             pixel_color= _bot_syms.MAP_IMAGE_PLAYER_FEMALE | ||||||
|  |           elif resource.player_gender_is_not_binary(): | ||||||
|  |             pixel_color= _bot_syms.MAP_IMAGE_PLAYER_NONBINARY | ||||||
|  |  | ||||||
|  |         scaled_x_coord= x * scale_x | ||||||
|  |         scaled_y_coord= y * scale_y | ||||||
|  |         # if x == 1 distance from top-right is 20 (because everything is 20x bigger) | ||||||
|  |         # then we want to draw a rectanghe 20x20 (instead of 1x1) | ||||||
|  |         # this mean that if x == 1 (20px from top-right), x+1 == 2 (40px from top-right) | ||||||
|  |         # the same for y | ||||||
|  |         # this means that i keep the same factor proportions but 20x bigger | ||||||
|  |         scaled_x_width= (x + 1) * scale_x | ||||||
|  |         scaled_y_height= (y + 1) * scale_y | ||||||
|  |  | ||||||
|  |         draw.rectangle([scaled_x_coord, scaled_y_coord, scaled_x_width, scaled_y_height], | ||||||
|  |           fill= pixel_color, outline= outline | ||||||
|  |         ) | ||||||
|  |      | ||||||
|  |     # debug | ||||||
|  |     #image.save('/tmp/battle_royale_map.png') | ||||||
|  |     #image.show() | ||||||
|  |  | ||||||
|  |     bio = _io.BytesIO() | ||||||
|  |     image.save(bio, 'PNG') | ||||||
|  |     bio.seek(0) | ||||||
|  |     return bio | ||||||
							
								
								
									
										27
									
								
								entities/items/item.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								entities/items/item.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | import random as _random | ||||||
|  | from entities import resource as _resource | ||||||
|  |  | ||||||
|  | class BrSimItem(_resource.BrSimResource): | ||||||
|  |  | ||||||
|  |   # test  | ||||||
|  |   def __init__(self): | ||||||
|  |     self.coord_x= 0 | ||||||
|  |     self.coord_y= 0 | ||||||
|  |  | ||||||
|  |   def is_item(self): | ||||||
|  |     return True | ||||||
|  |  | ||||||
|  |   def get_name(self): | ||||||
|  |     return self.name | ||||||
|  |  | ||||||
|  |   def get_item_type(self): | ||||||
|  |     return self.item_type | ||||||
|  |  | ||||||
|  |   def get_weight(self): | ||||||
|  |     return self.weight | ||||||
|  |  | ||||||
|  |   def is_weapon(self): | ||||||
|  |     return False | ||||||
|  |  | ||||||
|  |   def is_cure(self): | ||||||
|  |     return False | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| KNIFE= 1 | KNIFE= 1 | ||||||
| GUN= 2 | ARCH= 2 | ||||||
| BOMB= 3 |  | ||||||
| 
 | 
 | ||||||
| SHORT_RANGE= 1 | SHORT_RANGE= 1 | ||||||
| FAR_RANGE= 2 | FAR_RANGE= 2 | ||||||
| @@ -14,7 +13,7 @@ WEAPONS= { | |||||||
|       'ammons': -1, # -1, no limit |       'ammons': -1, # -1, no limit | ||||||
|       'range': SHORT_RANGE, |       'range': SHORT_RANGE, | ||||||
|       }, |       }, | ||||||
|     GUN: { |     ARCH: { | ||||||
|       'weight': 2, |       'weight': 2, | ||||||
|       'name': 'gun', |       'name': 'gun', | ||||||
|       'damage': 3, |       'damage': 3, | ||||||
| @@ -22,12 +21,4 @@ WEAPONS= { | |||||||
|       'ammons': 10, # -1, no limit |       'ammons': 10, # -1, no limit | ||||||
|       'range': FAR_RANGE, |       'range': FAR_RANGE, | ||||||
|       }, |       }, | ||||||
|     BOMB: { |  | ||||||
|       'weight': 2, |  | ||||||
|       'name': 'bomb', |  | ||||||
|       'damage': 10, |  | ||||||
|       'miss_chance': 5, # from 0 to 100, this is the probably to miss the hit |  | ||||||
|       'ammons': 1, |  | ||||||
|       'range': FAR_RANGE, |  | ||||||
|       }, |  | ||||||
|     } |     } | ||||||
| @@ -1,10 +1,11 @@ | |||||||
| import random as _random | import random as _random | ||||||
| from entities import weapon_syms as _syms | from entities.items import item as _item | ||||||
|  | from entities.items import weapon_syms as _syms | ||||||
| 
 | 
 | ||||||
| class BrSimWeapon(): | class BrSimWeapon(_item.BrSimItem): | ||||||
| 
 | 
 | ||||||
|   def __init__(self, wtype): |   def __init__(self, wtype= None): | ||||||
|     self.weapon= _syms.WEAPONS[wtype] |     self.weapon= _syms.WEAPONS[wtype or _random.randint(1,2)] | ||||||
|     self.name= self.weapon['name'] |     self.name= self.weapon['name'] | ||||||
|     self.damage= self.weapon['damage'] |     self.damage= self.weapon['damage'] | ||||||
|     self.weight= self.weapon['weight'] |     self.weight= self.weapon['weight'] | ||||||
| @@ -1,71 +1,208 @@ | |||||||
| import random as _random | import random as _random | ||||||
|  | import uuid as _uuid | ||||||
|  | from entities import resource as _resource | ||||||
|  | from utils import logs as _logs | ||||||
|  |  | ||||||
| class BrSimPlayer(): | class BrSimPlayer(_resource.BrSimResource): | ||||||
|  |  | ||||||
|   def __init__(self, name, inventory= None): |   def __init__(self, name, inventory= None): | ||||||
|  |     super() | ||||||
|  |     self.id= str(_uuid.uuid4()) | ||||||
|     self.name= name |     self.name= name | ||||||
|     self.health= 1 |     self.stats= '' | ||||||
|  |     self.health= _random.randint(1,1) | ||||||
|     self.inventory= inventory or [] |     self.inventory= inventory or [] | ||||||
|     self.damage= 1 # this is the punch damage amount |     self.damage= _random.randint(1,2) # this is the punch damage amount | ||||||
|     self.max_weight= 5 # this is the max inventory weight |     self.max_weight= 5 # this is the max inventory weight | ||||||
|     self.is_alive= True |     self.agility= _random.randint(1,3) # chance to avoid an hit | ||||||
|     self.agility= 10 # chance to avoid an hit |     self.kills= 0 # track the number of kills | ||||||
|  |     self.accused_damage= 0 | ||||||
|  |     self.survived_days= 0 # track the number of the survived days | ||||||
|  |     self.dodges= 0 | ||||||
|  |     self.equipped_weapon= None | ||||||
|  |     self.gender= _random.sample(['m', 'f', '-'], 1)[0] # for now get a random gender | ||||||
|  |     self.reputation= 50 #Like RDR2 the player can be evil(0) or good(100). This should influence the sponsors and internal alliance | ||||||
|  |  | ||||||
|   def is_alive(self): |   def is_player(self): | ||||||
|     return self.is_alive |     return True | ||||||
|  |  | ||||||
|   def attack(self, target): |   ### control methods | ||||||
|     if not self.is_alive(): return |  | ||||||
|     if not target.is_alive(): return |  | ||||||
|     if target.try_to_avoid_hit(): return # print something like 'enemy doges the attacl'     |  | ||||||
|  |  | ||||||
|     target.accuses_damage(self.damage) |   def get_name_and_stats(self): | ||||||
|  |     health= '♥️' * self.health or '☠️' | ||||||
|  |     strength= '⚔️' * self.damage | ||||||
|  |     agility= '🚀' * self.agility | ||||||
|  |     if self.player_gender_is_male(): gender= '♂' | ||||||
|  |     elif self.player_gender_is_female(): gender= '♀' | ||||||
|  |     else: gender= '⚩' | ||||||
|  |     name= f'{self.name} {gender} {health} {strength} {agility}' | ||||||
|  |     return name | ||||||
|  |  | ||||||
|   def accuses_damage(self, damage): |   def get_id(self): | ||||||
|     self.health -= damage |     return self.id | ||||||
|     if self.health <= 0: |  | ||||||
|         self.health = 0 |  | ||||||
|         self.is_alive = False |  | ||||||
|         # show something like 'player is dead' |  | ||||||
|     else: |  | ||||||
|         # show something like 'get hit' |  | ||||||
|         pass |  | ||||||
|  |  | ||||||
|   def try_to_avoid_hit(self): |  | ||||||
|     # maybe depend on the attack, if it is a gun shot it's quite impossible to dodge |  | ||||||
|     rnd= _random.randint(0, 100) |  | ||||||
|     # if rnd > self.agility: return True  ## XXX this is strange, if the agility is high the chances to dodge are lower |  | ||||||
|     if rnd < self.agility: return True |  | ||||||
|     return False |  | ||||||
|  |  | ||||||
|   def steal(self): |  | ||||||
|     #XXX can steal from death players or from sleeping players |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
|   def heal(self): |  | ||||||
|     #XXX if you have a wound and you have a medikit item, you can heal your wound or sickness |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
|   def get_inventory(self): |  | ||||||
|     return self.inventory |  | ||||||
|  |  | ||||||
|   def get_name(self): |   def get_name(self): | ||||||
|     return self.name |     return self.name | ||||||
|  |  | ||||||
|  |   def get_gender(self): | ||||||
|  |     return self.gender | ||||||
|  |  | ||||||
|  |   def player_gender_is_male(self): | ||||||
|  |     return self.gender == 'm' | ||||||
|  |  | ||||||
|  |   def player_gender_is_female(self): | ||||||
|  |     return self.gender == 'f' | ||||||
|  |  | ||||||
|  |   def player_gender_is_not_binary(self): | ||||||
|  |     return self.gender == '-' | ||||||
|  |  | ||||||
|  |   def get_inventory(self): | ||||||
|  |     return self.inventory | ||||||
|  |  | ||||||
|  |   def get_inventory_weight(self): | ||||||
|  |     weight= 0 | ||||||
|  |     for item in self.get_inventory(): | ||||||
|  |       weight+= item.get_weight() | ||||||
|  |     return weight | ||||||
|  |  | ||||||
|  |   def get_dodges(self): | ||||||
|  |     return self.dodges | ||||||
|  |  | ||||||
|  |   def get_max_weight(self): | ||||||
|  |     return self.max_weight | ||||||
|  |  | ||||||
|   def get_health(self): |   def get_health(self): | ||||||
|     return self.health |     return self.health | ||||||
|  |  | ||||||
|  |   def get_equipped_weapon(self): | ||||||
|  |     return self.equipped_weapon | ||||||
|  |  | ||||||
|   def get_damage(self): |   def get_damage(self): | ||||||
|     return self.damage |     weapon= self.get_equipped_weapon() | ||||||
|  |     if not weapon: return self.damage | ||||||
|  |     return weapon.get_damage() | ||||||
|  |  | ||||||
|   def get_agility(self): |   def get_agility(self): | ||||||
|     return self.agility |     return self.agility | ||||||
|    |    | ||||||
|  |   def get_reputation(self): | ||||||
|  |     return self.reputation | ||||||
|  |  | ||||||
|   def get_data(self): |   def get_data(self): | ||||||
|     return { |     return { | ||||||
|  |         'id': self.get_id(), | ||||||
|         'name': self.get_name(), |         'name': self.get_name(), | ||||||
|  |         'name_stats': self.get_name_and_stats(), | ||||||
|  |         'gender': self.get_gender(), | ||||||
|         'inventory': self.get_inventory(), |         'inventory': self.get_inventory(), | ||||||
|  |         'inventory_weight': self.get_inventory_weight(), | ||||||
|         'health': self.get_health(), |         'health': self.get_health(), | ||||||
|         'damage': self.get_damage(), |         'damage': self.get_damage(), | ||||||
|         'agility': self.get_agility(), |         'agility': self.get_agility(), | ||||||
|  |         'reputation': self.get_reputation(), | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |   def is_alive(self): | ||||||
|  |     return self.health > 0 | ||||||
|  |  | ||||||
|  |   def get_kills(self): | ||||||
|  |     return self.kills | ||||||
|  |  | ||||||
|  |   ### player actions | ||||||
|  |  | ||||||
|  |   def _equip_weapon(self): | ||||||
|  |     if not self.inventory: return | ||||||
|  |  | ||||||
|  |     available_weapons= [] | ||||||
|  |     for item in self.get_inventory(): | ||||||
|  |       # XXX | ||||||
|  |       # i don't know yet if this is ok, | ||||||
|  |       # we'll see it when weapon and items are defined | ||||||
|  |       # maybe we need item.is_weapon() method | ||||||
|  |       if not item.damage: continue | ||||||
|  |       available_weapons.append(item) | ||||||
|  |     self.equipped_weapon= random.sample(available_weapons, 1)[0] | ||||||
|  |  | ||||||
|  |   def dodge(self): | ||||||
|  |     # maybe depend on the attack, if it is a gun shot it's quite impossible to dodge | ||||||
|  |     rnd= _random.randint(0, 10) | ||||||
|  |     if rnd < self.agility: | ||||||
|  |       self.dodges+= 1 | ||||||
|  |       return True | ||||||
|  |     return False | ||||||
|  |  | ||||||
|  |   def accuses_damage(self, damage): | ||||||
|  |     self.health-= damage | ||||||
|  |     self.accused_damage+= damage | ||||||
|  |     if self.health > 0: return self.get_health() | ||||||
|  |  | ||||||
|  |     self.health= 0 | ||||||
|  |     if self.player_gender_is_male(): | ||||||
|  |       _logs.log_debug(f'[{self.get_name_and_stats()}]: Guarino, perdonami se sono morto x.x') | ||||||
|  |     elif self.player_gender_is_female(): | ||||||
|  |       _logs.log_debug(f'[{self.get_name_and_stats()}]: Guarino, perdonami se sono morta x.x') | ||||||
|  |     else: | ||||||
|  |       _logs.log_debug(f'[{self.get_name_and_stats()}]: Guarino, perdonami se sono mort* x.x') | ||||||
|  |     return damage | ||||||
|  |  | ||||||
|  |   def attack(self, target): | ||||||
|  |     self._equip_weapon() | ||||||
|  |     if target.dodge(): | ||||||
|  |       if target.player_gender_is_male(): | ||||||
|  |         msg= f'Ehhhh voleviiii!!! sei lentoo! {target.get_name_and_stats()} schiva il colpo di {self.get_name_and_stats()}' | ||||||
|  |       elif target.player_gender_is_female(): | ||||||
|  |         msg= f'Ehhhh voleviiii!!! sei lentaa! {target.get_name_and_stats()} schiva il colpo di {self.get_name_and_stats()}' | ||||||
|  |       else: | ||||||
|  |         msg= f'Ehhhh voleviiii!!! sei lent##! {target.get_name_and_stats()} schiva il colpo di {self.get_name_and_stats()}' | ||||||
|  |       return 0, msg | ||||||
|  |     target.accuses_damage(self.damage) | ||||||
|  |  | ||||||
|  |     msg= f'{self.get_name_and_stats()} Colpisce {target.get_name_and_stats()}' | ||||||
|  |     weapon= self.get_equipped_weapon() | ||||||
|  |     if weapon: msg+= f' con un {weapon.get_name}'  | ||||||
|  |     else: msg+= f' con un pugno' | ||||||
|  |     if not target.is_alive(): self.kills+= 1 | ||||||
|  |     return self.damage, msg | ||||||
|  |  | ||||||
|  |   def get_item(self, item): | ||||||
|  |     if self.get_inventory_weight() + item.get_weight() >= self.get_max_weight(): | ||||||
|  |       if self.player_gender_is_male(): | ||||||
|  |         _logs.log_debug(f'Sono sovraccarico, {self.get_name_and_stats} non puo\' prendere questo oggetto') | ||||||
|  |       elif self.player_gender_is_female(): | ||||||
|  |         _logs.log_debug(f'Sono sovraccarica, {self.get_name_and_stats} non puo\' prendere questo oggetto') | ||||||
|  |       else: | ||||||
|  |         _logs.log_debug(f'Sono sovraccaric#, {self.get_name_and_stats} non puo\' prendere questo oggetto') | ||||||
|  |       return False | ||||||
|  |     self.inventory.append(item) | ||||||
|  |  | ||||||
|  |   def move(self, delta_x, delta_y): | ||||||
|  |     # XXX maps limits: | ||||||
|  |     # probably this isn't player's business | ||||||
|  |     # game orchestror should manage it | ||||||
|  |     # to avoid that the player can go out from the map | ||||||
|  |     # or can reach unaccessible points | ||||||
|  |     # also because the player doens't know the Map (entities/gamemap.py) | ||||||
|  |     self.coord_x += delta_x | ||||||
|  |     self.coord_y += delta_y | ||||||
|  |  | ||||||
|  |   def move_right(self): | ||||||
|  |     self._move(1, 0) | ||||||
|  |  | ||||||
|  |   def move_left(self): | ||||||
|  |     self._move(-1, 0) | ||||||
|  |  | ||||||
|  |   def move_top(self): | ||||||
|  |     self._move(0, -1) | ||||||
|  |  | ||||||
|  |   def move_bottom(self): | ||||||
|  |     self._move(0, 1) | ||||||
|  |  | ||||||
|  |   def escape(self): | ||||||
|  |     # TODO It can run away from the fighting | ||||||
|  |     return | ||||||
|  |  | ||||||
|  |   def heal(self): | ||||||
|  |     # TODO heal system | ||||||
|  |     # if you have a wound and you have a medikit item, | ||||||
|  |     # you can heal your wound or sickness | ||||||
|  |     return | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								entities/resource.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								entities/resource.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | class BrSimResource(): | ||||||
|  |  | ||||||
|  |   def __init__(self): | ||||||
|  |     self.coord_x= 0 | ||||||
|  |     self.coord_y= 0 | ||||||
|  |  | ||||||
|  |   def is_player(self): | ||||||
|  |     return False | ||||||
|  |    | ||||||
|  |   def is_item(self): | ||||||
|  |     return False | ||||||
|  |  | ||||||
|  |   def get_coordinates(self): | ||||||
|  |     return self.coord_x, self.coord_y | ||||||
|  |  | ||||||
|  |   def set_coordinates(self, x, y): | ||||||
|  |     self.coord_x= x | ||||||
|  |     self.coord_y= y | ||||||
							
								
								
									
										84
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								main.py
									
									
									
									
									
								
							| @@ -1,82 +1,8 @@ | |||||||
| from entities import player as _player | from entities import arena as _arena | ||||||
| from entities import weapons as _weapons |  | ||||||
| from entities import weapon_syms as _wsyms |  | ||||||
|  |  | ||||||
| class BrSimArena(): | def init_arena(players= None, weapons= None): | ||||||
|  |   return _arena.BrSimArena(players, weapons) | ||||||
|  |  | ||||||
|   # players = [{'name': name, 'inventory': default_inventory, other_stats}] | def run_events(Arena): | ||||||
|   # weapons = [{WEAPON.KNIFE: quantity}, etc...] # this is the whole quantity of the items available on the world |   #A event for each player: | ||||||
|  |  | ||||||
|   def __init__(self, players, weapons): |  | ||||||
|     self.day= 1 |  | ||||||
|     self.players= [_player.BrSimPlayer(p['name'], p.get('inventory')) for p in players] |  | ||||||
|     self.weapons= [] |  | ||||||
|     for weapon in weapons: |  | ||||||
|       for wtype, quantity in weapon.items(): |  | ||||||
|         for i in range(quantity): self.weapons.append(_weapons.BrSimWeapon(wtype)) |  | ||||||
|  |  | ||||||
|   def next_day(self): |  | ||||||
|     self.day+= 1 |  | ||||||
|     print(f'Giorno: {self.day}') |  | ||||||
|     print(f'Giocatori vivi: {self.get_alive_players()}') |  | ||||||
|     death_players= self.get_death_players() |  | ||||||
|     if (death_players): |  | ||||||
|       print(f'Giocatori morti: {death_players}') |  | ||||||
|  |  | ||||||
|   def get_alive_players(self): |  | ||||||
|     res= [] |  | ||||||
|     for p in self.players: |  | ||||||
|       if not p.is_alive(): continue |  | ||||||
|       res.append(p) |  | ||||||
|     return res |  | ||||||
|  |  | ||||||
|   def get_death_players(self): |  | ||||||
|     res= [] |  | ||||||
|     for p in self.players: |  | ||||||
|       if p.is_alive(): continue |  | ||||||
|       res.append(p) |  | ||||||
|     return res |  | ||||||
|  |  | ||||||
|   def do_random_event(self): |  | ||||||
|     #XXX random player does random action according to his inventory health, wounds, available weapons on the world, etc... |  | ||||||
|   pass |   pass | ||||||
|  |  | ||||||
|   def supporter_donation(self): |  | ||||||
|     #XXX supporter donate a random item or weapon to a random player |  | ||||||
|     #TODO maybe in future a player can have charism stats that can influence the chance to get a donation |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
|   def add_player(self, name, inventory= None): |  | ||||||
|     player= _player.BrSimPlayer(name, inventory) |  | ||||||
|     self.players.append(player) |  | ||||||
|  |  | ||||||
|   def get_players(self): |  | ||||||
|     res= [] |  | ||||||
|     for p in self.players: |  | ||||||
|       res.append(p.get_data()) |  | ||||||
|     return res |  | ||||||
|  |  | ||||||
|   def get_weapons(self): |  | ||||||
|     res= [] |  | ||||||
|     for w in self.weapons: |  | ||||||
|       #XXX implement me |  | ||||||
|       res.append(w) |  | ||||||
|     return res |  | ||||||
|  |  | ||||||
| def init_arena(): |  | ||||||
|   players= [{'name': 'Crystal'}, {'name': 'Andrea'}] |  | ||||||
|   w= _wsyms.KNIFE |  | ||||||
|   #weapons= [{_wsyms.WEAPONS[w]['name' ]: 1}] |  | ||||||
|   weapons= [{w: 1}] |  | ||||||
|   return BrSimArena(players, weapons) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def run_event(Arena): |  | ||||||
|   pass |  | ||||||
|  |  | ||||||
| def local_debug(): |  | ||||||
|   Arena= init_arena() |  | ||||||
|   print(f'Players: {Arena.get_players()}') |  | ||||||
|   print(f'Weapons: {Arena.get_weapons()}') |  | ||||||
|   run_event(Arena) |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								requirements/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								requirements/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | python-telegram-bot[job-queue]==22.3 | ||||||
|  | pillow==11.3.0 | ||||||
							
								
								
									
										60
									
								
								utils/logs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								utils/logs.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | import os as _os | ||||||
|  | import logging as _logging | ||||||
|  | from datetime import datetime as _dt | ||||||
|  | from bot_libs import syms as _bot_syms | ||||||
|  |  | ||||||
|  | logger= _logging.getLogger(__name__) | ||||||
|  | file_handler= None | ||||||
|  |  | ||||||
|  | def get_log_name(): | ||||||
|  |   now= _dt.now() | ||||||
|  |   year= now.year | ||||||
|  |   month= now.month | ||||||
|  |   if month < 10: month= f'0{month}' | ||||||
|  |   day= now.day | ||||||
|  |   if day < 10: day= f'0{day}' | ||||||
|  |   fname= f'battle_royale-{now.year}{month}{day}.log' | ||||||
|  |  | ||||||
|  |   return _os.path.join(_bot_syms.LOG_PATH, fname) | ||||||
|  |  | ||||||
|  | def _create_file_handler(): | ||||||
|  |   global file_handler | ||||||
|  |   current_log_file = get_log_name() | ||||||
|  |   file_handler = _logging.FileHandler(current_log_file, encoding='utf-8') | ||||||
|  |   formatter = _logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') | ||||||
|  |   file_handler.setFormatter(formatter) | ||||||
|  |   logger.addHandler(file_handler) | ||||||
|  |   #logger.setLevel(_logging.INFO) | ||||||
|  |   logger.setLevel(_logging.DEBUG) | ||||||
|  |   return file_handler | ||||||
|  |  | ||||||
|  | def _setup_logging_file(): | ||||||
|  |   current_log_file = get_log_name() | ||||||
|  |    | ||||||
|  |   if not file_handler: return _create_file_handler() | ||||||
|  |   if file_handler.baseFilename == _os.path.abspath(current_log_file): return | ||||||
|  |  | ||||||
|  |   logger.removeHandler(file_handler) | ||||||
|  |   file_handler.close() | ||||||
|  |   _create_file_handler() | ||||||
|  |  | ||||||
|  | def log_debug(txt): | ||||||
|  |   print(txt) | ||||||
|  |   _setup_logging_file() | ||||||
|  |   logger.debug(txt) | ||||||
|  |  | ||||||
|  | def log_info(txt): | ||||||
|  |   _setup_logging_file() | ||||||
|  |   logger.info(txt) | ||||||
|  |  | ||||||
|  | def log_warning(txt): | ||||||
|  |   _setup_logging_file() | ||||||
|  |   logger.warning(txt) | ||||||
|  |  | ||||||
|  | def log_error(txt): | ||||||
|  |   _setup_logging_file() | ||||||
|  |   logger.error(txt) | ||||||
|  |  | ||||||
|  | def log_critical(txt): | ||||||
|  |   _setup_logging_file() | ||||||
|  |   logger.critical(txt) | ||||||
		Reference in New Issue
	
	Block a user