-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_history
More file actions
1 lines (1 loc) · 65.5 KB
/
_history
File metadata and controls
1 lines (1 loc) · 65.5 KB
1
{"entries":[{"timestamp":1762272808211,"editorVersion":"2.0.61","changes":[{"type":"edited","filename":"main.blocks","patch":[{"start1":0,"length1":137,"diffs":[[1,"<xml xmlns=\"https://developers.google.com/blockly/xml\"><variables><variable id=\"OGeBWh~@Rst8z-r(m?#7\">Camera</variable></variables><block type=\"keyonevent\" x=\"285\" y=\"-107\"><field name=\"button\">controller.menu</field><field name=\"event\">ControllerButtonEvent.Pressed</field><statement name=\"HANDLER\"><block type=\"rcRender_toggleViewMode\"></block></statement></block><block type=\"pxt-on-start\" x=\"0\" y=\"0\"><statement name=\"HANDLER\"><block type=\"rcRender_setViewAngleInDegree\"><value name=\"angle\"><shadow type=\"math_number_minmax\"><mutation min=\"0\" max=\"360\" label=\"Angle\" precision=\"0\"></mutation><field name=\"SLIDER\">90</field></shadow></value><next><block type=\"variables_set\"><field name=\"VAR\" id=\"OGeBWh~@Rst8z-r(m?#7\">Camera</field><value name=\"VALUE\"><shadow type=\"math_number\"><field name=\"NUM\">0</field></shadow><block type=\"rcRender_getRenderSpriteVariable\"></block></value><next><block type=\"set_current_tilemap\"><value name=\"tilemap\"><shadow type=\"tiles_tilemap_editor\"><field name=\"tilemap\">tilemap`level`</field><data>{\"commentRefs\":[],\"fieldData\":{\"tilemap\":\"level1\"}}</data></shadow></value></block></next></block></next></block></statement></block></xml>"]]}]},{"type":"edited","filename":"main.ts","patch":[{"start1":0,"length1":1,"diffs":[[1,"controller.menu.onEvent(ControllerButtonEvent.Pressed, function () {\n Render.toggleViewMode()\n})\nRender.setViewAngleInDegree(90)\nlet Camera = Render.getRenderSpriteVariable()\ntiles.setCurrentTilemap(tilemap`level`)\n"]]}]},{"type":"edited","filename":"pxt.json","patch":[{"start1":2,"length1":27,"diffs":[[1," \"name\": \"3d floor and background\",\n"]]},{"start1":359,"length1":44,"diffs":[[1,""]]}]},{"type":"edited","filename":"tilemap.g.jres","patch":[{"start1":718,"length1":1060,"diffs":[[1," \"data\": \"MTAxMDAwMTAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDEwMTAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMTAxMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAxMDEwMjAyMDIwMzAyMDIwMjAyMDIwMjAyMDIwMjAyMDEwMTAyMDIwMjAzMDIwMjAzMDMwMzAzMDMwMjAyMDIwMTAxMDIwMjAyMDIwMzAzMDIwMjAyMDMwMzAyMDIwMjAxMDEwMjAyMDMwMzAyMDMwMjAyMDMwMzAzMDIwMjAyMDEwMTAyMDIwMzAzMDMwMzAzMDMwMzAyMDIwMzAyMDIwMTAxMDIwMjAyMDIwMzAzMDMwMzAyMDIwMzAzMDIwMjAxMDEwMjAyMDIwMjAzMDMwMjAyMDIwMzAzMDIwMjAyMDEwMTAyMDIwMjAyMDIwMjAyMDMwMzAzMDIwMjAyMDIwMTAxMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAxMDEwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDEwMTAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMjIyMjIyMjIyMjIyMjIyMjAyMDAwMDAwMDAwMDAwMjAwMjAwMDAwMDAwMDAwMDIwMDIwMDAwMDAwMDAwMDAyMDAyMDAwMjAwMDAwMDAwMjAwMjAwMDIwMDAwMDAwMDIwMDIwMDIwMDIwMDAwMDAyMDAyMjAwMjAyMDAwMDAwMjAwMjIwMjIwMjAwMDAwMDIwMDIwMDAwMDAwMDAwMDAyMDAyMDAwMDAwMDAwMDAwMjAwMjAwMDAwMDAwMDAwMDIwMDIwMDAwMDAwMDAwMDAyMDAyMDAwMDAwMDAwMDAwMjAwMjAwMDAwMDAwMDAwMDIwMjIyMjIyMjIyMjIyMjIyMg==\",\n"]]},{"start1":1837,"length1":40,"diffs":[[1," \"sprites.castle.tileGrass2\",\n \"sprites.castle.tileDarkGrass2\",\n \"sprites.builtin.forestTiles0\"\n"]]}]},{"type":"edited","filename":"tilemap.g.ts","patch":[{"start1":405,"length1":1188,"diffs":[[1," case \"level1\":return tiles.createTilemap(hex`1000100001010101010101010101010101010101010202020202020202020202020202010102020202020202020202020202020101020202020202020202020202020201010202020302020202020202020202010102020203020203030303030202020101020202020303020202030302020201010202030302030202030303020202010102020303030303030302020302020101020202020303030302020303020201010202020203030202020303020202010102020202020202030303020202020101020202020202020202020202020201010202020202020202020202020202010102020202020202020202020202020101010101010101010101010101010101`, img`\n2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . 2 . . . . . . . . . . 2 \n2 . . . 2 . . . . . . . . . . 2 \n2 . . . . 2 2 . . . . . . . . 2 \n2 . . 2 2 . 2 . . . . . . . . 2 \n2 . . 2 2 2 2 . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 . . . . . . . . . . . . . . 2 \n2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 \n`, [myTiles.transparency16,sprites.castle.tileGrass2,sprites.castle.tileDarkGrass2,sprites.builtin.forestTiles0], TileScale.Sixteen);\n"]]}]},{"type":"added","filename":"test.ts","value":"// tests go here; this will not be compiled when this package is used as an extension.\n"}]}],"snapshots":[{"timestamp":1762272808210,"editorVersion":"2.0.61","text":{"main.blocks":"<xml xmlns=\"https://developers.google.com/blockly/xml\"><variables><variable id=\"OGeBWh~@Rst8z-r(m?#7\">Camera</variable></variables><block type=\"keyonevent\" x=\"285\" y=\"-107\"><field name=\"button\">controller.menu</field><field name=\"event\">ControllerButtonEvent.Pressed</field><statement name=\"HANDLER\"><block type=\"rcRender_toggleViewMode\"></block></statement></block><block type=\"pxt-on-start\" x=\"0\" y=\"0\"><statement name=\"HANDLER\"><block type=\"rcRender_setViewAngleInDegree\"><value name=\"angle\"><shadow type=\"math_number_minmax\"><mutation min=\"0\" max=\"360\" label=\"Angle\" precision=\"0\"></mutation><field name=\"SLIDER\">90</field></shadow></value><next><block type=\"variables_set\"><field name=\"VAR\" id=\"OGeBWh~@Rst8z-r(m?#7\">Camera</field><value name=\"VALUE\"><shadow type=\"math_number\"><field name=\"NUM\">0</field></shadow><block type=\"rcRender_getRenderSpriteVariable\"></block></value><next><block type=\"set_current_tilemap\"><value name=\"tilemap\"><shadow type=\"tiles_tilemap_editor\"><field name=\"tilemap\">tilemap`level`</field><data>{\"commentRefs\":[],\"fieldData\":{\"tilemap\":\"level1\"}}</data></shadow></value></block></next></block></next></block></statement></block></xml>","main.ts":"","README.md":" ","assets.json":"","_animation.ts":"//////////////////////// animation.ts contents of previous version, just for test.ts here for convenience /////////////////////\n// file contents:\n// directional animations defines for enging_raycasting.ts\n// for convenience, constructed animations as below, which images are builtin in Arcade, \n// in order from easy to complex\n\n// each are 2D Image array, Image[][]: \n// 1st dimension is direction: \n// length=1 to any, e.g. 1,2,3,4,...,8,..., engine can automatically arange them to 0~360° \n// order: start from left, then folling with others in CW order\n// e.g: \n// 2 directions should be [leftAni,RightAni], \n// 4-direction should be [leftAni, frontAni, rightAni, backAni], \n// 8-direction=[l,lf,f,fr,r,rb,b,lb], ...\n// the reason that directions start from left, is almost all Arcade builtin one side images are facing left, so that would be convient for using.\n// 2nd dimension is animation Images\n// length=1 to any, e.g. 1,2,3,4,..., engine will automatically loop them by aniInterval(set when create CompactSprite, or use default)\n\n\n//simplest, just one static image, looks the same in all angle of view\nconst texturesDonut = [[sprites.food.smallDonut]]\nconst texturesBigCake = [[sprites.food.bigCake]]\n\n//1-direction, with ani, looks the same Ani in all angle of view\nconst texturesCoin = [[sprites.builtin.coin0, sprites.builtin.coin1, sprites.builtin.coin2, sprites.builtin.coin3, sprites.builtin.coin4, sprites.builtin.coin5]]\n\nconst texturesDog = [[sprites.builtin.dog0, sprites.builtin.dog1, sprites.builtin.dog2]]\nconst texturesDuck = [[sprites.duck.duck1, sprites.duck.duck2, sprites.duck.duck3, sprites.duck.duck4, sprites.duck.duck5, sprites.duck.duck6]]\n\n//2-direction, with ani, looks difference from left or right side\nconst texturesPlane = [[sprites.vehicle.plane0, sprites.vehicle.plane1, sprites.vehicle.plane2, sprites.vehicle.plane3, sprites.vehicle.plane4, sprites.vehicle.plane5],\n[sprites.vehicle.plane0, sprites.vehicle.plane1, sprites.vehicle.plane2, sprites.vehicle.plane3, sprites.vehicle.plane4, sprites.vehicle.plane5]]\n//no right side ani images builtin, so make them from lefts, and then flipX\nimagesFlipX(texturesPlane[1])\n\nconst texturesFish = [[sprites.builtin.angelFish0, sprites.builtin.angelFish1, sprites.builtin.angelFish2, sprites.builtin.angelFish3],\n[sprites.builtin.angelFish0, sprites.builtin.angelFish1, sprites.builtin.angelFish2, sprites.builtin.angelFish3]]\nimagesFlipX(texturesFish[1])\n\n//4-direction\nconst texturesHero = [\n [sprites.castle.heroWalkSideLeft1, sprites.castle.heroWalkSideLeft2, sprites.castle.heroWalkSideLeft3, sprites.castle.heroWalkSideLeft4],\n [sprites.castle.heroWalkFront1, sprites.castle.heroWalkFront2, sprites.castle.heroWalkFront3, sprites.castle.heroWalkFront4],\n [sprites.castle.heroWalkSideRight1, sprites.castle.heroWalkSideRight2, sprites.castle.heroWalkSideRight3, sprites.castle.heroWalkSideRight4],\n [sprites.castle.heroWalkBack1, sprites.castle.heroWalkBack2, sprites.castle.heroWalkBack3, sprites.castle.heroWalkBack4],\n]\n\nconst texturesPrincess2 = [\n [sprites.castle.princess2Left1, sprites.castle.princess2Left2],\n [sprites.castle.princess2WalkFront1, sprites.castle.princess2WalkFront2, sprites.castle.princess2WalkFront3, sprites.castle.princess2WalkFront2],\n [sprites.castle.princess2Right1, sprites.castle.princess2Right2],\n [sprites.castle.princess2WalkBack1, sprites.castle.princess2WalkBack2, sprites.castle.princess2WalkBack3, sprites.castle.princess2WalkBack2],\n]\n\n//4-direction, but back side has only 1 image, so no ani when looking from back\nconst texturesSkelly = [\n [sprites.castle.skellyWalkLeft1, sprites.castle.skellyWalkLeft2],\n [sprites.castle.skellyWalkFront1, sprites.castle.skellyWalkFront2, sprites.castle.skellyWalkFront3],\n [sprites.castle.skellyWalkRight1, sprites.castle.skellyWalkRight2],\n [img`\n\t ........................\n\t ........................\n\t ........................\n\t ........................\n\t ..........ffff..........\n\t ........ff1111ff........\n\t .......fb111111bf.......\n\t .......f11111111f.......\n\t ......fd11111111df......\n\t ......fd11111111df......\n\t ......fd11111111df......\n\t ......fb11111111bf......\n\t ......fcd111111dcf......\n\t .......fb111111bf.......\n\t .....ffffdb1bdcfff......\n\t ....fc111cfbfbc111cf....\n\t ....f11111ffff11111f....\n\t ....fbfbfbffffffbfbf....\n\t .........ffffff.........\n\t ..........fff...........\n\t ........................\n\t ........................\n\t ........................\n\t ........................\n\t `],\n]\n\nconst texturesPrincess = [\n [sprites.castle.princessLeft0, sprites.castle.princessLeft1, sprites.castle.princessLeft0, sprites.castle.princessLeft2],\n [sprites.castle.princessFront0, sprites.castle.princessFront1, sprites.castle.princessFront0, sprites.castle.princessFront2],\n [],\n [sprites.castle.princessBack0, sprites.castle.princessBack1, sprites.castle.princessBack0, sprites.castle.princessBack2],\n]\ntexturesPrincess[0].forEach((v, i) => {\n texturesPrincess[2].push(v.clone())\n texturesPrincess[2][i].flipX()\n}\n)\n\nfunction imagesFlipX(ani: Image[]) {\n ani.forEach((img, i) => {\n ani[i] = img.clone() //don't worry memery leak or waste, cause old images are still using by left\n ani[i].flipX()\n })\n}\n\n//////////////////////// end of animation.ts contents of previous version //////////////\n\n\n","_render_blocks.ts":"enum RCSpriteAttribute {\n ZOffset,\n ZPosition,\n ZVelocity,\n ZAcceleration\n}\n/**\n * A 2.5D Screen Render, using Raycasting algorithm\n **/\n//% color=#03AA74 weight=1 icon=\"\\uf1b2\" //cube f1b2 , fold f279\n//% groups='[\"Instance\",\"Basic\", \"Dimension Z\", \"Animate\", \"Advanced\"]'\n//% block=\"3D Render\"\nnamespace Render {\n export enum attribute {\n dirX,\n dirY,\n fov,\n wallZScale,\n }\n\n export class Animations {\n constructor(public frameInterval: number, public animations: Image[][]) {\n }\n\n msLast = 0\n index = 0\n iAnimation = 0\n getFrameByDir(dir: number): Image {\n if (control.millis() - this.msLast > this.frameInterval) {\n this.msLast = control.millis()\n this.index++\n this.iAnimation = Math.round((dir * this.animations.length)) % this.animations.length\n if (this.index >= this.animations[this.iAnimation].length)\n this.index = 0\n }\n return this.animations[this.iAnimation][this.index]\n }\n }\n\n /**\n * Apply a directional image animations on a sprite\n * @param sprite the sprite to animate on\n * @param animations the directional animates\n */\n //% blockId=set_animation\n //% block=\"set $sprite=variables_get(mySprite) with animations$animations\"\n //% animations.shadow=create_animation\n //% group=\"Animate\"\n //% weight=100\n //% help=github:pxt-raycasting/docs/set-sprite-animations\n export function setSpriteAnimations(sprite: Sprite, animations: Animations) {\n raycastingRender.spriteAnimations[sprite.id] = animations\n }\n\n /**\n * Create a directional image animations, multi animations will applied to one round dirctions averagely, start from the left. \n * The reason that directions start from left, is almost all Arcade out-of-box 1 or 2-dirction images are facing left, so that would be convient for using.\n * @param frameInterval the time between changes, eg: 150\n * @param frames1 animation, if this is the first of multi animation it will be used as left, others will \n * @param frames2 optional, used for 2 or more dirctional\n * @param frames3 optional, used for 3 or more dirctional\n * @param frames4 optional, used for 4 or more dirctional\n */\n //% blockId=create_animation\n //% block=\"interval$frameInterval=timePicker animates:$frames1=animation_editor|| $frames2=animation_editor $frames3=animation_editor $frames4=animation_editor\"\n //% inlineInputMode=inline\n //% group=\"Animate\"\n //% weight=100\n //% help=github:pxt-raycasting/docs/create-animations\n export function createAnimations(frameInterval: number, frames1: Image[], frames2?: Image[], frames3?: Image[], frames4?: Image[]): Animations {\n const animationList = [frames1]\n if (frames2) animationList.push(frames2)\n if (frames3) animationList.push(frames3)\n if (frames4) animationList.push(frames4)\n return new Animations(frameInterval, animationList)\n }\n\n /**\n * Get the Render\n * @param img the image\n */\n //% group=\"Instance\"\n //% blockId=rcRender_getRCRenderInstance block=\"raycasting render\"\n //% expandableArgumentMode=toggle\n //% weight=100 \n //% blockHidden=true\n //% hidden=1\n export function getRCRenderInstance(): RayCastingRender {\n return raycastingRender\n }\n\n /**\n * Get the render Sprite, which create automatically, for physical collisions, and holding the view point.\n * You can consider it as \"myself\", and operate it like a usual sprite, eg.: position, speed, scale, collision, ...\n * But properties relative 3D, eg. ZOffset, ZPosition, viewAngle, and etc. are not in the Sprite class.\n */\n //% group=\"Instance\"\n //% blockId=rcRender_getRenderSpriteVariable block=\"myself sprite\"\n //% expandableArgumentMode=toggle\n //% blockSetVariable=mySprite\n //% weight=99\n //% help=github:pxt-raycasting/get-render-sprite-variable\n export function getRenderSpriteVariable(): Sprite {\n return raycastingRender.sprSelf\n }\n\n /**\n * Get the render Sprite, which create automatically, for physical collisions, and holding the view point.(but get/set view direction with dirX/dirY, which not in the Sprite class) \n * You can consider it as \"myself\", and operate it like a usual sprite.\n * eg: position, speed, scale, collision, ...\n */\n //% group=\"Instance\"\n //% blockId=rcRender_getRenderSpriteInstance block=\"myself sprite\"\n //% expandableArgumentMode=toggle\n //% weight=98\n //% help=github:pxt-raycasting/docs/get-render-sprite-instance\n export function getRenderSpriteInstance(): Sprite {\n return raycastingRender.sprSelf\n }\n\n /**\n * Toggle current view mode\n */\n //% blockId=rcRender_toggleViewMode block=\"toggle current view mode\"\n //% group=\"Basic\"\n //% weight=89\n //% help=github:pxt-raycasting/docs/toggle-view-mode\n export function toggleViewMode() {\n raycastingRender.viewMode = raycastingRender.viewMode == ViewMode.tilemapView ? ViewMode.raycastingView : ViewMode.tilemapView\n }\n\n /**\n * Current view mode is the specific one?\n * @param viewMode\n */\n //% blockId=rcRender_isViewMode block=\"current is $viewMode\"\n //% group=\"Basic\"\n //% weight=88\n //% help=github:pxt-raycasting/docs/is-view-mode\n export function isViewMode(viewMode: ViewMode): boolean {\n return viewMode == raycastingRender.viewMode\n }\n\n /**\n * Set view mode\n * @param viewMode\n */\n //% blockId=rcRender_setViewMode block=\"set view mode $viewMode\"\n //% group=\"Basic\"\n //% weight=87\n //% help=github:pxt-raycasting/docs/set-view-mode\n export function setViewMode(viewMode: ViewMode) {\n raycastingRender.viewMode = viewMode\n }\n\n /**\n * Get render arribute\n * @param viewMode\n */\n //% group=\"Basic\"\n //% block=\"get %attribute\" \n //% blockId=rcRender_getAttribute\n //% weight=83\n //% help=github:pxt-raycasting/docs/get-attribute\n export function getAttribute(attr: attribute): number {\n switch (attr) {\n case attribute.dirX:\n return raycastingRender.dirX\n case attribute.dirY:\n return raycastingRender.dirY\n case attribute.fov:\n return raycastingRender.fov\n case attribute.wallZScale:\n return raycastingRender.wallZScale\n default:\n return 0\n }\n }\n\n /**\n * Set render arribute\n * @param viewMode\n */\n //% group=\"Basic\"\n //% block=\"set %attribute = %value\" \n //% blockId=rcRender_setAttribute\n //% weight=82\n //% help=github:pxt-raycasting/docs/set-attribute\n export function setAttribute(attr: attribute, value: number) {\n switch (attr) {\n case attribute.dirX:\n raycastingRender.dirX = value\n break\n case attribute.dirY:\n raycastingRender.dirY = value\n break\n case attribute.fov:\n if (value < 0) value = 0\n raycastingRender.fov = value\n break\n case attribute.wallZScale:\n if (value < 0) value = 0\n raycastingRender.wallZScale = value\n break\n default:\n }\n }\n\n /**\n * Get default FOV (field of view) value\n * @param viewMode\n */\n //% group=\"Basic\"\n //% block=\"defaultFov\"\n //% blockId=rcRender_getDefaultFov\n //% weight=81\n //% help=github:pxt-raycasting/docs/get-default-fov\n export function getDefaultFov(): number {\n return defaultFov\n }\n\n /**\n * Set view angle\n * @param angle, unit: degree 0~360\n */\n //% blockId=rcRender_setViewAngleInDegree block=\"set view angle$angle\"\n //% angle.min=0 angle.max=360 angle.defl=90\n //% group=\"Basic\"\n //% weight=80\n //% help=github:pxt-raycasting/docs/set-view-angle-in-degree\n export function setViewAngleInDegree(angle: number) {\n raycastingRender.viewAngle = angle * Math.PI / 180\n }\n\n /**\n * Set view angle by dirX and dirY\n * @param dirX\n * @param dirY\n */\n //% blockId=rcRender_setViewAngle block=\"set view angle by dirX%dirX and dirY%dirY\"\n //% group=\"Basic\"\n //% weight=79\n //% help=github:pxt-raycasting/docs/set-view-angle\n export function setViewAngle(dirX: number, dirY: number) {\n raycastingRender.viewAngle = Math.atan2(dirY, dirX)\n }\n\n /**\n * Set floating offset height for a sprite at Z direction\n * @param sprite\n * @param Zoffset Negative floats down, affirmative goes up\n * @param duration moving time, 0 for immediately, unit: ms\n */\n //% blockId=rcRender_setZOffset block=\"set Sprite %spr=variables_get(mySprite) floating %offset pixels|| duration $duration=timePicker|ms \"\n //% offset.min=-100 offset.max=100 offset.defl=8\n //% duration.min=0 duration.max=5000 duration.defl=0\n //% group=\"Dimension Z\"\n //% weight=77\n //% blockHidden\n //% help=github:pxt-raycasting/docs/set-z-offset\n export function setZOffset(sprite: Sprite, offset: number, duration?: number) {\n raycastingRender.setZOffset(sprite, offset, duration)\n }\n\n /**\n * Set arribute of a Sprite\n * @param spr Sprite\n * @param attr RCSpriteAttribute\n */\n //% group=\"Dimension Z\"\n //% block=\"set Sprite %spr=variables_get(mySprite) %attribute = %value\"\n //% blockId=rcRender_setSpriteAttribute\n //% weight=75\n //% help=github:pxt-raycasting/docs/set-sprite-attribute\n export function setSpriteAttribute(spr: Sprite, attr: RCSpriteAttribute, value: number) {\n switch (attr) {\n case RCSpriteAttribute.ZOffset:\n raycastingRender.getMotionZ(spr).offset = value\n break\n case RCSpriteAttribute.ZPosition:\n raycastingRender.getMotionZ(spr).p = value\n break\n case RCSpriteAttribute.ZVelocity:\n raycastingRender.getMotionZ(spr).v = value\n break\n case RCSpriteAttribute.ZAcceleration:\n raycastingRender.getMotionZ(spr).a = value\n break\n default:\n }\n }\n\n /**\n * Get arribute of a Sprite\n * @param spr Sprite\n * @param attr RCSpriteAttribute\n */\n //% group=\"Dimension Z\"\n //% block=\"get Sprite %spr=variables_get(mySprite) %attribute\"\n //% blockId=rcRender_getSpriteAttribute\n //% weight=74\n //% help=github:pxt-raycasting/docs/get-sprite-attribute\n export function getSpriteAttribute(spr: Sprite, attr: RCSpriteAttribute): number {\n switch (attr) {\n case RCSpriteAttribute.ZOffset:\n return raycastingRender.getMotionZ(spr).offset\n case RCSpriteAttribute.ZPosition:\n return raycastingRender.getMotionZ(spr).p\n case RCSpriteAttribute.ZVelocity:\n return raycastingRender.getMotionZ(spr).v\n case RCSpriteAttribute.ZAcceleration:\n return raycastingRender.getMotionZ(spr).a\n default:\n return 0\n }\n }\n\n /**\n * Check if 2 sprites overlaps each another in Z dimension\n * Best work together with sprites.onOverlap(kind1, kind2)\n * @param sprite1\n * @param sprite2\n */\n //% blockId=rcRender_isSpritesOverlapZ\n //% block=\"is sprites $sprite1=variables_get(mySprite) and $sprite2=variables_get(mySprite2) overlaps in Z dimension\"\n //% group=\"Dimension Z\"\n //% weight=71\n //% help=github:pxt-raycasting/docs/is-sprites-overlap-z\n export function isSpritesOverlapZ(sprite1: Sprite, sprite2: Sprite): boolean {\n return raycastingRender.isOverlapZ(sprite1, sprite2)\n }\n\n /**\n * Make sprite jump, with specific height and duration\n * Jump can only happened when sprite is standing, current height = its offset .\n * @param sprite\n * @param height jump height in pixel\n * @param duration hover time span, unit: ms\n */\n //% blockId=rcRender_jumpWithHeightAndDuration block=\"Sprite %spr=variables_get(mySprite) jump, with height $height duration $duration=timePicker|ms \"\n //% height.min=0 height.max=100 height.defl=16\n //% duration.min=50 duration.max=5000 duration.defl=500\n //% group=\"Dimension Z\"\n //% weight=70\n //% help=github:pxt-raycasting/docs/jump-with-height-and-duration\n export function jumpWithHeightAndDuration(sprite: Sprite, height: number, duration: number) {\n raycastingRender.jumpWithHeightAndDuration(sprite, height, duration)\n }\n\n /**\n * Make sprite jump, with specific speed and acceleration\n * Simular with Move block, but jump can only happened when sprite is standing, current height = its offset.\n * @param sprite\n * @param v vetical speed, unit: pixel/s\n * @param a vetical acceleration, unit: pixel/s²\n */\n //% blockId=rcRender_jump block=\"Sprite %spr=variables_get(mySprite) jump||, with speed $v=spriteSpeedPicker acceleration $a\"\n //% v.min=-100 v.max=100 v.defl=60\n //% a.min=-1000 a.max=1000 a.defl=-250\n //% group=\"Dimension Z\"\n //% weight=68\n //% help=github:pxt-raycasting/docs/jump\n export function jump(sprite: Sprite, v?: number, a?: number) {\n raycastingRender.jump(sprite, v, a)\n }\n\n /**\n * Make sprite jump, with specific speed and acceleration\n * @param sprite\n * @param v vetical speed, unit: pixel/s\n * @param a vetical acceleration, unit: pixel/s²\n */\n //% blockId=rcRender_move block=\"Sprite %spr=variables_get(mySprite) move, with speed $v=spriteSpeedPicker|| acceleration $a\"\n //% v.min=-200 v.max=200 v.defl=60\n //% a.min=-1000 a.max=1000 a.defl=-250\n //% group=\"Dimension Z\"\n //% weight=66\n //% help=github:pxt-raycasting/docs/move\n export function move(sprite: Sprite, v?: number, a?: number) {\n raycastingRender.move(sprite, v, a)\n }\n\n /**\n * Control the self sprite using the direction buttons from the controller. \n * To stop controlling self sprite, pass 0 for v and va.\n *\n * @param v The velocity used for forward/backword movement when up/down is pressed, in pixel/s\n * @param va The angle velocity used for turn view direction when left/right is pressed, in radian/s.\n */\n //% blockId=\"rcRender_moveWithController\" block=\"move with buttons velocity $v|| turn speed $va camera sway$cameraSway pixels\"\n //% weight=60\n //% expandableArgumentMode=\"toggle\"\n //% v.defl=2 va.defl=3\n //% group=\"Advanced\"\n //% v.shadow=\"spriteSpeedPicker\"\n //% va.shadow=\"spriteSpeedPicker\"\n //% help=github:pxt-raycasting/docs/move-with-controller\n export function moveWithController(v: number = 2, va: number = 3, cameraSway?: number) {\n raycastingRender.velocity = v\n raycastingRender.velocityAngle = va\n if (cameraSway != undefined)\n raycastingRender.cameraSway = cameraSway | 0\n }\n\n /**\n * Render takeover all sprites in current scene\n * Render will call this automatically, but maybe not in time enough.\n * If you saw sprite draw at its tilemap position on screen, call this just after created the sprite.\n */\n //% blockId=rcRender_takeoverSceneSprites \n //% block=\"takeover sprites in scene\"\n //% group=\"Advanced\"\n //% weight=49\n //% help=github:pxt-raycasting/docs/takeover-scene-sprites\n export function takeoverSceneSprites() {\n raycastingRender.takeoverSceneSprites()\n }\n\n /**\n * Run on sprite dirction updated, present view point to Sprite facing dirction, or which angle you see of the sprite.\n * Just using with other animation extensions, to set proper Image for sprite.\n * Not required, if you have used the set animations block provided.\n * @param dir It is a float number, 0~1 corresponds to 0~360°, suggest use Math.round(dir*dirAniTotalCount)%dirAniTotalCount to get index of direction\n */\n //% blockId=rcRender_registerOnSpriteDirectionUpdateHandler\n //% block=\"run code when sprite $spr dirction updated to $dir\"\n //% draggableParameters\n //% group=\"Advanced\"\n //% weight=48\n //% help=github:pxt-raycasting/docs/register-on-sprite-direction-update-handler\n export function registerOnSpriteDirectionUpdateHandler(handler: (spr: Sprite, dir: number) => void) {\n raycastingRender.registerOnSpriteDirectionUpdate(handler)\n }\n}\n","_render_raycasting.ts":"//% shim=pxt::updateScreen\nfunction updateScreen(img: Image) { }\n\nenum ViewMode {\n //% block=\"TileMap Mode\"\n tilemapView,\n //% block=\"Raycasting Mode\"\n raycastingView,\n}\n\nnamespace Render {\n const SH = screen.height, SHHalf = SH / 2\n const SW = screen.width, SWHalf = SW / 2\n const fpx = 8\n const fpx_scale = 2 ** fpx\n function tofpx(n: number) { return (n * fpx_scale) | 0 }\n const one = 1 << fpx\n const one2 = 1 << (fpx + fpx)\n const FPX_MAX = (1 << fpx) - 1\n\n class MotionSet1D {\n p: number\n v: number = 0\n a: number = 0\n constructor(public offset: number) {\n this.p = offset\n }\n }\n\n export const defaultFov = SW / SH / 2 //Wall just fill screen height when standing 1 tile away\n\n export class RayCastingRender {\n private tempScreen: Image = image.create(SW, SH)\n\n velocityAngle: number = 2\n velocity: number = 3\n protected _viewMode = ViewMode.raycastingView\n protected dirXFpx: number\n protected dirYFpx: number\n protected planeX: number\n protected planeY: number\n protected _angle: number\n protected _fov: number\n protected _wallZScale: number = 1\n cameraSway = 0\n protected isWalking = false\n protected cameraOffsetX = 0\n protected cameraOffsetZ_fpx = 0\n\n //sprites & accessories\n sprSelf: Sprite\n sprites: Sprite[] = []\n sprites2D: Sprite[] = []\n spriteParticles: particles.ParticleSource[] = []\n spriteLikes: SpriteLike[] = []\n spriteAnimations: Animations[] = []\n protected spriteMotionZ: MotionSet1D[] = []\n protected sayRederers: sprites.BaseSpriteSayRenderer[] = []\n protected sayEndTimes: number[] = []\n\n //reference\n protected tilemapScaleSize = 1 << TileScale.Sixteen\n map: tiles.TileMapData\n bg: Image\n textures: Image[]\n protected oldRender: scene.Renderable\n protected myRender: scene.Renderable\n\n //render\n protected wallHeightInView: number\n protected wallWidthInView: number\n protected dist: number[] = []\n //render perf const\n cameraRangeAngle: number\n viewZPos: number\n selfXFpx: number\n selfYFpx: number\n\n //for drawing sprites\n protected invDet: number //required for correct matrix multiplication\n camera: scene.Camera\n tempSprite: Sprite = sprites.create(img`0`)\n protected transformX: number[] = []\n protected transformY: number[] = []\n protected angleSelfToSpr: number[] = []\n\n onSpriteDirectionUpdateHandler: (spr: Sprite, dir: number) => void\n\n get xFpx(): number {\n return Fx.add(this.sprSelf._x, Fx.div(this.sprSelf._width, Fx.twoFx8)) as any as number / this.tilemapScaleSize\n }\n\n // set xFpx(v: number) {\n // this.sprSelf._x = v * this.tilemapScaleSize as any as Fx8\n // }\n\n get yFpx(): number {\n return Fx.add(this.sprSelf._y, Fx.div(this.sprSelf._height, Fx.twoFx8)) as any as number / this.tilemapScaleSize\n }\n\n // set yFpx(v: number) {\n // this.sprSelf._y = v * this.tilemapScaleSize as any as Fx8\n // }\n\n get dirX(): number {\n return this.dirXFpx / fpx_scale\n }\n\n get dirY(): number {\n return this.dirYFpx / fpx_scale\n }\n\n set dirX(v: number) {\n this.dirXFpx = v * fpx_scale\n }\n\n set dirY(v: number) {\n this.dirYFpx = v * fpx_scale\n }\n\n sprXFx8(spr: Sprite) {\n return Fx.add(spr._x, Fx.div(spr._width, Fx.twoFx8)) as any as number / this.tilemapScaleSize\n }\n\n sprYFx8(spr: Sprite) {\n return Fx.add(spr._y, Fx.div(spr._height, Fx.twoFx8)) as any as number / this.tilemapScaleSize\n }\n\n get fov(): number {\n return this._fov\n }\n\n set fov(fov: number) {\n this._fov = fov\n this.wallHeightInView = (SW << (fpx - 1)) / this._fov\n this.wallWidthInView = this.wallHeightInView >> fpx // not fpx // wallSize / this.fov * 4 / 3 * 2\n this.cameraRangeAngle = Math.atan(this.fov) + .1 //tolerance for spr center just out of camera\n\n this.setVectors()\n }\n\n get viewAngle(): number {\n return this._angle\n }\n set viewAngle(angle: number) {\n this._angle = angle\n this.setVectors()\n this.updateSelfImage()\n }\n\n get wallZScale(): number {\n return this._wallZScale\n }\n set wallZScale(v: number) {\n this._wallZScale = v\n }\n\n getMotionZ(spr: Sprite, offsetZ: number = 0) {\n let motionZ = this.spriteMotionZ[spr.id]\n if (!motionZ) {\n motionZ = new MotionSet1D(tofpx(offsetZ))\n this.spriteMotionZ[spr.id] = motionZ\n }\n return motionZ\n }\n\n getZOffset(spr: Sprite) {\n return this.getMotionZ(spr).offset / fpx_scale\n }\n\n setZOffset(spr: Sprite, offsetZ: number, duration: number = 500) {\n const motionZ = this.getMotionZ(spr, offsetZ)\n\n motionZ.offset = tofpx(offsetZ)\n if (motionZ.p != motionZ.offset) {\n if (duration === 0)\n motionZ.p = motionZ.offset\n else if (motionZ.v == 0)\n this.move(spr, (motionZ.offset - motionZ.p) / fpx_scale * 1000 / duration, 0)\n }\n }\n\n getMotionZPosition(spr: Sprite) {\n return this.getMotionZ(spr).p / fpx_scale\n }\n\n //todo, use ZHeight(set from sprite.Height when takeover, then sprite.Height will be replace with width)\n isOverlapZ(sprite1: Sprite, sprite2: Sprite): boolean {\n const p1 = this.getMotionZPosition(sprite1)\n const p2 = this.getMotionZPosition(sprite2)\n if (p1 < p2) {\n if (p1 + sprite1.height > p2) return true\n } else {\n if (p2 + sprite2.height > p1) return true\n }\n return false\n }\n\n move(spr: Sprite, v: number, a: number) {\n const motionZ = this.getMotionZ(spr)\n\n motionZ.v = tofpx(v)\n motionZ.a = tofpx(a)\n }\n\n jump(spr: Sprite, v: number, a: number) {\n const motionZ = this.getMotionZ(spr)\n if (motionZ.p != motionZ.offset)\n return\n\n motionZ.v = tofpx(v)\n motionZ.a = tofpx(a)\n }\n\n jumpWithHeightAndDuration(spr: Sprite, height: number, duration: number) {\n const motionZ = this.getMotionZ(spr)\n if (motionZ.p != motionZ.offset)\n return\n\n // height= -v*v/a/2\n // duration = -v/a*2 *1000\n const v = height * 4000 / duration\n const a = -v * 2000 / duration\n motionZ.v = tofpx(v)\n motionZ.a = tofpx(a)\n }\n\n get viewMode(): ViewMode {\n return this._viewMode\n }\n\n set viewMode(v: ViewMode) {\n this._viewMode = v\n }\n\n updateViewZPos() {\n this.viewZPos = this.spriteMotionZ[this.sprSelf.id].p + (this.sprSelf._height as any as number) - (2 << fpx)\n }\n\n takeoverSceneSprites() {\n const sc_allSprites = game.currentScene().allSprites\n for (let i = 0; i < sc_allSprites.length;) {\n const spr = sc_allSprites[i]\n if (spr instanceof Sprite) {\n const sprList = (spr.flags & sprites.Flag.RelativeToCamera) ? this.sprites2D : this.sprites\n if (sprList.indexOf(spr) < 0) {\n sprList.push(spr as Sprite)\n this.getMotionZ(spr, 0)\n spr.onDestroyed(() => {\n this.sprites.removeElement(spr as Sprite) //can be in one of 2 lists\n this.sprites2D.removeElement(spr as Sprite) //can be in one of 2 lists\n const sayRenderer = this.sayRederers[spr.id]\n if (sayRenderer) {\n this.sayRederers.removeElement(sayRenderer)\n sayRenderer.destroy()\n }\n })\n }\n } else if (spr instanceof particles.ParticleSource) {\n const particle = (spr as particles.ParticleSource)\n if (this.spriteParticles.indexOf(particle) < 0) {\n this.spriteParticles[(particle.anchor as Sprite).id] = particle\n particle.anchor = this.tempSprite\n }\n } else {\n if (this.spriteLikes.indexOf(spr) < 0)\n this.spriteLikes.push(spr)\n }\n sc_allSprites.removeElement(spr)\n }\n this.sprites.forEach((spr) => {\n if (spr)\n this.takeoverSayRenderOfSprite(spr)\n })\n }\n takeoverSayRenderOfSprite(sprite: Sprite) {\n const sprite_as_any = (sprite as any)\n if (sprite_as_any.sayRenderer) {\n this.sayRederers[sprite.id] = sprite_as_any.sayRenderer\n this.sayEndTimes[sprite.id] = sprite_as_any.sayEndTime;\n sprite_as_any.sayRenderer = undefined\n sprite_as_any.sayEndTime = undefined\n }\n }\n\n tilemapLoaded() {\n const sc = game.currentScene()\n this.map = sc.tileMap.data\n this.textures = sc.tileMap.data.getTileset()\n this.tilemapScaleSize = 1 << sc.tileMap.data.scale\n this.oldRender = sc.tileMap.renderable\n this.spriteLikes.removeElement(this.oldRender)\n sc.allSprites.removeElement(this.oldRender)\n\n let frameCallback_update = sc.eventContext.registerFrameHandler(scene.PRE_RENDER_UPDATE_PRIORITY + 1, () => {\n const dt = sc.eventContext.deltaTime;\n // sc.camera.update(); // already did in scene\n for (const s of this.sprites)\n s.__update(sc.camera, dt);\n this.sprSelf.__update(sc.camera, dt)\n })\n\n let frameCallback_draw = sc.eventContext.registerFrameHandler(scene.RENDER_SPRITES_PRIORITY + 1, () => {\n if (this._viewMode == ViewMode.tilemapView) {\n // screen.drawImage(sc.background.image, 0, 0)\n this.oldRender.__drawCore(sc.camera)\n this.sprites.forEach(spr => spr.__draw(sc.camera))\n this.sprSelf.__draw(sc.camera)\n } else {\n // this.tempScreen.drawImage(sc.background.image, 0, 0)\n //debug\n // const ms=control.micros()\n this.render()\n // info.setScore(control.micros()-ms)\n screen.fill(0)\n }\n this.sprites2D.forEach(spr => spr.__draw(sc.camera))\n this.spriteLikes.forEach(spr => spr.__draw(sc.camera))\n if (this._viewMode == ViewMode.raycastingView)\n this.tempScreen.drawTransparentImage(screen, 0, 0)\n })\n\n sc.tileMap.addEventListener(tiles.TileMapEvent.Unloaded, data => {\n sc.eventContext.unregisterFrameHandler(frameCallback_update)\n sc.eventContext.unregisterFrameHandler(frameCallback_draw)\n })\n\n // this.myRender = scene.createRenderable(\n // scene.TILE_MAP_Z,\n // (t, c) => this.trace(t, c)\n // )\n\n }\n\n constructor() {\n this._angle = 0\n this.fov = defaultFov\n this.camera = new scene.Camera()\n\n const sc = game.currentScene()\n if (!sc.tileMap) {\n sc.tileMap = new tiles.TileMap();\n } else {\n this.tilemapLoaded()\n }\n game.currentScene().tileMap.addEventListener(tiles.TileMapEvent.Loaded, data => this.tilemapLoaded())\n\n //self sprite\n this.sprSelf = sprites.create(image.create(this.tilemapScaleSize >> 1, this.tilemapScaleSize >> 1), SpriteKind.Player)\n this.takeoverSceneSprites()\n this.sprites.removeElement(this.sprSelf)\n this.updateViewZPos()\n scene.cameraFollowSprite(this.sprSelf)\n this.updateSelfImage()\n\n game.onUpdate(function () {\n this.updateControls()\n })\n\n game.onUpdateInterval(400, () => {\n for (let i = 0; i < this.sprites.length;) {\n const spr = this.sprites[i]\n if (spr.flags & sprites.Flag.RelativeToCamera) {\n this.sprites.removeElement(spr)\n this.sprites2D.push(spr)\n } else { i++ }\n }\n for (let i = 0; i < this.sprites2D.length;) {\n const spr = this.sprites2D[i]\n if (!(spr.flags & sprites.Flag.RelativeToCamera)) {\n this.sprites2D.removeElement(spr)\n this.sprites.push(spr)\n } else { i++ }\n }\n this.takeoverSceneSprites() // in case some one new\n })\n\n\n game.onUpdateInterval(25, () => {\n if (this.cameraSway && this.isWalking) {\n this.cameraOffsetX = (Math.sin(control.millis() / 150) * this.cameraSway * 3) | 0\n this.cameraOffsetZ_fpx = tofpx(Math.cos(control.millis() / 75) * this.cameraSway) | 0\n }\n });\n control.__screen.setupUpdate(() => {\n if (this.viewMode == ViewMode.raycastingView)\n updateScreen(this.tempScreen)\n else\n updateScreen(screen)\n })\n\n game.addScenePushHandler((oldScene) => {\n control.__screen.setupUpdate(() => { updateScreen(screen) })\n })\n game.addScenePopHandler((oldScene) => {\n control.__screen.setupUpdate(() => {\n if (this.viewMode == ViewMode.raycastingView)\n updateScreen(this.tempScreen)\n else\n updateScreen(screen)\n })\n })\n }\n\n private setVectors() {\n const sin = Math.sin(this._angle)\n const cos = Math.cos(this._angle)\n this.dirXFpx = tofpx(cos)\n this.dirYFpx = tofpx(sin)\n this.planeX = tofpx(sin * this._fov)\n this.planeY = tofpx(cos * -this._fov)\n }\n\n //todo, pre-drawn dirctional image\n public updateSelfImage() {\n const img = this.sprSelf.image\n img.fill(6)\n const arrowLength = img.width / 2\n img.drawLine(arrowLength, arrowLength, arrowLength + this.dirX * arrowLength, arrowLength + this.dirY * arrowLength, 2)\n img.fillRect(arrowLength - 1, arrowLength - 1, 2, 2, 2)\n }\n\n updateControls() {\n if (this.velocityAngle !== 0) {\n const dx = controller.dx(this.velocityAngle)\n if (dx) {\n this.viewAngle += dx\n }\n }\n if (this.velocity !== 0) {\n this.isWalking = true\n const dy = controller.dy(this.velocity)\n if (dy) {\n const nx = this.xFpx - Math.round(this.dirXFpx * dy)\n const ny = this.yFpx - Math.round(this.dirYFpx * dy)\n this.sprSelf.setPosition((nx * this.tilemapScaleSize / fpx_scale), (ny * this.tilemapScaleSize / fpx_scale))\n } else {\n this.isWalking = false\n }\n }\n\n for (const spr of this.sprites) {\n this.updateMotionZ(spr)\n }\n this.updateMotionZ(this.sprSelf)\n }\n\n updateMotionZ(spr: Sprite) {\n const dt = game.eventContext().deltaTime\n const motionZ = this.spriteMotionZ[spr.id]\n //if (!motionZ) continue\n\n if (motionZ.v != 0 || motionZ.p != motionZ.offset) {\n motionZ.v += motionZ.a * dt, motionZ.p += motionZ.v * dt\n //landing\n if ((motionZ.a >= 0 && motionZ.v > 0 && motionZ.p > motionZ.offset) ||\n (motionZ.a <= 0 && motionZ.v < 0 && motionZ.p < motionZ.offset)) { motionZ.p = motionZ.offset, motionZ.v = 0 }\n if (spr === this.sprSelf)\n this.updateViewZPos()\n }\n\n }\n\n\n blitRowBreak(screenX: number, screenUp: number, screenDown: number, source: Image, sourceX: number, sourceYBreak: number) {\n\n let stepY = (sourceYBreak) / (SHHalf - screenUp )\n let sourceY = sourceYBreak - stepY\n let y = SHHalf -1\n if (screenUp < 0)\n screenUp = 0\n while (y >= Math.ceil(screenUp)-1) {\n if (sourceY < 0) \n sourceY = 0\n const c = source.getPixel(sourceX, sourceY)\n this.tempScreen.setPixel(screenX, y, c)\n y--\n sourceY -= stepY\n }\n // from screen half going down\n stepY = (source.height - sourceYBreak) / (screenDown - SHHalf)\n sourceY = sourceYBreak \n y = SHHalf\n if (screenDown > SH)\n screenDown = SH\n while (y < Math.round(screenDown)) {\n const c = source.getPixel(sourceX, sourceY)\n this.tempScreen.setPixel(screenX, y, c)\n y++\n sourceY += stepY\n }\n\n }\n \n render() {\n // based on https://lodev.org/cgtutor/raycasting.html\n this.selfXFpx = this.xFpx\n this.selfYFpx = this.yFpx\n\n let drawStart = 0\n let drawHeight = 0\n let lastDist = -1, lastTexX = -1, lastMapX = -1, lastMapY = -1\n this.viewZPos = this.spriteMotionZ[this.sprSelf.id].p + (this.sprSelf._height as any as number) - (2 << fpx) + this.cameraOffsetZ_fpx\n let cameraRangeAngle = Math.atan(this.fov) + .1 //tolerance for spr center just out of camera\n //debug\n // const ms=control.millis()\n\n \n\n\n const tex = this.textures[1]\n let rayDirX0 = this.dirXFpx / fpx_scale + (this.planeX / fpx_scale)\n let rayDirY0 = this.dirYFpx / fpx_scale + (this.planeY / fpx_scale)\n let rayDirX1 = this.dirXFpx / fpx_scale - (this.planeX / fpx_scale)\n let rayDirY1 = this.dirYFpx / fpx_scale - (this.planeY / fpx_scale)\n let fmapX = this.selfXFpx / fpx_scale\n let fmapY = this.selfYFpx / fpx_scale\n\n const sc = game.currentScene() \n // background\n const speed = 2 // 2: normal speed\n let backgroundOffset = (this._angle / Math.PI * speed ) % 1 // range -1..1\n if (backgroundOffset < 0) backgroundOffset++ // range 0..1\n backgroundOffset *= SW // range 0..screenwidth\n \n //floor\n for (let y = 60; y < SH; y++) {\n let p = y - SHHalf\n let posZ = SH * this.viewZPos / this.tilemapScaleSize / fpx_scale\n let rowDistance = posZ / p\n let floorStepX = rowDistance * (rayDirX1 - rayDirX0) / SW\n let floorStepY = rowDistance * (rayDirY1 - rayDirY0) / SW\n\n\n\n let floorX = fmapX + rowDistance * rayDirX0\n let floorY = fmapY + rowDistance * rayDirY0\n for (let x = 0; x < SW; x++) {\n let cellX = Math.floor(floorX)\n let cellY = Math.floor(floorY)\n let tx = (16 * (floorX - cellX)) & 15\n let ty = (16 * (floorY - cellY)) & (15)\n let mapX = Math.round(floorX - 0.5) % 16\n let mapY = Math.round(floorY - 0.5) % 16\n let tileType = this.map.getTile(mapX, mapY)\n let floorTex = this.textures[tileType]\n floorX += floorStepX\n floorY += floorStepY\n let c = floorTex.getPixel(tx, ty)\n this.tempScreen.setPixel(x, y, c)\n\n }\n\n\n\n }\n // walls\n\n for (let x = 0; x < SW; x++) {\n const cameraX: number = one - Math.idiv(((x + this.cameraOffsetX) << fpx) << 1, SW)\n let rayDirX = this.dirXFpx + (this.planeX * cameraX >> fpx)\n let rayDirY = this.dirYFpx + (this.planeY * cameraX >> fpx)\n\n // avoid division by zero\n if (rayDirX == 0) rayDirX = 1\n if (rayDirY == 0) rayDirY = 1\n\n let mapX = this.selfXFpx >> fpx\n let mapY = this.selfYFpx >> fpx\n\n // length of ray from current position to next x or y-side\n let sideDistX = 0, sideDistY = 0\n\n // length of ray from one x or y-side to next x or y-side\n const deltaDistX = Math.abs(Math.idiv(one2, rayDirX));\n const deltaDistY = Math.abs(Math.idiv(one2, rayDirY));\n\n let mapStepX = 0, mapStepY = 0\n\n let sideWallHit = false;\n\n //calculate step and initial sideDist\n if (rayDirX < 0) {\n mapStepX = -1;\n sideDistX = ((this.selfXFpx - (mapX << fpx)) * deltaDistX) >> fpx;\n } else {\n mapStepX = 1;\n sideDistX = (((mapX << fpx) + one - this.selfXFpx) * deltaDistX) >> fpx;\n }\n if (rayDirY < 0) {\n mapStepY = -1;\n sideDistY = ((this.selfYFpx - (mapY << fpx)) * deltaDistY) >> fpx;\n } else {\n mapStepY = 1;\n sideDistY = (((mapY << fpx) + one - this.selfYFpx) * deltaDistY) >> fpx;\n }\n\n let color = 0\n\n while (true) {\n //jump to next map square, OR in x-direction, OR in y-direction\n if (sideDistX < sideDistY) {\n sideDistX += deltaDistX;\n mapX += mapStepX;\n sideWallHit = false;\n } else {\n sideDistY += deltaDistY;\n mapY += mapStepY;\n sideWallHit = true;\n }\n\n if (this.map.isOutsideMap(mapX, mapY))\n break\n color = this.map.getTile(mapX, mapY)\n if (this.map.isWall(mapX, mapY))\n break; // hit!\n }\n\n if (this.map.isOutsideMap(mapX, mapY))\n continue\n\n let perpWallDist = 0\n let wallX = 0\n if (!sideWallHit) {\n perpWallDist = Math.idiv(((mapX << fpx) - this.selfXFpx + (1 - mapStepX << fpx - 1)) << fpx, rayDirX)\n wallX = this.selfYFpx + (perpWallDist * rayDirY >> fpx);\n } else {\n perpWallDist = Math.idiv(((mapY << fpx) - this.selfYFpx + (1 - mapStepY << fpx - 1)) << fpx, rayDirY)\n wallX = this.selfXFpx + (perpWallDist * rayDirX >> fpx);\n }\n wallX &= FPX_MAX\n\n // color = (color - 1) * 2\n // if (sideWallHit) color++\n\n const tex = this.textures[color]\n if (!tex)\n continue\n\n let texX = (wallX * tex.width) >> fpx;\n // if ((!sideWallHit && rayDirX > 0) || (sideWallHit && rayDirY < 0))\n // texX = tex.width - texX - 1;\n\n const lineHeight = (this.wallHeightInView / perpWallDist)\n const drawEnd = lineHeight * this.viewZPos / this.tilemapScaleSize / fpx_scale;\n const horizontBreak = 1 - this.viewZPos / this.tilemapScaleSize / fpx_scale;\n if (perpWallDist !== lastDist && (texX !== lastTexX || mapX !== lastMapX || mapY !== lastMapY)) {//neighbor line of tex share same parameters\n \n drawStart = drawEnd - lineHeight * (this._wallZScale) ;\n drawHeight = (Math.ceil(drawEnd) - Math.ceil(drawStart) )\n drawStart += (SH >> 1)\n\n lastDist = perpWallDist\n lastTexX = texX\n lastMapX = mapX\n lastMapY = mapY\n }\n //fix start&end points to avoid regmatic between lines\n \n\n //if (x < SWHalf)\n // this.tempScreen.blitRow(x, drawStart, tex, texX, drawHeight)\n //else\n this.blitRowBreak(x, SHHalf + drawEnd - lineHeight, SHHalf + drawEnd, tex, texX, tex.height * horizontBreak)\n\n this.dist[x] = perpWallDist\n\n // background \n for (let y = 0; y < drawStart; y++ ){\n let backX = (backgroundOffset + x) % SW\n let c = sc.background.image.getPixel(backX,y)\n this.tempScreen.setPixel(x,y,c)\n }\n }\n //debug\n // info.setScore(control.millis()-ms)\n // this.tempScreen.print(backgroundOffset.toString(), 0,0,7 )\n // this.tempScreen.print([Math.roundWithPrecision(this._angle, 3)].join(), 20, 5)\n\n this.drawSprites()\n }\n\n drawSprites() {\n //debug\n // let msSprs=control.millis()\n /////////////////// sprites ///////////////////\n\n //for sprite\n const invDet = one2 / (this.planeX * this.dirYFpx - this.dirXFpx * this.planeY); //required for correct matrix multiplication\n\n this.sprites\n .filter((spr, i) => {\n const spriteX = this.sprXFx8(spr) - this.xFpx // this.selfXFpx\n const spriteY = this.sprYFx8(spr) - this.yFpx // this.selfYFpx\n this.angleSelfToSpr[spr.id] = Math.atan2(spriteX, spriteY)\n this.transformX[spr.id] = invDet * (this.dirYFpx * spriteX - this.dirXFpx * spriteY) >> fpx;\n this.transformY[spr.id] = invDet * (-this.planeY * spriteX + this.planeX * spriteY) >> fpx; //this is actually the depth inside the screen, that what Z is in 3D\n const angleInCamera = Math.atan2(this.transformX[spr.id] * this.fov, this.transformY[spr.id])\n return angleInCamera > -this.cameraRangeAngle && angleInCamera < this.cameraRangeAngle //(this.transformY[spr.id] > 0\n }).sort((spr1, spr2) => { // far to near\n return (this.transformY[spr2.id] - this.transformY[spr1.id])\n }).forEach((spr, index) => {\n //debug\n // this.tempScreen.print([spr.id,Math.roundWithPrecision(angle[spr.id],3)].join(), 0, index * 10 + 10,9)\n this.drawSprite(spr, index, this.transformX[spr.id], this.transformY[spr.id], this.angleSelfToSpr[spr.id])\n })\n\n //debug\n // info.setLife(control.millis() - msSprs+1)\n //this.tempScreen.print([Math.roundWithPrecision(this._angle,3)].join(), 20, 0)\n\n }\n\n registerOnSpriteDirectionUpdate(handler: (spr: Sprite, dir: number) => void) {\n this.onSpriteDirectionUpdateHandler = handler\n }\n\n drawSprite(spr: Sprite, index: number, transformX: number, transformY: number, myAngle: number) {\n const spriteScreenX = Math.ceil((SWHalf) * (1 - transformX / transformY)) - this.cameraOffsetX;\n const spriteScreenHalfWidth = Math.idiv((spr._width as any as number) / this.tilemapScaleSize / 2 * this.wallWidthInView, transformY) //origin: (texSpr.width / 2 << fpx) / transformY / this.fov / 3 * 2 * 4\n const spriteScreenLeft = spriteScreenX - spriteScreenHalfWidth\n const spriteScreenRight = spriteScreenX + spriteScreenHalfWidth\n\n //calculate drawing range in X direction\n //assume there is one range only\n let blitX = 0, blitWidth = 0\n for (let sprX = 0; sprX < SW; sprX++) {\n if (this.dist[sprX] > transformY) {\n if (blitWidth == 0)\n blitX = sprX\n blitWidth++\n } else if (blitWidth > 0) {\n if (blitX <= spriteScreenRight && blitX + blitWidth >= spriteScreenLeft)\n break\n else\n blitX = 0, blitWidth = 0;\n }\n }\n // this.tempScreen.print([this.getxFx8(spr), this.getyFx8(spr)].join(), 0,index*10+10)\n const blitXSpr = Math.max(blitX, spriteScreenLeft)\n const blitWidthSpr = Math.min(blitX + blitWidth, spriteScreenRight) - blitXSpr\n if (blitWidthSpr <= 0)\n return\n\n const lineHeight = Math.idiv(this.wallHeightInView, transformY)\n const drawStart = SHHalf + (lineHeight * ((this.viewZPos - this.spriteMotionZ[spr.id].p - (spr._height as any as number)) / this.tilemapScaleSize) >> fpx)\n\n //for textures=image[][], abandoned\n // const texSpr = spr.getTexture(Math.floor(((Math.atan2(spr.vxFx8, spr.vyFx8) - myAngle) / Math.PI / 2 + 2-.25) * spr.textures.length +.5) % spr.textures.length)\n //for deal in user code\n if (this.onSpriteDirectionUpdateHandler)\n this.onSpriteDirectionUpdateHandler(spr, ((Math.atan2(spr._vx as any as number, spr._vy as any as number) - myAngle) / Math.PI / 2 + 2 - .25))\n //for CharacterAnimation ext.\n // const iTexture = Math.floor(((Math.atan2(spr._vx as any as number, spr._vy as any as number) - myAngle) / Math.PI / 2 + 2 - .25) * 4 + .5) % 4\n // const characterAniDirs = [Predicate.MovingLeft,Predicate.MovingDown, Predicate.MovingRight, Predicate.MovingUp]\n // character.setCharacterState(spr, character.rule(characterAniDirs[iTexture]))\n //for this.spriteAnimations\n const texSpr = !this.spriteAnimations[spr.id] ? spr.image : this.spriteAnimations[spr.id].getFrameByDir(((Math.atan2(spr._vx as any as number, spr._vy as any as number) - myAngle) / Math.PI / 2 + 2 - .25))\n\n const sprTexRatio = texSpr.width / spriteScreenHalfWidth / 2\n helpers.imageBlit(\n this.tempScreen,\n blitXSpr,\n drawStart,\n blitWidthSpr,\n lineHeight * spr.height / this.tilemapScaleSize,\n texSpr,\n (blitXSpr - (spriteScreenX - spriteScreenHalfWidth)) * sprTexRatio\n ,\n 0,\n blitWidthSpr * sprTexRatio, texSpr.height, true, false)\n\n screen.fill(0)\n const fpx_div_transformy = Math.roundWithPrecision(transformY / 4 / fpx_scale, 2)\n const height = (SH / fpx_div_transformy)\n const blitXSaySrc = ((blitX - spriteScreenX) * fpx_div_transformy) + SWHalf\n const blitWidthSaySrc = (blitWidth * fpx_div_transformy)\n\n //sprite\n // screen.drawImage(texSpr, SWHalf-texSpr.width/2, SHHalf)\n //sayText\n const sayRender = this.sayRederers[spr.id]\n if (sayRender) {\n if (this.sayEndTimes[spr.id] && control.millis() > this.sayEndTimes[spr.id]) {\n this.sayRederers[spr.id] = undefined\n } else {\n this.tempSprite.x = SWHalf\n this.tempSprite.y = SHHalf + 2\n this.camera.drawOffsetX = 0\n this.camera.drawOffsetY = 0\n sayRender.draw(screen, this.camera, this.tempSprite)\n }\n }\n //particle\n const particle = this.spriteParticles[spr.id]\n if (particle) {\n if (particle.lifespan) {\n //debug\n // this.tempScreen.print([spr.id].join(), 0,index*10+10)\n this.tempSprite.x = SWHalf\n this.tempSprite.y = SHHalf + spr.height\n this.camera.drawOffsetX = 0//spr.x-SWHalf\n this.camera.drawOffsetY = 0//spr.y-SH\n particle.__draw(this.camera)\n } else {\n this.spriteParticles[spr.id] = undefined\n }\n }\n //update screen for this spr\n // const sayTransformY = \n if (blitXSaySrc <= 0) { //imageBlit considers negative value as 0\n helpers.imageBlit(\n this.tempScreen,\n spriteScreenX - SWHalf / fpx_div_transformy, drawStart - height / 2, (blitWidthSaySrc + blitXSaySrc) / fpx_div_transformy, height,\n screen,\n 0, 0, blitWidthSaySrc + blitXSaySrc, SH, true, false)\n } else {\n helpers.imageBlit(\n this.tempScreen,\n // blitX, drawStart - height / 2 , blitWidth, height,\n blitX, drawStart - height / 2, blitWidth, height,\n screen,\n blitXSaySrc, 0, blitWidthSaySrc, SH,\n true, false)\n }\n }\n }\n\n //%fixedinstance\n export const raycastingRender = new Render.RayCastingRender()\n}\n\n","tilemap.g.jres":"{\n \"transparency16\": {\n \"data\": \"hwQQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\",\n \"mimeType\": \"image/x-mkcd-f4\",\n \"tilemapTile\": true\n },\n \"tile1\": {\n \"data\": \"hwQQABAAAABBbaLCvW3tfU1tosK9be19TW2iwr1t7X1NbaLCvW3tfU1tosK9be19TW2iwr1t7X1NbaLCvW3tfU1tosK9be19TW2iwr1t7X1NbaLCvW3tfU1tosK9be19TW2iwr1t7X1NbaLCvW3tfU1tosK9be19TW2iwr1t7X1NbaLCvW3tfQ==\",\n \"mimeType\": \"image/x-mkcd-f4\",\n \"tilemapTile\": true,\n \"displayName\": \"myTile\"\n },\n \"level1\": {\n \"id\": \"level1\",\n \"mimeType\": \"application/mkcd-tilemap\",\n \"data\": \"MTAxMDAwMTAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDEwMTAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMTAxMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAxMDEwMjAyMDIwMzAyMDIwMjAyMDIwMjAyMDIwMjAyMDEwMTAyMDIwMjAzMDIwMjAzMDMwMzAzMDMwMjAyMDIwMTAxMDIwMjAyMDIwMzAzMDIwMjAyMDMwMzAyMDIwMjAxMDEwMjAyMDMwMzAyMDMwMjAyMDMwMzAzMDIwMjAyMDEwMTAyMDIwMzAzMDMwMzAzMDMwMzAyMDIwMzAyMDIwMTAxMDIwMjAyMDIwMzAzMDMwMzAyMDIwMzAzMDIwMjAxMDEwMjAyMDIwMjAzMDMwMjAyMDIwMzAzMDIwMjAyMDEwMTAyMDIwMjAyMDIwMjAyMDMwMzAzMDIwMjAyMDIwMTAxMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAxMDEwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDEwMTAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMjIyMjIyMjIyMjIyMjIyMjAyMDAwMDAwMDAwMDAwMjAwMjAwMDAwMDAwMDAwMDIwMDIwMDAwMDAwMDAwMDAyMDAyMDAwMjAwMDAwMDAwMjAwMjAwMDIwMDAwMDAwMDIwMDIwMDIwMDIwMDAwMDAyMDAyMjAwMjAyMDAwMDAwMjAwMjIwMjIwMjAwMDAwMDIwMDIwMDAwMDAwMDAwMDAyMDAyMDAwMDAwMDAwMDAwMjAwMjAwMDAwMDAwMDAwMDIwMDIwMDAwMDAwMDAwMDAyMDAyMDAwMDAwMDAwMDAwMjAwMjAwMDAwMDAwMDAwMDIwMjIyMjIyMjIyMjIyMjIyMg==\",\n \"tileset\": [\n \"myTiles.transparency16\",\n \"sprites.castle.tileGrass2\",\n \"sprites.castle.tileDarkGrass2\",\n \"sprites.builtin.forestTiles0\"\n ],\n \"displayName\": \"level\"\n },\n \"*\": {\n \"mimeType\": \"image/x-mkcd-f4\",\n \"dataEncoding\": \"base64\",\n \"namespace\": \"myTiles\"\n }\n}","tilemap.g.ts":"","pxt.json":"{\n \"name\": \"3d floor and background\",\n \"description\": \"\",\n \"dependencies\": {\n \"device\": \"*\"\n },\n \"files\": [\n \"main.blocks\",\n \"main.ts\",\n \"README.md\",\n \"assets.json\",\n \"_animation.ts\",\n \"_render_blocks.ts\",\n \"_render_raycasting.ts\",\n \"tilemap.g.jres\",\n \"tilemap.g.ts\"\n ],\n \"targetVersions\": {\n \"branch\": \"v1.10.36\",\n \"tag\": \"v1.10.36\",\n \"commits\": \"https://github.com/microsoft/pxt-arcade/commits/3a5a7e13703edf0412c17bdb9b1469e282a5b31b\",\n \"target\": \"1.10.36\",\n \"pxt\": \"8.3.9\"\n },\n \"preferredEditor\": \"blocksprj\"\n}\n"}}],"shares":[],"lastSaveTime":1762273213347}