mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 20:32:58 +08:00 
			
		
		
		
	feat: 浏览地图
This commit is contained in:
		
							parent
							
								
									398de70fee
								
							
						
					
					
						commit
						574d765c69
					
				| @ -434,36 +434,85 @@ gameKey | |||||||
|         defaults: KeyCode.PageUp |         defaults: KeyCode.PageUp | ||||||
|     }) |     }) | ||||||
|     // #region 存档界面
 |     // #region 存档界面
 | ||||||
|     .group('@ui_save', 'save') |     .group('@ui_save', '存档界面') | ||||||
|     .register({ |     .register({ | ||||||
|         id: '@save_pageUp', |         id: '@save_pageUp', | ||||||
|         name: '存档向后翻页', |         name: '向后翻页', | ||||||
|         defaults: KeyCode.PageUp |         defaults: KeyCode.PageUp | ||||||
|     }) |     }) | ||||||
|     .register({ |     .register({ | ||||||
|         id: '@save_pageDown', |         id: '@save_pageDown', | ||||||
|         name: '存档向前翻页', |         name: '向前翻页', | ||||||
|         defaults: KeyCode.PageDown |         defaults: KeyCode.PageDown | ||||||
|     }) |     }) | ||||||
|     .register({ |     .register({ | ||||||
|         id: '@save_up', |         id: '@save_up', | ||||||
|         name: '存档选择框向上', |         name: '选择框向上', | ||||||
|         defaults: KeyCode.UpArrow |         defaults: KeyCode.UpArrow | ||||||
|     }) |     }) | ||||||
|     .register({ |     .register({ | ||||||
|         id: '@save_down', |         id: '@save_down', | ||||||
|         name: '存档选择框向下', |         name: '选择框向下', | ||||||
|         defaults: KeyCode.DownArrow |         defaults: KeyCode.DownArrow | ||||||
|     }) |     }) | ||||||
|     .register({ |     .register({ | ||||||
|         id: '@save_left', |         id: '@save_left', | ||||||
|         name: '存档选择框向左', |         name: '选择框向左', | ||||||
|         defaults: KeyCode.LeftArrow |         defaults: KeyCode.LeftArrow | ||||||
|     }) |     }) | ||||||
|     .register({ |     .register({ | ||||||
|         id: '@save_right', |         id: '@save_right', | ||||||
|         name: '存档选择框向右', |         name: '选择框向右', | ||||||
|         defaults: KeyCode.RightArrow |         defaults: KeyCode.RightArrow | ||||||
|  |     }) | ||||||
|  |     //#region 浏览地图
 | ||||||
|  |     .group('@ui_viewMap', '浏览地图') | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_up_1', | ||||||
|  |         name: '下一层地图_1', | ||||||
|  |         defaults: KeyCode.UpArrow | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_up_2', | ||||||
|  |         name: '下一层地图_2', | ||||||
|  |         defaults: KeyCode.PageUp | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_down_1', | ||||||
|  |         name: '上一层地图_1', | ||||||
|  |         defaults: KeyCode.DownArrow | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_down_2', | ||||||
|  |         name: '上一层地图_2', | ||||||
|  |         defaults: KeyCode.PageDown | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_up_ten', | ||||||
|  |         name: '下十层地图', | ||||||
|  |         defaults: KeyCode.UpArrow, | ||||||
|  |         ctrl: true | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_down_ten', | ||||||
|  |         name: '上十层地图', | ||||||
|  |         defaults: KeyCode.DownArrow, | ||||||
|  |         ctrl: true | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_book', | ||||||
|  |         name: '怪物手册', | ||||||
|  |         defaults: KeyCode.KeyX | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_fly', | ||||||
|  |         name: '传送至', | ||||||
|  |         defaults: KeyCode.KeyG | ||||||
|  |     }) | ||||||
|  |     .register({ | ||||||
|  |         id: '@viewMap_reset', | ||||||
|  |         name: '重置视角', | ||||||
|  |         defaults: KeyCode.KeyR | ||||||
|     }); |     }); | ||||||
| // #endregion
 | // #endregion
 | ||||||
| 
 | 
 | ||||||
| @ -492,9 +541,6 @@ gameKey | |||||||
|     .realize('shop', () => { |     .realize('shop', () => { | ||||||
|         core.openQuickShop(true); |         core.openQuickShop(true); | ||||||
|     }) |     }) | ||||||
|     .realize('viewMap', () => { |  | ||||||
|         core.ui._drawViewMaps(); |  | ||||||
|     }) |  | ||||||
|     .realize('skillTree', () => { |     .realize('skillTree', () => { | ||||||
|         core.useItem('skill1', true); |         core.useItem('skill1', true); | ||||||
|     }) |     }) | ||||||
|  | |||||||
| @ -6,7 +6,8 @@ import { | |||||||
|     openStatistics, |     openStatistics, | ||||||
|     saveLoad, |     saveLoad, | ||||||
|     openSettings, |     openSettings, | ||||||
|     ReplaySettingsUI |     ReplaySettingsUI, | ||||||
|  |     openViewMap | ||||||
| } from './ui'; | } from './ui'; | ||||||
| 
 | 
 | ||||||
| export function createAction() { | export function createAction() { | ||||||
| @ -27,5 +28,8 @@ export function createAction() { | |||||||
|             mainUIController.open(ReplaySettingsUI, { |             mainUIController.open(ReplaySettingsUI, { | ||||||
|                 loc: [420, 240, void 0, void 0, 0.5, 0.5] |                 loc: [420, 240, void 0, void 0, 0.5, 0.5] | ||||||
|             }); |             }); | ||||||
|  |         }) | ||||||
|  |         .realize('viewMap', () => { | ||||||
|  |             openViewMap(mainUIController, [0, 0, MAIN_WIDTH, MAIN_HEIGHT]); | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -37,6 +37,8 @@ export class BlockCacher< | |||||||
|     width: number; |     width: number; | ||||||
|     /** 区域高度 */ |     /** 区域高度 */ | ||||||
|     height: number; |     height: number; | ||||||
|  |     /** 区域面积 */ | ||||||
|  |     area: number = 0; | ||||||
|     /** 分块大小 */ |     /** 分块大小 */ | ||||||
|     blockSize: number; |     blockSize: number; | ||||||
|     /** 分块信息 */ |     /** 分块信息 */ | ||||||
| @ -116,6 +118,7 @@ export class BlockCacher< | |||||||
|             restWidth: this.width % this.blockSize, |             restWidth: this.width % this.blockSize, | ||||||
|             restHeight: this.height % this.blockSize |             restHeight: this.height % this.blockSize | ||||||
|         }; |         }; | ||||||
|  |         this.area = this.blockData.width * this.blockData.height; | ||||||
|         this.emit('split'); |         this.emit('split'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -303,7 +306,10 @@ export interface ICanvasCacheItem extends IBlockCacheable { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class CanvasCacheItem implements ICanvasCacheItem { | export class CanvasCacheItem implements ICanvasCacheItem { | ||||||
|     constructor(public canvas: MotaOffscreenCanvas2D, public symbol: number) {} |     constructor( | ||||||
|  |         public canvas: MotaOffscreenCanvas2D, | ||||||
|  |         public symbol: number | ||||||
|  |     ) {} | ||||||
| 
 | 
 | ||||||
|     destroy(): void {} |     destroy(): void {} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1132,6 +1132,7 @@ export class Layer extends Container<ELayerEvent> { | |||||||
|             const length = this.backImage.length; |             const length = this.backImage.length; | ||||||
|             const img = this.backImage[frame % length]; |             const img = this.backImage[frame % length]; | ||||||
|             need.forEach(index => { |             need.forEach(index => { | ||||||
|  |                 if (index >= this.block.area || index < 0) return; | ||||||
|                 const x = index % width; |                 const x = index % width; | ||||||
|                 const y = Math.floor(index / width); |                 const y = Math.floor(index / width); | ||||||
|                 const sx = x * blockSize; |                 const sx = x * blockSize; | ||||||
|  | |||||||
| @ -11,3 +11,4 @@ export * from './settings'; | |||||||
| export * from './statistics'; | export * from './statistics'; | ||||||
| export * from './statusBar'; | export * from './statusBar'; | ||||||
| export * from './toolbar'; | export * from './toolbar'; | ||||||
|  | export * from './viewmap'; | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ import { | |||||||
|     ReplayingToolbar |     ReplayingToolbar | ||||||
| } from './toolbar'; | } from './toolbar'; | ||||||
| import { HeroSkill } from '@user/data-state'; | import { HeroSkill } from '@user/data-state'; | ||||||
|  | import { openViewMap } from './viewmap'; | ||||||
|  | import { mainUIController } from './controller'; | ||||||
| 
 | 
 | ||||||
| export interface ILeftHeroStatus { | export interface ILeftHeroStatus { | ||||||
|     hp: number; |     hp: number; | ||||||
| @ -98,6 +100,10 @@ export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>( | |||||||
|             return [per * (n + 1), keyY, void 0, void 0, 0.5, 0.5]; |             return [per * (n + 1), keyY, void 0, void 0, 0.5, 0.5]; | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  |         const viewMap = () => { | ||||||
|  |             openViewMap(mainUIController, [0, 0, 840, 480]); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         return () => { |         return () => { | ||||||
|             return ( |             return ( | ||||||
|                 <container loc={p.loc} hidden={p.hidden}> |                 <container loc={p.loc} hidden={p.hidden}> | ||||||
| @ -106,6 +112,7 @@ export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>( | |||||||
|                         loc={central(24)} |                         loc={central(24)} | ||||||
|                         font={font1} |                         font={font1} | ||||||
|                         cursor="pointer" |                         cursor="pointer" | ||||||
|  |                         onClick={viewMap} | ||||||
|                     ></text> |                     ></text> | ||||||
|                     <text text={s.lv} loc={central(54)} font={font1}></text> |                     <text text={s.lv} loc={central(54)} font={font1}></text> | ||||||
|                     <image image={hpIcon} loc={iconLoc(0)}></image> |                     <image image={hpIcon} loc={iconLoc(0)}></image> | ||||||
|  | |||||||
| @ -332,8 +332,8 @@ export const GameTitle = defineComponent<GameTitleProps>(props => { | |||||||
| 
 | 
 | ||||||
|     const createMaskGradient = (ctx: CanvasRenderingContext2D) => { |     const createMaskGradient = (ctx: CanvasRenderingContext2D) => { | ||||||
|         maskGradient = ctx.createLinearGradient(100, 100, 200, 0); |         maskGradient = ctx.createLinearGradient(100, 100, 200, 0); | ||||||
|         maskGradient.addColorStop(0, '#fff'); |         maskGradient.addColorStop(0, 'rgba(255,255,255,0)'); | ||||||
|         maskGradient.addColorStop(1, '#000'); |         maskGradient.addColorStop(1, 'rgba(0,0,0,1)'); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const createTitleGradient = (ctx: CanvasRenderingContext2D) => { |     const createTitleGradient = (ctx: CanvasRenderingContext2D) => { | ||||||
|  | |||||||
							
								
								
									
										503
									
								
								packages-user/client-modules/src/render/ui/viewmap.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										503
									
								
								packages-user/client-modules/src/render/ui/viewmap.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,503 @@ | |||||||
|  | import { | ||||||
|  |     ElementLocator, | ||||||
|  |     IActionEvent, | ||||||
|  |     IActionEventBase, | ||||||
|  |     IWheelEvent, | ||||||
|  |     MotaOffscreenCanvas2D | ||||||
|  | } from '@motajs/render-core'; | ||||||
|  | import { BaseProps } from '@motajs/render-vue'; | ||||||
|  | import { | ||||||
|  |     GameUI, | ||||||
|  |     IUIMountable, | ||||||
|  |     SetupComponentOptions, | ||||||
|  |     UIComponentProps | ||||||
|  | } from '@motajs/system-ui'; | ||||||
|  | import { | ||||||
|  |     computed, | ||||||
|  |     defineComponent, | ||||||
|  |     markRaw, | ||||||
|  |     onMounted, | ||||||
|  |     onUnmounted, | ||||||
|  |     ref, | ||||||
|  |     shallowRef, | ||||||
|  |     watch | ||||||
|  | } from 'vue'; | ||||||
|  | import { FloorSelector } from '../components/floorSelect'; | ||||||
|  | import { | ||||||
|  |     ILayerGroupRenderExtends, | ||||||
|  |     FloorDamageExtends, | ||||||
|  |     FloorItemDetail, | ||||||
|  |     LayerGroupAnimate, | ||||||
|  |     LayerGroup, | ||||||
|  |     LayerGroupFloorBinder | ||||||
|  | } from '../elements'; | ||||||
|  | import { LayerGroupHalo } from '../legacy/halo'; | ||||||
|  | import { LayerGroupPortal } from '../legacy/portal'; | ||||||
|  | import { Font } from '@motajs/render-style'; | ||||||
|  | import { clamp, mean } from 'lodash-es'; | ||||||
|  | import { calculateStatisticsOne, StatisticsDataOneFloor } from './statistics'; | ||||||
|  | import { Tip, TipExpose } from '../components'; | ||||||
|  | import { useKey } from '../use'; | ||||||
|  | 
 | ||||||
|  | export interface ViewMapProps extends UIComponentProps, BaseProps { | ||||||
|  |     loc: ElementLocator; | ||||||
|  |     floorId?: FloorIds; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const viewMapProps = { | ||||||
|  |     props: ['loc', 'floorId', 'controller', 'instance'] | ||||||
|  | } satisfies SetupComponentOptions<ViewMapProps>; | ||||||
|  | 
 | ||||||
|  | export const ViewMap = defineComponent<ViewMapProps>(props => { | ||||||
|  |     const nowFloorId = core.status.floorId; | ||||||
|  | 
 | ||||||
|  |     const layerGroupExtends: ILayerGroupRenderExtends[] = [ | ||||||
|  |         new FloorDamageExtends(), | ||||||
|  |         new FloorItemDetail(), | ||||||
|  |         new LayerGroupPortal(), | ||||||
|  |         new LayerGroupHalo(), | ||||||
|  |         new LayerGroupAnimate() | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     const rightFont = new Font(Font.defaultFamily, 15); | ||||||
|  | 
 | ||||||
|  |     const viewableFloor = markRaw( | ||||||
|  |         core.floorIds.filter(v => { | ||||||
|  |             return ( | ||||||
|  |                 !core.floors[v].cannotViewMap && | ||||||
|  |                 !core.status?.hero?.flags?.__removed__?.includes(v) | ||||||
|  |             ); | ||||||
|  |         }) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     const group = ref<LayerGroup>(); | ||||||
|  |     const tip = ref<TipExpose>(); | ||||||
|  |     const statistics = shallowRef<StatisticsDataOneFloor>(); | ||||||
|  | 
 | ||||||
|  |     const now = ref(0); | ||||||
|  |     if (props.floorId) { | ||||||
|  |         const index = viewableFloor.indexOf(props.floorId); | ||||||
|  |         if (index !== -1) now.value = index; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const floorId = computed(() => viewableFloor[now.value]); | ||||||
|  | 
 | ||||||
|  |     //#region 按键实现
 | ||||||
|  | 
 | ||||||
|  |     const [key] = useKey(); | ||||||
|  |     key.realize('@viewMap_up', () => changeFloor(1), { type: 'down-repeat' }) | ||||||
|  |         .realize('@viewMap_down', () => changeFloor(-1), { | ||||||
|  |             type: 'down-repeat' | ||||||
|  |         }) | ||||||
|  |         .realize('@viewMap_up_ten', () => changeFloor(10)) | ||||||
|  |         .realize('@viewMap_down_ten', () => changeFloor(-10)) | ||||||
|  |         .realize('@viewMap_book', () => openBook()) | ||||||
|  |         .realize('@viewMap_fly', () => fly()) | ||||||
|  |         .realize('@viewMap_reset', () => resetCamera()) | ||||||
|  |         .realize('confirm', () => close()); | ||||||
|  | 
 | ||||||
|  |     //#region 功能函数
 | ||||||
|  | 
 | ||||||
|  |     const close = () => { | ||||||
|  |         props.controller.close(props.instance); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const format = (num?: number) => { | ||||||
|  |         return core.formatBigNumber(num ?? 0, 6); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const changeTo = (index: number) => { | ||||||
|  |         const res = clamp(index, 0, viewableFloor.length - 1); | ||||||
|  |         now.value = res; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const changeFloor = (delta: number) => { | ||||||
|  |         changeTo(now.value + delta); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const openBook = () => core.openBook(true); | ||||||
|  | 
 | ||||||
|  |     const fly = () => { | ||||||
|  |         const id = viewableFloor[now.value]; | ||||||
|  |         const success = core.flyTo(id); | ||||||
|  |         if (success) close(); | ||||||
|  |         else tip.value?.drawTip(`无法飞往${core.floors[id].title}`); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const resetCamera = () => { | ||||||
|  |         group.value?.camera.reset(); | ||||||
|  |         group.value?.update(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     //#region 渐变渲染
 | ||||||
|  | 
 | ||||||
|  |     const topAlpha = ref(0.7); | ||||||
|  |     const bottomAlpha = ref(0.7); | ||||||
|  | 
 | ||||||
|  |     let topGradient: CanvasGradient | null = null; | ||||||
|  |     let bottomGradient: CanvasGradient | null = null; | ||||||
|  | 
 | ||||||
|  |     const getTopGradient = (ctx: CanvasRenderingContext2D) => { | ||||||
|  |         if (topGradient) return topGradient; | ||||||
|  |         topGradient = ctx.createLinearGradient(0, 0, 0, 64); | ||||||
|  |         topGradient.addColorStop(0, 'rgba(0,0,0,1)'); | ||||||
|  |         topGradient.addColorStop(0.75, 'rgba(0,0,0,0.5)'); | ||||||
|  |         topGradient.addColorStop(1, 'rgba(0,0,0,0)'); | ||||||
|  |         return topGradient; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const getBottomGradient = (ctx: CanvasRenderingContext2D) => { | ||||||
|  |         if (bottomGradient) return bottomGradient; | ||||||
|  |         bottomGradient = ctx.createLinearGradient(0, 64, 0, 0); | ||||||
|  |         bottomGradient.addColorStop(0, 'rgba(0,0,0,1)'); | ||||||
|  |         bottomGradient.addColorStop(0.75, 'rgba(0,0,0,0.5)'); | ||||||
|  |         bottomGradient.addColorStop(1, 'rgba(0,0,0,0)'); | ||||||
|  |         return bottomGradient; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const renderTop = (canvas: MotaOffscreenCanvas2D) => { | ||||||
|  |         const ctx = canvas.ctx; | ||||||
|  |         ctx.fillStyle = getTopGradient(ctx); | ||||||
|  |         ctx.fillRect(0, 0, 480, 64); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const renderBottom = (canvas: MotaOffscreenCanvas2D) => { | ||||||
|  |         const ctx = canvas.ctx; | ||||||
|  |         ctx.fillStyle = getBottomGradient(ctx); | ||||||
|  |         ctx.fillRect(0, 0, 480, 64); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const enterTop = () => (topAlpha.value = 0.9); | ||||||
|  |     const enterBottom = () => (bottomAlpha.value = 0.9); | ||||||
|  |     const leaveTop = () => (topAlpha.value = 0.7); | ||||||
|  |     const leaveBottom = () => (bottomAlpha.value = 0.7); | ||||||
|  | 
 | ||||||
|  |     //#region 地图渲染
 | ||||||
|  | 
 | ||||||
|  |     const renderLayer = (floorId: FloorIds) => { | ||||||
|  |         const binder = group.value?.getExtends( | ||||||
|  |             'floor-binder' | ||||||
|  |         ) as LayerGroupFloorBinder; | ||||||
|  |         binder.bindFloor(floorId); | ||||||
|  |         group.value?.camera.reset(); | ||||||
|  |         core.status.floorId = floorId; | ||||||
|  |         core.status.thisMap = core.status.maps[floorId]; | ||||||
|  |         statistics.value = calculateStatisticsOne(floorId); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const moveCamera = (dx: number, dy: number) => { | ||||||
|  |         const camera = group.value?.camera; | ||||||
|  |         if (!camera) return; | ||||||
|  |         camera.translate(dx / camera.scaleX, dy / camera.scaleX); | ||||||
|  |         group.value?.update(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const scaleCamera = (scale: number, x: number, y: number) => { | ||||||
|  |         const camera = group.value?.camera; | ||||||
|  |         if (!camera) return; | ||||||
|  |         const [cx, cy] = camera.untransformed(x, y); | ||||||
|  |         camera.translate(cx, cy); | ||||||
|  |         camera.scale(scale); | ||||||
|  |         camera.translate(-cx, -cy); | ||||||
|  |         group.value?.update(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     //#region 地图交互
 | ||||||
|  | 
 | ||||||
|  |     let mouseDown = false; | ||||||
|  |     let moved = false; | ||||||
|  |     let scaled = false; | ||||||
|  |     let lastMoveX = 0; | ||||||
|  |     let lastMoveY = 0; | ||||||
|  |     let lastDis = 0; | ||||||
|  |     let movement = 0; | ||||||
|  | 
 | ||||||
|  |     const touches = new Map<number, IActionEvent>(); | ||||||
|  | 
 | ||||||
|  |     const downMap = (ev: IActionEvent) => { | ||||||
|  |         moved = false; | ||||||
|  |         lastMoveX = ev.offsetX; | ||||||
|  |         lastMoveY = ev.offsetY; | ||||||
|  |         movement = 0; | ||||||
|  | 
 | ||||||
|  |         if (ev.touch) { | ||||||
|  |             touches.set(ev.identifier, ev); | ||||||
|  |             if (touches.size >= 2) { | ||||||
|  |                 const [touch1, touch2] = touches.values(); | ||||||
|  |                 lastDis = Math.hypot( | ||||||
|  |                     touch1.offsetX - touch2.offsetX, | ||||||
|  |                     touch1.offsetY - touch2.offsetY | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             mouseDown = true; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const upMap = (ev: IActionEvent) => { | ||||||
|  |         if (ev.touch) { | ||||||
|  |             touches.delete(ev.identifier); | ||||||
|  |         } else { | ||||||
|  |             mouseDown = false; | ||||||
|  |         } | ||||||
|  |         if (touches.size === 0) { | ||||||
|  |             scaled = false; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const move = (ev: IActionEvent) => { | ||||||
|  |         if (moved) { | ||||||
|  |             const dx = ev.offsetX - lastMoveX; | ||||||
|  |             const dy = ev.offsetY - lastMoveY; | ||||||
|  |             movement += Math.hypot(dx, dy); | ||||||
|  |             moveCamera(dx, dy); | ||||||
|  |         } | ||||||
|  |         moved = true; | ||||||
|  |         lastMoveX = ev.offsetX; | ||||||
|  |         lastMoveY = ev.offsetY; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const moveMap = (ev: IActionEvent) => { | ||||||
|  |         if (ev.touch) { | ||||||
|  |             if (touches.size === 0) return; | ||||||
|  |             else if (touches.size === 1) { | ||||||
|  |                 // 移动
 | ||||||
|  |                 if (scaled) return; | ||||||
|  |                 move(ev); | ||||||
|  |             } else { | ||||||
|  |                 // 缩放
 | ||||||
|  |                 const [touch1, touch2] = touches.values(); | ||||||
|  |                 const cx = mean([touch1.offsetX, touch2.offsetX]); | ||||||
|  |                 const cy = mean([touch1.offsetY, touch2.offsetY]); | ||||||
|  |                 const dis = Math.hypot( | ||||||
|  |                     touch1.offsetX - touch2.offsetX, | ||||||
|  |                     touch1.offsetY - touch2.offsetY | ||||||
|  |                 ); | ||||||
|  |                 const scale = dis / lastDis; | ||||||
|  |                 if (!scaled) { | ||||||
|  |                     lastDis = dis; | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 if (!isFinite(scale) || scale === 0) return; | ||||||
|  |                 scaleCamera(scale, cx, cy); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if (mouseDown) { | ||||||
|  |                 move(ev); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const leaveMap = (ev: IActionEventBase) => { | ||||||
|  |         if (ev.touch) { | ||||||
|  |             touches.delete(ev.identifier); | ||||||
|  |         } else { | ||||||
|  |             mouseDown = false; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const wheelMap = (ev: IWheelEvent) => { | ||||||
|  |         if (ev.altKey) { | ||||||
|  |             const scale = ev.wheelY < 0 ? 1.1 : 0.9; | ||||||
|  |             scaleCamera(scale, ev.offsetX, ev.offsetY); | ||||||
|  |         } else if (ev.ctrlKey) { | ||||||
|  |             changeFloor(-Math.sign(ev.wheelY) * 10); | ||||||
|  |         } else { | ||||||
|  |             changeFloor(-Math.sign(ev.wheelY)); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const clickMap = (ev: IActionEvent) => { | ||||||
|  |         if (movement > 5) return; | ||||||
|  |         if (ev.touch) { | ||||||
|  |             if (touches.size === 0) { | ||||||
|  |                 close(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             close(); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     onMounted(() => { | ||||||
|  |         renderLayer(floorId.value); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     onUnmounted(() => { | ||||||
|  |         core.status.floorId = nowFloorId; | ||||||
|  |         core.status.thisMap = core.status.maps[nowFloorId]; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     watch(floorId, value => { | ||||||
|  |         renderLayer(value); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     //#region 组件树
 | ||||||
|  | 
 | ||||||
|  |     return () => ( | ||||||
|  |         <container loc={props.loc} nocache> | ||||||
|  |             <g-rect fillStyle="black" fill loc={[0, 0, 840, 480]} /> | ||||||
|  |             <g-rect stroke zIndex={100} loc={[0, 0, 840, 480]} noevent /> | ||||||
|  |             <g-line line={[180, 0, 180, 480]} lineWidth={1} /> | ||||||
|  |             <g-line line={[180 + 480, 0, 180 + 480, 480]} lineWidth={1} /> | ||||||
|  |             <FloorSelector | ||||||
|  |                 loc={[0, 0, 180, 480]} | ||||||
|  |                 floors={viewableFloor} | ||||||
|  |                 v-model:now={now.value} | ||||||
|  |                 onClose={close} | ||||||
|  |             /> | ||||||
|  |             <layer-group | ||||||
|  |                 ref={group} | ||||||
|  |                 ex={layerGroupExtends} | ||||||
|  |                 loc={[180, 0, 480, 480]} | ||||||
|  |                 onDown={downMap} | ||||||
|  |                 onMove={moveMap} | ||||||
|  |                 onUp={upMap} | ||||||
|  |                 onLeave={leaveMap} | ||||||
|  |                 onWheel={wheelMap} | ||||||
|  |                 onClick={clickMap} | ||||||
|  |             > | ||||||
|  |                 <layer layer="bg" zIndex={10}></layer> | ||||||
|  |                 <layer layer="bg2" zIndex={20}></layer> | ||||||
|  |                 <layer layer="event" zIndex={30}></layer> | ||||||
|  |                 <layer layer="fg" zIndex={40}></layer> | ||||||
|  |                 <layer layer="fg2" zIndex={50}></layer> | ||||||
|  |             </layer-group> | ||||||
|  |             <Tip | ||||||
|  |                 ref={tip} | ||||||
|  |                 zIndex={40} | ||||||
|  |                 loc={[188, 8, 200, 32]} | ||||||
|  |                 pad={[12, 6]} | ||||||
|  |                 corner={16} | ||||||
|  |             /> | ||||||
|  |             <sprite | ||||||
|  |                 loc={[180, 0, 480, 64]} | ||||||
|  |                 render={renderTop} | ||||||
|  |                 alpha={topAlpha.value} | ||||||
|  |                 zIndex={10} | ||||||
|  |                 cursor="pointer" | ||||||
|  |                 onEnter={enterTop} | ||||||
|  |                 onLeave={leaveTop} | ||||||
|  |                 onClick={() => changeFloor(1)} | ||||||
|  |             /> | ||||||
|  |             <sprite | ||||||
|  |                 loc={[180, 416, 480, 64]} | ||||||
|  |                 render={renderBottom} | ||||||
|  |                 alpha={bottomAlpha.value} | ||||||
|  |                 zIndex={10} | ||||||
|  |                 cursor="pointer" | ||||||
|  |                 onEnter={enterBottom} | ||||||
|  |                 onLeave={leaveBottom} | ||||||
|  |                 onClick={() => changeFloor(-1)} | ||||||
|  |             /> | ||||||
|  |             <text | ||||||
|  |                 text="上移地图" | ||||||
|  |                 loc={[420, 24]} | ||||||
|  |                 anc={[0.5, 0.5]} | ||||||
|  |                 zIndex={20} | ||||||
|  |                 noevent | ||||||
|  |             /> | ||||||
|  |             <text | ||||||
|  |                 text="下移地图" | ||||||
|  |                 loc={[420, 456]} | ||||||
|  |                 anc={[0.5, 0.5]} | ||||||
|  |                 zIndex={20} | ||||||
|  |                 noevent | ||||||
|  |             /> | ||||||
|  |             <container loc={[660, 0, 180, 480]}> | ||||||
|  |                 <text | ||||||
|  |                     text="鼠标 / 单指拖动地图" | ||||||
|  |                     font={rightFont} | ||||||
|  |                     loc={[90, 24]} | ||||||
|  |                     anc={[0.5, 0.5]} | ||||||
|  |                     fillStyle="yellow" | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text="Alt+滚轮 / 双指缩放地图" | ||||||
|  |                     font={rightFont} | ||||||
|  |                     loc={[90, 48]} | ||||||
|  |                     anc={[0.5, 0.5]} | ||||||
|  |                     fillStyle="yellow" | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text="Ctrl+滚轮 / 滚轮切换地图" | ||||||
|  |                     font={rightFont} | ||||||
|  |                     loc={[90, 72]} | ||||||
|  |                     anc={[0.5, 0.5]} | ||||||
|  |                     fillStyle="yellow" | ||||||
|  |                 /> | ||||||
|  |                 <g-line line={[12, 96, 168, 96]} lineWidth={1} /> | ||||||
|  |                 <text | ||||||
|  |                     text={`怪物数量:${statistics.value?.enemyCount}`} | ||||||
|  |                     loc={[20, 120]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text={`血瓶数量:${statistics.value?.potionCount}`} | ||||||
|  |                     loc={[20, 144]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text={`宝石数量:${statistics.value?.gemCount}`} | ||||||
|  |                     loc={[20, 168]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text={`血瓶数值:${format(statistics.value?.potionValue)}`} | ||||||
|  |                     loc={[20, 192]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text={`攻击数值:${format(statistics.value?.atkValue)}`} | ||||||
|  |                     loc={[20, 216]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text={`防御数值:${format(statistics.value?.defValue)}`} | ||||||
|  |                     loc={[20, 240]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text={`智慧数值:${format(statistics.value?.mdefValue)}`} | ||||||
|  |                     loc={[20, 264]} | ||||||
|  |                     anc={[0, 0.5]} | ||||||
|  |                 /> | ||||||
|  |                 <g-line line={[12, 292, 168, 292]} lineWidth={1} /> | ||||||
|  |                 <text | ||||||
|  |                     text="「 怪物手册 」" | ||||||
|  |                     loc={[90, 330]} | ||||||
|  |                     anc={[0.5, 0.5]} | ||||||
|  |                     cursor="pointer" | ||||||
|  |                     onClick={openBook} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text="「 传送至此 」" | ||||||
|  |                     loc={[90, 380]} | ||||||
|  |                     anc={[0.5, 0.5]} | ||||||
|  |                     cursor="pointer" | ||||||
|  |                     onClick={fly} | ||||||
|  |                 /> | ||||||
|  |                 <text | ||||||
|  |                     text="「 重置视角 」" | ||||||
|  |                     loc={[90, 430]} | ||||||
|  |                     anc={[0.5, 0.5]} | ||||||
|  |                     cursor="pointer" | ||||||
|  |                     onClick={resetCamera} | ||||||
|  |                 /> | ||||||
|  |             </container> | ||||||
|  |         </container> | ||||||
|  |     ); | ||||||
|  | }, viewMapProps); | ||||||
|  | 
 | ||||||
|  | export const ViewMapUI = new GameUI('view-map', ViewMap); | ||||||
|  | 
 | ||||||
|  | export function openViewMap( | ||||||
|  |     controller: IUIMountable, | ||||||
|  |     loc: ElementLocator, | ||||||
|  |     props?: ViewMapProps | ||||||
|  | ) { | ||||||
|  |     controller.open(ViewMapUI, { | ||||||
|  |         ...props, | ||||||
|  |         loc, | ||||||
|  |         floorId: core.status.floorId | ||||||
|  |     }); | ||||||
|  | } | ||||||
| @ -101,7 +101,6 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> { | |||||||
|     /** |     /** | ||||||
|      * 复制一个离屏Canvas2D对象,一般用于缓存等操作 |      * 复制一个离屏Canvas2D对象,一般用于缓存等操作 | ||||||
|      * @param canvas 被复制的MotaOffscreenCanvas2D对象 |      * @param canvas 被复制的MotaOffscreenCanvas2D对象 | ||||||
|      * @returns 复制结果,注意复制结果是被冻结的,无法进行大小等的修改,但是可以继续绘制 |  | ||||||
|      */ |      */ | ||||||
|     static clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D { |     static clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D { | ||||||
|         const newCanvas = new MotaOffscreenCanvas2D(); |         const newCanvas = new MotaOffscreenCanvas2D(); | ||||||
|  | |||||||
| @ -140,7 +140,7 @@ export class Container<E extends EContainerEvent = EContainerEvent> | |||||||
|     destroy(): void { |     destroy(): void { | ||||||
|         super.destroy(); |         super.destroy(); | ||||||
|         this.children.forEach(v => { |         this.children.forEach(v => { | ||||||
|             v.remove(); |             v.destroy(); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -193,6 +193,8 @@ export class MotaRenderer extends Container implements IRenderTreeRoot { | |||||||
|             this.createTouchAction(ev, ActionType.Up).forEach(v => { |             this.createTouchAction(ev, ActionType.Up).forEach(v => { | ||||||
|                 this.captureEvent(ActionType.Up, v); |                 this.captureEvent(ActionType.Up, v); | ||||||
|                 this.captureEvent(ActionType.Click, v); |                 this.captureEvent(ActionType.Click, v); | ||||||
|  |             }); | ||||||
|  |             [...ev.touches].forEach(v => { | ||||||
|                 this.touchInfo.delete(v.identifier); |                 this.touchInfo.delete(v.identifier); | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
| @ -200,14 +202,18 @@ export class MotaRenderer extends Container implements IRenderTreeRoot { | |||||||
|             ev.preventDefault(); |             ev.preventDefault(); | ||||||
|             this.createTouchAction(ev, ActionType.Up).forEach(v => { |             this.createTouchAction(ev, ActionType.Up).forEach(v => { | ||||||
|                 this.captureEvent(ActionType.Up, v); |                 this.captureEvent(ActionType.Up, v); | ||||||
|  |             }); | ||||||
|  |             [...ev.touches].forEach(v => { | ||||||
|                 this.touchInfo.delete(v.identifier); |                 this.touchInfo.delete(v.identifier); | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
|         document.addEventListener('touchmove', ev => { |         document.addEventListener('touchmove', ev => { | ||||||
|             ev.preventDefault(); |             ev.preventDefault(); | ||||||
|             this.createTouchAction(ev, ActionType.Move).forEach(v => { |             this.createTouchAction(ev, ActionType.Move).forEach(v => { | ||||||
|                 const touch = this.touchInfo.get(v.identifier); |                 const list = this.touchInfo.values(); | ||||||
|                 if (!touch) return; |                 if (!list.some(vv => v.identifier === vv.identifier)) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|                 const temp = this.beforeHovered; |                 const temp = this.beforeHovered; | ||||||
|                 temp.clear(); |                 temp.clear(); | ||||||
|                 this.beforeHovered = this.hoveredElement; |                 this.beforeHovered = this.hoveredElement; | ||||||
|  | |||||||
| @ -163,7 +163,7 @@ export class Image extends RenderItem<EImageEvent> { | |||||||
|     image: CanvasImageSource; |     image: CanvasImageSource; | ||||||
| 
 | 
 | ||||||
|     constructor(image: CanvasImageSource, type: RenderItemPosition = 'static') { |     constructor(image: CanvasImageSource, type: RenderItemPosition = 'static') { | ||||||
|         super(type); |         super(type, false); | ||||||
|         this.image = image; |         this.image = image; | ||||||
|         if (image instanceof VideoFrame || image instanceof SVGElement) { |         if (image instanceof VideoFrame || image instanceof SVGElement) { | ||||||
|             this.size(200, 200); |             this.size(200, 200); | ||||||
| @ -177,7 +177,7 @@ export class Image extends RenderItem<EImageEvent> { | |||||||
|         _transform: Transform |         _transform: Transform | ||||||
|     ): void { |     ): void { | ||||||
|         const ctx = canvas.ctx; |         const ctx = canvas.ctx; | ||||||
|         ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height); |         ctx.drawImage(this.image, 0, 0, this.width, this.height); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ export const { createApp, render } = createRenderer<RenderItem, RenderItem>({ | |||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     remove: function (el: RenderItem<ERenderItemEvent>): void { |     remove: function (el: RenderItem<ERenderItemEvent>): void { | ||||||
|         el.remove(); |         el.destroy(); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     createElement: function ( |     createElement: function ( | ||||||
|  | |||||||
| @ -436,6 +436,13 @@ export const gameKey = new Hotkey('gameKey', '游戏按键'); | |||||||
| document.addEventListener('keyup', e => { | document.addEventListener('keyup', e => { | ||||||
|     const assist = generateBinary([e.ctrlKey, e.shiftKey, e.altKey]); |     const assist = generateBinary([e.ctrlKey, e.shiftKey, e.altKey]); | ||||||
|     const code = keycode(e.keyCode); |     const code = keycode(e.keyCode); | ||||||
|  |     if ( | ||||||
|  |         code === KeyCode.Alt || | ||||||
|  |         code === KeyCode.Shift || | ||||||
|  |         code === KeyCode.Ctrl | ||||||
|  |     ) { | ||||||
|  |         e.preventDefault(); | ||||||
|  |     } | ||||||
|     if (gameKey.emitKey(code, assist, 'up', e)) { |     if (gameKey.emitKey(code, assist, 'up', e)) { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         if (core.status.holdingKeys) { |         if (core.status.holdingKeys) { | ||||||
|  | |||||||
| @ -40,8 +40,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "小绿宝石", | 		"name": "小绿宝石", | ||||||
| 		"text": ",护盾+${core.values.greenGem}", | 		"text": ",护盾+${core.values.greenGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(20 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(20 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(20 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(20 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
| @ -626,8 +626,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "中绿宝石", | 		"name": "中绿宝石", | ||||||
| 		"text": ",护盾+${core.values.greenGem}", | 		"text": ",护盾+${core.values.greenGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(40 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(40 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(40 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(40 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
| @ -729,8 +729,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "大绿宝石", | 		"name": "大绿宝石", | ||||||
| 		"text": ",护盾+${core.values.greenGem}", | 		"text": ",护盾+${core.values.greenGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(80 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(80 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(80 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(80 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
| @ -896,8 +896,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "超大绿宝石", | 		"name": "超大绿宝石", | ||||||
| 		"text": ",护盾+${core.values.greenGem}", | 		"text": ",护盾+${core.values.greenGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(160 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(160 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(160 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(160 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
| @ -1019,8 +1019,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "璀璨绿宝石", | 		"name": "璀璨绿宝石", | ||||||
| 		"text": ",护盾+${core.values.greenGem}", | 		"text": ",护盾+${core.values.greenGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(320 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(320 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(320 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(320 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
| @ -1050,8 +1050,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "传奇绿宝石", | 		"name": "传奇绿宝石", | ||||||
| 		"text": ",防御+${core.values.blueGem}", | 		"text": ",防御+${core.values.blueGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(640 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(640 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(640 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(640 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.def += core.values.blueGem", | 		"useItemEffect": "core.status.hero.def += core.values.blueGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
| @ -1071,8 +1071,8 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = | |||||||
| 		"cls": "items", | 		"cls": "items", | ||||||
| 		"name": "史诗绿宝石", | 		"name": "史诗绿宝石", | ||||||
| 		"text": ",护盾+${core.values.greenGem}", | 		"text": ",护盾+${core.values.greenGem}", | ||||||
| 		"itemEffect": "core.status.hero.mdef += Math.round(1280 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | 		"itemEffect": "core.status.hero.mdef += Math.round(1280 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))", | ||||||
| 		"itemEffectTip": ",智慧+${Math.round(1280 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | 		"itemEffectTip": ",智慧+${Math.round(1280 * core.status.thisMap.ratio / (core.getFlag(\"hard\") + 1) * (Mota.require('@user/data-state').getSkillLevel(12) / 20 + 1))}", | ||||||
| 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | 		"useItemEffect": "core.status.hero.mdef += core.values.greenGem", | ||||||
| 		"canUseItemEffect": "true" | 		"canUseItemEffect": "true" | ||||||
| 	}, | 	}, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user