475 lines
19 KiB
CoffeeScript
475 lines
19 KiB
CoffeeScript
|
|
# 00000000 000 0000000 000 000 00000000 00000000
|
|
# 000 000 000 000 000 000 000 000 000 000
|
|
# 00000000 000 000000000 00000 0000000 0000000
|
|
# 000 000 000 000 000 000 000 000
|
|
# 000 0000000 000 000 000 00000000 000 000
|
|
|
|
Bot = require './bot'
|
|
|
|
forward_key = "UP"
|
|
backward_key = "DOWN"
|
|
turn_left_key = "LEFT"
|
|
turn_right_key = "RIGHT"
|
|
shoot_key = "SPACE"
|
|
jump_key = "CTRL"
|
|
push_key = "SHIFT"
|
|
look_up_key = "HOME"
|
|
look_down_key = "END"
|
|
view_key = "PAGEDOWN"
|
|
|
|
# KikiActionKey actionKeyMapping[] =
|
|
|
|
class Player extends Bot
|
|
|
|
constructor: ->
|
|
|
|
super
|
|
|
|
@look_action = null
|
|
@look_angle = 0.0
|
|
@new_dir_sgn = 1.0
|
|
@rotate = 0
|
|
|
|
@recorder = null
|
|
@playback = null
|
|
|
|
# @flags[KDL_KEYHANDLER_FLAG_HANDLES_RELEASE] = true
|
|
|
|
@addAction new KikiAction @, ACTION_LOOK_UP, "look up", 220
|
|
@addAction new KikiAction @, ACTION_LOOK_DOWN, "look down", 220
|
|
@addAction new KikiAction @, ACTION_LOOK_RESET, "look reset", 60
|
|
|
|
@addEventWithName "keyset"
|
|
@addEventWithName "keyset failed"
|
|
@addEventWithName "landed"
|
|
|
|
# @projection = new KLightingProjection (90.0)
|
|
# @projection.updateViewport()
|
|
# @projection.getLight().setCutoff (90.0)
|
|
# @projection.getLight().setAttenuation (1.0, 0.0, 0.05)
|
|
|
|
# Controller.player_status->setStatus status
|
|
|
|
|
|
getActionForKey: (keyName) ->
|
|
index = 0
|
|
while actionKeyMapping[index].actionName
|
|
if keyName == actionKeyMapping[index].keyName
|
|
return actionKeyMapping[index].actionName
|
|
index++
|
|
|
|
return ""
|
|
|
|
getKeyForAction: (actionName) ->
|
|
index = 0
|
|
while actionKeyMapping[index].actionName
|
|
if actionName == actionKeyMapping[index].actionName
|
|
return actionKeyMapping[index].keyName
|
|
index++
|
|
|
|
return ""
|
|
|
|
setKeyForAction: (keyName, actionName) ->
|
|
index = 0
|
|
while actionKeyMapping[index].actionName
|
|
if actionName == actionKeyMapping[index].actionName
|
|
actionKeyMapping[index].keyName = keyName
|
|
index++
|
|
|
|
recordKeyForAction: (actionName) ->
|
|
RecordingActionName = actionName
|
|
KeyRecorder.startRecordingSequence @, @setRecordedKey, 1
|
|
|
|
setRecordedKey: (keyName) ->
|
|
index = 0
|
|
while (actionKeyMapping[index].actionName)
|
|
if keyName == actionKeyMapping[index].keyName and actionKeyMapping[index].actionName != RecordingActionName
|
|
setKeyForAction "", actionKeyMapping[index].actionName
|
|
index += 1
|
|
setKeyForAction keyName, RecordingActionName
|
|
getEventWithName("keyset").triggerActions()
|
|
|
|
updatePosition: () ->
|
|
if (move_action)
|
|
relTime = (Controller.getTime() - move_action.getStart()) / move_action.getDuration()
|
|
if (relTime <= 1.0)
|
|
switch move_action.getId()
|
|
when ACTION_FORWARD
|
|
current_position = position + relTime * getDir()
|
|
when ACTION_FALL
|
|
current_position = position - relTime * getUp()
|
|
when ACTION_JUMP_FORWARD
|
|
current_position = position + (1.0 - Math.cos(Math.PI/2 * relTime)) * getDir() + Math.cos(Math.PI/2 - Math.PI/2 * relTime) * getUp()
|
|
when ACTION_FALL_FORWARD
|
|
current_position = position + Math.cos(Math.PI/2 - Math.PI/2 * relTime) * getDir() + (1.0 - Math.cos(Math.PI/2 * relTime)) * -getUp()
|
|
|
|
getProjection: () ->
|
|
# smooth camera movement a little bit
|
|
posDelta = Controller.getSpeed() / 10.0
|
|
projection.setPosition ((1.0 - posDelta) * projection.getPosition() + posDelta * current_position)
|
|
|
|
KVector playerDir = getCurrentDir()
|
|
KVector playerUp = current_orientation.rotate (KVector(0,1,0)).normal()
|
|
|
|
if (look_angle) # player is looking up or down
|
|
projection.setXVector (playerUp.cross (playerDir).normal())
|
|
look_rot = KQuaternion.rotationAroundVector look_angle, projection.getXVector()
|
|
projection.setYVector (look_rot.rotate (playerUp))
|
|
projection.setZVector (look_rot.rotate (-playerDir))
|
|
else
|
|
# smooth camera rotation a little bit
|
|
lookDelta = (2.0 - projection.getZVector() * playerDir) * Controller.getSpeed() / 50.0
|
|
KVector newLookVector = (1.0 - lookDelta) * projection.getZVector() - lookDelta * playerDir
|
|
newLookVector.normalize()
|
|
|
|
projection.setXVector (playerUp.cross(newLookVector).normal())
|
|
projection.setYVector (playerUp)
|
|
projection.setZVector (newLookVector)
|
|
|
|
return projection
|
|
|
|
getBehindProjection: () ->
|
|
updatePosition()
|
|
|
|
playerDir = getCurrentDir()
|
|
playerUp = current_orientation.rotate(KVector(0,1,0)).normal()
|
|
|
|
# find a valid camera position
|
|
botToCamera = (playerUp - 2 * playerDir)
|
|
min_f = botToCamera.length()
|
|
botToCamera.normalize()
|
|
|
|
min_f = Math.min world.getWallDistanceForRay(current_position, botToCamera), min_f
|
|
cameraPos = current_position + kMax(min_f, 0.72) * botToCamera
|
|
cameraPos = world.getInsideWallPosWithDelta cameraPos, 0.2
|
|
|
|
# smooth camera movement a little bit
|
|
posDelta = 0.2
|
|
projection.setPosition ((1.0 - posDelta) * projection.getPosition() + posDelta * cameraPos)
|
|
|
|
if (look_angle)
|
|
projection.setXVector(playerUp.cross(playerDir).normal())
|
|
KQuaternion look_rot = KQuaternion.rotationAroundVector(look_angle, projection.getXVector())
|
|
projection.setYVector(look_rot.rotate(playerUp))
|
|
projection.setZVector(look_rot.rotate(-playerDir))
|
|
else
|
|
# smooth camera rotation a little bit
|
|
lookDelta = 0.3
|
|
KVector newLookVector =(1.0 - lookDelta) * projection.getZVector() - lookDelta * playerDir
|
|
newLookVector.normalize()
|
|
|
|
projection.setZVector(newLookVector)
|
|
projection.setXVector(playerUp.cross(newLookVector).normal())
|
|
projection.setYVector(newLookVector.cross(projection.getXVector()).normal())
|
|
|
|
return projection
|
|
|
|
getFollowProjection: () ->
|
|
cameraPos = projection.getPosition() # current camera position
|
|
desiredDistance = 2.0 # desired distance from camera to bot
|
|
|
|
updatePosition()
|
|
|
|
playerPos = current_position # desired look pos
|
|
playerDir = getCurrentDir()
|
|
playerUp = current_orientation.rotate(KVector(0,1,0)).normal()
|
|
playerRight = playerDir.cross(playerUp).normal()
|
|
|
|
# .................................................................. camera follows bot
|
|
# first, adjust distance from camera to bot
|
|
|
|
botToCamera = cameraPos - playerPos # vector from bot to current pos
|
|
cameraBotDistance = botToCamera.length() # distance from camera to bot
|
|
|
|
if cameraBotDistance >= desiredDistance
|
|
difference = cameraBotDistance - desiredDistance
|
|
delta = (difference*difference)/400.0 # weight for following speed
|
|
cameraPos = (1.0 - delta) * cameraPos + delta * playerPos
|
|
else
|
|
difference = desiredDistance - cameraBotDistance
|
|
delta = difference/20.0 # weight for negative following speed
|
|
cameraPos = (1.0 - delta) * cameraPos + delta * (playerPos + desiredDistance * botToCamera.normal())
|
|
|
|
# .................................................................. refining camera position
|
|
# second, rotate around bot
|
|
|
|
botToCamera = cameraPos - playerPos
|
|
KVector botToCameraNormal = botToCamera.normal()
|
|
|
|
# .................................................................. try view bot from above
|
|
# if camera below bot, rotate up
|
|
if (botToCameraNormal * playerUp < 0)
|
|
# calculate angle between player to camera vector and player up vector
|
|
verticalAngle = RAD2DEG (acos (kMinMax(-1.0, 1.0, botToCameraNormal * playerUp))) - 90.0
|
|
cameraPos = playerPos + KQuaternion.rotationAroundVector(verticalAngle/40.0, botToCameraNormal.cross(playerUp)).rotate(botToCamera)
|
|
|
|
botToCamera = cameraPos - playerPos
|
|
botToCameraNormal = (cameraPos - playerPos).normal()
|
|
|
|
rot_factor = 1.0
|
|
wall_distance = world.getWallDistanceForPos (playerPos + botToCamera)
|
|
if wall_distance < 0.5
|
|
# .................................................................. apiercing walls
|
|
|
|
if (wall_distance < 0.2)
|
|
cameraPos = world.getInsideWallPosWithDelta cameraPos, 0.2
|
|
botToCamera = cameraPos - playerPos
|
|
botToCameraNormal = (cameraPos - playerPos).normal()
|
|
|
|
rot_factor = 0.5 / (wall_distance-0.2)
|
|
|
|
# .................................................................. try view bot from behind
|
|
# calculate horizontal angle between bot orientation and vector to camera
|
|
mappedToXZ ((botToCamera - playerUp * (botToCamera * playerUp)).normal())
|
|
horizontalAngle = RAD2DEG (acos (kMinMax(-1.0, 1.0, -playerDir * mappedToXZ)))
|
|
if (botToCameraNormal * playerRight > 0)
|
|
horizontalAngle = -horizontalAngle
|
|
|
|
cameraPos = playerPos + KQuaternion.rotationAroundVector(horizontalAngle / (rot_factor * 400.0), playerUp).rotate botToCamera
|
|
|
|
botToCamera = cameraPos - playerPos
|
|
botToCameraNormal = botToCamera.normal()
|
|
|
|
# .................................................................. finally, set the position
|
|
|
|
projection.setPosition cameraPos
|
|
|
|
# .................................................................. refining camera orientation
|
|
|
|
# slowly adjust look direction by interpolating current and desired directions
|
|
lookDelta = 2.0 - projection.getZVector() * botToCameraNormal
|
|
lookDelta *= lookDelta / 30.0
|
|
KVector newLookVector = (1.0 - lookDelta) * projection.getZVector() + lookDelta * botToCameraNormal
|
|
newLookVector.normalize()
|
|
|
|
# slowly adjust up vector by interpolating current and desired up vectors
|
|
upDelta = 2.0 - projection.getYVector() * playerUp
|
|
upDelta *= upDelta / 100.0
|
|
KVector newRightVector = ((1.0 - upDelta) * projection.getYVector() + upDelta * playerUp).cross(newLookVector)
|
|
newRightVector.normalize()
|
|
KVector newUpVector = newLookVector.cross(newRightVector).normal()
|
|
|
|
# finished interpolations, update camera matrix
|
|
projection.setZVector (newLookVector)
|
|
projection.setXVector (newRightVector)
|
|
projection.setYVector (newUpVector)
|
|
|
|
return projection
|
|
|
|
initAction: (action) ->
|
|
actionId = action.getId()
|
|
switch actionId
|
|
when ACTION_CLIMB_DOWN, ACTION_FORWARD
|
|
status.addMoves(1)
|
|
when ACTION_TURN_LEFT, ACTION_TURN_RIGHT
|
|
Controller.sound.playSound(KikiSound.BOT_MOVE)
|
|
when ACTION_JUMP, ACTION_JUMP_FORWARD
|
|
status.addMoves(actionId == ACTION_JUMP ? 1 : 2)
|
|
Controller.sound.playSound(KikiSound.BOT_JUMP)
|
|
|
|
KikiBot.initAction(action)
|
|
|
|
performAction: (action) ->
|
|
relTime = action.getRelativeTime()
|
|
|
|
switch (action.getId())
|
|
when ACTION_NOOP then return
|
|
|
|
when ACTION_LOOK_UP
|
|
look_angle = relTime * -90.0
|
|
|
|
when ACTION_LOOK_DOWN
|
|
look_angle = relTime * 90.0
|
|
|
|
when ACTION_LOOK_RESET
|
|
if look_angle > 0
|
|
look_angle = Math.min look_angle, (1.0-relTime) * 90.0
|
|
else
|
|
look_angle = Math.max look_angle, (1.0-relTime) * -90.0
|
|
else
|
|
KikiBot.performAction(action)
|
|
|
|
finishAction: (action) ->
|
|
actionId = action.getId()
|
|
|
|
if (actionId == ACTION_LOOK_RESET)
|
|
look_action = null
|
|
look_angle = 0.0
|
|
else
|
|
if (action == move_action) # move finished, update direction
|
|
dir_sgn = new_dir_sgn
|
|
|
|
if (actionId != ACTION_LOOK_UP and actionId != ACTION_LOOK_DOWN)
|
|
KikiBot.finishAction(action)
|
|
|
|
if (actionId == ACTION_TURN_LEFT or actionId == ACTION_TURN_RIGHT)
|
|
if (rotate)
|
|
rotate_action = getActionWithId (rotate)
|
|
rotate_action.reset()
|
|
Controller.timer_event.addAction (rotate_action)
|
|
|
|
die: () ->
|
|
Controller.removeKeyHandler (this)
|
|
KikiBot.die()
|
|
Controller.displayText("game over")
|
|
Controller.sound.playSound (KikiSound.BOT_DEATH)
|
|
world.setCameraMode (KikiWorld.CAMERA_FOLLOW)
|
|
|
|
reborn: () ->
|
|
Controller.addKeyHandler (this)
|
|
died = false
|
|
|
|
reset: () ->
|
|
KikiBot.reset()
|
|
Controller.timer_event.removeActionsOfObject *
|
|
|
|
look_action = null
|
|
look_angle = 0.0
|
|
new_dir_sgn = 1.0
|
|
rotate = 0
|
|
|
|
recorder = null
|
|
playback = null
|
|
|
|
saveRecorder: () ->
|
|
if @recorder
|
|
@recorder.save()
|
|
@recorder = null
|
|
|
|
startRecorder: (file) ->
|
|
if @recorder
|
|
saveRecorder()
|
|
@recorder = new KikiRecorder file
|
|
|
|
#define KEY_HANDLED if (recorder) recorder->recordKey (key) return true
|
|
|
|
handleKey: (key) ->
|
|
keyName = key.getUnmodifiedName()
|
|
|
|
if (keyName == forward_key or keyName == backward_key)
|
|
move = true # try to move as long as the key is not released
|
|
|
|
if (move_action == null) # player is currently not performing a move action
|
|
# forward or backward direction
|
|
new_dir_sgn = dir_sgn = (key.getUnmodifiedName() == backward_key) ? -1 : 1
|
|
|
|
moveBot() # perform new move action (depending on environment)
|
|
else
|
|
new_dir_sgn = (keyName == backward_key) ? -1 : 1
|
|
|
|
KEY_HANDLED
|
|
|
|
if (keyName == turn_left_key or keyName == turn_right_key)
|
|
rotate = (keyName == turn_left_key) ? ACTION_TURN_LEFT : ACTION_TURN_RIGHT
|
|
|
|
if (rotate_action == null and spiked == false) # player is not performing a rotation and unspiked
|
|
rotate_action = getActionWithId rotate
|
|
Controller.timer_event.addAction rotate_action
|
|
|
|
KEY_HANDLED
|
|
|
|
if key.name == jump_key
|
|
jump = true # switch to jump mode until jump_key released
|
|
jump_once = true
|
|
KEY_HANDLED
|
|
|
|
if key.name == push_key
|
|
push = true
|
|
KEY_HANDLED
|
|
|
|
if keyName == shoot_key
|
|
if (shoot == false)
|
|
shoot = true
|
|
Controller.timer_event.addAction (getActionWithId (ACTION_SHOOT))
|
|
|
|
KEY_HANDLED
|
|
|
|
if keyName == look_up_key or keyName == look_down_key
|
|
if (!look_action)
|
|
look_action = getActionWithId ((key.name == look_up_key) ? ACTION_LOOK_UP : ACTION_LOOK_DOWN)
|
|
look_action.reset()
|
|
Controller.timer_event.addAction (look_action)
|
|
KEY_HANDLED
|
|
|
|
if keyName == view_key
|
|
world.changeCameraMode()
|
|
KEY_HANDLED
|
|
|
|
return false
|
|
|
|
#define KEY_RELEASE_HANDLED if (recorder) recorder->recordKeyRelease (key) return true
|
|
|
|
handleKeyRelease: (key) ->
|
|
keyName = key.getUnmodifiedName()
|
|
|
|
if (keyName == shoot_key)
|
|
Controller.timer_event.removeAction (getActionWithId(ACTION_SHOOT))
|
|
shoot = false
|
|
KEY_RELEASE_HANDLED
|
|
|
|
if (keyName == forward_key or keyName == backward_key)
|
|
move = false
|
|
KEY_RELEASE_HANDLED
|
|
|
|
if key.name == jump_key
|
|
jump = false
|
|
if jump_once
|
|
if (move_action == null and world.isUnoccupiedPos (position + getUp()))
|
|
jump_once = false
|
|
move_action = getActionWithId (ACTION_JUMP)
|
|
Controller.sound.playSound (KikiSound.BOT_JUMP)
|
|
Controller.timer_event.addAction (move_action)
|
|
KEY_RELEASE_HANDLED
|
|
|
|
if keyName == turn_left_key or keyName == turn_right_key
|
|
rotate = 0
|
|
KEY_RELEASE_HANDLED
|
|
|
|
if key.name == push_key
|
|
push = false
|
|
KEY_RELEASE_HANDLED
|
|
|
|
if keyName == look_down_key or keyName == look_up_key
|
|
if (look_action and look_action.getId() != ACTION_LOOK_RESET)
|
|
Controller.timer_event.removeAction (look_action)
|
|
look_action = getActionWithId (ACTION_LOOK_RESET)
|
|
Controller.timer_event.addAction (look_action)
|
|
KEY_RELEASE_HANDLED
|
|
|
|
if keyName == view_key
|
|
KEY_RELEASE_HANDLED
|
|
|
|
return false
|
|
|
|
display: () ->
|
|
if world.getCameraMode() != KikiWorld.CAMERA_INSIDE or world.getEditMode()
|
|
# glPushMatrix()
|
|
# current_position.glTranslate()
|
|
render()
|
|
# glPopMatrix()
|
|
|
|
getBodyColor: () ->
|
|
if world.getCameraMode() == KikiWorld.CAMERA_BEHIND
|
|
# static bodyColor
|
|
bodyColor = colors[KikiPlayer_base_color]
|
|
bodyColor.setAlpha(kMin(0.7, (projection.getPosition()-current_position).length()-0.4))
|
|
return bodyColor
|
|
|
|
return colors[KikiPlayer_base_color]
|
|
|
|
getTireColor: () ->
|
|
if world.getCameraMode() == KikiWorld.CAMERA_BEHIND
|
|
# static tireColor
|
|
tireColor = colors[KikiPlayer_tire_color]
|
|
tireColor.setAlpha(kMin(1.0, (projection.getPosition()-current_position).length()-0.4))
|
|
return tireColor
|
|
|
|
return colors[KikiPlayer_tire_color]
|
|
|
|
finishRotateAction: () ->
|
|
if (rotate_action)
|
|
rotate = false
|
|
finishAction(rotate_action)
|
|
|