//TESH.scrollpos=-1
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
library Init initializer Init uses Game, TimerUtils, PlayerCamLib
globals
constant real GAME_MIN_X = -2048
constant real GAME_MAX_X = 2048
constant real GAME_MIN_Y = -1024
constant real GAME_MAX_Y = 1024
constant real TARGET_DISTANCE = 4000
constant real TITLE_SCREEN_TIME = 6.
constant real MSSAGE_OFFSET_TIME = 0.5
constant real HOW_TO_PLAY_TIME = 6.
endglobals
globals
private PlayerCam MessageCam
private timer MessagesTimer
endglobals
private function Messages4 takes nothing returns nothing
call ClearTextMessages()
call ReleaseTimer(MessagesTimer)
call MessageCam.destroy()
call PlayerInstance.Create(Player(0), GAME_MIN_X, GAME_MIN_Y, GAME_MAX_X, GAME_MAX_Y, TARGET_DISTANCE)
endfunction
private function Messages3 takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, " HOW TO PLAY")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, "Hold down your left mouse button to hook to a mine. You will")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, "be accelerated towards that mine. But be careful! If you")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, "collide with the mine you're currently hooked to, you die.")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, HOW_TO_PLAY_TIME, "The goal of this game is to get as high as possible.")
call TimerStart(MessagesTimer, HOW_TO_PLAY_TIME, false, function Messages4)
endfunction
private function Messages2 takes nothing returns nothing
call ClearTextMessages()
call TimerStart(MessagesTimer, MSSAGE_OFFSET_TIME, false, function Messages3)
endfunction
private function Messages1 takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, " GRAVITY HOOK")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "A proof-of-concept minigame for Reinventing the Craft")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "THANKS TO:")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "Firstly, i'd like to thank |cffffcc00MindWorX|r and |cffffcc00SFilip|r for making")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "this map possible with their RtC project.")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "Secondly, I'd like to thank the original creators of")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "Gravity Hook: |cffffcc00Adam Atomic|r and |cffffcc00Danny Baranowsky|r")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "See |cffffcc00http://www.adamatomic.com/|r for more information about")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.6,0, TITLE_SCREEN_TIME, "the original Gravity Hook")
call TimerStart(MessagesTimer, TITLE_SCREEN_TIME, false, function Messages2)
endfunction
private function Init takes nothing returns nothing
local trigger t
set MessageCam=PlayerCam.Create(Player(0), GetStartLocationX(GetPlayerStartLocation(Player(0))), GetStartLocationY(GetPlayerStartLocation(Player(0))), 500)
call CreateQuestBJ(bj_QUESTTYPE_OPT_DISCOVERED, "Credits", " - |cffffcc00MindWorX|r and |cffffcc00SFilip|r for their RtC project.\n - |cffffcc00Adam Atomic|r and |cffffcc00Danny Baranowsky|r for the original game.\n - |cffffcc00Vexorian|r for JassHelper, his Optimizer, TimerUtils, CSData and his dummy model.\n - |cffffcc00grim001|r for fixing up the physics in this map.", "ReplaceableTextures\\CommandButtons\\BTNBansheeAdept.blp")
call CreateQuestBJ(bj_QUESTTYPE_OPT_DISCOVERED, "Changelog", "|cffffcc00Version 1.1.0|r\n - Improved the gameplay by changing the way the acceleration towards the hooked mine is calculated.\n - Improved efficiency by implementing simple unit recycling.\n - Other minor performance related changes.\n\n|cffffcc00Version 1.1.1|r\n - Hopefully fixed most problems related to starting this map.\n - Added a command to fix problems related to viewing the quest log.\n\n|cffffcc00Version 1.2.0|r\n - Updated map for RtC Version 1.0 which is compatible to WC3 1.24e.", "ReplaceableTextures\\CommandButtons\\BTNBansheeMaster.blp")
call FogEnable(false)
call FogMaskEnable(false)
call EnableSelect(false, false)
call EnableDragSelect(false, false)
call EnablePreSelect(false, false)
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12)
call SuspendTimeOfDay(true)
set MessagesTimer=NewTimer()
call TimerStart(MessagesTimer, 0, false, function Messages1)
set t=CreateTrigger()
call TriggerAddAction(t, function Messages4)
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
endfunction
endlibrary
library RtCMouse initializer Init
globals
constant integer MOUSE_BUTTON_LEFT = 1
constant integer MOUSE_BUTTON_RIGHT = 2
constant integer MOUSE_BUTTON_MIDDLE = 3
constant integer MOUSE_BUTTON_4 = 4
constant integer MOUSE_BUTTON_5 = 5
endglobals
globals
private boolean array ButtonState
private real MousePosX = 0.0
private real MousePosY = 0.0
private trigger TMouseDown = CreateTrigger()
private trigger TMouseUp = CreateTrigger()
private trigger TMouseMove = CreateTrigger()
endglobals
function GetMouseButtonState takes integer btn returns boolean // isDown
return ButtonState[btn]
endfunction
function GetMouseTerrainX takes nothing returns real
return MousePosX
endfunction
function GetMouseTerrainY takes nothing returns real
return MousePosY
endfunction
private function MouseDown takes nothing returns nothing
set ButtonState[GetHandleId(BlzGetTriggerPlayerMouseButton())] = true
endfunction
private function MouseUp takes nothing returns nothing
set ButtonState[GetHandleId(BlzGetTriggerPlayerMouseButton())] = false
endfunction
private function MouseUpdate takes nothing returns nothing
if BlzGetTriggerPlayerMouseX() != 0.0 then
set MousePosX = BlzGetTriggerPlayerMouseX()
endif
if BlzGetTriggerPlayerMouseY() != 0.0 then
set MousePosY = BlzGetTriggerPlayerMouseY()
endif
endfunction
private function Init takes nothing returns nothing
call TriggerAddAction(TMouseDown, function MouseDown)
call TriggerRegisterPlayerEvent(TMouseDown, GetLocalPlayer(), EVENT_PLAYER_MOUSE_DOWN)
call TriggerAddAction(TMouseUp, function MouseUp)
call TriggerRegisterPlayerEvent(TMouseUp, GetLocalPlayer(), EVENT_PLAYER_MOUSE_UP)
call TriggerAddAction(TMouseMove, function MouseUpdate)
call TriggerRegisterPlayerEvent(TMouseMove, GetLocalPlayer(), EVENT_PLAYER_MOUSE_MOVE)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = false
private constant boolean USE_FLEXIBLE_OFFSET = true
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library CSData initializer Init uses Table
//****************************************************************************************************
// CSData 15.2
// ¯¯¯¯¯¯¯¯¯¯¯
// CSDatas are like UserData in units and items, they are faster than gamecache unless you have more
// than 8191 handles in your map. In that case it would be a little slower but only for those
// handles. And if you have more than 8191 handles your map is too slow already anyways.
//
// Notice that for public spells or systems to be distributed you should only use these
// for private objects (those who the mapper would never have access to) If you are making something
// for your map you can use them wherever you want.
//
// Best to be used in conjunction to CSArrays so you just specify an array id for a handle.
//
// DO NOT USE THIS ON THESE HANDLE TYPES: -lightning, -ubersplat, -image, -texttag,
// -any 'argument' handle (like playerstate, damagetype, etc)
//
//****************************************************************************************************
//====================================================================================================
globals
private HandleTable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetCSData takes handle h, integer v returns nothing
set ht[h]=v
endfunction
function GetCSData takes handle h returns integer
return ht[h]
endfunction
private function Init takes nothing returns nothing
set ht=HandleTable.create()
endfunction
endlibrary
library Game uses TimerUtils, CSData, PlayerCamLib, RtCMouse
// currently single player only.
globals
private constant integer SHIP_UID = 'ship'
private constant integer MINE_UID = 'mine'
private constant integer MAX_MINES = 256
private constant real TICK = 1./40
private constant real SHIP_COLLSIZE = 24.
private constant real MINE_COLLSIZE = 24.
private constant real RELATIVE_UNIT = 2048./80.
private constant real COLL_ABSORB = 0.65 // 1 - 0.35
private constant real STOP_THRESHOLD = 0.05 // in RELATIVE_UNITS
private constant real GRAVITY = 70. // in RELATIVE_UNITS
private constant real HOOK_PULL = 67. // in RELATIVE_UNITS // Acceleration towards mine at ROPE_LENGTH distance
private constant real ROPE_LENGTH = 850.
private constant real AIR_FRICTION = 0.994 // 1 - 0.006
private constant real MINE_PULL_VEL = 3 // in RELATIVE_UNITS
private constant real BREAK_HEIGHT_FACTOR = 0.67 // break after 67%
private constant real INITIAL_GROUND_FACTOR = 0.15 // Ground at 15%
private constant real MINE_MIN_DIST = 120.
private constant real MINE_MAX_H_DIST = 625.
private constant real MINE_MAX_DIST = 1024.
private constant real MINE_START_H = 450.
private constant real MINE_PRELOAD_DIST = 512.
private constant integer INITIAL_SPAWN_GRANULATION = 8
private constant integer DYNAMIC_SPAWN_GRANULATION = 1
private constant real DEATH_PAUSE = 2.00
private constant string DEATH_FX = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
private constant integer DUMMY_UID = 'dumm'
private constant string GLOW_FX = "war3mapImported\\HeroGlow_Origin.mdx"
private constant string GLOW_FX_ATTPT = "origin"
private constant string LIGHTNING_TYPE = "CLEA" // custom one inserted via SLK editing
private constant real LIGHTNING_TO_SHIP_ALPHA = 1
private constant real LIGHTNING_TO_SHIP_RED = 1
private constant real LIGHTNING_TO_SHIP_GREEN = 1
private constant real LIGHTNING_TO_SHIP_BLUE = 1
private constant real LIGHTNING_TO_MOUSE_ALPHA = 1
private constant real LIGHTNING_TO_MOUSE_RED = 1
private constant real LIGHTNING_TO_MOUSE_GREEN = 0
private constant real LIGHTNING_TO_MOUSE_BLUE = 0
private constant real SAFE_X = -3072.
private constant real SAFE_Y = 0.
endglobals
globals
private unit array D
private integer N=0
endglobals
private function NewMine takes player p, real x, real y returns unit
local unit u
if N==0 then
set u=CreateUnit(p, MINE_UID, x, y, 0)
call SetUnitX(u, x)
call SetUnitY(u, y)
return u
else
set N=N-1
if GetOwningPlayer(D[N])!=p then
call SetUnitOwner(D[N], p, true)
endif
call SetUnitX(D[N], x)
call SetUnitY(D[N], y)
return D[N]
endif
endfunction
private function ReleaseMine takes unit u returns nothing
call SetUnitX(u, SAFE_X)
call SetUnitY(u, SAFE_Y)
set D[N]=u
set N=N+1
endfunction
struct PlayerInstance
private player p
private integer pid
private timer t
private PlayerCam cam
private trigger TriggerFix
private trigger MouseLeftDown
private trigger MouseLeftUp
private lightning to_mouse
private lightning to_ship
private lightning ground
private boolean hooked=false
private unit ship
private unit hook
private unit nt
private unit mark
private effect markfx
private unit array Mines[MAX_MINES]
private integer MCnt=0
private real speedx=0
private real speedy=0
private real posx
private real posy
private real maxy
private real maxx
private real miny
private real minx
private real lastminey
private real basey
private real maxheight
private real alltimehigh=0
private real breaky
private real groundy
private boolean oplimithit
//
private static group ENUM_GROUP=CreateGroup()
private method SpawnNewMines takes real OverDist, integer granulation returns nothing
local integer i=R2I(.basey+MINE_PRELOAD_DIST+.maxy-.miny-OverDist)
local integer j
local real x
local real y
local real mx
local real my
local boolean b
loop
exitwhen i>=.basey+MINE_PRELOAD_DIST+.maxy-.miny
if GetRandomReal(0,1)<granulation/(SquareRoot(i)+1) and i>MINE_START_H then // chance to spawn a new mine
set y=i-.basey+.miny
set x=GetRandomReal(.minx, .maxx)
set j=0
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, MINE_MIN_DIST, null)
set b=FirstOfGroup(ENUM_GROUP)==null
call GroupClear(ENUM_GROUP)
if b then // if distance is ok, spawn the mine
set .lastminey=i
set .Mines[.MCnt]=NewMine(.p,x,y)
set .MCnt=.MCnt+1
endif
elseif i-.lastminey>MINE_MAX_H_DIST then // if the last mine is far enough away, force the spawning of a new one
set y=i-.basey+.miny
set x=GetRandomReal(.minx, .maxx)
set mx=GetUnitX(.Mines[.MCnt-1])
// keep the distance in check, we dont want to create impossible scenarios
if (mx-x)*(mx-x)>(MINE_MAX_DIST*MINE_MAX_DIST)+(MINE_MAX_H_DIST*MINE_MAX_H_DIST) then
if mx>x then
set x=mx-SquareRoot((MINE_MAX_DIST*MINE_MAX_DIST)-(MINE_MAX_H_DIST*MINE_MAX_H_DIST))
elseif mx<x then
set x=mx+SquareRoot((MINE_MAX_DIST*MINE_MAX_DIST)-(MINE_MAX_H_DIST*MINE_MAX_H_DIST))
endif
endif
set .lastminey=i
set .Mines[.MCnt]=NewMine(.p,x,y)
set .MCnt=.MCnt+1
endif
set i=i+granulation
endloop
endmethod
private static method Callback takes nothing returns nothing
local PlayerInstance s = GetTimerData(GetExpiredTimer())
local integer i
local real mouseX = GetMouseTerrainX()
local real mouseY = GetMouseTerrainY()
local real deltaX
local real deltaY
local real distance
local real minDistance = 0x7FFFFF0
local real x
local real y
local real hx
local real hy
local real d
local real r
local boolean b
if s.oplimithit then
debug call BJDebugMsg("ERROR: PlayerInstance.New() hit the op limit! Try increasing INITIAL_SPAWN_GRANULATION or decreasing MINE_PRELOAD_DIST.")
set s.oplimithit=false
endif
if not s.hooked or not GetMouseButtonState(MOUSE_BUTTON_LEFT) then
// search for next target mine
set i = 0
loop
exitwhen i >= s.MCnt
set deltaX = GetUnitX(s.Mines[i]) - mouseX
set deltaY = GetUnitY(s.Mines[i]) - mouseY
set distance = deltaX * deltaX + deltaY * deltaY
if distance < minDistance then
set minDistance = distance
set s.nt = s.Mines[i]
endif
set i=i+1
endloop
if s.hooked then
call SetLightningColor(s.to_ship, 0,0,0,0)
call DestroyEffect(s.markfx)
call RemoveUnit(s.mark)
elseif GetMouseButtonState(MOUSE_BUTTON_LEFT) then
set s.hook = s.nt
call MoveLightning(s.to_ship, false, GetUnitX(s.ship), GetUnitY(s.ship), GetUnitX(s.hook), GetUnitY(s.hook))
set s.mark = CreateUnit(s.p, DUMMY_UID, GetUnitX(s.hook), GetUnitY(s.hook), 0)
call SetUnitX(s.mark, GetUnitX(s.hook))
call SetUnitY(s.mark, GetUnitY(s.hook))
set s.markfx = AddSpecialEffectTarget(GLOW_FX, s.mark, GLOW_FX_ATTPT)
call SetLightningColor(s.to_ship, LIGHTNING_TO_SHIP_RED,LIGHTNING_TO_SHIP_GREEN,LIGHTNING_TO_SHIP_BLUE,LIGHTNING_TO_SHIP_ALPHA)
endif
endif
set s.hooked = GetMouseButtonState(MOUSE_BUTTON_LEFT)
set hx = GetUnitX(s.hook)
set hy = GetUnitY(s.hook)
if s.hooked then
call MoveLightning(s.to_mouse, false, GetUnitX(s.hook), GetUnitY(s.hook), mouseX, mouseY)
else
call MoveLightning(s.to_mouse, false, GetUnitX(s.nt), GetUnitY(s.nt), mouseX, mouseY)
endif
// update y-coord
set y = s.posy + (s.speedy * TICK)
if s.groundy > s.miny and y < s.groundy then // collision with the ground
set s.speedy = -COLL_ABSORB * s.speedy
if s.speedy <= STOP_THRESHOLD then
set s.speedy = 0
set y = s.groundy
else
set y = ((1-((s.groundy - s.posy) / (y - s.posy))) * s.speedy * TICK) + s.groundy
endif
elseif y > s.breaky then
// move all mines a bit down, recycle invisible mines, spawn new ones, keep ship on s.breaky
set d = y - s.breaky
set i = 0
loop
exitwhen i >= s.MCnt
call SetUnitY(s.Mines[i], GetUnitY(s.Mines[i]) -d) // move mines down, CHECK
if GetUnitY(s.Mines[i]) < s.miny then // recycle dead mines, CHECK
// recycle mine
call ReleaseMine(s.Mines[i])
set s.MCnt = s.MCnt - 1
set s.Mines[i] = s.Mines[s.MCnt]
set s.Mines[s.MCnt] = null
set i=i-1
endif
set i=i+1
endloop
set s.basey=s.basey+d
// dynamically spawn new mines
call s.SpawnNewMines(d, DYNAMIC_SPAWN_GRANULATION)
// create new mines, CHECK
set y=s.breaky // keep ship on s.breaky, CHECK
set s.groundy=s.groundy-d
if s.groundy>s.miny then
call MoveLightning(s.ground,false, s.minx, s.groundy-SHIP_COLLSIZE, s.maxx, s.groundy-SHIP_COLLSIZE)
else
call SetLightningColor(s.ground, 0,0,0,0)
endif
elseif y < s.miny then
call s.Death()
endif
//alright, done.
set s.posy=y
call SetUnitY(s.ship, y)
// update x-coord
set x=s.posx+s.speedx*TICK
if x>s.maxx then // collision with right boundary
set s.speedx=-1*COLL_ABSORB*s.speedx
set x=((1-((s.maxx-s.posx)/(x-s.posx)))*s.speedx*TICK)+s.maxx
elseif x<s.minx then // collision with left boundary
set s.speedx=-1*COLL_ABSORB*s.speedx
set x=((1-((s.minx-s.posx)/(x-s.posx)))*s.speedx*TICK)+s.minx
endif
// alright, done.
set s.posx=x
call SetUnitX(s.ship, x)
// check for Mine Hit // a^2+b^2=c^2
if ((hx-x)*(hx-x))+((hy-y)*(hy-y))<=(SHIP_COLLSIZE+MINE_COLLSIZE)*(SHIP_COLLSIZE+MINE_COLLSIZE) and s.hooked then
call s.Death()
endif
// calculate height in RELATIVE_UNITS, lets say in meters
set d=((s.basey+(s.posy-s.miny))/RELATIVE_UNIT)
if d>s.maxheight then
set s.maxheight=d
call SetPlayerState(s.p, PLAYER_STATE_RESOURCE_GOLD, R2I(s.maxheight))
if d>s.alltimehigh then
set s.alltimehigh=d
call SetPlayerState(s.p, PLAYER_STATE_RESOURCE_LUMBER, R2I(s.alltimehigh))
endif
endif
// update speeds
if not (s.posy==s.groundy and s.speedy==0) then
set s.speedy=s.speedy-(GRAVITY*RELATIVE_UNIT*TICK) // gravity
endif
if s.hooked then // hooked
set r=Atan2(s.posy-hy, s.posx-hx) // angle between ship and hooked mine
set d=HOOK_PULL*RELATIVE_UNIT*(ROPE_LENGTH / SquareRoot(((s.posx-hx)*(s.posx-hx))+((s.posy-hy)*(s.posy-hy))))
set s.speedy=s.speedy-(Sin(r)*d)*TICK
call SetUnitY(s.hook, GetUnitY(s.hook)+(MINE_PULL_VEL*TICK*RELATIVE_UNIT*Sin(r)))
call SetUnitY(s.mark, GetUnitY(s.hook))
set s.speedx=s.speedx-(Cos(r)*d)*TICK
call SetUnitX(s.hook, GetUnitX(s.hook)+(MINE_PULL_VEL*TICK*RELATIVE_UNIT*Cos(r)))
call SetUnitX(s.mark, GetUnitX(s.hook))
endif
// friction
set s.speedy=s.speedy*AIR_FRICTION
set s.speedx=s.speedx*AIR_FRICTION
call MoveLightning(s.to_ship, false, x, y, hx, hy) // update the lightning from ship to hooked mine
endmethod
private static method New takes nothing returns nothing
local PlayerInstance s=GetTimerData(GetExpiredTimer())
local integer i=0
local integer j=0
local real x
local real y
local real mx
local real my
local boolean b
set s.oplimithit=true
call TimerStart(s.t, TICK, true, function PlayerInstance.Callback)
loop // Cleanse this world
exitwhen i>=s.MCnt
call ReleaseMine(s.Mines[i])
set i=i+1
endloop
set s.MCnt=0
//
set s.speedx=0
set s.speedy=0
call EnableTrigger(s.MouseLeftDown)
call EnableTrigger(s.MouseLeftUp)
call SetLightningColor(s.to_mouse, LIGHTNING_TO_MOUSE_RED,LIGHTNING_TO_MOUSE_GREEN,LIGHTNING_TO_MOUSE_BLUE,LIGHTNING_TO_MOUSE_ALPHA)
call SetLightningColor(s.to_ship, 0,0,0,0)
set s.hooked=false
set s.posx=0.5*(s.maxx+s.minx)
set s.posy=s.miny+INITIAL_GROUND_FACTOR*(s.maxy-s.miny)
call SetUnitX(s.ship, s.posx)
call SetUnitY(s.ship, s.posy)
call ShowUnit(s.ship, true)
set s.maxheight=0
call SetPlayerState(s.p, PLAYER_STATE_RESOURCE_GOLD, 0)
set s.groundy=s.miny+INITIAL_GROUND_FACTOR*(s.maxy-s.miny)
set s.basey=-INITIAL_GROUND_FACTOR*(s.maxy-s.miny)
call MoveLightning(s.ground, false, s.minx, s.groundy-SHIP_COLLSIZE, s.maxx, s.groundy-SHIP_COLLSIZE)
call SetLightningColor(s.ground, 1,1,1,1)
set s.lastminey=MINE_START_H
// Spawn New Mines
set x=GetRandomReal(-1*MINE_MIN_DIST, MINE_MIN_DIST)
set s.Mines[0]=NewMine(s.p, x, s.groundy+MINE_START_H)
set s.MCnt=1
call s.SpawnNewMines(s.maxy-s.groundy+MINE_PRELOAD_DIST, INITIAL_SPAWN_GRANULATION)
set s.oplimithit=false
endmethod
private method Death takes nothing returns nothing
call ShowUnit(.ship, false)
call DestroyEffect(AddSpecialEffect(DEATH_FX, .posx, .posy))
call SetLightningColor(.to_ship,0,0,0,0)
call SetLightningColor(.to_mouse,0,0,0,0)
set .basey=0
call RemoveUnit(.mark)
call DestroyEffect(.markfx)
call TimerStart(.t, DEATH_PAUSE, false, function PlayerInstance.New)
endmethod
static method Create takes player forplayer, real minx, real miny, real maxx, real maxy, real tdist returns PlayerInstance
local PlayerInstance s=PlayerInstance.allocate()
set s.p=forplayer
set s.pid=GetPlayerId(forplayer)
if GetLocalPlayer()==forplayer then // disable a few things
call EnableSelect(false, false)
call EnableDragSelect(false, false)
call EnableUserUI(false)
endif
set s.t=NewTimer()
call SetTimerData(s.t, s)
set s.maxx=maxx
set s.minx=minx
set s.maxy=maxy
set s.miny=miny
set s.cam=PlayerCam.create(forplayer, (maxx+minx)*0.5, (maxy+miny)*0.5, tdist)
set s.ship=CreateUnit(forplayer, SHIP_UID, 0.5*(maxx+minx), miny+INITIAL_GROUND_FACTOR*(maxy-miny), 270)
set s.breaky=(miny+BREAK_HEIGHT_FACTOR*(maxy-miny))
set s.ground=AddLightning(LIGHTNING_TYPE, false, minx, s.groundy-SHIP_COLLSIZE, maxx, s.groundy-SHIP_COLLSIZE)
set s.to_mouse=AddLightning(LIGHTNING_TYPE, false,GetUnitX(s.ship), GetUnitY(s.ship), 0,0)
set s.to_ship=AddLightning(LIGHTNING_TYPE, false, GetUnitX(s.ship), GetUnitY(s.ship), 0,0)
call TimerStart(s.t, 0, false, function PlayerInstance.New)
return s
endmethod
method onDestroy takes nothing returns nothing
local integer i=0
call ReleaseTimer(.t)
call DestroyLightning(.to_mouse)
call DestroyLightning(.to_ship)
call DestroyLightning(.ground)
call .cam.destroy()
call RemoveUnit(.ship)
set .ship=null
loop
exitwhen i>=.MCnt
call RemoveUnit(.Mines[i])
set .Mines[i]=null
set i=i+1
endloop
if .hooked then
call RemoveUnit(.mark)
call DestroyEffect(.markfx)
set .mark=null
set .markfx=null
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library PlayerCamLib uses TimerUtils
// make sure you have only one of these structs running for a player at one time.
globals
private constant real TICK = 1./40
endglobals
struct PlayerCam
private player p
private timer t
private real x
private real y
private real tdist
private static method Center takes nothing returns nothing
local PlayerCam s=GetTimerData(GetExpiredTimer())
if GetLocalPlayer()==s.p then
call PanCameraToTimed(s.x, s.y, 0)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270, 0)
call SetCameraField(CAMERA_FIELD_ROLL, 0, 0)
call SetCameraField(CAMERA_FIELD_ROTATION, 90, 0)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, s.tdist, 0)
endif
endmethod
static method create takes player which, real x, real y, real tdist returns PlayerCam
local PlayerCam s=PlayerCam.allocate()
set s.p=which
set s.t=NewTimer()
call SetTimerData(s.t, s)
call TimerStart(s.t, TICK, true, function PlayerCam.Center)
set s.x=x
set s.y=y
set s.tdist=tdist
return s
endmethod
static method Create takes player which, real x, real y, real tdist returns PlayerCam
return PlayerCam.create(which, x, y, tdist)
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
endlibrary