Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions studio/app/core/project_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
*(f"LPT{i}" for i in range(1, 10)),
}

PROJECT_TEMPLATES = {
"basic": "basic",
"doodle-quest": "DoodleQuest",
"ninja-runner": "NinjaRunner",
"catbox-mre": "CatBoxMRE",
}

@dataclass(slots=True)
class ProjectInfo:
root: Path
Expand Down Expand Up @@ -74,13 +81,25 @@ def validate_project_name(name: str) -> str:
def project_path(self, name: str) -> Path:
return self.default_projects_root / self.validate_project_name(name)

def create_project(self, name: str, metadata: dict | None = None, sdk_metadata: dict | None = None) -> ProjectInfo:
def create_project(
self,
name: str,
metadata: dict | None = None,
sdk_metadata: dict | None = None,
*,
template_name: str = "basic",
) -> ProjectInfo:
name = self.validate_project_name(name)
destination = self.project_path(name)
if destination.exists():
raise FileExistsError(destination)

template = self.engine_root / "templates" / "basic"
template_key = str(template_name or "basic").strip()
template_dir = PROJECT_TEMPLATES.get(template_key)
if not template_dir:
raise ValueError(f"Unknown project template: {template_key}")

template = self.engine_root / "templates" / template_dir
if not template.is_dir():
raise FileNotFoundError(f"Project template not found: {template}")

Expand All @@ -101,6 +120,7 @@ def create_project(self, name: str, metadata: dict | None = None, sdk_metadata:
appid = payload.get("appid")
payload.update(dict(metadata))
payload["name"] = name
payload["template"] = template_key
if appid is not None:
payload["appid"] = appid
descriptor.write_text(
Expand Down
53 changes: 37 additions & 16 deletions studio/app/ui/mediatek_mre_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

@dataclass(frozen=True)
class MediaTekProjectConfig:
template_id: str
app_name: str
app_version: str
vendor: str
Expand All @@ -34,6 +35,7 @@ class MediaTekProjectConfig:
def project_metadata(self) -> dict:
return {
"name": self.app_name,
"template": self.template_id,
"app_version": self.app_version,
"vendor": self.vendor,
"ram_kb": self.ram_kb,
Expand Down Expand Up @@ -74,6 +76,14 @@ def sdk_metadata(self) -> dict:
}


PROJECT_TEMPLATE_OPTIONS = (
("basic", "Blank Project", "Khung Lua tối giản để bắt đầu từ đầu"),
("doodle-quest", "Doodle Quest", "Game mẫu có UI notebook, HUD, combo, 3 màn"),
("ninja-runner", "Ninja Runner", "Game runner/parkour mẫu 240x320"),
("catbox-mre", "CatBoxMRE", "Mẫu game nâng cao với nhiều hệ thống"),
)


RESOLUTIONS = (
("240x320", "240x320 (QVGA - Chuẩn Nokia)", 240, 320),
("320x240", "320x240 (QVGA - Ngang)", 320, 240),
Expand Down Expand Up @@ -202,6 +212,12 @@ def __init__(
grid.setHorizontalSpacing(14)
grid.setVerticalSpacing(8)

self.template = QComboBox()
self.template.setObjectName("MRECombo")
for template_id, label, description in PROJECT_TEMPLATE_OPTIONS:
self.template.addItem(f"{label} — {description}", template_id)
self.template.setCurrentIndex(1)

self.app_name = QLineEdit(app_name)
self.app_name.setObjectName("MREField")
self.app_name.setPlaceholderText("Ví dụ: MRE Snake Retro")
Expand Down Expand Up @@ -234,31 +250,35 @@ def __init__(
self.path_preview.setObjectName("MREPathPreview")
self.path_preview.setWordWrap(True)

self._label(grid, "Tên ứng dụng (APPNAME)", 0, 0)
self._label(grid, "Phiên bản (APPVER)", 0, 1)
grid.addWidget(self.app_name, 1, 0)
grid.addWidget(self.app_version, 1, 1)
self._label(grid, "Mẫu dự án", 0, 0, 1, 2)
grid.addWidget(self.template, 1, 0, 1, 2)

self._label(grid, "Tên ứng dụng (APPNAME)", 2, 0)
self._label(grid, "Phiên bản (APPVER)", 2, 1)
grid.addWidget(self.app_name, 3, 0)
grid.addWidget(self.app_version, 3, 1)

self._label(grid, "Nhà phát triển (VENDOR)", 2, 0, 1, 2)
grid.addWidget(self.vendor, 3, 0, 1, 2)
self._label(grid, "Nhà phát triển (VENDOR)", 4, 0, 1, 2)
grid.addWidget(self.vendor, 5, 0, 1, 2)

self._label(grid, "Màn hình (Resolution)", 4, 0)
self._label(grid, "Chipset MediaTek", 4, 1)
grid.addWidget(self.resolution, 5, 0)
grid.addWidget(self.chipset, 5, 1)
self._label(grid, "Màn hình (Resolution)", 6, 0)
self._label(grid, "Chipset MediaTek", 6, 1)
grid.addWidget(self.resolution, 7, 0)
grid.addWidget(self.chipset, 7, 1)

self._label(grid, "Dung lượng Heap RAM cấp phát", 6, 0, 1, 2)
grid.addWidget(self.ram, 7, 0, 1, 2)
self._label(grid, "Dung lượng Heap RAM cấp phát", 8, 0, 1, 2)
grid.addWidget(self.ram, 9, 0, 1, 2)

hint = QLabel(
"AppID được tạo tự động. MTK6260 mặc định dùng profile Nokia 225 / "
"S30+ native; đường dẫn MRE SDK dùng cấu hình chung trong Settings."
"AppID được tạo tự động. Chọn Doodle Quest để tạo sẵn game có "
"Splash/Menu/HUD/gameplay notebook. MTK6260 mặc định dùng profile "
"Nokia 225 / S30+ native."
)
hint.setObjectName("MREDialogHint")
hint.setWordWrap(True)
grid.addWidget(hint, 8, 0, 1, 2)
grid.addWidget(hint, 10, 0, 1, 2)

grid.addWidget(self.path_preview, 9, 0, 1, 2)
grid.addWidget(self.path_preview, 11, 0, 1, 2)
root.addWidget(body)

footer = QFrame()
Expand Down Expand Up @@ -360,6 +380,7 @@ def configuration(self) -> MediaTekProjectConfig:
cid, compat, api = self.chipset.currentData()
ram_kb = int(self.ram.currentData())
return MediaTekProjectConfig(
template_id=str(self.template.currentData() or "basic"),
app_name=ProjectSession.validate_project_name(self.app_name.text()),
app_version=self.app_version.text().strip(),
vendor=self.vendor.text().strip(),
Expand Down
2 changes: 2 additions & 0 deletions studio/app/vxpui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,7 @@ def create_project_from_template(self) -> None:
name,
metadata=config.project_metadata(),
sdk_metadata=config.sdk_metadata(),
template_name=config.template_id,
)
except FileExistsError as exc:
NoticeDialog("Dự án đã tồn tại", f"Đã có dự án trùng tên:\n{exc}", self, error=True).exec()
Expand All @@ -976,6 +977,7 @@ def create_project_from_template(self) -> None:
self.show_status(
f"Project created: {info.root}"
+ (f" | AppID {appid}" if appid else "")
+ f" | Template {config.template_id}"
+ f" | {config.chipset_id} | {config.screen_width}x{config.screen_height}"
)

Expand Down
21 changes: 21 additions & 0 deletions templates/DoodleQuest/.luas30/mre_sdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"schema": 1,
"platform": "MediaTek MRE",
"appname": "DoodleQuest",
"appver": "1.0.0",
"vendor": "LuaS30",
"resolution": {
"id": "240x320",
"label": "240x320 (QVGA - Chuẩn Nokia)",
"width": 240,
"height": 320
},
"chipset": {
"id": "MTK6260",
"label": "MTK6260 (Nokia 220, 225)"
},
"heap_kb": 1024,
"heap_label": "1024 KB (1MB - Tiêu chuẩn game MRE)",
"compat_profile": "nokia225-rm1011",
"mre_api": "Audio File ProMng"
}
16 changes: 16 additions & 0 deletions templates/DoodleQuest/.luas30/ui_design.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"format": "LuaS30 UI Design",
"version": 2,
"canvas": {
"width": 240,
"height": 320
},
"current": "main",
"screens": [
{
"id": "main",
"name": "Main",
"items": []
}
]
}
31 changes: 31 additions & 0 deletions templates/DoodleQuest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Doodle Quest — LuaS30 game template

A lightweight 240×320 game template with procedural notebook/doodle graphics and a complete keypad UI.

## Included screens

- Splash
- Main menu
- How To Play
- Settings
- About
- Gameplay + HUD
- Win / Game Over state

## Controls

- 2 / 8: up / down
- 4 / 6: left / right
- 5 or #: confirm
- 0 / Back / Soft Left: back

## Target

- Nokia S30+ / MediaTek MRE
- MTK6260
- 240×320
- 1024 KB baseline heap
- 15 FPS
- profile: `nokia225-rm1011`

The graphics are drawn with rectangles, frames and text, so the template works without loading large bitmap assets and is suitable as a low-RAM starter project.
7 changes: 7 additions & 0 deletions templates/DoodleQuest/conf.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
config = {
name = "Doodle Quest",
screen_width = 240,
screen_height = 320,
fps = 15
}
return config
52 changes: 52 additions & 0 deletions templates/DoodleQuest/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-- Doodle Quest: lightweight notebook-art game template for LuaS30 / MRE.
-- Screens: splash -> main menu -> play -> pause, plus guide/settings/about.

local E = engine
local Gfx = require("src.gfx")
local UI = require("src.ui")
local Game = require("src.game")

local mode = "ui"

local KEYMAP = {
["2"] = "up", ["8"] = "down", ["4"] = "left", ["6"] = "right",
["5"] = "ok", ["#"] = "ok",
["0"] = "back", ["clear"] = "back", ["back"] = "back",
["softleft"] = "back", ["softright"] = "ok",
}

local function route(name)
if name == "play" then
mode = "game"
Game.start()
else
mode = "ui"
UI.enter(name)
end
end

function E.load()
E.set_font(8)
UI.go = route
Game.onExit = function() route("menu") end
route("splash")
end

function E.update(dt)
if mode == "game" then Game.update(dt) else UI.update(dt) end
end

function E.draw()
if mode == "game" then Game.draw() else UI.draw() end
E.flush()
end

function E.keypressed(key)
local k = KEYMAP[key] or key
if mode == "game" then Game.keypressed(k) else UI.keypressed(k) end
end

function E.keyreleased(key)
local k = KEYMAP[key] or key
if mode == "game" then Game.keyreleased(k) end
end
19 changes: 19 additions & 0 deletions templates/DoodleQuest/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "DoodleQuest",
"vendor": "LuaS30",
"appid": 586534796,
"ram_kb": 1024,
"screen_width": 240,
"screen_height": 320,
"fps": 15,
"runtime_target": "mre-s30plus",
"single_vxp": true,
"compat_profile": "nokia225-rm1011",
"mre_api": "Audio File ProMng",
"app_version": "1.0.0",
"mediatek_chipset": "MTK6260",
"mediatek_chipset_label": "MTK6260 (Nokia 220, 225)",
"resolution": "240x320",
"resolution_label": "240x320 (QVGA - Chuẩn Nokia)",
"project_wizard": "mediatek-mre-sdk-v1"
}
Loading
Loading