diff --git a/coffee/bot.coffee b/coffee/bot.coffee index 276c270..ddbf0d9 100644 --- a/coffee/bot.coffee +++ b/coffee/bot.coffee @@ -107,7 +107,6 @@ class Bot extends Pushable @addEventWithName "died" @startTimedAction @getActionWithId(Action.NOOP), 500 - # 0000000 000 00000000 00000000 0000000 000000000 000 0000000 000 000 # 000 000 000 000 000 000 000 000 000 000 000 0000 000 @@ -371,7 +370,7 @@ class Bot extends Pushable # 000 000 000 000 000 0000000 000 000 00000000 0000000 actionFinished: (action) -> - log "bot.actionFinished #{action.name} #{action.id}" + # log "bot.actionFinished #{action.name} #{action.id}" # if @isDead() # log "DIE!" @@ -391,7 +390,7 @@ class Bot extends Pushable super action return - if @move_action # action was not a move action -> return + if @move_action? # action was not a move action -> return # log 'bot.actionFinished was not a move action!' return @@ -409,9 +408,10 @@ class Bot extends Pushable @move_action = @getActionWithId Action.CLIMB_UP world.playSound 'BOT_LAND', @getPos(), 0.5 else if world.isUnoccupiedPos @position.plus @getDown() # below will be empty - log 'below empty', world.isUnoccupiedPos(@position.plus @getDown()), @position.plus @getDown() + # log 'bot.actionFinished below empty', world.isUnoccupiedPos(@position.plus @getDown()), @position.plus @getDown() if @move # sticky if moving - if world.isUnoccupiedPos @position.plus @getDir() # forward will be empty + if world.isUnoccupiedPos @position.plus @getDir() # forward will be empty + log 'bot.actionFinished forward empty' if world.isOccupiedPos @position.plus @getDir().minus @getUp() # below forward is solid occupant = world.getOccupantAtPos @position.plus @getDir().minus @getUp() if not occupant? or not occupant?.isSlippery() @@ -421,7 +421,7 @@ class Bot extends Pushable if not occupant? or not occupant?.isSlippery() @move_action = @getActionWithId Action.CLIMB_UP - if @move_action == null + if not @move_action? @move_action = @getActionWithId Action.FALL @direction = @getDown() @@ -431,13 +431,15 @@ class Bot extends Pushable else world.playSound 'BOT_LAND', @getPos() - if @move_action + if @move_action? Timer.addAction @move_action return - return if @rotate_action + return if @rotate_action? - if @move or @jump + @fixOrientationAndPosition() + + if @move or @jump or @jump_once @moveBot() else @dir_sgn = 1 @@ -445,6 +447,10 @@ class Bot extends Pushable # keep action chain flowinwg in order to detect environment changes # @startTimedAction @getActionWithId(Action.NOOP), 0 + fixOrientationAndPosition: -> + @setPosition @current_position.round() + @setOrientation @current_orientation.round() + # 00 00 0000000 000 000 00000000 # 000 000 000 000 000 000 000 # 000000000 000 000 000 000 0000000 diff --git a/coffee/item.coffee b/coffee/item.coffee index dfac47e..246a673 100644 --- a/coffee/item.coffee +++ b/coffee/item.coffee @@ -51,7 +51,6 @@ class Item extends Actor setOrientation: (q) -> @current_orientation = @orientation = new Quaternion q # log "item.setOrientation:", @orientation - @orientation setCurrentPosition: (p) -> @current_position = p setCurrentOrientation: (q) -> @current_orientation = q diff --git a/coffee/levels/flower.coffee b/coffee/levels/flower.coffee index 98d4c06..2db58af 100644 --- a/coffee/levels/flower.coffee +++ b/coffee/levels/flower.coffee @@ -16,7 +16,7 @@ module.exports = player: coordinates: [3,0,1] nostatus: 0 - orientation: roty0 + orientation: rot0 exits: [ name: "exit" active: 1 diff --git a/coffee/levels/jump.coffee b/coffee/levels/jump.coffee index 23c1e92..5bc73c4 100644 --- a/coffee/levels/jump.coffee +++ b/coffee/levels/jump.coffee @@ -1,3 +1,10 @@ + +# 000 000 000 00 00 00000000 +# 000 000 000 000 000 000 000 +# 000 000 000 000000000 00000000 +# 000 000 000 000 000 0 000 000 +# 0000000 0000000 000 000 000 + module.exports = name: "jump" scheme: "red_scheme" @@ -12,7 +19,9 @@ module.exports = you can attach to a stone when falling if you move into its direction """ - player: position: [0,0,5] + player: + position: [0,0,5] + orientation: rotx270 exits: [ name: "exit" active: 1 diff --git a/coffee/levels/steps.coffee b/coffee/levels/steps.coffee index 85120cf..1b0eb21 100644 --- a/coffee/levels/steps.coffee +++ b/coffee/levels/steps.coffee @@ -22,7 +22,7 @@ module.exports = """ player: position: [0,0,-5] - orientation: roty0 + orientation: rot0 exits: [ name: "exit" active: 1 diff --git a/coffee/lib/quaternion.coffee b/coffee/lib/quaternion.coffee index 6e38a5d..18b34f8 100644 --- a/coffee/lib/quaternion.coffee +++ b/coffee/lib/quaternion.coffee @@ -9,7 +9,7 @@ log = require '/Users/kodi/s/ko/js/tools/log' Vector = require './vector' class Quaternion - + constructor: (w=1, x=0, y=0, z=0) -> if w instanceof Vector @x = w.x @@ -33,13 +33,62 @@ class Quaternion @w = w if Number.isNaN @x throw new Error - - @rotationAroundVector: (theta, vector) -> - v = new Vector vector - v.normalize() - t = Vector.DEG2RAD(theta)/2.0 - s = Math.sin t - new Quaternion(Math.cos(t), v.x*s, v.y*s, v.z*s).normalize() + + copy: -> new Quaternion @ + clone: (q) -> + @x = q.x + @y = q.y + @z = q.z + @w = q.w + @ + + round: -> + @normalize() + minDist = 1000 + minQuat = null + up = @rotate Vector.unitY + back = @rotate Vector.unitZ + for q in [ Quaternion.XupY + Quaternion.XupZ + Quaternion.XdownY + Quaternion.XdownZ + Quaternion.YupX + Quaternion.YupZ + Quaternion.YdownX + Quaternion.YdownZ + Quaternion.ZupX + Quaternion.ZupY + Quaternion.ZdownX + Quaternion.ZdownY + Quaternion.minusXupY + Quaternion.minusXupZ + Quaternion.minusXdownY + Quaternion.minusXdownZ + Quaternion.minusYupX + Quaternion.minusYupZ + Quaternion.minusYdownX + Quaternion.minusYdownZ + Quaternion.minusZupX + Quaternion.minusZupY + Quaternion.minusZdownX + Quaternion.minusZdownY + ] + upDiff = 1 - up.dot q.rotate Vector.unitY + backDiff = 1 - back.dot q.rotate Vector.unitZ + l = upDiff + backDiff + # log "length #{upDiff} #{backDiff} #{q.name} #{l}" + if l < minDist + minDist = l + minQuat = q + if l < 0.0001 + break + log "differ a lot! #{minDist}" if minDist > 0.05 + return @clone minQuat + + euler: -> [ + Vector.RAD2DEG Math.atan2 2*(@w*@x+@y*@z), 1-2*(@x*@x+@y*@y) + Vector.RAD2DEG Math.asin 2*(@w*@y-@z*@x) + Vector.RAD2DEG Math.atan2 2*(@w*@z+@x*@y), 1-2*(@y*@y+@z*@z)] add: (quat) -> @w += quat.w @@ -55,6 +104,10 @@ class Quaternion @z -= quat.z @ + minus: (quat) -> @copy().sub quat + + dot: (q) -> @x*q.x + @y*q.y + @z*q.z + @w*q.w + rotate: (v) -> qv = new Quaternion v rq = @mul qv.mul @getConjugate() @@ -73,9 +126,9 @@ class Quaternion l = Math.sqrt @w*@w + @x*@x + @y*@y + @z*@z if l != 0.0 @w /= l - @x = -x/l - @y = -y/l - @z = -z/l + @x = -@x/l + @y = -@y/l + @z = -@z/l @ isZero: -> @x==@y==@z==0 and @w==1 @@ -90,9 +143,9 @@ class Quaternion @z = -@z @ - getNormal: -> new Quaternion(@).normalize() - getConjugate: -> new Quaternion(@).conjugate() - getInverse: -> new Quaternion(@).invert() + getNormal: -> @copy().normalize() + getConjugate: -> @copy().conjugate() + getInverse: -> @copy().invert() neg: -> new Quaternion -@w,-@x,-@y,-@z vector: -> new Vector @x, @y, @z length: -> Math.sqrt @w*@w + @x*@x + @y*@y + @z*@z @@ -152,4 +205,99 @@ class Quaternion scale0 * @y + scale1 * to1[1], scale0 * @z + scale1 * to1[2] + @rotationAroundVector: (theta, vector) -> + v = new Vector vector + v.normalize() + t = Vector.DEG2RAD(theta)/2.0 + s = Math.sin t + (new Quaternion Math.cos(t), v.x*s, v.y*s, v.z*s).normalize() + + @rotationFromEuler: (x,y,z) -> + x = Vector.DEG2RAD x + y = Vector.DEG2RAD y + z = Vector.DEG2RAD z + q=new Quaternion Math.cos(x/2) * Math.cos(y/2) * Math.cos(z/2) + Math.sin(x/2) * Math.sin(y/2) * Math.sin(z/2), + Math.sin(x/2) * Math.cos(y/2) * Math.cos(z/2) - Math.cos(x/2) * Math.sin(y/2) * Math.sin(z/2), + Math.cos(x/2) * Math.sin(y/2) * Math.cos(z/2) + Math.sin(x/2) * Math.cos(y/2) * Math.sin(z/2), + Math.cos(x/2) * Math.cos(y/2) * Math.sin(z/2) - Math.sin(x/2) * Math.sin(y/2) * Math.cos(z/2) + q.normalize() + + @rot_0 = new Quaternion() + + @rot_90_X = @rotationAroundVector 90, Vector.unitX + @rot_90_Y = @rotationAroundVector 90, Vector.unitY + @rot_90_Z = @rotationAroundVector 90, Vector.unitZ + @rot_180_X = @rotationAroundVector 180, Vector.unitX + @rot_180_Y = @rotationAroundVector 180, Vector.unitY + @rot_180_Z = @rotationAroundVector 180, Vector.unitZ + @rot_270_X = @rotationAroundVector 270, Vector.unitX + @rot_270_Y = @rotationAroundVector 270, Vector.unitY + @rot_270_Z = @rotationAroundVector 270, Vector.unitZ + + @XupY = @rot_270_Y + @XupZ = @rot_90_X.mul @rot_270_Y + @XdownY = @rot_180_X.mul @rot_270_Y + @XdownZ = @rot_270_X.mul @rot_270_Y + + @YupX = @rot_90_Y.mul @rot_90_X + @YupZ = @rot_90_X + @YdownX = @rot_270_Y.mul @rot_90_X + @YdownZ = @rot_180_Y.mul @rot_90_X + + @ZupX = @rot_90_Z.mul @rot_180_X + @ZupY = @rot_180_Z.mul @rot_180_X + @ZdownX = @rot_270_Z.mul @rot_180_X + @ZdownY = @rot_180_X + + @minusXupY = @rot_90_Y + @minusXupZ = @rot_90_X.mul @rot_90_Y + @minusXdownY = @rot_180_X.mul @rot_90_Y + @minusXdownZ = @rot_270_X.mul @rot_90_Y + + @minusYupX = @rot_270_Y.mul @rot_270_X + @minusYupZ = @rot_180_Y.mul @rot_270_X + @minusYdownX = @rot_90_Y.mul @rot_270_X + @minusYdownZ = @rot_270_X + + @minusZupX = @rot_270_Z + @minusZupY = @rot_0 + @minusZdownX = @rot_90_Z + @minusZdownY = @rot_180_Z + + @rot_0.name = '@rot_0' + @rot_90_X.name = '@rot_90_X' + @rot_90_Y.name = '@rot_90_Y' + @rot_90_Z.name = '@rot_90_Z' + @rot_180_X.name = '@rot_180_X' + @rot_180_Y.name = '@rot_180_Y' + @rot_180_Z.name = '@rot_180_Z' + @rot_270_X.name = '@rot_270_X' + @rot_270_Y.name = '@rot_270_Y' + @rot_270_Z.name = '@rot_270_Z' + + @XupY.name = '@XupY' + @XupZ.name = '@XupZ' + @XdownY.name = '@XdownY' + @XdownZ.name = '@XdownZ' + @YupX.name = '@YupX' + @YupZ.name = '@YupZ' + @YdownX.name = '@YdownX' + @YdownZ.name = '@YdownZ' + @ZupX.name = '@ZupX' + @ZupY.name = '@ZupY' + @ZdownX.name = '@ZdownX' + @ZdownY.name = '@ZdownY' + @minusXupY.name = '@minusXupY' + @minusXupZ.name = '@minusXupZ' + @minusXdownY.name = '@minusXdownY' + @minusXdownZ.name = '@minusXdownZ' + @minusYupX.name = '@minusYupX' + @minusYupZ.name = '@minusYupZ' + @minusYdownX.name = '@minusYdownX' + @minusYdownZ.name = '@minusYdownZ' + @minusZupX.name = '@minusZupX' + @minusZupY.name = '@minusZupY' + @minusZdownX.name = '@minusZdownX' + @minusZdownY.name = '@minusZdownY' + module.exports = Quaternion \ No newline at end of file diff --git a/coffee/player.coffee b/coffee/player.coffee index 97c1be4..bbfbbbf 100644 --- a/coffee/player.coffee +++ b/coffee/player.coffee @@ -331,7 +331,7 @@ class Player extends Bot @new_dir_sgn = @dir_sgn = (combo == @key.backward) and -1 or 1 @moveBot() # perform new move action (depending on environment) else - if @move_action.name == 'jump' and @move_action.getRelativeTime() < 1 + if @move_action.id == Action.JUMP and @move_action.getRelativeTime() < 1 if world.isUnoccupiedPos(@position.plus(@getUp()).plus(@getDir())) and world.isUnoccupiedPos(@position.plus(@getDir())) # forward and above forward also empty action = @getActionWithId Action.JUMP_FORWARD @@ -353,12 +353,16 @@ class Player extends Bot @jump = true # switch to jump mode until jump_key released @jump_once = true if not @move_action? - @moveBot() # perform new move action (depending on environment) + @moveBot() # perform jump action (depending on environment) @jump_once = false else - if @move_action.id == Action.MOVE and @move_action.getRelativeTime() < 0.6 or + # log 'jump:moving' + if @move_action.id == Action.FORWARD and @move_action.getRelativeTime() < 0.6 or @move_action.id == Action.CLIMB_DOWN and @move_action.getRelativeTime() < 0.4 + # abort current move and jump instead + # log 'jump:move or climb down' if world.isUnoccupiedPos @position.plus @getUp() + # log 'jump:can do' if world.isUnoccupiedPos @position.plus @getUp().plus @getDir() action = @getActionWithId Action.JUMP_FORWARD else @@ -366,6 +370,7 @@ class Player extends Bot action.takeOver @move_action Timer.removeAction @move_action @move_action = action + @jump_once = false Timer.addAction @move_action else if @move_action.id in [Action.JUMP, Action.JUMP_FORWARD] @jump_once = false @@ -414,13 +419,7 @@ class Player extends Bot when @key.jump @jump = false - @jump_once = false - # if @jump_once - # if not @move_action? and world.isUnoccupiedPos @position.plus @getUp() - # @jump_once = false - # @move_action = @getActionWithId Action.JUMP - # world.playSound 'BOT_JUMP' - # Timer.addAction @move_action + # @jump_once = false return true when @key.left, @key.right @@ -443,11 +442,11 @@ class Player extends Bot false - # 0000000 000 0000000 00000000 000 0000000 000 000 - # 000 000 000 000 000 000 000 000 000 000 000 - # 000 000 000 0000000 00000000 000 000000000 00000 - # 000 000 000 000 000 000 000 000 000 - # 0000000 000 0000000 000 0000000 000 000 000 + # 0000000 000 0000000 00000000 000 0000000 000 000 + # 000 000 000 000 000 000 000 000 000 000 000 + # 000 000 000 0000000 00000000 000 000000000 00000 + # 000 000 000 000 000 000 000 000 000 + # 0000000 000 0000000 000 0000000 000 000 000 step: (step) -> super step diff --git a/coffee/pushable.coffee b/coffee/pushable.coffee index 7474176..6f6f29b 100644 --- a/coffee/pushable.coffee +++ b/coffee/pushable.coffee @@ -42,7 +42,7 @@ class Pushable extends Item initAction: (action) -> switch action.id when Action.FALL - log 'Pushable.initAction FALL direction:', @direction + # log 'Pushable.initAction FALL direction:', @direction world.objectWillMoveToPos @, @position.plus(@direction), action.getDuration() performAction: (action) -> @@ -58,7 +58,7 @@ class Pushable extends Item @move_action = null targetPos = @current_position.round() world.objectMoved @, @position, targetPos - log "Pushable.finishAction setPosition:", targetPos + # log "Pushable.finishAction setPosition:", targetPos @setPosition targetPos actionFinished: (action) -> @@ -83,7 +83,7 @@ class Pushable extends Item if world.isUnoccupiedPos @position.plus gravityDir @direction = gravityDir @move_action = @getActionWithId Action.FALL - log 'Pushable.actionFinished below empty, fall!' + # log 'Pushable.actionFinished below empty, fall!' Timer.addAction @move_action else @direction.reset() diff --git a/coffee/world.coffee b/coffee/world.coffee index 9b3c777..00cbf6c 100644 --- a/coffee/world.coffee +++ b/coffee/world.coffee @@ -131,17 +131,17 @@ class World extends Actor @initGlobal: () -> return if @levels? - - global.rot0 = new Quaternion() - global.rotz90 = Quaternion.rotationAroundVector 90, Vector.unitZ - global.rotz180 = Quaternion.rotationAroundVector 180, Vector.unitZ - global.roty0 = Quaternion.rotationAroundVector 0, Vector.unitY - global.roty90 = Quaternion.rotationAroundVector 90, Vector.unitY - global.roty180 = Quaternion.rotationAroundVector 180, Vector.unitY - global.roty270 = Quaternion.rotationAroundVector 270, Vector.unitY - global.rotx90 = Quaternion.rotationAroundVector 90, Vector.unitX - global.rotx180 = Quaternion.rotationAroundVector 180, Vector.unitX - global.rotx270 = Quaternion.rotationAroundVector 270, Vector.unitX + + global.rot0 = Quaternion.rot_0 + global.rotx90 = Quaternion.rot_90_X + global.roty90 = Quaternion.rot_90_Y + global.rotz90 = Quaternion.rot_90_Z + global.rotx180 = Quaternion.rot_180_X + global.roty180 = Quaternion.rot_180_Y + global.rotz180 = Quaternion.rot_180_Z + global.rotx270 = Quaternion.rot_270_X + global.roty270 = Quaternion.rot_270_Y + global.rotz270 = Quaternion.rot_270_Z @levels = new Levels @@ -269,27 +269,6 @@ class World extends Actor log 'world.levelFinished' # saves the current level status in highscore file # highscore.levelFinished world.level_name, Controller.player.getStatus().getMoves() - - # 00000000 00000000 0000000 00000000 000000000 00000000 000 0000000 000 000 00000000 00000000 - # 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 - # 0000000 0000000 0000000 0000000 000 00000000 000 000000000 00000 0000000 0000000 - # 000 000 000 000 000 000 000 000 000 000 000 000 000 000 - # 000 000 00000000 0000000 00000000 000 000 0000000 000 000 000 00000000 000 000 - - resetPlayer: () -> - # reset the player to it's original position and orientation - log 'world.resetPlayer', @dict.player - if @dict.player.resetOrientation? - @player.setOrientation @dict.player.resetOrientation - else if @dict.player.orientation? - @player.setOrientation @dict.player.orientation - else - @player.setOrientation rot0 - - if @dict.player.resetPosition? - world.moveObjectToPos @player, world.decenter @dict.player.resetPosition - else - world.moveObjectToPos @player, world.decenter @dict.player.position # 000 000 0000000 000 000 000000000 # 000 000 000 000 000 000