mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 03:30:13 +01:00
update to add every missing feature ever
This commit is contained in:
@@ -1,40 +1,48 @@
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from libs.tja import TJAParser
|
||||
from libs.utils import get_config
|
||||
|
||||
song_hashes: Optional[dict] = None
|
||||
|
||||
|
||||
def process_tja_file(tja_file):
|
||||
"""Process a single TJA file and return hash or None if error"""
|
||||
tja = TJAParser(tja_file)
|
||||
all_notes = []
|
||||
for diff in tja.metadata.course_data:
|
||||
all_notes.extend(TJAParser.notes_to_position(TJAParser(tja.file_path), diff))
|
||||
all_notes.extend(
|
||||
TJAParser.notes_to_position(TJAParser(tja.file_path), diff)
|
||||
)
|
||||
hash = tja.hash_note_data(all_notes[0], all_notes[2])
|
||||
return hash
|
||||
|
||||
def build_song_hashes(output_file='cache/song_hashes.json'):
|
||||
|
||||
def build_song_hashes(output_file="cache/song_hashes.json"):
|
||||
existing_hashes = {}
|
||||
output_path = Path(output_file)
|
||||
if output_path.exists():
|
||||
try:
|
||||
with open(output_file, 'r', encoding='utf-8') as f:
|
||||
with open(output_file, "r", encoding="utf-8") as f:
|
||||
existing_hashes = json.load(f)
|
||||
except (json.JSONDecodeError, IOError) as e:
|
||||
print(f"Warning: Could not load existing hashes from {output_file}: {e}")
|
||||
print(
|
||||
f"Warning: Could not load existing hashes from {output_file}: {e}"
|
||||
)
|
||||
existing_hashes = {}
|
||||
|
||||
song_hashes = existing_hashes.copy()
|
||||
tja_paths = get_config()['paths']['tja_path']
|
||||
tja_paths = get_config()["paths"]["tja_path"]
|
||||
all_tja_files = []
|
||||
|
||||
for root_dir in tja_paths:
|
||||
root_path = Path(root_dir)
|
||||
all_tja_files.extend(root_path.rglob('*.tja'))
|
||||
all_tja_files.extend(root_path.rglob("*.tja"))
|
||||
|
||||
updated_count = 0
|
||||
for tja_file in all_tja_files:
|
||||
@@ -45,14 +53,14 @@ def build_song_hashes(output_file='cache/song_hashes.json'):
|
||||
|
||||
existing_hash = None
|
||||
for h, data in song_hashes.items():
|
||||
if data['file_path'] == str(tja_file):
|
||||
if data["file_path"] == str(tja_file):
|
||||
existing_hash = h
|
||||
break
|
||||
|
||||
if existing_hash is None:
|
||||
should_update = True
|
||||
else:
|
||||
stored_modified = song_hashes[existing_hash].get('last_modified', 0)
|
||||
stored_modified = song_hashes[existing_hash].get("last_modified", 0)
|
||||
if current_modified > stored_modified:
|
||||
should_update = True
|
||||
del song_hashes[existing_hash]
|
||||
@@ -61,17 +69,19 @@ def build_song_hashes(output_file='cache/song_hashes.json'):
|
||||
tja = TJAParser(tja_file)
|
||||
all_notes = []
|
||||
for diff in tja.metadata.course_data:
|
||||
all_notes.extend(TJAParser.notes_to_position(TJAParser(tja.file_path), diff))
|
||||
all_notes.extend(
|
||||
TJAParser.notes_to_position(TJAParser(tja.file_path), diff)
|
||||
)
|
||||
hash_val = tja.hash_note_data(all_notes[0], all_notes[2])
|
||||
song_hashes[hash_val] = {
|
||||
'file_path': str(tja_file),
|
||||
'last_modified': current_modified,
|
||||
'title': tja.metadata.title,
|
||||
'subtitle': tja.metadata.subtitle
|
||||
"file_path": str(tja_file),
|
||||
"last_modified": current_modified,
|
||||
"title": tja.metadata.title,
|
||||
"subtitle": tja.metadata.subtitle,
|
||||
}
|
||||
updated_count += 1
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
with open(output_file, "w", encoding="utf-8") as f:
|
||||
json.dump(song_hashes, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"Song hashes saved to {output_file}. Updated {updated_count} files.")
|
||||
@@ -80,29 +90,84 @@ def build_song_hashes(output_file='cache/song_hashes.json'):
|
||||
|
||||
def get_japanese_songs_for_version(df, version_column):
|
||||
# Filter rows where the specified version column has 'YES'
|
||||
version_songs = df[df[version_column] == 'YES']
|
||||
version_songs = df[df[version_column] != "NO"]
|
||||
|
||||
# Extract Japanese titles (JPTITLE column)
|
||||
japanese_titles = version_songs['TITLE 【TITLE2】\nJPTITLE/「TITLE2」 より'].tolist()
|
||||
japanese_titles = version_songs[
|
||||
"TITLE 【TITLE2】\nJPTITLE/「TITLE2」 より"
|
||||
].tolist()
|
||||
|
||||
japanese_titles = [name.split('\n') for name in japanese_titles]
|
||||
second_lines = [name[1] for name in japanese_titles if len(name) > 1]
|
||||
japanese_titles = [name.split("\n") for name in japanese_titles]
|
||||
second_lines = [
|
||||
name[1] if len(name) > 1 else name[0] for name in japanese_titles
|
||||
]
|
||||
|
||||
all_tja_files = []
|
||||
direct_tja_paths = dict()
|
||||
text_files = dict()
|
||||
tja_paths = get_config()['paths']['tja_path']
|
||||
tja_paths = get_config()["paths"]["tja_path"]
|
||||
for root_dir in tja_paths:
|
||||
root_path = Path(root_dir)
|
||||
all_tja_files.extend(root_path.rglob('*.tja'))
|
||||
all_tja_files.extend(root_path.rglob("*.tja"))
|
||||
for tja in all_tja_files:
|
||||
tja_parse = TJAParser(tja)
|
||||
direct_tja_paths[tja_parse.metadata.title.get('ja', tja_parse.metadata.title['en'])] = tja
|
||||
tja_name = tja_parse.metadata.title.get(
|
||||
"ja", tja_parse.metadata.title["en"]
|
||||
)
|
||||
if "【双打】" in tja_name:
|
||||
tja_name = tja_name.strip("【双打】")
|
||||
tja_name = tja_name.strip()
|
||||
if tja_name in direct_tja_paths:
|
||||
direct_tja_paths[tja_name].append(tja)
|
||||
else:
|
||||
direct_tja_paths[tja_name] = [tja]
|
||||
for title in second_lines:
|
||||
if "・・・" in title:
|
||||
title = title.replace("・・・", "…")
|
||||
if "..." in title:
|
||||
title = title.replace("・・・", "…")
|
||||
|
||||
# Find all matching keys
|
||||
matches = []
|
||||
|
||||
# Check for exact title match
|
||||
if title in direct_tja_paths:
|
||||
path = direct_tja_paths[title]
|
||||
elif title.split('/')[0] in direct_tja_paths:
|
||||
path = direct_tja_paths[title.split('/')[0]]
|
||||
for path in direct_tja_paths[title]:
|
||||
matches.append((title, path))
|
||||
|
||||
# Also check for partial matches with the first part before '/'
|
||||
title_prefix = title.split("/")[0]
|
||||
for key in direct_tja_paths:
|
||||
if key.startswith(title_prefix) and key != title:
|
||||
for path in direct_tja_paths[key]:
|
||||
matches.append((key, path))
|
||||
|
||||
if not matches:
|
||||
for key in direct_tja_paths:
|
||||
if title.lower() in key.lower() or key.lower() in title.lower():
|
||||
for path in direct_tja_paths[key]:
|
||||
matches.append((key, path))
|
||||
|
||||
if not matches:
|
||||
from difflib import get_close_matches
|
||||
|
||||
close_matches = get_close_matches(
|
||||
title, direct_tja_paths.keys(), n=3, cutoff=0.6
|
||||
)
|
||||
for close_match in close_matches:
|
||||
for path in direct_tja_paths[close_match]:
|
||||
matches.append((close_match, path))
|
||||
|
||||
if len(matches) == 1:
|
||||
path = matches[0][1]
|
||||
elif len(matches) > 1:
|
||||
print(
|
||||
f"Multiple matches found for '{title.split('/')[0]} ({title.split('/')[1] if len(title.split('/')) > 1 else ''})':"
|
||||
)
|
||||
for i, (key, path_val) in enumerate(matches, 1):
|
||||
print(f"{i}. {key}: {path_val}")
|
||||
choice = int(input("Choose number: ")) - 1
|
||||
path = matches[choice][1]
|
||||
else:
|
||||
path = Path(input(f"NOT FOUND {title}: "))
|
||||
hash = process_tja_file(path)
|
||||
@@ -110,15 +175,24 @@ def get_japanese_songs_for_version(df, version_column):
|
||||
genre = Path(path).parent.parent.name
|
||||
if genre not in text_files:
|
||||
text_files[genre] = []
|
||||
text_files[genre].append(f"{hash}|{tja_parse.metadata.title['en'].strip()}|{tja_parse.metadata.subtitle['en'].strip()}")
|
||||
text_files[genre].append(
|
||||
f"{hash}|{tja_parse.metadata.title['en'].strip()}|{tja_parse.metadata.subtitle['en'].strip()}"
|
||||
)
|
||||
print(f"Added {title}: {path}")
|
||||
for genre in text_files:
|
||||
if not Path(version_column).exists():
|
||||
Path(version_column).mkdir()
|
||||
if not Path(f"{version_column}/{genre}").exists():
|
||||
Path(f"{version_column}/{genre}").mkdir()
|
||||
with open(Path(f"{version_column}/{genre}/song_list.txt"), 'w', encoding='utf-8-sig') as text_file:
|
||||
with open(
|
||||
Path(f"{version_column}/{genre}/song_list.txt"),
|
||||
"w",
|
||||
encoding="utf-8-sig",
|
||||
) as text_file:
|
||||
for item in text_files[genre]:
|
||||
text_file.write(item + '\n')
|
||||
text_file.write(item + "\n")
|
||||
return text_files
|
||||
|
||||
get_japanese_songs_for_version(pd.read_csv('full.csv'), 'AC12')
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
get_japanese_songs_for_version(pd.read_csv("full.csv"), sys.argv[1])
|
||||
|
||||
112
libs/tja.py
112
libs/tja.py
@@ -1,12 +1,25 @@
|
||||
import bisect
|
||||
import hashlib
|
||||
import math
|
||||
from collections import deque
|
||||
from dataclasses import dataclass, field, fields
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from libs.utils import get_pixels_per_frame, strip_comments
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def get_ms_per_measure(bpm_val, time_sig):
|
||||
#https://gist.github.com/KatieFrogs/e000f406bbc70a12f3c34a07303eec8b#measure
|
||||
if bpm_val == 0:
|
||||
return 0
|
||||
return 60000 * (time_sig * 4) / bpm_val
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def get_pixels_per_ms(pixels_per_frame):
|
||||
return pixels_per_frame / (1000 / 60)
|
||||
|
||||
@dataclass()
|
||||
class Note:
|
||||
type: int = field(init=False)
|
||||
@@ -150,6 +163,7 @@ def calculate_base_score(play_note_list: deque[Note | Drumroll | Balloon]) -> in
|
||||
return math.ceil(total_score / 10) * 10
|
||||
|
||||
class TJAParser:
|
||||
DIFFS = {0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"}
|
||||
def __init__(self, path: Path, start_delay: int = 0, distance: int = 866):
|
||||
self.file_path: Path = path
|
||||
|
||||
@@ -260,59 +274,54 @@ class TJAParser:
|
||||
self.ex_data.limited_time = True
|
||||
|
||||
def data_to_notes(self, diff):
|
||||
note_start = -1
|
||||
note_end = -1
|
||||
diff_name = self.DIFFS.get(diff, "").lower()
|
||||
|
||||
# Use enumerate for single iteration
|
||||
note_start = note_end = -1
|
||||
target_found = False
|
||||
diffs = {0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"}
|
||||
# Get the name corresponding to this difficulty number
|
||||
diff_name = diffs.get(diff, "").lower()
|
||||
|
||||
i = 0
|
||||
while i < len(self.data):
|
||||
line = self.data[i]
|
||||
|
||||
# Check if this is the start of a difficulty section
|
||||
# Find the section boundaries
|
||||
for i, line in enumerate(self.data):
|
||||
if line.startswith("COURSE:"):
|
||||
course_value = line[7:].strip().lower()
|
||||
|
||||
# Match either the exact number or the name
|
||||
if (course_value.isdigit() and int(course_value) == diff) or course_value == diff_name:
|
||||
target_found = True
|
||||
else:
|
||||
target_found = False
|
||||
|
||||
# If we found our target section, look for START and END markers
|
||||
if target_found:
|
||||
if line == "#START":
|
||||
target_found = (course_value.isdigit() and int(course_value) == diff) or course_value == diff_name
|
||||
elif target_found:
|
||||
if note_start == -1 and line in ("#START", "#START P1"):
|
||||
note_start = i + 1
|
||||
elif line == "#END" and note_start != -1:
|
||||
note_end = i
|
||||
break # We found our complete section
|
||||
break
|
||||
|
||||
i += 1
|
||||
if note_start == -1 or note_end == -1:
|
||||
return []
|
||||
|
||||
# Process the section with minimal string operations
|
||||
notes = []
|
||||
bar = []
|
||||
#Check for measures and separate when comma exists
|
||||
for i in range(note_start, note_end):
|
||||
line = self.data[i]
|
||||
section_data = self.data[note_start:note_end]
|
||||
|
||||
for line in section_data:
|
||||
if line.startswith("#"):
|
||||
bar.append(line)
|
||||
elif line == ',':
|
||||
if not bar or all(item.startswith('#') for item in bar):
|
||||
bar.append('')
|
||||
notes.append(bar)
|
||||
bar = []
|
||||
else:
|
||||
if line == ',':
|
||||
if len(bar) == 0 or all(item.startswith('#') for item in bar):
|
||||
bar.append('')
|
||||
if line.endswith(','):
|
||||
bar.append(line[:-1])
|
||||
notes.append(bar)
|
||||
bar = []
|
||||
else:
|
||||
item = line.strip(',')
|
||||
bar.append(item)
|
||||
if item != line:
|
||||
notes.append(bar)
|
||||
bar = []
|
||||
bar.append(line)
|
||||
|
||||
if bar: # Add remaining items
|
||||
notes.append(bar)
|
||||
|
||||
return notes
|
||||
|
||||
def get_moji(self, play_note_list: deque[Note], ms_per_measure: float) -> None:
|
||||
def get_moji(self, play_note_list: list[Note], ms_per_measure: float) -> None:
|
||||
se_notes = {
|
||||
1: [0, 1, 2], # Note '1' has three possible sound effects
|
||||
2: [3, 4], # Note '2' has two possible sound effects
|
||||
@@ -373,9 +382,9 @@ class TJAParser:
|
||||
play_note_list[-3].moji = se_notes[play_note_list[-3].moji][2]
|
||||
|
||||
def notes_to_position(self, diff: int):
|
||||
play_note_list: deque[Note | Drumroll | Balloon] = deque()
|
||||
bar_list: deque[Note] = deque()
|
||||
draw_note_list: deque[Note | Drumroll | Balloon] = deque()
|
||||
play_note_list: list[Note | Drumroll | Balloon] = []
|
||||
draw_note_list: list[Note | Drumroll | Balloon] = []
|
||||
bar_list: list[Note] = []
|
||||
notes = self.data_to_notes(diff)
|
||||
balloon = self.metadata.course_data[diff].balloon.copy()
|
||||
count = 0
|
||||
@@ -431,18 +440,14 @@ class TJAParser:
|
||||
if skip_branch:
|
||||
continue
|
||||
|
||||
if bpm == 0:
|
||||
ms_per_measure = 0
|
||||
else:
|
||||
#https://gist.github.com/KatieFrogs/e000f406bbc70a12f3c34a07303eec8b#measure
|
||||
ms_per_measure = 60000 * (time_signature*4) / bpm
|
||||
ms_per_measure = get_ms_per_measure(bpm, time_signature)
|
||||
|
||||
#Create note object
|
||||
bar_line = Note()
|
||||
|
||||
#Determines how quickly the notes need to move across the screen to reach the judgment circle in time
|
||||
bar_line.pixels_per_frame = get_pixels_per_frame(bpm * time_signature * scroll_modifier, time_signature*4, self.distance)
|
||||
pixels_per_ms = bar_line.pixels_per_frame / (1000 / 60)
|
||||
pixels_per_ms = get_pixels_per_ms(bar_line.pixels_per_frame)
|
||||
|
||||
bar_line.hit_ms = self.current_ms
|
||||
if pixels_per_ms == 0:
|
||||
@@ -455,7 +460,7 @@ class TJAParser:
|
||||
if barline_added:
|
||||
bar_line.display = False
|
||||
|
||||
bar_list.append(bar_line)
|
||||
bisect.insort(bar_list, bar_line, key=lambda x: x.load_ms)
|
||||
barline_added = True
|
||||
|
||||
#Empty bar is still a bar, otherwise start increment
|
||||
@@ -471,12 +476,11 @@ class TJAParser:
|
||||
continue
|
||||
note = Note()
|
||||
note.hit_ms = self.current_ms
|
||||
if pixels_per_ms == 0:
|
||||
note.load_ms = note.hit_ms
|
||||
else:
|
||||
note.load_ms = note.hit_ms - (self.distance / pixels_per_ms)
|
||||
note.type = int(item)
|
||||
note.pixels_per_frame = bar_line.pixels_per_frame
|
||||
pixels_per_ms = get_pixels_per_ms(note.pixels_per_frame)
|
||||
note.load_ms = (note.hit_ms if pixels_per_ms == 0
|
||||
else note.hit_ms - (self.distance / pixels_per_ms))
|
||||
note.type = int(item)
|
||||
note.index = index
|
||||
note.bpm = bpm
|
||||
note.gogo_time = gogo_time
|
||||
@@ -489,10 +493,7 @@ class TJAParser:
|
||||
if balloon is None:
|
||||
raise Exception("Balloon note found, but no count was specified")
|
||||
note = Balloon(note)
|
||||
if not balloon:
|
||||
note.count = 1
|
||||
else:
|
||||
note.count = balloon.pop(0)
|
||||
note.count = 1 if not balloon else balloon.pop(0)
|
||||
elif item == '8':
|
||||
new_pixels_per_ms = play_note_list[-1].pixels_per_frame / (1000 / 60)
|
||||
if new_pixels_per_ms == 0:
|
||||
@@ -502,6 +503,7 @@ class TJAParser:
|
||||
note.pixels_per_frame = play_note_list[-1].pixels_per_frame
|
||||
self.current_ms += increment
|
||||
play_note_list.append(note)
|
||||
bisect.insort(draw_note_list, note, key=lambda x: x.load_ms)
|
||||
self.get_moji(play_note_list, ms_per_measure)
|
||||
index += 1
|
||||
if len(play_note_list) > 3:
|
||||
@@ -513,9 +515,7 @@ class TJAParser:
|
||||
# Sorting by load_ms is necessary for drawing, as some notes appear on the
|
||||
# screen slower regardless of when they reach the judge circle
|
||||
# Bars can be sorted like this because they don't need hit detection
|
||||
draw_note_list = deque(sorted(play_note_list, key=lambda n: n.load_ms))
|
||||
bar_list = deque(sorted(bar_list, key=lambda b: b.load_ms))
|
||||
return play_note_list, draw_note_list, bar_list
|
||||
return deque(play_note_list), deque(draw_note_list), deque(bar_list)
|
||||
|
||||
def hash_note_data(self, play_notes: deque[Note | Drumroll | Balloon], bars: deque[Note]):
|
||||
n = hashlib.sha256()
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import hashlib
|
||||
import math
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
import tomllib
|
||||
import tomlkit
|
||||
import zipfile
|
||||
from dataclasses import dataclass, field
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -85,6 +87,7 @@ def strip_comments(code: str) -> str:
|
||||
index += 1
|
||||
return result
|
||||
|
||||
@lru_cache
|
||||
def get_pixels_per_frame(bpm: float, time_signature: float, distance: float) -> float:
|
||||
if bpm == 0:
|
||||
return 0
|
||||
@@ -94,10 +97,94 @@ def get_pixels_per_frame(bpm: float, time_signature: float, distance: float) ->
|
||||
return (distance / total_frames)
|
||||
|
||||
def get_config() -> dict[str, Any]:
|
||||
with open('config.toml', "rb") as f:
|
||||
config_file = tomllib.load(f)
|
||||
with open('config.toml', "r", encoding="utf-8") as f:
|
||||
config_file = tomlkit.load(f)
|
||||
return config_file
|
||||
|
||||
def save_config(config: dict[str, Any]) -> None:
|
||||
with open('config.toml', "w", encoding="utf-8") as f:
|
||||
tomlkit.dump(config, f)
|
||||
|
||||
def is_l_don_pressed() -> bool:
|
||||
keys = get_config()["keybinds"]["left_don"]
|
||||
for key in keys:
|
||||
if ray.is_key_pressed(ord(key)):
|
||||
return True
|
||||
|
||||
if ray.is_gamepad_available(0):
|
||||
if ray.is_gamepad_button_pressed(0, 16):
|
||||
return True
|
||||
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
for i in range(min(ray.get_touch_point_count(), 10)):
|
||||
tap_pos = (ray.get_touch_position(i).x, ray.get_touch_position(i).y)
|
||||
if math.dist(tap_pos, (mid_x, mid_y)) < 300 and tap_pos[0] <= mid_x:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_r_don_pressed() -> bool:
|
||||
keys = get_config()["keybinds"]["right_don"]
|
||||
for key in keys:
|
||||
if ray.is_key_pressed(ord(key)):
|
||||
return True
|
||||
|
||||
if ray.is_gamepad_available(0):
|
||||
if ray.is_gamepad_button_pressed(0, 17):
|
||||
return True
|
||||
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
for i in range(min(ray.get_touch_point_count(), 10)):
|
||||
tap_pos = (ray.get_touch_position(i).x, ray.get_touch_position(i).y)
|
||||
if math.dist(tap_pos, (mid_x, mid_y)) < 300 and tap_pos[0] > mid_x:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_l_kat_pressed() -> bool:
|
||||
keys = get_config()["keybinds"]["left_kat"]
|
||||
for key in keys:
|
||||
if ray.is_key_pressed(ord(key)):
|
||||
return True
|
||||
|
||||
if ray.is_gamepad_available(0):
|
||||
if ray.is_gamepad_button_pressed(0, 10):
|
||||
return True
|
||||
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
for i in range(min(ray.get_touch_point_count(), 10)):
|
||||
tap_pos = (ray.get_touch_position(i).x, ray.get_touch_position(i).y)
|
||||
if math.dist(tap_pos, (mid_x, mid_y)) >= 300 and tap_pos[0] <= mid_x:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_r_kat_pressed() -> bool:
|
||||
keys = get_config()["keybinds"]["right_kat"]
|
||||
for key in keys:
|
||||
if ray.is_key_pressed(ord(key)):
|
||||
return True
|
||||
|
||||
if ray.is_gamepad_available(0):
|
||||
if ray.is_gamepad_button_pressed(0, 12):
|
||||
return True
|
||||
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
for i in range(min(ray.get_touch_point_count(), 10)):
|
||||
tap_pos = (ray.get_touch_position(i).x, ray.get_touch_position(i).y)
|
||||
if math.dist(tap_pos, (mid_x, mid_y)) >= 300 and tap_pos[0] > mid_x:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def draw_scaled_texture(texture: ray.Texture, x: int, y: int, scale: float, color: ray.Color) -> None:
|
||||
src_rect = ray.Rectangle(0, 0, texture.width, texture.height)
|
||||
dst_rect = ray.Rectangle(x, y, texture.width*scale, texture.height*scale)
|
||||
|
||||
Reference in New Issue
Block a user