diff --git a/level_editor/data/level_loader.py b/level_editor/data/level_loader.py new file mode 100644 index 00000000..575d697d --- /dev/null +++ b/level_editor/data/level_loader.py @@ -0,0 +1,8 @@ +import json + +class LevelLoader: + + @staticmethod + def load_level(file_path): + with open(file_path, "r") as file: + return json.load(file) diff --git a/level_editor/data/level_saver.py b/level_editor/data/level_saver.py new file mode 100644 index 00000000..9c8bfb50 --- /dev/null +++ b/level_editor/data/level_saver.py @@ -0,0 +1,8 @@ +import json + +class LevelSaver: + + @staticmethod + def save_level(file_path, level_data): + with open(file_path, "w") as file: + json.dump(level_data, file, indent=4) diff --git a/level_editor/editor.py b/level_editor/editor.py new file mode 100644 index 00000000..a8535dde --- /dev/null +++ b/level_editor/editor.py @@ -0,0 +1,56 @@ +import json + +class LevelEditor: + + def __init__(self): + self.level_data = [] + self.current_level = None + + def new_level(self): + self.level_data = [] + self.current_level = None + print("New level created") + + def load_level(self, file_path): + try: + with open(file_path, 'r') as file: + self.level_data = json.load(file) + self.current_level = file_path + print(f"Level loaded from {file_path}") + except Exception as e: + print(f"Failed to load level: {e}") + + def save_level(self, file_path): + try: + with open(file_path, 'w') as file: + json.dump(self.level_data, file, indent=4) + self.current_level = file_path + print(f"Level saved to {file_path}") + except Exception as e: + print(f"Failed to save level: {e}") + + def add_object(self, obj): + self.level_data.append(obj) + print(f"Object {obj} added to the level") + + def remove_object(self, obj): + if obj in self.level_data: + self.level_data.remove(obj) + print(f"Object {obj} removed from the level") + else: + print(f"Object {obj} not found in the level") + + def modify_object(self, obj, new_obj): + if obj in self.level_data: + index = self.level_data.index(obj) + self.level_data[index] = new_obj + print(f"Object {obj} modified to {new_obj}") + else: + print(f"Object {obj} not found in the level") + + def run(self): + print("Running Level Editor...") + +if __name__ == "__main__": + editor = LevelEditor() + editor.run() diff --git a/level_editor/file_manager.py b/level_editor/file_manager.py new file mode 100644 index 00000000..69d4e4ae --- /dev/null +++ b/level_editor/file_manager.py @@ -0,0 +1,13 @@ +import json + +class FileManager: + + @staticmethod + def save_level(file_path, level_data): + with open(file_path, "w") as file: + json.dump(level_data, file, indent=4) + + @staticmethod + def load_level(file_path): + with open(file_path, "r") as file: + return json.load(file) diff --git a/level_editor/main.py b/level_editor/main.py new file mode 100644 index 00000000..7cfc3db1 --- /dev/null +++ b/level_editor/main.py @@ -0,0 +1,15 @@ +import os + +def open_level_editor(): + os.system("python level_editor/editor.py") + +def start_level_editor(): + print("Starting Level Editor...") + open_level_editor() + +if __name__ == "__main__": + print("1. Start Game") + print("2. Open Level Editor") + choice = input("Choose an option: ") + if choice == "2": + start_level_editor() diff --git a/level_editor/tools/object_placer.py b/level_editor/tools/object_placer.py new file mode 100644 index 00000000..bebbcd3e --- /dev/null +++ b/level_editor/tools/object_placer.py @@ -0,0 +1,13 @@ +class ObjectPlacer: + + def __init__(self, canvas): + self.canvas = canvas + self.objects = [] + + def place_object(self, event): + obj = self.canvas.create_rectangle(event.x, event.y, event.x + 20, event.y + 20, fill="blue") + self.objects.append(obj) + print(f"Placed object at {event.x}, {event.y}") + + def enable(self): + self.canvas.bind("", self.place_object) diff --git a/level_editor/tools/object_rotator.py b/level_editor/tools/object_rotator.py new file mode 100644 index 00000000..1f34ab1a --- /dev/null +++ b/level_editor/tools/object_rotator.py @@ -0,0 +1,41 @@ +import math + +class ObjectRotator: + + def __init__(self, canvas): + self.canvas = canvas + + def rotate_object(self, obj, angle): + coords = self.canvas.coords(obj) + center_x = (coords[0] + coords[2]) / 2 + center_y = (coords[1] + coords[3]) / 2 + new_coords = [] + + for i in range(0, len(coords), 2): + x, y = coords[i], coords[i+1] + new_x = center_x + (x - center_x) * math.cos(angle) - (y - center_y) * math.sin(angle) + new_y = center_y + (x - center_x) * math.sin(angle) + (y - center_y) * math.cos(angle) + new_coords.extend([new_x, new_y]) + + self.canvas.coords(obj, *new_coords) + print(f"Rotated object {obj} by {angle} degrees") + + def enable(self): + self.canvas.bind("", self.start_rotating) + self.canvas.bind("", self.perform_rotating) + self.canvas.bind("", self.stop_rotating) + self.rotating = False + + def start_rotating(self, event): + self.rotating = True + self.start_x = event.x + self.start_y = event.y + + def perform_rotating(self, event): + if self.rotating: + angle = math.atan2(event.y - self.start_y, event.x - self.start_x) + obj = self.canvas.find_closest(self.start_x, self.start_y) + self.rotate_object(obj, angle) + + def stop_rotating(self, event): + self.rotating = False diff --git a/level_editor/tools/object_scaler.py b/level_editor/tools/object_scaler.py new file mode 100644 index 00000000..b3b65a1f --- /dev/null +++ b/level_editor/tools/object_scaler.py @@ -0,0 +1,30 @@ +class ObjectScaler: + + def __init__(self, canvas): + self.canvas = canvas + + def scale_object(self, obj, scale_factor): + coords = self.canvas.coords(obj) + new_coords = [coords[0], coords[1], coords[2] * scale_factor, coords[3] * scale_factor] + self.canvas.coords(obj, *new_coords) + print(f"Scaled object {obj} by {scale_factor}") + + def enable(self): + self.canvas.bind("", self.start_scaling) + self.canvas.bind("", self.perform_scaling) + self.canvas.bind("", self.stop_scaling) + self.scaling = False + + def start_scaling(self, event): + self.scaling = True + self.start_x = event.x + self.start_y = event.y + + def perform_scaling(self, event): + if self.scaling: + scale_factor = (event.x - self.start_x) / 100 + obj = self.canvas.find_closest(self.start_x, self.start_y) + self.scale_object(obj, scale_factor) + + def stop_scaling(self, event): + self.scaling = False diff --git a/level_editor/ui/editor_window.py b/level_editor/ui/editor_window.py new file mode 100644 index 00000000..788a62f6 --- /dev/null +++ b/level_editor/ui/editor_window.py @@ -0,0 +1,38 @@ +from tkinter import Tk, Menu, Frame, Button + +class EditorWindow: + + def __init__(self): + self.root = Tk() + self.root.title("Level Editor") + self.create_menu() + self.create_layout() + + def create_menu(self): + menu_bar = Menu(self.root) + file_menu = Menu(menu_bar, tearoff=0) + file_menu.add_command(label="New Level") + file_menu.add_command(label="Load Level") + file_menu.add_command(label="Save Level") + menu_bar.add_cascade(label="File", menu=file_menu) + self.root.config(menu=menu_bar) + + def create_layout(self): + self.frame = Frame(self.root) + self.frame.pack() + + self.place_button = Button(self.frame, text="Place Object") + self.place_button.pack() + + self.scale_button = Button(self.frame, text="Scale Object") + self.scale_button.pack() + + self.rotate_button = Button(self.frame, text="Rotate Object") + self.rotate_button.pack() + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + window = EditorWindow() + window.run() diff --git a/level_editor/ui/tool_panel.py b/level_editor/ui/tool_panel.py new file mode 100644 index 00000000..94b0684d --- /dev/null +++ b/level_editor/ui/tool_panel.py @@ -0,0 +1,20 @@ +from tkinter import Frame, Button + +class ToolPanel: + + def __init__(self, parent): + self.frame = Frame(parent) + self.create_tools() + + def create_tools(self): + place_button = Button(self.frame, text="Place Object") + place_button.pack() + + scale_button = Button(self.frame, text="Scale Object") + scale_button.pack() + + rotate_button = Button(self.frame, text="Rotate Object") + rotate_button.pack() + + def pack(self): + self.frame.pack() diff --git a/projectExporter.py b/projectExporter.py new file mode 100644 index 00000000..b8146964 --- /dev/null +++ b/projectExporter.py @@ -0,0 +1,62 @@ +import os +import json +import re +# from ProjectSavingEncoder import ProjectSavingEncoder + +def ProjectExporter(ProjectName,ProjectPath,ToSavePath,Demo = False): + #Loading the items + if not os.path.exists(f"{ToSavePath}/{ProjectName}"): + os.makedirs(f"{ToSavePath}/{ProjectName}") + + + with open(f"{ProjectPath}/{ProjectName}/World items.txt","r") as WorldItemsFile: + WorldItems = json.load(WorldItemsFile) + + with open(f"{ProjectPath}/{ProjectName}/To import.txt","r") as ToImportFile: + ToImport = json.load(ToImportFile) + + # with open(f"{ProjectPath}/{ProjectName}/User defined functions.txt","r") as UdFuncFile: + # UdFunc = json.load(UdFuncFile) + + # with open(f"{ProjectPath}/{ProjectName}/User defined src.txt","r") as UdSrcFile: + # UdSrc = json.load(UdSrcFile) + + # with open(f"{ProjectPath}/{ProjectName}/User defined vars.txt","r") as UdVarsFile: + # UdVars = json.load(UdVarsFile) + + # with open(f"{ProjectPath}/{ProjectName}/Window config.txt","r") as WindowConfigFile: + # WindowConfig = json.load(WindowConfigFile) + + #Making a .py file + + FinalFile = '''''' + TabIndent = " " + Indent = " " + for i in range(len(ToImport)): + FinalFile += f"{ToImport[i]}\n" + + FinalFile += "\n" + + FinalFile += "class Game:\n" + FinalFile += f"{TabIndent}def __init__(self):\n" + + # WorldItems = [{"cls": "Entity", "args": "(parent=scene, name=\'item_0\', enabled=True, eternal=False, position=Vec3(0, 0.6, 0), rotation=Vec3(0, 0, 0), scale=Vec3(1, 1, 1), model=\'cube\', origin=Vec3(0, 0, 0), shader=None, texture=\'white_cube\', color=color.white, collider=\'mesh\', )"}, {"cls": "Button", "args": "(parent=scene, name=\'item_1\', enabled=True, eternal=False, position=Vec3(-1.11, 0, 0), rotation=Vec3(0, 0, 0), scale=Vec3(1, 1, 1), model=\'cube\', origin=Vec3(0, 0, 0), shader=None, texture=\'white_cube\', color=color.green, )"}, {"cls": "InputField", "args": "(parent=scene, name=\'item_2\', enabled=True, eternal=False, position=Vec3(0, 1.73, 0), rotation=Vec3(0, 0, 0), scale=Vec3(1, 1, 1), model=\'cube\', origin=Vec3(0, 0, 0), shader=None, texture=\'white_cube\', color=color.green, )"}, {"cls": "Button", "args": "(parent=scene, name=\'item_3\', enabled=True, eternal=False, position=Vec3(0.06, -0.72, 0), rotation=Vec3(0, 0, 0), scale=Vec3(1, 1, 1), model=\'cube\', origin=Vec3(0, 0, 0), shader=None, texture=\'white_cube\', color=color.green, )"}, {"cls": "Entity", "args": "(parent=scene, name=\'item_4\', enabled=True, eternal=False, position=Vec3(1.09, 0, 0), rotation=Vec3(0, 0, 0), scale=Vec3(1, 1, 1), model=\'cube\', origin=Vec3(0, 0, 0), shader=None, texture=\'white_cube\', color=color.white, collider=\'mesh\', )"}] + # names = [re.search(r"name='([^']*)'", item).group(1) for item in ] + + for item in WorldItems: + name = re.search(r"name='([^']*)'", item['args']).group(1) + FinalFile += f'{Indent}self.{name} = {item["cls"] + item["args"]}\n' + if not Demo: + FinalFile += "if __name__ == '__main__':\n app = Ursina()\n application.development_mode = False\n Sky()\n Game()\n render.setAntialias(AntialiasAttrib.MAuto)\n EditorCamera()\n app.run()" + else: + FinalFile += "if __name__ == '__main__':\n app = Ursina()\n window.center_on_screen()\n application.development_mode = False\n window.borderless = False\n window.size = (960,540)\n window.exit_button.disable()\n Sky()\n Game()\n render.setAntialias(AntialiasAttrib.MAuto)\n EditorCamera()\n app.run()" + + with open(f"{ToSavePath}/{ProjectName}/Main.py","w") as File: + File.write(FinalFile) + + return True + +if __name__ == "__main__": + from GamePlusEditor.OtherStuff import CurrentFolderNameReturner + + ProjectExporter("w",f"{CurrentFolderNameReturner()}/Current Games",ToSavePath=CurrentFolderNameReturner().replace("Editor","FInal")) diff --git a/samples/main_menu.py b/samples/main_menu.py index 8c4f9abc..f8e7a1bb 100644 --- a/samples/main_menu.py +++ b/samples/main_menu.py @@ -29,6 +29,7 @@ def __init__(self, text='', **kwargs): main_menu.buttons = [ MenuButton('start', on_click=Func(setattr, state_handler, 'state', 'load_menu')), MenuButton('options', on_click=Func(setattr, state_handler, 'state', 'options_menu')), + MenuButton('level editor', on_click=Func(os.system, "python level_editor/main.py")), MenuButton('quit', on_click=Sequence(Wait(.01), Func(application.quit))), ] for i, e in enumerate(main_menu.buttons): diff --git a/ursina/main.py b/ursina/main.py index 99154a48..89eeb7ad 100644 --- a/ursina/main.py +++ b/ursina/main.py @@ -14,6 +14,28 @@ from ursina import entity from ursina import shader +import os +import atexit +import socket +import subprocess +from direct.filter.CommonFilters import CommonFilters +from ursina import color +from ursina.prefabs.memory_counter import MemoryCounter +from ursina.color import tint +from ursina import Entity, Sky, EditorCamera, Func, destroy, Ursina +from GamePlusEditor.StartingUI import StartingUI +from GamePlusEditor.SceneEditor import SceneEditor +from GamePlusEditor.ProjectSaver import ProjectSaver, SceneStateSaver +from GamePlusEditor.ProjectLoader import SceneStateLoader +from GamePlusEditor.OtherStuff import CurrentFolderNameReturner, RecursivePerformer +from GamePlusEditor.OpenFile import OpenFile, SaveFile +from GamePlusEditor.CoreFiles.InstructionMenu import InstructionMenu +from GamePlusEditor.ProjectExporter import ProjectExporter +from GamePlusEditor.ProjectEditor import ProjectEditor +from GamePlusEditor.CodeEditorPython import CodeEditorPython +from GamePlusEditor.CodeEditorUrsaVisor import CodeEditorUrsaVisor +from GamePlusEditor.CoreFiles.InstructionList import InstructionList +from GamePlusEditor.CoreFiles.Terminal import Terminal import __main__ time.dt = 0 @@ -338,12 +360,276 @@ def run(self, info=True): super().run() -if __name__ == '__main__': - from ursina import * - app = Ursina( - # development_mode=False, - # use_ingame_console=True - ) - def input(key): - print(key) +class UrsinaEditor(Entity): + def __init__(self, EditorCamera, **kwargs): + super().__init__() + self.CurrentSupportedEditors = ["Scene editor", "Code editor", "Ursa-visor editor"] + self.AddCurrentSupportedEditors = [self.AddSceneEditor, self.AddPythonCodeEditor, self.AddUrsaVisorEditor] + self.ProjectEditorsList: list = [] + self.CurrentProjectEditor: ProjectEditor = None + self.CurrentTerminals: list = [] + self.CurrentOpenedGames: list = [] + + self.EditorCamera = EditorCamera + self.NonConfiableEditorDataDefault = {"CurrentProjectNames": [], "RecentEdits": []} + self.NonConfiableEditorData = OpenFile("Non configable editor data.txt", f"{CurrentFolderNameReturner()}/Editor data", self.NonConfiableEditorDataDefault, True) + self.InstructionList = InstructionList() + + self.ConfiableEditorDataDefault = {"Show tooltip": True, "Auto save on exit": False, "Show memory counter": True, "Fullscreen": False, "Anti-aliasing sample": 4, "Render distance (near)": .10, "Render distance (far)": 10000.0, } + self.ConfiableEditorDataDefaultType = ("bool", "bool", "bool", "bool", "int", "float", "float") + self.ConfiableEditorData = OpenFile("Configable editor data.txt", f"{CurrentFolderNameReturner()}/Editor data", self.ConfiableEditorDataDefault, True) + self.FolderName = os.path.dirname(os.path.abspath(__file__)) + self.ProjectSettings = None + self.Filter = CommonFilters(base.win, base.cam) + + self.MemoryCounter = MemoryCounter(enabled=self.ConfiableEditorData["Show memory counter"]) + self.StartingUi = StartingUI(EditorDataDictConfigable=self.ConfiableEditorData, RegiveDataDictFunc=self.RetakeDataDict, RetakeDataDictFunc=self.RegiveDataDict, EditorDataDictNonConfigable=self.NonConfiableEditorData, OnProjectStart=self.StartEdit, ChangeConfigDataToDefaultTypeFunc=self.ChangeConfigDataToDefaultType, ProjectName="", SaveNonConfiableData=self.SaveData, FuncToEnableOnOpen=self.SetupSceneOfWorkedProject, ShowInstructionFunc=self.ShowInstruction, RemoveProjectNameFunc=self.RemoveProject, ExportToPyFunc=self.ExportProjectToPy) + + def StartEdit(self): + self.ProjectEditorsList.append(ProjectEditor(ExportToPyFunc=self.ExportProjectToPy, EditorDataDict=self.ConfiableEditorData, CurrentTabs=[], ShowInstructionFunc=self.ShowInstruction, EditorCamera=self.EditorCamera, ReadyToHostProjectFunc=self.ReadyToHostProject, HostProjectFunc=self.HostProject, enabled=True, ToAddTabsText=self.CurrentSupportedEditors, ToAddTabsFunc=self.AddCurrentSupportedEditors, PlayFunction=self.PlayProject)) + self.CurrentProjectEditor: ProjectEditor = self.ProjectEditorsList[-1] + self.CurrentProjectEditor.SetUp() + self.CurrentProjectEditor.ProjectName = self.StartingUi.ProjectName + self.CurrentProjectEditor.ProjectSettings = self.StartingUi.ProjectSettings + self.StartingUi.ProjectName = "" + self.StartingUi.ProjectSettings = {"ProjectGraphicsQuality": "Low", "ProjectLanguage": "Python", "ProjectNetworkingOnline": False, "CurrentTargatedPlatform": "windows", "CurrentProjectBase": "FPC"} + self.AddSceneEditor() + self.CurrentProjectEditor.JumpTabs(0) + + self.EditorCamera.item_to_in_find_on_mouse_hit_rotate = self.CurrentProjectEditor.CurrentSceneEditor.WorldItems + + self.CurrentProjectEditor.CurrentSceneEditor.enable() + self.CurrentProjectEditor.CurrentSceneEditor.UniversalParentEntity.enable() + RecursivePerformer(self.CurrentProjectEditor.CurrentSceneEditor.UniversalParentEntity) + + def SetupEditor(self, Editor): + if Editor.name.startswith("Scene"): + Editor.MakeEditorEnvironment(application.base.camNode, (255, 255, 255, 0), (0.2399, .999, 0.1009, 0.938)) + elif Editor.name.startswith("Code"): + Editor.SetUp() + elif Editor.name.startswith("Ursa"): + Editor.SetUp() + + def Save(self, SaveOnlyIfProjectAlreayExists=False): + if self.CurrentProjectEditor is None: + return + ProjectSaver(ProjectName=self.CurrentProjectEditor.ProjectName, UdFunc=self.CurrentProjectEditor.UDFunc, UdVar=self.CurrentProjectEditor.UDVars, Udsrc=self.CurrentProjectEditor.UDSrc, WindowConfig=self.CurrentProjectEditor.UDWindowConfig, ToImport=list(self.CurrentProjectEditor.ToImport), Items=self.CurrentProjectEditor.CurrentSceneEditor.WorldItems, Path=f'{self.FolderName}/Current Games', GameSettings=self.CurrentProjectEditor.ProjectSettings, SaveOnlyIfProjectAlreayExists=SaveOnlyIfProjectAlreayExists) + + SceneState = self.CurrentProjectEditor.CurrentSceneEditor.GetState() + SceneStateSaver(self.CurrentProjectEditor.ProjectName, f'{self.FolderName}/Current Games', SceneState) + + if not self.CurrentProjectEditor.ProjectName in self.NonConfiableEditorData["CurrentProjectNames"] and not SaveOnlyIfProjectAlreayExists: + self.NonConfiableEditorData["CurrentProjectNames"].append(self.CurrentProjectEditor.ProjectName) + self.SaveData() + + def OnExit(self): + self.Save(True) + for process in self.CurrentOpenedGames: + if process.poll() is not None: + process.terminate() + + if self.ConfiableEditorData["Auto save on exit"] and self.StartingUi.ProjectName: + self.Save() + + def Setup(self): + self.ConfigEditorAsSettings() + atexit.register(self.OnExit) + self.StartingUi.Setup() + self.StartingUi.ShowRecentProjects(self.SetupSceneOfWorkedProject) + + def SaveData(self): + SaveFile("Non configable editor data.txt", f"{CurrentFolderNameReturner()}/Editor data", self.NonConfiableEditorData) + SaveFile("Configable editor data.txt", f"{CurrentFolderNameReturner()}/Editor data", self.ConfiableEditorData) + + def ChangeConfigDataToDefaultType(self, Data: dict): + try: + for i, j in enumerate(self.ConfiableEditorDataDefault): + if type(self.ConfiableEditorDataDefault[j]).__name__ == "int": + Data[j] = int(Data[j]) + + elif type(self.ConfiableEditorDataDefault[j]).__name__ == "float": + if Data[j].find('.') != -1: + Data[j] = Data[j][:Data[j].find('.') + 1] + Data[j][Data[j].find('.') + 1:].replace('.', '') + Data[j] = float(Data[j]) + + elif type(self.ConfiableEditorDataDefault[j]).__name__ == "bool": + if Data[j].lower() == "true": + Data[j] = True + elif Data[j].lower() == "false": + Data[j] = False + + elif type(self.ConfiableEditorDataDefault[j]).__name__ == "str": + Data[j] = str(Data[j]) + + self.ConfiableEditorData = Data + self.ConfigEditorAsSettings() + + except Exception as e: + print(f"{__file__}:: {e}") + + def ShowInstruction(self, Str, Color=tint(color.white, -.6), Title="Info", KillIn=1, KillAfter=5, WordWrap=40): + self.InstructionList.append(InstructionMenu(ToSay=Str, Title=Title, Color=Color, KillIn=KillIn, killAfter=KillAfter, WordWrap=WordWrap)) + self.InstructionList[-1].CloseButton.on_click = Func(self.DestroyInstruction, self.InstructionList[-1]) + + def DestroyInstruction(self, Instruction): + self.Index = self.InstructionList.index(Instruction) + destroy(self.InstructionList[self.Index]) + del self.InstructionList[self.Index] + + def AddTextToTerminal(self, text): + for i in self.CurrentTerminals: + i.AddTextToTerminal(text) + + def AddSceneEditor(self): + def AddTerminal(ParnetEntity): + TempTerminal = Terminal(self.AddTextToTerminal, parent=ParnetEntity) + self.CurrentTerminals.append(TempTerminal) + + TempTerminal.Bg.scale_x = 1.35 + TempTerminal.Bg.origin = (.5, 0, 0) + TempTerminal.UniversalParentEntity.origin = (.5, 0, 0) + TempTerminal.UniversalParentEntity.position = Vec3(0.887, -0.3, 2) + TempTerminal.Bg.scale = Vec3(1.35, 0.2, 1) + TempTerminal.SetUp() + + self.CurrentProjectEditor.CurrentTabs.append(SceneEditor(enabled=True, SaveFunction=self.Save, EditorCamera=self.EditorCamera, EditorDataDict=self.ConfiableEditorData, ShowInstructionFunc=self.ShowInstruction, AddTerminalFunc=None, ParentProjectEditor=self.CurrentProjectEditor)) + self.CurrentProjectEditor.CurrentTabs[-1].AddTerminal = Func(AddTerminal, self.CurrentProjectEditor.CurrentTabs[-1].UniversalParentEntity) + + self.CurrentProjectEditor.CurrentTabs[-1].name = f"Scene Editor {len([i for i in range(len(self.CurrentProjectEditor.CurrentTabs)) if type(self.CurrentProjectEditor.CurrentTabs[i]).__name__ == 'SceneEditor'])}" + self.CurrentProjectEditor.CurrentTabs[-1].GetPosTemp() + self.CurrentProjectEditor.CurrentTabs[-1].Setup() + + if len(self.CurrentProjectEditor.CurrentTabs) > 1: + self.CurrentProjectEditor.CurrentTabs[-1].WorldItems = self.CurrentProjectEditor.CurrentSceneEditor.WorldItems + self.CurrentProjectEditor.CurrentTabs[-1].ignore = True + self.CurrentProjectEditor.CurrentTabs[-1].disable() + self.CurrentProjectEditor.CurrentTabs[-1].UniversalParentEntity.disable() + RecursivePerformer(self.CurrentProjectEditor.CurrentTabs[-1].SpecialEntities, "disable") + + else: + self.CurrentProjectEditor.CurrentSceneEditor = self.CurrentProjectEditor.CurrentTabs[-1] + self.CurrentProjectEditor.CurrentEditor = self.CurrentProjectEditor.CurrentTabs[-1] + self.CurrentProjectEditor.CurrentTabs[-1].enable() + RecursivePerformer(self.CurrentProjectEditor.CurrentTabs[-1].UniversalParentEntity) + RecursivePerformer(self.CurrentProjectEditor.CurrentTabs[-1].SpecialEntities) + self.SetupEditor(self.CurrentProjectEditor.CurrentTabs[-1]) + self.CurrentProjectEditor.AfterSceneEditorSetUp() + + self.CurrentProjectEditor.UpdateTabsMenu() + + def AddPythonCodeEditor(self): + self.CurrentProjectEditor.CurrentTabs.append(CodeEditorPython(ProjectName=self.CurrentProjectEditor.ProjectName, SaveFunction=self.Save, enabled=False, EditorDataDict=self.ConfiableEditorData, ignore=True, UdSrc=self.CurrentProjectEditor.UDSrc, OnFileAdded=self.CurrentProjectEditor.OnFileAdded, ShowInstructionFunc=self.ShowInstruction)) + + self.CurrentProjectEditor.CurrentTabs[-1].name = f"Code Editor {len([i for i in range(len(self.CurrentProjectEditor.CurrentTabs)) if type(self.CurrentProjectEditor.CurrentTabs[i]).__name__ == 'CodeEditorPython'])}" + self.SetupEditor(self.CurrentProjectEditor.CurrentTabs[-1]) + self.CurrentProjectEditor.UpdateTabsMenu() + + def AddUrsaVisorEditor(self): + self.CurrentProjectEditor.CurrentTabs.append(CodeEditorUrsaVisor(enabled=False, ignore=True, SaveFunction=self.Save, EditorDataDict=self.ConfiableEditorData)) + + self.CurrentProjectEditor.CurrentTabs[-1].name = f"Ursa Editor {len([i for i in range(len(self.CurrentProjectEditor.CurrentTabs)) if type(self.CurrentProjectEditor.CurrentTabs[i]).__name__ == 'CodeEditorUrsaVisor'])}" + + self.SetupEditor(self.CurrentProjectEditor.CurrentTabs[-1]) + self.CurrentProjectEditor.UpdateTabsMenu() + + def ConfigEditorAsSettings(self): + self.StartingUi.ConfigEditorAsSettings(self.ConfiableEditorData) + for i in range(len(self.ProjectEditorsList)): + for j in range(len(self.ProjectEditorsList[i].CurrentTabs)): + self.ProjectEditorsList[i].CurrentTabs[j].ConfigEditorAsSettings(self.ConfiableEditorData) + + self.MemoryCounter.enabled = self.ConfiableEditorData["Show memory counter"] + self.StartingUi.EditorDataDict = self.ConfiableEditorData + window.fullscreen = self.ConfiableEditorData["Fullscreen"] + camera.clip_plane_near = self.ConfiableEditorData["Render distance (near)"] + camera.clip_plane_far = self.ConfiableEditorData["Render distance (far)"] + if self.ConfiableEditorData["Anti-aliasing sample"] > 0: + self.Filter.delMSAA() + self.Filter.setMSAA(self.ConfiableEditorData["Anti-aliasing sample"]) + else: + self.Filter.delMSAA() + + def ExportProjectToPy(self, Path, ProjectName=None, InProjectEditor=True, Demo=False): + if ProjectName is None: + ProjectName = self.CurrentProjectEditor.ProjectName + + if Path != "": + if InProjectEditor: + self.Save() + Path.replace("\\", "/") + Path += "/Exported games" + + ProjectExporter(ProjectName=ProjectName, ProjectPath=f'{self.FolderName}/Current Games', ToSavePath=Path, Demo=Demo) + + self.ShowInstruction("Exported successfully", KillAfter=6, KillIn=3) + else: + self.ShowInstruction("Invalid path", KillAfter=6, KillIn=3, Title="Error") + + def PlayProject(self): + self.CurrentProjectEditor.SaveProjectButton.on_click() + self.ExportProjectToPy(CurrentFolderNameReturner(), self.CurrentProjectEditor.ProjectName, False, Demo=True) + self.CurrentOpenedGames.append(subprocess.Popen(["python", f"{CurrentFolderNameReturner()}/Exported games/{self.CurrentProjectEditor.ProjectName}/Main.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)) + + def ReadyToHostProject(self): + self.Port = 48513 + self.Ip = "localhost" + + self.Server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + return self.Port, self.Ip + + def HostProject(self, MaxJoin=0): + self.Server.bind((self.Ip, self.Port)) + if MaxJoin != 0: + self.Server.listen(MaxJoin) + else: + self.Server.listen() + + def SetupSceneOfWorkedProject(self, WorldItemsList, Project=""): + self.StartEdit() + for item in WorldItemsList: + self.CurrentProjectEditor.CurrentSceneEditor.WorldItems.append(eval(item['cls'] + item['args'])) + self.SetProjectName(Project) + print(f"Project name: {self.CurrentProjectEditor.ProjectName}") + LoadedSceneState = SceneStateLoader(self.CurrentProjectEditor.ProjectName, f'{self.FolderName}/Current Games') + if LoadedSceneState is not False: + self.CurrentProjectEditor.CurrentSceneEditor.SetState(LoadedSceneState) + + def SetProjectName(self, Value: str): + self.CurrentProjectEditor.ProjectName = Value + + def RemoveProject(self, Name, Replace=False): + if Replace is False: + self.NonConfiableEditorData["CurrentProjectNames"].remove(Name) + self.NonConfiableEditorData["RecentEdits"].remove(Name) + + else: + if Replace not in self.NonConfiableEditorData["CurrentProjectNames"]: + self.NonConfiableEditorData["CurrentProjectNames"][self.NonConfiableEditorData["CurrentProjectNames"].index(Name)] = Replace + self.NonConfiableEditorData["RecentEdits"][self.NonConfiableEditorData["RecentEdits"].index(Name)] = Replace + + self.SaveData() + + def RegiveDataDict(self): + return self.ConfiableEditorData, self.NonConfiableEditorData + + def RetakeDataDict(self, ConfigableEditorData, NonConfigableEditorData): + self.ConfiableEditorData = ConfigableEditorData + self.NonConfigableEditorData = NonConfigableEditorData + + +def BaseRunner(): + app = Ursina() + window.exit_button.disable() + window.fps_counter.disable() + application.development_mode = False + window.cog_button.disable() + + Sky() + Editor = UrsinaEditor(EditCam := EditorCamera()) + Editor.Setup() app.run() + + +if __name__ == "__main__": + BaseRunner()