Compare commits

..

1 Commits

Author SHA1 Message Date
AncTe
a1dae80f71
Merge 8b40e53c2c into 820dc5bf4c 2025-09-13 14:23:21 +00:00
19 changed files with 77 additions and 151 deletions

View File

@ -1,5 +1,5 @@
import { Patch, PatchClass } from '@motajs/legacy-common'; import { Patch, PatchClass } from '@motajs/legacy-common';
import { WeatherController } from '../render/weather'; import { WeatherController } from '../weather';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
// todo: 添加弃用警告 logger.warn(56) // todo: 添加弃用警告 logger.warn(56)

View File

@ -54,7 +54,6 @@ export * from './fx';
export * from './legacy'; export * from './legacy';
export * from './ui'; export * from './ui';
export * from './utils'; export * from './utils';
export * from './weather';
export * from './renderer'; export * from './renderer';
export * from './shared'; export * from './shared';
export * from './use'; export * from './use';

View File

@ -80,7 +80,6 @@ const MainScene = defineComponent(() => {
const hideStatus = ref(false); const hideStatus = ref(false);
const locked = ref(false); const locked = ref(false);
const weather = new WeatherController(); const weather = new WeatherController();
weather.extern('main');
onMounted(() => { onMounted(() => {
if (map.value) { if (map.value) {
@ -202,6 +201,7 @@ const MainScene = defineComponent(() => {
const step = core.status.stepPostfix; const step = core.status.stepPostfix;
if (!step) return; if (!step) return;
const ctx = canvas.ctx; const ctx = canvas.ctx;
ctx.save();
ctx.fillStyle = '#fff'; ctx.fillStyle = '#fff';
step.forEach(({ x, y, direction }) => { step.forEach(({ x, y, direction }) => {
ctx.fillRect(x * 32 + 12, y * 32 + 12, 8, 8); ctx.fillRect(x * 32 + 12, y * 32 + 12, 8, 8);
@ -222,6 +222,7 @@ const MainScene = defineComponent(() => {
} }
} }
}); });
ctx.restore();
}; };
//#region 交互监听 //#region 交互监听

View File

@ -297,6 +297,7 @@ export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
let linked = false; let linked = false;
const drawMinimap = (canvas: MotaOffscreenCanvas2D) => { const drawMinimap = (canvas: MotaOffscreenCanvas2D) => {
const ctx = canvas.ctx; const ctx = canvas.ctx;
ctx.save();
ctx.scale( ctx.scale(
1 / core.domStyle.scale / devicePixelRatio, 1 / core.domStyle.scale / devicePixelRatio,
1 / core.domStyle.scale / devicePixelRatio 1 / core.domStyle.scale / devicePixelRatio
@ -319,6 +320,7 @@ export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
) ?? ''; ) ?? '';
minimapDrawer.locateMap(minimapDrawer.nowFloor); minimapDrawer.locateMap(minimapDrawer.nowFloor);
minimapDrawer.drawMap(); minimapDrawer.drawMap();
ctx.restore();
}; };
watch( watch(

View File

@ -376,9 +376,11 @@ export const GameTitle = defineComponent<GameTitleProps>(props => {
createMaskGradient(ctx); createMaskGradient(ctx);
} }
const pos = maskPos.value; const pos = maskPos.value;
ctx.save();
ctx.translate(pos, 0); ctx.translate(pos, 0);
ctx.fillStyle = maskGradient!; ctx.fillStyle = maskGradient!;
ctx.fillRect(0, 0, MAIN_WIDTH + MAIN_HEIGHT + 200, MAIN_HEIGHT); ctx.fillRect(0, 0, MAIN_WIDTH + MAIN_HEIGHT + 200, MAIN_HEIGHT);
ctx.restore();
}; };
const renderTitle = (canvas: MotaOffscreenCanvas2D) => { const renderTitle = (canvas: MotaOffscreenCanvas2D) => {
@ -386,6 +388,7 @@ export const GameTitle = defineComponent<GameTitleProps>(props => {
if (titleGradient === null) { if (titleGradient === null) {
createTitleGradient(ctx); createTitleGradient(ctx);
} }
ctx.save();
ctx.textAlign = 'center'; ctx.textAlign = 'center';
ctx.textBaseline = 'middle'; ctx.textBaseline = 'middle';
ctx.font = titleFont; ctx.font = titleFont;
@ -397,10 +400,12 @@ export const GameTitle = defineComponent<GameTitleProps>(props => {
blur(1px) blur(1px)
`; `;
ctx.fillText(core.firstData.title, 320, 50); ctx.fillText(core.firstData.title, 320, 50);
ctx.restore();
}; };
const renderCursor = (canvas: MotaOffscreenCanvas2D) => { const renderCursor = (canvas: MotaOffscreenCanvas2D) => {
const ctx = canvas.ctx; const ctx = canvas.ctx;
ctx.save();
ctx.translate(0, 5); ctx.translate(0, 5);
ctx.scale(1, cursorScale); ctx.scale(1, cursorScale);
ctx.beginPath(); ctx.beginPath();
@ -410,6 +415,7 @@ export const GameTitle = defineComponent<GameTitleProps>(props => {
ctx.strokeStyle = '#fff'; ctx.strokeStyle = '#fff';
ctx.lineWidth = 1; ctx.lineWidth = 1;
ctx.stroke(); ctx.stroke();
ctx.restore();
}; };
return () => ( return () => (

View File

@ -1,8 +1,11 @@
import { onUnmounted } from 'vue'; import { onUnmounted } from 'vue';
import { WeatherController } from '../weather'; import { WeatherController } from '../../weather';
let weatherId = 0;
export function useWeather(): [WeatherController] { export function useWeather(): [WeatherController] {
const weather = new WeatherController(); const weather = new WeatherController(`@weather-${weatherId}`);
weatherId++;
onUnmounted(() => { onUnmounted(() => {
weather.destroy(); weather.destroy();

View File

@ -77,9 +77,8 @@ export class WeatherController implements IWeatherController {
level: number = 5 level: number = 5
): IWeatherInstance<R, T> | null { ): IWeatherInstance<R, T> | null {
const obj = this.getWeatherObject<R, T>(weather); const obj = this.getWeatherObject<R, T>(weather);
if (!obj || !this.container) return null; if (!obj) return null;
const element = obj.create(level); const element = obj.create(level);
element.size(this.container.width, this.container.height);
const instance = new WeatherInstance(obj, element); const instance = new WeatherInstance(obj, element);
instance.setZIndex(this.zIndex + this.active.size); instance.setZIndex(this.zIndex + this.active.size);
this.active.add(instance); this.active.add(instance);
@ -93,13 +92,6 @@ export class WeatherController implements IWeatherController {
this.active.delete(instance); this.active.delete(instance);
} }
clearWeather(): void {
this.active.forEach(v => {
v.weather.destroy();
});
this.active.clear();
}
/** /**
* 使 {@link WeatherController.get} * 使 {@link WeatherController.get}
* @param id id * @param id id
@ -110,7 +102,10 @@ export class WeatherController implements IWeatherController {
} }
destroy() { destroy() {
this.clearWeather(); this.active.forEach(v => {
v.weather.destroy();
});
this.active.clear();
WeatherController.ticker.remove(this.tick); WeatherController.ticker.remove(this.tick);
if (!isNil(this.externId)) { if (!isNil(this.externId)) {
WeatherController.extern.delete(this.externId); WeatherController.extern.delete(this.externId);

View File

@ -1,11 +1,11 @@
import { WeatherController } from './controller'; import { WeatherController } from './controller';
import { CloudWeather, RainWeather } from './presets'; import { CloudWeather, RainWeather, SnowWeather, SunWeather } from './presets';
export function createWeather() { export function createWeather() {
WeatherController.register('cloud', CloudWeather); WeatherController.register('cloud', CloudWeather);
WeatherController.register('rain', RainWeather); WeatherController.register('rain', RainWeather);
// WeatherController.register('snow', SnowWeather); WeatherController.register('snow', SnowWeather);
// WeatherController.register('sun', SunWeather); WeatherController.register('sun', SunWeather);
} }
export * from './presets'; export * from './presets';

View File

@ -1,82 +1,16 @@
import { MotaOffscreenCanvas2D, Sprite } from '@motajs/render-core'; import { Sprite } from '@motajs/render-core';
import { Weather } from '../weather'; import { Weather } from '../weather';
export class CloudWeather extends Weather<Sprite> { export class CloudWeather extends Weather<Sprite> {
/** 云层的不透明度 */ tick(timestamp: number): void {
private alpha: number = 0; throw new Error('Method not implemented.');
/** 水平速度 */
private vx: number = 0;
/** 竖直速度 */
private vy: number = 0;
/** 水平位置 */
private cx: number = 0;
/** 竖直位置 */
private cy: number = 0;
/** 云层移动的最大速度 */
private maxSpeed: number = 1;
/** 云层图像 */
private image: HTMLImageElement | null = null;
/** 上一次执行速度变换的时刻 */
private lastDvTime = 0;
private drawCloud(canvas: MotaOffscreenCanvas2D) {
const ctx = canvas.ctx;
if (!this.image) return;
ctx.globalAlpha = this.alpha;
const { width, height } = this.image;
for (let x = -1; x < 2; x++) {
for (let y = -1; y < 2; y++) {
const dx = x * width + this.cx;
const dy = y * height + this.cy;
if (dx > canvas.width || dy > canvas.height) continue;
if (dx + width < 0 || dy + height < 0) continue;
ctx.drawImage(this.image, dx, dy, width, height);
}
}
}
tick(time: number): void {
if (!this.element || !this.image) return;
this.element.update();
if (time - this.lastDvTime > 50) {
this.lastDvTime = time;
const dvx = ((Math.random() - 0.5) * this.level) / 20;
const dvy = ((Math.random() - 0.5) * this.level) / 20;
if (Math.sign(dvx) === Math.sign(this.vx)) {
const ratio = Math.sqrt(
(this.maxSpeed - Math.abs(this.vx)) / this.maxSpeed
);
const value = Math.abs(dvx) * ratio;
this.vx += value * Math.sign(dvx);
} else {
this.vx += dvx;
}
if (Math.sign(dvy) === Math.sign(this.vy)) {
const ratio = Math.sqrt(
(this.maxSpeed - Math.abs(this.vy)) / this.maxSpeed
);
const value = Math.abs(dvy) * ratio;
this.vy += value * Math.sign(dvy);
} else {
this.vy += dvy;
}
}
this.cx += this.vx;
this.cy += this.vy;
this.cx %= this.image.width;
this.cy %= this.image.height;
} }
createElement(level: number): Sprite { createElement(level: number): Sprite {
const element = new Sprite('static', true); throw new Error('Method not implemented.');
element.setRenderFn(canvas => this.drawCloud(canvas));
this.maxSpeed = Math.sqrt(level) * 5;
this.vx = ((Math.random() - 0.5) * this.maxSpeed) / 2;
this.vy = ((Math.random() - 0.5) * this.maxSpeed) / 2;
this.alpha = Math.sqrt(level) / 10;
this.image = core.material.images.images['cloud.png'];
return element;
} }
onDestroy(): void {} onDestroy(): void {
throw new Error('Method not implemented.');
}
} }

View File

@ -75,11 +75,6 @@ export interface IWeatherController {
*/ */
deactivate(instance: IWeatherInstance): void; deactivate(instance: IWeatherInstance): void;
/**
*
*/
clearWeather(): void;
/** /**
* *
*/ */

View File

@ -575,7 +575,7 @@ export function loadDefaultResource() {
] = res.resource; ] = res.resource;
}); });
}); });
const weathers: (keyof Weather)[] = ['fog', 'sun']; const weathers: (keyof Weather)[] = ['fog', 'cloud', 'sun'];
weathers.forEach(v => { weathers.forEach(v => {
const res = LoadTask.add('material', `material/${v}.png`); const res = LoadTask.add('material', `material/${v}.png`);
res.once('load', res => { res.once('load', res => {
@ -618,7 +618,7 @@ export async function loadCompressedResource() {
HTMLImageElement HTMLImageElement
>[]; >[];
materialImages.push('keyboard'); materialImages.push('keyboard');
const weathers: (keyof Weather)[] = ['fog', 'sun']; const weathers: (keyof Weather)[] = ['fog', 'cloud', 'sun'];
Object.entries(list).forEach(v => { Object.entries(list).forEach(v => {
const [uri, list] = v; const [uri, list] = v;

View File

@ -33,9 +33,7 @@ export class Sprite<
canvas: MotaOffscreenCanvas2D, canvas: MotaOffscreenCanvas2D,
transform: Transform transform: Transform
): void { ): void {
canvas.ctx.save();
this.renderFn(canvas, transform); this.renderFn(canvas, transform);
canvas.ctx.restore();
} }
setRenderFn(fn: RenderFunction) { setRenderFn(fn: RenderFunction) {

View File

@ -127,7 +127,6 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"bg.webp", "bg.webp",
"boom.png", "boom.png",
"botton.png", "botton.png",
"cloud.png",
"def.png", "def.png",
"exp.png", "exp.png",
"hero1.png", "hero1.png",

View File

@ -420,7 +420,7 @@ main.floors.MT14=
], ],
"weather": [ "weather": [
"cloud", "cloud",
5 1
], ],
"beforeBattle": {}, "beforeBattle": {},
"cannotMoveIn": {}, "cannotMoveIn": {},

View File

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -61,16 +61,16 @@ import fs from 'fs/promises';
names += '}'; names += '}';
// 5. 写入文件 // 5. 写入文件
await fs.writeFile('./src/types/source/cls.d.ts', id2cls, 'utf-8'); await fs.writeFile('./src/source/cls.d.ts', id2cls, 'utf-8');
await fs.writeFile('./src/types/source/events.d.ts', eventDec, 'utf-8'); await fs.writeFile('./src/source/events.d.ts', eventDec, 'utf-8');
await fs.writeFile('./src/types/source/items.d.ts', itemDec, 'utf-8'); await fs.writeFile('./src/source/items.d.ts', itemDec, 'utf-8');
await fs.writeFile( await fs.writeFile(
'./src/types/source/maps.d.ts', './src/source/maps.d.ts',
`${id2num}\n${num2id}`, `${id2num}\n${num2id}`,
'utf-8' 'utf-8'
); );
await fs.writeFile( await fs.writeFile(
'./src/types/source/data.d.ts', './src/source/data.d.ts',
` `
${floorId} ${floorId}
${d.images.length > 0 ? imgs : 'type ImageIds = never\n'} ${d.images.length > 0 ? imgs : 'type ImageIds = never\n'}

View File

@ -350,17 +350,16 @@ const apiWriteFile = withSafeCheck(async (req, res, path) => {
encoding: type as BufferEncoding encoding: type as BufferEncoding
}); });
res.end(); res.end();
if (path.resolved.endsWith('project/events.js')) {
if (/project(\/|\\)events\.js/.test(path.resolved)) {
doDeclaration('events', value); doDeclaration('events', value);
} }
if (/project(\/|\\)items\.js/.test(path.resolved)) { if (path.resolved.endsWith('project/items.js')) {
doDeclaration('items', value); doDeclaration('items', value);
} }
if (/project(\/|\\)maps\.js/.test(path.resolved)) { if (path.resolved.endsWith('project/maps.js')) {
doDeclaration('maps', value); doDeclaration('maps', value);
} }
if (/project(\/|\\)data\.js/.test(path.resolved)) { if (path.resolved.endsWith('project/data.js')) {
doDeclaration('data', value); doDeclaration('data', value);
} }
} catch (e) { } catch (e) {
@ -517,7 +516,7 @@ async function doDeclaration(type: string, data: string) {
for (const id in eventData.commonEvent) { for (const id in eventData.commonEvent) {
eventDec += ` | '${id}'\n`; eventDec += ` | '${id}'\n`;
} }
await writeFile('src/types/source/events.d.ts', eventDec, 'utf-8'); await writeFile('src/source/events.d.ts', eventDec, 'utf-8');
} else if (type === 'items') { } else if (type === 'items') {
// 道具 // 道具
const itemData = JSON.parse(data.split('\n').slice(1).join('')); const itemData = JSON.parse(data.split('\n').slice(1).join(''));
@ -527,7 +526,7 @@ async function doDeclaration(type: string, data: string) {
itemDec += ` ${id}: '${itemData[id].cls}';\n`; itemDec += ` ${id}: '${itemData[id].cls}';\n`;
} }
itemDec += '}'; itemDec += '}';
await writeFile('src/types/source/items.d.ts', itemDec, 'utf-8'); await writeFile('src/source/items.d.ts', itemDec, 'utf-8');
} else if (type === 'maps') { } else if (type === 'maps') {
// 映射 // 映射
const d = JSON.parse(data.split('\n').slice(1).join('')); const d = JSON.parse(data.split('\n').slice(1).join(''));
@ -544,7 +543,7 @@ async function doDeclaration(type: string, data: string) {
id2cls += '}'; id2cls += '}';
id2num += '}'; id2num += '}';
num2id += '}'; num2id += '}';
await writeFile('src/types/source/cls.d.ts', id2cls, 'utf-8'); await writeFile('src/source/cls.d.ts', id2cls, 'utf-8');
await writeFile( await writeFile(
'src/source/maps.d.ts', 'src/source/maps.d.ts',
`${id2num}\n${num2id}`, `${id2num}\n${num2id}`,
@ -574,7 +573,7 @@ async function doDeclaration(type: string, data: string) {
names += '}'; names += '}';
await writeFile( await writeFile(
'src/types/source/data.d.ts', 'src/source/data.d.ts',
` `
${floorId} ${floorId}
${d.images.length > 0 ? imgs : 'type ImageIds = never\n'} ${d.images.length > 0 ? imgs : 'type ImageIds = never\n'}

View File

@ -258,7 +258,6 @@ interface AnimateFrame {
leftLeg: boolean; leftLeg: boolean;
/** /**
* @deprecated
* *
*/ */
readonly weather: Weather; readonly weather: Weather;

View File

@ -1,4 +1,3 @@
type FloorIds = type FloorIds =
| 'empty' | 'empty'
| 'MT0' | 'MT0'
@ -106,7 +105,7 @@ type FloorIds =
| 'MT94' | 'MT94'
| 'MT95' | 'MT95'
| 'MT96' | 'MT96'
| 'MT97' | 'MT97';
type ImageIds = type ImageIds =
| 'IQ.png' | 'IQ.png'
@ -115,7 +114,6 @@ type ImageIds =
| 'bg.webp' | 'bg.webp'
| 'boom.png' | 'boom.png'
| 'botton.png' | 'botton.png'
| 'cloud.png'
| 'def.png' | 'def.png'
| 'exp.png' | 'exp.png'
| 'hero1.png' | 'hero1.png'
@ -141,7 +139,7 @@ type ImageIds =
| 'tower7.webp' | 'tower7.webp'
| 'winskin.png' | 'winskin.png'
| 'winskin2.png' | 'winskin2.png'
| 'winskin3.png' | 'winskin3.png';
type AnimationIds = type AnimationIds =
| 'amazed' | 'amazed'
@ -165,7 +163,7 @@ type AnimationIds =
| 'sweat' | 'sweat'
| 'sweat2' | 'sweat2'
| 'sword' | 'sword'
| 'zone' | 'zone';
type SoundIds = type SoundIds =
| '008-System08.opus' | '008-System08.opus'
@ -207,7 +205,7 @@ type SoundIds =
| 'shop.opus' | 'shop.opus'
| 'thunder.opus' | 'thunder.opus'
| 'tree.opus' | 'tree.opus'
| 'zone.opus' | 'zone.opus';
type BgmIds = type BgmIds =
| 'beforeBoss.opus' | 'beforeBoss.opus'
@ -230,35 +228,33 @@ type BgmIds =
| 'towerBoss2.opus' | 'towerBoss2.opus'
| 'towerBoss3.opus' | 'towerBoss3.opus'
| 'winter.opus' | 'winter.opus'
| 'winterTown.opus' | 'winterTown.opus';
type FontIds = type FontIds = 'normal' | 'FiraCode';
| 'normal'
| 'FiraCode'
interface NameMap { interface NameMap {
'确定': 'confirm.opus'; : 'confirm.opus';
'取消': 'cancel.opus'; : 'cancel.opus';
'操作失败': 'error.opus'; : 'error.opus';
'光标移动': 'cursor.opus'; : 'cursor.opus';
'打开界面': 'open_ui.opus'; : 'open_ui.opus';
'读档': 'load.opus'; : 'load.opus';
'存档': 'save.opus'; : 'save.opus';
'获得道具': 'item.opus'; : 'item.opus';
'回血': 'recovery.opus'; : 'recovery.opus';
'炸弹': 'bomb.opus'; : 'bomb.opus';
'飞行器': 'centerFly.opus'; : 'centerFly.opus';
'开关门': 'door.opus'; : 'door.opus';
'上下楼': 'floor.opus'; : 'floor.opus';
'跳跃': 'jump.opus'; : 'jump.opus';
'破墙镐': 'pickaxe.opus'; : 'pickaxe.opus';
'破冰镐': 'icePickaxe.opus'; : 'icePickaxe.opus';
'宝石': 'gem.opus'; : 'gem.opus';
'阻激夹域': 'zone.opus'; : 'zone.opus';
'穿脱装备': 'equip.opus'; 穿: 'equip.opus';
'背景音乐': 'bgm.opus'; : 'bgm.opus';
'攻击': 'attack.opus'; : 'attack.opus';
'背景图': 'bg.jpg'; : 'bg.jpg';
'商店': 'shop.opus'; : 'shop.opus';
'领域': 'zone'; : 'zone';
} }