1 /*
2 	Crosshair
3 	Author: Newton
4 
5 	Virtual cursor for gamepad controls
6 */
7 
8 local crew, angle, xpos, ypos, aiming, menu;
9 
10 static const CURSOR_Radius = 100;
11 // This is supposed to be a constant, but C4Script doesn't allow constant expressions there.
CURSOR_Deadzone()12 private func CURSOR_Deadzone() { return PLRCON_MaxStrength / 5; }
13 
Initialize()14 protected func Initialize()
15 {
16 	SetVisibility(false);
17 	xpos = ypos = 0;
18 	aiming = false;
19 }
20 
FxMoveTimer()21 public func FxMoveTimer()
22 {
23 	if (!crew)
24 	{
25 		RemoveObject();
26 		return FX_Execute_Kill;
27 	}
28 
29 	var target_angle = Angle(0,0,xpos,ypos)*10;
30 
31 	if (!Visible() && !InDeadzone())
32 	{
33 		// The player moved the aiming stick while the crosshair wasn't visible: Use angle directly.
34 		angle = target_angle;
35 		SetVisibility(true);
36 	}
37 	else if (!InDeadzone())
38 	{
39 		// Smooth small movements of the stick while the crosshair is visible.
40 		var angle_diff = Normalize(target_angle - angle, -1800, 10);
41 		if (Abs(angle_diff) < 450)
42 			angle = angle + angle_diff / 8;
43 		else
44 			angle = target_angle;
45 	}
46 	else if (!aiming)
47 	{
48 		// The player doesn't touch the stick and no item is using the crosshair right now.
49 		SetVisibility(false);
50 		// Aim somewhere useful. Note that this can be overwritten by objects and isn't used for throwing.
51 		angle = 800*(crew->GetDir()*2-1);
52 	}
53 
54 	UpdatePosition();
55 	crew->TriggerHoldingControl();
56 }
57 
AnalogStrength()58 private func AnalogStrength() { return BoundBy(Sqrt(xpos*xpos+ypos*ypos), 0, PLRCON_MaxStrength); }
InDeadzone()59 private func InDeadzone() { return AnalogStrength() < CURSOR_Deadzone(); }
Visible()60 private func Visible() { return this.Visibility != VIS_None; }
61 
62 // Updates the visibility, returing true if it was changed.
SetVisibility(bool visible)63 private func SetVisibility(bool visible)
64 {
65 	var newvis, oldvis;
66 	if (visible)
67 		newvis = VIS_Owner;
68 	else
69 		newvis = VIS_None;
70 	oldvis = this.Visibility;
71 	this.Visibility = newvis;
72 	return newvis != oldvis;
73 }
74 
CreateMoveEffect(object clonk)75 private func CreateMoveEffect(object clonk)
76 {
77 	crew = clonk;
78 	UpdatePosition();
79 	RemoveEffect("Move",this);
80 	AddEffect("Move",this,1,1,this);
81 }
82 
StartAim(object clonk,int default_angle,object GUImenu)83 public func StartAim(object clonk, int default_angle, object GUImenu)
84 {
85 	aiming = true;
86 
87 	// gui or landscape mode:
88 	if (GUImenu)
89 	{
90 		SetCategory(C4D_StaticBack | C4D_IgnoreFoW | C4D_Foreground | C4D_Parallax);
91 		menu = GUImenu;
92 		this["Parallaxity"] = [0,0];
93 	}
94 	else
95 	{
96 		SetCategory(C4D_StaticBack | C4D_IgnoreFoW);
97 		menu = nil;
98 	}
99 
100 	// Use the given angle if the player wasn't aiming before.
101 	if (SetVisibility(true) && default_angle)
102 		angle = default_angle;
103 
104 	CreateMoveEffect(clonk);
105 }
106 
UpdatePosition()107 private func UpdatePosition()
108 {
109 	var x = +Sin(angle,CURSOR_Radius,10);
110 	var y = -Cos(angle,CURSOR_Radius,10);
111 
112 	if (menu)
113 		SetPosition(menu->GetX()+x,menu->GetY()+y);
114 	else
115 		SetPosition(crew->GetX()+x,crew->GetY()+y);
116 
117 	crew->UpdateVirtualCursorPos();
118 }
119 
MirrorCursor()120 private func MirrorCursor()
121 {
122 	return;
123 	angle = -Normalize(angle,-1800,10);
124 }
125 
StopAim()126 public func StopAim()
127 {
128 	aiming = false;
129 }
130 
131 // Aiming means that some object is currently actively using the crosshair.
IsAiming()132 public func IsAiming()
133 {
134 	return aiming;
135 }
136 
137 // The crosshair is also active when the player is holding the aiming stick.
IsActive()138 public func IsActive()
139 {
140 	return aiming || Visible();
141 }
142 
Aim(int ctrl,object clonk,int strength,int repeat,int status)143 public func Aim(int ctrl, object clonk, int strength, int repeat, int status)
144 {
145 	// start (stealth) aiming
146 	if(!GetEffect("Move",this))
147 		CreateMoveEffect(clonk);
148 
149 	// aiming with analog pad
150 	if (status == CONS_Moved &&
151 		(ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight))
152 	{
153 		if(ctrl == CON_AimAxisUp) ypos = -strength;
154 		if(ctrl == CON_AimAxisDown) ypos = strength;
155 		if(ctrl == CON_AimAxisLeft) xpos = -strength;
156 		if(ctrl == CON_AimAxisRight) xpos = strength;
157 		return true;
158 	}
159 	return false;
160 }
161 
Direction(int ctrl)162 public func Direction(int ctrl)
163 {
164 	if(!crew) return;
165 
166 	angle = Normalize(angle,-1800,10);
167 	//Message("%d, %d",this,angle,ctrl);
168 	if(ctrl == CON_Left)
169 		if(angle > 0)
170 			MirrorCursor();
171 
172 	if(ctrl == CON_Right)
173 		if(angle < 0)
174 			MirrorCursor();
175 
176 	return;
177 }
178