├── BooView.bat ├── assets ├── egg.png ├── arrow.png ├── cheep.png ├── dk_jr.png ├── empty.png ├── icon.png ├── koopa.png ├── luigi.png ├── mario.png ├── mole.png ├── peach.png ├── pole.png ├── toad.png ├── yoshi.png ├── banana.png ├── bowser.png ├── fireball.png ├── g_shell.png ├── killer.png ├── p_mush.png ├── pipe_g.png ├── pipe_o.png ├── plant_A.png ├── plant_B.png ├── r_shell.png ├── thwomp.png ├── balloon_b.png ├── balloon_r.png ├── thwomp_2_A.png ├── thwomp_2_B.png ├── toad_arrow.png ├── bowser_arrow.png ├── dk_jr_arrow.png ├── freesansbold.ttf ├── g_shell old.png ├── koopa_arrow.png ├── luigi_arrow.png ├── mario_arrow.png ├── peach_arrow.png ├── r_shell old.png ├── yoshi_arrow.png └── button icons │ └── replay.tif ├── config.json ├── SETUP.bat ├── KartScreen.py ├── socket.lua ├── README.md ├── BVmain.py ├── Assets.py ├── TrackHelper.py ├── Objects.py ├── LuaSide.lua └── BooView.py /BooView.bat: -------------------------------------------------------------------------------- 1 | @echo OFF 2 | py BooView.py 3 | pause 4 | @echo ON -------------------------------------------------------------------------------- /assets/egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/egg.png -------------------------------------------------------------------------------- /assets/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/arrow.png -------------------------------------------------------------------------------- /assets/cheep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/cheep.png -------------------------------------------------------------------------------- /assets/dk_jr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/dk_jr.png -------------------------------------------------------------------------------- /assets/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/empty.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/koopa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/koopa.png -------------------------------------------------------------------------------- /assets/luigi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/luigi.png -------------------------------------------------------------------------------- /assets/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/mario.png -------------------------------------------------------------------------------- /assets/mole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/mole.png -------------------------------------------------------------------------------- /assets/peach.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/peach.png -------------------------------------------------------------------------------- /assets/pole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/pole.png -------------------------------------------------------------------------------- /assets/toad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/toad.png -------------------------------------------------------------------------------- /assets/yoshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/yoshi.png -------------------------------------------------------------------------------- /assets/banana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/banana.png -------------------------------------------------------------------------------- /assets/bowser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/bowser.png -------------------------------------------------------------------------------- /assets/fireball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/fireball.png -------------------------------------------------------------------------------- /assets/g_shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/g_shell.png -------------------------------------------------------------------------------- /assets/killer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/killer.png -------------------------------------------------------------------------------- /assets/p_mush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/p_mush.png -------------------------------------------------------------------------------- /assets/pipe_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/pipe_g.png -------------------------------------------------------------------------------- /assets/pipe_o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/pipe_o.png -------------------------------------------------------------------------------- /assets/plant_A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/plant_A.png -------------------------------------------------------------------------------- /assets/plant_B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/plant_B.png -------------------------------------------------------------------------------- /assets/r_shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/r_shell.png -------------------------------------------------------------------------------- /assets/thwomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/thwomp.png -------------------------------------------------------------------------------- /assets/balloon_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/balloon_b.png -------------------------------------------------------------------------------- /assets/balloon_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/balloon_r.png -------------------------------------------------------------------------------- /assets/thwomp_2_A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/thwomp_2_A.png -------------------------------------------------------------------------------- /assets/thwomp_2_B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/thwomp_2_B.png -------------------------------------------------------------------------------- /assets/toad_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/toad_arrow.png -------------------------------------------------------------------------------- /assets/bowser_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/bowser_arrow.png -------------------------------------------------------------------------------- /assets/dk_jr_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/dk_jr_arrow.png -------------------------------------------------------------------------------- /assets/freesansbold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/freesansbold.ttf -------------------------------------------------------------------------------- /assets/g_shell old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/g_shell old.png -------------------------------------------------------------------------------- /assets/koopa_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/koopa_arrow.png -------------------------------------------------------------------------------- /assets/luigi_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/luigi_arrow.png -------------------------------------------------------------------------------- /assets/mario_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/mario_arrow.png -------------------------------------------------------------------------------- /assets/peach_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/peach_arrow.png -------------------------------------------------------------------------------- /assets/r_shell old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/r_shell old.png -------------------------------------------------------------------------------- /assets/yoshi_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/yoshi_arrow.png -------------------------------------------------------------------------------- /assets/button icons/replay.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrL314/BooView/HEAD/assets/button icons/replay.tif -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "window_size": 512, 3 | "FRAME_SKIP": 0, 4 | "max_trail_length": 6000, 5 | "trail_log_rate": 2, 6 | "flowmap_quality": 1 7 | } -------------------------------------------------------------------------------- /SETUP.bat: -------------------------------------------------------------------------------- 1 | @echo OFF 2 | pip install pygame==1.9.6 --force-reinstall 3 | pip install pillow==9.1.0 --force-reinstall 4 | pip install PyOpenGL==3.1.6 --force-reinstall 5 | pip install PyOpenGL_accelerate==3.1.6 --force-reinstall 6 | pip install numpy==1.22.3 --force-reinstall 7 | 8 | pause 9 | @echo ON 10 | -------------------------------------------------------------------------------- /KartScreen.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.gfxdraw import * 3 | from pygame.locals import * 4 | 5 | 6 | 7 | class Screen(pygame.Surface): 8 | def __init__(self, size, x=0, y=0): 9 | super().__init__(size) 10 | self._x = x 11 | self._y = y 12 | self._DEFAULT_X = x 13 | self._DEFAULT_Y = y 14 | self._w = size[0] 15 | self._h = size[1] 16 | self._DEFAULT_WIDTH = size[0] 17 | self._DEFAULT_HEIGHT = size[1] 18 | self._canvas = pygame.Surface(size, SRCALPHA) 19 | self._canvas.fill((0, 0, 0)) 20 | self._screen = pygame.Surface(size, SRCALPHA) 21 | 22 | @property 23 | def x(self): 24 | return self._x 25 | @x.setter 26 | def x(self, _x): 27 | self._x = _x 28 | self.check_bound() 29 | 30 | @property 31 | def y(self): 32 | return self._y 33 | @y.setter 34 | def y(self, _y): 35 | self._y = _y 36 | self.check_bound() 37 | 38 | @property 39 | def w(self): 40 | return self._w 41 | @w.setter 42 | def w(self, _w): 43 | self._w = _w 44 | 45 | @property 46 | def h(self): 47 | return self._h 48 | @h.setter 49 | def h(self, _h): 50 | self._h = _h 51 | 52 | @property 53 | def DEFAULT_X(self): 54 | return self._DEFAULT_X 55 | @property 56 | def DEFAULT_Y(self): 57 | return self._DEFAULT_Y 58 | @property 59 | def DEFAULT_WIDTH(self): 60 | return self._DEFAULT_WIDTH 61 | @property 62 | def DEFAULT_HEIGHT(self): 63 | return self._DEFAULT_HEIGHT 64 | 65 | 66 | @property 67 | def SCALE(self): 68 | #return (self.w / self.DEFAULT_WIDTH) / 2 69 | return (self.w / self.DEFAULT_WIDTH) 70 | 71 | @property 72 | def INV_SCALE(self): 73 | #return (self.DEFAULT_WIDTH / self.w) * 2 74 | return (self.DEFAULT_WIDTH / self.w) 75 | 76 | 77 | 78 | 79 | @property 80 | def canvas(self): 81 | 82 | 83 | cv = pygame.Surface(( ((self.DEFAULT_WIDTH*self.INV_SCALE/2)//1 + 1) * 1, ((self.DEFAULT_HEIGHT*self.INV_SCALE/2)//1 + 1) * 1), SRCALPHA) 84 | 85 | #cv.blit(self._screen, (self.x*self.INV_SCALE, self.y*self.INV_SCALE), area=cv.get_rect().move(self.x * self.INV_SCALE * -1, self.y * self.INV_SCALE * -1)) 86 | #cv.blit(self._screen, (0, 0), area=cv.get_rect().move(self.x * self.INV_SCALE * -1, self.y * self.INV_SCALE * -1)) 87 | 88 | cv.blit(self._screen, (self.x*self.INV_SCALE/2, self.y*self.INV_SCALE/2)) 89 | 90 | #cv.blit(self._screen, (self.x*self.INV_SCALE, self.y*self.INV_SCALE), area=cv.get_rect()) 91 | 92 | cv = pygame.transform.smoothscale(cv, (self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)) 93 | 94 | return cv 95 | 96 | 97 | 98 | @property 99 | def center_x(self): 100 | return self.INV_SCALE*(-self.x + self.DEFAULT_WIDTH/2) 101 | @center_x.setter 102 | def center_x(self, _x): 103 | self.x = -(_x*self.SCALE) + (self.DEFAULT_WIDTH/2) 104 | 105 | 106 | @property 107 | def center_y(self): 108 | return self.INV_SCALE*(-self.y + self.DEFAULT_HEIGHT/2) 109 | @center_y.setter 110 | def center_y(self, _y): 111 | self.y = -(_y*self.SCALE) + (self.DEFAULT_HEIGHT/2) 112 | 113 | 114 | 115 | 116 | def check_bound(self): 117 | if self._w < self._DEFAULT_WIDTH: 118 | self._w = self._DEFAULT_WIDTH 119 | self._h = self._DEFAULT_HEIGHT 120 | 121 | if self._x > self._DEFAULT_X: 122 | self._x = self._DEFAULT_X 123 | if self._y > self._DEFAULT_Y: 124 | self._y = self._DEFAULT_Y 125 | if self._x + self._w < self._DEFAULT_X + self._DEFAULT_WIDTH: 126 | self._x = self._DEFAULT_X + self._DEFAULT_WIDTH - self._w 127 | if self._y + self._h < self._DEFAULT_Y + self._DEFAULT_HEIGHT: 128 | self._y = self._DEFAULT_Y + self._DEFAULT_HEIGHT - self._h 129 | 130 | 131 | 132 | def zoom(self, pos, zoom_amt): 133 | 134 | z_r = self.SCALE 135 | 136 | #zoom_ratio = z_r + zoom_amt 137 | zoom_ratio = zoom_amt 138 | 139 | 140 | 141 | if zoom_ratio < 1: zoom_ratio = 1 142 | if zoom_ratio > 8: zoom_ratio = 8 143 | 144 | self.w = self.DEFAULT_WIDTH * zoom_ratio 145 | self.h = self.DEFAULT_HEIGHT * zoom_ratio 146 | x,y = pos 147 | 148 | 149 | self.x = x - (zoom_ratio/z_r)*(x - self.x) 150 | self.y = y - (zoom_ratio/z_r)*(y - self.y) 151 | 152 | 153 | 154 | 155 | 156 | 157 | def blit(self, surface, position, area=None, special_flags=0): 158 | self._screen.blit(surface, position, area=area, special_flags=special_flags) 159 | -------------------------------------------------------------------------------- /socket.lua: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------------------------------- 2 | -- LuaSocket helper module 3 | -- Author: Diego Nehab 4 | -- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ 5 | ----------------------------------------------------------------------------- 6 | 7 | ----------------------------------------------------------------------------- 8 | -- Declare module and import dependencies 9 | ----------------------------------------------------------------------------- 10 | local base = _G 11 | local string = require("string") 12 | local math = require("math") 13 | local socket = require("socket.core") 14 | module("socket") 15 | 16 | ----------------------------------------------------------------------------- 17 | -- Exported auxiliar functions 18 | ----------------------------------------------------------------------------- 19 | function connect(address, port, laddress, lport) 20 | local sock, err = socket.tcp() 21 | if not sock then return nil, err end 22 | if laddress then 23 | local res, err = sock:bind(laddress, lport, -1) 24 | if not res then return nil, err end 25 | end 26 | local res, err = sock:connect(address, port) 27 | if not res then return nil, err end 28 | return sock 29 | end 30 | 31 | function bind(host, port, backlog) 32 | local sock, err = socket.tcp() 33 | if not sock then return nil, err end 34 | sock:setoption("reuseaddr", true) 35 | local res, err = sock:bind(host, port) 36 | if not res then return nil, err end 37 | res, err = sock:listen(backlog) 38 | if not res then return nil, err end 39 | return sock 40 | end 41 | 42 | try = newtry() 43 | 44 | function choose(table) 45 | return function(name, opt1, opt2) 46 | if base.type(name) ~= "string" then 47 | name, opt1, opt2 = "default", name, opt1 48 | end 49 | local f = table[name or "nil"] 50 | if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) 51 | else return f(opt1, opt2) end 52 | end 53 | end 54 | 55 | ----------------------------------------------------------------------------- 56 | -- Socket sources and sinks, conforming to LTN12 57 | ----------------------------------------------------------------------------- 58 | -- create namespaces inside LuaSocket namespace 59 | sourcet = {} 60 | sinkt = {} 61 | 62 | BLOCKSIZE = 2048 63 | 64 | sinkt["close-when-done"] = function(sock) 65 | return base.setmetatable({ 66 | getfd = function() return sock:getfd() end, 67 | dirty = function() return sock:dirty() end 68 | }, { 69 | __call = function(self, chunk, err) 70 | if not chunk then 71 | sock:close() 72 | return 1 73 | else return sock:send(chunk) end 74 | end 75 | }) 76 | end 77 | 78 | sinkt["keep-open"] = function(sock) 79 | return base.setmetatable({ 80 | getfd = function() return sock:getfd() end, 81 | dirty = function() return sock:dirty() end 82 | }, { 83 | __call = function(self, chunk, err) 84 | if chunk then return sock:send(chunk) 85 | else return 1 end 86 | end 87 | }) 88 | end 89 | 90 | sinkt["default"] = sinkt["keep-open"] 91 | 92 | sink = choose(sinkt) 93 | 94 | sourcet["by-length"] = function(sock, length) 95 | return base.setmetatable({ 96 | getfd = function() return sock:getfd() end, 97 | dirty = function() return sock:dirty() end 98 | }, { 99 | __call = function() 100 | if length <= 0 then return nil end 101 | local size = math.min(socket.BLOCKSIZE, length) 102 | local chunk, err = sock:receive(size) 103 | if err then return nil, err end 104 | length = length - string.len(chunk) 105 | return chunk 106 | end 107 | }) 108 | end 109 | 110 | sourcet["until-closed"] = function(sock) 111 | local done 112 | return base.setmetatable({ 113 | getfd = function() return sock:getfd() end, 114 | dirty = function() return sock:dirty() end 115 | }, { 116 | __call = function() 117 | if done then return nil end 118 | local chunk, err, partial = sock:receive(socket.BLOCKSIZE) 119 | if not err then return chunk 120 | elseif err == "closed" then 121 | sock:close() 122 | done = 1 123 | return partial 124 | else return nil, err end 125 | end 126 | }) 127 | end 128 | 129 | 130 | sourcet["default"] = sourcet["until-closed"] 131 | 132 | source = choose(sourcet) 133 | 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![BooView Logo](https://github.com/MrL314/BooView/blob/main/assets/icon.png) 3 | 4 | # BooView (v2.1) by MrL314 5 | **Data Visualizer for Super Mario Kart (SNES) by MrL314** 6 | 7 | Please read through this README before starting! 8 | 9 | 10 | ### Contact Me! 11 | - **Twitter:** [@LF_MrL314](https://twitter.com/LF_MrL314) 12 | - **Email:** LFmisterL314@gmail.com 13 | - **YouTube:** [MrL314](https://youtube.com/user/misterL314) 14 | - **Patreon:** [MrL314](https://www.patreon.com/MrL314) 15 | 16 | ### [[Quick Download Here!]](https://github.com/MrL314/BooView/archive/main.zip) 17 | 18 | 19 | ## Current Features 20 | - View **Dynamically rendered** Flow Maps and Checkpoints in a top-down view! 21 | - Now dynamically renders track data as well 22 | - Scroll to zoom in on track 23 | - Drag left-mouse to move screen 24 | - View previous positions of racers via Trail mode, showing the racer's recent path 25 | - Interact with objects and change their position in-game from the tool window 26 | - Right-click an object to grab an object and move it around 27 | - Follow along with racers by clicking their icon in the right panel 28 | - Scale sprites in the window for ease of viewing 29 | - Top-down view sprites, mixed from custom and existing assets 30 | - SRM file uploading to load your favorite save data into the game with ease 31 | - Replay time trial ghosts as if it were running real-time! 32 | - Combined with Trails mode, you can see the racing line of your best records! 33 | - OpenGL rendering back-end to utilize GPU for hardware rendering! 34 | - **Improved performance** to now handle >100% emulation speed! 35 | - View racer vector data such as facing-direction, momentum, camera direction, etc. 36 | - View racer target locations on the map 37 | - View players' viewable area as well as camera object tracking the players! 38 | - Increase quality of flow map for crispier displays! 39 | - Export map data to rendered images! (not fully implemented yet!) 40 | 41 | 42 | ## Requirements: 43 | 44 | 1. Bizhawk **2.3** (http://tasvideos.org/BizHawk.html) 45 | - Run the prereq installer before installing if on Windows 46 | - **MAKE SURE it's version 2.3!** Other versions may not work as intended! 47 | 2. Python **3.8 or higher**. (https://www.python.org/downloads/) 48 | - When installing, make sure "Add to PATH" is checked 49 | - **DO NOT** use the Windows Store downloaded version! If you have this version installed, re-install from the link above. 50 | - If using Windows, make sure to turn off app execution aliases for Python (see **Installation**) 51 | 3. [Recommended] 64-Bit Windows OS (not required, but I cannot test unix systems.) 52 | 53 | 54 | 55 | ## Installation 56 | 57 | 1. Download BizHawk **2.3** 58 | - (http://tasvideos.org/BizHawk.html) 59 | 2. (If on Windows) Download and run the Bizhawk Prereq installer 60 | 3. Install BizHawk 61 | 4. Download Python **3.8 or higher** from the [Python official download page](https://www.python.org/downloads/) **(NOT the Windows Store!!)** 62 | 5. When installing Python, make sure "Add to PATH" is checked 63 | 6. If using Windows, turn off app execution aliases for Python in `Settings > Apps > Apps & Features > App execution aliases` 64 | - (https://www.windowscentral.com/how-manage-app-execution-aliases-windows-10) 65 | 7. Download **BooView** and unzip 66 | 8. Run `SETUP.bat`. 67 | - If you already have other versions of these packages installed, `SETUP.bat` will overwrite them. Unfortunately SDL2 has major compatibility issues, so I cannot guarantee stability when versions other than these are used 68 | - **pygame** (will become v1.9.6) 69 | - **pillow** (will become v9.1.0) 70 | - **PyOpenGL** (will become v3.1.6) 71 | - **PyOpenGL_accelerate** (will become v3.1.6) 72 | - **numpy** (will become v1.22.3) 73 | 74 | 75 | 76 | ## How to run 77 | 78 | 1. Open BizHawk (**EmuHawk.exe**) and load Super Mario Kart 79 | - Version should not matter 80 | 2. In BizHawk, go to **Config > Customize**. Then at the bottom of that window, ensure that **Lua+LuaInterface** is selected. Then restart BizHawk. 81 | 3. In BizHawk, go to **Tools > Lua Console** 82 | 4. In the Lua Console, go to **Script > Open Script** 83 | 5. Navigate to the downloaded folder, and click on **LuaSide.lua** 84 | - This should freeze the emulator for a few seconds, then resume after. 85 | - This will load the script into the console. 86 | 6. Run **BooView.bat** 87 | - Wait a few seconds until the welcome message appears 88 | - If running from raw source, just run `py BooView.py` 89 | 7. In the Lua Console, double click on the red square next to **LuaSide** 90 | 91 | If the script is already loaded in the console, all you need to do is step 6 and 7. 92 | 93 | 94 | 95 | ## Config Options 96 | 97 | If you are having framerate issues, considering editting the options in the **config.json** file. 98 | - `window_size` will determine the size of the render window 99 | - Default is 512 (512x512 render window) 100 | - `FRAME_SKIP` will determine the amount of frames to wait between renders and polling the Lua script 101 | - (experimental!) 102 | - `max_trail_length` is the buffer size for the trails when trails are enabled. 103 | - Consider lowering this if you are getting major lag 104 | - `trail_log_rate` is how long between each position in the trail is polled 105 | - 1 = every frame, 2 = every 2 frames, etc 106 | - For maximum accuracy, this should be set to 1 107 | - `flowmap_quality` is the quality level of the arrows in the flow map. 108 | - Set to 1 by default (1024x1024 flowmap image size) 109 | 110 | 111 | 112 | 113 | ## Special Notes 114 | ### Crashes and Errors 115 | There seems to be a few random sparse errors from time to time. If you encounter an error, first restart the tool. If the error is persistent, feel free to contact me at `LFmisterL314@gmail.com`, and I will try to fix it as soon as I can! 116 | 117 | Sometimes the program will fail to run when starting. If this happens, just exit the program and restart it. 118 | 119 | 120 | ### SMKWorkshop Discord: (https://discord.gg/QNcKNQC) 121 | If you are interested in updates to this project, or Super Mario Kart in general, come join the 122 | Super Mario Kart Workshop Discord! 123 | 124 | 125 | ### MrL's Patreon: (https://www.patreon.com/MrL314) 126 | This program is provided completely free of charge, at no cost to the user. However, if you would 127 | like to donate in order to support me in my efforts in making more for the community as a whole, 128 | join via the link above. 10% of all proceeds earned will go towards the Autistic Self Advocacy 129 | Network, a charity devoted to the betterment of autistic and disabled people. 130 | 131 | 132 | 133 | 134 | ### Special Thanks to 135 | - ScouB 136 | - Dirtbag 137 | - SmorBjorn 138 | - The SMK Workshop Community 139 | - and YOU! 140 | 141 | 142 | ## GNU License 143 | BooView is free software: you can redistribute it and/or modify 144 | it under the terms of the GNU General Public License as published by 145 | the Free Software Foundation, version 3 of the License. 146 | 147 | This program is distributed in the hope that it will be useful, 148 | but WITHOUT ANY WARRANTY; without even the implied warranty of 149 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 150 | GNU General Public License for more details. 151 | 152 | You should have received a copy of the GNU General Public License 153 | along with this program. If not, see . 154 | -------------------------------------------------------------------------------- /BVmain.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import tkinter as tk 5 | from tkinter import * 6 | from tkinter import filedialog 7 | 8 | 9 | import threading 10 | 11 | 12 | import os, sys, platform 13 | 14 | import Assets 15 | 16 | import time 17 | 18 | 19 | 20 | PRESSED_RELIEF = SUNKEN 21 | 22 | 23 | class BV_Instance(): 24 | 25 | def __init__(self, WINDOW_SIZE=(0, 0), PYG_SIZE=(0, 0)): 26 | 27 | self.root = tk.Tk() 28 | 29 | self.WINDOW_WIDTH, self.WINDOW_HEIGHT = WINDOW_SIZE 30 | 31 | self.embed = tk.Frame(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) 32 | self.embed.grid(columnspan = (PYG_SIZE[0]), rowspan = PYG_SIZE[1]) # check and fix? 33 | self.embed.pack(side=LEFT) 34 | 35 | 36 | 37 | 38 | # Set up all "global" variables 39 | self.setup_variables() 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | def setup_window(self): 50 | 51 | 52 | self.root.title("BooView") 53 | self.root.iconphoto(False, PhotoImage(file='assets/icon.png')) 54 | self.root.resizable(False, False) 55 | self.root.lift() 56 | self.root.attributes("-topmost", True) 57 | self.root.attributes("-topmost", False) 58 | 59 | 60 | # frame management 61 | 62 | SIDE_FRAME_WIDTH = 100 63 | button_width = 50 64 | button_height = 50 65 | 66 | Assets.create_tk_images() 67 | 68 | 69 | 70 | SIDE_FRAME = tk.Frame(self.root, width=SIDE_FRAME_WIDTH, height=self.WINDOW_HEIGHT) 71 | 72 | 73 | 74 | self.REPLAY_BUTTON = tk.Button(SIDE_FRAME, text="Replay Ghost", command=self.toggle_REPLAY_MODE) 75 | 76 | self.REPLAY_BUTTON.config(state="disabled") 77 | 78 | 79 | 80 | 81 | CBOX_FRAME = tk.Frame(SIDE_FRAME, width=SIDE_FRAME_WIDTH) 82 | 83 | TRAIL_CBOX_VAR = tk.BooleanVar() 84 | self.TRAIL_CBOX = tk.Checkbutton(CBOX_FRAME, text="Show Trails", variable=TRAIL_CBOX_VAR, onvalue=True, offvalue=False, command=self.set_trail_var) 85 | 86 | ZONE_CBOX_VAR = tk.BooleanVar() 87 | self.ZONE_CBOX = tk.Checkbutton(CBOX_FRAME, text="Show Zones", variable=ZONE_CBOX_VAR, onvalue=True, offvalue=False, command=self.set_zone_var) 88 | 89 | FLOW_CBOX_VAR = tk.BooleanVar() 90 | self.FLOW_CBOX = tk.Checkbutton(CBOX_FRAME, text="Show Flowmap", variable=FLOW_CBOX_VAR, onvalue=True, offvalue=False, command=self.set_flow_var) 91 | 92 | CAM_CBOX_VAR = tk.BooleanVar() 93 | self.CAM_CBOX = tk.Checkbutton(CBOX_FRAME, text="Show Cameras", variable=CAM_CBOX_VAR, onvalue=True, offvalue=False, command=self.set_cam_var) 94 | 95 | VEC_CBOX_VAR = tk.BooleanVar() 96 | self.VEC_CBOX = tk.Checkbutton(CBOX_FRAME, text="Show Vectors", variable=VEC_CBOX_VAR, onvalue=True, offvalue=False, command=self.set_vec_var) 97 | 98 | TGT_CBOX_VAR = tk.BooleanVar() 99 | self.TGT_CBOX = tk.Checkbutton(CBOX_FRAME, text="Show Targets", variable=TGT_CBOX_VAR, onvalue=True, offvalue=False, command=self.set_tgt_var) 100 | 101 | 102 | 103 | 104 | 105 | FOLLOW_FRAME = tk.LabelFrame(SIDE_FRAME, relief=GROOVE, text="FOLLOW RACER") 106 | 107 | FOLLOW_1_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_1, width=button_width, height=button_height, relief=FLAT) 108 | FOLLOW_2_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_2, width=button_width, height=button_height, relief=FLAT) 109 | FOLLOW_3_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_3, width=button_width, height=button_height, relief=FLAT) 110 | FOLLOW_4_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_4, width=button_width, height=button_height, relief=FLAT) 111 | FOLLOW_5_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_5, width=button_width, height=button_height, relief=FLAT) 112 | FOLLOW_6_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_6, width=button_width, height=button_height, relief=FLAT) 113 | FOLLOW_7_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_7, width=button_width, height=button_height, relief=FLAT) 114 | FOLLOW_8_BUTTON = tk.Button(FOLLOW_FRAME, image=Assets.EMPTY_TK, command=self.toggle_follow_8, width=button_width, height=button_height, relief=FLAT) 115 | 116 | self.FOLLOW_BUTTONS = [FOLLOW_1_BUTTON, FOLLOW_2_BUTTON, FOLLOW_3_BUTTON, FOLLOW_4_BUTTON, FOLLOW_5_BUTTON, FOLLOW_6_BUTTON, FOLLOW_7_BUTTON, FOLLOW_8_BUTTON] 117 | 118 | 119 | SPRITE_SIZE_FRAME = tk.LabelFrame(SIDE_FRAME, relief=GROOVE, text="SPRITE SCALE") 120 | 121 | SPRITE_SCALE_VAR = StringVar() 122 | 123 | self.SPRITE_SIZE_WIDGET = tk.Spinbox(SPRITE_SIZE_FRAME, increment=1, from_=1, to=4, state='readonly', width=4 , justify=RIGHT, textvariable=SPRITE_SCALE_VAR) 124 | self.SPRITE_SIZE_WIDGET.pack(side=TOP, fill=X) 125 | 126 | 127 | self.SRAM_UPLOAD_BUTTON = tk.Button(SIDE_FRAME, text="Upload SRAM Data", command=self.set_in_file_dialogue) 128 | 129 | 130 | 131 | 132 | self.TK_VARS = { 133 | "CAM_CBOX": CAM_CBOX_VAR, 134 | "VEC_CBOX": VEC_CBOX_VAR, 135 | "SPRITE_SCALE": SPRITE_SCALE_VAR, 136 | "TRAIL_CBOX": TRAIL_CBOX_VAR, 137 | "ZONE_CBOX": ZONE_CBOX_VAR, 138 | "FLOW_CBOX": FLOW_CBOX_VAR, 139 | "TGT_CBOX": TGT_CBOX_VAR, 140 | 141 | } 142 | 143 | 144 | 145 | self.set_tkvar("SPRITE_SCALE", "2") 146 | 147 | 148 | 149 | SIDE_TABLE = [ 150 | [(self.REPLAY_BUTTON, 2)], 151 | [(CBOX_FRAME, 2)], 152 | [(FOLLOW_FRAME, 2)], 153 | [(SPRITE_SIZE_FRAME, 2)], 154 | [(self.SRAM_UPLOAD_BUTTON, 2)] 155 | ] 156 | 157 | 158 | CBOX_TABLE = [ 159 | [self.TRAIL_CBOX], 160 | [self.ZONE_CBOX], 161 | [self.FLOW_CBOX], 162 | [self.CAM_CBOX], 163 | [self.VEC_CBOX], 164 | [self.TGT_CBOX], 165 | ] 166 | 167 | 168 | FOLLOW_TABLE = [ 169 | [self.FOLLOW_BUTTONS[0], self.FOLLOW_BUTTONS[1]], 170 | [self.FOLLOW_BUTTONS[2], self.FOLLOW_BUTTONS[3]], 171 | [self.FOLLOW_BUTTONS[4], self.FOLLOW_BUTTONS[5]], 172 | [self.FOLLOW_BUTTONS[6], self.FOLLOW_BUTTONS[7]], 173 | ] 174 | 175 | 176 | self.pack_group(FOLLOW_TABLE) 177 | 178 | self.pack_group(CBOX_TABLE, sticky="NW") 179 | 180 | 181 | self.pack_group(SIDE_TABLE) 182 | 183 | 184 | SIDE_FRAME.pack(side=LEFT) 185 | self.root.update() 186 | 187 | 188 | def pack_group(self, g, sticky="NEWS"): 189 | row = 0 190 | for el_row in g: 191 | col = 0 192 | for b in el_row: 193 | if type(b) == type(tuple()): 194 | b[0].grid(column = col, row = row, columnspan=b[1], sticky=sticky) 195 | else: 196 | b.grid(column = col, row = row, sticky=sticky) 197 | 198 | col += 1 199 | 200 | row += 1 201 | 202 | 203 | 204 | 205 | 206 | def setup_variables(self): 207 | self.racers_to_follow = { 208 | "racer0": False, 209 | "racer1": False, 210 | "racer2": False, 211 | "racer3": False, 212 | "racer4": False, 213 | "racer5": False, 214 | "racer6": False, 215 | "racer7": False 216 | } 217 | 218 | 219 | self.REPLAY_MODE = False 220 | self.P_REPLAY_MODE = False 221 | self.SHOW_GHOST = False 222 | self.GHOST_TURN_OFF = False 223 | self.TRAIL_LINES = False 224 | 225 | self.TRAILS_TOGGLED = False 226 | 227 | self.P_FOLLOW = [-1]*8 228 | 229 | self.show_zones = False 230 | self.show_flows = False 231 | 232 | 233 | 234 | self.SHOW_DIR_NORMALIZED = False 235 | self.SHOW_CAMERA_ANGLE = False 236 | self.SHOW_CAM = False 237 | self.SHOW_VEL_VECTOR = False 238 | self.SHOW_TARGETS = False 239 | 240 | 241 | self.SHOW_VEL_COMPONENTS = False 242 | self.SHOW_VEL_NORMALIZED = False 243 | self.SHOW_ACCELERATION = False 244 | 245 | self.in_file_dialogue = False 246 | 247 | 248 | 249 | def get_sprite_scale(self): 250 | return int(self.get_tkvar('SPRITE_SCALE')) 251 | 252 | 253 | 254 | 255 | 256 | def toggle_REPLAY_MODE(self): 257 | 258 | R = not self.REPLAY_MODE 259 | 260 | if R: 261 | self.REPLAY_BUTTON.config(relief=PRESSED_RELIEF) 262 | self.TRAIL_CBOX.deselect() 263 | self.TRAIL_CBOX.invoke() 264 | #self.TRAIL_LINES = True 265 | self.SPRITE_SIZE_WIDGET.config(state='disabled') 266 | else: 267 | self.REPLAY_BUTTON.config(relief=RAISED) 268 | self.SPRITE_SIZE_WIDGET.config(state='readonly') 269 | self.GHOST_TURN_OFF = True 270 | 271 | self.REPLAY_MODE = R 272 | 273 | 274 | 275 | 276 | 277 | def toggle_follow_1(self): self.toggle_follow(0) 278 | def toggle_follow_2(self): self.toggle_follow(1) 279 | def toggle_follow_3(self): self.toggle_follow(2) 280 | def toggle_follow_4(self): self.toggle_follow(3) 281 | def toggle_follow_5(self): self.toggle_follow(4) 282 | def toggle_follow_6(self): self.toggle_follow(5) 283 | def toggle_follow_7(self): self.toggle_follow(6) 284 | def toggle_follow_8(self): self.toggle_follow(7) 285 | def toggle_follow(self, n): 286 | r_nm = "racer" + str(n) 287 | 288 | self.racers_to_follow[r_nm] = not self.racers_to_follow[r_nm] 289 | 290 | if self.racers_to_follow[r_nm]: 291 | self.FOLLOW_BUTTONS[n].config(bg="#c8c8c8") 292 | else: 293 | self.FOLLOW_BUTTONS[n].config(bg="#f0f0f0") 294 | 295 | 296 | 297 | def set_in_file_dialogue(self): 298 | self.in_file_dialogue = True 299 | 300 | def set_not_in_file_dialogue(self): 301 | self.in_file_dialogue = False 302 | 303 | 304 | 305 | def get_tkvar(self, v): 306 | return self.TK_VARS[v].get() 307 | 308 | def set_tkvar(self, v, val): 309 | self.TK_VARS[v].set(val) 310 | 311 | 312 | def set_trail_var(self): 313 | 314 | if not self.REPLAY_MODE: 315 | 316 | self.TRAIL_LINES = self.get_tkvar('TRAIL_CBOX') 317 | 318 | #if self.TRAIL_LINES: self.TRAIL_BUTTON.config(relief=PRESSED_RELIEF) 319 | #else: self.TRAIL_BUTTON.config(relief=RAISED) 320 | 321 | self.TRAILS_TOGGLED = True 322 | 323 | def set_zone_var(self): 324 | self.show_zones = self.get_tkvar('ZONE_CBOX') 325 | 326 | #if self.show_zones: self.CP_BUTTON.config(relief=PRESSED_RELIEF) 327 | #else: self.CP_BUTTON.config(relief=RAISED) 328 | 329 | def set_flow_var(self): 330 | self.show_flows = self.get_tkvar('FLOW_CBOX') 331 | 332 | #if self.show_flows: self.FLOW_BUTTON.config(relief=PRESSED_RELIEF) 333 | #else: self.FLOW_BUTTON.config(relief=RAISED) 334 | 335 | 336 | def set_cam_var(self): 337 | self.SHOW_CAM = self.get_tkvar('CAM_CBOX') 338 | 339 | 340 | def set_vec_var(self): 341 | self.SHOW_DIR_NORMALIZED = self.SHOW_CAMERA_ANGLE = self.SHOW_VEL_VECTOR = self.get_tkvar('VEC_CBOX') 342 | 343 | 344 | def set_tgt_var(self): 345 | self.SHOW_TARGETS = self.get_tkvar('TGT_CBOX') 346 | 347 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /Assets.py: -------------------------------------------------------------------------------- 1 | import pygame, numpy 2 | from pygame.gfxdraw import * 3 | from pygame.locals import * 4 | 5 | from tkinter import * 6 | 7 | from PIL import Image 8 | from PIL import ImageTk 9 | 10 | import OpenGL 11 | 12 | from OpenGL.GL import * 13 | from OpenGL.GLU import * 14 | 15 | #========================================================================================================================= 16 | # 17 | # IMAGES 18 | # 19 | #========================================================================================================================= 20 | 21 | #global TOOL_ICON 22 | #TOOL_ICON = pygame.image.load('assets/icon.png') 23 | 24 | 25 | ''' 26 | MARIO_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/mario.png') , (width//64, height//64)) 27 | LUIGI_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/luigi.png') , (width//64, height//64)) 28 | BOWSER_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/bowser.png'), (width//64, height//64)) 29 | PEACH_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/peach.png') , (width//64, height//64)) 30 | DK_JR_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/dk_jr.png') , (width//64, height//64)) 31 | KOOPA_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/koopa.png') , (width//64, height//64)) 32 | TOAD_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/toad.png') , (width//64, height//64)) 33 | YOSHI_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/yoshi.png') , (width//64, height//64)) 34 | ARROW_IMAGE = pygame.transform.smoothscale(pygame.image.load('assets/arrow.png') , (width//64, height//64)) 35 | ''' 36 | 37 | global MARIO_IMAGE 38 | global LUIGI_IMAGE 39 | global BOWSER_IMAGE 40 | global PEACH_IMAGE 41 | global DK_JR_IMAGE 42 | global KOOPA_IMAGE 43 | global TOAD_IMAGE 44 | global YOSHI_IMAGE 45 | global ARROW_IMAGE 46 | 47 | global OBJECT_IMAGES 48 | global OBJECT_TEXTURES 49 | 50 | global MARIO_TK 51 | global LUIGI_TK 52 | global BOWSER_TK 53 | global PEACH_TK 54 | global DK_JR_TK 55 | global KOOPA_TK 56 | global TOAD_TK 57 | global YOSHI_TK 58 | global ARROW_TK 59 | global EMPTY_TK 60 | 61 | 62 | 63 | global GL_CNV_ID 64 | 65 | 66 | 67 | OBJECT_IMAGES = {} 68 | 69 | MARIO_IMAGE = None 70 | LUIGI_IMAGE = None 71 | BOWSER_IMAGE = None 72 | PEACH_IMAGE = None 73 | DK_JR_IMAGE = None 74 | KOOPA_IMAGE = None 75 | TOAD_IMAGE = None 76 | YOSHI_IMAGE = None 77 | ARROW_IMAGE = None 78 | 79 | 80 | 81 | global CHARACTERS_TK 82 | 83 | 84 | 85 | def create_tk_images(): 86 | global MARIO_TK 87 | global LUIGI_TK 88 | global BOWSER_TK 89 | global PEACH_TK 90 | global DK_JR_TK 91 | global KOOPA_TK 92 | global TOAD_TK 93 | global YOSHI_TK 94 | global ARROW_TK 95 | global EMPTY_TK 96 | 97 | global CHARACTERS_TK 98 | 99 | button_width = 50 100 | button_height = 50 101 | 102 | MARIO_TK = ImageTk.PhotoImage(Image.open('assets/mario.png').resize((button_width, button_height), Image.ANTIALIAS)) 103 | LUIGI_TK = ImageTk.PhotoImage(Image.open('assets/luigi.png').resize((button_width, button_height), Image.ANTIALIAS)) 104 | BOWSER_TK = ImageTk.PhotoImage(Image.open('assets/bowser.png').resize((button_width, button_height), Image.ANTIALIAS)) 105 | PEACH_TK = ImageTk.PhotoImage(Image.open('assets/peach.png').resize((button_width, button_height), Image.ANTIALIAS)) 106 | DK_JR_TK = ImageTk.PhotoImage(Image.open('assets/dk_jr.png').resize((button_width, button_height), Image.ANTIALIAS)) 107 | KOOPA_TK = ImageTk.PhotoImage(Image.open('assets/koopa.png').resize((button_width, button_height), Image.ANTIALIAS)) 108 | TOAD_TK = ImageTk.PhotoImage(Image.open('assets/toad.png').resize((button_width, button_height), Image.ANTIALIAS)) 109 | YOSHI_TK = ImageTk.PhotoImage(Image.open('assets/yoshi.png').resize((button_width, button_height), Image.ANTIALIAS)) 110 | ARROW_TK = ImageTk.PhotoImage(Image.open('assets/arrow.png').resize((button_width, button_height), Image.ANTIALIAS)) 111 | EMPTY_TK = ImageTk.PhotoImage(Image.open('assets/empty.png').resize((button_width, button_height), Image.ANTIALIAS)) 112 | 113 | CHARACTERS_TK = { 114 | "mario": MARIO_TK, 115 | "luigi": LUIGI_TK, 116 | "bowser": BOWSER_TK, 117 | "peach": PEACH_TK, 118 | "dk": DK_JR_TK, 119 | "koopa": KOOPA_TK, 120 | "toad": TOAD_TK, 121 | "yoshi": YOSHI_TK, 122 | "arrow": ARROW_TK, 123 | "empty": EMPTY_TK 124 | } 125 | 126 | 127 | 128 | MARIO_TK = None 129 | LUIGI_TK = None 130 | BOWSER_TK = None 131 | PEACH_TK = None 132 | DK_JR_TK = None 133 | KOOPA_TK = None 134 | TOAD_TK = None 135 | YOSHI_TK = None 136 | EMPTY_TK = None 137 | ICON_TK = None 138 | 139 | 140 | CHARACTERS_TK = { 141 | "mario": MARIO_TK, 142 | "luigi": LUIGI_TK, 143 | "bowser": BOWSER_TK, 144 | "peach": PEACH_TK, 145 | "dk": DK_JR_TK, 146 | "koopa": KOOPA_TK, 147 | "toad": TOAD_TK, 148 | "yoshi": YOSHI_TK, 149 | "arrow": EMPTY_TK, 150 | "empty": EMPTY_TK 151 | } 152 | 153 | 154 | def create_pygame_images(): 155 | 156 | global MARIO_IMAGE 157 | global LUIGI_IMAGE 158 | global BOWSER_IMAGE 159 | global PEACH_IMAGE 160 | global DK_JR_IMAGE 161 | global KOOPA_IMAGE 162 | global TOAD_IMAGE 163 | global YOSHI_IMAGE 164 | global ARROW_IMAGE 165 | 166 | 167 | MARIO_IMAGE = pygame.image.load('assets/mario.png').convert_alpha() 168 | LUIGI_IMAGE = pygame.image.load('assets/luigi.png').convert_alpha() 169 | BOWSER_IMAGE = pygame.image.load('assets/bowser.png').convert_alpha() 170 | PEACH_IMAGE = pygame.image.load('assets/peach.png').convert_alpha() 171 | DK_JR_IMAGE = pygame.image.load('assets/dk_jr.png').convert_alpha() 172 | KOOPA_IMAGE = pygame.image.load('assets/koopa.png').convert_alpha() 173 | TOAD_IMAGE = pygame.image.load('assets/toad.png').convert_alpha() 174 | YOSHI_IMAGE = pygame.image.load('assets/yoshi.png').convert_alpha() 175 | ARROW_IMAGE = pygame.image.load('assets/arrow.png').convert_alpha() 176 | 177 | # -------------- 178 | 179 | G_SHELL_IMAGE = pygame.image.load('assets/g_shell.png').convert_alpha() 180 | R_SHELL_IMAGE = pygame.image.load('assets/r_shell.png').convert_alpha() 181 | BANANA_IMAGE = pygame.image.load('assets/banana.png').convert_alpha() 182 | FIREBALL_IMAGE = pygame.image.load('assets/fireball.png').convert_alpha() 183 | P_MUSH_IMAGE = pygame.image.load('assets/p_mush.png').convert_alpha() 184 | EGG_IMAGE = pygame.image.load('assets/egg.png').convert_alpha() 185 | 186 | KILLER_IMAGE = pygame.image.load('assets/killer.png').convert_alpha() 187 | 188 | 189 | 190 | PIPE_G_IMAGE = pygame.image.load('assets/pipe_g.png').convert_alpha() 191 | PIPE_O_IMAGE = pygame.image.load('assets/pipe_o.png').convert_alpha() 192 | 193 | THWOMP_IMAGE = pygame.image.load('assets/thwomp.png').convert_alpha() 194 | THWOMP_2_A_IMAGE = pygame.image.load('assets/thwomp_2_A.png').convert_alpha() 195 | THWOMP_2_B_IMAGE = pygame.image.load('assets/thwomp_2_B.png').convert_alpha() 196 | 197 | CHEEP_IMAGE = pygame.image.load('assets/cheep.png').convert_alpha() 198 | MOLE_IMAGE = pygame.image.load('assets/mole.png').convert_alpha() 199 | POLE_IMAGE = pygame.image.load('assets/pole.png').convert_alpha() 200 | 201 | PLANT_A_IMAGE = pygame.image.load('assets/plant_A.png').convert_alpha() 202 | PLANT_B_IMAGE = pygame.image.load('assets/plant_B.png').convert_alpha() 203 | 204 | BALLOON_B_IMAGE = pygame.image.load('assets/balloon_b.png').convert_alpha() 205 | BALLOON_R_IMAGE = pygame.image.load('assets/balloon_r.png').convert_alpha() 206 | 207 | global OBJECT_IMAGES 208 | 209 | OBJECT_IMAGES = { 210 | "mario": MARIO_IMAGE, 211 | "luigi": LUIGI_IMAGE, 212 | "bowser": BOWSER_IMAGE, 213 | "peach": PEACH_IMAGE, 214 | "dk": DK_JR_IMAGE, 215 | "koopa": KOOPA_IMAGE, 216 | "toad": TOAD_IMAGE, 217 | "yoshi": YOSHI_IMAGE, 218 | "arrow": ARROW_IMAGE, 219 | "g_shell": G_SHELL_IMAGE, 220 | "r_shell": R_SHELL_IMAGE, 221 | "banana": BANANA_IMAGE, 222 | "fireball": FIREBALL_IMAGE, 223 | "p_mush": P_MUSH_IMAGE, 224 | "egg": EGG_IMAGE, 225 | "killer": KILLER_IMAGE, 226 | "pipe_g": PIPE_G_IMAGE, 227 | "pipe_o": PIPE_O_IMAGE, 228 | "thwomp": THWOMP_IMAGE, 229 | "thwomp_2_a": THWOMP_2_A_IMAGE, 230 | "thwomp_2_b": THWOMP_2_B_IMAGE, 231 | "cheep": CHEEP_IMAGE, 232 | "mole": MOLE_IMAGE, 233 | "pole": POLE_IMAGE, 234 | "plant_a": PLANT_A_IMAGE, 235 | "plant_b": PLANT_B_IMAGE, 236 | "balloon_b": BALLOON_B_IMAGE, 237 | "balloon_r": BALLOON_R_IMAGE 238 | } 239 | 240 | 241 | 242 | 243 | def create_GL_textures(): 244 | global OBJECT_TEXTURES 245 | global GL_CNV_ID 246 | 247 | GL_CNV_ID = glGenTextures(1) 248 | 249 | OBJECT_TEXTURES = {} 250 | 251 | for img in OBJECT_IMAGES: 252 | OBJECT_TEXTURES[img] = surfToNewGLTex(GET_IMAGE(img)) 253 | 254 | 255 | 256 | 257 | def surfToNewGLTex(surf): 258 | new_tex = glGenTextures(1) 259 | return surfToGLTex(surf, ID=new_tex) 260 | 261 | 262 | def surfToGLTex(surf, ID=None): 263 | global GL_CNV_ID 264 | 265 | rgb_S = pygame.image.tostring(surf, 'RGBA') 266 | 267 | if ID == None: glBindTexture(GL_TEXTURE_2D, GL_CNV_ID) 268 | else: glBindTexture(GL_TEXTURE_2D, ID) 269 | 270 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 271 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) 272 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) 273 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) 274 | s_rect = surf.get_rect() 275 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s_rect.width, s_rect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgb_S) 276 | glGenerateMipmap(GL_TEXTURE_2D) 277 | glBindTexture(GL_TEXTURE_2D, 0) 278 | 279 | if ID == None: return GL_CNV_ID 280 | else: return ID 281 | 282 | 283 | 284 | 285 | 286 | def buffToGLTex(buff, size, ID=None, ALPHA=True): 287 | global GL_CNV_ID 288 | 289 | if ID == None: ID = GL_CNV_ID 290 | 291 | glBindTexture(GL_TEXTURE_2D, ID) 292 | 293 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 294 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) 295 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) 296 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) 297 | 298 | if ALPHA: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, buff) 299 | else: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size[0], size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, buff) 300 | 301 | glGenerateMipmap(GL_TEXTURE_2D) 302 | glBindTexture(GL_TEXTURE_2D, 0) 303 | 304 | return ID 305 | 306 | 307 | 308 | 309 | 310 | 311 | def IndentityMat44(): return numpy.matrix(numpy.identity(4), copy=False, dtype='float32') 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | def GET_IMAGE(obj_name): 320 | global OBJECT_IMAGES 321 | 322 | if obj_name.lower() in OBJECT_IMAGES: 323 | img = OBJECT_IMAGES[obj_name.lower()].copy() 324 | else: 325 | img = ARROW_IMAGE.copy() 326 | 327 | return img 328 | 329 | 330 | def GET_TEXTURE(obj_name): 331 | global OBJECT_TEXTURES 332 | 333 | if obj_name.lower() in OBJECT_TEXTURES: 334 | img = OBJECT_TEXTURES[obj_name.lower()] 335 | else: 336 | img = OBJECT_TEXTURES['arrow'] 337 | 338 | return img 339 | 340 | 341 | def rot_image(image, angle): 342 | orig_rect = image.get_rect() 343 | rot_image = pygame.transform.rotate(image, angle) 344 | rot_rect = orig_rect.copy() 345 | rot_rect.center = rot_image.get_rect().center 346 | rot_image = rot_image.subsurface(rot_rect).copy() 347 | return rot_image -------------------------------------------------------------------------------- /TrackHelper.py: -------------------------------------------------------------------------------- 1 | 2 | import pygame 3 | from pygame.gfxdraw import * 4 | from pygame.locals import * 5 | 6 | 7 | import random 8 | 9 | import Assets 10 | 11 | import time 12 | 13 | def word_to_RGB(w): 14 | r = ((w ) % 32) * 8 15 | g = ((w / 32) % 32) * 8 16 | b = ((w / 1024) % 32) * 8 17 | 18 | r = (r + (r / 32)) // 1 19 | g = (g + (g / 32)) // 1 20 | b = (b + (b / 32)) // 1 21 | 22 | if r > 255: r = 255 23 | if g > 255: g = 255 24 | if b > 255: b = 255 25 | 26 | return (r, g, b) 27 | 28 | 29 | 30 | 31 | def generate_palette(p_data): 32 | pal_vals = [] 33 | 34 | 35 | val = 0 36 | for i in range(len(p_data)): 37 | if i & 1 == 0: 38 | val = p_data[i] 39 | else: 40 | val += p_data[i] * 256 41 | 42 | pal_vals.append(word_to_RGB(val)) 43 | 44 | #pal = [pal_vals[i*8: (i+1)*8] for i in range(len(pal_vals)//8)] 45 | 46 | return pal_vals 47 | 48 | 49 | 50 | 51 | def pixels_to_bytes(p): 52 | p_b = [] 53 | 54 | for r,g,b in p: 55 | p_b.append(int(r)) 56 | p_b.append(int(g)) 57 | p_b.append(int(b)) 58 | 59 | return bytes(p_b) 60 | 61 | def pixels_to_bytes_alpha(p, alpha): 62 | p_b = [] 63 | 64 | for r,g,b in p: 65 | p_b.append(int(r)) 66 | p_b.append(int(g)) 67 | p_b.append(int(b)) 68 | p_b.append(int(alpha)) 69 | 70 | return bytes(p_b) 71 | 72 | 73 | 74 | def bytes_to_tile_buffer(tile_bytes, pal): 75 | 76 | tile = [] 77 | 78 | for b in tile_bytes: 79 | tile.append(pal[b]) 80 | 81 | return pixels_to_bytes(tile) 82 | 83 | 84 | 85 | 86 | 87 | def get_tile_buffs(t_data, p_data): 88 | 89 | pal = generate_palette(p_data) 90 | 91 | TILES = [bytearray(bytes_to_tile_buffer(t_data[n * 64:(n + 1) * 64], pal)) for n in range(len(t_data)//64)] 92 | 93 | return TILES 94 | 95 | 96 | 97 | 98 | def get_tilemap_from_data(m_data, t_data, p_data): 99 | 100 | MAP = pygame.Surface((1024, 1024)) 101 | 102 | TILES = get_tile_buffs(t_data, p_data) 103 | 104 | TILE_IMGS = [pygame.image.frombuffer(TILE, (8, 8), "RGB").convert() for TILE in TILES] 105 | 106 | 107 | for y in range(128): 108 | for x in range(128): 109 | MAP.blit(TILE_IMGS[m_data[y * 128 + x]], (x * 8, y * 8)) 110 | 111 | rt = (bytearray(pygame.image.tostring(MAP, 'RGB')), TILES) 112 | 113 | return rt 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | def fix_diff_surround(diff_map): 129 | 130 | new_diff_map = [[False for x in range(3)] for y in range(3)] 131 | 132 | 133 | TOP = 0 # define top row 134 | MID = 1 # define mid (both row and column) 135 | BOT = 2 # define bottom row 136 | 137 | LEF = 0 # define left column 138 | RHT = 2 # define right column 139 | 140 | 141 | # checking order 142 | # 143 | # 1 . . . 2 144 | # . . 145 | # . . 146 | # . . 147 | # 3 . . . 4 148 | 149 | # set corners 150 | if diff_map[TOP][LEF] == True: new_diff_map[TOP][LEF] = True # check 1 151 | if diff_map[TOP][RHT] == True: new_diff_map[TOP][RHT] = True # check 2 152 | if diff_map[BOT][LEF] == True: new_diff_map[BOT][LEF] = True # check 3 153 | if diff_map[BOT][RHT] == True: new_diff_map[BOT][RHT] = True # check 4 154 | 155 | 156 | 157 | 158 | 159 | # checking order 160 | # 161 | # . 5 5 5 . 162 | # 6 7 163 | # 6 7 164 | # 6 7 165 | # . 8 8 8 . 166 | 167 | 168 | # top band 169 | # if top enabled, set top corners as well 170 | # x - - - x 171 | # . . 172 | # . . 173 | # . . 174 | # . . . . . 175 | if diff_map[TOP][MID]: 176 | new_diff_map[TOP][LEF] = True 177 | new_diff_map[TOP][MID] = True 178 | new_diff_map[TOP][RHT] = True 179 | 180 | 181 | # left band 182 | # if left enabled, set left corners as well 183 | # x . . . . 184 | # | . 185 | # | . 186 | # | . 187 | # x . . . . 188 | if diff_map[MID][LEF]: 189 | new_diff_map[TOP][LEF] = True 190 | new_diff_map[MID][LEF] = True 191 | new_diff_map[BOT][LEF] = True 192 | 193 | 194 | # right band 195 | # if right enabled, set right corners as well 196 | # . . . . x 197 | # . | 198 | # . | 199 | # . | 200 | # . . . . x 201 | if diff_map[MID][RHT]: 202 | new_diff_map[TOP][RHT] = True 203 | new_diff_map[MID][RHT] = True 204 | new_diff_map[BOT][RHT] = True 205 | 206 | 207 | # bottom band 208 | # if bottom enabled, set bottom corners as well 209 | # . . . . . 210 | # . . 211 | # . . 212 | # . . 213 | # x - - - x 214 | if diff_map[BOT][MID]: 215 | new_diff_map[BOT][LEF] = True 216 | new_diff_map[BOT][MID] = True 217 | new_diff_map[BOT][RHT] = True 218 | 219 | 220 | return new_diff_map 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | def make_base_cp_tile(c_dict): 229 | return pygame.image.frombuffer(pixels_to_bytes_alpha([c_dict["base"]]*256, 100), (16, 16), "RGBA") 230 | 231 | 232 | CP_COLS = { 233 | "red": {"base": (255, 100, 100), "border": (255, 50, 50)}, 234 | "orange": {"base": (255, 200, 100), "border": (255, 100, 0)}, 235 | "green": {"base": (100, 255, 100), "border": (0, 255, 0)}, 236 | "yellow": {"base": (255, 255, 100), "border": (255, 255, 0)}, 237 | "blue": {"base": (150, 150, 255), "border": (100, 100, 255)} 238 | } 239 | 240 | 241 | 242 | cp_colors = ( 243 | #CP_COLS["blue"], 244 | CP_COLS["green"], 245 | CP_COLS["yellow"], 246 | CP_COLS["orange"], 247 | CP_COLS["red"] 248 | ) 249 | 250 | 251 | red_tile = make_base_cp_tile(CP_COLS["red"]) 252 | orange_tile = make_base_cp_tile(CP_COLS["orange"]) 253 | green_tile = make_base_cp_tile(CP_COLS["green"]) 254 | yellow_tile = make_base_cp_tile(CP_COLS["yellow"]) 255 | blue_tile = make_base_cp_tile(CP_COLS["blue"]) 256 | 257 | #cp_tiles = (red_tile, orange_tile, green_tile, yellow_tile) 258 | #cp_tiles = (blue_tile, green_tile, yellow_tile, red_tile) 259 | cp_tiles = (green_tile, yellow_tile, orange_tile, red_tile) 260 | 261 | 262 | 263 | 264 | 265 | POSSIBLE_ZONE_SHAPES = [ 266 | [[False, False, False], [False, False, False], [False, False, False]], 267 | [[True, False, False], [False, False, False], [False, False, False]], 268 | [[True, True, True], [False, False, False], [False, False, False]], 269 | [[False, False, True], [False, False, False], [False, False, False]], 270 | [[True, False, True], [False, False, False], [False, False, False]], 271 | [[True, False, False], [True, False, False], [True, False, False]], 272 | [[True, True, True], [True, False, False], [True, False, False]], 273 | [[True, False, True], [True, False, False], [True, False, False]], 274 | [[False, False, True], [False, False, True], [False, False, True]], 275 | [[True, False, True], [False, False, True], [False, False, True]], 276 | [[True, True, True], [False, False, True], [False, False, True]], 277 | [[True, False, True], [True, False, True], [True, False, True]], 278 | [[True, True, True], [True, False, True], [True, False, True]], 279 | [[False, False, False], [False, False, False], [True, False, False]], 280 | [[True, False, False], [False, False, False], [True, False, False]], 281 | [[True, True, True], [False, False, False], [True, False, False]], 282 | [[False, False, True], [False, False, False], [True, False, False]], 283 | [[True, False, True], [False, False, False], [True, False, False]], 284 | [[False, False, True], [False, False, True], [True, False, True]], 285 | [[True, False, True], [False, False, True], [True, False, True]], 286 | [[True, True, True], [False, False, True], [True, False, True]], 287 | [[False, False, False], [False, False, False], [True, True, True]], 288 | [[True, False, False], [False, False, False], [True, True, True]], 289 | [[True, True, True], [False, False, False], [True, True, True]], 290 | [[False, False, True], [False, False, False], [True, True, True]], 291 | [[True, False, True], [False, False, False], [True, True, True]], 292 | [[True, False, False], [True, False, False], [True, True, True]], 293 | [[True, True, True], [True, False, False], [True, True, True]], 294 | [[True, False, True], [True, False, False], [True, True, True]], 295 | [[False, False, True], [False, False, True], [True, True, True]], 296 | [[True, False, True], [False, False, True], [True, True, True]], 297 | [[True, True, True], [False, False, True], [True, True, True]], 298 | [[True, False, True], [True, False, True], [True, True, True]], 299 | [[True, True, True], [True, False, True], [True, True, True]], 300 | [[False, False, False], [False, False, False], [False, False, True]], 301 | [[True, False, False], [False, False, False], [False, False, True]], 302 | [[True, True, True], [False, False, False], [False, False, True]], 303 | [[False, False, True], [False, False, False], [False, False, True]], 304 | [[True, False, True], [False, False, False], [False, False, True]], 305 | [[True, False, False], [True, False, False], [True, False, True]], 306 | [[True, True, True], [True, False, False], [True, False, True]], 307 | [[True, False, True], [True, False, False], [True, False, True]], 308 | [[False, False, False], [False, False, False], [True, False, True]], 309 | [[True, False, False], [False, False, False], [True, False, True]], 310 | [[True, True, True], [False, False, False], [True, False, True]], 311 | [[False, False, True], [False, False, False], [True, False, True]], 312 | [[True, False, True], [False, False, False], [True, False, True]] 313 | ] 314 | 315 | 316 | # storage for already-generated 317 | PRE_GEN_TILES = { 318 | 0: {}, 319 | 1: {}, 320 | 2: {}, 321 | 3: {} 322 | } 323 | 324 | 325 | df_hsh = ( 326 | (0x01, 0x40, 0x02), 327 | (0x80, 0x00, 0x20), 328 | (0x08, 0x10, 0x04) 329 | ) 330 | 331 | 332 | ZONE_QUALITY = None 333 | FLOW_QUALITY = None 334 | 335 | 336 | 337 | def diff_hash(diff_map): 338 | 339 | h = 0 340 | for i in range(3): 341 | for j in range(3): 342 | if diff_map[i][j]: h += df_hsh[i][j] 343 | 344 | return h 345 | 346 | 347 | def gen_cp_tile(cp_attribute, diff_map): 348 | 349 | 350 | BORDER_WIDTH = 1 351 | 352 | 353 | cp_col = cp_attribute % 4 # which color to use based on the cp speed setting 354 | border_map = fix_diff_surround(diff_map) # adjusted border map to fix corner overwriting 355 | 356 | 357 | TOP = 0 # define top row 358 | MID = 1 # define mid (both row and column) 359 | BOT = 2 # define bottom row 360 | 361 | LEF = 0 # define left column 362 | RHT = 2 # define right column 363 | 364 | 365 | base_tile = cp_tiles[cp_col] # base cp tile (pre-rendered to save minimal time) 366 | CP_COLOR = cp_colors[cp_col] # color dict based on cp number 367 | 368 | 369 | BASE_COLOR = ( 370 | CP_COLOR["base"][0], # red channel 371 | CP_COLOR["base"][1], # green channel 372 | CP_COLOR["base"][2], # blue channel 373 | 100 # alpha channel 374 | ) 375 | 376 | 377 | BORDER_COLOR = ( 378 | CP_COLOR["border"][0], # red channel 379 | CP_COLOR["border"][1], # green channel 380 | CP_COLOR["border"][2], # blue channel 381 | 150 # alpha channel 382 | ) 383 | 384 | 385 | img_buff = list(pygame.image.tostring(base_tile, "RGBA")) 386 | pix = [] 387 | 388 | 389 | # convert buffer to pixel 4-tuples 390 | for i in range(len(img_buff) // 4): 391 | pixel = ( 392 | img_buff[i*4 + 0], # red channel 393 | img_buff[i*4 + 1], # green channel 394 | img_buff[i*4 + 2], # blue channel 395 | img_buff[i*4 + 3] # alpha channel 396 | ) 397 | pix.append(pixel) 398 | 399 | 400 | 401 | ##### process border pixels here 402 | 403 | # corners are a NxN square of border pixels 404 | 405 | # top left corner 406 | if border_map[TOP][LEF] == True: 407 | for i in range(BORDER_WIDTH): 408 | for j in range(BORDER_WIDTH): 409 | x = j 410 | y = i 411 | pix[y * 16 + x] = BORDER_COLOR 412 | 413 | 414 | # top right corner 415 | if border_map[TOP][RHT] == True: 416 | for i in range(BORDER_WIDTH): 417 | for j in range(BORDER_WIDTH): 418 | x = 15-j 419 | y = i 420 | pix[y * 16 + x] = BORDER_COLOR 421 | 422 | 423 | # bottom left corner 424 | if border_map[BOT][LEF] == True: 425 | for i in range(BORDER_WIDTH): 426 | for j in range(BORDER_WIDTH): 427 | x = j 428 | y = 15-i 429 | pix[y * 16 + x] = BORDER_COLOR 430 | 431 | 432 | # bottom right corner 433 | if border_map[BOT][RHT] == True: 434 | for i in range(BORDER_WIDTH): 435 | for j in range(BORDER_WIDTH): 436 | x = 15-j 437 | y = 15-i 438 | pix[y * 16 + x] = BORDER_COLOR 439 | 440 | 441 | 442 | # side-bands are N pixels wide, and 16-(N*2) pixels long 443 | # (N pixels on each border dont need to be rendered again) 444 | 445 | # top band 446 | if border_map[TOP][MID] == True: 447 | for i in range(BORDER_WIDTH): 448 | for j in range(16 - (BORDER_WIDTH * 2)): 449 | x = BORDER_WIDTH + j 450 | y = i 451 | pix[y * 16 + x] = BORDER_COLOR 452 | 453 | # bottom band 454 | if border_map[BOT][MID] == True: 455 | for i in range(BORDER_WIDTH): 456 | for j in range(16 - (BORDER_WIDTH * 2)): 457 | x = BORDER_WIDTH + j 458 | y = 15-i 459 | pix[y * 16 + x] = BORDER_COLOR 460 | 461 | # left band 462 | if border_map[MID][LEF] == True: 463 | for i in range(16 - (BORDER_WIDTH * 2)): 464 | for j in range(BORDER_WIDTH): 465 | x = j 466 | y = BORDER_WIDTH + i 467 | pix[y * 16 + x] = BORDER_COLOR 468 | 469 | # right band 470 | if border_map[MID][RHT] == True: 471 | for i in range(16 - (BORDER_WIDTH * 2)): 472 | for j in range(BORDER_WIDTH): 473 | x = 15-j 474 | y = BORDER_WIDTH + i 475 | pix[y * 16 + x] = BORDER_COLOR 476 | 477 | 478 | 479 | 480 | 481 | 482 | # convert pixel buffer back to image 483 | out_buff = [] 484 | 485 | for p in pix: 486 | out_buff.append(p[0]) # red channel 487 | out_buff.append(p[1]) # green channel 488 | out_buff.append(p[2]) # blue channel 489 | out_buff.append(p[3]) # alpha channel 490 | 491 | 492 | return pygame.image.frombuffer(bytes(out_buff), (16, 16), "RGBA") 493 | 494 | 495 | 496 | 497 | def gen_cp_tiles(): 498 | global PRE_GEN_TILES 499 | 500 | PRE_GEN_TILES = { 501 | 0: {}, 502 | 1: {}, 503 | 2: {}, 504 | 3: {} 505 | } 506 | 507 | for S in POSSIBLE_ZONE_SHAPES: 508 | for attr in range(4): 509 | tile = gen_cp_tile(attr, S) 510 | PRE_GEN_TILES[attr][diff_hash(S)] = pygame.transform.smoothscale( 511 | tile, # zone tile 512 | (16*ZONE_QUALITY, 16*ZONE_QUALITY) # resize to a "16x16" image 513 | ).convert_alpha() 514 | 515 | 516 | 517 | 518 | def get_cp_tile(cp_attribute, diff_map): 519 | 520 | return PRE_GEN_TILES[cp_attribute % 4][diff_hash(fix_diff_surround(diff_map))] 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | def gen_arrow_images(): 531 | global ROTATED_ARROWS 532 | 533 | # pre-generate arrow images, saves time when rendering map! 534 | ROTATED_ARROWS = [] 535 | 536 | for i in range(256): 537 | flow_angle = -(360 * i / 255) 538 | ROTATED_ARROWS.append(pygame.transform.smoothscale( 539 | Assets.rot_image(Assets.ARROW_IMAGE, flow_angle), # rotated arrow image 540 | (16*FLOW_QUALITY, 16*FLOW_QUALITY) # resize to a "16x16" image 541 | ).convert_alpha()) 542 | 543 | 544 | 545 | def set_render_quality(zone_scl=1, flow_scl=1): 546 | global ZONE_QUALITY 547 | global FLOW_QUALITY 548 | 549 | rerender_zone = False 550 | rerender_flow = False 551 | 552 | if zone_scl != ZONE_QUALITY: rerender_zone = True 553 | if flow_scl != FLOW_QUALITY: rerender_flow = True 554 | 555 | ZONE_QUALITY = zone_scl 556 | FLOW_QUALITY = flow_scl 557 | 558 | if rerender_zone: gen_cp_tiles() 559 | if rerender_flow: gen_arrow_images() 560 | 561 | 562 | 563 | 564 | def get_cpmap_flowmap_from_data(zone_data, flow_data, cp_attr): 565 | global ROTATED_ARROWS 566 | global ZONE_QUALITY 567 | global FLOW_QUALITY 568 | """Generate the images for the overlay for the cp and flow maps""" 569 | 570 | 571 | 572 | ZONE_MAP = pygame.Surface((1024*ZONE_QUALITY, 1024*ZONE_QUALITY), SRCALPHA) # zone map base surface, SRCALPHA to set as alpha enabled 573 | FLOW_MAP = pygame.Surface((1024*FLOW_QUALITY, 1024*FLOW_QUALITY), SRCALPHA) # flow map base surface, SRCALPHA to set as alpha enabled 574 | 575 | 576 | # pad the borders of the cp map in order to handle literal edge cases later 577 | cp_padded = [[-1 for x in range(64+2)] for y in range(64+2)] 578 | 579 | for y in range(64): 580 | for x in range(64): 581 | cp_padded[y+1][x+1] = zone_data[y * 64 + x] & 0x7f 582 | 583 | 584 | #flow_time = 0 585 | #zone_time = 0 586 | 587 | # checkpoint and flow maps are a 64x64 table 588 | for y in range(64): 589 | for x in range(64): 590 | 591 | 592 | _y = y+1 # padded map coords 593 | _x = x+1 # padded map coords 594 | 595 | cp_num = cp_padded[_y][_x] # checkpoint tile number 596 | 597 | 598 | if cp_num == 0x7f: continue # skip render if cp=0x7f (null cp) 599 | 600 | 601 | 602 | ######## make flow map tile 603 | 604 | #t1 = time.perf_counter() 605 | 606 | # blit flow arrow onto map 607 | FLOW_MAP.blit( 608 | ROTATED_ARROWS[flow_data[y * 64 + x]], # rotated arrow image 609 | (x * 16 * FLOW_QUALITY, y * 16 * FLOW_QUALITY) # convert tile xy to coordinate xy 610 | ) 611 | 612 | #flow_time += time.perf_counter() - t1 613 | 614 | 615 | 616 | 617 | ######## make cp map tile 618 | 619 | #t1 = time.perf_counter() 620 | 621 | diff_map = [[False for j in range(3)] for i in range(3)] 622 | 623 | 624 | # check the 3x3 area around current cp, to make the border for the tile 625 | for y_off in range(3): 626 | for x_off in range(3): 627 | other_cp = cp_padded[_y + y_off - 1][_x + x_off - 1] # change coordinates to the padded cp map coords 628 | 629 | if cp_num != other_cp: diff_map[y_off][x_off] = True # set proper "different checkpoint" index 630 | 631 | 632 | ZONE_MAP.blit( 633 | get_cp_tile(cp_attr[cp_num * 2], diff_map), # get the cp tile, with borders 634 | (x * 16 * ZONE_QUALITY, y * 16 * ZONE_QUALITY) # convert tile xy to coordinate xy 635 | ) 636 | 637 | 638 | #zone_time += time.perf_counter() - t1 639 | 640 | 641 | #print("ZONE:", 1000 * zone_time) 642 | #print("FLOW:", 1000 * flow_time) 643 | 644 | return (pygame.image.tostring(ZONE_MAP, 'RGBA'), pygame.image.tostring(FLOW_MAP, 'RGBA')) 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | -------------------------------------------------------------------------------- /Objects.py: -------------------------------------------------------------------------------- 1 | 2 | import pygame 3 | from pygame.gfxdraw import * 4 | from pygame.locals import * 5 | 6 | from collections import deque 7 | 8 | import math 9 | 10 | 11 | import Assets 12 | 13 | 14 | 15 | 16 | 17 | global CHARACTER_NAMES 18 | 19 | SQRT_2 = 2 ** 0.5 20 | 21 | 22 | CHARACTER_NAMES = ("mario", "luigi", "bowser", "peach", "dk", "koopa", "toad", "yoshi") 23 | 24 | def CHAR_NUM_TO_NAME(n): 25 | if n < 0x10: 26 | if n%2==0: 27 | return CHARACTER_NAMES[n//2] 28 | else: 29 | raise IndexError("Invalid Character Number: " + str(n)) 30 | else: 31 | # fix up later to include the items 32 | return "arrow" 33 | 34 | 35 | #ITEM_NAMES = ("banana", "gshell", "rshell", "killer", "ai1", "ai2", "banana2", "gshell2") 36 | #ITEM_NAMES = ("mario_arrow", "luigi_arrow", "bowser_arrow", "peach_arrow", "dk_jr_arrow", "koopa_arrow", "toad_arrow", "yoshi_arrow") # ONLY FOR TESTING 37 | ITEM_NAMES = ("banana", "g_shell", "r_shell", "killer", "p_mush", "p_mush", "banana", "g_shell", 38 | "banana", "g_shell", "r_shell", "killer", "p_mush", "p_mush", "banana", "g_shell", 39 | "banana", "g_shell", "r_shell", "killer", "fireball", "fireball", "banana", "g_shell", 40 | "banana", "g_shell", "r_shell", "killer", "p_mush", "p_mush", "banana", "g_shell", 41 | "banana", "g_shell", "r_shell", "killer", "banana", "banana", "banana", "g_shell", 42 | "banana", "g_shell", "r_shell", "killer", "g_shell", "g_shell", "banana", "g_shell", 43 | "banana", "g_shell", "r_shell", "killer", "p_mush", "p_mush", "banana", "g_shell", 44 | "banana", "g_shell", "r_shell", "killer", "egg", "egg", "banana", "g_shell" 45 | ) # ONLY FOR TESTING 46 | 47 | 48 | 49 | def ITEM_NUM_TO_NAME(n): 50 | try: 51 | name = ITEM_NAMES[n//2] 52 | return name 53 | except: 54 | raise IndexError("Invalid Character Number: " + str(n)) 55 | 56 | 57 | 58 | ''' 59 | OBSTACLE_NAMES = ("pipe_g", "pipe_g", "pipe_g", "pipe_g", 60 | "pole", "pole", "pole", "pole", 61 | "mole", "mole", "mole", "mole", 62 | "thwomp", "thwomp", "thwomp", "thwomp", 63 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", 64 | "thwomp_2_A", "thwomp_2_B", "thwomp_2_A", "thwomp_2_B", 65 | "cheep", "cheep", "cheep", "cheep", 66 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", 67 | "pole", "pole", "pole", "pole", 68 | "thwomp", "thwomp", "thwomp", "thwomp", 69 | "plant_A", "plant_A", "plant_B", "plant_B", 70 | "mole", "mole", "mole", "mole", 71 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", 72 | "cheep", "cheep", "cheep", "cheep", 73 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", 74 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", 75 | "pole", "pole", "pole", "pole", 76 | "thwomp", "thwomp", "thwomp", "thwomp", 77 | "plant_A", "plant_A", "plant_B", "plant_B", 78 | "pipe_o", "pipe_o", "pipe_o", "pipe_o", 79 | "balloon_b", "balloon_r", "balloon_b", "balloon_r", 80 | "balloon_b", "balloon_r", "balloon_b", "balloon_r", 81 | "balloon_b", "balloon_r", "balloon_b", "balloon_r", 82 | "balloon_b", "balloon_r", "balloon_b", "balloon_r",) 83 | ''' 84 | 85 | OBSTACLE_NAMES = ("pole", "pole", "pole", "pole", 86 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", 87 | "mole", "mole", "mole", "mole", 88 | "plant_A", "plant_A", "plant_B", "plant_B", 89 | "pipe_g", "pipe_g", "pipe_g", "pipe_g", #VL pipes 90 | "cheep", "cheep", "cheep", "cheep", 91 | "thwomp", "thwomp", "thwomp", "thwomp", 92 | "balloon_b", "balloon_r", "balloon_b", "balloon_r",) 93 | 94 | 95 | 96 | 97 | def OBSTACLE_NUM_TO_NAME(n): 98 | try: 99 | o = OBSTACLE_NAMES[n] 100 | #print(o) 101 | return o 102 | except: 103 | return "pipe_g" 104 | 105 | 106 | #========================================================================================================================= 107 | # 108 | # Base Object Class 109 | # 110 | #========================================================================================================================= 111 | 112 | 113 | class Game_Object(object): 114 | 115 | def __init__(self, x=-100, y=-100, angle=0, img="", surface=None, OBJ_ID="", img_scale=1, address=0): 116 | self.x = x 117 | self.y = y 118 | self.z = 0 119 | #self.disp_x = x 120 | #self.disp_y = y 121 | self.angle = angle 122 | self.img = img 123 | self.surface = surface 124 | self.scl = img_scale 125 | self.ID = OBJ_ID 126 | self.address = address 127 | self.win_scl = 1 128 | #self.tex = img 129 | 130 | @property 131 | def x(self): 132 | return self._x 133 | @x.setter 134 | def x(self, _x): 135 | self._x = _x 136 | 137 | @property 138 | def y(self): 139 | return self._y 140 | @y.setter 141 | def y(self, _y): 142 | self._y = _y 143 | 144 | @property 145 | def z(self): 146 | return self._z 147 | @z.setter 148 | def z(self, _z): 149 | self._z = _z 150 | 151 | @property 152 | def disp_x(self): 153 | return self._disp_x 154 | @disp_x.setter 155 | def disp_x(self, _disp_x): 156 | self._disp_x = _disp_x 157 | 158 | @property 159 | def disp_y(self): 160 | return self._disp_y 161 | @disp_y.setter 162 | def disp_y(self, _disp_y): 163 | self._disp_y = _disp_y 164 | 165 | @property 166 | def OBJ_ID(self): 167 | return self._OBJ_ID 168 | @OBJ_ID.setter 169 | def OBJ_ID(self, _OBJ_ID): 170 | self._OBJ_ID = _OBJ_ID 171 | 172 | 173 | @property 174 | def img(self): 175 | return self._img 176 | @img.setter 177 | def img(self, _img): 178 | _t = _img 179 | 180 | obj_name = "" 181 | if type(_img) == type(""): 182 | obj_name = _img 183 | else: 184 | pass 185 | 186 | if obj_name != "": 187 | _img = Assets.GET_IMAGE(obj_name) 188 | 189 | self._img = _img 190 | 191 | self.tex = _t 192 | 193 | 194 | 195 | @property 196 | def tex(self): 197 | return self._tex 198 | @tex.setter 199 | def tex(self, _tex): 200 | obj_name = "" 201 | if type(_tex) == type(""): 202 | obj_name = _tex 203 | else: 204 | pass 205 | 206 | if obj_name != "": 207 | _tex = Assets.GET_TEXTURE(obj_name) 208 | 209 | #print("Setting tex to", _tex) 210 | 211 | self._tex = _tex 212 | 213 | @property 214 | def scl(self): 215 | return self._scl 216 | @scl.setter 217 | def scl(self, _scl): 218 | self._scl = _scl 219 | 220 | 221 | @property 222 | def win_scl(self): 223 | return self._win_scl 224 | @win_scl.setter 225 | def win_scl(self, _win_scl): 226 | self._win_scl = _win_scl 227 | 228 | 229 | @property 230 | def angle(self): 231 | return self._angle 232 | @angle.setter 233 | def angle(self, _angle): 234 | self._angle = _angle 235 | 236 | 237 | @property 238 | def surface(self): 239 | return self._surface 240 | @surface.setter 241 | def surface(self, _surface): 242 | self._surface = _surface 243 | 244 | 245 | @property 246 | def address(self): 247 | return self._address 248 | @address.setter 249 | def address(self, _address): 250 | self._address = _address 251 | 252 | 253 | 254 | 255 | 256 | def display_on(self, surface=None): 257 | main_surface = self.surface 258 | 259 | if surface == None: 260 | surface = self.surface 261 | 262 | self.surface = surface 263 | self.display() 264 | self.surface = main_surface 265 | 266 | 267 | def display(self, d_method=None): 268 | 269 | z_scl = self.z 270 | if z_scl > 0x8000: 271 | z_scl -= 0x10000 272 | z_scl /= 0x2000 273 | z_scl += 1 274 | 275 | #if z_scl > 10: print(self.z) 276 | if d_method == None: 277 | """ 278 | 279 | #IMG = pygame.transform.smoothscale(self.img, (self.scl, self.scl)) 280 | #IMG = pygame.transform.scale(self.img, (math.floor(self.scl * z_scl / self.win_scl), math.floor(self.scl * z_scl / self.win_scl))) 281 | IMG = pygame.transform.scale(self.img, (math.floor(self.scl * z_scl), math.floor(self.scl * z_scl))) 282 | #IMG = pygame.transform.smoothscale(self.img, (self.img.get_width()*self.scl, self.img.get_height()*self.scl)) 283 | #IMG = pygame.transform.scale(self.img, (self.img.get_width()*self.scl, self.img.get_height()*self.scl)) 284 | IMG = Assets.rot_image(IMG, self.angle) 285 | 286 | ''' 287 | self.surface.blit(IMG, ( 288 | math.floor( (self.disp_x-(IMG.get_width() ))//self.win_scl), 289 | math.floor( (self.disp_y-(IMG.get_height() ))//self.win_scl) 290 | )) 291 | ''' 292 | self.surface.blit(IMG, ( 293 | math.floor( ((self.disp_x)-(IMG.get_width() / 2 ))), 294 | math.floor( ((self.disp_y)-(IMG.get_height()/ 2 ))) 295 | )) 296 | """ 297 | 298 | else: 299 | 300 | d_method( 301 | self.tex, 302 | pos=( 303 | self.disp_x, 304 | self.disp_y 305 | ), 306 | dim=( 307 | self.scl * z_scl, 308 | self.scl * z_scl 309 | ), 310 | rot=( 311 | 0, 312 | 0, 313 | self.angle 314 | ), 315 | centered=True 316 | ) 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | #========================================================================================================================= 325 | # 326 | # RACER CLASS 327 | # 328 | #========================================================================================================================= 329 | 330 | 331 | class Racer(Game_Object): 332 | 333 | def __init__(self, x=-100, y=-100, angle=0, obj_name="", surface=None, OBJ_ID="", display_dest=False, img_scale=1, address=0): 334 | super().__init__(x, y, angle, obj_name, surface, OBJ_ID, img_scale=img_scale, address=address) 335 | 336 | self.vel = (0, 0, 0) 337 | self.speed = 0 338 | self.max_speed = 0 339 | self.accel = 0 340 | self.v_angle = 0 341 | self.c_angle = 0 342 | self.angle_vel = 0 343 | self.dest = (x, y) 344 | self.display_dest = display_dest 345 | self.ch_num = -1 346 | self.buttons = 0 347 | 348 | ''' 349 | self.prev_positions = deque() 350 | self.p_pos_counter = deque() 351 | self.trail_cbuff_len = 0 352 | self.trail_disp_len = 0 353 | self.avg_speed = 0 354 | ''' 355 | 356 | self.reset_trail() 357 | 358 | self.log = 0 359 | 360 | self.max_trails = 0 361 | self._show_trails = self.show_trails = False 362 | 363 | @property 364 | def vel(self): 365 | return self._vel 366 | @vel.setter 367 | def vel(self, _vel): 368 | self._vel = _vel 369 | 370 | 371 | @property 372 | def speed(self): 373 | return self._speed 374 | @speed.setter 375 | def speed(self, _speed): 376 | self._speed = _speed 377 | 378 | @property 379 | def max_speed(self): 380 | return self._max_speed 381 | @max_speed.setter 382 | def max_speed(self, _max_speed): 383 | self._max_speed = _max_speed 384 | 385 | 386 | @property 387 | def accel(self): 388 | return self._accel 389 | @accel.setter 390 | def accel(self, _accel): 391 | self._accel = _accel 392 | 393 | 394 | @property 395 | def v_angle(self): 396 | return self._v_angle 397 | @v_angle.setter 398 | def v_angle(self, _v_angle): 399 | self._v_angle = _v_angle 400 | 401 | 402 | @property 403 | def c_angle(self): 404 | return self._c_angle 405 | @c_angle.setter 406 | def c_angle(self, _c_angle): 407 | self._c_angle = _c_angle 408 | 409 | 410 | @property 411 | def angle_vel(self): 412 | return self._angle_vel 413 | @angle_vel.setter 414 | def angle_vel(self, _angle_vel): 415 | self._angle_vel = _angle_vel 416 | 417 | 418 | @property 419 | def ch_num(self): 420 | return self._ch_num 421 | @ch_num.setter 422 | def ch_num(self, _ch_num): 423 | try: 424 | self.ch_num 425 | except: 426 | self._ch_num = -1 427 | 428 | if self.ch_num != _ch_num: 429 | self.img = CHAR_NUM_TO_NAME(_ch_num) 430 | 431 | self._ch_num = _ch_num 432 | 433 | 434 | 435 | 436 | @property 437 | def dest(self): 438 | return self._dest 439 | @dest.setter 440 | def dest(self, _dest): 441 | self._dest = _dest 442 | 443 | 444 | @property 445 | def buttons(self): 446 | return self._buttons 447 | @buttons.setter 448 | def buttons(self, _buttons): 449 | self._buttons = _buttons 450 | 451 | 452 | @property 453 | def max_trails(self): 454 | return self._max_trails 455 | @max_trails.setter 456 | def max_trails(self, _max_trails): 457 | self._max_trails = _max_trails 458 | 459 | @property 460 | def show_trails(self): 461 | return self._show_trails 462 | @show_trails.setter 463 | def show_trails(self, _show_trails): 464 | if _show_trails != self._show_trails: self.reset_trail() 465 | 466 | self._show_trails = _show_trails 467 | 468 | 469 | 470 | 471 | 472 | def reset_trail(self): 473 | self.prev_positions = deque() 474 | self.p_pos_counter = deque() 475 | self.trail_cbuff_len = 0 476 | self.trail_len = 0 477 | self.avg_speed = 0 478 | 479 | self.prev_x = None 480 | self.prev_y = None 481 | #self.last_s = None 482 | 483 | 484 | def copy_trail(self, other): 485 | self.trail_cbuff_len = other.trail_cbuff_len + 0 486 | self.trail_len = other.trail_len + 0 487 | self.avg_speed = other.avg_speed + 0 488 | self.log = other.log + 0 489 | 490 | if other.prev_x == None: 491 | self.prev_x = None 492 | else: 493 | self.prev_x = other.prev_x + 0 494 | 495 | if other.prev_y == None: 496 | self.prev_y = None 497 | else: 498 | self.prev_y = other.prev_y + 0 499 | 500 | 501 | 502 | self.prev_positions = deque() 503 | self.p_pos_counter = deque() 504 | 505 | for i in range(len(other.prev_positions)): 506 | self.prev_positions.append(0 + other.prev_positions[i]) 507 | 508 | for i in range(len(other.p_pos_counter)): 509 | self.p_pos_counter.append(0 + other.p_pos_counter[i]) 510 | ''' 511 | self.prev_positions = other.prev_positions 512 | self.p_pos_counter = other.p_pos_counter 513 | ''' 514 | 515 | 516 | def display_on(self, surface=None): 517 | main_surface = self.surface 518 | 519 | if surface == None: 520 | surface = self.surface 521 | 522 | self.surface = surface 523 | self.display() 524 | self.surface = main_surface 525 | 526 | 527 | def map_pos(self, pos, SCREEN): 528 | return (pos[0]*SCREEN.SCALE + SCREEN.x, pos[1]*SCREEN.SCALE + SCREEN.y) 529 | 530 | def update_trails(self, trails=0, log_freq=6): 531 | p_show = self.show_trails 532 | 533 | if trails > 1: self.show_trails = True 534 | else: self.show_trails = False 535 | 536 | 537 | 538 | 539 | 540 | if self.show_trails: 541 | self.max_trails = trails 542 | 543 | self.avg_speed += self.speed 544 | 545 | 546 | if self.log == log_freq-1: 547 | 548 | 549 | ### OLD VERSION 550 | ###################################################################### 551 | """ 552 | 553 | #if coord_fnc == None: coord_fnc = iden 554 | #if speed_fnc == None: speed_fnc = iden 555 | 556 | # add new position for trail 557 | 558 | next_pos = (self.x, self.y) 559 | next_spd = self.avg_speed/log_freq 560 | 561 | 562 | 563 | 564 | if self.trail_cbuff_len != 0: 565 | self.prev_positions.popleft() 566 | self.prev_positions.popleft() 567 | self.prev_positions.popleft() 568 | 569 | self.trail_cbuff_len += 1 570 | 571 | 572 | 573 | 574 | #self.prev_positions.appendleft(0) # dummy data 575 | self.prev_positions.appendleft(next_spd) 576 | self.prev_positions.appendleft(next_pos[1]) 577 | self.prev_positions.appendleft(next_pos[0]) 578 | 579 | self.prev_positions.appendleft(next_spd) 580 | self.prev_positions.appendleft(next_pos[1]) 581 | self.prev_positions.appendleft(next_pos[0]) 582 | 583 | 584 | 585 | 586 | 587 | while self.trail_cbuff_len > self.max_trails: 588 | 589 | self.prev_positions.pop() 590 | self.prev_positions.pop() 591 | self.prev_positions.pop() 592 | 593 | #self.prev_positions.pop() # dummy 594 | 595 | 596 | self.trail_cbuff_len -= 1 597 | """ 598 | ############################################################################## 599 | 600 | 601 | ### NEW VERSION 602 | ###################################################################### 603 | 604 | # add new position for trail 605 | 606 | #next_pos = (self.x, self.y) 607 | new_x = self.x 608 | new_y = self.y 609 | new_s = self.avg_speed/log_freq 610 | 611 | new_pt = True 612 | 613 | if self.prev_x != None: 614 | if self.trail_cbuff_len != 0: 615 | if self.prev_x == new_x and self.prev_y == new_y: 616 | new_pt = False 617 | 618 | 619 | 620 | 621 | self.prev_x = new_x 622 | self.prev_y = new_y 623 | 624 | self.trail_len += 1 625 | 626 | 627 | 628 | if new_pt: 629 | 630 | # append number of frames this position is held 631 | self.p_pos_counter.append(1) 632 | 633 | ''' 634 | # if there is a duplicate entry point, remove it from the queue 635 | if self.trail_cbuff_len > 0: 636 | self.prev_positions.pop() 637 | self.prev_positions.pop() 638 | self.prev_positions.pop() 639 | ''' 640 | 641 | # append new point 642 | self.prev_positions.append(new_x) 643 | self.prev_positions.append(new_y) 644 | self.prev_positions.append(new_s) 645 | 646 | ''' 647 | # append copy of new point so display looks correct (maybe change later?) 648 | self.prev_positions.append(new_x) 649 | self.prev_positions.append(new_y) 650 | self.prev_positions.append(new_s) 651 | ''' 652 | 653 | # increment length of buffer 654 | self.trail_cbuff_len += 1 655 | 656 | 657 | else: 658 | # increment number of frames last position is repeated 659 | self.p_pos_counter[-1] += 1 660 | 661 | 662 | 663 | 664 | while self.trail_len >= self.max_trails: 665 | 666 | end_cnt = self.p_pos_counter[0] - 1 667 | 668 | if end_cnt == 0: 669 | # remove end point from position list 670 | self.prev_positions.popleft() 671 | self.prev_positions.popleft() 672 | self.prev_positions.popleft() 673 | 674 | # remove end count from count list 675 | self.p_pos_counter.popleft() 676 | 677 | # decrease buffer count 678 | self.trail_cbuff_len -= 1 679 | else: 680 | # decrement count of end 681 | self.p_pos_counter[0] = end_cnt 682 | 683 | self.trail_len -= 1 684 | 685 | 686 | ###################################################################### 687 | 688 | self.log += 1 689 | if self.log == log_freq: 690 | self.log = 0 691 | self.avg_speed = 0 692 | 693 | else: 694 | if p_show: self.reset_trail() 695 | 696 | 697 | def display(self, ghost=False, d_method=None): 698 | 699 | if not ghost: 700 | super().display(d_method=d_method) 701 | 702 | 703 | 704 | def iden(x): return x 705 | 706 | 707 | 708 | 709 | #========================================================================================================================= 710 | # 711 | # ITEM CLASS 712 | # 713 | #========================================================================================================================= 714 | 715 | 716 | 717 | 718 | 719 | 720 | class Item(Game_Object): 721 | def __init__(self, x=-100, y=-100, angle=0, obj_name="", surface=None, OBJ_ID="", img_scale=1, address=0): 722 | super().__init__(x, y, angle, obj_name, surface, OBJ_ID, img_scale, address) 723 | 724 | self.vel = (0, 0) 725 | self.dest = (x, y) 726 | self.ch_num = -1 727 | self.is_alive = False 728 | 729 | 730 | 731 | @property 732 | def speed(self): 733 | return (self.vel[0]**2 + self.vel[1]**2)**0.5 734 | 735 | @property 736 | def vel(self): 737 | return self._vel 738 | @vel.setter 739 | def vel(self, _vel): 740 | self._vel = _vel 741 | 742 | 743 | @property 744 | def ch_num(self): 745 | return self._ch_num 746 | @ch_num.setter 747 | def ch_num(self, _ch_num): 748 | try: 749 | self.ch_num 750 | except: 751 | self._ch_num = -1 752 | 753 | if self.ch_num != _ch_num: 754 | self.img = ITEM_NUM_TO_NAME(_ch_num) 755 | 756 | self._ch_num = _ch_num 757 | 758 | @property 759 | def is_alive(self): 760 | return self._is_alive 761 | @is_alive.setter 762 | def is_alive(self, _is_alive): 763 | self._is_alive = _is_alive 764 | 765 | 766 | 767 | 768 | 769 | 770 | def display_on(self, surface=None): 771 | main_surface = self.surface 772 | 773 | if surface == None: 774 | surface = self.surface 775 | 776 | self.surface = surface 777 | self.display() 778 | self.surface = main_surface 779 | 780 | 781 | def display(self, d_method=None): 782 | super().display(d_method=d_method) 783 | 784 | 785 | 786 | 787 | #========================================================================================================================= 788 | # 789 | # OBSTACLE CLASS 790 | # 791 | #========================================================================================================================= 792 | 793 | 794 | 795 | 796 | 797 | class Obstacle(Game_Object): 798 | def __init__(self, x=-100, y=-100, angle=0, obj_name="", surface=None, OBJ_ID="", img_scale=1, address=0): 799 | super().__init__(x, y, angle, obj_name, surface, OBJ_ID, img_scale, address) 800 | 801 | self.vel = (0, 0) 802 | self.dest = (x, y) 803 | self.ch_num = -1 804 | 805 | 806 | 807 | @property 808 | def speed(self): 809 | return (self.vel[0]**2 + self.vel[1]**2)**0.5 810 | 811 | @property 812 | def vel(self): 813 | return self._vel 814 | @vel.setter 815 | def vel(self, _vel): 816 | self._vel = _vel 817 | 818 | 819 | @property 820 | def ch_num(self): 821 | return self._ch_num 822 | @ch_num.setter 823 | def ch_num(self, _ch_num): 824 | try: 825 | self.ch_num 826 | except: 827 | self._ch_num = -1 828 | 829 | if self.ch_num != _ch_num: 830 | #self.img = "pipe" 831 | self.img = OBSTACLE_NUM_TO_NAME(_ch_num) 832 | 833 | self._ch_num = _ch_num 834 | 835 | 836 | 837 | 838 | 839 | 840 | def display_on(self, surface=None): 841 | main_surface = self.surface 842 | 843 | if surface == None: 844 | surface = self.surface 845 | 846 | self.surface = surface 847 | self.display() 848 | self.surface = main_surface 849 | 850 | 851 | def display(self, d_method=None): 852 | super().display(d_method=d_method) 853 | -------------------------------------------------------------------------------- /LuaSide.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | LUA_VERSION_NUMBER = "2.1" 9 | PY_VERSION_NUMBER = "2.1" 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | --------------------------------------------------------------------------------------- 18 | 19 | local socket = require("socket") 20 | 21 | --local host, port = "127.0.0.1", 65432 22 | local host, port = "localhost", 65432 23 | 24 | 25 | 26 | emu.yield() 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | RAM_HIGH_BANK = 0x10000 38 | 39 | memory.usememorydomain("WRAM") 40 | 41 | --console.write(memory.getcurrentmemorydomain()) 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ButtonNames = { 51 | "A", 52 | "B", 53 | "X", 54 | "Y", 55 | "Up", 56 | "Down", 57 | "Left", 58 | "Right", 59 | "L" 60 | } 61 | 62 | 63 | function tokenize(s) 64 | local t = {} 65 | 66 | local from = 1 67 | local d_from, d_to = string.find(s, " ", from) 68 | while d_from do 69 | table.insert(t, string.sub(s, from, d_from-1)) 70 | from = d_to + 1 71 | d_from, d_to = string.find(s, " ", from) 72 | end 73 | table.insert(t, string.sub(s, from)) 74 | 75 | 76 | 77 | return t 78 | end 79 | 80 | 81 | function isEndFlag(data) 82 | if data == nil then 83 | return true 84 | elseif data == "close" then 85 | return true 86 | end 87 | return false 88 | end 89 | 90 | 91 | 92 | 93 | function onExit() 94 | close_socket_client("Client Script Forcibly Stopped") 95 | end 96 | 97 | event.onexit(onExit) 98 | 99 | socket_client = nil 100 | 101 | SOCKET_TIMEOUT = 0.01 102 | CURR_TIMEOUT = 0.01 103 | --RECV_BUFFER = {} 104 | 105 | CLIENT_NAME = "" 106 | --SYNCED = false 107 | 108 | function connect_client() 109 | 110 | socket_client = socket.connect(host, port) 111 | --socket_client:settimeout(0.1, 't') 112 | --socket_client:settimeout(1, 't') 113 | socket_client:settimeout(SOCKET_TIMEOUT) 114 | 115 | end 116 | 117 | 118 | 119 | function socket_client_is_open() 120 | if socket_client == nil then 121 | return false 122 | end 123 | return true 124 | end 125 | 126 | 127 | function close_socket_client(msg) 128 | if socket_client_is_open() then 129 | local ad = socket_client:getpeername() 130 | if CLIENT_NAME ~= "" then ad = CLIENT_NAME end 131 | console.write("\n[INFO] Disconnected from " .. ad .. ".") 132 | if msg ~= "" then 133 | console.write("\n Reason: " .. msg .. "\n") 134 | end 135 | socket_client:send("close\n" .. msg) 136 | socket_client:close() 137 | end 138 | socket_client = nil 139 | end 140 | 141 | 142 | function send_data(data) 143 | if socket_client_is_open() then 144 | --console.log(">>> " .. data .. "\n") 145 | 146 | socket_client:send(data) 147 | end 148 | end 149 | 150 | 151 | function receive_from_server() 152 | if socket_client_is_open() then 153 | local data,err = socket_client:receive('*l') 154 | 155 | if err ~= nil then 156 | if err == "timeout" then 157 | return "SOCKET_ERROR_TIMEOUT" 158 | end 159 | if err ~= "closed" then 160 | console.write("[ERROR] Socket Error: " .. err .. "\n") 161 | end 162 | return err 163 | end 164 | 165 | if data == nil then 166 | close_socket_client("Server sync failed") 167 | return nil 168 | end 169 | 170 | --console.log("<<< " .. data .. "\n") 171 | 172 | return data 173 | end 174 | return nil 175 | end 176 | 177 | 178 | function receive_data() 179 | if socket_client_is_open() then 180 | local data = receive_from_server() 181 | 182 | if data == nil then 183 | close_socket_client("Server sync failed.") 184 | return nil 185 | end 186 | 187 | if data == "close" then 188 | close_socket_client("Server was forcibly closed.") 189 | return nil 190 | end 191 | 192 | 193 | 194 | if data == "SOCKET_ERROR_TIMEOUT" then 195 | return data 196 | end 197 | 198 | return data 199 | 200 | end 201 | return nil 202 | end 203 | 204 | 205 | 206 | function sync_with_server() 207 | if socket_client_is_open() then 208 | --socket_client:settimeout(nil) 209 | socket_client:settimeout(0.01) 210 | send_data("sync\n") 211 | 212 | local FAILURES = 0 213 | local data = "SOCKET_ERROR_TIMEOUT" 214 | 215 | while data == "SOCKET_ERROR_TIMEOUT" do 216 | data = receive_from_server() 217 | FAILURES = FAILURES + 1 218 | if data == "SOCKET_ERROR_TIMEOUT" then 219 | emu.frameadvance(); 220 | if FAILURES > 1000 then 221 | break 222 | end 223 | end 224 | end 225 | 226 | --local data = receive_from_server() 227 | socket_client:settimeout(SOCKET_TIMEOUT) 228 | 229 | --if data == true then return true 230 | 231 | if data == nil then 232 | return false 233 | end 234 | 235 | if data == "ack" then 236 | --console.write("recieved syn ack") 237 | return true 238 | end 239 | 240 | if data == "closed" or data == "close" then 241 | close_socket_client("Server was forcibly closed.") 242 | return false 243 | end 244 | 245 | if data == "SOCKET_ERROR_TIMEOUT" then 246 | close_socket_client("Server sync failed.") 247 | return false 248 | end 249 | 250 | 251 | -- test this to make sure it makes sense 252 | console.write("\n[WARNING] SYNC RECEIVED: " .. data .. "\n") 253 | return true 254 | end 255 | return false 256 | end 257 | 258 | 259 | 260 | 261 | 262 | function readMemoryBlock(domain, start, block_size) 263 | local return_domain = memory.getcurrentmemorydomain() 264 | 265 | local block = {} 266 | 267 | memory.usememorydomain(domain) 268 | 269 | local bts = memory.readbyterange(start, block_size) 270 | 271 | for i=0,block_size-1 do 272 | block[i+1] = string.format("%02x", bts[i]) 273 | end 274 | 275 | memory.usememorydomain(return_domain) 276 | 277 | return table.concat(block) 278 | 279 | end 280 | 281 | 282 | function readMemoryBlock_skipped(domain, start, block_size, skip_freq) 283 | 284 | local return_domain = memory.getcurrentmemorydomain() 285 | 286 | local block = {} 287 | 288 | memory.usememorydomain(domain) 289 | 290 | local bts = memory.readbyterange(start, block_size*skip_freq) 291 | 292 | for i=0,block_size-1 do 293 | block[i+1] = string.format("%02x", bts[i*skip_freq]) 294 | end 295 | 296 | 297 | memory.usememorydomain(return_domain) 298 | 299 | return table.concat(block) 300 | 301 | end 302 | 303 | 304 | 305 | function READ_BYTE(addr) 306 | 307 | return memory.readbyte(addr) 308 | 309 | end 310 | 311 | function WRITE_BYTE(addr, val) 312 | 313 | return memory.writebyte(addr, val) 314 | 315 | end 316 | 317 | function READ_WORD(addr) 318 | 319 | return READ_BYTE(addr) + 256*READ_BYTE(addr+1) 320 | 321 | end 322 | 323 | function WRITE_WORD(addr, val) 324 | WRITE_BYTE(addr, val % 256) 325 | WRITE_BYTE(addr+1, (val / 256) % 256) 326 | end 327 | 328 | local FRAME_COUNT = 2 329 | 330 | 331 | joypad_buttons = nil 332 | p_joypad = nil 333 | 334 | emu.frameadvance(); 335 | 336 | track_num = 0xff 337 | 338 | --readMap() 339 | 340 | prev_open = false 341 | 342 | local player = 1 343 | 344 | local currentJoypad = joypad.get(player) 345 | 346 | local frame = 0 347 | 348 | 349 | local P1_VRAM_A = -1 350 | local P1_VRAM_B = -1 351 | local P1_VRAM_C = -1 352 | local P1_VRAM_D = -1 353 | local P2_VRAM_A = -1 354 | local P2_VRAM_B = -1 355 | local P2_VRAM_C = -1 356 | local P2_VRAM_D = -1 357 | 358 | local END_TRANSMISSION = false 359 | 360 | 361 | 362 | local base_item_addresses_gp = {0x1a00, 0x1a80} 363 | local base_item_addresses_mr = {0x1a00, 0x1a80, 0x1b00, 0x1b80, 0x1438, 0x1538, 0x1638, 0x1738} 364 | local base_item_addresses_tt = {} 365 | local base_item_addresses_bt = {0x1a00, 0x1a80, 0x1b00, 0x1b80, 0x1438, 0x1538, 0x1638, 0x1738} 366 | 367 | local base_item_addresses = {base_item_addresses_gp, base_item_addresses_mr, base_item_addresses_tt, base_item_addresses_bt} 368 | 369 | 370 | local base_obj_addresses_gp = {0x1800, 0x1840, 0x1880, 0x18c0, 0x1900, 0x1940, 0x1980, 0x19c0} 371 | local base_obj_addresses_mr = {0x1800, 0x1840, 0x1880, 0x18c0, 0x1900, 0x1940, 0x1980, 0x19c0} 372 | local base_obj_addresses_tt = {0x1800, 0x1840, 0x1880, 0x18c0, 0x1900, 0x1940, 0x1980, 0x19c0} 373 | local base_obj_addresses_bt = {0x1800, 0x1880, 0x1900, 0x1980, 0x1238, 0x1338, 0x1940, 0x19c0} -- last two are dummy 374 | 375 | local base_obj_addresses = {base_obj_addresses_gp, base_obj_addresses_mr, base_obj_addresses_tt, base_obj_addresses_bt} 376 | 377 | 378 | local REASON = "" 379 | 380 | SOCKET_DATA = " " 381 | 382 | 383 | local prev_map = {} 384 | for i=1,0x4000 do 385 | table.insert(prev_map, -1) 386 | end 387 | 388 | local check_map_updates = false 389 | 390 | local paused = false 391 | 392 | 393 | console.clear() 394 | console.write("=======\n") 395 | console.write("Welcome to BooView's LuaSide (v" .. LUA_VERSION_NUMBER .. ") by MrL314!\n") 396 | console.write("=======") 397 | console.write("\n\n[INFO] Attempting to connect...") 398 | emu.frameadvance() 399 | 400 | 401 | if pcall(connect_client) then 402 | 403 | END_TRANSMISSION = false 404 | 405 | console.clear() 406 | console.write("=======\n") 407 | console.write("Welcome to BooView's LuaSide (v" .. LUA_VERSION_NUMBER .. ") by MrL314!\n") 408 | console.write("=======") 409 | 410 | 411 | local SYNCED = sync_with_server() 412 | 413 | if SYNCED == false then 414 | close_socket_client("Failed to connect to BooView.") 415 | END_TRANSMISSION = true 416 | end 417 | 418 | 419 | 420 | 421 | while socket_client_is_open() do 422 | 423 | 424 | --local tmt = socket_client:gettimeout() 425 | socket_client:settimeout(CURR_TIMEOUT) 426 | local data = receive_data() 427 | if socket_client_is_open() then socket_client:settimeout(SOCKET_TIMEOUT) end 428 | 429 | 430 | 431 | if isEndFlag(data) then 432 | END_TRANSMISSION = true 433 | REASON = "END FLAG" 434 | break 435 | end 436 | 437 | 438 | 439 | if string.sub(data, 1, 5) == "FRAME" then 440 | --sync 441 | 442 | 443 | 444 | 445 | 446 | 447 | if frame >= 2 then 448 | frame = 0 449 | end 450 | 451 | 452 | 453 | 454 | race_checkpoints = READ_BYTE(0x148) 455 | 456 | track_num = READ_BYTE(0x124) 457 | 458 | --local x_pos = READ_WORD(0x1018) * 4 459 | --local y_pos = READ_WORD(0x101c) * 4 460 | --local heading = READ_BYTE(0x102b) 461 | --local camera = READ_BYTE(0x0095) 462 | 463 | local game_type = READ_BYTE(0x2c) 464 | 465 | currentJoypad = joypad.get(player) 466 | 467 | 468 | 469 | 470 | 471 | -- send data to server 472 | character_bytes = "CH_DATA" 473 | 474 | -- gamemode 475 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x36)) 476 | 477 | -- camera mode 478 | local cm = READ_BYTE(0x2e) 479 | character_bytes = character_bytes .. string.format("%02x", cm) 480 | 481 | 482 | 483 | -- ghost enable 484 | g_enable = READ_BYTE(RAM_HIGH_BANK + 0xff02) 485 | character_bytes = character_bytes .. string.format("%02x", g_enable) 486 | 487 | if g_enable == 0 then 488 | if cm == 2 then 489 | if READ_BYTE(0x11c1) > 0x84 then 490 | g_enable = 2 491 | end 492 | elseif cm == 4 then 493 | if READ_BYTE(0x10c1) > 0x84 then 494 | g_enable = 2 495 | end 496 | end 497 | end 498 | 499 | character_bytes = character_bytes .. string.format("%02x", g_enable) 500 | 501 | 502 | -- in demo? 503 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0xe32)) 504 | 505 | -- game type 506 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x2C)) 507 | 508 | -- track number 509 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x124)) 510 | 511 | -- track theme 512 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x126)) 513 | 514 | -- theme object 515 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0xe30)) 516 | 517 | -- camera angle 518 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x95)) 519 | 520 | -- camera pos x 521 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x4c)) 522 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x4d)) 523 | 524 | -- camera pos y 525 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x52)) 526 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x53)) 527 | 528 | 529 | 530 | for i=0, 7 do 531 | 532 | -- character number 533 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x12)) 534 | 535 | 536 | -- x coord low 537 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x16)) 538 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x17)) 539 | -- x coord high 540 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x18)) 541 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x19)) 542 | -- y coord low 543 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x1a)) 544 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x1b)) 545 | -- y coord high 546 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x1c)) 547 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x1d)) 548 | -- z coord low 549 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x1e)) 550 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x1f)) 551 | -- z coord high 552 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x20)) 553 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x21)) 554 | 555 | -- x velocity 556 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x22)) 557 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x23)) 558 | -- y velocity 559 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x24)) 560 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x25)) 561 | -- z velocity 562 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x26)) 563 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x27)) 564 | 565 | -- speed 566 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xea)) 567 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xeb)) 568 | 569 | -- max speed 570 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xd6)) 571 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xd7)) 572 | 573 | -- acceleration 574 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xee)) 575 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xef)) 576 | 577 | 578 | 579 | -- heading angle 580 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x2a)) 581 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x2b)) 582 | 583 | -- momentum angle 584 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xa2)) 585 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xa3)) 586 | 587 | -- camera angle 588 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xa4)) 589 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xa5)) 590 | 591 | -- angular velocity 592 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xb2)) 593 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0xb3)) 594 | 595 | 596 | 597 | 598 | 599 | if race_checkpoints ~= 0 then 600 | racer_cp = READ_BYTE(0x1000 + (i*0x100) + 0xc0) 601 | table_index = 2 * ((racer_cp)%race_checkpoints) 602 | else 603 | table_index = 0 604 | end 605 | 606 | 607 | -- checkpoint x position 608 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x900 + table_index)) 609 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0x901 + table_index)) 610 | -- checkpoint y position 611 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0xa00 + table_index)) 612 | character_bytes = character_bytes .. string.format("%02x", READ_BYTE(0xa01 + table_index)) 613 | end 614 | 615 | 616 | 617 | --send_data(character_bytes .. "\n") 618 | 619 | --[[ 620 | if receive_data() ~= "received data" then 621 | close_socket_client("No response on ch data") 622 | END_TRANSMISSION = true 623 | break 624 | end 625 | --]] 626 | 627 | --[[send_data(" ") 628 | if receive_data() ~= "received data" then 629 | close_socket_client() 630 | END_TRANSMISSION = true 631 | break 632 | end 633 | 634 | --]] 635 | 636 | obj_bytes = "" 637 | local base_address = 0 638 | 639 | --obstacles 640 | for i=0, 7 do 641 | 642 | -- character number 643 | ---obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(0x1800 + (i*0x40) + 0x12)) 644 | 645 | base_address = base_obj_addresses[game_type/2 + 1][i + 1] 646 | 647 | --console.write("\nObject " .. i .. " : " .. string.format("%02x", (base_address / 256) % 256) .. string.format("%02x", (base_address % 256))) 648 | 649 | -- obj address 650 | obj_bytes = obj_bytes .. string.format("%02x", (base_address % 256)) 651 | obj_bytes = obj_bytes .. string.format("%02x", (base_address / 256) % 256) 652 | 653 | -- x coord low 654 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x16)) 655 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x17)) 656 | -- x coord high 657 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x18)) 658 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x19)) 659 | -- y coord low 660 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1a)) 661 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1b)) 662 | -- y coord high 663 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1c)) 664 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1d)) 665 | -- z coord low 666 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1e)) 667 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1f)) 668 | -- z coord high 669 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x20)) 670 | obj_bytes = obj_bytes .. string.format("%02x", READ_BYTE(base_address + 0x21)) 671 | 672 | end 673 | 674 | 675 | 676 | -- items 677 | local item_bytes = "" 678 | 679 | 680 | for i=0, 7 do 681 | 682 | if (game_type == 0 and i < 2) or (game_type ~= 0 and game_type ~= 4) then 683 | 684 | base_address = base_item_addresses[game_type/2 + 1][i + 1] 685 | 686 | -- obj address 687 | item_bytes = item_bytes .. string.format("%02x", (base_address % 256)) 688 | item_bytes = item_bytes .. string.format("%02x", (base_address / 256) % 256) 689 | 690 | 691 | -- is alive 692 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x13)) 693 | 694 | 695 | -- character number 696 | ch_num = READ_BYTE(base_address + 0x70) 697 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x70)) 698 | 699 | 700 | user = READ_BYTE(base_address + 0x6b) 701 | 702 | if user == 0 then 703 | user = 0x10 704 | end 705 | 706 | --console.write("\naddr " .. string.format("%04x", base_address) .. " val " .. string.format("%02x", ch_num) .. " user " .. string.format("%02x", user) .. "\n") 707 | 708 | user_ch = READ_BYTE(user*256 + 0x12) 709 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(user*256 + 0x12)) 710 | 711 | -- x coord low 712 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x16)) 713 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x17)) 714 | -- x coord high 715 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x18)) 716 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x19)) 717 | -- y coord low 718 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1a)) 719 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1b)) 720 | -- y coord high 721 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1c)) 722 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1d)) 723 | -- z coord low 724 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1e)) 725 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x1f)) 726 | -- z coord high 727 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x20)) 728 | item_bytes = item_bytes .. string.format("%02x", READ_BYTE(base_address + 0x21)) 729 | 730 | -- heading angle 731 | --item_bytes = item_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x2b)) 732 | 733 | -- x velocity 734 | --item_bytes = item_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x22)) 735 | --item_bytes = item_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x23)) 736 | -- y velocity 737 | --item_bytes = item_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x24)) 738 | --item_bytes = item_bytes .. string.format("%02x", READ_BYTE(0x1000 + (i*0x100) + 0x25)) 739 | end 740 | 741 | end 742 | 743 | 744 | 745 | 746 | -- map updating 747 | 748 | map_updates = "" 749 | 750 | 751 | if check_map_updates == false then 752 | --console.write("\nMAP RESET") 753 | 754 | for i=1,0x4000 do 755 | local tile = READ_BYTE(RAM_HIGH_BANK + i-1) 756 | prev_map[i] = tile 757 | end 758 | 759 | else 760 | 761 | 762 | local SENSING_RADIUS = 5 --3 763 | 764 | for i=1,8 do 765 | 766 | local TILE_IND = 0 767 | 768 | local ch_offs = 0x1000 + (i-1)*0x100 769 | 770 | local tile_ind = READ_WORD(ch_offs + 0x58) 771 | 772 | 773 | for xoff= -1 * (SENSING_RADIUS-1), (SENSING_RADIUS-1) do 774 | for yoff=-1 * (SENSING_RADIUS-1), (SENSING_RADIUS-1) do 775 | 776 | local TILE_IND = (tile_ind + xoff) + (yoff * 128) 777 | 778 | if TILE_IND >= 0 and TILE_IND < 0x4000 then 779 | 780 | local tile = READ_BYTE(RAM_HIGH_BANK + TILE_IND) 781 | 782 | if tile ~= prev_map[TILE_IND + 1] then 783 | 784 | if check_map_updates == true then 785 | 786 | local map_ind = TILE_IND 787 | 788 | map_updates = map_updates .. string.format("%02x", map_ind % 256) 789 | map_updates = map_updates .. string.format("%02x", (map_ind / 256) % 256) 790 | map_updates = map_updates .. string.format("%02x", tile) 791 | 792 | end 793 | 794 | prev_map[TILE_IND + 1] = tile 795 | end 796 | 797 | end 798 | 799 | end 800 | end 801 | 802 | end 803 | 804 | end 805 | 806 | check_map_updates = true 807 | 808 | 809 | 810 | 811 | 812 | 813 | local SYNCED = sync_with_server() 814 | 815 | if SYNCED == false then 816 | REASON = "BAD SYNC" 817 | END_TRANSMISSION = true 818 | break 819 | --emu.frameadvance() 820 | end 821 | 822 | 823 | 824 | 825 | send_data(character_bytes .. "\n" .. obj_bytes .. "\n" .. item_bytes .. "\n" .. map_updates .. "\n") 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | emu.frameadvance(); 836 | frame = frame + 1 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | elseif string.sub(data, 1, 9) == "GHOST_OFF" then 847 | 848 | WRITE_WORD(0x1012, P1_VRAM_A) 849 | WRITE_WORD(0x1014, P1_VRAM_B) 850 | WRITE_WORD(0x10B6, P1_VRAM_C) 851 | WRITE_WORD(0x10B8, P1_VRAM_D) 852 | WRITE_WORD(0x1112, P2_VRAM_A) 853 | WRITE_WORD(0x1114, P2_VRAM_B) 854 | WRITE_WORD(0x11B6, P2_VRAM_C) 855 | WRITE_WORD(0x11B8, P2_VRAM_D) 856 | 857 | P1_VRAM_A = -1 858 | P1_VRAM_B = -1 859 | P1_VRAM_C = -1 860 | P1_VRAM_D = -1 861 | P2_VRAM_A = -1 862 | P2_VRAM_B = -1 863 | P2_VRAM_C = -1 864 | P2_VRAM_D = -1 865 | 866 | 867 | 868 | elseif string.sub(data, 1, 8) == "DO_GHOST" then 869 | 870 | if P1_VRAM_A == -1 then 871 | P1_VRAM_A = READ_WORD(0x1012) 872 | P1_VRAM_B = READ_WORD(0x1014) 873 | P1_VRAM_C = READ_WORD(0x10B6) 874 | P1_VRAM_D = READ_WORD(0x10B8) 875 | P2_VRAM_A = READ_WORD(0x1112) 876 | P2_VRAM_B = READ_WORD(0x1114) 877 | P2_VRAM_C = READ_WORD(0x11B6) 878 | P2_VRAM_D = READ_WORD(0x11B8) 879 | end 880 | 881 | player = 0x1000 882 | ghost = 0x1100 883 | 884 | disp_mode = READ_BYTE(0x2e) 885 | 886 | if disp_mode == 2 then 887 | player = 0x1000 888 | ghost = 0x1100 889 | elseif disp_mode == 4 then 890 | player = 0x1100 891 | ghost = 0x1000 892 | end 893 | 894 | nonce = 0 895 | 896 | if READ_BYTE(ghost + 0xc1) < 0x85 then 897 | for i=0x00,0xff do 898 | 899 | local no_write = false 900 | 901 | if i == -1 then 902 | no_write = true 903 | 904 | elseif i >= 0x00 and i <= 0x0f then 905 | no_write = true 906 | 907 | --[[ 908 | elseif i == 0x00 or i == 0x01 then 909 | nonce = 0 -- helps with top screen ghosting 910 | elseif i == 0x02 or i == 0x03 then 911 | nonce = 0 -- NEED! means top screen not ghosty 912 | 913 | elseif i == 0x04 or i == 0x05 then 914 | nonce = 0 -- NEED! means top screen not ghosty 915 | 916 | elseif i == 0x06 or i == 0x07 then 917 | nonce = 0 -- NEED! means top screen not ghosty 918 | 919 | elseif i == 0x08 or i == 0x09 then 920 | nonce = 0 -- fixes crash on exit 921 | 922 | 923 | elseif i == 0x0a or i == 0x0b then 924 | nonce = 0 -- NEED! Fixes bottom screen issue 925 | 926 | 927 | elseif i == 0x0c or i == 0x0d then 928 | nonce = 0 -- NEED! Fixes bottom screen issue 929 | 930 | elseif i == 0x0e or i == 0x0f then 931 | nonce = 0 -- NEED! Fixes bottom screen issue 932 | --]] 933 | 934 | --[[ 935 | elseif i >= 0x30 and i < 0x40 then 936 | nonce = 0 -- is this needed? 937 | 938 | ---- onto other data 939 | --]] 940 | --[[ 941 | elseif i >= 0xb6 and i <= 0xbd then 942 | nonce = 0 -- NEED? Fixes VRAM stuff? 943 | 944 | elseif i >= 0x70 and i <= 0x7f then 945 | nonce = 0 -- NEED? Fixes VRAM stuff? 946 | --]] 947 | 948 | 949 | 950 | 951 | elseif i == 0xd5 then 952 | local flags = READ_BYTE(ghost + i) 953 | 954 | if flags % 2 == 1 then flags = flags - 1 end 955 | 956 | WRITE_BYTE(player + i, flags) 957 | no_write = true 958 | end 959 | 960 | 961 | if no_write == false then 962 | WRITE_BYTE(player + i, READ_BYTE(ghost + i)) 963 | end 964 | 965 | 966 | end 967 | end 968 | 969 | 970 | 971 | 972 | --WRITE_BYTE(0x94, READ_BYTE(ghost + 0xa4)) 973 | --WRITE_BYTE(0x95, READ_BYTE(ghost + 0xa5)) 974 | 975 | 976 | 977 | elseif string.sub(data, 1, 7) == "OVERLAY" then 978 | 979 | 980 | overlay_data = "" 981 | 982 | num_tiles = 0 983 | 984 | for i=0,0x3fff do 985 | local tile = READ_BYTE(RAM_HIGH_BANK + i) 986 | if tile >= 0xc0 then 987 | overlay_data = overlay_data .. string.format("%02x", i % 256) 988 | overlay_data = overlay_data .. string.format("%02x", (i / 256) % 256) 989 | overlay_data = overlay_data .. string.format("%02x", tile) 990 | num_tiles = num_tiles + 1 991 | end 992 | end 993 | 994 | overlay_data = string.format("%02x", num_tiles % 256) .. string.format("%02x", (num_tiles / 256) % 256) .. overlay_data 995 | 996 | send_data(overlay_data .. "\n") 997 | 998 | emu.frameadvance(); 999 | frame = frame + 1 1000 | 1001 | 1002 | 1003 | 1004 | elseif string.sub(data, 1, 7) == "W_BYTES" then 1005 | 1006 | -- write bytes instructions 1007 | 1008 | data = string.sub(data, 9, #data) 1009 | 1010 | wasBad = false 1011 | 1012 | local toks = tokenize(data) 1013 | 1014 | 1015 | local instr = {} 1016 | 1017 | --console.write(toks) 1018 | local i = 1 1019 | while i <= #toks do 1020 | 1021 | TOK = toks[i] 1022 | 1023 | if TOK == "addr" then 1024 | if i+1 <= #toks then 1025 | instr["addr"] = tonumber(toks[i+1]) 1026 | i = i + 1 1027 | else 1028 | console.write("[ERROR] W_BYTES Error: no address specified\n") 1029 | wasBad = true 1030 | break 1031 | end 1032 | elseif TOK == "bytes" then 1033 | local W_BYTES = {} 1034 | if i+1 <= #toks then 1035 | while i+1 <= #toks do 1036 | table.insert(W_BYTES, tonumber(toks[i+1])) 1037 | i = i + 1 1038 | end 1039 | instr["data"] = W_BYTES 1040 | else 1041 | console.write("[ERROR] W_BYTES Error: no data specified\n") 1042 | wasBad = true 1043 | break 1044 | end 1045 | else 1046 | console.write("[ERROR] W_BYTES Error: cannot understand " .. TOK .. "\n") 1047 | wasBad = true 1048 | end 1049 | i = i + 1 1050 | end 1051 | 1052 | 1053 | 1054 | 1055 | if wasBad == true then 1056 | END_TRANSMISSION = true 1057 | break 1058 | end 1059 | 1060 | --console.write("Writing to " .. instr["addr"] .. "\n") 1061 | 1062 | for i=1, #instr["data"] do 1063 | 1064 | WRITE_BYTE(instr["addr"] + i - 1, instr["data"][i]) 1065 | 1066 | end 1067 | 1068 | 1069 | if wasBad == true then 1070 | END_TRANSMISSION = true 1071 | REASON = "wasBad" 1072 | end 1073 | 1074 | 1075 | elseif string.sub(data, 1, 7) == "R_BYTES" then 1076 | 1077 | -- request bytes data 1078 | frame = frame 1079 | 1080 | 1081 | 1082 | elseif string.sub(data, 1, 5) == "YIELD" then 1083 | CURR_TIMEOUT = 0.01 1084 | 1085 | paused = true 1086 | 1087 | 1088 | 1089 | 1090 | elseif string.sub(data, 1, 7) == "UNYIELD" then 1091 | 1092 | --CURR_TIMEOUT = nil 1093 | CURR_TIMEOUT = 0.01 1094 | paused = false 1095 | client.unpause() 1096 | 1097 | 1098 | elseif string.sub(data, 1, 5) == "PAUSE" then 1099 | CURR_TIMEOUT = 0.01 1100 | 1101 | paused = true 1102 | --client.SetSoundOn(false) 1103 | --emu.frameadvance(); 1104 | --client.pause() 1105 | --emu.yield(); 1106 | 1107 | console.write("\n[INFO] Paused\n") 1108 | 1109 | 1110 | --socket_client:settimeout(0.01) 1111 | --while string.sub(receive_data(), 1, 7) ~= "UNPAUSE" do 1112 | -- frame = frame + 1 1113 | -- emu.frameadvance(); 1114 | --end 1115 | --socket_client:settimeout(nil) 1116 | 1117 | 1118 | 1119 | 1120 | elseif string.sub(data, 1, 7) == "UNPAUSE" then 1121 | 1122 | CURR_TIMEOUT = 0.01 1123 | paused = false 1124 | client.unpause() 1125 | console.write("[INFO] Unpaused\n") 1126 | 1127 | 1128 | 1129 | 1130 | elseif string.sub(data, 1, 6) == "W_SRAM" then 1131 | 1132 | data = string.sub(data, 8, #data) 1133 | 1134 | 1135 | local toks = tokenize(data) 1136 | 1137 | 1138 | 1139 | if not (#toks == 1 and toks[1] == "") then 1140 | memory.usememorydomain("CARTRAM") 1141 | 1142 | for i=0,#toks-1 do 1143 | WRITE_BYTE(i, tonumber(toks[i+1])) 1144 | end 1145 | 1146 | memory.usememorydomain("WRAM") 1147 | end 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | elseif string.sub(data, 1, 5) == "TRACK" then 1156 | 1157 | emu.frameadvance(); 1158 | frame = frame + 1 1159 | 1160 | local track_data = "" 1161 | local nonce = "" 1162 | 1163 | for i=1,8 do 1164 | track_data = readMemoryBlock("WRAM", RAM_HIGH_BANK + ((i-1)*0x800), 0x800) 1165 | send_data(track_data .. "\n") 1166 | nonce = receive_data() 1167 | 1168 | emu.frameadvance(); 1169 | frame = frame + 1 1170 | end 1171 | 1172 | 1173 | 1174 | elseif string.sub(data, 1, 4) == "ZONE" then 1175 | 1176 | emu.frameadvance(); 1177 | frame = frame + 1 1178 | 1179 | local zone_data = "" 1180 | local nonce = "" 1181 | 1182 | for i=1,2 do 1183 | zone_data = readMemoryBlock("WRAM", RAM_HIGH_BANK + 0x5000 + (i-1)*0x800, 0x800) 1184 | send_data(zone_data .. "\n") 1185 | nonce = receive_data() 1186 | 1187 | emu.frameadvance(); 1188 | frame = frame + 1 1189 | end 1190 | 1191 | 1192 | elseif string.sub(data, 1, 4) == "FLOW" then 1193 | 1194 | emu.frameadvance(); 1195 | frame = frame + 1 1196 | 1197 | local flow_data = "" 1198 | local nonce = "" 1199 | 1200 | for i=1,2 do 1201 | flow_data = readMemoryBlock("WRAM", RAM_HIGH_BANK + 0x4000 + (i-1)*0x800, 0x800) 1202 | send_data(flow_data .. "\n") 1203 | nonce = receive_data() 1204 | 1205 | emu.frameadvance(); 1206 | frame = frame + 1 1207 | end 1208 | 1209 | 1210 | 1211 | elseif string.sub(data, 1, 7) == "PALETTE" then 1212 | 1213 | emu.frameadvance(); 1214 | frame = frame + 1 1215 | 1216 | local palette_data = readMemoryBlock("WRAM", 0x3a80, 0x200) 1217 | 1218 | send_data(palette_data .. "\n") 1219 | emu.frameadvance(); 1220 | frame = frame + 1 1221 | 1222 | 1223 | elseif string.sub(data, 1, 5) == "TILES" then 1224 | 1225 | emu.frameadvance(); 1226 | frame = frame + 1 1227 | 1228 | local tile_data = "" 1229 | local nonce = "" 1230 | 1231 | 1232 | for i=1,8 do 1233 | tile_data = readMemoryBlock_skipped("VRAM", ((i-1) * 0x1000) + 1, 0x800, 2) 1234 | send_data(tile_data .. "\n") 1235 | nonce = receive_data() 1236 | 1237 | emu.frameadvance(); 1238 | frame = frame + 1 1239 | end 1240 | 1241 | 1242 | elseif string.sub(data, 1, 7) == "CP_DATA" then 1243 | 1244 | emu.frameadvance(); 1245 | frame = frame + 1 1246 | 1247 | local cp_data = readMemoryBlock("WRAM", 0x800, 0x100) 1248 | send_data(cp_data .. "\n") 1249 | 1250 | emu.frameadvance(); 1251 | frame = frame + 1 1252 | 1253 | 1254 | 1255 | elseif string.sub(data, 1, 9) == "RESET_MAP" then 1256 | 1257 | emu.frameadvance(); 1258 | frame = frame + 1 1259 | 1260 | local return_domain = memory.getcurrentmemorydomain() 1261 | memory.usememorydomain("WRAM") 1262 | 1263 | local i = 1 1264 | for f=1,8 do 1265 | for k=1,0x800 do 1266 | local tile = READ_BYTE(RAM_HIGH_BANK + i-1) 1267 | prev_map[i] = tile 1268 | i = i + 1 1269 | end 1270 | emu.frameadvance(); 1271 | frame = frame + 1 1272 | end 1273 | 1274 | memory.usememorydomain(return_domain) 1275 | 1276 | 1277 | 1278 | check_map_updates = false 1279 | 1280 | send_data("nonce\n") 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | elseif string.sub(data, 1, 10) == "UPDATE_MAP" then 1288 | --[[ 1289 | 1290 | 1291 | 1292 | 1293 | local return_domain = memory.getcurrentmemorydomain() 1294 | memory.usememorydomain("WRAM") 1295 | 1296 | 1297 | map_updates = "" 1298 | 1299 | local SENSING_RADIUS = 10 --3 1300 | 1301 | for i=1,8 do 1302 | 1303 | local TILE_IND = 0 1304 | 1305 | local ch_offs = 0x1000 + (i-1)*0x100 1306 | 1307 | 1308 | 1309 | local tile_ind = READ_WORD(ch_offs + 0x58) 1310 | 1311 | 1312 | for xoff= -1 * (SENSING_RADIUS-1), (SENSING_RADIUS-1) do 1313 | for yoff=-1 * (SENSING_RADIUS-1), (SENSING_RADIUS-1) do 1314 | 1315 | local TILE_IND = (tile_ind + xoff) + (yoff * 128) 1316 | 1317 | if TILE_IND >= 0 and TILE_IND < 0x4000 then 1318 | 1319 | local tile = READ_BYTE(RAM_HIGH_BANK + TILE_IND) 1320 | 1321 | if tile ~= prev_map[TILE_IND + 1] then 1322 | 1323 | if check_map_updates == true then 1324 | 1325 | --console.write(string.format("%04x", TILE_IND) .. " " .. string.format("%02x", prev_map[TILE_IND]) .. " " .. string.format("%02x", tile) .. "\n") 1326 | 1327 | local map_ind = TILE_IND 1328 | 1329 | map_updates = map_updates .. string.format("%02x", map_ind % 256) 1330 | map_updates = map_updates .. string.format("%02x", (map_ind / 256) % 256) 1331 | map_updates = map_updates .. string.format("%02x", tile) 1332 | 1333 | end 1334 | 1335 | prev_map[TILE_IND + 1] = tile 1336 | end 1337 | 1338 | end 1339 | 1340 | end 1341 | end 1342 | 1343 | end 1344 | 1345 | check_map_updates = true 1346 | 1347 | 1348 | 1349 | memory.usememorydomain(return_domain) 1350 | 1351 | send_data(map_updates .. "\n") 1352 | --]] 1353 | 1354 | 1355 | 1356 | 1357 | elseif string.sub(data, 1, 8) == "ID_BVPY_" then 1358 | 1359 | local num_id = string.sub(data, 9) 1360 | 1361 | 1362 | send_data("ID_BVLUA_" .. LUA_VERSION_NUMBER .. "\n") 1363 | 1364 | 1365 | if num_id ~= PY_VERSION_NUMBER then 1366 | -- bad data, close off 1367 | END_TRANSMISSION = true 1368 | REASON = "Cannot identify valid BooView script. Please make sure you are using version " .. LUA_VERSION_NUMBER .. " of BooView." 1369 | break 1370 | else 1371 | CLIENT_NAME = socket_client:getpeername() .. " (" .. data .. ")" 1372 | console.write("\n\n[INFO] ID: ID_BVLUA_" .. LUA_VERSION_NUMBER .. "\n[INFO] Connected to " .. CLIENT_NAME) 1373 | end 1374 | 1375 | 1376 | 1377 | 1378 | elseif data == "SOCKET_ERROR_TIMEOUT" then 1379 | --console.write("timeout") 1380 | 1381 | 1382 | --close_socket_client("Client Timed Out") 1383 | --END_TRANSMISSION = true 1384 | --break 1385 | frame = frame -- dummy operation 1386 | -- TODO is this correct??? 1387 | 1388 | if CURR_TIMEOUT ~= nil then 1389 | emu.frameadvance(); 1390 | frame = frame + 1 1391 | end 1392 | 1393 | 1394 | elseif data == "received data" then 1395 | 1396 | -- 1397 | frame = frame -- dummy operation 1398 | 1399 | elseif data == "closed" then 1400 | close_socket_client("Server was forcibly closed.") 1401 | END_TRANSMISSION = true 1402 | 1403 | elseif data == "ack" then 1404 | 1405 | 1406 | else 1407 | -- bad data, close off 1408 | END_TRANSMISSION = true 1409 | REASON = "BAD DATA" 1410 | console.write("[ERROR] BAD DATA\n" .. data .. "\n") 1411 | break 1412 | end 1413 | 1414 | 1415 | if END_TRANSMISSION == true then 1416 | break 1417 | end 1418 | 1419 | 1420 | end 1421 | 1422 | 1423 | if socket_client ~= nil then 1424 | close_socket_client(REASON) 1425 | end 1426 | socket_client = nil 1427 | 1428 | 1429 | 1430 | else 1431 | console.write("\n[INFO] Failed to connect to BooView socket. Please make sure to run BooView first!") 1432 | 1433 | socket_client = nil 1434 | end 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | 1442 | 1443 | -------------------------------------------------------------------------------- /BooView.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" 4 | 5 | import math, pygame, sys, json, OpenGL, numpy 6 | from pygame.gfxdraw import * 7 | from pygame.locals import * 8 | 9 | from OpenGL.GL import * 10 | from OpenGL.GL import shaders 11 | from OpenGL.GLU import * 12 | 13 | from ctypes import * 14 | 15 | import socket 16 | 17 | import math 18 | import errno 19 | import time 20 | 21 | from collections import deque 22 | 23 | 24 | if False: 25 | import pygame._view 26 | 27 | #pygame.init() 28 | #pygame.display.set_mode((1, 1), pygame.NOFRAME) 29 | import Assets 30 | #pygame.quit() 31 | 32 | 33 | 34 | import Objects 35 | import KartScreen as KS 36 | 37 | import TrackHelper 38 | 39 | import BVmain 40 | 41 | 42 | 43 | import threading 44 | 45 | 46 | 47 | import tkinter as tk 48 | from tkinter import * 49 | from tkinter import filedialog 50 | 51 | import platform 52 | 53 | 54 | 55 | import threading 56 | 57 | import traceback 58 | 59 | 60 | 61 | 62 | #HOST = '127.0.0.1' 63 | HOST = 'localhost' 64 | PORT = 65432 65 | 66 | SOCKET_CONN = None 67 | 68 | 69 | CURR_TIME = 0 70 | 71 | 72 | 73 | with open('config.json', 'r') as f: 74 | config = json.load(f) 75 | 76 | 77 | class character_bytes(object): 78 | 79 | def __init__(self): 80 | self.c_bytes = [] 81 | 82 | 83 | def set_bytes(self, b): 84 | self.c_bytes = b 85 | 86 | def get_bytes(self): 87 | return self.c_bytes 88 | 89 | 90 | 91 | 92 | 93 | PY_VERSION_NUMBER = "2.1" 94 | LUA_VERSION_NUMBER = "2.1" 95 | 96 | CH_BYTES = character_bytes() 97 | OBJ_BYTES = character_bytes() 98 | ITEM_BYTES = character_bytes() 99 | 100 | 101 | 102 | SQRT_2 = 2 ** 0.5 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | WINDOW_WIDTH = config["window_size"] 115 | WINDOW_HEIGHT = config["window_size"] 116 | 117 | MAX_TRAILS_CONFIG = config["max_trail_length"] 118 | 119 | TRAIL_FREQ_CONFIG = config["trail_log_rate"] 120 | 121 | #width = config["window_size"] 122 | #height = config["window_size"] 123 | 124 | 125 | 126 | width = height = 1024 127 | MAP_W = MAP_H = 1024 128 | 129 | 130 | 131 | spf = 1.0 / 60.0 132 | 133 | 134 | 135 | screen = None 136 | 137 | 138 | def hex_to_int(h): 139 | return int(h, 16) 140 | 141 | 142 | def byte_buffer(buf): 143 | index = 0 144 | 145 | while index < len(buf): 146 | yield buf[index] 147 | index += 1 148 | 149 | def next_byte(g): 150 | return next(g) 151 | 152 | 153 | def next_word(g): 154 | L = next(g) 155 | H = next(g) 156 | return L + (H << 8) 157 | 158 | def next_word_signed(g): 159 | val = next_word(g) 160 | if val > 0x7fff: val -= 0x10000 161 | return val 162 | 163 | def next_long(g): 164 | L = next(g) 165 | M = next(g) 166 | H = next(g) 167 | return L + (M << 8) + (H << 16) 168 | 169 | def next_double(g): 170 | L1 = next(g) 171 | L2 = next(g) 172 | H1 = next(g) 173 | H2 = next(g) 174 | return ((L1 + (L2 << 8))/65536) + (H1 + (H2 << 8)) 175 | 176 | def next_double_signed(g): 177 | val = next_double(g) 178 | if val > 0x7fff: val -= 0x10000 179 | return val 180 | 181 | 182 | 183 | 184 | MAP_SCLS = { 185 | 'width': width / 1024, 186 | 'height': height / 1024, 187 | 'radians': -2 * math.pi / 255, 188 | 'degrees': -360 / 255, 189 | } 190 | 191 | 192 | def map_value(val, map_type=None): 193 | try: return val * MAP_SCLS[map_type] 194 | except: return val 195 | 196 | 197 | 198 | def angle_between(angle_1, angle_2): 199 | 200 | x1 = math.cos(angle_1 * math.pi/180) 201 | y1 = math.sin(angle_1 * math.pi/180) 202 | x2 = math.cos(angle_2 * math.pi/180) 203 | y2 = math.sin(angle_2 * math.pi/180) 204 | 205 | dot = x1*x2 + y1*y2 206 | det = x1*y2 - y1*x2 207 | 208 | return math.atan2(det, dot) * 180/math.pi 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | # ========================== VARIABLES for general use ============================== 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | Racer0 = Objects.Racer(OBJ_ID="racer0", address=0x1000, display_dest=False) 232 | Racer1 = Objects.Racer(OBJ_ID="racer1", address=0x1100) 233 | Racer2 = Objects.Racer(OBJ_ID="racer2", address=0x1200) 234 | Racer3 = Objects.Racer(OBJ_ID="racer3", address=0x1300) 235 | Racer4 = Objects.Racer(OBJ_ID="racer4", address=0x1400) 236 | Racer5 = Objects.Racer(OBJ_ID="racer5", address=0x1500) 237 | Racer6 = Objects.Racer(OBJ_ID="racer6", address=0x1600) 238 | Racer7 = Objects.Racer(OBJ_ID="racer7", address=0x1700) 239 | 240 | 241 | Obj0 = Objects.Obstacle(OBJ_ID="obj0", address=0x1800) 242 | Obj1 = Objects.Obstacle(OBJ_ID="obj0", address=0x1840) 243 | Obj2 = Objects.Obstacle(OBJ_ID="obj0", address=0x1880) 244 | Obj3 = Objects.Obstacle(OBJ_ID="obj0", address=0x18c0) 245 | Obj4 = Objects.Obstacle(OBJ_ID="obj0", address=0x1900) 246 | Obj5 = Objects.Obstacle(OBJ_ID="obj0", address=0x1940) 247 | Obj6 = Objects.Obstacle(OBJ_ID="obj0", address=0x1980) 248 | Obj7 = Objects.Obstacle(OBJ_ID="obj0", address=0x19c0) 249 | 250 | 251 | Item0 = Objects.Item(OBJ_ID="item0", address=0x1a00) 252 | Item1 = Objects.Item(OBJ_ID="item1", address=0x1a80) 253 | Item2 = Objects.Item(OBJ_ID="item2", address=0x1b00) 254 | Item3 = Objects.Item(OBJ_ID="item3", address=0x1b80) 255 | Item4 = Objects.Item(OBJ_ID="item4", address=0x1438) 256 | Item5 = Objects.Item(OBJ_ID="item5", address=0x1538) 257 | Item6 = Objects.Item(OBJ_ID="item6", address=0x1638) 258 | Item7 = Objects.Item(OBJ_ID="item7", address=0x1738) 259 | 260 | num_racers = 8 261 | num_obstacles = 8 262 | num_items = 8 263 | 264 | 265 | 266 | OBJECTS = [Racer0, Racer1, Racer2, Racer3, Racer4, Racer5, Racer6, Racer7, Obj0, Obj1, Obj2, Obj3, Obj4, Obj5, Obj6, Obj7, Item0, Item1, Item2, Item3, Item4, Item5, Item6, Item7] 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | mouse = (-1, -1) 276 | mouse_rel = (0, 0) 277 | ###pressed = (False, False, False) 278 | 279 | 280 | F = 0 281 | 282 | 283 | LEFT_PRESSED = False 284 | RIGHT_PRESSED = False 285 | 286 | 287 | 288 | 289 | FOLLOW_MODE = False 290 | FOLLOW_MODE_2P = False 291 | 292 | 293 | 294 | 295 | grabbed = -1 296 | 297 | m_off = (0, 0) 298 | 299 | 300 | 301 | mouse_button_names = ("left", "middle", "right", "scroll_up", "scroll_down") 302 | 303 | CURR_FRAME_MOUSE = { 304 | "left": False, 305 | "middle": False, 306 | "right": False, 307 | "scroll_up": False, 308 | "scroll_down": False 309 | } 310 | 311 | PREV_FRAME_MOUSE = {} 312 | MOUSE_NEW = {} 313 | MOUSE_RELEASED = {} 314 | 315 | for m_button in CURR_FRAME_MOUSE: 316 | PREV_FRAME_MOUSE[m_button] = CURR_FRAME_MOUSE[m_button] 317 | MOUSE_NEW[m_button] = CURR_FRAME_MOUSE[m_button] 318 | MOUSE_RELEASED[m_button] = CURR_FRAME_MOUSE[m_button] 319 | 320 | 321 | 322 | 323 | 324 | KEY_NAMES = { 325 | pygame.K_1: "1", 326 | pygame.K_2: "2", 327 | pygame.K_3: "3", 328 | pygame.K_4: "4", 329 | pygame.K_5: "5", 330 | pygame.K_6: "6", 331 | pygame.K_7: "7", 332 | pygame.K_8: "8", 333 | pygame.K_9: "9", 334 | pygame.K_0: "0", 335 | pygame.K_g: "g", 336 | pygame.K_c: "c", 337 | pygame.K_f: "f", 338 | pygame.K_i: "i", 339 | pygame.K_o: "o", 340 | pygame.K_t: "l", 341 | pygame.K_s: "s", 342 | pygame.K_r: "r", 343 | pygame.K_d: "d" 344 | 345 | } 346 | 347 | 348 | 349 | CURR_FRAME_KEYS = { 350 | "1": False, 351 | "2": False, 352 | "3": False, 353 | "4": False, 354 | "5": False, 355 | "6": False, 356 | "7": False, 357 | "8": False, 358 | "9": False, 359 | "0": False, 360 | "g": False, 361 | "c": False, 362 | "f": False, 363 | "i": False, 364 | "o": False, 365 | "l": False, 366 | "s": False, 367 | "r": False, 368 | "d": False 369 | } 370 | 371 | 372 | PREV_FRAME_KEYS = {} 373 | KEYS_NEW = {} 374 | KEYS_RELEASED = {} 375 | 376 | for k_button in CURR_FRAME_KEYS: 377 | PREV_FRAME_KEYS[k_button] = CURR_FRAME_KEYS[k_button] 378 | KEYS_NEW[k_button] = CURR_FRAME_KEYS[k_button] 379 | KEYS_RELEASED[k_button] = CURR_FRAME_KEYS[k_button] 380 | 381 | 382 | 383 | FOLLOWING = [] 384 | 385 | 386 | 387 | 388 | ghost_mode_byte = 0 389 | 390 | 391 | 392 | 393 | ANIM_TIMER = 0 394 | ANIM_TIMER_SWAP = 15 395 | 396 | 397 | FRAME_SKIP = config["FRAME_SKIP"] 398 | 399 | FRAME_NUMBER = 0 400 | 401 | 402 | 403 | demo_byte = 0 404 | 405 | 406 | 407 | 408 | GHOST_FLASH_TIMER = 0 409 | 410 | 411 | 412 | SPRITE_SCALE = 2 413 | 414 | 415 | 416 | 417 | SHOW_DEBUG = False 418 | 419 | 420 | 421 | 422 | 423 | 424 | MAP_UPDATE_BYTES = [] 425 | 426 | # ================================================================================================== 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | # ================================================================================================== 436 | 437 | 438 | 439 | 440 | 441 | SOCKET_DEBUG = False 442 | 443 | 444 | 445 | 446 | 447 | def print_socket(d): 448 | if SOCKET_DEBUG: 449 | print("[DEBUG]", d) 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | def SEND_DATA(data, send_all=False): 461 | 462 | global SOCKET_CONN 463 | 464 | bytes_left = size = len(data) 465 | bytes_sent = 0 466 | 467 | 468 | while bytes_left > 0: 469 | try: 470 | b = data[bytes_sent:] 471 | 472 | #print_socket(">>>" + b.decode("ascii")) 473 | 474 | _num_sent = SOCKET_CONN.send(b) 475 | 476 | if _num_sent == 0: raise ConnectionResetError("CONNECTION ERROR!") 477 | 478 | bytes_sent += _num_sent 479 | bytes_left -= _num_sent 480 | 481 | except socket.error as e: 482 | 483 | if e.errno in [errno.EAGAIN, errno.EWOULDBLOCK]: 484 | if send_all: 485 | continue 486 | return data[bytes_sent:] 487 | else: 488 | raise e 489 | 490 | 491 | 492 | 493 | 494 | def RECV_DATA(block=True): 495 | global SOCKET_CONN 496 | 497 | if SOCKET_CONN.gettimeout() == None: 498 | if block == False: 499 | SOCKET_CONN.settimeout(2) 500 | 501 | CONN_DAT = "" 502 | 503 | CONN_DAT += SOCKET_CONN.recv(8192).decode("ascii") 504 | 505 | #print_socket("<<< " + CONN_DAT) 506 | 507 | 508 | return CONN_DAT 509 | 510 | 511 | 512 | 513 | def send_signal(sig): 514 | 515 | if type(sig) == type(0): 516 | sig = str(sig) 517 | 518 | try: 519 | sig.decode("utf-8") # dummy test to see if it is a string or a bytes list 520 | except AttributeError: 521 | sig = sig.encode("utf-8") 522 | 523 | if sig[-1:] != b'\n': 524 | sig = (sig.decode("utf-8") + "\n").encode("utf-8") 525 | 526 | SEND_DATA(sig) 527 | 528 | 529 | 530 | def send_raw_signal(raw): send_signal(raw) 531 | def send_frame_signal(): send_signal("FRAME") 532 | def send_close_signal(): send_signal("close") 533 | def send_ghost_signal(): send_signal("DO_GHOST") 534 | def send_ghost_off_signal(): send_signal("GHOST_OFF") 535 | def send_ack_signal(): send_signal("ack") 536 | def send_pause_signal(): send_signal("PAUSE") 537 | def send_unpause_signal(): send_signal("UNPAUSE") 538 | def send_yield_signal(): send_signal("YIELD") 539 | def send_unyield_signal(): send_signal("UNYIELD") 540 | def send_reset_map_signal(): send_signal("RESET_MAP") 541 | 542 | def send_palette_signal(): send_signal("PALETTE") 543 | def send_tileset_signal(): send_signal("TILES") 544 | def send_tilemap_signal(): send_signal("TRACK") 545 | def send_zonemap_signal(): send_signal("ZONE") 546 | def send_flowmap_signal(): send_signal("FLOW") 547 | def send_cp_data_signal(): send_signal("CP_DATA") 548 | 549 | def send_id_signal(): send_signal("ID_BVPY_" + PY_VERSION_NUMBER) 550 | 551 | def send_nonce_signal(): send_signal("nonce") 552 | 553 | 554 | 555 | 556 | def send_w_sram_signal(sram_data): 557 | 558 | try: 559 | while sram_data[0] == " ": sram_data = sram_data[1:] 560 | except Exception as e: 561 | pass 562 | sram_data = " " + sram_data 563 | send_signal("W_SRAM" + sram_data) 564 | 565 | 566 | 567 | def CLOSE_CONN(): 568 | global SOCKET_CONN 569 | 570 | send_close_signal() 571 | SOCKET_CONN.close() 572 | 573 | 574 | 575 | 576 | 577 | def wait_for_sync(initial_sync=False): 578 | global SOCKET_CONN 579 | 580 | while True: 581 | 582 | if initial_sync: 583 | SOCKET_CONN.settimeout(0.5) 584 | else: 585 | SOCKET_CONN.settimeout(10) 586 | 587 | failed = False 588 | try: 589 | CONN_DAT = RECV_DATA(block=True).split("\n") 590 | except socket.timeout: 591 | failed = True 592 | 593 | if failed: 594 | SOCKET_CONN.settimeout(None) 595 | raise Exception("Socket took too long to respond.") 596 | 597 | if CONN_DAT[0] == "sync": 598 | send_ack_signal() 599 | CONN_DAT = '\n'.join(CONN_DAT[1:]) 600 | break 601 | 602 | #else: 603 | # raise ValueError("NOT SYNC: " + CONN_DAT[0]) 604 | 605 | SOCKET_CONN.settimeout(None) 606 | 607 | 608 | 609 | 610 | def match_lua_id(lua_version=LUA_VERSION_NUMBER): 611 | 612 | global SOCKET_CONN 613 | 614 | e_what = "" 615 | 616 | 617 | send_id_signal() 618 | 619 | d = "" 620 | try: 621 | SOCKET_CONN.settimeout(0.1) 622 | d += RECV_DATA().split('\n')[0] 623 | except Exception as e: 624 | e_what = str(e) 625 | 626 | if e_what != "": raise Exception(e_what) 627 | 628 | SOCKET_CONN.settimeout(None) 629 | 630 | if d != "ID_BVLUA_" + lua_version: raise ZeroDivisionError() 631 | 632 | return d 633 | 634 | 635 | 636 | 637 | 638 | 639 | #def toGL_X(x): 640 | # return x/(WINDOW_WIDTH/2) - 0.5 641 | #def toGL_Y(y): 642 | # return -y/(WINDOW_HEIGHT/2) + 0.5 643 | 644 | 645 | 646 | def cnvGL_X(x): return (x*2)-1 647 | def cnvGL_Y(y): return -(y*2)+1 648 | 649 | def glb2GL_X(x): return cnvGL_X(x/WINDOW_WIDTH) 650 | def glb2GL_Y(y): return cnvGL_Y(y/WINDOW_HEIGHT) 651 | def glb2GL_W(w): return (w/WINDOW_WIDTH)*2 652 | def glb2GL_H(h): return (h/WINDOW_HEIGHT)*-2 653 | 654 | 655 | def map2disp_X(m_x): return screen.x + screen.SCALE * (m_x * screen.DEFAULT_WIDTH/MAP_W) 656 | def map2disp_Y(m_y): return screen.y + screen.SCALE * (m_y * screen.DEFAULT_HEIGHT/MAP_H) 657 | 658 | 659 | def disp2map_X(m_x): return screen.INV_SCALE*(-screen.x + m_x) * MAP_W/screen.DEFAULT_WIDTH 660 | def disp2map_Y(m_y): return screen.INV_SCALE*(-screen.y + m_y) * MAP_H/screen.DEFAULT_HEIGHT 661 | 662 | 663 | def map2GL_X(x): return glb2GL_X(map2disp_X(x)) 664 | def map2GL_Y(y): return glb2GL_Y(map2disp_Y(y)) 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | def renderSurface(surf, pos=None, dim=None, rot=None, centered=False): # SHOULD NEVER NEED TO BE USED ANYMORE!! 677 | #global GL_CNV_ID 678 | 679 | if pos == None: 680 | _x = 0 681 | _y = 0 682 | else: 683 | _x,_y = pos 684 | 685 | if dim == None: 686 | r = surf.get_rect() 687 | _w = r.width 688 | _h = r.height 689 | else: 690 | _w,_h = dim 691 | 692 | if rot == None: 693 | _a1 = 0 694 | _a2 = 0 695 | _a3 = 0 696 | else: 697 | _a1, _a2, _a3 = rot 698 | 699 | Assets.surfToGLTex(surf) 700 | 701 | renderTexture(Assets.GL_CNV_ID, (_x,_y), (_w,_h), (_a1, _a2, _a3), centered=centered) 702 | 703 | 704 | 705 | 706 | 707 | def renderTexture(texID, pos=None, dim=None, rot=None, centered=False): 708 | 709 | if pos == None: 710 | _x = 0 711 | _y = 0 712 | else: 713 | _x,_y = pos 714 | 715 | if dim == None: 716 | _w = WINDOW_WIDTH 717 | _h = WINDOW_HEIGHT 718 | else: 719 | _w,_h = dim 720 | 721 | if rot == None: 722 | _a1 = 0 723 | _a2 = 0 724 | _a3 = 0 725 | else: 726 | _a1, _a2, _a3 = rot 727 | 728 | 729 | 730 | 731 | c_x = _x 732 | c_y = _y 733 | 734 | 735 | if not centered: 736 | c_x += _w/2 737 | c_y += _h/2 738 | 739 | glDisable(GL_LIGHTING) 740 | glEnable(GL_TEXTURE_2D) 741 | 742 | 743 | glMatrixMode(GL_MODELVIEW) 744 | 745 | glPushMatrix() 746 | 747 | #glOrtho(0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, -1, 1); 748 | 749 | 750 | 751 | glLoadIdentity() 752 | 753 | glTranslatef(glb2GL_X(c_x), glb2GL_Y(c_y), 0) 754 | 755 | #glRotatef(_a1, 1, 0, 0) 756 | #glRotatef(_a2, 0, 1, 0) 757 | glRotatef(_a3, 0, 0, 1) 758 | 759 | glTranslatef(glb2GL_W(WINDOW_WIDTH/2), glb2GL_H(WINDOW_HEIGHT/2), 0) 760 | 761 | glBindTexture(GL_TEXTURE_2D, texID) 762 | 763 | 764 | glBegin(GL_QUADS) 765 | 766 | 767 | if centered: 768 | glTexCoord2f(0, 0); glVertex2f(glb2GL_X(0-_w/2), glb2GL_Y(0-_h/2)) # top-left 769 | glTexCoord2f(0, 1); glVertex2f(glb2GL_X(0-_w/2), glb2GL_Y(0+_h/2)) # bottom-left 770 | glTexCoord2f(1, 1); glVertex2f(glb2GL_X(0+_w/2), glb2GL_Y(0+_h/2)) # bottom-right 771 | glTexCoord2f(1, 0); glVertex2f(glb2GL_X(0+_w/2), glb2GL_Y(0-_h/2)) # top-right 772 | else: 773 | glTexCoord2f(0, 0); glVertex2f(glb2GL_W(0-_w/2), glb2GL_H(0-_h/2)) # top-left 774 | glTexCoord2f(0, 1); glVertex2f(glb2GL_W(0-_w/2), glb2GL_H(0+_h/2)) # bottom-left 775 | glTexCoord2f(1, 1); glVertex2f(glb2GL_W(0+_w/2), glb2GL_H(0+_h/2)) # bottom-right 776 | glTexCoord2f(1, 0); glVertex2f(glb2GL_W(0+_w/2), glb2GL_H(0-_h/2)) # top-right 777 | 778 | glEnd() 779 | 780 | glPopMatrix() 781 | 782 | glDisable(GL_TEXTURE_2D) 783 | 784 | 785 | 786 | 787 | 788 | def renderObject(texID, pos=None, dim=None, rot=None, centered=True): 789 | renderTexture(texID, pos=pos, dim=dim, rot=rot, centered=centered) 790 | 791 | 792 | 793 | 794 | 795 | def do_frame_update(): 796 | send_frame_signal() 797 | update_window() 798 | 799 | 800 | 801 | def update_window(): 802 | 803 | 804 | #T = time.perf_counter() 805 | 806 | # ============================ 807 | BV.root.update() 808 | 809 | 810 | 811 | 812 | glBindFramebuffer(GL_FRAMEBUFFER, 0) 813 | glDisable(GL_DEPTH_TEST) 814 | 815 | 816 | 817 | glDrawBuffer(GL_FRONT) 818 | 819 | glClearColor(1.0, 1.0, 1.0, 1.0) 820 | glClear(GL_COLOR_BUFFER_BIT) 821 | glLoadIdentity() 822 | 823 | glUseProgram(SCREEN_SHADER) 824 | glBindVertexArray(SCREEN_VAO) 825 | glBindTexture(GL_TEXTURE_2D, FBO_TEX) 826 | glDrawArrays(GL_TRIANGLES, 0, 6) 827 | glUseProgram(0) 828 | 829 | 830 | glFlush() 831 | 832 | glDrawBuffer(GL_BACK) 833 | 834 | 835 | glBindFramebuffer(GL_FRAMEBUFFER, FBO_0) 836 | 837 | 838 | 839 | 840 | glClearColor(0.0, 0.0, 0.0, 0.0) 841 | glClear(GL_COLOR_BUFFER_BIT) 842 | glLoadIdentity() 843 | 844 | 845 | # ============================ 846 | 847 | #T = time.perf_counter() 848 | 849 | wait_for_sync() 850 | 851 | #T_0 = time.perf_counter() - T 852 | 853 | #print(100 * T_0 * 60) 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | M7_TILES = None 868 | TILEMAP = None 869 | 870 | TILEMAP_TEX = None 871 | CPMAP_TEX = None 872 | FLOWMAP_TEX = None 873 | 874 | ZONE_SCL = 1 875 | FLOW_SCL = config["flowmap_quality"] 876 | 877 | 878 | 879 | def make_map_textures(m_data, t_data, p_data, z_data, f_data, c_data, THREADED=False): 880 | 881 | global M7_TILES 882 | global TILEMAP 883 | global ZONEMAP 884 | global FLOWMAP 885 | global map_ready 886 | 887 | global TILESET_IMAGE 888 | global TILEMAP_IMAGE 889 | global ZONEMAP_IMAGE 890 | global FLOWMAP_IMAGE 891 | 892 | 893 | TrackHelper.set_render_quality( 894 | zone_scl=ZONE_SCL, # scale factor for zone map, for crispier zones! 895 | flow_scl=FLOW_SCL # scale factor for flow map, for crispier arrows! 896 | ) 897 | 898 | 899 | 900 | TILEMAP, M7_TILES = TrackHelper.get_tilemap_from_data( 901 | m_data, # tilemap data 902 | t_data, # tileset image data 903 | p_data # palette data 904 | ) 905 | 906 | ZONEMAP, FLOWMAP = TrackHelper.get_cpmap_flowmap_from_data( 907 | z_data, # checkpoint bound data 908 | f_data, # flowmap data 909 | c_data # checkpoint attribute data 910 | ) 911 | 912 | update_map_textures(THREADED=THREADED) 913 | 914 | map_ready = True 915 | 916 | #pygame.image.save(create_surface_from_buff(TILEMAP, (1024, 1024), ALPHA=False), "TRACK/TILEMAP.png") 917 | #pygame.image.save(create_surface_from_buff(ZONEMAP, (1024*ZONE_SCL, 1024*ZONE_SCL)), "TRACK/ZONEMAP.png") 918 | #pygame.image.save(create_surface_from_buff(FLOWMAP, (1024*FLOW_SCL, 1024*FLOW_SCL)), "TRACK/FLOWMAP.png") 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | def create_surface_from_buff(buff, in_dim=(0, 0), ALPHA=True): 929 | 930 | 931 | frmt = "RGB" 932 | if ALPHA: frmt = "RGBA" 933 | 934 | return pygame.image.frombuffer(buff, in_dim, frmt) 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | def update_map_textures(THREADED=False): 946 | 947 | if not THREADED: update_tilemap() 948 | 949 | 950 | def update_tilemap(): 951 | global TILEMAP_TEX 952 | 953 | TILEMAP_TEX = Assets.buffToGLTex(TILEMAP, (1024, 1024), TILEMAP_TEX, ALPHA=False) 954 | 955 | 956 | def update_zone_flow(): 957 | global ZONEMAP_TEX 958 | global FLOWMAP_TEX 959 | 960 | ZONEMAP_TEX = Assets.buffToGLTex(ZONEMAP, (1024*ZONE_SCL, 1024*ZONE_SCL), ZONEMAP_TEX, ALPHA=True) 961 | FLOWMAP_TEX = Assets.buffToGLTex(FLOWMAP, (1024*FLOW_SCL, 1024*FLOW_SCL), FLOWMAP_TEX, ALPHA=True) 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | def update_map_data(update_data): 970 | """Updates the texture data for the map given a list of tile offsets and tile values""" 971 | 972 | for offs, val in update_data: update_map_tile(offs % 128, offs // 128, val) 973 | 974 | if update_data != []: update_map_textures() # only update map if updates happen 975 | 976 | 977 | 978 | 979 | 980 | def update_map_tile(TILE_X, TILE_Y, TILE_NUM): 981 | """Updates the texture data for the map for a given tile. DOES NOT UPDATE THE TEXTURE ITSELF!!""" 982 | 983 | global TILEMAP 984 | 985 | 986 | M7_TILE_DATA = M7_TILES[TILE_NUM] 987 | 988 | for j in range(8): 989 | TM_Y = TILE_Y*8 + j 990 | TL_Y = j 991 | 992 | for i in range(8): 993 | TM_X = TILE_X*8 + i 994 | TL_X = i 995 | for p in range(3): #RGB 996 | TILEMAP[((TM_Y * 1024) + TM_X)*3 + p] = M7_TILE_DATA[((TL_Y * 8) + TL_X)*3 + p] 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | def setup_map_socket(): 1005 | 1006 | global map_ready 1007 | 1008 | 1009 | #T = time.perf_counter() 1010 | 1011 | #T0 = time.perf_counter() - T 1012 | 1013 | #print("TIME:", 60 * T0 * 100) 1014 | 1015 | 1016 | 1017 | # PALETTE DATA 1018 | 1019 | send_palette_signal() 1020 | CONN_DAT = RECV_DATA().split("\n") 1021 | 1022 | 1023 | P_DAT = CONN_DAT[0] 1024 | p_data = bytes.fromhex(P_DAT) 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | # TILE DATA 1033 | 1034 | send_tileset_signal() 1035 | T_DAT = "" 1036 | 1037 | for i in range(8): 1038 | T_DAT += RECV_DATA().split("\n")[0] 1039 | send_nonce_signal() 1040 | 1041 | t_data = bytes.fromhex(T_DAT) 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | # MAP DATA 1048 | 1049 | send_tilemap_signal() 1050 | M_DAT = "" 1051 | 1052 | for i in range(8): 1053 | M_DAT += RECV_DATA().split("\n")[0] 1054 | send_nonce_signal() 1055 | 1056 | m_data = bytes.fromhex(M_DAT) 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | # ZONE DATA 1063 | 1064 | send_zonemap_signal() 1065 | Z_DAT = "" 1066 | 1067 | for i in range(2): 1068 | Z_DAT += RECV_DATA().split("\n")[0] 1069 | send_nonce_signal() 1070 | 1071 | z_data = bytes.fromhex(Z_DAT) 1072 | 1073 | 1074 | 1075 | 1076 | # FLOW DATA 1077 | 1078 | send_flowmap_signal() 1079 | F_DAT = "" 1080 | 1081 | for i in range(2): 1082 | F_DAT += RECV_DATA().split("\n")[0] 1083 | send_nonce_signal() 1084 | 1085 | f_data = bytes.fromhex(F_DAT) 1086 | 1087 | 1088 | 1089 | # ZONE ATTRIBUTES 1090 | 1091 | send_cp_data_signal() 1092 | C_DAT = RECV_DATA().split("\n")[0] 1093 | c_data = bytes.fromhex(C_DAT) 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | map_ready = False 1103 | map_thread = threading.Thread(target=make_map_textures, args=(m_data, t_data, p_data, z_data, f_data, c_data, True)) 1104 | map_thread.start() 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | def elapsed_time(reset=False): 1114 | global _TIME 1115 | 1116 | t = time.perf_counter() 1117 | e = t - _TIME 1118 | 1119 | if reset: _TIME = time.perf_counter() 1120 | 1121 | return e 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | def GENERATE_TEXTURES(): 1131 | global TILEMAP_TEX 1132 | global ZONEMAP_TEX 1133 | global FLOWMAP_TEX 1134 | 1135 | 1136 | #global GL_CNV_ID 1137 | 1138 | TILEMAP_TEX = glGenTextures(1) 1139 | ZONEMAP_TEX = glGenTextures(1) 1140 | FLOWMAP_TEX = glGenTextures(1) 1141 | 1142 | 1143 | #GL_CNV_ID = glGenTextures(1) 1144 | 1145 | 1146 | 1147 | Assets.create_GL_textures() 1148 | 1149 | 1150 | 1151 | def GEN_VBO(): 1152 | global VBO1 1153 | global VBO2 1154 | 1155 | global VAO1 1156 | 1157 | VBO1 = glGenBuffers(1) 1158 | VBO2 = glGenBuffers(1) 1159 | 1160 | VAO1 = glGenVertexArrays(1) 1161 | 1162 | 1163 | def GEN_FBO(): 1164 | global FBO_TEX 1165 | global FBO_0 1166 | global RBO_0 1167 | 1168 | 1169 | global SCREEN_VAO 1170 | global SCREEN_VERTS 1171 | 1172 | 1173 | SCREEN_TRI_COORDS = [ 1174 | -1, 1, 1175 | -1, -1, 1176 | 1, -1, 1177 | 1178 | -1, 1, 1179 | 1, -1, 1180 | 1, 1 1181 | ] 1182 | 1183 | SCREEN_TEX_COORDS = [ 1184 | 0, 1, 1185 | 0, 0, 1186 | 1, 0, 1187 | 1188 | 0, 1, 1189 | 1, 0, 1190 | 1, 1 1191 | ] 1192 | 1193 | SCREEN_VAO = glGenVertexArrays(1) 1194 | SCREEN_VBO1 = glGenBuffers(1) 1195 | SCREEN_VBO2 = glGenBuffers(1) 1196 | 1197 | glBindVertexArray(SCREEN_VAO) 1198 | glBindBuffer(GL_ARRAY_BUFFER, SCREEN_VBO1) 1199 | glBufferData(GL_ARRAY_BUFFER, len(SCREEN_TRI_COORDS)*4, (c_float*len(SCREEN_TRI_COORDS))(*SCREEN_TRI_COORDS), GL_STATIC_DRAW) 1200 | glEnableVertexAttribArray(0) 1201 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None) 1202 | glBindBuffer(GL_ARRAY_BUFFER, SCREEN_VBO2) 1203 | glBufferData(GL_ARRAY_BUFFER, len(SCREEN_TEX_COORDS)*4, (c_float*len(SCREEN_TEX_COORDS))(*SCREEN_TEX_COORDS), GL_STATIC_DRAW) 1204 | glEnableVertexAttribArray(1) 1205 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None) 1206 | 1207 | 1208 | FBO_0 = glGenFramebuffers(1) 1209 | glBindFramebuffer(GL_FRAMEBUFFER, FBO_0) 1210 | 1211 | FBO_TEX = glGenTextures(1) 1212 | glBindTexture(GL_TEXTURE_2D, FBO_TEX) 1213 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, None) 1214 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 1215 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) 1216 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FBO_TEX, 0) 1217 | 1218 | 1219 | RBO_0 = glGenRenderbuffers(1) 1220 | glBindRenderbuffer(GL_RENDERBUFFER, RBO_0) 1221 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WINDOW_WIDTH, WINDOW_HEIGHT) 1222 | glBindRenderbuffer(GL_RENDERBUFFER, 0) 1223 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO_0) 1224 | 1225 | if glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE: print("[WARNING] GL FRAMEBUFFER NOT COMPLETE") 1226 | 1227 | glBindFramebuffer(GL_FRAMEBUFFER, 0) 1228 | #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) 1229 | 1230 | glUseProgram(SCREEN_SHADER) 1231 | glUniform1i(screen_tex_screenTexture, 0) # ? 1232 | glUseProgram(0) 1233 | 1234 | 1235 | 1236 | 1237 | def GEN_RBO(): 1238 | global RBO_0 1239 | 1240 | #RBO_0 = glGenRenderbuffers(1) 1241 | pass 1242 | 1243 | 1244 | 1245 | def GEN_SHADERS(): 1246 | ################################################################################### 1247 | ##### TRAIL SHADER 1248 | ################################################################################### 1249 | global TRAIL_VERTEX_SHADER 1250 | global TRAIL_FRAGMENT_SHADER 1251 | global TRAIL_SHADER 1252 | ################################################################################### 1253 | 1254 | TRAIL_VERTEX_SHADER = shaders.compileShader(""" 1255 | #version 460 1256 | 1257 | layout(std430, binding = 0) buffer TVertex 1258 | { 1259 | float vert_data[]; 1260 | }; 1261 | 1262 | uniform float S_w; 1263 | uniform float S_h; 1264 | uniform float S_x; 1265 | uniform float S_y; 1266 | uniform float W_w; 1267 | uniform float W_h; 1268 | 1269 | uniform float spd_min; 1270 | uniform float spd_max; 1271 | 1272 | uniform float line_width; 1273 | 1274 | out vec3 vout_color; 1275 | 1276 | 1277 | 1278 | float toGL_X(float x) 1279 | { 1280 | return 2 * ((S_x + S_w * x) / W_w) - 1; 1281 | } 1282 | 1283 | float toGL_Y(float y) 1284 | { 1285 | return -1 * (2 * ((S_y + S_h * y) / W_h) - 1); 1286 | } 1287 | 1288 | 1289 | 1290 | void main() 1291 | { 1292 | 1293 | int line_i = gl_VertexID / 6; 1294 | int tri_i = gl_VertexID % 6; 1295 | 1296 | 1297 | 1298 | vec4 va[4]; 1299 | int line_ind = line_i * 3; 1300 | 1301 | va[0] = vec4(toGL_X(vert_data[line_ind+0]), toGL_Y(vert_data[line_ind+1]), 0.0f, 1.0f); 1302 | va[1] = vec4(toGL_X(vert_data[line_ind+3]), toGL_Y(vert_data[line_ind+4]), 0.0f, 1.0f); 1303 | va[2] = vec4(toGL_X(vert_data[line_ind+6]), toGL_Y(vert_data[line_ind+7]), 0.0f, 1.0f); 1304 | va[3] = vec4(toGL_X(vert_data[line_ind+9]), toGL_Y(vert_data[line_ind+10]), 0.0f, 1.0f); 1305 | 1306 | 1307 | vec2 v_line = normalize(va[2].xy - va[1].xy); 1308 | vec2 nv_line = vec2(-v_line.y, v_line.x); 1309 | 1310 | 1311 | vec4 pos; 1312 | float col; 1313 | 1314 | 1315 | if (tri_i == 0 || tri_i == 1 || tri_i == 3) 1316 | { 1317 | vec2 v_pred = normalize(va[1].xy - va[0].xy); 1318 | vec2 v_miter = normalize(nv_line + vec2(-v_pred.y, v_pred.x)); 1319 | 1320 | pos = va[1]; 1321 | pos.xy += v_miter * line_width * 2 * (tri_i == 1 ? -0.5 : 0.5) / dot(v_miter, nv_line); 1322 | 1323 | col = vert_data[line_ind+5]; 1324 | } 1325 | else 1326 | { 1327 | vec2 v_succ = normalize(va[3].xy - va[2].xy); 1328 | vec2 v_miter = normalize(nv_line + vec2(-v_succ.y, v_succ.x)); 1329 | 1330 | pos = va[2]; 1331 | pos.xy += v_miter * line_width * 2 * (tri_i == 5 ? 0.5 : -0.5) / dot(v_miter, nv_line); 1332 | 1333 | col = vert_data[line_ind+8]; 1334 | } 1335 | 1336 | 1337 | gl_Position = pos; 1338 | 1339 | 1340 | // color calculation 1341 | float max2 = spd_max - spd_min; 1342 | 1343 | col = (clamp(col, spd_min, spd_max) - spd_min) / max2; 1344 | 1345 | col = clamp(col, 0.1, 0.9); 1346 | 1347 | // set hue 1348 | vec3 c = vec3((1 - col) / 3, 1.0f, 1.0f); 1349 | 1350 | // hsv to rgb 1351 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 1352 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 1353 | 1354 | vout_color = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 1355 | } 1356 | """, GL_VERTEX_SHADER) 1357 | 1358 | TRAIL_FRAGMENT_SHADER = shaders.compileShader(""" 1359 | #version 460 1360 | 1361 | in vec3 vout_color; 1362 | out vec4 fragColor; 1363 | 1364 | void main() 1365 | { 1366 | fragColor = vec4(vout_color, 1.0); 1367 | } 1368 | """, GL_FRAGMENT_SHADER) 1369 | 1370 | 1371 | TRAIL_SHADER = shaders.compileProgram(TRAIL_VERTEX_SHADER, TRAIL_FRAGMENT_SHADER) 1372 | ################################################################################### 1373 | global trail_S_w 1374 | global trail_S_h 1375 | global trail_S_x 1376 | global trail_S_y 1377 | global trail_W_w 1378 | global trail_W_h 1379 | 1380 | global trail_spd_min 1381 | global trail_spd_max 1382 | global trail_line_width 1383 | 1384 | trail_S_w = glGetUniformLocation(TRAIL_SHADER, "S_w") 1385 | trail_S_h = glGetUniformLocation(TRAIL_SHADER, "S_h") 1386 | trail_S_x = glGetUniformLocation(TRAIL_SHADER, "S_x") 1387 | trail_S_y = glGetUniformLocation(TRAIL_SHADER, "S_y") 1388 | trail_W_w = glGetUniformLocation(TRAIL_SHADER, "W_w") 1389 | trail_W_h = glGetUniformLocation(TRAIL_SHADER, "W_h") 1390 | 1391 | trail_spd_min = glGetUniformLocation(TRAIL_SHADER, "spd_min") 1392 | trail_spd_max = glGetUniformLocation(TRAIL_SHADER, "spd_max") 1393 | trail_line_width = glGetUniformLocation(TRAIL_SHADER, "line_width") 1394 | ################################################################################### 1395 | 1396 | 1397 | 1398 | ################################################################################### 1399 | ##### LINE SETS SHADER 1400 | ################################################################################### 1401 | global LINESET_VERTEX_SHADER 1402 | global LINESET_FRAGMENT_SHADER 1403 | global LINESET_SHADER 1404 | ################################################################################### 1405 | LINESET_VERTEX_SHADER = shaders.compileShader(""" 1406 | #version 460 1407 | 1408 | in vec2 vin_pos; 1409 | in vec4 vin_col; 1410 | 1411 | uniform float S_w; 1412 | uniform float S_h; 1413 | uniform float S_x; 1414 | uniform float S_y; 1415 | uniform float W_w; 1416 | uniform float W_h; 1417 | 1418 | out vec4 vout_col; 1419 | 1420 | 1421 | float toGL_X(float x) 1422 | { 1423 | return 2 * ((S_x + S_w * x) / W_w) - 1; 1424 | } 1425 | 1426 | float toGL_Y(float y) 1427 | { 1428 | return -1 * (2 * ((S_y + S_h * y) / W_h) - 1); 1429 | } 1430 | 1431 | 1432 | void main() 1433 | { 1434 | gl_Position = vec4(toGL_X(vin_pos[0]), toGL_Y(vin_pos[1]), 0.0f, 1.0f); 1435 | vout_col = vin_col; 1436 | } 1437 | 1438 | """, GL_VERTEX_SHADER) 1439 | 1440 | 1441 | LINESET_FRAGMENT_SHADER = shaders.compileShader(""" 1442 | #version 460 1443 | 1444 | in vec4 vout_col; 1445 | out vec4 fragColor; 1446 | 1447 | void main() 1448 | { 1449 | //fragColor = vec4(vout_col, 1.0); 1450 | fragColor = vout_col; 1451 | } 1452 | """, GL_FRAGMENT_SHADER) 1453 | 1454 | 1455 | 1456 | LINESET_SHADER = shaders.compileProgram(LINESET_VERTEX_SHADER, LINESET_FRAGMENT_SHADER) 1457 | ################################################################################### 1458 | global lset_S_w 1459 | global lset_S_h 1460 | global lset_S_x 1461 | global lset_S_y 1462 | global lset_W_w 1463 | global lset_W_h 1464 | 1465 | lset_S_w = glGetUniformLocation(LINESET_SHADER, "S_w") 1466 | lset_S_h = glGetUniformLocation(LINESET_SHADER, "S_h") 1467 | lset_S_x = glGetUniformLocation(LINESET_SHADER, "S_x") 1468 | lset_S_y = glGetUniformLocation(LINESET_SHADER, "S_y") 1469 | lset_W_w = glGetUniformLocation(LINESET_SHADER, "W_w") 1470 | lset_W_h = glGetUniformLocation(LINESET_SHADER, "W_h") 1471 | ################################################################################### 1472 | 1473 | 1474 | 1475 | ################################################################################### 1476 | ##### SCREEN RENDER SHADER 1477 | ################################################################################### 1478 | global SCREEN_VERTEX_SHADER 1479 | global SCREEN_FRAGMENT_SHADER 1480 | global SCREEN_SHADER 1481 | ################################################################################### 1482 | SCREEN_VERTEX_SHADER = shaders.compileShader(""" 1483 | #version 330 core 1484 | layout (location = 0) in vec2 aPos; 1485 | layout (location = 1) in vec2 aTexCoords; 1486 | 1487 | out vec2 TexCoords; 1488 | 1489 | void main() 1490 | { 1491 | TexCoords = aTexCoords; 1492 | gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 1493 | } 1494 | """, GL_VERTEX_SHADER) 1495 | 1496 | 1497 | 1498 | SCREEN_FRAGMENT_SHADER = shaders.compileShader(""" 1499 | #version 330 core 1500 | out vec4 FragColor; 1501 | 1502 | in vec2 TexCoords; 1503 | 1504 | uniform sampler2D screenTexture; 1505 | 1506 | void main() 1507 | { 1508 | vec3 col = texture(screenTexture, TexCoords).rgb; 1509 | FragColor = vec4(col, 1.0); 1510 | } 1511 | 1512 | 1513 | """, GL_FRAGMENT_SHADER) 1514 | 1515 | 1516 | 1517 | SCREEN_SHADER = shaders.compileProgram(SCREEN_VERTEX_SHADER, SCREEN_FRAGMENT_SHADER) 1518 | ################################################################################### 1519 | global screen_tex_screenTexture 1520 | 1521 | screen_tex_screenTexture = glGetUniformLocation(SCREEN_SHADER, "screenTexture") 1522 | ################################################################################### 1523 | 1524 | 1525 | 1526 | 1527 | def SHOW_TRAIL_LINES(): 1528 | 1529 | LW = max(1, screen.w / MAP_W) 1530 | 1531 | 1532 | 1533 | glUseProgram(TRAIL_SHADER) 1534 | 1535 | glUniform1f(trail_S_w, screen.SCALE * screen.DEFAULT_WIDTH / MAP_W) 1536 | glUniform1f(trail_S_h, screen.SCALE * screen.DEFAULT_HEIGHT / MAP_H) 1537 | glUniform1f(trail_S_x, screen.x) 1538 | glUniform1f(trail_S_y, screen.y) 1539 | glUniform1f(trail_W_w, WINDOW_WIDTH) 1540 | glUniform1f(trail_W_h, WINDOW_HEIGHT) 1541 | 1542 | 1543 | glUniform1f(trail_spd_min, MIN_SPD) 1544 | glUniform1f(trail_spd_max, MAX_SPD) 1545 | 1546 | glUniform1f(trail_line_width, LW/WINDOW_WIDTH) 1547 | 1548 | 1549 | glBindVertexArray(VAO1) 1550 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, VBO1) 1551 | 1552 | for i in range(7, -1, -1): 1553 | 1554 | if not OBJECTS[i].show_trails: continue 1555 | 1556 | 1557 | pos = [x+0 for x in OBJECTS[i].prev_positions] 1558 | c_len = OBJECTS[i].trail_cbuff_len + 1 1559 | L = c_len*3 1560 | 1561 | if L == 0: continue 1562 | 1563 | pos.append(OBJECTS[i].x) 1564 | pos.append(OBJECTS[i].y) 1565 | pos.append(OBJECTS[i].speed) 1566 | 1567 | 1568 | glBufferData(GL_SHADER_STORAGE_BUFFER, L*4, (c_float*L)(*pos), GL_STATIC_DRAW) 1569 | 1570 | 1571 | 1572 | N = c_len - 2 1573 | 1574 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, VBO1) 1575 | 1576 | glDrawArrays(GL_TRIANGLES, 0, max(0, 6*(N-1))) 1577 | 1578 | 1579 | 1580 | 1581 | #glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) 1582 | 1583 | 1584 | 1585 | 1586 | ####################################################################################### 1587 | 1588 | glUseProgram(0) 1589 | 1590 | #glFlush() # enable if getting weird clipping!! 1591 | 1592 | ''' 1593 | for i in range(7, -1, -1): 1594 | 1595 | if not OBJECTS[i].show_trails: continue 1596 | 1597 | OBJECTS[i].prev_positions.pop() 1598 | OBJECTS[i].prev_positions.pop() 1599 | OBJECTS[i].prev_positions.pop() 1600 | ''' 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | 1609 | 1610 | 1611 | 1612 | DEG_2_RAD = math.pi / 180 1613 | RAD_2_DEG = 180 / math.pi 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | VECTOR_SCALE = 32 1627 | NUM_WIDTHS = 3 1628 | VEC_W = 2 1629 | 1630 | 1631 | 1632 | CAM_POS = (0, 0) 1633 | C_MODE = 0 1634 | 1635 | 1636 | 1637 | cam_scl_a = 8 1638 | cam_scl_b = 0 1639 | 1640 | C_SCL = 8 1641 | 1642 | CAM_WIDTH = 32 # idk why this works but it does 1643 | CAM_LINE_ALPHA = 0.5 1644 | 1645 | A_VEL_SEGMENTS = 3 1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | 1653 | 1654 | 1655 | ls_numl = 0 1656 | ls_vdata = [] 1657 | ls_cdata = [] 1658 | 1659 | 1660 | 1661 | def is_active_racer(racer_num): 1662 | 1663 | if racer_num > 7: return False 1664 | 1665 | # GP mode 1666 | if gametype == 0: return True 1667 | 1668 | # Not GP-mode, so only racers 0 and 1 can possibly be active 1669 | if racer_num > 1: return False 1670 | 1671 | # Match Race or Battle Mode 1672 | if gametype == 2 or gametype == 6: return True 1673 | 1674 | # Time Trial [gametype == 4] (Need to check ghost) 1675 | if C_MODE == 0: return True# both screens active (shouldn't happen in base game, but for mod support) 1676 | elif C_MODE == 2: # top screen active only 1677 | # racer 0 is player 1678 | if racer_num == 0: return True 1679 | 1680 | # racer 1: check if should show ghost racer 1681 | if BV.SHOW_GHOST and not BV.REPLAY_MODE: return True 1682 | 1683 | # racer 1: either ghost not enabled, or ghost enabled and in replay mode 1684 | return False 1685 | 1686 | elif C_MODE == 4: # bottom screen active only 1687 | # racer 1 is player 1688 | if racer_num == 1: return True 1689 | 1690 | # racer 0: check if should show ghost racer 1691 | if BV.SHOW_GHOST and not BV.REPLAY_MODE: return True 1692 | 1693 | # racer 0: either ghost not enabled, or ghost enabled and in replay mode 1694 | return False 1695 | 1696 | 1697 | return False 1698 | 1699 | 1700 | 1701 | def racer_has_camera(racer_num): 1702 | return (C_MODE == 0 and racer_num < 2) or (C_MODE == 2 and racer_num == 0) or (C_MODE == 4 and racer_num == 1) 1703 | 1704 | 1705 | 1706 | 1707 | def draw_line(v_a=(0, 0), v_b=(0,0), c_a=(0.0, 0.0, 0.0, 1.0), c_b=(0.0, 0.0, 0.0, 1.0)): 1708 | global ls_vdata 1709 | global ls_cdata 1710 | global ls_numl 1711 | 1712 | 1713 | ####################### 1714 | # Vertex 1 1715 | ####################### 1716 | 1717 | # Position 1718 | ls_vdata.append(v_a[0]) 1719 | ls_vdata.append(v_a[1]) 1720 | 1721 | # Color 1722 | ls_cdata.append(c_a[0]) 1723 | ls_cdata.append(c_a[1]) 1724 | ls_cdata.append(c_a[2]) 1725 | 1726 | # alpha channel 1727 | try: ls_cdata.append(c_a[3]) 1728 | except IndexError: ls_cdata.append(1.0) 1729 | 1730 | ####################### 1731 | # Vertex 2 1732 | ####################### 1733 | 1734 | # Position 1735 | ls_vdata.append(v_b[0]) 1736 | ls_vdata.append(v_b[1]) 1737 | 1738 | # Color 1739 | # Color 1740 | ls_cdata.append(c_b[0]) 1741 | ls_cdata.append(c_b[1]) 1742 | ls_cdata.append(c_b[2]) 1743 | 1744 | # alpha channel 1745 | try: ls_cdata.append(c_b[3]) 1746 | except IndexError: ls_cdata.append(1.0) 1747 | 1748 | ls_numl += 1 1749 | 1750 | 1751 | def draw_lines(pnts, cols): 1752 | 1753 | L_p = len(pnts) 1754 | L_c = len(cols) 1755 | 1756 | if L_p != L_c: raise IndexError("Mismatched sizes between points and color sets: pnts: " + str(L_p) + ", cols: " + str(L_c)) 1757 | 1758 | for i in range(L_p - 1): 1759 | draw_line( 1760 | v_a = pnts[i], 1761 | v_b = pnts[i+1], 1762 | c_a = cols[i], 1763 | c_b = cols[i+1] 1764 | ) 1765 | 1766 | 1767 | 1768 | 1769 | def DRAW_LINESET(): 1770 | global ls_vdata 1771 | global ls_cdata 1772 | global ls_numl 1773 | 1774 | glBindVertexArray(VAO1) 1775 | 1776 | glBindBuffer(GL_ARRAY_BUFFER, VBO1) 1777 | #glBufferData(GL_ARRAY_BUFFER, ls_numl*(2*2)*4, (c_float*(ls_numl*(2*2)))(*ls_vdata), GL_STATIC_DRAW) 1778 | glBufferData(GL_ARRAY_BUFFER, ls_numl*16, (c_float*(ls_numl*4))(*ls_vdata), GL_STATIC_DRAW) 1779 | glVertexAttribPointer(glGetAttribLocation(LINESET_SHADER, 'vin_pos'), 2, GL_FLOAT, GL_FALSE, 0, None) 1780 | glEnableVertexAttribArray(0) 1781 | 1782 | glBindBuffer(GL_ARRAY_BUFFER, VBO2) 1783 | #glBufferData(GL_ARRAY_BUFFER, ls_numl*(4*2)*4, (c_float*(ls_numl*(4*2)))(*ls_cdata), GL_STATIC_DRAW) 1784 | glBufferData(GL_ARRAY_BUFFER, ls_numl*32, (c_float*(ls_numl*8))(*ls_cdata), GL_STATIC_DRAW) 1785 | glVertexAttribPointer(glGetAttribLocation(LINESET_SHADER, 'vin_col'), 4, GL_FLOAT, GL_FALSE, 0, None) 1786 | glEnableVertexAttribArray(1) 1787 | 1788 | glDrawArrays(GL_LINES, 0, ls_numl*2) # The *2 here is necessary 1789 | 1790 | 1791 | 1792 | 1793 | def SHOW_WIDTH_1_VECTORS(): 1794 | global ls_vdata 1795 | global ls_cdata 1796 | global ls_numl 1797 | 1798 | LW = max(1, VEC_W * screen.w / MAP_W * (1 / NUM_WIDTHS)) 1799 | glLineWidth(LW) 1800 | 1801 | 1802 | ls_numl = 0 1803 | ls_vdata = [] 1804 | ls_cdata = [] 1805 | 1806 | # show velocity data 1807 | for i in range(7, -1, -1): 1808 | 1809 | if not is_active_racer(i): continue 1810 | 1811 | OBJ = OBJECTS[i] 1812 | 1813 | OBJ_x = OBJ.x 1814 | OBJ_y = OBJ.y 1815 | OBJ_vel = OBJ.vel 1816 | OBJ_vx = OBJ_vel[0]/256 1817 | OBJ_vy = OBJ_vel[1]/256 1818 | OBJ_vz = OBJ_vel[2]#/256 1819 | OBJ_spd = OBJ.speed/256 1820 | OBJ_mspd = OBJ.max_speed / 256 1821 | OBJ_acc = OBJ.accel/256 1822 | OBJ_angle = OBJ.angle * DEG_2_RAD 1823 | OBJ_v_angle = OBJ.v_angle * DEG_2_RAD 1824 | OBJ_avel = OBJ.angle_vel * DEG_2_RAD 1825 | 1826 | OBJ_c_angle = OBJ.c_angle * DEG_2_RAD 1827 | 1828 | OBJ_tx = OBJ.dest[0] 1829 | OBJ_ty = OBJ.dest[1] 1830 | 1831 | 1832 | 1833 | ################################################ 1834 | ''' 1835 | draw_line( 1836 | v_a = ( 1837 | CAM_POS[0] - 10, 1838 | CAM_POS[1] 1839 | ), 1840 | v_b = ( 1841 | CAM_POS[0] + 10, 1842 | CAM_POS[1] 1843 | ), 1844 | c_a = (1.0, 1.0, 1.0, 1.0), 1845 | c_b = (1.0, 1.0, 1.0, 1.0) 1846 | ) 1847 | 1848 | draw_line( 1849 | v_a = ( 1850 | CAM_POS[0], 1851 | CAM_POS[1] - 10 1852 | ), 1853 | v_b = ( 1854 | CAM_POS[0], 1855 | CAM_POS[1] + 10 1856 | ), 1857 | c_a = (1.0, 1.0, 1.0, 1.0), 1858 | c_b = (1.0, 1.0, 1.0, 1.0) 1859 | ) 1860 | ''' 1861 | ################################################ 1862 | 1863 | 1864 | 1865 | 1866 | # Target coordinates 1867 | ################################################ 1868 | if BV.SHOW_TARGETS: 1869 | t_alpha = 0.6 1870 | t_size = 4 1871 | # target line 1872 | draw_line( 1873 | v_a = (OBJ_x, OBJ_y), 1874 | v_b = (OBJ_tx, OBJ_ty), 1875 | c_a = (1.0, 1.0, 1.0, t_alpha), 1876 | c_b = (1.0, 1.0, 1.0, t_alpha) 1877 | ) 1878 | 1879 | # target point 1880 | ''' 1881 | vs = [] 1882 | cs = [] 1883 | num_segs = 12 1884 | 1885 | for j in range(num_segs+1): 1886 | 1887 | vs.append(( 1888 | (OBJ_tx + math.cos(j * math.pi * 2 / num_segs) * t_size), 1889 | (OBJ_ty + math.sin(j * math.pi * 2 / num_segs) * t_size) 1890 | )) 1891 | 1892 | cs.append((1.0, 1.0, 1.0, t_alpha)) 1893 | 1894 | draw_lines(vs, cs) 1895 | ''' 1896 | ''' 1897 | draw_line( 1898 | v_a = (OBJ_tx-t_size, OBJ_ty), 1899 | v_b = (OBJ_tx+t_size, OBJ_ty), 1900 | c_a = (1.0, 1.0, 1.0, t_alpha), 1901 | c_b = (1.0, 1.0, 1.0, t_alpha) 1902 | ) 1903 | 1904 | draw_line( 1905 | v_a = (OBJ_tx, OBJ_ty-t_size), 1906 | v_b = (OBJ_tx, OBJ_ty+t_size), 1907 | c_a = (1.0, 1.0, 1.0, t_alpha), 1908 | c_b = (1.0, 1.0, 1.0, t_alpha) 1909 | ) 1910 | ''' 1911 | 1912 | ################################################ 1913 | 1914 | 1915 | 1916 | # Velocity components 1917 | ################################################ 1918 | if BV.SHOW_VEL_COMPONENTS: 1919 | # X velocity 1920 | draw_line( 1921 | v_a = (OBJ_x, OBJ_y), 1922 | v_b = ( 1923 | OBJ_x + OBJ_vx * VECTOR_SCALE, 1924 | OBJ_y 1925 | ), 1926 | c_a = (1.0, 1.0, 1.0, 1.0), 1927 | c_b = (0.0, 1.0, 1.0, 1.0) 1928 | ) 1929 | 1930 | 1931 | 1932 | # Y velocity 1933 | draw_line( 1934 | v_a = (OBJ_x, OBJ_y), 1935 | v_b = ( 1936 | OBJ_x, 1937 | OBJ_y + OBJ_vy * VECTOR_SCALE 1938 | ), 1939 | c_a = (1.0, 1.0, 1.0, 1.0), 1940 | c_b = (0.0, 1.0, 1.0, 1.0) 1941 | ) 1942 | 1943 | 1944 | ################################################ 1945 | 1946 | 1947 | 1948 | 1949 | # Direction Normalized 1950 | ################################################ 1951 | if BV.SHOW_DIR_NORMALIZED: 1952 | 1953 | x_norm = - math.sin(OBJ_angle) #OBJ_mspd 1954 | y_norm = - math.cos(OBJ_angle) #OBJ_mspd 1955 | 1956 | # Normalized direction 1957 | draw_line( 1958 | v_a = (OBJ_x, OBJ_y), 1959 | v_b = ( 1960 | OBJ_x + x_norm * VECTOR_SCALE * 0.5, 1961 | OBJ_y + y_norm * VECTOR_SCALE * 0.5 1962 | ), 1963 | c_a = (1.0, 1.0, 1.0, 1.0), 1964 | c_b = (0.0, 1.0, 1.0, 1.0) 1965 | ) 1966 | 1967 | ################################################ 1968 | 1969 | 1970 | # Camera 1971 | ################################################ 1972 | if (BV.SHOW_CAMERA_ANGLE or BV.SHOW_CAM) and racer_has_camera(i): 1973 | 1974 | C = - math.sin(OBJ_c_angle) 1975 | S = - math.cos(OBJ_c_angle) 1976 | 1977 | F_x = OBJ_x + C * VECTOR_SCALE 1978 | F_y = OBJ_y + S * VECTOR_SCALE 1979 | 1980 | 1981 | if BV.SHOW_CAMERA_ANGLE: 1982 | # Actual direction 1983 | draw_line( 1984 | v_a = (OBJ_x, OBJ_y), 1985 | v_b = (F_x, F_y), 1986 | c_a = (0.9, 0.0, 1.0, 1.0), 1987 | c_b = (0.9, 0.0, 1.0, 1.0) 1988 | ) 1989 | 1990 | 1991 | 1992 | 1993 | C_W = CAM_WIDTH 1994 | C_alpha = CAM_LINE_ALPHA 1995 | 1996 | if not BV.SHOW_CAM: 1997 | C_W = 0.5 * VECTOR_SCALE 1998 | #C_alpha = 1.0 1999 | 2000 | R_x = OBJ_x - C * cam_scl_b * C_SCL - S * C_W 2001 | R_y = OBJ_y - S * cam_scl_b * C_SCL + C * C_W 2002 | 2003 | L_x = OBJ_x - C * cam_scl_b * C_SCL + S * C_W 2004 | L_y = OBJ_y - S * cam_scl_b * C_SCL - C * C_W 2005 | 2006 | 2007 | 2008 | # Ortho direction 2009 | draw_line( 2010 | v_a = (L_x, L_y), 2011 | v_b = (R_x, R_y), 2012 | c_a = (0.9, 0.0, 1.0, C_alpha), 2013 | c_b = (0.9, 0.0, 1.0, C_alpha) 2014 | ) 2015 | 2016 | 2017 | 2018 | 2019 | if BV.SHOW_CAM: 2020 | CAM_DIST = 4 2021 | 2022 | C_x = OBJ_x - C * cam_scl_a * C_SCL 2023 | C_y = OBJ_y - S * cam_scl_a * C_SCL 2024 | 2025 | 2026 | # Camera direction 2027 | draw_line( 2028 | v_a = (OBJ_x, OBJ_y), 2029 | v_b = (C_x, C_y), 2030 | c_a = (0.9, 0.0, 1.0, CAM_LINE_ALPHA), 2031 | c_b = (0.9, 0.0, 1.0, CAM_LINE_ALPHA) 2032 | ) 2033 | 2034 | 2035 | 2036 | # Cam_line A 2037 | draw_line( 2038 | v_a = (C_x, C_y), 2039 | v_b = ( 2040 | C_x + (L_x - C_x) * CAM_DIST, 2041 | C_y + (L_y - C_y) * CAM_DIST 2042 | ), 2043 | c_a = (1.0, 1.0, 0.0, CAM_LINE_ALPHA), 2044 | c_b = (1.0, 1.0, 0.0, 0.0) 2045 | ) 2046 | 2047 | 2048 | # Cam_line B 2049 | draw_line( 2050 | v_a = (C_x, C_y), 2051 | v_b = ( 2052 | C_x + (R_x - C_x) * CAM_DIST, 2053 | C_y + (R_y - C_y) * CAM_DIST 2054 | ), 2055 | c_a = (1.0, 1.0, 0.0, CAM_LINE_ALPHA), 2056 | c_b = (1.0, 1.0, 0.0, 0.0) 2057 | ) 2058 | 2059 | 2060 | 2061 | 2062 | 2063 | 2064 | 2065 | 2066 | 2067 | ################################################ 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | 2075 | 2076 | 2077 | # Velocity Vector 2078 | ################################################ 2079 | if BV.SHOW_VEL_VECTOR: 2080 | 2081 | vx = - math.sin(OBJ_v_angle) * OBJ_spd 2082 | vy = - math.cos(OBJ_v_angle) * OBJ_spd 2083 | 2084 | # velocity 2085 | draw_line( 2086 | v_a = (OBJ_x, OBJ_y), 2087 | v_b = ( 2088 | OBJ_x + vx * VECTOR_SCALE, 2089 | OBJ_y + vy * VECTOR_SCALE 2090 | ), 2091 | c_a = (1.0, 1.0, 1.0, 1.0), 2092 | c_b = (0.0, 1.0, 0.2, 1.0) 2093 | ) 2094 | ################################################ 2095 | 2096 | 2097 | 2098 | 2099 | 2100 | 2101 | 2102 | # Angular Velocity 2103 | ################################################ 2104 | if BV.SHOW_DIR_NORMALIZED and racer_has_camera(i): 2105 | if OBJ_spd != 0: 2106 | 2107 | ang = OBJ_c_angle 2108 | inc_ang = OBJ_avel / A_VEL_SEGMENTS 2109 | 2110 | if OBJ_avel < -math.pi: 2111 | inc_ang = (OBJ_avel + math.pi*2) / A_VEL_SEGMENTS 2112 | 2113 | 2114 | 2115 | vs = [] 2116 | cs = [] 2117 | 2118 | for j in range(A_VEL_SEGMENTS+1): # do first point, and then A_VEL_SEGMENTS new points 2119 | 2120 | x_norm = - math.sin(ang) #OBJ_mspd 2121 | y_norm = - math.cos(ang) #OBJ_mspd 2122 | 2123 | ang += inc_ang 2124 | 2125 | vs.append(( 2126 | OBJ_x + x_norm * VECTOR_SCALE, 2127 | OBJ_y + y_norm * VECTOR_SCALE 2128 | )) 2129 | 2130 | cs.append((1.0, 0.0, 1.0, 1.0)) 2131 | 2132 | # return line 2133 | draw_line( 2134 | v_a = ( 2135 | (OBJ_x + vs[-1][0]) / 2, 2136 | (OBJ_y + vs[-1][1]) / 2 2137 | ), 2138 | v_b = vs[-1], 2139 | c_a = (1.0, 0.0, 1.0, 0.0), 2140 | c_b = (1.0, 0.0, 1.0, 0.3) 2141 | ) 2142 | 2143 | 2144 | draw_lines(vs, cs) 2145 | 2146 | 2147 | 2148 | 2149 | ################################################ 2150 | 2151 | 2152 | 2153 | 2154 | 2155 | 2156 | DRAW_LINESET() 2157 | 2158 | 2159 | 2160 | 2161 | 2162 | 2163 | 2164 | def SHOW_WIDTH_2_VECTORS(): 2165 | global ls_vdata 2166 | global ls_cdata 2167 | global ls_numl 2168 | 2169 | LW = max(1, VEC_W * screen.w / MAP_W * (2 / NUM_WIDTHS)) 2170 | glLineWidth(LW) 2171 | 2172 | 2173 | ls_numl = 0 2174 | ls_vdata = [] 2175 | ls_cdata = [] 2176 | 2177 | # show velocity data 2178 | for i in range(7, -1, -1): 2179 | 2180 | if not is_active_racer(i): continue 2181 | 2182 | OBJ = OBJECTS[i] 2183 | 2184 | OBJ_x = OBJ.x 2185 | OBJ_y = OBJ.y 2186 | OBJ_vel = OBJ.vel 2187 | OBJ_vx = OBJ_vel[0]/256 2188 | OBJ_vy = OBJ_vel[1]/256 2189 | OBJ_vz = OBJ_vel[2]#/256 2190 | OBJ_spd = OBJ.speed / 256 2191 | OBJ_mspd = OBJ.max_speed / 256 2192 | OBJ_v_angle = OBJ.v_angle * DEG_2_RAD 2193 | OBJ_angle = OBJ.angle * DEG_2_RAD 2194 | 2195 | 2196 | 2197 | 2198 | 2199 | 2200 | 2201 | 2202 | 2203 | 2204 | DRAW_LINESET() 2205 | 2206 | 2207 | 2208 | 2209 | def SHOW_WIDTH_3_VECTORS(): 2210 | global ls_vdata 2211 | global ls_cdata 2212 | global ls_numl 2213 | 2214 | LW = max(1, VEC_W * screen.w / MAP_W * (3 / NUM_WIDTHS)) 2215 | glLineWidth(LW) 2216 | 2217 | 2218 | ls_numl = 0 2219 | ls_vdata = [] 2220 | ls_cdata = [] 2221 | 2222 | # show velocity data 2223 | for i in range(7, -1, -1): 2224 | 2225 | if not is_active_racer(i): continue 2226 | 2227 | OBJ = OBJECTS[i] 2228 | 2229 | OBJ_x = OBJ.x 2230 | OBJ_y = OBJ.y 2231 | OBJ_vel = OBJ.vel 2232 | OBJ_vx = OBJ_vel[0]/256 2233 | OBJ_vy = OBJ_vel[1]/256 2234 | OBJ_vz = OBJ_vel[2]#/256 2235 | OBJ_spd = OBJ.speed / 256 2236 | OBJ_mspd = OBJ.max_speed / 256 2237 | OBJ_v_angle = OBJ.v_angle * DEG_2_RAD 2238 | OBJ_angle = OBJ.angle * DEG_2_RAD 2239 | OBJ_acc = OBJ.accel / 256 2240 | 2241 | 2242 | 2243 | 2244 | 2245 | 2246 | 2247 | 2248 | 2249 | # Momentum Normalized 2250 | ################################################ 2251 | if BV.SHOW_VEL_NORMALIZED: 2252 | 2253 | if OBJ_spd != 0: 2254 | 2255 | x_norm = - math.sin(OBJ_v_angle) * OBJ_mspd 2256 | y_norm = - math.cos(OBJ_v_angle) * OBJ_mspd 2257 | 2258 | 2259 | # Normalized velocity 2260 | ''' 2261 | ls_vdata.append(OBJ_x) 2262 | ls_vdata.append(OBJ_y) 2263 | #--- 2264 | ls_vdata.append(OBJ_x + x_norm * VECTOR_SCALE) 2265 | ls_vdata.append(OBJ_y + y_norm * VECTOR_SCALE) 2266 | 2267 | 2268 | # Colors 2269 | ls_cdata.append(0.0) 2270 | ls_cdata.append(0.0) 2271 | ls_cdata.append(0.0) 2272 | #--- 2273 | ls_cdata.append(0.0) 2274 | ls_cdata.append(0.0) 2275 | ls_cdata.append(0.0) 2276 | 2277 | ls_numl += 1 2278 | ''' 2279 | 2280 | draw_line( 2281 | v_a = (OBJ_x, OBJ_y), 2282 | v_b = ( 2283 | OBJ_x + x_norm * VECTOR_SCALE, 2284 | OBJ_y + y_norm * VECTOR_SCALE 2285 | ), 2286 | c_a = (0.0, 0.0, 0.0, 1.0), 2287 | c_b = (0.0, 0.0, 0.0, 1.0) 2288 | ) 2289 | ################################################ 2290 | 2291 | 2292 | 2293 | # Acceleration 2294 | ################################################ 2295 | if BV.SHOW_ACCELERATION: 2296 | 2297 | if OBJ_acc != 0: 2298 | 2299 | C = - math.sin(OBJ_v_angle) 2300 | S = - math.cos(OBJ_v_angle) 2301 | 2302 | #x_vel = C * OBJ_spd 2303 | #y_vel = S * OBJ_spd 2304 | 2305 | x_acc = C * (OBJ_acc * 64) 2306 | y_acc = S * (OBJ_acc * 64) 2307 | 2308 | 2309 | # Normalized velocity 2310 | 2311 | if OBJ_acc < 0: 2312 | # Colors 2313 | COL_A = (0.4, 0.2, 0.2, 1.0) 2314 | COL_B = (1.0, 0.5, 0.5, 1.0) 2315 | else: 2316 | # Colors 2317 | COL_A = (0.2, 0.4, 0.2, 1.0) 2318 | COL_B = (0.5, 1.0, 0.5, 1.0) 2319 | 2320 | 2321 | draw_line( 2322 | v_a = (OBJ_x, OBJ_y), 2323 | v_b = ( 2324 | OBJ_x + x_acc * VECTOR_SCALE, 2325 | OBJ_y + y_acc * VECTOR_SCALE 2326 | ), 2327 | c_a = COL_A, 2328 | c_b = COL_B 2329 | ) 2330 | 2331 | ################################################ 2332 | 2333 | 2334 | 2335 | 2336 | 2337 | 2338 | 2339 | 2340 | 2341 | 2342 | 2343 | DRAW_LINESET() 2344 | 2345 | 2346 | 2347 | 2348 | 2349 | 2350 | 2351 | 2352 | 2353 | def SHOW_EXTRA_VECTORS(): 2354 | glUseProgram(LINESET_SHADER) 2355 | 2356 | glUniform1f(lset_S_w, screen.SCALE * screen.DEFAULT_WIDTH / MAP_W) 2357 | glUniform1f(lset_S_h, screen.SCALE * screen.DEFAULT_HEIGHT / MAP_H) 2358 | glUniform1f(lset_S_x, screen.x) 2359 | glUniform1f(lset_S_y, screen.y) 2360 | glUniform1f(lset_W_w, WINDOW_WIDTH) 2361 | glUniform1f(lset_W_h, WINDOW_HEIGHT) 2362 | 2363 | 2364 | 2365 | 2366 | #SHOW_WIDTH_3_VECTORS() 2367 | 2368 | #SHOW_WIDTH_2_VECTORS() 2369 | 2370 | SHOW_WIDTH_1_VECTORS() 2371 | 2372 | 2373 | 2374 | 2375 | 2376 | glUseProgram(0) 2377 | 2378 | glLineWidth(1) 2379 | 2380 | 2381 | 2382 | 2383 | 2384 | 2385 | 2386 | 2387 | MAX_SPD = 0x100 2388 | MIN_SPD = 0xc0 2389 | 2390 | 2391 | 2392 | 2393 | 2394 | 2395 | 2396 | 2397 | 2398 | 2399 | 2400 | def init_tk(): 2401 | # ============================ Tkinter setup stuff ================================ 2402 | 2403 | global BV 2404 | 2405 | 2406 | 2407 | 2408 | 2409 | 2410 | # setup window 2411 | 2412 | BV = BVmain.BV_Instance(WINDOW_SIZE=(WINDOW_WIDTH, WINDOW_HEIGHT), PYG_SIZE=(width, height)) 2413 | 2414 | 2415 | os.environ['SDL_WINDOWID'] = str(BV.embed.winfo_id()) 2416 | 2417 | if platform.system() == "Windows": # system()??? 2418 | os.environ['SDL_VIDEODRIVER'] = 'windib' 2419 | 2420 | BV.setup_window() 2421 | 2422 | 2423 | 2424 | # ================================================================================= 2425 | 2426 | 2427 | 2428 | 2429 | 2430 | 2431 | 2432 | 2433 | def init_pygame(): 2434 | # ============================ Pygame setup stuff ================================= 2435 | global GL_CANVAS 2436 | global screen 2437 | global font 2438 | 2439 | pygame.init() 2440 | 2441 | pg_flags = DOUBLEBUF | OPENGL 2442 | 2443 | GL_CANVAS = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pg_flags) 2444 | 2445 | Assets.create_pygame_images() 2446 | TrackHelper.set_render_quality(zone_scl=ZONE_SCL, flow_scl=FLOW_SCL) 2447 | 2448 | # these will get automatically called by set_render_quality since the scales for both will be changed 2449 | #TrackHelper.gen_arrow_images() 2450 | #TrackHelper.gen_cp_tiles() 2451 | 2452 | 2453 | screen = KS.Screen((WINDOW_WIDTH, WINDOW_HEIGHT)) 2454 | 2455 | font = None 2456 | # ================================================================================= 2457 | 2458 | 2459 | def init_gl(): 2460 | # ============================ OpenGL setup stuff ================================= 2461 | 2462 | 2463 | 2464 | glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT) 2465 | glDepthRange(0, 1) 2466 | glMatrixMode(GL_PROJECTION) 2467 | #gluPerspective(0, (WINDOW_WIDTH/WINDOW_HEIGHT), 0.1, 50.0) 2468 | #view_mat = IndentityMat44() 2469 | glMatrixMode(GL_MODELVIEW) 2470 | glLoadIdentity() 2471 | glShadeModel(GL_SMOOTH) 2472 | glClearColor(0.0, 0.0, 0.0, 0.0) 2473 | glClearDepth(1.0) 2474 | glDisable(GL_DEPTH_TEST) 2475 | glDisable(GL_LIGHTING) 2476 | glDepthFunc(GL_LEQUAL) 2477 | glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) 2478 | glEnable(GL_BLEND) 2479 | glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) 2480 | 2481 | 2482 | GEN_SHADERS() 2483 | GENERATE_TEXTURES() 2484 | GEN_VBO() 2485 | GEN_FBO() 2486 | 2487 | 2488 | 2489 | 2490 | 2491 | 2492 | glBindFramebuffer(GL_FRAMEBUFFER, FBO_0) 2493 | 2494 | # ================================================================================= 2495 | 2496 | 2497 | 2498 | 2499 | 2500 | 2501 | 2502 | 2503 | 2504 | 2505 | # ================================================================================================== 2506 | # MAIN 2507 | # ================================================================================================== 2508 | 2509 | if platform.system() == "Windows": 2510 | # set windows tray icon (if using windows of course :p) 2511 | app_id = u'mrl314.booview.window.v20' 2512 | ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id) 2513 | 2514 | print("====================================================") 2515 | print(" Welcome to BooView (v" + PY_VERSION_NUMBER + ") by MrL314! ") 2516 | print("====================================================") 2517 | 2518 | 2519 | 2520 | 2521 | WINDOW_SCALE = 1 2522 | 2523 | FRAMES = 0 2524 | 2525 | data = b'' 2526 | 2527 | BV = None 2528 | 2529 | CONN_LUA_V = "" 2530 | CONN_NAME = "" 2531 | 2532 | print("\n# Please connect socket via BizHawk's Lua Console. #\n") 2533 | 2534 | while True: 2535 | 2536 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 2537 | s.bind((HOST, PORT)) 2538 | s.listen() 2539 | 2540 | conn, addr = s.accept() 2541 | 2542 | SOCKET_CONN = conn 2543 | 2544 | BYTES_AS_LIST = [] 2545 | 2546 | 2547 | with conn: 2548 | 2549 | _TIME = time.perf_counter() 2550 | 2551 | try: 2552 | 2553 | 2554 | 2555 | 2556 | 2557 | # ================================= 2558 | # INIT 2559 | # ================================= 2560 | 2561 | #send_frame_signal() # call "frame" once to set up 2562 | 2563 | # check if lua socket properly connected 2564 | try: 2565 | wait_for_sync(initial_sync=True) 2566 | except Exception as e: 2567 | print("[INFO] Connection failed. This occasionally happens when\n starting up for the first time. Try reconnecting!") 2568 | 2569 | try: CLOSE_CONN() 2570 | except ConnectionResetError: pass 2571 | except OSError: pass 2572 | 2573 | continue 2574 | 2575 | 2576 | #_ = RECV_DATA() # dummy! 2577 | 2578 | 2579 | 2580 | BAD_LUA_CONN = False 2581 | 2582 | try: 2583 | CONN_LUA_V = match_lua_id(lua_version=LUA_VERSION_NUMBER) # tell Lua that it is connected to the python script (better safe than sorry) 2584 | except ZeroDivisionError: 2585 | BAD_LUA_CONN = True 2586 | except Exception as e: 2587 | raise e 2588 | 2589 | if BAD_LUA_CONN: 2590 | print("[ERROR] Could not verify identity of connected socket.\n Please ensure you are using version " + LUA_VERSION_NUMBER + " of the LuaSide.lua script\n") 2591 | try: CLOSE_CONN() 2592 | except ConnectionResetError: pass 2593 | except OSError: pass 2594 | 2595 | break 2596 | 2597 | CONN_NAME = str(addr[0]) + " (" + str(CONN_LUA_V) + ")" 2598 | 2599 | print('[INFO] ID: ID_BVPY_' + str(PY_VERSION_NUMBER)) 2600 | print('[INFO] Connected to', CONN_NAME) 2601 | 2602 | send_frame_signal() # call "frame" once to set up 2603 | wait_for_sync() 2604 | 2605 | 2606 | send_yield_signal() 2607 | 2608 | 2609 | init_tk() 2610 | init_pygame() 2611 | init_gl() 2612 | 2613 | send_unyield_signal() 2614 | 2615 | 2616 | map_ready = False 2617 | map_changed = False 2618 | map_loading = False 2619 | 2620 | prev_gamemode = 0x02 2621 | prev_track_number = 0xff 2622 | 2623 | ghost_mode_byte = -2 2624 | demo_byte = -2 2625 | 2626 | done = False 2627 | 2628 | 2629 | 2630 | # ================================= 2631 | 2632 | 2633 | 2634 | 2635 | 2636 | 2637 | # main loop 2638 | 2639 | while done == False: 2640 | 2641 | 2642 | 2643 | 2644 | 2645 | 2646 | 2647 | 2648 | 2649 | 2650 | 2651 | 2652 | 2653 | 2654 | # ======================================= Button Display ======================================== 2655 | 2656 | 2657 | for i in range(8): 2658 | if BV.racers_to_follow["racer" + str(i)]: 2659 | if not is_active_racer(i): 2660 | BV.toggle_follow(i) 2661 | 2662 | 2663 | if BV.TRAILS_TOGGLED: 2664 | for i in range(8): OBJECTS[i].show_trails = BV.TRAIL_LINES # maybe change later? 2665 | 2666 | BV.TRAILS_TOGGLED = False 2667 | 2668 | 2669 | 2670 | 2671 | 2672 | 2673 | 2674 | 2675 | 2676 | 2677 | 2678 | 2679 | 2680 | 2681 | 2682 | # ====================================================================================================== 2683 | 2684 | 2685 | 2686 | 2687 | 2688 | 2689 | 2690 | 2691 | 2692 | 2693 | 2694 | 2695 | 2696 | # ====================================================================================================== 2697 | # Get pygame event values and setup 2698 | 2699 | 2700 | # pre-polling clear 2701 | #for m_button in CURR_FRAME_MOUSE: 2702 | # CURR_FRAME_MOUSE[m_button] = False 2703 | 2704 | #for k_button in CURR_FRAME_KEYS: 2705 | # CURR_FRAME_KEYS[k_button] = False 2706 | CURR_FRAME_MOUSE["scroll_up"] = False 2707 | CURR_FRAME_MOUSE["scroll_down"] = False 2708 | 2709 | ANIM_TIMER += 1 2710 | FRAME_NUMBER += 1 2711 | if ANIM_TIMER > (ANIM_TIMER_SWAP * 4) - 1: 2712 | ANIM_TIMER = 0 2713 | 2714 | SPRITE_SCALE = BV.get_sprite_scale() 2715 | 2716 | 2717 | 2718 | #T = time.perf_counter() 2719 | 2720 | for event in pygame.event.get(): 2721 | if event.type == QUIT: 2722 | done = True 2723 | BV.root.destroy() 2724 | BV.root = None 2725 | 2726 | elif event.type == pygame.MOUSEBUTTONDOWN: 2727 | 2728 | if event.button >= 1 and event.button <= 5: 2729 | CURR_FRAME_MOUSE[mouse_button_names[event.button - 1]] = True 2730 | 2731 | 2732 | 2733 | elif event.type == pygame.MOUSEBUTTONUP: 2734 | 2735 | if event.button >= 1 and event.button <= 3: 2736 | CURR_FRAME_MOUSE[mouse_button_names[event.button - 1]] = False 2737 | 2738 | elif event.type == pygame.MOUSEWHEEL: 2739 | 2740 | if event.y < 0: 2741 | CURR_FRAME_MOUSE["scroll_down"] = True 2742 | elif event.y > 0: 2743 | CURR_FRAME_MOUSE["scroll_up"] = True 2744 | 2745 | 2746 | elif event.type == pygame.MOUSEMOTION: 2747 | #mouse_rel = pygame.mouse.get_rel() 2748 | pass 2749 | 2750 | elif event.type == pygame.KEYDOWN: 2751 | #parse keys pressed here 2752 | if event.key in KEY_NAMES: 2753 | CURR_FRAME_KEYS[KEY_NAMES[event.key]] = True 2754 | 2755 | elif event.type == pygame.KEYUP: 2756 | #parse keys released here 2757 | if event.key in KEY_NAMES: 2758 | CURR_FRAME_KEYS[KEY_NAMES[event.key]] = False 2759 | 2760 | 2761 | 2762 | 2763 | 2764 | 2765 | ## SET UP "CLICKED/RELEASED" OBJECTS 2766 | 2767 | for m_button in CURR_FRAME_MOUSE: 2768 | 2769 | if CURR_FRAME_MOUSE[m_button] == True: 2770 | if PREV_FRAME_MOUSE[m_button] == True: 2771 | MOUSE_NEW[m_button] = False 2772 | elif PREV_FRAME_MOUSE[m_button] == False: 2773 | MOUSE_NEW[m_button] = True 2774 | 2775 | elif CURR_FRAME_MOUSE[m_button] == False: 2776 | if PREV_FRAME_MOUSE[m_button] == True: 2777 | MOUSE_RELEASED[m_button] = True 2778 | elif PREV_FRAME_MOUSE[m_button] == False: 2779 | MOUSE_RELEASED[m_button] = False 2780 | 2781 | 2782 | for k_button in CURR_FRAME_KEYS: 2783 | 2784 | if CURR_FRAME_KEYS[k_button] == True: 2785 | if PREV_FRAME_KEYS[k_button] == True: 2786 | KEYS_NEW[k_button] = False 2787 | elif PREV_FRAME_KEYS[k_button] == False: 2788 | KEYS_NEW[k_button] = True 2789 | 2790 | elif CURR_FRAME_KEYS[k_button] == False: 2791 | if PREV_FRAME_KEYS[k_button] == True: 2792 | KEYS_RELEASED[k_button] = True 2793 | elif PREV_FRAME_KEYS[k_button] == False: 2794 | KEYS_RELEASED[k_button] = False 2795 | 2796 | 2797 | 2798 | 2799 | 2800 | 2801 | 2802 | 2803 | #T_0 = time.perf_counter() - T 2804 | 2805 | #print(100 * T_0 * 60) 2806 | 2807 | 2808 | # ====================================================================================================== 2809 | 2810 | 2811 | 2812 | 2813 | 2814 | 2815 | 2816 | try: 2817 | 2818 | CH_BYTES.set_bytes([]) 2819 | OBJ_BYTES.set_bytes([]) 2820 | ITEM_BYTES.set_bytes([]) 2821 | 2822 | SOCKET_CONN.settimeout(1/120) 2823 | try: 2824 | data = RECV_DATA(block=False) 2825 | except socket.timeout: 2826 | data = "SOCKET_TIMEOUT" 2827 | BYTES_AS_LIST = [] 2828 | SOCKET_CONN.settimeout(None) 2829 | 2830 | 2831 | 2832 | 2833 | 2834 | 2835 | if not data: 2836 | CLOSE_CONN() 2837 | break 2838 | 2839 | 2840 | 2841 | 2842 | if data == "close" or data == "close\n": 2843 | break 2844 | 2845 | elif data[:7] == 'CH_DATA': 2846 | 2847 | data = data[7:] 2848 | 2849 | 2850 | raw_bytes = data.split("\n") 2851 | 2852 | 2853 | CH_BYTES.set_bytes(bytes.fromhex(raw_bytes[0])) 2854 | 2855 | OBJ_BYTES.set_bytes(bytes.fromhex(raw_bytes[1])) 2856 | 2857 | ITEM_BYTES.set_bytes(bytes.fromhex(raw_bytes[2])) 2858 | 2859 | # map update bytes 2860 | MAP_UPDATE_BYTES = bytes.fromhex(raw_bytes[3]) 2861 | 2862 | raw_bytes = raw_bytes[4:] 2863 | 2864 | elif data == "SOCKET_TIMEOUT": 2865 | #print("[INFO] TIMEOUT") 2866 | do_frame_update() 2867 | else: 2868 | pass 2869 | 2870 | 2871 | 2872 | 2873 | 2874 | if BV.in_file_dialogue: 2875 | 2876 | send_yield_signal() 2877 | 2878 | file_path = tk.filedialog.askopenfilename(filetypes=[("SRAM files", ".srm .sram")]) 2879 | 2880 | _unpaused = True 2881 | 2882 | send_unyield_signal() 2883 | 2884 | 2885 | if file_path != "": 2886 | 2887 | SRM_DATA = [] 2888 | with open(file_path, "rb") as SRM_FILE: 2889 | SRM_DATA = SRM_FILE.read() 2890 | 2891 | srm_send = "" 2892 | for d in SRM_DATA: 2893 | srm_send += " " + str(d) 2894 | 2895 | send_w_sram_signal(srm_send) 2896 | 2897 | print("[INFO] Uploaded data from ", file_path) 2898 | 2899 | 2900 | BV.set_not_in_file_dialogue() 2901 | 2902 | do_frame_update() 2903 | 2904 | 2905 | continue 2906 | 2907 | 2908 | 2909 | 2910 | 2911 | 2912 | 2913 | BYTES_AS_LIST = CH_BYTES.get_bytes() 2914 | 2915 | if BYTES_AS_LIST != []: 2916 | 2917 | 2918 | ch_bytes = byte_buffer(BYTES_AS_LIST) 2919 | obj_bytes = byte_buffer(OBJ_BYTES.get_bytes()) 2920 | item_bytes = byte_buffer(ITEM_BYTES.get_bytes()) 2921 | 2922 | 2923 | 2924 | 2925 | gamemode = next_byte(ch_bytes) 2926 | 2927 | C_MODE = next_byte(ch_bytes) 2928 | 2929 | p_ghost_mode_byte = ghost_mode_byte 2930 | ghost_mode_byte = next_byte(ch_bytes) 2931 | 2932 | ghost_mode_2 = next_byte(ch_bytes) 2933 | 2934 | p_demo_byte = demo_byte 2935 | demo_byte = next_byte(ch_bytes) 2936 | 2937 | 2938 | if ghost_mode_byte == 0x02 and demo_byte == 0: 2939 | if not (p_ghost_mode_byte == 0x02 and p_demo_byte == 0): 2940 | BV.REPLAY_BUTTON.config(state="normal") 2941 | 2942 | if BV.REPLAY_MODE: 2943 | BV.SHOW_GHOST = True 2944 | BV.TRAIL_LINES = True 2945 | SPRITE_SCALE = 2 2946 | BV.set_tkvar('SPRITE_SCALE', "2") 2947 | BV.SPRITE_SIZE_WIDGET.config(state='disabled') 2948 | 2949 | if C_MODE == 2: 2950 | OBJECTS[0].show_trails = True 2951 | elif C_MODE == 4: 2952 | OBJECTS[1].show_trails = True 2953 | 2954 | else: 2955 | pass 2956 | 2957 | BV.SHOW_GHOST = True 2958 | else: 2959 | if (p_ghost_mode_byte == 0x02 and p_demo_byte == 0): 2960 | BV.SPRITE_SIZE_WIDGET.config(state='readonly') 2961 | 2962 | BV.SHOW_GHOST = False 2963 | BV.REPLAY_MODE = False 2964 | BV.REPLAY_BUTTON.config(relief=RAISED) 2965 | BV.REPLAY_BUTTON.config(state="disabled") 2966 | 2967 | BV.SHOW_GHOST = ghost_mode_2 2968 | 2969 | 2970 | 2971 | 2972 | 2973 | 2974 | 2975 | # do not try to update stuff if not in a right gamemode 2976 | if not (gamemode == 0x02 or gamemode == 0x0e): 2977 | if prev_gamemode == 0x02 or prev_gamemode == 0x0e: 2978 | for i in range(8): 2979 | 2980 | #BV.racers_to_follow["racer" + str(i)] = False 2981 | #BV.P_FOLLOW[i] = -1 2982 | 2983 | BV.FOLLOW_BUTTONS[i].config(bg="#f0f0f0") 2984 | BV.FOLLOW_BUTTONS[i].config(image=Assets.EMPTY_TK) 2985 | BV.FOLLOW_BUTTONS[i].config(state="disabled") 2986 | 2987 | 2988 | BV.REPLAY_BUTTON.config(state="disabled") 2989 | BV.REPLAY_MODE = False 2990 | BV.SHOW_GHOST = False 2991 | 2992 | 2993 | do_frame_update() 2994 | 2995 | 2996 | 2997 | for m_button in CURR_FRAME_MOUSE: 2998 | PREV_FRAME_MOUSE[m_button] = CURR_FRAME_MOUSE[m_button] 2999 | 3000 | for k_button in CURR_FRAME_KEYS: 3001 | PREV_FRAME_KEYS[k_button] = CURR_FRAME_KEYS[k_button] 3002 | 3003 | ## set up prev values 3004 | prev_gamemode = gamemode 3005 | prev_track_number = -1 3006 | 3007 | continue 3008 | 3009 | 3010 | # In correct gamemode to display 3011 | 3012 | gametype = next_byte(ch_bytes) 3013 | 3014 | 3015 | 3016 | ###### Control what map should be displayed 3017 | display_overlay = True 3018 | 3019 | 3020 | 3021 | current_track_number = next_byte(ch_bytes) 3022 | 3023 | current_theme_number = next_byte(ch_bytes) 3024 | current_theme_object = next_byte(ch_bytes) 3025 | 3026 | 3027 | 3028 | if current_track_number != prev_track_number: 3029 | 3030 | send_reset_map_signal() 3031 | 3032 | 3033 | RECV_DATA() # nonce 3034 | 3035 | send_yield_signal() 3036 | 3037 | map_loading = True 3038 | 3039 | setup_map_socket() 3040 | 3041 | 3042 | for i in range(8): 3043 | OBJECTS[i].reset_trail() 3044 | 3045 | #BV.racers_to_follow["racer" + str(i)] = False 3046 | BV.P_FOLLOW[i] = -1 3047 | #print("CLEARING BV.P_FOLLOW[" + str(i) + "] from track change") 3048 | 3049 | BV.FOLLOW_BUTTONS[i].config(bg="#f0f0f0") 3050 | BV.FOLLOW_BUTTONS[i].config(image=Assets.EMPTY_TK) 3051 | BV.FOLLOW_BUTTONS[i].config(state="disabled") 3052 | 3053 | map_changed = True 3054 | 3055 | else: 3056 | 3057 | 3058 | 3059 | 3060 | byte_list = MAP_UPDATE_BYTES 3061 | 3062 | map_updates = [] 3063 | 3064 | while len(byte_list) >= 3: 3065 | 3066 | offs = byte_list[0] + (byte_list[1] * 256) 3067 | val = byte_list[2] 3068 | 3069 | map_updates.append((offs, val)) 3070 | 3071 | byte_list = byte_list[3:] 3072 | 3073 | update_map_data(map_updates) 3074 | 3075 | 3076 | 3077 | 3078 | 3079 | 3080 | 3081 | 3082 | 3083 | 3084 | 3085 | 3086 | 3087 | 3088 | 3089 | 3090 | if map_ready: 3091 | 3092 | FRAMES += 1 3093 | 3094 | 3095 | 3096 | 3097 | 3098 | ########################################################### 3099 | 3100 | if map_changed: 3101 | update_tilemap() 3102 | update_zone_flow() 3103 | map_changed = False 3104 | 3105 | if map_loading: 3106 | map_loading = False 3107 | send_unyield_signal() 3108 | 3109 | 3110 | # render map 3111 | renderTexture(TILEMAP_TEX, pos=(screen.x+screen.w/2, screen.y+screen.h/2), dim=(screen.w, screen.h), centered=True) 3112 | 3113 | 3114 | if BV.show_zones: renderTexture(ZONEMAP_TEX, pos=(screen.x+screen.w/2, screen.y+screen.h/2), dim=(screen.w, screen.h), centered=True) 3115 | 3116 | if BV.show_flows: renderTexture(FLOWMAP_TEX, pos=(screen.x+screen.w/2, screen.y+screen.h/2), dim=(screen.w, screen.h), centered=True) 3117 | 3118 | 3119 | ########################################################### 3120 | 3121 | 3122 | 3123 | 3124 | 3125 | 3126 | camera_angle = map_value(next_byte(ch_bytes), "degrees") + 90 3127 | 3128 | camx = next_word(ch_bytes) + 0x80 3129 | camy = next_word(ch_bytes) + 0x66 3130 | 3131 | CAM_POS = (camx, camy) 3132 | 3133 | 3134 | 3135 | ## DISPLAY OVERLAY DATA OVER TRACK 3136 | if display_overlay: 3137 | 3138 | 3139 | 3140 | # Racer object data setting 3141 | for i in range(8): 3142 | 3143 | 3144 | ########### PARSE RECIEVED DATA PER CHARACTER ############## 3145 | 3146 | OBJECTS[i].ch_num = next_byte(ch_bytes) 3147 | 3148 | X_coord = map_value(next_double(ch_bytes), "width") 3149 | Y_coord = map_value(next_double(ch_bytes), "height") 3150 | 3151 | 3152 | if is_active_racer(i): 3153 | 3154 | if OBJECTS[i].ch_num != BV.P_FOLLOW[i]: 3155 | pfi = BV.P_FOLLOW[i] 3156 | #print("SETTING UP FOLLOW BUTTON", i, "with character number", OBJECTS[i].ch_num, "BV.P_FOLLOW =", BV.P_FOLLOW[i], "BV.SHOW_GHOST =", BV.SHOW_GHOST) 3157 | BV.P_FOLLOW[i] = OBJECTS[i].ch_num 3158 | BV.FOLLOW_BUTTONS[i].config(image=Assets.CHARACTERS_TK[Objects.CHAR_NUM_TO_NAME(OBJECTS[i].ch_num)]) 3159 | BV.FOLLOW_BUTTONS[i].config(state="normal") 3160 | 3161 | if BV.racers_to_follow["racer" + str(i)] and pfi == -1: 3162 | BV.FOLLOW_BUTTONS[i].config(bg="#c8c8c8") 3163 | 3164 | 3165 | else: 3166 | X_coord = -1000 3167 | Y_coord = -1000 3168 | #OBJECTS[i].reset_trail() 3169 | OBJECTS[i].show_trails = False 3170 | 3171 | BV.racers_to_follow["racer" + str(i)] = False 3172 | 3173 | if BV.P_FOLLOW[i] != -1: 3174 | BV.P_FOLLOW[i] = -1 3175 | #print("DISABLING FOLLOW BUTTON", i) 3176 | BV.FOLLOW_BUTTONS[i].config(bg="#f0f0f0") 3177 | BV.FOLLOW_BUTTONS[i].config(image=Assets.EMPTY_TK) 3178 | BV.FOLLOW_BUTTONS[i].config(state="disabled") 3179 | 3180 | 3181 | 3182 | 3183 | OBJECTS[i].x = X_coord 3184 | OBJECTS[i].y = Y_coord 3185 | OBJECTS[i].z = next_double(ch_bytes)*256 # TO-DO MAYBE REMOVE THIS 256? 3186 | 3187 | OBJECTS[i].vel = (map_value(next_word_signed(ch_bytes)/4, "width"), 3188 | map_value(next_word_signed(ch_bytes)/4, "height"), 3189 | map_value(next_word_signed(ch_bytes)/4, "height")) 3190 | 3191 | OBJECTS[i].speed = map_value(next_word_signed(ch_bytes)/4, "width") 3192 | 3193 | OBJECTS[i].max_speed = map_value(next_word_signed(ch_bytes)/4, "width") 3194 | 3195 | OBJECTS[i].accel = map_value(next_word_signed(ch_bytes)/4, "width") 3196 | 3197 | 3198 | OBJECTS[i].angle = map_value(next_word(ch_bytes)/256, "degrees") 3199 | 3200 | OBJECTS[i].v_angle = map_value(next_word(ch_bytes)/256, "degrees") 3201 | 3202 | OBJECTS[i].c_angle = map_value(next_word(ch_bytes)/256, "degrees") 3203 | 3204 | OBJECTS[i].angle_vel = map_value(next_word(ch_bytes)/256, "degrees") 3205 | 3206 | OBJECTS[i].dest = (map_value(next_word(ch_bytes), "width"), 3207 | map_value(next_word(ch_bytes), "height")) 3208 | 3209 | 3210 | 3211 | #print("Racer Data Time:", 100 * (time.perf_counter() - T) * 60) 3212 | 3213 | #T = time.perf_counter() 3214 | 3215 | #Obstacle object data 3216 | for i in range(8): 3217 | 3218 | #obj_addr = next_word(obj_bytes) 3219 | OBJECTS[num_racers + i].address = next_word(obj_bytes) 3220 | 3221 | OBJECTS[num_racers + i].x = map_value(next_double(obj_bytes), "width") 3222 | OBJECTS[num_racers + i].y = map_value(next_double(obj_bytes), "height") 3223 | OBJECTS[num_racers + i].z = next_byte(obj_bytes)/256 + next_word(obj_bytes) # TO-DO MAYBE REMOVE THIS 256? 3224 | next_byte(obj_bytes) # waste because there's a bug with MONTY????????? 3225 | 3226 | #if OBJECTS[num_racers + i].z > 0x1000: print(hex(int(OBJECTS[num_racers + i].z * 256))) 3227 | 3228 | 3229 | if gametype != 6: 3230 | #OBJECTS[num_racers + i].ch_num = (current_track_number * 4) + (ANIM_TIMER // ANIM_TIMER_SWAP) # dummy 3231 | #print(((current_theme_object//2) * 4)) 3232 | OBJECTS[num_racers + i].ch_num = ((current_theme_object//2) * 4) + (ANIM_TIMER // ANIM_TIMER_SWAP) # dummy 3233 | 3234 | else: 3235 | #OBJECTS[num_racers + i].ch_num = (current_track_number * 4) + (i // 3) 3236 | OBJECTS[num_racers + i].ch_num = 28 + (i // 3) 3237 | 3238 | 3239 | #print("Object", i, ": ", format(obj_addr, "04x")) 3240 | 3241 | #print("Obstacle Data Time:", 100 * (time.perf_counter() - T) * 60) 3242 | 3243 | #T = time.perf_counter() 3244 | 3245 | 3246 | # Item object data 3247 | for i in range(8): 3248 | 3249 | if (gametype == 0 and i < 2) or (gametype == 2) or (gametype == 6 and i < 6): 3250 | 3251 | ########### PARSE RECIEVED DATA PER ITEM ############## 3252 | 3253 | OBJECTS[num_racers + num_obstacles + i].address = next_word(item_bytes) 3254 | 3255 | alive = next_byte(item_bytes) 3256 | 3257 | if alive < 0x80: 3258 | OBJECTS[num_racers + num_obstacles + i].is_alive = False 3259 | else: 3260 | OBJECTS[num_racers + num_obstacles + i].is_alive = True 3261 | 3262 | ch_num_1 = next_byte(item_bytes) 3263 | ch_num_2 = next_byte(item_bytes) 3264 | 3265 | OBJECTS[num_racers + num_obstacles + i].ch_num = (ch_num_2 * 8) + ch_num_1 3266 | 3267 | 3268 | 3269 | 3270 | OBJECTS[num_racers + num_obstacles + i].x = map_value(next_double(item_bytes), "width") 3271 | OBJECTS[num_racers + num_obstacles + i].y = map_value(next_double(item_bytes), "height") 3272 | OBJECTS[num_racers + num_obstacles + i].z = next_double(item_bytes)*256 # TO-DO MAYBE REMOVE THIS 256? 3273 | 3274 | 3275 | 3276 | #print("Item Data Time:", 100 * (time.perf_counter() - T) * 60) 3277 | #UPDATING 3278 | #T1 = time.perf_counter() - T0 3279 | 3280 | #print("TOTAL OBJECT TIME:", 100 * T1 * 60) 3281 | 3282 | 3283 | #============================================================================================================== 3284 | # 3285 | # MAIN UPDATING ROUTINE 3286 | # 3287 | #============================================================================================================== 3288 | 3289 | 3290 | 3291 | 3292 | 3293 | if True: 3294 | m_x, m_y = pygame.mouse.get_pos() 3295 | mouse = (m_x, m_y) 3296 | m_x = disp2map_X(m_x) 3297 | m_y = disp2map_Y(m_y) 3298 | 3299 | 3300 | mouse_rel = pygame.mouse.get_rel() 3301 | 3302 | 3303 | 3304 | 3305 | 3306 | 3307 | 3308 | 3309 | 3310 | if MOUSE_NEW["right"]: 3311 | 3312 | for i in range(len(OBJECTS)): 3313 | 3314 | OBJ = OBJECTS[i] 3315 | 3316 | O_x = OBJ.x 3317 | O_y = OBJ.y 3318 | 3319 | 3320 | 3321 | if abs(O_x - m_x) < 5 * SPRITE_SCALE: 3322 | if abs(O_y - m_y) < 5 * SPRITE_SCALE: 3323 | grabbed = i 3324 | m_off = (O_x - m_x, O_y - m_y) 3325 | break 3326 | 3327 | 3328 | 3329 | 3330 | 3331 | if CURR_FRAME_MOUSE["right"]: 3332 | 3333 | if grabbed != -1: 3334 | 3335 | 3336 | X_COORD = int((m_x + m_off[0])) 3337 | Y_COORD = int((m_y + m_off[1] + 4)) 3338 | 3339 | 3340 | 3341 | OBJECTS[grabbed].x = X_COORD 3342 | OBJECTS[grabbed].y = Y_COORD 3343 | 3344 | ADDR = OBJECTS[grabbed].address 3345 | 3346 | 3347 | if F == 1: 3348 | F = 0 3349 | 3350 | 3351 | 3352 | part1 = "W_BYTES addr" + " " + str(ADDR + 0x18) + " " + "bytes" + " " + str(X_COORD & 0xff) + " " + str((X_COORD>>8 & 0xff)) + " 00 00 " + str(Y_COORD & 0xff) + " " + str((Y_COORD>>8 & 0xff)) + " 00 00 05 " + "\n" 3353 | 3354 | send_raw_signal(part1) 3355 | 3356 | F += 1 3357 | 3358 | else: 3359 | if grabbed != -1: 3360 | ADDR = OBJECTS[grabbed].address 3361 | ground_instr = "W_BYTES addr" + " " + str(ADDR + 0x1e) + " " + "bytes" + " " + "00 00 00 00" + "\n" 3362 | 3363 | send_raw_signal(ground_instr) 3364 | 3365 | grabbed = -1 3366 | 3367 | 3368 | 3369 | 3370 | 3371 | 3372 | 3373 | 3374 | 3375 | 3376 | 3377 | 3378 | 3379 | 3380 | 3381 | 3382 | 3383 | 3384 | 3385 | 3386 | 3387 | 3388 | 3389 | 3390 | 3391 | 3392 | if BV.REPLAY_MODE and gametype == 4 and demo_byte == 0: 3393 | 3394 | 3395 | send_ghost_signal() 3396 | 3397 | if C_MODE == 2: 3398 | # top screen 3399 | p_f = BV.racers_to_follow["racer0"] 3400 | BV.racers_to_follow["racer0"] = True 3401 | if p_f == False: BV.FOLLOW_BUTTONS[0].config(bg="#c8c8c8") 3402 | 3403 | elif C_MODE == 4: 3404 | # bottom screen 3405 | p_f = BV.racers_to_follow["racer1"] 3406 | BV.racers_to_follow["racer1"] = True 3407 | if p_f == False: BV.FOLLOW_BUTTONS[1].config(bg="#c8c8c8") 3408 | 3409 | else: 3410 | if BV.GHOST_TURN_OFF: 3411 | send_ghost_off_signal() 3412 | BV.GHOST_TURN_OFF = False 3413 | 3414 | 3415 | 3416 | FOLLOWING = [] 3417 | 3418 | for racer in BV.racers_to_follow: 3419 | if BV.racers_to_follow[racer] == True: 3420 | FOLLOWING.append(int(racer[-1])) # cheap but dirty way to add the racer number in 3421 | 3422 | 3423 | 3424 | 3425 | 3426 | 3427 | 3428 | 3429 | 3430 | 3431 | 3432 | if CURR_FRAME_MOUSE["scroll_up"]: 3433 | screen.zoom(mouse, screen.SCALE * 1.1) 3434 | elif CURR_FRAME_MOUSE["scroll_down"]: 3435 | screen.zoom(mouse, screen.SCALE * 0.9) 3436 | 3437 | 3438 | 3439 | 3440 | 3441 | 3442 | 3443 | if CURR_FRAME_MOUSE["left"]: 3444 | screen.x = screen.x + mouse_rel[0] 3445 | screen.y = screen.y + mouse_rel[1] 3446 | 3447 | 3448 | 3449 | if len(FOLLOWING) > 0: 3450 | # if following at least 1 racer 3451 | 3452 | CENTER_X = OBJECTS[FOLLOWING[0]].x 3453 | CENTER_Y = OBJECTS[FOLLOWING[0]].y 3454 | 3455 | 3456 | if len(FOLLOWING) > 1: 3457 | left_x = 0x5000 3458 | right_x = -1 3459 | 3460 | top_y = 0x5000 3461 | bottom_y = -1 3462 | 3463 | for racer_id in FOLLOWING: 3464 | O_x = OBJECTS[racer_id].x 3465 | O_y = OBJECTS[racer_id].y 3466 | 3467 | if O_x < left_x: left_x = O_x 3468 | if O_x > right_x: right_x = O_x 3469 | 3470 | if O_y < top_y: top_y = O_y 3471 | if O_y > bottom_y: bottom_y = O_y 3472 | 3473 | 3474 | 3475 | 3476 | 3477 | CENTER_X = math.floor((left_x + right_x)/2) 3478 | CENTER_Y = math.floor((top_y + bottom_y)/2) 3479 | 3480 | 3481 | screen.center_x = CENTER_X * screen.DEFAULT_WIDTH/MAP_W 3482 | screen.center_y = CENTER_Y * screen.DEFAULT_HEIGHT/MAP_H 3483 | 3484 | 3485 | 3486 | 3487 | 3488 | 3489 | 3490 | 3491 | 3492 | 3493 | 3494 | #============================================================================================================== 3495 | 3496 | 3497 | #DISPLAY 3498 | 3499 | 3500 | if BV.TRAIL_LINES == False: 3501 | NUM_TRAILS = 0 3502 | elif gametype == 4: 3503 | NUM_TRAILS = MAX_TRAILS_CONFIG 3504 | else: 3505 | NUM_TRAILS = MAX_TRAILS_CONFIG/16 3506 | 3507 | 3508 | if FRAME_NUMBER > FRAME_SKIP: 3509 | 3510 | #T0 = time.perf_counter() 3511 | 3512 | #print(screen.SCALE, screen.w, screen.DEFAULT_WIDTH) 3513 | 3514 | FRAME_NUMBER = 0 3515 | 3516 | #CANVAS.blit(BG, (0, 0)) 3517 | 3518 | ####CANVAS.blit(screen.canvas, (0, 0)) #--- come back to this later! 3519 | 3520 | #T0 = time.perf_counter() 3521 | # trails 3522 | #l_width = screen.w / MAP_W 3523 | 3524 | if BV.TRAIL_LINES: 3525 | SHOW_TRAIL_LINES() 3526 | 3527 | #T1 = time.perf_counter() - T0 3528 | 3529 | #print("Line Time:", 100 * T1 * 60) 3530 | 3531 | 3532 | 3533 | #glFinish() 3534 | #glFlush() 3535 | 3536 | #glColor3f(1.0, 1.0, 1.0) 3537 | 3538 | #T0 = time.perf_counter() 3539 | 3540 | TRAIL_AMT = NUM_TRAILS 3541 | if BV.REPLAY_MODE: TRAIL_AMT = TRAIL_AMT // 2 3542 | 3543 | # racers 3544 | for i in range(7, -1, -1): 3545 | OBJ = OBJECTS[i] 3546 | if (gametype == 0) or (gametype == 2 and i < 2) or (gametype == 4 and i < 2) or (gametype == 6 and i < 2): 3547 | #print(i) 3548 | OBJ.scl = math.floor(screen.SCALE * screen.DEFAULT_WIDTH/MAP_W * width//128 * SQRT_2 * SPRITE_SCALE / 1) 3549 | OBJ.disp_x = map2disp_X(OBJ.x) 3550 | OBJ.disp_y = map2disp_Y(OBJ.y) 3551 | if BV.REPLAY_MODE and i == 1 and gametype == 4: 3552 | #OBJ.display(screen, trails=False) 3553 | pass 3554 | else: 3555 | if (gametype == 4 and i == 1): 3556 | GHOST_FLASH_TIMER += 1 3557 | if GHOST_FLASH_TIMER == 2: 3558 | GHOST_FLASH_TIMER = 0 3559 | 3560 | t = TRAIL_AMT 3561 | if BV.REPLAY_MODE: t = 0 3562 | 3563 | if GHOST_FLASH_TIMER % 2 == 0: 3564 | OBJ.display(d_method=renderObject) 3565 | OBJ.update_trails(trails=t, log_freq=TRAIL_FREQ_CONFIG) 3566 | #pass 3567 | else: 3568 | OBJ.display(ghost=True, d_method=renderObject) 3569 | OBJ.update_trails(trails=t, log_freq=TRAIL_FREQ_CONFIG) 3570 | 3571 | else: 3572 | OBJ.display(d_method=renderObject) 3573 | OBJ.update_trails(trails=TRAIL_AMT, log_freq=TRAIL_FREQ_CONFIG) 3574 | 3575 | # obstacles 3576 | for i in range(7, -1, -1): 3577 | j = num_racers + i 3578 | OBJ = OBJECTS[j] 3579 | OBJ.scl = max(0, math.floor(screen.SCALE * screen.DEFAULT_WIDTH/MAP_W * width//128 * SPRITE_SCALE / 1)) 3580 | OBJ.disp_x = map2disp_X(OBJ.x) 3581 | OBJ.disp_y = map2disp_Y(OBJ.y) 3582 | OBJ.display(d_method=renderObject) 3583 | 3584 | #items 3585 | for i in range(7, -1, -1): 3586 | j = num_racers + num_obstacles + i 3587 | OBJ = OBJECTS[j] 3588 | if (gametype == 0 and i < 2) or (gametype == 2) or (gametype == 6 and i < 6): 3589 | if OBJ.is_alive: 3590 | OBJ.scl = math.floor(screen.SCALE * screen.DEFAULT_WIDTH/MAP_W * width//256 * SPRITE_SCALE / 1) 3591 | OBJ.disp_x = map2disp_X(OBJ.x) 3592 | OBJ.disp_y = map2disp_Y(OBJ.y) 3593 | OBJ.display(d_method=renderObject) 3594 | 3595 | 3596 | 3597 | 3598 | 3599 | 3600 | if BV.REPLAY_MODE: 3601 | if not BV.P_REPLAY_MODE: 3602 | if C_MODE == 2: 3603 | OBJECTS[0].show_trails = True 3604 | OBJECTS[0].copy_trail(OBJECTS[1]) 3605 | elif C_MODE == 4: 3606 | OBJECTS[1].show_trails = True 3607 | OBJECTS[1].copy_trail(OBJECTS[0]) 3608 | else: 3609 | if BV.P_REPLAY_MODE: 3610 | if C_MODE == 2: 3611 | OBJECTS[1].copy_trail(OBJECTS[0]) 3612 | elif C_MODE == 4: 3613 | OBJECTS[0].copy_trail(OBJECTS[1]) 3614 | 3615 | BV.P_REPLAY_MODE = BV.REPLAY_MODE 3616 | 3617 | 3618 | 3619 | ############################################################# 3620 | # Direction lines for racers 3621 | ############################################################# 3622 | 3623 | if True: # later change to "if SHOW_:" 3624 | SHOW_EXTRA_VECTORS() 3625 | 3626 | 3627 | 3628 | 3629 | 3630 | 3631 | ''' 3632 | 3633 | if SHOW_DEBUG: 3634 | 3635 | DEBUG_LIST = [ 3636 | "BV.REPLAY_MODE: " + str(BV.REPLAY_MODE), 3637 | "NUM_TRAILS: " + str(NUM_TRAILS), 3638 | "SPRITE_SCALE: " + str(SPRITE_SCALE), 3639 | "FOLLOWING: " + str([x + 1 for x in FOLLOWING]) 3640 | ] 3641 | 3642 | D_LINE = 0 3643 | for DEBUG_TEXT in DEBUG_LIST: 3644 | text_surface = font.render(DEBUG_TEXT, True, (255, 255, 255)) 3645 | CANVAS.blit(text_surface, dest=(0, D_LINE * 14)) 3646 | D_LINE += 1 3647 | pass 3648 | ''' 3649 | 3650 | 3651 | 3652 | #glPopMatrix() 3653 | 3654 | 3655 | 3656 | 3657 | else: 3658 | #print("[INFO] MAP NOT YET READY") 3659 | pass 3660 | 3661 | 3662 | 3663 | 3664 | do_frame_update() 3665 | 3666 | 3667 | 3668 | 3669 | 3670 | 3671 | 3672 | 3673 | # SET UP "PREV" VALUES 3674 | 3675 | for m_button in CURR_FRAME_MOUSE: 3676 | PREV_FRAME_MOUSE[m_button] = CURR_FRAME_MOUSE[m_button] 3677 | 3678 | for k_button in CURR_FRAME_KEYS: 3679 | PREV_FRAME_KEYS[k_button] = CURR_FRAME_KEYS[k_button] 3680 | 3681 | 3682 | 3683 | ## set up prev values 3684 | prev_gamemode = gamemode 3685 | prev_track_number = current_track_number 3686 | except ZeroDivisionError: 3687 | 3688 | do_frame_update() 3689 | 3690 | except Exception as e: 3691 | #traceback.print_exc() 3692 | #print(e) 3693 | 3694 | #print(BYTES_AS_LIST) 3695 | #conn.send(b"close\n") 3696 | #conn.close() 3697 | CLOSE_CONN() 3698 | raise 3699 | 3700 | 3701 | 3702 | 3703 | except TclError as e: 3704 | #traceback.print_exc() 3705 | #print(e) 3706 | pass 3707 | except Exception as e: 3708 | traceback.print_exc() 3709 | print(e) 3710 | 3711 | 3712 | 3713 | pygame.quit() 3714 | 3715 | if BV.root != None: 3716 | try: 3717 | BV.root.destroy() 3718 | BV.root = None 3719 | except Exception: 3720 | pass 3721 | 3722 | print("[INFO] Disconnected from", CONN_NAME, end="") 3723 | printed_reason = False 3724 | 3725 | if data == "close" or data == "close\n": 3726 | if len(data) > 6: 3727 | print("\n Reason: ", data[6:]) 3728 | printed_reason = True 3729 | 3730 | 3731 | if not printed_reason: print("\n Reason: Server Script Forcibly Stopped") 3732 | 3733 | 3734 | 3735 | try: CLOSE_CONN() 3736 | except ConnectionResetError: pass 3737 | except OSError: pass 3738 | 3739 | break 3740 | 3741 | 3742 | 3743 | --------------------------------------------------------------------------------