Control: Extract library for using objects

External packs use this functionality without using the whole default Clonk control.
ipv6
Mark 2017-01-03 16:52:35 +01:00
parent 577da1566a
commit 39c54c323c
3 changed files with 447 additions and 376 deletions

View File

@ -34,6 +34,7 @@
#include Library_Inventory
#include Library_ClonkInventoryControl
#include Library_ClonkInteractionControl
#include Library_ClonkUseControl
#include Library_ClonkGamepadControl
// used for interaction with objects
@ -55,14 +56,9 @@ static const DEFAULT_THROWING_ANGLE = 500;
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
this.control.alt: alternate usage by right mouse button
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
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
this.control.menu: the menu that is currently assigned to the Clonk. Use the methods SetMenu/GetMenu/etc to access it.
*/
@ -82,32 +78,17 @@ protected func Construction()
this.control.hotkeypressed = false;
this.control.alt = false;
this.control.current_object = nil;
this.control.using_type = nil;
this.control.shelved_command = nil;
this.control.menu = nil;
return _inherited(...);
}
public func GetUsedObject() { return this.control.current_object; }
// 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(...); }
// ...aaand the same for when the clonk is deselected
protected func CrewSelection(bool unselect)
{
if (unselect)
{
// cancel usage on unselect first...
CancelUse();
// and if there is still a menu, cancel it too...
// if there is still a menu, cancel it too...
CancelMenu();
}
return _inherited(unselect,...);
@ -115,16 +96,14 @@ protected func CrewSelection(bool unselect)
protected func Destruction()
{
// close open menus, cancel usage...
CancelUse();
// close open menus, ...
CancelMenu();
return _inherited(...);
}
protected func Death()
{
// close open menus, cancel usage...
CancelUse();
// close open menus, ...
CancelMenu();
return _inherited(...);
}
@ -218,7 +197,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
For the rest of the control code, it looks like the x,y coordinates
came from CON_Use.
*/
if (this.control.current_object && ctrl == CON_Aim)
if (GetUsedObject() && ctrl == CON_Aim)
{
if (this.control.alt) ctrl = CON_UseAlt;
else ctrl = CON_Use;
@ -318,16 +297,16 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// handled if the horse is the used object
// ("using" is set to the object in StartUseControl - when the
// object returns true on that callback. Exactly what we want)
if (this.control.current_object == vehicle) return true;
if (GetUsedObject() == vehicle) return true;
// has been cancelled (it is not the start of the usage but no object is used)
// if (vehicle && !this.control.current_object && (repeat || status == CONS_Up)) return true;
// if (vehicle && !GetUsedObject() && (repeat || status == CONS_Up)) return true;
}
}
// releasing the use-key always cancels shelved commands (in that case no this.control.current_object exists)
// releasing the use-key always cancels shelved commands (in that case no GetUsedObject() exists)
if(status == CONS_Up) StopShelvedCommand();
// Release commands are always forwarded even if contents is 0, in case we
// need to cancel use of an object that left inventory
if (contents || (status == CONS_Up && this.control.current_object))
if (contents || (status == CONS_Up && GetUsedObject()))
{
if (ControlUse2Script(ctrl, x, y, strength, repeat, status, contents))
return true;
@ -336,7 +315,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// 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;
var currently_in_use = GetUsedObject() != nil;
if (ctrl == CON_Throw && currently_in_use && status == CONS_Down)
{
CancelUse();
@ -505,351 +484,7 @@ public func ControlCommand(string command, object target, int tx, int ty)
return _inherited(command, target, tx, ty, ...);
}
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;
}
/* ++++++++++++++++++++++++ Use Controls ++++++++++++++++++++++++ */
public func CancelUse()
{
if (!this.control.current_object)
{
// just forget any possibly stored actions
StopShelvedCommand();
return;
}
// use the saved x,y coordinates for canceling
CancelUseControl(this.control.mlastx, this.control.mlasty);
}
// 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);
}
func DetermineUsageType(object obj)
{
if(!obj) return nil;
// house
if (obj == Contained())
return C4D_Structure;
// object
if (obj->Contained() == this)
return C4D_Object;
// vehicle
var proc = GetProcedure();
if (obj == GetActionTarget())
if (proc == "ATTACH" && proc == "PUSH")
return C4D_Vehicle;
// unknown
return nil;
}
func GetUseCallString(string action)
{
// Control... or Contained...
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";
// Action
if (!action)
action = "";
return Format("~%sUse%s%s", control_string, estr, action);
}
func CanReIssueCommand(proplist data)
{
if (!data.obj) return false;
if(data.ctrl == CON_Use)
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);
}
func StartUseControl(int ctrl, int x, int y, object obj)
{
this.control.started_use = false;
if(obj->~RejectUse(this))
{
// remember for later:
PauseUse(obj);
// but still catch command
return true;
}
// Disable climb/hangle actions for the duration of this use
if (obj.ForceFreeHands && !GetEffect("IntControlFreeHands", this)) AddEffect("IntControlFreeHands", this, 130, 0, this);
obj->SetController(GetController());
this.control.current_object = obj;
this.control.using_type = DetermineUsageType(obj);
this.control.alt = ctrl != CON_Use;
if (HasVirtualCursor())
{
var cursor = VirtualCursor(), angle;
if (!cursor->IsActive() && (angle = obj->~DefaultCrosshairAngle(this, GetDir()*2 - 1)))
{
x = +Sin(angle, CURSOR_Radius, 10);
y = -Cos(angle, CURSOR_Radius, 10);
}
cursor->StartAim(this, angle);
}
var hold_enabled = obj->Call("~HoldingEnabled");
if (hold_enabled)
SetPlayerControlEnabled(GetOwner(), CON_Aim, true);
// first call UseStart. If unhandled, call Use (mousecontrol)
var handled = obj->Call(GetUseCallString("Start"),this,x,y);
if (!handled)
{
handled = obj->Call(GetUseCallString(),this,x,y);
this.control.noholdingcallbacks = handled;
}
else
{
// *Start was handled. So clean up possible old noholdingcallbacks-values.
this.control.noholdingcallbacks = false;
}
if (!handled)
{
this.control.current_object = nil;
this.control.using_type = nil;
if (hold_enabled)
SetPlayerControlEnabled(GetOwner(), CON_Aim, false);
return false;
}
else
{
this.control.started_use = true;
// 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
}
return handled;
}
func CancelUseControl(int x, int y)
{
// forget possibly stored commands
StopShelvedCommand();
// to horse first (if there is one)
var horse = GetActionTarget();
if(horse && GetProcedure() == "ATTACH" && this.control.current_object != horse)
StopUseControl(x, y, horse, true);
return StopUseControl(x, y, this.control.current_object, true);
}
func StopUseControl(int x, int y, object obj, bool cancel)
{
var stop = "Stop";
if (cancel) stop = "Cancel";
// ControlUseStop, ControlUseAltStop, ContainedUseAltStop, ContainedUseCancel, etc...
var handled = obj->Call(GetUseCallString(stop),this,x,y);
if (obj == this.control.current_object)
{
// 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)
{
// 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);
RemoveEffect("IntControlFreeHands", this); // make sure we can climb again
this.control.current_object = nil;
this.control.using_type = nil;
this.control.alt = false;
if (removal_helper)
{
RemoveEffect(nil, nil, removal_helper, true);
}
}
this.control.noholdingcallbacks = false;
SetPlayerControlEnabled(GetOwner(), CON_Aim, false);
if (virtual_cursor)
virtual_cursor->StopAim();
}
return handled;
}
func HoldingUseControl(int ctrl, int x, int y, object obj)
{
var mex = x;
var mey = y;
//Message("%d,%d",this,mex,mey);
// automatic adjustment of the direction
// --------------------
// 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
// mounts (naturally). If turning vehicles just like that without issuing
// a turning animation is the question. But after all, the clonk can turn
// by setting the dir too.
// not riding and not in building not while scaling
if (GetProcedure() != "ATTACH" && !Contained() && GetProcedure() != "SCALE")
{
// pushing vehicle: object to turn is the vehicle
var dir_obj = GetActionTarget();
if (GetProcedure() != "PUSH") dir_obj = nil;
// otherwise, turn the clonk
if (!dir_obj) dir_obj = this;
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);
}
}
}
var handled = obj->Call(GetUseCallString("Holding"),this,mex,mey);
return handled;
}
// 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;
}
// Control use redirected to script
func ControlUse2Script(int ctrl, int x, int y, int strength, bool repeat, int status, object obj)
{
// standard use
if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
if (status == CONS_Down && !repeat)
{
return StartUseControl(ctrl,x, y, obj);
}
else if (status == CONS_Up && (obj == this.control.current_object || obj == GetActionTarget()))
{
return StopUseControl(x, y, obj);
}
}
// more use (holding)
if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
if (status == CONS_Up)
{
// leftover use release
CancelUse();
return true;
}
else if (status == CONS_Down && repeat && !this.control.noholdingcallbacks)
{
return HoldingUseControl(ctrl, x, y, obj);
}
}
return false;
}
/* ++++++++++++++++++++++++ Movement Controls ++++++++++++++++++++++++ */
// Control use redirected to script
func ControlMovement2Script(int ctrl, int x, int y, int strength, bool repeat, int status, object obj)

View File

@ -0,0 +1,5 @@
[DefCore]
id=Library_ClonkUseControl
Version=8,0
Category=C4D_StaticBack
HideInCreator=true

View File

@ -0,0 +1,431 @@
/**
Clonk use controls
Author: Newton
Objects that inherit this object need to return _inherited(...) in the
following callbacks (if defined):
Construction, Departure,
Entrance, AttachTargetLost, CrewSelection, Death,
Destruction
The following callbacks are made to other objects:
*Use, *UseStop, *UseStart, *UseHolding, *UseCancel
Used properties
this.control.current_object: object that is being used at the moment
this.control.using_type: way of usage
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
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
wheras * is 'Contained' if the clonk is contained and otherwise (riding,
pushing, to self) it is 'Control'. The item in the inventory only gets
the Use*-calls. If the callback is handled, you should return true.
Currently, this is explained more in detail here:
http://forum.openclonk.org/topic_show.pl?tid=337
*/
/* ++++++++++++++++++++++++ Callbacks ++++++++++++++++++++++++ */
protected func Construction()
{
if(this.control == nil)
this.control = {};
this.control.current_object = nil;
this.control.using_type = nil;
this.control.shelved_command = nil;
return _inherited(...);
}
public func GetUsedObject() { return this.control.current_object; }
// 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(...); }
// ...aaand the same for when the clonk is deselected
protected func CrewSelection(bool unselect)
{
if (unselect)
{
// cancel usage on unselect first...
CancelUse();
}
return _inherited(unselect,...);
}
protected func Destruction()
{
// cancel usage...
CancelUse();
return _inherited(...);
}
protected func Death()
{
// cancel usage...
CancelUse();
return _inherited(...);
}
/* ++++++++++++++++++++++++ Shelved command ++++++++++++++++++++++++ */
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;
}
/* ++++++++++++++++++++++++ Use Controls ++++++++++++++++++++++++ */
public func CancelUse()
{
if (!GetUsedObject())
{
// just forget any possibly stored actions
StopShelvedCommand();
return;
}
// use the saved x,y coordinates for canceling
CancelUseControl(this.control.mlastx, this.control.mlasty);
}
// 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);
}
func DetermineUsageType(object obj)
{
if(!obj) return nil;
// house
if (obj == Contained())
return C4D_Structure;
// object
if (obj->Contained() == this)
return C4D_Object;
// vehicle
var proc = GetProcedure();
if (obj == GetActionTarget())
if (proc == "ATTACH" && proc == "PUSH")
return C4D_Vehicle;
// unknown
return nil;
}
func GetUseCallString(string action)
{
// Control... or Contained...
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";
// Action
if (!action)
action = "";
return Format("~%sUse%s%s", control_string, estr, action);
}
func CanReIssueCommand(proplist data)
{
if (!data.obj) return false;
if(data.ctrl == CON_Use)
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);
}
func StartUseControl(int ctrl, int x, int y, object obj)
{
this.control.started_use = false;
if(obj->~RejectUse(this))
{
// remember for later:
PauseUse(obj);
// but still catch command
return true;
}
// Disable climb/hangle actions for the duration of this use
if (obj.ForceFreeHands && !GetEffect("IntControlFreeHands", this)) AddEffect("IntControlFreeHands", this, 130, 0, this);
obj->SetController(GetController());
this.control.current_object = obj;
this.control.using_type = DetermineUsageType(obj);
this.control.alt = ctrl != CON_Use;
if (this->~HasVirtualCursor())
{
var cursor = this->~VirtualCursor(), angle;
if (!cursor->IsActive() && (angle = obj->~DefaultCrosshairAngle(this, GetDir()*2 - 1)))
{
x = +Sin(angle, CURSOR_Radius, 10);
y = -Cos(angle, CURSOR_Radius, 10);
}
cursor->StartAim(this, angle);
}
var hold_enabled = obj->Call("~HoldingEnabled");
if (hold_enabled)
SetPlayerControlEnabled(GetOwner(), CON_Aim, true);
// first call UseStart. If unhandled, call Use (mousecontrol)
var handled = obj->Call(GetUseCallString("Start"),this,x,y);
if (!handled)
{
handled = obj->Call(GetUseCallString(),this,x,y);
this.control.noholdingcallbacks = handled;
}
else
{
// *Start was handled. So clean up possible old noholdingcallbacks-values.
this.control.noholdingcallbacks = false;
}
if (!handled)
{
this.control.current_object = nil;
this.control.using_type = nil;
if (hold_enabled)
SetPlayerControlEnabled(GetOwner(), CON_Aim, false);
return false;
}
else
{
this.control.started_use = true;
// add helper effect that prevents errors when objects are suddenly deleted by quickly cancelling their use beforehand
AddEffect("ItemRemovalCheck", GetUsedObject(), 1, 100, this, nil); // the slow timer is arbitrary and will just clean up the effect if necessary
}
return handled;
}
func CancelUseControl(int x, int y)
{
// forget possibly stored commands
StopShelvedCommand();
// to horse first (if there is one)
var horse = GetActionTarget();
if(horse && GetProcedure() == "ATTACH" && GetUsedObject() != horse)
StopUseControl(x, y, horse, true);
return StopUseControl(x, y, GetUsedObject(), true);
}
func StopUseControl(int x, int y, object obj, bool cancel)
{
var stop = "Stop";
if (cancel) stop = "Cancel";
// ControlUseStop, ControlUseAltStop, ContainedUseAltStop, ContainedUseCancel, etc...
var handled = obj->Call(GetUseCallString(stop),this,x,y);
if (obj == GetUsedObject())
{
// 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)
{
// look for correct removal helper effect and remove it
var effect_index = 0;
var removal_helper = nil;
do
{
removal_helper = GetEffect("ItemRemovalCheck", GetUsedObject(), effect_index++);
if (!removal_helper) break;
if (removal_helper.CommandTarget != this) continue;
break;
} while (true);
RemoveEffect("IntControlFreeHands", this); // make sure we can climb again
this.control.current_object = nil;
this.control.using_type = nil;
this.control.alt = false;
if (removal_helper)
{
RemoveEffect(nil, nil, removal_helper, true);
}
}
this.control.noholdingcallbacks = false;
SetPlayerControlEnabled(GetOwner(), CON_Aim, false);
if (this->~HasVirtualCursor())
this->~VirtualCursor()->StopAim();
}
return handled;
}
func HoldingUseControl(int ctrl, int x, int y, object obj)
{
var mex = x;
var mey = y;
//Message("%d,%d",this,mex,mey);
// automatic adjustment of the direction
// --------------------
// 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
// mounts (naturally). If turning vehicles just like that without issuing
// a turning animation is the question. But after all, the clonk can turn
// by setting the dir too.
// not riding and not in building not while scaling
if (GetProcedure() != "ATTACH" && !Contained() && GetProcedure() != "SCALE")
{
// pushing vehicle: object to turn is the vehicle
var dir_obj = GetActionTarget();
if (GetProcedure() != "PUSH") dir_obj = nil;
// otherwise, turn the clonk
if (!dir_obj) dir_obj = this;
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);
}
}
}
var handled = obj->Call(GetUseCallString("Holding"),this,mex,mey);
return handled;
}
// 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->~GetUsedObject() != 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->~GetUsedObject() != target) return;
// quickly cancel use in a clean way while the object is still available
effect.CommandTarget->CancelUse();
return;
}
// Control use redirected to script
func ControlUse2Script(int ctrl, int x, int y, int strength, bool repeat, int status, object obj)
{
// standard use
if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
if (status == CONS_Down && !repeat)
{
return StartUseControl(ctrl,x, y, obj);
}
else if (status == CONS_Up && (obj == GetUsedObject() || obj == GetActionTarget()))
{
return StopUseControl(x, y, obj);
}
}
// more use (holding)
if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
if (status == CONS_Up)
{
// leftover use release
CancelUse();
return true;
}
else if (status == CONS_Down && repeat && !this.control.noholdingcallbacks)
{
return HoldingUseControl(ctrl, x, y, obj);
}
}
return false;
}