This commit is contained in:
monsterkodi 2016-08-15 01:08:47 +02:00
parent 910ba6d2fa
commit 24af8b6cce
9 changed files with 219 additions and 199 deletions

View File

@ -75,21 +75,21 @@ class Action
log "action.reset #{@name}" log "action.reset #{@name}"
@start = 0 # world time @start = 0 # world time
@rest = 0 @rest = 0
@last = 0 # relative @last = 0 # relative (ms since @start)
@current = 0 # relative @current = 0 # relative (ms since @start)
#@event = null
takeRest: (action) -> takeOver: (action) ->
@current = action.rest log "takeOver #{action.rest} from #{action.name} this: #{@name}"
@current = action.current
@start = action.start @start = action.start
@last = 0 @last = action.last
@rest = 0 @rest = action.rest
keepRest: () -> keepRest: () ->
if @rest != 0 if @rest != 0
@current = @rest @current = @rest
@rest = 0 @rest = 0
# @last = 0
# @current = 0
getRelativeTime: -> @current / @getDuration() getRelativeTime: -> @current / @getDuration()
getRelativeDelta: -> (@current-@last) / @getDuration() getRelativeDelta: -> (@current-@last) / @getDuration()
@ -109,15 +109,17 @@ class Action
@finished() if @duration == 0 and @mode == Action.ONCE @finished() if @duration == 0 and @mode == Action.ONCE
else else
currentDiff = eventTime - @start currentDiff = eventTime - @start
if currentDiff >= @getDuration() msDur = @getDuration()
@current = @getDuration() if currentDiff >= msDur
@current = msDur
@start += @current # @start = msDur
@rest = eventTime - @start @rest = currentDiff - msDur
# log "action #{name} performWithEvent start #{@start} rest #{currentDiff}-#{msDur} = #{@rest}" if @name != 'noop'
@perform() @perform()
@last = 0 @last = 0
if @mode == Action.CONTINUOUS if @mode == Action.CONTINUOUS
log 'Action.CONTINUOUS'
@current = @rest @current = @rest
return return
event.removeAction @ if @mode == Action.ONCE event.removeAction @ if @mode == Action.ONCE
@ -125,7 +127,7 @@ class Action
@finish() @finish()
if @mode == Action.REPEAT if @mode == Action.REPEAT
if @current == @getDuration() # if keepRest wasn't called -> reset start and current values if @current >= @getDuration() # if keepRest wasn't called -> reset start and current values
@reset() @reset()
return return

View File

@ -17,29 +17,26 @@ class Bot extends Pushable
constructor: () -> constructor: () ->
@direction = new Quaternion @direction = new Vector
@orientation = new Quaternion @orientation = new Quaternion
@current_orientation = new Quaternion @current_orientation = new Quaternion
@rotate_orientation = new Quaternion @rotate_orientation = new Quaternion
@climb_orientation = new Quaternion @climb_orientation = new Quaternion
@rest_orientation = new Quaternion @rest_orientation = new Quaternion
tireRadius = 0.15 tireRadius = 0.05
nose = new THREE.ConeGeometry 0.404, 0.5, 32, 16, true nose = new THREE.ConeGeometry 0.404, 0.5, 32, 16, true
geom = new THREE.SphereGeometry 0.5, 32, 32, 16, Math.PI geom = new THREE.SphereGeometry 0.5, 32, 32, 16, Math.PI
geom = new THREE.SphereGeometry 0.5, 32, 32, 0, 2*Math.PI, 0, 2.2 geom = new THREE.SphereGeometry 0.5, 32, 32, 0, 2*Math.PI, 0, 2.2
noseMat = new THREE.Matrix4() noseMat = new THREE.Matrix4()
trans = new THREE.Vector3 0,-0.543,0 trans = new THREE.Vector3 0,-0.543,0
rot = new THREE.Quaternion().setFromEuler new THREE.Euler Vector.DEG2RAD(180), 0, 0 rot = new THREE.Quaternion().setFromEuler new THREE.Euler Vector.DEG2RAD(180), 0, 0
noseMat.compose trans, rot, new THREE.Vector3 1,1,1 noseMat.compose trans, rot, new THREE.Vector3 1,1,1
geom.merge nose, noseMat geom.merge nose, noseMat
geom.rotateX Vector.DEG2RAD -90 geom.rotateX Vector.DEG2RAD -90
# geom.mergeVertices()
# geom.computeFaceNormals()
# geom.computeVertexNormals()
geom.scale 0.7, 0.7, 0.7 geom.scale 0.7, 0.7, 0.7
botMat = new THREE.MeshPhongMaterial botMat = new THREE.MeshPhongMaterial
@ -48,30 +45,27 @@ class Bot extends Pushable
shading: THREE.SmoothShading shading: THREE.SmoothShading
roughness: 0.9 roughness: 0.9
metalness: 1 metalness: 1
transparent: true
opacity: 0.9
shininess: 5 shininess: 5
@mesh = new THREE.Mesh geom, botMat @mesh = new THREE.Mesh geom, botMat
geom = new THREE.TorusGeometry 0.5-tireRadius, tireRadius, 16, 16 geom = new THREE.TorusGeometry 0.5-tireRadius, tireRadius, 16, 32
geom.scale 1,1,2.5
tireMat = new THREE.MeshPhongMaterial tireMat = new THREE.MeshPhongMaterial
color: 0x000066 color: 0x000066
specular: 0x222255 specular: 0x222255
side: THREE.FrontSide side: THREE.FrontSide
shading: THREE.FlatShading shading: THREE.FlatShading
transparent: true
opacity: 0.7
shininess: 4 shininess: 4
@leftTire = new THREE.Mesh geom, tireMat @leftTire = new THREE.Mesh geom, tireMat
@leftTire.position.set 0.5-tireRadius,0,0 @leftTire.position.set 0.35,0,0
@leftTire.rotation.set 0, Vector.DEG2RAD(90), 0 @leftTire.rotation.set 0, Vector.DEG2RAD(90), 0
@mesh.add @leftTire @mesh.add @leftTire
@rightTire = new THREE.Mesh geom, tireMat @rightTire = new THREE.Mesh geom, tireMat
@rightTire.position.set -0.5+tireRadius,0,0 @rightTire.position.set -0.35,0,0
@rightTire.rotation.set 0, Vector.DEG2RAD(-90), 0 @rightTire.rotation.set 0, Vector.DEG2RAD(-90), 0
@mesh.add @rightTire @mesh.add @rightTire
@ -113,10 +107,20 @@ class Bot extends Pushable
@startTimedAction @getActionWithId(Action.NOOP), 500 @startTimedAction @getActionWithId(Action.NOOP), 500
getDown: -> @orientation.rotate(new Vector 0,1,0).neg()
getUp: -> @orientation.rotate(new Vector 0,1,0) # 0000000 000 00000000 00000000 0000000 000000000 000 0000000 000 000
getDir: -> @orientation.rotate(new Vector 0,0,1).mul @dir_sgn # 000 000 000 000 000 000 000 000 000 000 000 0000 000
getCurrentDir: -> @current_orientation.rotate(new Vector(0,0,1)).normal() # 000 000 000 0000000 0000000 000 000 000 000 000 000 0 000
# 000 000 000 000 000 000 000 000 000 000 000 000 0000
# 0000000 000 000 000 00000000 0000000 000 000 0000000 000 000
getDown: -> @orientation.rotate new Vector 0,-1,0
getUp: -> @orientation.rotate new Vector 0,1,0
getDir: -> @orientation.rotate new Vector 0,0,@dir_sgn
getCurrentDir: -> @current_orientation.rotate(new Vector 0,0,1).normal()
getCurrentUp: -> @current_orientation.rotate(new Vector 0,1,0).normal()
getCurrentLeft: -> @current_orientation.rotate(new Vector 1,0,0).normal()
addMoves: (m) -> @moves += m addMoves: (m) -> @moves += m
addHealth: (h) -> @health = Math.max @health+h addHealth: (h) -> @health = Math.max @health+h
@ -176,7 +180,7 @@ class Bot extends Pushable
initAction: (action) -> initAction: (action) ->
newPos = new Pos @position newPos = new Pos @position
log "initAction #{action.name} pos", newPos # log "initAction #{action.name} pos", newPos
switch action.id switch action.id
when Action.NOOP then return when Action.NOOP then return
@ -189,15 +193,13 @@ class Bot extends Pushable
if not @direction.isZero() if not @direction.isZero()
super action super action
return return
else newPos.add @getDown()
newPos.add @getDown()
break
else else
super action super action
return return
if not newPos.eql new Pos @position if not newPos.eql new Pos @position
log 'bot.initAction objectWillMoveToPos:', newPos # log 'bot.initAction objectWillMoveToPos:', newPos
world.objectWillMoveToPos @, newPos, action.getDuration() world.objectWillMoveToPos @, newPos, action.getDuration()
# 00000000 00000000 00000000 00000000 0000000 00000000 00 00 # 00000000 00000000 00000000 00000000 0000000 00000000 00 00
@ -213,7 +215,10 @@ class Bot extends Pushable
# log "Bot.performAction #{action.name} #{action.current} #{action.last} #{action.duration} id #{action.id}" # log "Bot.performAction #{action.name} #{action.current} #{action.last} #{action.duration} id #{action.id}"
# log "Bot.performAction #{action.name} #{relTime} #{dltTime} id #{action.id}" # log "Bot.performAction #{action.name} #{relTime} #{dltTime} id #{action.id}"
# cosFac = 1.0 - Math.cos(Math.PI/2 * relTime)
cosFac = Math.cos Math.PI/2 - Math.PI/2 * relTime
sinFac = Math.sin Math.PI/2 * relTime
# log "bot.performAction peform #{action.name} #{relTime} #{action.current} #{action.getDuration()}"
switch action.id switch action.id
when Action.SHOOT when Action.SHOOT
if relTime == 0 if relTime == 0
@ -225,25 +230,25 @@ class Bot extends Pushable
@left_tire_rot += @dir_sgn * dltTime @left_tire_rot += @dir_sgn * dltTime
@right_tire_rot += @dir_sgn * dltTime @right_tire_rot += @dir_sgn * dltTime
@current_position = @position.plus @getDir().mul relTime @current_position = @position.plus @getDir().mul(relTime)
# log 'bot.forward', @current_position # log 'bot.forward', @current_position
return return
when Action.JUMP when Action.JUMP
@current_position = @position.plus @getUp().mul Math.cos(Math.PI/2 - Math.PI/2 * relTime) @current_position = @position.plus @getUp().mul(sinFac)
return return
when Action.JUMP_FORWARD when Action.JUMP_FORWARD
@left_tire_rot += Math.cos(Math.PI/2 - Math.PI/2 * dltTime) @left_tire_rot += 1 - Math.cos(Math.PI/2 * dltTime)
@right_tire_rot += Math.cos(Math.PI/2 - Math.PI/2 * dltTime) @right_tire_rot += 1 - Math.cos(Math.PI/2 * dltTime)
@current_position = @position.plus @getDir().mul(1.0 - Math.cos(Math.PI/2 * relTime)).plus @getUp().mul Math.cos(Math.PI/2 - Math.PI/2 * relTime) @current_position = @position.plus @getDir().mul(relTime).plus @getUp().mul(sinFac)
return return
when Action.FALL_FORWARD when Action.FALL_FORWARD
@current_position = @position.plus @getDir().mul(Math.cos(Math.PI/2 - Math.PI/2 * relTime)).plus @getDown().mul (1.0 - Math.cos(Math.PI/2 * relTime)) @current_position = @position.plus @getDir().mul(cosFac).plus @getDown().mul(cosFac)
return return
when Action.FALL when Action.FALL
@ -251,14 +256,14 @@ class Bot extends Pushable
if not @direction.isZero() if not @direction.isZero()
super action super action
return return
@current_position = @position.plus @getDown().mul relTime @current_position = @position.plus @getDown().mul(relTime)
return return
when Action.CLIMB_UP when Action.CLIMB_UP
@left_tire_rot += @dir_sgn * dltTime/2 @left_tire_rot += @dir_sgn * dltTime/2
@right_tire_rot += @dir_sgn * dltTime/2 @right_tire_rot += @dir_sgn * dltTime/2
@climb_orientation = Quaternion.rotationAroundVector @dir_sgn * relTime * -90.0, new Vector(1,0,0) @climb_orientation = Quaternion.rotationAroundVector @dir_sgn * relTime * -90.0, new Vector 1,0,0
break break
when Action.CLIMB_DOWN when Action.CLIMB_DOWN
@ -378,7 +383,7 @@ class Bot extends Pushable
@startTimedAction @getActionWithId(Action.NOOP), 0 @startTimedAction @getActionWithId(Action.NOOP), 0
return return
if action.id == Action.PUSH #or action.id == Action.FALL # not @direction.isZero() if action.id == Action.PUSH and not @direction.isZero() #or action.id == Action.FALL # not @direction.isZero()
log 'super (Pushable) action!' log 'super (Pushable) action!'
super action super action
return return
@ -390,13 +395,13 @@ class Bot extends Pushable
# find next action depending on type of finished action and surrounding environment # find next action depending on type of finished action and surrounding environment
if action.id == Action.JUMP_FORWARD if action.id == Action.JUMP_FORWARD
forwardPos = @position.plus @getDir() forwardPos = @position.plus @getDir()
log 'jump forwardPos', forwardPos # log 'jump forwardPos', forwardPos
if world.isUnoccupiedPos forwardPos if world.isUnoccupiedPos forwardPos
# forward will be empty # forward will be empty
if world.isUnoccupiedPos forwardPos.minus @getUp() if world.isUnoccupiedPos forwardPos.minus @getUp()
# below forward will also be empty # below forward will also be empty
@move_action = @getActionWithId Action.FALL_FORWARD @move_action = @getActionWithId Action.FALL_FORWARD
@move_action.takeRest action # @move_action.takeRwest action
else else
@move_action = @getActionWithId Action.FORWARD @move_action = @getActionWithId Action.FORWARD
world.playSound 'BOT_LAND', @getPos(), 0.25 world.playSound 'BOT_LAND', @getPos(), 0.25
@ -404,7 +409,7 @@ class Bot extends Pushable
if world.isUnoccupiedPos @position.minus @getUp() # below is empty if world.isUnoccupiedPos @position.minus @getUp() # below is empty
@move_action = @getActionWithId Action.CLIMB_UP @move_action = @getActionWithId Action.CLIMB_UP
world.playSound 'BOT_LAND', @getPos(), 0.5 world.playSound 'BOT_LAND', @getPos(), 0.5
else if world.isUnoccupiedPos @position.minus @getUp() # below will be empty else if world.isUnoccupiedPos @position.plus @getDown() # below will be empty
log 'below will be empty!' log 'below will be empty!'
if @move # sticky if moving if @move # sticky if moving
if world.isUnoccupiedPos @position.plus @getDir() if world.isUnoccupiedPos @position.plus @getDir()
@ -420,9 +425,9 @@ class Bot extends Pushable
@move_action = @getActionWithId Action.CLIMB_UP @move_action = @getActionWithId Action.CLIMB_UP
if @move_action == null if @move_action == null
log 'fall!' log 'bot.actionFinished fall!'
@move_action = @getActionWithId Action.FALL @move_action = @getActionWithId Action.FALL
@move_action.takeRest action # @move_action.takeRest action
else if action.id == Action.FALL or action.id == Action.FALL_FORWARD # landed else if action.id == Action.FALL or action.id == Action.FALL_FORWARD # landed
log 'fall|forward!' log 'fall|forward!'
@ -432,14 +437,14 @@ class Bot extends Pushable
world.playSound 'BOT_LAND', @getPos() world.playSound 'BOT_LAND', @getPos()
if @move_action if @move_action
log 'move_action!' log "add move_action! #{@move_action.name}"
Timer.addAction @move_action Timer.addAction @move_action
return return
return if @rotate_action return if @rotate_action
if @move if @move or @jump
log '!move' log '!move or jump!'
@moveBot() @moveBot()
else else
@dir_sgn = 1 @dir_sgn = 1
@ -455,37 +460,38 @@ class Bot extends Pushable
moveBot: () -> moveBot: () ->
@move_action = null @move_action = null
# log "bot.moveBot @position", @position
# log "bot.moveBot @getDir", @getDir()
forwardPos = @position.plus @getDir() forwardPos = @position.plus @getDir()
# log "bot.moveBot", forwardPos if @move and (@jump or @jump_once) and # jump mode or jump activated while moving
if @jump or @jump_once and # jump mode or jump activated while moving
@dir_sgn == 1.0 and # and moving forward @dir_sgn == 1.0 and # and moving forward
world.isUnoccupiedPos @position.plus @getUp() # and above empty world.isUnoccupiedPos(@position.plus @getUp()) # and above empty
if world.isUnoccupiedPos forwardPos.plus @getUp() and if world.isUnoccupiedPos(forwardPos.plus @getUp()) and
world.isUnoccupiedPos forwardPos # forward and above forward also empty world.isUnoccupiedPos(forwardPos) # forward and above forward also empty
@move_action = @getActionWithId Action.JUMP_FORWARD @move_action = @getActionWithId Action.JUMP_FORWARD
else # no space to jump forward -> jump up else # no space to jump forward -> jump up
@move_action = @getActionWithId Action.JUMP @move_action = @getActionWithId Action.JUMP
else if world.isUnoccupiedPos forwardPos # forward is empty else if @move
log 'forward is empty' if world.isUnoccupiedPos forwardPos # forward is empty
if world.isUnoccupiedPos forwardPos.plus @getDown() log 'forward is empty'
# below forward also empty if world.isUnoccupiedPos forwardPos.plus @getDown()
@move_action = @getActionWithId Action.CLIMB_DOWN # below forward also empty
else # forward down is solid
@move_action = @getActionWithId Action.FORWARD
else # forward is not empty
log 'forward is not empty'
moveAction = @getActionWithId Action.FORWARD
if @push and world.mayObjectPushToPos @, forwardPos, moveAction.getDuration()
moveAction.reset()
# player in push mode and pushing object is possible
if world.isUnoccupiedPos forwardPos.plus @getDown() # below forward is empty
@move_action = @getActionWithId Action.CLIMB_DOWN @move_action = @getActionWithId Action.CLIMB_DOWN
else else # forward down is solid
@move_action = moveAction @move_action = @getActionWithId Action.FORWARD
else # just climb up else # forward is not empty
@move_action = @getActionWithId Action.CLIMB_UP log 'forward is not empty'
moveAction = @getActionWithId Action.FORWARD
if @push and world.mayObjectPushToPos @, forwardPos, moveAction.getDuration()
moveAction.reset()
# player in push mode and pushing object is possible
if world.isUnoccupiedPos forwardPos.plus @getDown() # below forward is empty
@move_action = @getActionWithId Action.CLIMB_DOWN
else
@move_action = moveAction
else # just climb up
@move_action = @getActionWithId Action.CLIMB_UP
else if @jump or @jump_once
if world.isUnoccupiedPos(@position.plus @getUp())
@move_action = @getActionWithId Action.JUMP
# reset the jump once flag (either we jumped or it's not possible to jump at current @position) # reset the jump once flag (either we jumped or it's not possible to jump at current @position)
@jump_once = false @jump_once = false

View File

@ -21,22 +21,20 @@ class Cage
side: THREE.FrontSide side: THREE.FrontSide
shading: THREE.SmoothShading shading: THREE.SmoothShading
shininess: 20 shininess: 20
geom = @wallTiles @gap
@cage = new THREE.Mesh geom, cageMat
@cage.translateX -0.5
@cage.translateY -0.5
@cage.translateZ -0.5
world.scene.add @cage
geom = @wallTiles 0 geom = @wallTiles 0
@raster = new THREE.Mesh geom, rasterMat @raster = new THREE.Mesh geom, rasterMat
@raster.translateX -0.5 @raster.translateX -0.5
@raster.translateY -0.5 @raster.translateY -0.5
@raster.translateZ -0.5 @raster.translateZ -0.5
world.scene.add @raster world.scene.add @raster
geom = @wallTiles @gap
@cage = new THREE.Mesh geom, cageMat
@cage.translateX -0.5
@cage.translateY -0.5
@cage.translateZ -0.5
world.scene.add @cage
del: -> del: ->
world.scene.remove @raster world.scene.remove @raster
@ -52,7 +50,7 @@ class Cage
s = 1-raster s = 1-raster
o = raster o = raster
i = -1 i = -1
offset = raster/10 offset = raster/20
z = offset z = offset
n = 1 n = 1

View File

@ -50,6 +50,7 @@ class Event
removeAction: (action) -> removeAction: (action) ->
action.event = null action.event = null
action.reset()
_.pull @actions, action _.pull @actions, action
_.pull @save_actions, action _.pull @save_actions, action
_.pull @finished_actions, action _.pull @finished_actions, action

View File

@ -30,8 +30,8 @@ module.exports =
] ]
create: -> create: ->
world.addObjectAtPos('KikiWall', world.decenter(0,0,3)) world.addObjectAtPos 'KikiWall', world.decenter 0,0,3
world.addObjectAtPos('KikiWall', world.decenter(0,-1,1)) world.addObjectAtPos 'KikiWall', world.decenter 0,-1,1
world.addObjectAtPos('KikiWall', world.decenter(0,-2,-1)) world.addObjectAtPos 'KikiWall', world.decenter 0,-2,-1
world.addObjectAtPos('KikiWall', world.decenter(0,-3,-3)) world.addObjectAtPos 'KikiWall', world.decenter 0,-3,-3

View File

@ -153,100 +153,89 @@ class Player extends Bot
getFollowProjection: () -> getFollowProjection: () ->
cameraPos = @projection.getPosition() camPos = @projection.getPosition()
desiredDistance = 2.0 # desired distance from camera to bot desiredDistance = 2.0 # desired distance from camera to bot
playerPos = @current_position # desired look pos playerPos = @current_position
playerDir = @getCurrentDir() playerDir = @getCurrentDir()
playerUp = @current_orientation.rotate new Vector(0,1,0).normal() playerUp = @getCurrentUp()
playerRight = playerDir.cross(playerUp).normal() playerLeft = @getCurrentLeft()
# ____________________________________________________ camera follows bot
# first, adjust distance from camera to bot # first, adjust distance from camera to bot
botToCamera = cameraPos.minus playerPos # vector from bot to current pos botToCamera = camPos.minus playerPos # vector from bot to current pos
cameraBotDistance = botToCamera.length() # distance from camera to bot cameraBotDistance = botToCamera.length() # distance from camera to bot
# log 'getFollowProjection 1', botToCamera, cameraPos, playerPos
if cameraBotDistance >= desiredDistance if cameraBotDistance >= desiredDistance
difference = cameraBotDistance - desiredDistance difference = cameraBotDistance - desiredDistance
delta = difference*difference/400.0 # weight for following speed delta = difference*difference/400.0 # weight for following speed
cameraPos = cameraPos.mul(1.0-delta).plus playerPos.mul delta camPos = camPos.mul(1.0-delta).plus playerPos.mul delta
else else
difference = desiredDistance - cameraBotDistance difference = desiredDistance - cameraBotDistance
delta = difference/20.0 # weight for negative following speed delta = difference/20.0 # weight for negative following speed
cameraPos = cameraPos.mul(1.0-delta).plus (playerPos.plus botToCamera.normal().mul desiredDistance).mul delta camPos = camPos.mul(1.0-delta).plus (playerPos.plus botToCamera.normal().mul desiredDistance).mul delta
# log 'getFollowProjection 2', botToCamera, cameraPos, playerPos
# ____________________________________________________ refining camera position
# second, rotate around bot # second, rotate around bot
botToCamera = cameraPos.minus playerPos botToCamera = camPos.minus playerPos
botToCameraNormal = botToCamera.normal() botToCameraNormal = botToCamera.normal()
# ____________________________________________________ try view bot from above
# if camera below bot, rotate up # if camera below bot, rotate up
if botToCameraNormal.dot(playerUp) < 0 if botToCameraNormal.dot(playerUp) < 0
# calculate angle between player to camera vector and player up vector # calculate angle between player to camera vector and player up vector
verticalAngle = Vector.RAD2DEG Math.acos(clamp(-1.0, 1.0, botToCameraNormal.dot playerUp)) - 90.0 verticalAngle = Vector.RAD2DEG Math.acos(clamp(-1.0, 1.0, botToCameraNormal.dot playerUp))
cameraPos = playerPos.plus Quaternion.rotationAroundVector(verticalAngle/40.0, botToCameraNormal.cross(playerUp)).rotate botToCamera log "verticalAngle #{verticalAngle}"
rotQuat = Quaternion.rotationAroundVector(verticalAngle/40.0, botToCameraNormal.cross(playerUp))
botToCamera = cameraPos.minus playerPos botToCamera = rotQuat.rotate botToCamera
botToCameraNormal = botToCamera.normal() botToCameraNormal = botToCamera.normal()
camPos = playerPos.plus botToCamera
# log 'getFollowProjection 3', botToCamera, cameraPos, playerPos
rotFactor = 1.0 rotFactor = 1.0
wall_distance = world.getWallDistanceForPos playerPos.plus botToCamera wall_distance = world.getWallDistanceForPos camPos
if wall_distance < 0.5 if wall_distance < 0.5 # try avoid piercing walls
# ____________________________________________________ piercing walls
if wall_distance < 0.2 if wall_distance < 0.2
cameraPos = world.getInsideWallPosWithDelta cameraPos, 0.2 camPos = world.getInsideWallPosWithDelta camPos, 0.2
botToCamera = cameraPos.minus playerPos botToCamera = camPos.minus playerPos
botToCameraNormal = botToCamera.normal() botToCameraNormal = botToCamera.normal()
rotFactor = 0.5 / (wall_distance-0.2) rotFactor = 0.5 / (wall_distance-0.2)
log "rotFactor #{rotFactor}"
# ____________________________________________________ try view bot from behind
# try view bot from behind
# calculate horizontal angle between bot orientation and vector to camera # calculate horizontal angle between bot orientation and vector to camera
mappedToXZ = (botToCamera.minus playerUp.mul(botToCamera.dot playerUp)).normal() mappedToXZ = (botToCamera.minus playerUp.mul(botToCamera.dot playerUp)).normal()
horizontalAngle = Vector.RAD2DEG Math.acos(clamp(-1.0, 1.0, -playerDir.dot mappedToXZ)) horizontalAngle = Vector.RAD2DEG Math.acos(clamp(-1.0, 1.0, -playerDir.dot mappedToXZ))
if botToCameraNormal.dot(playerRight) > 0 if botToCameraNormal.dot(playerLeft) < 0
horizontalAngle = -horizontalAngle horizontalAngle = -horizontalAngle
log "horizontalAngle #{horizontalAngle}"
cameraPos = playerPos.plus Quaternion.rotationAroundVector(horizontalAngle/(rotFactor*400.0), playerUp).rotate botToCamera rotQuat = Quaternion.rotationAroundVector horizontalAngle/(rotFactor*400.0), playerUp
camPos = playerPos.plus rotQuat.rotate botToCamera
botToCamera = cameraPos.minus playerPos
botToCamera = camPos.minus playerPos
botToCameraNormal = botToCamera.normal() botToCameraNormal = botToCamera.normal()
# ____________________________________________________ finally, set the position # finally, set the position
@projection.setPosition cameraPos @projection.setPosition camPos
# log 'cameraPos:', cameraPos
# ____________________________________________________ refining camera orientation
# slowly adjust look direction by interpolating current and desired directions # slowly adjust look direction by interpolating current and desired directions
lookDelta = 2.0 - @projection.getZVector().dot botToCameraNormal lookDelta = @projection.getZVector().dot botToCameraNormal
lookDelta *= lookDelta / 30.0 lookDelta *= lookDelta / 30.0
# newLookVector = @projection.getZVector().mul(1.0-lookDelta).plus botToCameraNormal.mul(lookDelta)
newLookVector = @projection.getZVector().mul(1.0-lookDelta).plus botToCameraNormal.neg().mul(lookDelta) newLookVector = @projection.getZVector().mul(1.0-lookDelta).plus botToCameraNormal.neg().mul(lookDelta)
newLookVector.normalize() newLookVector.normalize()
# slowly adjust up vector by interpolating current and desired up vectors # slowly adjust up vector by interpolating current and desired up vectors
upDelta = 2.0 - @projection.getYVector().dot playerUp upDelta = @projection.getYVector().dot playerUp
upDelta *= upDelta / 100.0 upDelta *= upDelta / 100.0
newRightVector = (@projection.getYVector().mul(1.0-upDelta).plus playerUp.mul(upDelta)).cross newLookVector newUpVector = @projection.getYVector().mul(1.0-upDelta).plus playerUp.mul(upDelta)
newRightVector.normalize() newUpVector.normalize()
newUpVector = newLookVector.cross(newRightVector).normal()
newLeftVector = newUpVector.cross newLookVector
# finished interpolations, update camera matrix # finished interpolations, update camera matrix
@projection.setZVector newLookVector @projection.setXVector newLeftVector
@projection.setXVector newRightVector
@projection.setYVector newUpVector @projection.setYVector newUpVector
# log 'Player.getFollowProjection', @projection.getPosition() @projection.setZVector newLookVector
@projection @projection
# 0000000 0000000 000000000 000 0000000 000 000 # 0000000 0000000 000000000 000 0000000 000 000
@ -349,6 +338,14 @@ class Player extends Bot
@new_dir_sgn = @dir_sgn = (combo == @key.backward) and -1 or 1 @new_dir_sgn = @dir_sgn = (combo == @key.backward) and -1 or 1
@moveBot() # perform new move action (depending on environment) @moveBot() # perform new move action (depending on environment)
else else
if @move_action.name == '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
action.takeOver @move_action
Timer.removeAction @move_action
@move_action = action
Timer.addAction @move_action
@new_dir_sgn = (combo == @key.backward) and -1 or 1 @new_dir_sgn = (combo == @key.backward) and -1 or 1
return true return true
@ -365,6 +362,23 @@ class Player extends Bot
when @key.jump when @key.jump
@jump = true # switch to jump mode until jump_key released @jump = true # switch to jump mode until jump_key released
@jump_once = true @jump_once = true
if not @move_action?
@moveBot() # perform new move action (depending on environment)
@jump_once = false
else
if @move_action.name == 'move forward' and @move_action.getRelativeTime() < 0.6 or
@move_action.name == 'climb down' and @move_action.getRelativeTime() < 0.4
if world.isUnoccupiedPos @position.plus @getUp()
if world.isUnoccupiedPos @position.plus @getUp().plus @getDir()
action = @getActionWithId Action.JUMP_FORWARD
else
action = @getActionWithId Action.JUMP
action.takeOver @move_action
Timer.removeAction @move_action
@move_action = action
Timer.addAction @move_action
else
log "cant jump #{@move_action.name}"
return true return true
when @key.push when @key.push

View File

@ -14,7 +14,7 @@ class Pushable extends Item
constructor: () -> constructor: () ->
super super
@pusher = null @pusher = null
@direction = new Vector() @direction = new Vector
@addAction new Action @, Action.NOOP, "noop" @addAction new Action @, Action.NOOP, "noop"
@addAction new Action @, Action.PUSH, "push" @addAction new Action @, Action.PUSH, "push"
@ -34,21 +34,22 @@ class Pushable extends Item
initAction: (action) -> initAction: (action) ->
switch action.id switch action.id
when Action.FALL when Action.FALL
log 'fall!' log 'Pushable.initAction FALL direction:', @direction
world.objectWillMoveToPos @, @position.plus(@direction), action.getDuration() world.objectWillMoveToPos @, @position.plus(@direction), action.getDuration()
performAction: (action) -> performAction: (action) ->
# log "Pushable.performAction action #{action.name}"
switch action.id switch action.id
when Action.PUSH, Action.FALL when Action.PUSH, Action.FALL
@setCurrentPosition @position.plus @direction.mul action.getRelativeTime() @setCurrentPosition @position.plus @direction.mul action.getRelativeTime()
finishAction: (action) -> finishAction: (action) ->
log "Pushable.finishAction #{action.name}" # log "Pushable.finishAction #{action.name}"
switch action.id switch action.id
when Action.PUSH, Action.FALL when Action.PUSH, Action.FALL
@move_action = null @move_action = null
world.objectMovedFromPos @, @position world.objectMovedFromPos @, @position
log "Pushable.finishAction setPosition #{@current_position}" # log "Pushable.finishAction setPosition #{@current_position}"
@setPosition @current_position @setPosition @current_position
actionFinished: (action) -> actionFinished: (action) ->
@ -57,17 +58,17 @@ class Pushable extends Item
if action.id == Action.PUSH if action.id == Action.PUSH
if @pusher instanceof Bot if @pusher instanceof Bot
gravityDir = pusher.getDown() gravityDir = @pusher.getDown()
else if pusher instanceof Bomb else if @pusher instanceof Bomb
if @ instanceof Bot if @ instanceof Bot
if @direction == @getUp() if @direction.eql @getUp()
# bots don't fall through bomb splitter # bots don't fall through bomb splitter
@direction.reset() @direction.reset()
return return
else else
gravityDir = @getDown() # bots pushed by bombs fall down gravityDir = @getDown() # bots pushed by bombs fall down
else else
direction.reset() @direction.reset()
return # objects pushed by bombs don't fall return # objects pushed by bombs don't fall
if world.isUnoccupiedPos @position.plus gravityDir if world.isUnoccupiedPos @position.plus gravityDir
@ -76,6 +77,6 @@ class Pushable extends Item
Timer.addAction @move_action Timer.addAction @move_action
else else
@direction.reset() @direction.reset()
world.playSound @landing_sound, position world.playSound @landing_sound, @position
module.exports = Pushable module.exports = Pushable

View File

@ -26,6 +26,7 @@ class Timer
@removeAction: (a) -> @removeAction: (a) ->
log "Timer.removeAction #{a.name}" log "Timer.removeAction #{a.name}"
a.reset()
@event.removeAction a @event.removeAction a
module.exports = Timer module.exports = Timer

View File

@ -47,7 +47,7 @@ class World extends Actor
constructor: (@view) -> constructor: (@view) ->
@speed = 6 @speed = 4
@raster_size = 0.05 @raster_size = 0.05
# @camera_mode = World.CAMERA_INSIDE # @camera_mode = World.CAMERA_INSIDE
@camera_mode = World.CAMERA_BEHIND @camera_mode = World.CAMERA_BEHIND
@ -130,37 +130,36 @@ class World extends Actor
# 0000000 00000000 0 00000000 0000000 0000000 # 0000000 00000000 0 00000000 0000000 0000000
@levelList = [ @levelList = [
# intro # intro
"start", # "start",
# "steps", "steps",
#"move", "electro", "elevate", #"move", "electro", "elevate",
# "throw", # "throw",
# easy # easy
"gold", "jump", "escape", "gears", "gold", "jump", "escape", "gears",
# "gamma", # "gamma",
"cube", "switch", "borg", "cube", "switch", "borg",
"mini", "mini",
# "blocks", # "blocks",
"bombs", "sandbox", "energy", "maze", "love", "bombs", "sandbox", "energy", "maze", "love",
# medium # medium
"towers", "edge", "random", "plate", "nice", "entropy", "towers", "edge", "random", "plate", "nice", "entropy",
# owen hay's levels (TODO: sort in) # owen hay's levels (TODO: sort in)
"grasp", "fallen", "cheese", "invisimaze", "spiral", "grasp", "fallen", "cheese", "invisimaze", "spiral",
# difficult # difficult
"slick", "bridge", "flower", "stones", "walls", "grid", "slick", "bridge", "flower", "stones", "walls", "grid",
"rings", "rings",
# "core", # "core",
"bronze", "pool", "bronze", "pool",
# tough # tough
"hidden", "church", "hidden", "church",
# "strange", # "strange",
"mesh", "columns", "machine", "mesh", "columns", "machine",
# very hard # very hard
# "neutron", # "neutron",
"captured", "circuit", "regal", "conductor", "evil", "captured", "circuit", "regal", "conductor", "evil",
# outro # outro
"mutants", "mutants"]
]
# import the levels # import the levels
for levelName in @levelList for levelName in @levelList
@ -719,17 +718,18 @@ class World extends Actor
else else
log "world.objectWillMoveToPos [WARNING] already occupied:", pos log "world.objectWillMoveToPos [WARNING] already occupied:", pos
@unsetObject object # remove object from cell grid if object != @player
# log 'tmpObject at new pos', pos @unsetObject object # remove object from cell grid
tmpObject = new TmpObject object # insert tmp object at new pos # log 'tmpObject at new pos', pos
tmpObject.setPosition pos tmpObject = new TmpObject object # insert tmp object at new pos
tmpObject.time = duration tmpObject.setPosition pos
@addObjectAtPos tmpObject, pos tmpObject.time = duration
# log 'tmpObject at old pos', object.position @addObjectAtPos tmpObject, pos
tmpObject = new TmpObject object # insert tmp object at old pos # log 'tmpObject at old pos', object.position
tmpObject.setPosition object.position tmpObject = new TmpObject object # insert tmp object at old pos
tmpObject.time = -duration tmpObject.setPosition object.position
@addObjectAtPos tmpObject, object.getPos() tmpObject.time = -duration
@addObjectAtPos tmpObject, object.getPos()
updateStatus: () -> updateStatus: () ->
@ -807,9 +807,6 @@ class World extends Actor
# log "unmapMsTime #{mapped} #{@speed} #{parseInt mapped * @speed/10.0}" # log "unmapMsTime #{mapped} #{@speed} #{parseInt mapped * @speed/10.0}"
parseInt mapped * @speed/10.0 parseInt mapped * @speed/10.0
getRelativeTime: -> @frame_time % (10000/@speed)/(10000.0/@speed)
getRelativeDelta: -> (@frame_time - @last_time)/(10000.0/@speed)
continuous: (cb) -> continuous: (cb) ->
new Action new Action
func: cb func: cb