2009-12-29 13:44:16 +00:00
/*
Standard clonk controls
Author : Newton
This object provides handling of the clonk controls including item
2010-04-09 15:06:28 +00:00
management , backpack controls and standard throwing behaviour . It
2010-08-19 11:17:43 +00:00
should be included into any clonk / crew definition .
2011-03-13 15:16:45 +00:00
The controls in System . ocg / PlayerControl . c only provide basic movement
2009-12-29 13:44:16 +00:00
handling , namely the movement left , right , up and down . The rest is
handled here :
Grabbing , ungrabbing , shifting and pushing vehicles into buildings ;
2010-04-09 15:06:28 +00:00
entering and exiting buildings ; throwing , dropping ; backpack control ,
( object ) menu control , hotkey controls , usage and it ' s callbacks and
2012-12-25 11:19:23 +00:00
forwards to script .
2009-12-29 13:44:16 +00:00
Objects that inherit this object need to return _inherited ( ) in the
following callbacks ( if defined ) :
Construction , Collection2 , Ejection , RejectCollect , Departure ,
2010-08-19 11:17:43 +00:00
Entrance , AttachTargetLost , CrewSelection , Death ,
Destruction , OnActionChanged
2010-03-30 16:48:38 +00:00
2009-12-29 13:44:16 +00:00
The following callbacks are made to other objects :
* Stop
* Left , * Right , * Up , * Down
2010-04-09 15:06:28 +00:00
* Use , * UseStop , * UseStart , * UseHolding , * UseCancel
2009-12-29 13:44:16 +00:00
wheras * is ' Contained ' if the clonk is contained and otherwise ( riding ,
2010-09-13 15:27:48 +00:00
pushing , to self ) it is ' Control ' . The item in the inventory only gets
2009-12-29 13:44:16 +00:00
the Use * - calls . If the callback is handled , you should return true .
2010-04-09 15:06:28 +00:00
Currently , this is explained more in detail here :
http : //forum.openclonk.org/topic_show.pl?tid=337
2009-12-29 13:44:16 +00:00
*/
2012-12-25 11:19:23 +00:00
// make use of other sub-libraries
# include Library_Inventory
# include Library_ClonkInventoryControl
2016-01-02 12:02:45 +00:00
# include Library_ClonkInteractionControl
2012-12-25 11:19:23 +00:00
# include Library_ClonkGamepadControl
2010-03-25 23:56:55 +00:00
2013-05-26 01:24:46 +00:00
// used for interaction with objects
static const ACTIONTYPE_INVENTORY = 0 ;
static const ACTIONTYPE_VEHICLE = 1 ;
static const ACTIONTYPE_STRUCTURE = 2 ;
static const ACTIONTYPE_SCRIPT = 3 ;
static const ACTIONTYPE_EXTRA = 4 ;
2012-04-22 16:44:28 +00:00
2015-10-06 13:08:50 +00:00
// elevators within this range (x) can be called
static const ELEVATOR_CALL_DISTANCE = 30 ;
2012-12-25 11:19:23 +00:00
/* ++++++++++++++++++++++++ Clonk Inventory Control ++++++++++++++++++++++++ */
2012-10-07 13:44:01 +00:00
2012-12-25 11:19:23 +00:00
/*
used properties
this . control . hotkeypressed : used to determine if an interaction has already been handled by a hotkey ( space + 1 - 9 )
this . control . current_object : object that is being used at the moment
this . control . using_type : way of usage
2015-06-27 14:56:45 +00:00
this . control . alt : alternate usage by right mouse button
2012-12-25 11:19:23 +00:00
this . control . mlastx : last x position of the cursor
this . control . mlasty : last y position of the cursor
this . control . noholdingcallbacks : whether to do HoldingUseControl callbacks
2012-12-29 13:08:53 +00:00
this . control . shelved_command : command ( function ) with condition that will be executed when the condition is met
used for example to re - call * Use / Throw commands when the Clonk finished scaling
2016-01-29 21:48:59 +00:00
this . control . menu : the menu that is currently assigned to the Clonk . Use the methods SetMenu / GetMenu / etc to access it .
2012-12-25 11:19:23 +00:00
*/
2010-02-18 21:22:58 +00:00
2009-12-29 13:44:16 +00:00
/* Item limit */
2016-01-10 15:41:34 +00:00
local MaxContentsCount = 5 ; // Size of the clonks inventory
local HandObjects = 1 ; // Amount of hands to select items
2012-04-22 16:44:28 +00:00
public func NoStackedContentMenu ( ) { return true ; } // Contents-Menu shall display each object in a seperate slot
2009-12-29 13:44:16 +00:00
2012-04-22 16:44:28 +00:00
2009-12-29 13:44:16 +00:00
/* ################################################# */
protected func Construction ( )
{
2012-12-25 11:19:23 +00:00
if ( this . control = = nil )
this . control = { } ;
this . control . hotkeypressed = false ;
2010-03-25 23:56:55 +00:00
2015-06-27 14:56:45 +00:00
this . control . alt = false ;
2012-12-25 11:19:23 +00:00
this . control . current_object = nil ;
this . control . using_type = nil ;
2012-12-29 13:08:53 +00:00
this . control . shelved_command = nil ;
2016-01-29 21:48:59 +00:00
this . control . menu = nil ;
2009-12-29 13:44:16 +00:00
return _inherited ( . . . ) ;
}
2012-12-25 11:19:23 +00:00
public func GetUsedObject ( ) { return this . control . current_object ; }
2009-12-29 13:44:16 +00:00
// The using-command hast to be canceled if the clonk is entered into
// or exited from a building.
protected func Entrance ( ) { CancelUse ( ) ; return _inherited ( . . . ) ; }
protected func Departure ( ) { CancelUse ( ) ; return _inherited ( . . . ) ; }
// The same for vehicles
protected func AttachTargetLost ( ) { CancelUse ( ) ; return _inherited ( . . . ) ; }
2010-03-25 23:56:55 +00:00
// ...aaand the same for when the clonk is deselected
protected func CrewSelection ( bool unselect )
{
2010-09-13 15:27:48 +00:00
if ( unselect )
2010-04-09 15:06:28 +00:00
{
// cancel usage on unselect first...
CancelUse ( ) ;
// and if there is still a menu, cancel it too...
CancelMenu ( ) ;
}
2010-03-25 23:56:55 +00:00
return _inherited ( unselect , . . . ) ;
}
2010-04-09 15:06:28 +00:00
protected func Destruction ( )
{
// close open menus, cancel usage...
CancelUse ( ) ;
CancelMenu ( ) ;
return _inherited ( . . . ) ;
}
protected func Death ( )
{
// close open menus, cancel usage...
CancelUse ( ) ;
CancelMenu ( ) ;
return _inherited ( . . . ) ;
}
2010-08-19 11:17:43 +00:00
protected func OnActionChanged ( string oldaction )
{
var old_act = this [ " ActMap " ] [ oldaction ] ;
var act = this [ " ActMap " ] [ GetAction ( ) ] ;
var old_proc = 0 ;
if ( old_act ) old_proc = old_act [ " Procedure " ] ;
var proc = 0 ;
if ( act ) proc = act [ " Procedure " ] ;
// if the object's procedure has changed from a non Push/Attach
// to a Push/Attach action or the other way round, the usage needs
// to be cancelled
if ( proc ! = old_proc )
{
if ( proc = = DFA_PUSH | | proc = = DFA_ATTACH
| | old_proc = = DFA_PUSH | | old_proc = = DFA_ATTACH )
{
CancelUse ( ) ;
}
}
return _inherited ( oldaction , . . . ) ;
}
2010-03-25 23:56:55 +00:00
2012-05-07 20:57:52 +00:00
/** Returns additional interactions the clonk possesses as an array of function pointers.
Returned Proplist contains :
Fn = Name of the function to call
2015-01-03 14:32:06 +00:00
Object = Object to call the function in . Will also be displayed on the interaction - button
Description = A description of what the interaction does
2012-05-07 20:57:52 +00:00
IconID = ID of the definition that contains the icon ( like GetInteractionMetaInfo )
2015-01-03 14:32:06 +00:00
IconName = Name of the graphic for the icon ( like GetInteractionMetaInfo )
Priority = Where to sort in in the interaction - list . 0 = front , 10 = after script , 20 = after vehicles , 30 = after structures , nil means no preverence
2012-05-07 20:57:52 +00:00
*/
public func GetExtraInteractions ( )
{
2015-01-03 14:32:06 +00:00
var functions = _inherited ( . . . ) ? ? [ ] ;
2012-05-08 18:27:24 +00:00
// flipping construction-preview
var effect ;
if ( effect = GetEffect ( " ControlConstructionPreview " , this ) )
{
if ( effect . flipable )
PushBack ( functions , { Fn = " Flip " , Description = ConstructionPreviewer - > GetFlipDescription ( ) , Object = effect . preview , IconID = ConstructionPreviewer_IconFlip , Priority = 0 } ) ;
}
2015-10-06 13:08:50 +00:00
// call elevator cases
var elevators = FindObjects ( Find_ID ( ElevatorCase ) , Find_InRect ( - ELEVATOR_CALL_DISTANCE , AbsY ( 0 ) , ELEVATOR_CALL_DISTANCE * 2 , GetY ( ) + AbsY ( LandscapeHeight ( ) ) ) , Find_Func ( " Ready " , this ) ) ;
for ( var elevator in elevators )
PushBack ( functions , { Fn = " CallCase " , Object = elevator , Description = elevator - > GetCallDescription ( ) , Priority = 0 } ) ;
2012-05-07 20:57:52 +00:00
return functions ;
}
2010-03-25 23:56:55 +00:00
/* +++++++++++++++++++++++++++ Clonk Control +++++++++++++++++++++++++++ */
2009-12-29 13:44:16 +00:00
/* Main control function */
public func ObjectControl ( int plr , int ctrl , int x , int y , int strength , bool repeat , bool release )
{
2012-01-01 23:00:13 +00:00
if ( ! this )
return false ;
2012-03-22 19:26:06 +00:00
2012-01-01 23:00:13 +00:00
// Contents menu
2013-06-16 17:08:05 +00:00
if ( ctrl = = CON_Contents & & ! release )
2011-12-08 18:57:14 +00:00
{
2012-01-01 23:00:13 +00:00
// Close any menu if open.
2011-12-08 18:57:14 +00:00
if ( GetMenu ( ) )
{
2012-01-01 23:00:13 +00:00
var is_content = GetMenu ( ) - > ~ IsContentMenu ( ) ;
2014-02-26 11:03:27 +00:00
// unclosable menu? bad luck
if ( ! TryCancelMenu ( ) ) return true ;
2012-01-01 23:00:13 +00:00
// If contents menu, don't open new one and return.
if ( is_content )
2011-12-08 18:57:14 +00:00
return true ;
2010-03-25 23:56:55 +00:00
}
2012-01-01 23:00:13 +00:00
// Open contents menu.
2011-12-08 18:57:14 +00:00
CancelUse ( ) ;
2013-06-16 17:08:05 +00:00
GUI_ObjectInteractionMenu - > CreateFor ( this ) ;
// the interaction menu calls SetMenu(this) in the clonk
2011-12-08 18:57:14 +00:00
// so after this call menu = the created menu
2012-06-17 19:37:13 +00:00
if ( GetMenu ( ) )
2014-02-26 11:03:27 +00:00
GetMenu ( ) - > ~ Show ( ) ;
2011-03-06 15:59:58 +00:00
return true ;
2010-03-25 23:56:55 +00:00
}
/* aiming with mouse:
The CON_Aim control is transformed into a use command . Con_Use if
repeated does not bear the updated x , y coordinates , that ' s why this
other control needs to be issued and transformed . CON_Aim is a
control which is issued on mouse move but disabled when not aiming
or when HoldingEnabled ( ) of the used item does not return true .
For the rest of the control code , it looks like the x , y coordinates
came from CON_Use .
*/
2012-12-25 11:19:23 +00:00
if ( this . control . current_object & & ctrl = = CON_Aim )
2009-12-29 13:44:16 +00:00
{
2015-06-27 14:56:45 +00:00
if ( this . control . alt ) ctrl = CON_UseAlt ;
else ctrl = CON_Use ;
2010-02-02 15:09:56 +00:00
2010-02-04 01:08:21 +00:00
repeat = true ;
release = false ;
2009-12-29 13:44:16 +00:00
}
2010-03-25 23:56:55 +00:00
// controls except a few reset a previously given command
2009-12-29 13:44:16 +00:00
else SetCommand ( " None " ) ;
2010-03-25 23:56:55 +00:00
/* aiming with analog pad or keys:
This works completely different . There are CON_AimAxis * and CON_Aim * ,
both work about the same . A virtual cursor is created which moves in a
circle around the clonk and is controlled via these CON_Aim * functions .
CON_Aim * is normally on the same buttons as the movement and has a
higher priority , thus is called first . The aim is always done , even if
the clonk is not aiming . However this returns only true ( = handled ) if
the clonk is really aiming . So if the clonk is not aiming , the virtual
cursor aims into the direction in which the clonk is running and e . g .
CON_Left is still called afterwards . So if the clonk finally starts to
aim , the virtual cursor already aims into the direction in which he ran
*/
if ( ctrl = = CON_AimAxisUp | | ctrl = = CON_AimAxisDown | | ctrl = = CON_AimAxisLeft | | ctrl = = CON_AimAxisRight
| | ctrl = = CON_AimUp | | ctrl = = CON_AimDown | | ctrl = = CON_AimLeft | | ctrl = = CON_AimRight )
2010-03-03 18:02:47 +00:00
{
var success = VirtualCursor ( ) - > Aim ( ctrl , this , strength , repeat , release ) ;
2010-03-25 23:56:55 +00:00
// in any case, CON_Aim* is called but it is only successful if the virtual cursor is aiming
2011-03-06 15:59:58 +00:00
return success & & VirtualCursor ( ) - > IsAiming ( ) ;
2010-03-03 18:02:47 +00:00
}
2010-03-25 23:56:55 +00:00
// save last mouse position:
// if the using has to be canceled, no information about the current x,y
// is available. Thus, the last x,y position needs to be saved
2015-06-27 14:56:45 +00:00
if ( ctrl = = CON_Use | | ctrl = = CON_UseAlt )
2010-02-02 15:09:56 +00:00
{
2012-12-25 11:19:23 +00:00
this . control . mlastx = x ;
this . control . mlasty = y ;
2010-02-02 15:09:56 +00:00
}
2012-02-26 15:19:52 +00:00
2009-12-29 13:44:16 +00:00
var proc = GetProcedure ( ) ;
2010-06-24 19:55:01 +00:00
// building, vehicle, mount, contents, menu control
2009-12-29 13:44:16 +00:00
var house = Contained ( ) ;
var vehicle = GetActionTarget ( ) ;
2010-10-07 16:02:52 +00:00
// the clonk can have an action target even though he lost his action.
// That's why the clonk may only interact with a vehicle if in an
// appropiate procedure:
if ( proc ! = " ATTACH " & & proc ! = " PUSH " )
vehicle = nil ;
2010-02-02 15:09:56 +00:00
2010-09-22 14:49:44 +00:00
// menu
2016-01-29 21:48:59 +00:00
if ( this . control . menu )
2010-03-22 15:40:54 +00:00
{
2011-03-06 15:59:58 +00:00
return Control2Menu ( ctrl , x , y , strength , repeat , release ) ;
2010-03-22 15:40:54 +00:00
}
2010-09-22 14:49:44 +00:00
2012-12-29 13:08:53 +00:00
var contents = this - > GetHandItem ( 0 ) ;
2011-01-03 20:15:19 +00:00
2010-09-22 14:49:44 +00:00
// usage
2015-06-27 14:56:45 +00:00
var use = ( ctrl = = CON_Use | | ctrl = = CON_UseDelayed | | ctrl = = CON_UseAlt | | ctrl = = CON_UseAltDelayed ) ;
2010-09-22 14:49:44 +00:00
if ( use )
2010-02-15 23:33:38 +00:00
{
2010-09-22 14:49:44 +00:00
if ( house )
{
2011-03-06 15:59:58 +00:00
return ControlUse2Script ( ctrl , x , y , strength , repeat , release , house ) ;
2010-09-22 14:49:44 +00:00
}
2010-02-15 23:33:38 +00:00
// control to grabbed vehicle
2010-09-22 14:49:44 +00:00
else if ( vehicle & & proc = = " PUSH " )
{
2011-03-06 15:59:58 +00:00
return ControlUse2Script ( ctrl , x , y , strength , repeat , release , vehicle ) ;
2010-09-22 14:49:44 +00:00
}
else if ( vehicle & & proc = = " ATTACH " )
2010-05-28 19:29:32 +00:00
{
2010-09-22 14:49:44 +00:00
/* objects to which clonks are attached (like horses, mechs,...) have
a special handling :
Use controls are , forwarded to the
horse but if the control is considered unhandled ( return false ) on
the start of the usage , the control is forwarded further to the
item . If the item then returns true on the call , that item is
regarded as the used item for the subsequent ControlUse * calls .
BUT the horse always gets the ControlUse * - calls that ' d go to the used
item , too and before it so it can decide at any time to cancel its
usage via CancelUse ( ) .
*/
2010-10-09 17:08:58 +00:00
if ( ControlUse2Script ( ctrl , x , y , strength , repeat , release , vehicle ) )
2011-03-06 15:59:58 +00:00
return true ;
2010-07-17 14:37:44 +00:00
else
{
// handled if the horse is the used object
// ("using" is set to the object in StartUse(Delayed)Control - when the
// object returns true on that callback. Exactly what we want)
2012-12-25 11:19:23 +00:00
if ( this . control . current_object = = vehicle ) return true ;
2010-07-17 14:37:44 +00:00
// has been cancelled (it is not the start of the usage but no object is used)
2012-12-25 11:19:23 +00:00
if ( ! this . control . current_object & & ( repeat | | release ) ) return true ;
2010-07-17 14:37:44 +00:00
}
2010-05-28 19:29:32 +00:00
}
2012-12-29 17:42:19 +00:00
// releasing the use-key always cancels shelved commands (in that case no this.control.current_object exists)
if ( release ) StopShelvedCommand ( ) ;
2010-09-22 14:49:44 +00:00
// Release commands are always forwarded even if contents is 0, in case we
// need to cancel use of an object that left inventory
2012-12-29 17:42:19 +00:00
if ( contents | | ( release & & this . control . current_object ) )
2010-09-22 14:49:44 +00:00
{
2010-10-09 17:08:58 +00:00
if ( ControlUse2Script ( ctrl , x , y , strength , repeat , release , contents ) )
2011-03-06 15:59:58 +00:00
return true ;
2010-09-22 14:49:44 +00:00
}
2009-12-29 13:44:16 +00:00
}
2011-08-11 14:37:46 +00:00
2015-03-09 12:33:31 +00:00
// A click on throw can also just abort usage without having any other effects.
// todo: figure out if wise.
var currently_in_use = this . control . current_object ! = nil ;
if ( ( ctrl = = CON_Throw | | ctrl = = CON_ThrowDelayed ) & & currently_in_use & & ! release )
{
CancelUse ( ) ;
return true ;
}
2010-09-22 14:49:44 +00:00
// Throwing and dropping
2009-12-29 13:44:16 +00:00
// only if not in house, not grabbing a vehicle and an item selected
2012-02-27 15:24:34 +00:00
// only act on press, not release
2015-09-22 16:22:17 +00:00
if ( ( ctrl = = CON_Throw | | ctrl = = CON_ThrowDelayed ) & & ! house & & ( ! vehicle | | proc = = " ATTACH " ) & & ! release )
2015-03-09 12:33:31 +00:00
{
2015-09-11 19:08:38 +00:00
if ( contents )
2015-09-13 17:04:18 +00:00
{
// Object overloaded throw control?
// Call this before QueryRejectDeparture to allow alternate use of non-droppable objects
2015-09-13 17:09:28 +00:00
if ( contents - > ~ ControlThrow ( this , x , y ) )
2015-09-13 17:04:18 +00:00
return true ;
2015-09-11 19:08:38 +00:00
// The object does not want to be dropped? Still handle command.
if ( contents - > ~ QueryRejectDeparture ( this ) )
return true ;
2015-03-09 12:33:31 +00:00
// just drop in certain situations
var only_drop = proc = = " SCALE " | | proc = = " HANGLE " | | proc = = " SWIM " ;
// also drop if no throw would be possible anyway
if ( only_drop | | Distance ( 0 , 0 , x , y ) < 10 | | ( Abs ( x ) < 10 & & y > 10 ) )
only_drop = true ;
2009-12-29 13:44:16 +00:00
// throw
if ( ctrl = = CON_Throw )
{
2011-02-13 20:37:58 +00:00
CancelUse ( ) ;
2012-02-27 15:24:34 +00:00
2015-03-09 12:33:31 +00:00
if ( only_drop )
2011-03-06 15:59:58 +00:00
return ObjectCommand ( " Drop " , contents ) ;
2010-03-30 16:48:38 +00:00
else
2011-03-06 15:59:58 +00:00
return ObjectCommand ( " Throw " , contents , x , y ) ;
2009-12-29 13:44:16 +00:00
}
2010-02-02 15:09:56 +00:00
// throw delayed
2010-02-04 01:08:21 +00:00
if ( ctrl = = CON_ThrowDelayed )
2010-02-02 15:09:56 +00:00
{
2011-02-13 20:37:58 +00:00
CancelUse ( ) ;
2010-03-03 18:02:47 +00:00
if ( release )
2010-02-04 01:08:21 +00:00
{
VirtualCursor ( ) - > StopAim ( ) ;
2015-03-09 12:33:31 +00:00
if ( only_drop )
2011-03-06 15:59:58 +00:00
return ObjectCommand ( " Drop " , contents ) ;
2010-02-04 01:08:21 +00:00
else
2012-12-25 11:19:23 +00:00
return ObjectCommand ( " Throw " , contents , this . control . mlastx , this . control . mlasty ) ;
2010-02-04 01:08:21 +00:00
}
else
{
2010-03-23 20:57:30 +00:00
VirtualCursor ( ) - > StartAim ( this ) ;
2011-03-06 15:59:58 +00:00
return true ;
2010-02-04 01:08:21 +00:00
}
2010-02-02 15:09:56 +00:00
}
2009-12-29 13:44:16 +00:00
}
}
2010-06-25 17:06:59 +00:00
// Movement controls (defined in PlayerControl.c, partly overloaded here)
if ( ctrl = = CON_Left | | ctrl = = CON_Right | | ctrl = = CON_Up | | ctrl = = CON_Down | | ctrl = = CON_Jump )
2010-09-22 14:49:44 +00:00
{
// forward to script...
if ( house )
{
2011-03-06 15:59:58 +00:00
return ControlMovement2Script ( ctrl , x , y , strength , repeat , release , house ) ;
2010-09-22 14:49:44 +00:00
}
else if ( vehicle )
{
2011-03-06 15:59:58 +00:00
if ( ControlMovement2Script ( ctrl , x , y , strength , repeat , release , vehicle ) ) return true ;
2010-09-22 14:49:44 +00:00
}
2011-03-06 15:59:58 +00:00
return ObjectControlMovement ( plr , ctrl , strength , release ) ;
2010-09-22 14:49:44 +00:00
}
2010-06-25 17:06:59 +00:00
2015-08-07 14:12:41 +00:00
// Do a roll on landing or when standing. This means that the CON_Down was not handled previously.
2015-11-14 13:58:02 +00:00
if ( ctrl = = CON_Roll & & ComDir2XY ( GetComDir ( ) ) [ 0 ] ! = 0 )
2015-08-07 14:12:41 +00:00
{
if ( this - > IsWalking ( ) )
{
2015-09-20 15:53:05 +00:00
if ( this - > Stuck ( ) )
{
// Still show some visual feedback for the player.
this - > DoKneel ( ) ;
}
else
{
this - > DoRoll ( ) ;
}
2015-08-07 14:12:41 +00:00
return true ;
}
}
2015-11-08 18:49:41 +00:00
// Fall through half-solid mask
if ( ctrl = = CON_FallThrough )
{
if ( ! release )
{
if ( this - > IsWalking ( ) )
{
HalfVehicleFadeJumpStart ( ) ;
}
}
else
{
HalfVehicleFadeJumpStop ( ) ;
}
return true ;
}
2015-08-07 14:12:41 +00:00
2013-05-26 01:24:46 +00:00
// hotkeys action bar hotkeys
var hot = 0 ;
if ( ctrl = = CON_InteractionHotkey0 ) hot = 10 ;
if ( ctrl = = CON_InteractionHotkey1 ) hot = 1 ;
if ( ctrl = = CON_InteractionHotkey2 ) hot = 2 ;
if ( ctrl = = CON_InteractionHotkey3 ) hot = 3 ;
if ( ctrl = = CON_InteractionHotkey4 ) hot = 4 ;
if ( ctrl = = CON_InteractionHotkey5 ) hot = 5 ;
if ( ctrl = = CON_InteractionHotkey6 ) hot = 6 ;
if ( ctrl = = CON_InteractionHotkey7 ) hot = 7 ;
if ( ctrl = = CON_InteractionHotkey8 ) hot = 8 ;
if ( ctrl = = CON_InteractionHotkey9 ) hot = 9 ;
if ( hot > 0 )
{
this . control . hotkeypressed = true ;
this - > ~ ControlHotkey ( hot - 1 ) ;
this - > ~ StopInteractionCheck ( ) ; // for GUI_Controller_ActionBar
return true ;
}
2009-12-29 13:44:16 +00:00
// Unhandled control
2012-12-25 11:19:23 +00:00
return _inherited ( plr , ctrl , x , y , strength , repeat , release , . . . ) ;
2010-03-03 18:02:47 +00:00
}
2015-03-26 11:23:20 +00:00
// A wrapper to SetCommand to catch special behaviour for some actions.
2009-12-29 13:44:16 +00:00
public func ObjectCommand ( string command , object target , int tx , int ty , object target2 )
{
// special control for throw and jump
// but only with controls, not with general commands
2012-02-26 14:54:09 +00:00
if ( command = = " Throw " ) return this - > ~ ControlThrow ( target , tx , ty ) ;
else if ( command = = " Jump " ) return this - > ~ ControlJump ( ) ;
2009-12-29 13:44:16 +00:00
// else standard command
2015-03-26 11:23:20 +00:00
else
{
// Make sure to not recollect the item immediately on drops.
if ( command = = " Drop " )
{
// Disable collection for a moment.
if ( target ) this - > OnDropped ( target ) ;
}
return SetCommand ( command , target , tx , ty , target2 ) ;
}
2010-07-31 12:56:15 +00:00
// this function might be obsolete: a normal SetCommand does make a callback to
// script before it is executed: ControlCommand(szCommand, pTarget, iTx, iTy)
2009-12-29 13:44:16 +00:00
}
2015-03-26 11:23:20 +00:00
/*
Called by the engine before a command is executed .
Beware that this is NOT called when SetCommand was called by a script .
At this point I am not sure whether we need this callback at all .
*/
public func ControlCommand ( string command , object target , int tx , int ty )
{
if ( command = = " Drop " )
{
// Disable collection for a moment.
if ( target ) this - > OnDropped ( target ) ;
}
return _inherited ( command , target , tx , ty , . . . ) ;
}
2012-12-29 17:42:19 +00:00
public func ShelveCommand ( object condition_obj , string condition , object callback_obj , string callback , proplist data )
{
this . control . shelved_command = { cond = condition , cond_obj = condition_obj , callback = callback , callback_obj = callback_obj , data = data } ;
AddEffect ( " ShelvedCommand " , this , 1 , 5 , this ) ;
}
public func StopShelvedCommand ( )
{
this . control . shelved_command = nil ;
if ( GetEffect ( " ShelvedCommand " , this ) )
RemoveEffect ( " ShelvedCommand " , this ) ;
}
func FxShelvedCommandTimer ( _ , effect , time )
{
if ( ! this . control . shelved_command ) return - 1 ;
if ( ! this . control . shelved_command . callback_obj ) return - 1 ;
if ( ! this . control . shelved_command . cond_obj ) return - 1 ;
if ( ! this . control . shelved_command . cond_obj - > Call ( this . control . shelved_command . cond , this . control . shelved_command . data ) ) return 1 ;
this . control . shelved_command . callback_obj - > Call ( this . control . shelved_command . callback , this . control . shelved_command . data ) ;
return - 1 ;
}
func FxShelvedCommandStop ( target , effect , reason , temp )
{
if ( temp ) return ;
this . control . shelved_command = nil ;
}
2010-03-25 23:56:55 +00:00
/* ++++++++++++++++++++++++ Use Controls ++++++++++++++++++++++++ */
2010-02-04 15:44:01 +00:00
public func CancelUse ( )
2009-12-29 13:44:16 +00:00
{
2015-03-09 12:33:31 +00:00
if ( ! this . control . current_object )
{
// just forget any possibly stored actions
StopShelvedCommand ( ) ;
return ;
}
2010-06-25 17:20:50 +00:00
2010-03-25 23:56:55 +00:00
// use the saved x,y coordinates for canceling
2012-12-25 11:19:23 +00:00
CancelUseControl ( this . control . mlastx , this . control . mlasty ) ;
2009-12-29 13:44:16 +00:00
}
2012-12-29 17:42:19 +00:00
// to be called during usage of an object to re-start usage as soon as possible
func PauseUse ( object obj , string custom_condition , proplist data )
{
// cancel use first, since it removes old shelved commands
if ( this . control . started_use )
{
CancelUse ( ) ;
this . control . started_use = false ;
}
var callback_obj = this ;
if ( custom_condition ! = nil )
{
callback_obj = obj ;
}
else custom_condition = " CanReIssueCommand " ;
data = data ? ? { } ;
data . obj = obj ;
data . ctrl = CON_Use ;
ShelveCommand ( callback_obj , custom_condition , this , " ReIssueCommand " , data ) ;
}
2013-01-30 20:26:26 +00:00
func DetermineUsageType ( object obj )
2010-10-09 17:08:58 +00:00
{
2012-12-25 11:19:23 +00:00
if ( ! obj ) return nil ;
2010-10-09 17:08:58 +00:00
// house
if ( obj = = Contained ( ) )
return C4D_Structure ;
// object
2012-12-25 11:19:23 +00:00
if ( obj - > Contained ( ) = = this )
return C4D_Object ;
2010-10-09 17:08:58 +00:00
// vehicle
var proc = GetProcedure ( ) ;
if ( obj = = GetActionTarget ( ) )
if ( proc = = " ATTACH " & & proc = = " PUSH " )
return C4D_Vehicle ;
2012-12-25 11:19:23 +00:00
// unknown
2010-10-09 17:08:58 +00:00
return nil ;
}
2013-01-30 20:26:26 +00:00
func GetUseCallString ( string action )
2012-12-25 11:19:23 +00:00
{
2010-10-09 17:08:58 +00:00
// Control... or Contained...
2015-06-27 14:56:45 +00:00
var control_string = " Control " ;
if ( this . control . using_type = = C4D_Structure )
control_string = " Contained " ;
// ..Use.. or ..UseAlt...
var estr = " " ;
if ( this . control . alt & & this . control . using_type ! = C4D_Object )
estr = " Alt " ;
2010-10-09 17:08:58 +00:00
// Action
2015-06-27 14:56:45 +00:00
if ( ! action )
action = " " ;
return Format ( " ~%sUse%s%s " , control_string , estr , action ) ;
2010-10-09 17:08:58 +00:00
}
2012-12-29 17:42:19 +00:00
func CanReIssueCommand ( proplist data )
{
2015-10-23 03:58:15 +00:00
if ( ! data . obj ) return false ;
2012-12-29 17:42:19 +00:00
if ( data . ctrl = = CON_Use )
return ! data . obj - > ~ RejectUse ( this ) ;
if ( data . ctrl = = CON_UseDelayed )
return ! data . obj - > ~ RejectUse ( this ) ;
}
func ReIssueCommand ( proplist data )
{
if ( data . ctrl = = CON_Use )
return StartUseControl ( data . ctrl , this . control . mlastx , this . control . mlasty , data . obj ) ;
if ( data . ctrl = = CON_UseDelayed )
return StartUseDelayedControl ( data . ctrl , data . obj ) ;
}
2013-01-30 20:26:26 +00:00
func StartUseControl ( int ctrl , int x , int y , object obj )
2009-12-29 13:44:16 +00:00
{
2012-12-29 17:42:19 +00:00
this . control . started_use = false ;
if ( obj - > ~ RejectUse ( this ) )
{
// remember for later:
PauseUse ( obj ) ;
// but still catch command
return true ;
}
2016-01-17 19:27:26 +00:00
// Disable climb/hangle actions for the duration of this use
if ( obj . ForceFreeHands & & ! GetEffect ( " IntControlFreeHands " , this ) ) AddEffect ( " IntControlFreeHands " , this , 130 , 0 , this ) ;
2012-12-29 17:42:19 +00:00
2015-02-08 12:30:54 +00:00
obj - > SetController ( GetController ( ) ) ;
2012-12-25 11:19:23 +00:00
this . control . current_object = obj ;
this . control . using_type = DetermineUsageType ( obj ) ;
2015-06-27 14:56:45 +00:00
this . control . alt = ctrl ! = CON_Use ;
2010-10-09 17:08:58 +00:00
2010-01-22 16:29:46 +00:00
var hold_enabled = obj - > Call ( " ~HoldingEnabled " ) ;
2010-02-02 15:09:56 +00:00
if ( hold_enabled )
2010-01-22 16:29:46 +00:00
SetPlayerControlEnabled ( GetOwner ( ) , CON_Aim , true ) ;
2010-10-09 17:08:58 +00:00
2010-01-22 16:29:46 +00:00
// first call UseStart. If unhandled, call Use (mousecontrol)
2010-10-09 17:08:58 +00:00
var handled = obj - > Call ( GetUseCallString ( " Start " ) , this , x , y ) ;
2010-01-22 16:29:46 +00:00
if ( ! handled )
2010-02-04 15:44:01 +00:00
{
2010-10-09 17:08:58 +00:00
handled = obj - > Call ( GetUseCallString ( ) , this , x , y ) ;
2012-12-25 11:19:23 +00:00
this . control . noholdingcallbacks = handled ;
2010-02-04 15:44:01 +00:00
}
2012-12-29 17:42:19 +00:00
2010-01-22 16:29:46 +00:00
if ( ! handled )
{
2012-12-25 11:19:23 +00:00
this . control . current_object = nil ;
this . control . using_type = nil ;
2010-02-02 15:09:56 +00:00
if ( hold_enabled )
2010-01-22 16:29:46 +00:00
SetPlayerControlEnabled ( GetOwner ( ) , CON_Aim , false ) ;
return false ;
}
2012-12-29 17:42:19 +00:00
else
2014-12-25 17:04:19 +00:00
{
2012-12-29 17:42:19 +00:00
this . control . started_use = true ;
2014-12-25 17:04:19 +00:00
// add helper effect that prevents errors when objects are suddenly deleted by quickly cancelling their use beforehand
AddEffect ( " ItemRemovalCheck " , this . control . current_object , 1 , 100 , this , nil ) ; // the slow timer is arbitrary and will just clean up the effect if necessary
}
2009-12-29 13:44:16 +00:00
return handled ;
}
2013-01-30 20:26:26 +00:00
func StartUseDelayedControl ( int ctrl , object obj )
2010-02-02 15:09:56 +00:00
{
2012-12-29 17:42:19 +00:00
this . control . started_use = false ;
if ( obj - > ~ RejectUse ( this ) )
{
// remember for later:
ShelveCommand ( this , " CanReIssueCommand " , this , " ReIssueCommand " , { obj = obj , ctrl = ctrl } ) ;
// but still catch command
return true ;
}
2016-01-17 19:27:26 +00:00
// Disable climb/hangle actions for the duration of this use
if ( obj . ForceFreeHands & & ! GetEffect ( " IntControlFreeHands " , this ) ) AddEffect ( " IntControlFreeHands " , this , 130 , 0 , this ) ;
2012-12-25 11:19:23 +00:00
this . control . current_object = obj ;
this . control . using_type = DetermineUsageType ( obj ) ;
2015-06-27 14:56:45 +00:00
this . control . alt = ctrl ! = CON_UseDelayed ;
2010-02-02 15:09:56 +00:00
2010-03-23 20:57:30 +00:00
VirtualCursor ( ) - > StartAim ( this ) ;
2010-02-04 01:08:21 +00:00
2010-02-02 15:09:56 +00:00
// call UseStart
2012-12-25 11:19:23 +00:00
var handled = obj - > Call ( GetUseCallString ( " Start " ) , this , this . control . mlastx , this . control . mlasty ) ;
this . control . noholdingcallbacks = ! handled ;
2010-02-04 01:08:21 +00:00
2012-12-29 17:42:19 +00:00
if ( handled )
this . control . started_use = true ;
2010-02-02 15:09:56 +00:00
return handled ;
}
2013-01-30 20:26:26 +00:00
func CancelUseControl ( int x , int y )
2010-01-22 16:29:46 +00:00
{
2012-12-29 17:42:19 +00:00
// forget possibly stored commands
StopShelvedCommand ( ) ;
2010-05-28 19:29:32 +00:00
// to horse first (if there is one)
var horse = GetActionTarget ( ) ;
2012-12-25 11:19:23 +00:00
if ( horse & & GetProcedure ( ) = = " ATTACH " & & this . control . current_object ! = horse )
2010-10-09 17:08:58 +00:00
StopUseControl ( x , y , horse , true ) ;
2010-05-28 19:29:32 +00:00
2012-12-25 11:19:23 +00:00
return StopUseControl ( x , y , this . control . current_object , true ) ;
2010-01-22 16:29:46 +00:00
}
2013-01-30 20:26:26 +00:00
func StopUseControl ( int x , int y , object obj , bool cancel )
2009-12-29 13:44:16 +00:00
{
2010-01-22 16:29:46 +00:00
var stop = " Stop " ;
2010-02-02 15:09:56 +00:00
if ( cancel ) stop = " Cancel " ;
2010-01-22 16:29:46 +00:00
2015-06-27 14:56:45 +00:00
// ControlUseStop, ControlUseAltStop, ContainedUseAltStop, ContainedUseCancel, etc...
2010-10-09 17:08:58 +00:00
var handled = obj - > Call ( GetUseCallString ( stop ) , this , x , y ) ;
2012-12-25 11:19:23 +00:00
if ( obj = = this . control . current_object )
2010-05-28 19:29:32 +00:00
{
2011-02-13 20:49:02 +00:00
// if ControlUseStop returned -1, the current object is kept as "used object"
// but no more callbacks except for ControlUseCancel are made. The usage of this
// object is finally cancelled on ControlUseCancel.
if ( cancel | | handled ! = - 1 )
{
2014-12-25 17:04:19 +00:00
// look for correct removal helper effect and remove it
var effect_index = 0 ;
var removal_helper = nil ;
do
{
removal_helper = GetEffect ( " ItemRemovalCheck " , this . control . current_object , effect_index + + ) ;
if ( ! removal_helper ) break ;
if ( removal_helper . CommandTarget ! = this ) continue ;
break ;
} while ( true ) ;
2016-01-17 19:27:26 +00:00
RemoveEffect ( " IntControlFreeHands " , this ) ; // make sure we can climb again
2014-12-25 17:04:19 +00:00
2012-12-25 11:19:23 +00:00
this . control . current_object = nil ;
this . control . using_type = nil ;
2015-06-27 14:56:45 +00:00
this . control . alt = false ;
2014-12-25 17:04:19 +00:00
if ( removal_helper )
{
RemoveEffect ( nil , nil , removal_helper , true ) ;
}
2011-02-13 20:49:02 +00:00
}
2012-12-25 11:19:23 +00:00
this . control . noholdingcallbacks = false ;
2010-05-28 19:29:32 +00:00
SetPlayerControlEnabled ( GetOwner ( ) , CON_Aim , false ) ;
2010-02-24 22:53:17 +00:00
2010-05-28 19:29:32 +00:00
if ( virtual_cursor )
virtual_cursor - > StopAim ( ) ;
}
2010-02-02 15:09:56 +00:00
return handled ;
}
2013-01-30 20:26:26 +00:00
func HoldingUseControl ( int ctrl , int x , int y , object obj )
2010-02-04 01:08:21 +00:00
{
var mex = x ;
var mey = y ;
2015-06-27 14:56:45 +00:00
if ( ctrl = = CON_UseDelayed | | ctrl = = CON_UseAltDelayed )
2010-02-04 01:08:21 +00:00
{
2012-12-25 11:19:23 +00:00
mex = this . control . mlastx ;
mey = this . control . mlasty ;
2010-02-04 01:08:21 +00:00
}
2010-02-04 15:44:01 +00:00
//Message("%d,%d",this,mex,mey);
2010-02-25 11:47:13 +00:00
2010-03-23 20:57:30 +00:00
// automatic adjustment of the direction
2010-02-25 11:47:13 +00:00
// --------------------
// if this is desired for ALL objects is the question, we will find out.
// For now, this is done for items and vehicles, not for buildings and
2012-12-25 11:19:23 +00:00
// mounts (naturally). If turning vehicles just like that without issuing
2010-02-25 11:47:13 +00:00
// a turning animation is the question. But after all, the clonk can turn
// by setting the dir too.
2010-03-08 21:10:55 +00:00
// not riding and not in building not while scaling
if ( GetProcedure ( ) ! = " ATTACH " & & ! Contained ( ) & & GetProcedure ( ) ! = " SCALE " )
2010-02-25 11:47:13 +00:00
{
// pushing vehicle: object to turn is the vehicle
var dir_obj = GetActionTarget ( ) ;
2010-10-07 16:02:52 +00:00
if ( GetProcedure ( ) ! = " PUSH " ) dir_obj = nil ;
2010-02-25 11:47:13 +00:00
// otherwise, turn the clonk
2010-03-03 18:02:47 +00:00
if ( ! dir_obj ) dir_obj = this ;
2010-02-25 11:47:13 +00:00
if ( ( dir_obj - > GetComDir ( ) = = COMD_Stop & & dir_obj - > GetXDir ( ) = = 0 ) | | dir_obj - > GetProcedure ( ) = = " FLIGHT " )
{
if ( dir_obj - > GetDir ( ) = = DIR_Left )
{
if ( mex > 0 )
dir_obj - > SetDir ( DIR_Right ) ;
}
else
{
if ( mex < 0 )
dir_obj - > SetDir ( DIR_Left ) ;
}
}
}
2010-02-04 01:08:21 +00:00
2010-10-09 17:08:58 +00:00
var handled = obj - > Call ( GetUseCallString ( " Holding " ) , this , mex , mey ) ;
2010-02-04 01:08:21 +00:00
return handled ;
}
2013-01-30 20:26:26 +00:00
func StopUseDelayedControl ( object obj )
2010-02-02 15:09:56 +00:00
{
2015-06-27 14:56:45 +00:00
// ControlUseStop, ControlUseAltStop, ContainedUseAltStop, etc...
var handled = obj - > Call ( GetUseCallString ( " Stop " ) , this , this . control . mlastx , this . control . mlasty ) ;
2010-02-02 15:09:56 +00:00
if ( ! handled )
2015-06-27 14:56:45 +00:00
handled = obj - > Call ( GetUseCallString ( ) , this , this . control . mlastx , this . control . mlasty ) ;
2010-02-04 01:08:21 +00:00
2012-12-25 11:19:23 +00:00
if ( obj = = this . control . current_object )
2010-05-28 19:29:32 +00:00
{
VirtualCursor ( ) - > StopAim ( ) ;
2011-02-13 20:49:02 +00:00
// see StopUseControl
if ( handled ! = - 1 )
{
2012-12-25 11:19:23 +00:00
this . control . current_object = nil ;
this . control . using_type = nil ;
2015-06-27 14:56:45 +00:00
this . control . alt = false ;
2011-02-13 20:49:02 +00:00
}
2012-12-25 11:19:23 +00:00
this . control . noholdingcallbacks = false ;
2010-05-28 19:29:32 +00:00
}
2009-12-29 13:44:16 +00:00
return handled ;
}
2014-12-25 17:04:19 +00:00
// very infrequent timer to prevent dangling effects, this is not necessary for correct functioning
func FxItemRemovalCheckTimer ( object target , proplist effect , int time )
{
if ( ! effect . CommandTarget ) return - 1 ;
if ( effect . CommandTarget . control . current_object ! = target ) return - 1 ;
return 1 ;
}
// this will be called when an inventory object (that is in use) is suddenly removed
func FxItemRemovalCheckStop ( object target , proplist effect , int reason , bool temporary )
{
if ( temporary ) return ;
// only trigger when the object has been removed
if ( reason ! = FX_Call_RemoveClear ) return ;
// safety
if ( ! effect . CommandTarget ) return ;
if ( effect . CommandTarget . control . current_object ! = target ) return ;
// quickly cancel use in a clean way while the object is still available
effect . CommandTarget - > CancelUse ( ) ;
return ;
}
2010-03-22 15:40:54 +00:00
2010-07-17 14:37:44 +00:00
// Control use redirected to script
2013-01-30 20:26:26 +00:00
func ControlUse2Script ( int ctrl , int x , int y , int strength , bool repeat , bool release , object obj )
2015-03-09 12:33:31 +00:00
{
2010-02-02 15:09:56 +00:00
// standard use
2015-06-27 14:56:45 +00:00
if ( ctrl = = CON_Use | | ctrl = = CON_UseAlt )
2009-12-29 13:44:16 +00:00
{
if ( ! release & & ! repeat )
{
2010-10-09 17:08:58 +00:00
return StartUseControl ( ctrl , x , y , obj ) ;
2009-12-29 13:44:16 +00:00
}
2012-12-25 11:19:23 +00:00
else if ( release & & obj = = this . control . current_object )
2009-12-29 13:44:16 +00:00
{
2010-10-09 17:08:58 +00:00
return StopUseControl ( x , y , obj ) ;
2009-12-29 13:44:16 +00:00
}
2010-02-02 15:09:56 +00:00
}
// gamepad use
2015-06-27 14:56:45 +00:00
else if ( ctrl = = CON_UseDelayed | | ctrl = = CON_UseAltDelayed )
2010-02-02 15:09:56 +00:00
{
if ( ! release & & ! repeat )
{
2010-10-09 17:08:58 +00:00
return StartUseDelayedControl ( ctrl , obj ) ;
2010-02-02 15:09:56 +00:00
}
2012-12-25 11:19:23 +00:00
else if ( release & & obj = = this . control . current_object )
2010-02-02 15:09:56 +00:00
{
2010-10-09 17:08:58 +00:00
return StopUseDelayedControl ( obj ) ;
2010-02-02 15:09:56 +00:00
}
}
// more use (holding)
2015-06-27 14:56:45 +00:00
if ( ctrl = = CON_Use | | ctrl = = CON_UseAlt | | ctrl = = CON_UseDelayed | | ctrl = = CON_UseAltDelayed )
2010-02-02 15:09:56 +00:00
{
2010-03-22 18:33:24 +00:00
if ( release )
2010-01-03 22:16:35 +00:00
{
2010-03-25 23:56:55 +00:00
// leftover use release
CancelUse ( ) ;
return true ;
2010-01-03 22:16:35 +00:00
}
2012-12-25 11:19:23 +00:00
else if ( repeat & & ! this . control . noholdingcallbacks )
2009-12-29 13:44:16 +00:00
{
2010-10-09 17:08:58 +00:00
return HoldingUseControl ( ctrl , x , y , obj ) ;
2009-12-29 13:44:16 +00:00
}
}
2010-07-17 14:37:44 +00:00
return false ;
}
// Control use redirected to script
2013-01-30 20:26:26 +00:00
func ControlMovement2Script ( int ctrl , int x , int y , int strength , bool repeat , bool release , object obj )
2010-07-17 14:37:44 +00:00
{
2009-12-29 13:44:16 +00:00
// overloads of movement commandos
2010-07-17 14:37:44 +00:00
if ( ctrl = = CON_Left | | ctrl = = CON_Right | | ctrl = = CON_Down | | ctrl = = CON_Up | | ctrl = = CON_Jump )
2009-12-29 13:44:16 +00:00
{
2015-06-27 14:56:45 +00:00
var control_string = " Control " ;
if ( Contained ( ) = = obj )
control_string = " Contained " ;
2010-10-09 17:08:58 +00:00
2009-12-29 13:44:16 +00:00
if ( release )
{
// if any movement key has been released, ControlStop is called
2015-06-27 14:56:45 +00:00
if ( obj - > Call ( Format ( " ~%sStop " , control_string ) , this , ctrl ) )
return true ;
2009-12-29 13:44:16 +00:00
}
else
{
2012-12-25 11:19:23 +00:00
// what about gamepad-deadzone?
2010-02-24 22:53:17 +00:00
if ( strength ! = nil & & strength < CON_Gamepad_Deadzone )
return true ;
2009-12-29 13:44:16 +00:00
// Control*
2015-06-27 14:56:45 +00:00
if ( ctrl = = CON_Left ) if ( obj - > Call ( Format ( " ~%sLeft " , control_string ) , this ) ) return true ;
if ( ctrl = = CON_Right ) if ( obj - > Call ( Format ( " ~%sRight " , control_string ) , this ) ) return true ;
if ( ctrl = = CON_Up ) if ( obj - > Call ( Format ( " ~%sUp " , control_string ) , this ) ) return true ;
if ( ctrl = = CON_Down ) if ( obj - > Call ( Format ( " ~%sDown " , control_string ) , this ) ) return true ;
2010-02-23 11:50:14 +00:00
// for attached (e.g. horse: also Jump command
2010-03-03 18:02:47 +00:00
if ( GetProcedure ( ) = = " ATTACH " )
if ( ctrl = = CON_Jump ) if ( obj - > Call ( " ControlJump " , this ) ) return true ;
2009-12-29 13:44:16 +00:00
}
}
2010-07-17 14:37:44 +00:00
2009-12-29 13:44:16 +00:00
}
2016-01-17 19:27:26 +00:00
// Effect to free/unfree hands by disabling/enabling scale and hangle procedures
public func FxIntControlFreeHandsStart ( object target , proplist fx , int temp )
{
// Process on non-temp as well in case scale/handle effects need to stack
// Stop current action
var proc = GetProcedure ( ) ;
if ( proc = = " SCALE " | | proc = = " HANGLE " ) SetAction ( " Walk " ) ;
// Make sure ActMap is writable
if ( this . ActMap = = this . Prototype . ActMap ) this . ActMap = new this . ActMap { } ;
// Kill scale/hangle effects
fx . act_scale = this . ActMap . Scale ;
this . ActMap . Scale = nil ;
fx . act_hangle = this . ActMap . Hangle ;
this . ActMap . Hangle = nil ;
return FX_OK ;
}
public func FxIntControlFreeHandsStop ( object target , proplist fx , int reason , bool temp )
{
// Restore scale/hangle effects (engine will handle re-grabbing walls if needed)
if ( fx . act_scale ) this . ActMap . Scale = fx . act_scale ;
if ( fx . act_hangle ) this . ActMap . Hangle = fx . act_hangle ;
return FX_OK ;
}
2010-03-25 23:56:55 +00:00
// returns true if the clonk is able to enter a building (procedurewise)
2009-12-29 13:44:16 +00:00
public func CanEnter ( )
{
var proc = GetProcedure ( ) ;
if ( proc ! = " WALK " & & proc ! = " SWIM " & & proc ! = " SCALE " & &
proc ! = " HANGLE " & & proc ! = " FLOAT " & & proc ! = " FLIGHT " & &
proc ! = " PUSH " ) return false ;
return true ;
}
2010-06-25 17:06:59 +00:00
public func IsMounted ( ) { return GetProcedure ( ) = = " ATTACH " ; }
2010-03-25 23:56:55 +00:00
/* +++++++++++++++++++++++ Menu control +++++++++++++++++++++++ */
2010-03-23 20:57:30 +00:00
func HasMenuControl ( )
{
return true ;
}
2014-02-26 11:03:27 +00:00
// helper function that can be attached to a proplist to set callbacks on-the-fly
func GetTrue ( ) { return true ; }
/*
Sets the menu this Clonk currently has focus of . Old menus that have been opened via SetMenu will be closed , making sure that only one menu is open at a time .
Additionally , the Clonk ' s control is disabled while a menu is open .
The menu parameter can either be an object that closes its menu via a Close ( ) callback or it can be a menu ID as returned by GuiOpen . When / menu / is such an ID ,
the menu will be closed via GuiClose when a new menu is opened . If you need to do cleaning up , you will have to use the OnClose callback of the menu .
When you call SetMenu with a menu ID , you should also call clonk - > MenuClosed ( ) , once your menu is closed .
*/
func SetMenu ( new_menu , bool unclosable )
2010-03-23 20:57:30 +00:00
{
2014-02-26 11:03:27 +00:00
unclosable = unclosable ? ? false ;
2016-01-29 21:48:59 +00:00
var current_menu = this . control . menu ;
2014-02-26 11:03:27 +00:00
// no news?
if ( new_menu ) // if new_menu==nil, it is important that we still do the cleaning-up below even if we didn't have a menu before (see MenuClosed())
2016-01-29 21:48:59 +00:00
if ( current_menu = = new_menu ) return ;
2014-02-26 11:03:27 +00:00
// close old one!
2016-01-29 21:48:59 +00:00
if ( current_menu ! = nil )
2010-05-07 22:10:09 +00:00
{
2016-01-29 21:48:59 +00:00
if ( GetType ( current_menu ) = = C4V_C4Object )
current_menu - > Close ( ) ;
else if ( GetType ( current_menu ) = = C4V_PropList )
GuiClose ( current_menu . ID ) ;
2014-02-26 11:03:27 +00:00
else
FatalError ( " Library_ClonkControl::SetMenu() was called with invalid parameter. " ) ;
2010-05-07 22:10:09 +00:00
}
2014-02-26 11:03:27 +00:00
else
2010-03-23 20:57:30 +00:00
{
2014-02-26 11:03:27 +00:00
// we have a new menu but didn't have another one before? Enable menu controls!
if ( new_menu )
{
CancelUse ( ) ;
// stop clonk
SetComDir ( COMD_Stop ) ;
if ( PlayerHasVirtualCursor ( GetOwner ( ) ) )
VirtualCursor ( ) - > StartAim ( this , false , new_menu ) ;
else
{
if ( GetType ( new_menu ) = = C4V_C4Object & & new_menu - > ~ CursorUpdatesEnabled ( ) )
SetPlayerControlEnabled ( GetOwner ( ) , CON_GUICursor , true ) ;
SetPlayerControlEnabled ( GetOwner ( ) , CON_GUIClick1 , true ) ;
SetPlayerControlEnabled ( GetOwner ( ) , CON_GUIClick2 , true ) ;
}
}
2010-03-23 20:57:30 +00:00
}
2010-03-26 14:43:17 +00:00
2014-02-26 11:03:27 +00:00
if ( new_menu )
{
if ( GetType ( new_menu ) = = C4V_C4Object )
{
2016-01-29 21:48:59 +00:00
this . control . menu = new_menu ;
2014-02-26 11:03:27 +00:00
}
else if ( GetType ( new_menu ) = = C4V_Int )
{
// add a proplist, so that it is always safe to call functions on clonk->GetMenu()
2016-01-29 21:48:59 +00:00
this . control . menu =
2014-02-26 11:03:27 +00:00
{
ID = new_menu
} ;
}
2010-03-23 20:57:30 +00:00
else
2014-02-26 11:03:27 +00:00
FatalError ( " Library_ClonkControl::SetMenu called with invalid parameter! " ) ;
// make sure the menu is unclosable even if it is just a GUI ID
if ( unclosable )
2010-04-25 01:36:52 +00:00
{
2016-01-29 21:48:59 +00:00
this . control . menu . Unclosable = Library_ClonkControl . GetTrue ;
2010-04-25 01:36:52 +00:00
}
2010-03-23 20:57:30 +00:00
}
2014-02-26 11:03:27 +00:00
else
2010-03-23 20:57:30 +00:00
{
2014-02-26 11:03:27 +00:00
// always disable cursors, even if no old menu existed, because it can happen that a menu removes itself and thus the Clonk never knows whether the cursors are active or not
2012-12-25 11:19:23 +00:00
RemoveVirtualCursor ( ) ; // for gamepads
2010-04-25 01:36:52 +00:00
SetPlayerControlEnabled ( GetOwner ( ) , CON_GUICursor , false ) ;
SetPlayerControlEnabled ( GetOwner ( ) , CON_GUIClick1 , false ) ;
SetPlayerControlEnabled ( GetOwner ( ) , CON_GUIClick2 , false ) ;
2014-02-26 11:03:27 +00:00
2016-01-29 21:48:59 +00:00
this . control . menu = nil ;
2010-03-23 20:57:30 +00:00
}
2016-01-29 21:48:59 +00:00
return this . control . menu ;
2010-03-23 20:57:30 +00:00
}
2010-04-28 22:52:02 +00:00
func MenuClosed ( )
{
2014-02-26 11:03:27 +00:00
// make sure not to clean up the menu again
2016-01-29 21:48:59 +00:00
this . control . menu = nil ;
2014-02-26 11:03:27 +00:00
// and remove cursors etc.
2010-04-28 22:52:02 +00:00
SetMenu ( nil ) ;
}
2014-02-26 11:03:27 +00:00
/*
Returns the current menu or nil . If a menu is returned , it is always a proplist ( but not necessarily an object ) .
Stuff like if ( clonk - > GetMenu ( ) ) clonk - > GetMenu ( ) - > ~ IsClosable ( ) ; is always safe .
If you want to remove the menu , the suggested method is clonk - > TryCancelMenu ( ) to handle unclosable menus correctly .
*/
2010-03-23 20:57:30 +00:00
func GetMenu ( )
{
2016-01-29 21:48:59 +00:00
return this . control . menu ;
2010-03-23 20:57:30 +00:00
}
2014-02-26 11:03:27 +00:00
// Returns true when an existing menu was closed
2010-04-09 15:06:28 +00:00
func CancelMenu ( )
{
2016-01-29 21:48:59 +00:00
if ( this . control . menu )
2012-04-14 20:53:36 +00:00
{
SetMenu ( nil ) ;
return true ;
}
return false ;
2010-04-09 15:06:28 +00:00
}
2014-02-26 11:03:27 +00:00
// Tries to cancel a non-unclosable menu. Returns true when there is no menu left after this call (even if there never was one).
func TryCancelMenu ( )
{
2016-01-29 21:48:59 +00:00
if ( ! this . control . menu ) return true ;
if ( this . control . menu - > ~ Unclosable ( ) ) return false ;
2014-02-26 11:03:27 +00:00
CancelMenu ( ) ;
return true ;
}
2010-03-25 23:56:55 +00:00
2016-01-02 12:02:45 +00:00
public func RejectShiftCursor ( )
{
2016-01-29 21:48:59 +00:00
if ( this . control . menu & & this . control . menu - > ~ Unclosable ( ) ) return true ;
2016-01-02 12:02:45 +00:00
return _inherited ( . . . ) ;
}
public func OnShiftCursor ( )
{
TryCancelMenu ( ) ;
return _inherited ( . . . ) ;
}
2010-03-25 23:56:55 +00:00
/* +++++++++++++++ Throwing, jumping +++++++++++++++ */
2009-12-29 13:44:16 +00:00
// Throwing
2013-01-30 20:26:26 +00:00
func DoThrow ( object obj , int angle )
2009-12-29 13:44:16 +00:00
{
2010-03-30 16:48:38 +00:00
// parameters...
var iX , iY , iR , iXDir , iYDir , iRDir ;
2012-10-21 22:11:24 +00:00
iX = 4 ; if ( ! GetDir ( ) ) iX = - iX ;
iY = Cos ( angle , - 4 ) ;
2010-03-30 16:48:38 +00:00
iR = Random ( 360 ) ;
iRDir = RandomX ( - 10 , 10 ) ;
2010-12-13 01:17:21 +00:00
iXDir = Sin ( angle , this . ThrowSpeed ) ;
iYDir = Cos ( angle , - this . ThrowSpeed ) ;
2010-03-30 16:48:38 +00:00
// throw boost (throws stronger upwards than downwards)
if ( iYDir < 0 ) iYDir = iYDir * 13 / 10 ;
if ( iYDir > 0 ) iYDir = iYDir * 8 / 10 ;
// add own velocity
2010-12-13 01:17:21 +00:00
iXDir + = GetXDir ( 100 ) / 2 ;
iYDir + = GetYDir ( 100 ) / 2 ;
2010-03-30 16:48:38 +00:00
// throw
2010-09-13 15:27:48 +00:00
obj - > Exit ( iX , iY , iR , 0 , 0 , iRDir ) ;
2010-12-13 01:17:21 +00:00
obj - > SetXDir ( iXDir , 100 ) ;
obj - > SetYDir ( iYDir , 100 ) ;
2010-03-30 16:48:38 +00:00
return true ;
2009-12-29 13:44:16 +00:00
}
// custom throw
2012-12-29 13:08:53 +00:00
// implemented in Clonk.ocd/Animations.ocd
public func ControlThrow ( ) { return _inherited ( . . . ) ; }
2009-12-29 13:44:16 +00:00
public func ControlJump ( )
{
var ydir = 0 ;
2010-02-02 15:09:56 +00:00
if ( GetProcedure ( ) = = " WALK " )
2009-12-29 13:44:16 +00:00
{
2010-12-12 22:49:18 +00:00
ydir = this . JumpSpeed ;
2009-12-29 13:44:16 +00:00
}
2011-06-03 19:11:23 +00:00
if ( InLiquid ( ) )
2009-12-29 13:44:16 +00:00
{
2010-04-27 20:41:27 +00:00
if ( ! GBackSemiSolid ( 0 , - 5 ) )
2010-12-12 22:49:18 +00:00
ydir = BoundBy ( this . JumpSpeed * 3 / 5 , 240 , 380 ) ;
2011-06-03 19:11:23 +00:00
}
2015-12-08 16:22:13 +00:00
// Jump speed of the wall kick is halved.
if ( GetProcedure ( ) = = " SCALE " | | GetAction ( ) = = " Climb " )
2011-06-03 19:11:23 +00:00
{
ydir = this . JumpSpeed / 2 ;
}
2009-12-29 13:44:16 +00:00
2010-12-08 15:46:54 +00:00
if ( ydir & & ! Stuck ( ) )
2009-12-29 13:44:16 +00:00
{
SetPosition ( GetX ( ) , GetY ( ) - 1 ) ;
2011-06-03 19:11:23 +00:00
2015-12-08 16:22:13 +00:00
// Wall kick if scaling or climbing.
if ( GetProcedure ( ) = = " SCALE " | | GetAction ( ) = = " Climb " )
2011-06-03 19:11:23 +00:00
{
AddEffect ( " WallKick " , this , 1 ) ;
SetAction ( " Jump " ) ;
var xdir ;
if ( GetDir ( ) = = DIR_Right )
{
xdir = - 1 ;
SetDir ( DIR_Left ) ;
}
else if ( GetDir ( ) = = DIR_Left )
{
xdir = 1 ;
SetDir ( DIR_Right ) ;
}
SetYDir ( - ydir * GetCon ( ) , 100 * 100 ) ;
SetXDir ( xdir * 17 ) ;
return true ;
}
//Normal jump
else
{
SetAction ( " Jump " ) ;
SetYDir ( - ydir * GetCon ( ) , 100 * 100 ) ;
return true ;
}
2009-12-29 13:44:16 +00:00
}
return false ;
}
2010-12-12 22:49:18 +00:00
2015-09-16 01:13:53 +00:00
// Interaction with clonks is special:
// * The clonk opening the menu should always have higher priority so the clonk is predictably selected on the left side even if standing behind e.g. a crate
// * Other clonks should be behind because interaction with them is rare but having your fellow players stand in front of a building is very common
// (Allies also tend to run in front just when you opened that menu...)
func GetInteractionPriority ( object target )
{
// Self with high priority
if ( target = = this ) return 100 ;
2015-12-29 17:25:23 +00:00
// Dead Clonks are shown (for a death message e.g.) but sorted to the bottom.
if ( ! GetAlive ( ) ) return - 190 ;
2015-09-16 01:13:53 +00:00
var owner = NO_OWNER ;
if ( target ) owner = target - > GetOwner ( ) ;
// Prefer own clonks for item transfer
if ( owner = = GetOwner ( ) ) return - 100 ;
// If no own clonk, prefer friendly
if ( ! Hostile ( owner , GetOwner ( ) ) ) return - 120 ;
// Hostile clonks? Lowest priority.
return - 200 ;
}