├── 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 | 
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 |
--------------------------------------------------------------------------------