library Ghostly requires mouse/*, Blackboard*/
globals
// we use GetCameraBound* for these
private real CAMERA_MIN_X
private real CAMERA_MAX_X
private real CAMERA_MIN_Y
private real CAMERA_MAX_Y
private constant boolean CAN_PASS_THROUGH_TERRAIN = false
// The camera gets stuck around this height (camera z bound).
private constant real CAMERA_MAX_Z = 4192.0
private constant real DT = 1.0 / 32.0
endglobals
private function dd takes string s returns nothing
call BJDebugMsg(s)
endfunction
private function b2s takes boolean b returns string
if b then
return "T"
else
return "F"
endif
endfunction
// Without this function this library would suck!
// http://www.wc3c.net/showthread.php?p=1029058
// <3 Tc =)
private function SetCameraZ takes real z returns nothing
set z = GetCameraField(CAMERA_FIELD_ZOFFSET) + z - GetCameraTargetPositionZ()
call SetCameraField(CAMERA_FIELD_ZOFFSET, z, -0.01)
call SetCameraField(CAMERA_FIELD_ZOFFSET, z, 0.01)
endfunction
globals
private constant integer KEY_LEFT = 0
private constant integer KEY_RIGHT = 1
private constant integer KEY_DOWN = 2
private constant integer KEY_UP = 3
private boolean array is_player_key_down[/*player*/ 12][/*key*/ 4]
private location loc = Location(0.0, 0.0)
endglobals
struct Ghostly extends array
private boolean enabled
private player pp
real x
real y
real z
real speed
real rot
real rot_inc
integer rot_d
real aoa
real aoa_inc
integer aoa_d
real fov
real farz
// previous mouse screen position
integer pmsx
integer pmsy
private static integer first_timer_offset
timer t
private static code on_cmd_handler
private static code on_key_handler
private static code update_handler
private static method onInit takes nothing returns nothing
local thistype this
local trigger t
local trigger t2
call ExecuteFunc("s__" + "thistype" + "_set_handlers")
set CAMERA_MIN_X = GetCameraBoundMinX()
set CAMERA_MAX_X = GetCameraBoundMaxX()
set CAMERA_MIN_Y = GetCameraBoundMinY()
set CAMERA_MAX_Y = GetCameraBoundMaxY()
set t = CreateTrigger()
set t2 = CreateTrigger()
set this = 0
loop
exitwhen this >= bj_MAX_PLAYERS
set this.pp = Player(this)
call TriggerRegisterPlayerChatEvent(t2, this.pp, "", false)
// set this.enabled = false // default value
// set this.x = 0.0
set this.y = -530.0
set this.z = 800.0
set this.speed = 600.0
set this.rot = 90.0
set this.rot_inc = 3.0
set this.rot_d = 3
set this.aoa = -56.0
set this.aoa_inc = 2.5
set this.aoa_d = 2
set this.fov = 120.0
set this.farz = 3000.0
// set this.pmsx = 0
// set this.pmsy = 0
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_LEFT_DOWN)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_LEFT_UP)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_RIGHT_DOWN)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_RIGHT_UP)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_DOWN_DOWN)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_DOWN_UP)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_UP_DOWN)
call TriggerRegisterPlayerEvent(t, this.pp, EVENT_PLAYER_ARROW_UP_UP)
set this = this + 1
endloop
set this = 0
loop
exitwhen this >= bj_MAX_PLAYERS
set this.t = CreateTimer()
set this = this + 1
endloop
set first_timer_offset = GetHandleId(thistype(0).t)
call TriggerAddAction(t, on_key_handler)
call TriggerAddAction(t2, on_cmd_handler)
endmethod
method enable takes boolean flag returns nothing
if flag then
set this.enabled = true
call TimerStart(this.t, DT, true, update_handler)
else
set this.enabled = false
call PauseTimer(this.t)
if this.pp == GetLocalPlayer() then
call ResetToGameCamera(0.0)
endif
endif
endmethod
static method on_cmd takes nothing returns nothing
local string cmd
local thistype this
call ExecuteFunc("s__" + "thistype" + "_tokenize")
set this = GetPlayerId(GetTriggerPlayer())
set cmd = T[1]
if cmd == "-gh-enable" then
if T[2] == "1" or T[2] == "true" or T[2] == "t" then
call this.enable(true)
elseif T[2] == "0" or T[2] == "false" or T[2] == "f" then
call this.enable(false)
endif
elseif cmd == "-gh-speed" then
set this.speed = S2R(T[2])
elseif cmd == "-gh-rot-inc" then
set this.rot_inc = S2R(T[2])
elseif cmd == "-gh-rot-d" then
set this.rot_inc = S2I(T[2])
elseif cmd == "-gh-aoa-inc" then
set this.aoa_inc = S2R(T[2])
elseif cmd == "-gh-aoa-d" then
set this.aoa_inc = S2I(T[2])
elseif cmd == "-gh-fov" then
set this.fov = S2R(T[2])
elseif cmd == "-gh-farz" then
set this.farz = S2R(T[2])
endif
endmethod
static method on_key takes nothing returns nothing
local thistype this
local integer p
local integer e
local integer key
local boolean is_key_down
local real ang
local real ang2
local real terrain_z
set this = GetPlayerId(GetTriggerPlayer())
if not this.enabled then
return
endif
set p = this
// EVENT_PLAYER_ARROW_LEFT_DOWN = ConvertPlayerEvent(261) // 0
// EVENT_PLAYER_ARROW_LEFT_UP = ConvertPlayerEvent(262) // 1
// EVENT_PLAYER_ARROW_RIGHT_DOWN = ConvertPlayerEvent(263) // 2
// EVENT_PLAYER_ARROW_RIGHT_UP = ConvertPlayerEvent(264) // 3
// EVENT_PLAYER_ARROW_DOWN_DOWN = ConvertPlayerEvent(265) // 4
// EVENT_PLAYER_ARROW_DOWN_UP = ConvertPlayerEvent(266) // 5
// EVENT_PLAYER_ARROW_UP_DOWN = ConvertPlayerEvent(267) // 6
// EVENT_PLAYER_ARROW_UP_UP = ConvertPlayerEvent(268) // 7
//
set e = GetHandleId(GetTriggerEventId()) - 261 // 0 .. 7
set key = e / 2 // 0 .. 3
set is_key_down = e == 2 * key
set is_player_key_down[key] = is_key_down
//! runtextmacro GHOSTLY_CAMERA_UPDATE()
endmethod
static method update takes nothing returns nothing
local thistype this = GetHandleId(GetExpiredTimer()) - first_timer_offset
local integer p = this
// local Blackboard bb = Blackboard(this)
local integer dx
local integer dy
local real ang
local real ang2
local real terrain_z
call mouse_get_screen_xy()
set dx = this.pmsx - mouse_screen_x
set dy = this.pmsy - mouse_screen_y
// call bb.println("msx: " + I2S(mouse_screen_x))
// call bb.println("msy: " + I2S(mouse_screen_y))
// call bb.println("dx: " + R2S(dx))
// call bb.println("dy: " + R2S(dy))
// call bb.println("rot: " + R2S(this.rot))
// call bb.println("aoa: " + R2S(this.aoa))
// call bb.println("left down: " + b2s(is_player_key_down[KEY_LEFT]))
// call bb.println("right down: " + b2s(is_player_key_down[KEY_RIGHT]))
// call bb.println("down down: " + b2s(is_player_key_down[KEY_DOWN]))
// call bb.println("up down: " + b2s(is_player_key_down[KEY_UP]))
// call bb.flush(bb.HALF_WIDTH).show()
if dx < -rot_d then
set this.rot = this.rot - this.rot_inc
elseif dx > rot_d then
set this.rot = this.rot + this.rot_inc
endif
if dy < -aoa_d then
set this.aoa = this.aoa - this.aoa_inc
elseif dy > aoa_d then
set this.aoa = this.aoa + this.aoa_inc
endif
if this.aoa < -89.0 then
set this.aoa = -89.0
elseif this.aoa > 89.0 then
set this.aoa = 89.0
endif
//! textmacro GHOSTLY_CAMERA_UPDATE
if is_player_key_down[KEY_LEFT] then
set ang = bj_DEGTORAD * (this.rot + 90.0)
set this.x = this.x + this.speed * DT * Cos(ang)
set this.y = this.y + this.speed * DT * Sin(ang)
elseif is_player_key_down[KEY_RIGHT] then
set ang = (this.rot - 90.0) * bj_DEGTORAD
set this.x = this.x + this.speed * DT * Cos(ang)
set this.y = this.y + this.speed * DT * Sin(ang)
endif
if is_player_key_down[KEY_DOWN] then
set ang = bj_DEGTORAD * this.rot
set ang2 = bj_DEGTORAD * this.aoa
set this.x = this.x - Cos(ang) * Cos(ang2) * this.speed * DT
set this.y = this.y - Sin(ang) * Cos(ang2) * this.speed * DT
set this.z = this.z - Sin(ang2) * this.speed * DT
elseif is_player_key_down[KEY_UP] then
set ang = bj_DEGTORAD * this.rot
set ang2 = bj_DEGTORAD * this.aoa
set this.x = this.x + Cos(ang) * Cos(ang2) * this.speed * DT
set this.y = this.y + Sin(ang) * Cos(ang2) * this.speed * DT
set this.z = this.z + Sin(ang2) * this.speed * DT
endif
if this.x < CAMERA_MIN_X then
set this.x = CAMERA_MIN_X
elseif this.x > CAMERA_MAX_X then
set this.x = CAMERA_MAX_X
endif
if this.y < CAMERA_MIN_Y then
set this.y = CAMERA_MIN_Y
elseif this.y > CAMERA_MAX_Y then
set this.y = CAMERA_MAX_Y
endif
static if not CAN_PASS_THROUGH_TERRAIN then
call MoveLocation(loc, x, y)
set terrain_z = GetLocationZ(loc)
if this.z < terrain_z + 128.0 then
set this.z = terrain_z + 128.0
endif
endif
if this.z > CAMERA_MAX_Z then
set this.z = CAMERA_MAX_Z
endif
if this.pp == GetLocalPlayer() then
call PanCameraToTimed(this.x, this.y, 0.0)
call SetCameraZ(this.z)
// disables the camera fidgeting (pressing the arrow keys moves the "normal" game camera as well)
call PanCameraToTimed(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 0.0, 0.0)
call SetCameraField(CAMERA_FIELD_ROTATION, this.rot, 0.0)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, this.aoa, 0.0)
call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW, this.fov, 0.0)
call SetCameraField(CAMERA_FIELD_FARZ, this.farz, 0.0)
endif
//! endtextmacro
//! runtextmacro GHOSTLY_CAMERA_UPDATE()
set this.pmsx = mouse_screen_x
set this.pmsy = mouse_screen_y
endmethod
private static string array T
private static integer T_count = 0
private static method tokenize takes nothing returns nothing
local string s = GetEventPlayerChatString()
local integer si
local integer i
local string ch
set T_count = 0
set i = 0
loop
loop
set ch = SubString(s, i, i + 1)
exitwhen ch != " "
set i = i + 1
endloop
exitwhen ch == ""
set si = i
loop
set i = i + 1
set ch = SubString(s, i, i + 1)
exitwhen ch == " " or ch == ""
endloop
set T_count = T_count + 1
set T[T_count] = SubString(s, si, i)
endloop
endmethod
static method set_handlers takes nothing returns nothing
set on_cmd_handler = function thistype.on_cmd
set on_key_handler = function thistype.on_key
set update_handler = function thistype.update
endmethod
endstruct
endlibrary