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