1 /*-- Switch --*/
2 
3 #include Library_Switch
4 
5 local handle, last_controlling_clonk;
6 
Initialize()7 public func Initialize()
8 {
9 	handle = CreateObject(Switch_Handle);
10 	if (handle) handle->SetSwitch(this);
11 	SetAction("Idle");
12 }
13 
SaveScenarioObject(props)14 public func SaveScenarioObject(props)
15 {
16 	if (!inherited(props, ...)) return false;
17 	if (handle)
18 	{
19 		var pos = GetHandlePosition();
20 		if (pos) props->AddCall("Handle", this, "SetSwitchDir", (pos>0)-(pos<0));
21 	}
22 	if (left_action || right_action) props->AddCall("Action", this, "SetActions", left_action, right_action);
23 	return true;
24 }
25 
SetActions(new_left_action,new_right_action)26 public func SetActions(new_left_action, new_right_action)
27 {
28 	left_action = new_left_action;
29 	right_action = new_right_action;
30 	return true;
31 }
32 
ControlUp(object clonk)33 public func ControlUp(object clonk)
34 {
35 	var dir = Rot2Dir(0,-1);
36 	if (!dir) return false;
37 	return ControlSwitchDir(clonk, dir);
38 }
39 
ControlDown(object clonk)40 public func ControlDown(object clonk)
41 {
42 	var dir = Rot2Dir(0,+1);
43 	if (!dir) return false;
44 	return ControlSwitchDir(clonk, dir);
45 }
46 
ControlLeft(object clonk)47 public func ControlLeft(object clonk)
48 {
49 	var dir = Rot2Dir(-1,0);
50 	if (!dir) return false;
51 	return ControlSwitchDir(clonk, dir);
52 }
53 
ControlRight(object clonk)54 public func ControlRight(object clonk)
55 {
56 	var dir = Rot2Dir(+1,0);
57 	if (!dir) return false;
58 	return ControlSwitchDir(clonk, dir);
59 }
60 
ControlSwitchDir(object clonk,int dir)61 private func ControlSwitchDir(object clonk, int dir)
62 {
63 	if (!handle || (!GetSwitchTarget() && !right_action && !left_action))
64 	{
65 		Sound("Structures::SwitchStuck");
66 		Message("$MsgStuck$");
67 		return false;
68 	}
69 	// remember clonk for clalbacks
70 	last_controlling_clonk = clonk;
71 	// move handle
72 	var handle_pos = GetHandlePosition();
73 	if (dir<0)
74 	{
75 		if (GetAction() == "SwitchLeft") return false;
76 		if (handle_pos == -MaxHandleAngle) { Sound("Structures::SwitchStuck"); return false; }
77 		SetAction("SwitchLeft");
78 	}
79 	else
80 	{
81 		if (GetAction() == "SwitchRight") return false;
82 		if (handle_pos == MaxHandleAngle) { Sound("Structures::SwitchStuck"); return false; }
83 		SetAction("SwitchRight");
84 	}
85 	Sound("Structures::SwitchMove");
86 	return true;
87 }
88 
Rot2Dir(int dx,int dy)89 private func Rot2Dir(int dx, int dy)
90 {
91 	// Convert direction dx,dy in world coordinates to horizontal direction in local coordinates
92 	var r = GetR();
93 	return BoundBy(dx*Cos(r, 3) + dy*Sin(r, 3), -1,+1);
94 }
95 
GetHandlePosition()96 private func GetHandlePosition()
97 {
98 	// returns position of handle relative to switch in degrees [-180 to +180]
99 	return (handle->GetR() - GetR() + 540) % 360 - 180;
100 }
101 
SetSwitchDir(int to_dir)102 public func SetSwitchDir(int to_dir)
103 {
104 	// Set angle of switch without doing any effects or callbacks
105 	if (!handle) return false;
106 	return handle->SetR(GetR() + MaxHandleAngle * to_dir);
107 }
108 
SetR(int to_r)109 public func SetR(int to_r)
110 {
111 	// Set rotation: Also update handle rotation
112 	if (handle) handle->SetR(handle->GetR() + to_r - GetR());
113 	return _inherited(to_r, ...);
114 }
115 
SwitchingTimer(int dir)116 private func SwitchingTimer(int dir)
117 {
118 	if (!handle || (!GetSwitchTarget() && !right_action && !left_action))
119 	{
120 		Sound("Structures::SwitchStuck");
121 		return SetAction("Idle");
122 	}
123 	var handle_pos = GetHandlePosition();
124 	var handle_pos_new = BoundBy(handle_pos + HandleSpeed * dir, -MaxHandleAngle, +MaxHandleAngle);
125 	if (!handle_pos_new) handle_pos_new = dir; // avoid direct central position, so player cannot force the same direction twice
126 	handle->SetR(GetR() + handle_pos_new);
127 	// Reached end?
128 	if (handle_pos_new == MaxHandleAngle * dir) SetAction("Idle");
129 	// Passed by middle? (last call because callback might delete switch)
130 	if (handle_pos * handle_pos_new <= 0)
131 	{
132 		Sound("Structures::SwitchFlip*");
133 		DoSwitchFlip(last_controlling_clonk, dir);
134 	}
135 	return true;
136 }
137 
DoSwitchFlip(object clonk,int dir)138 private func DoSwitchFlip(object clonk, int dir)
139 {
140 	// Perform action associated to this switch
141 	if (dir > 0)
142 	{
143 		// Open/close should be aligned to vertical component of direction
144 		SetSwitchState(GetR() < 0, clonk); // switch on if rotation < 0
145 		// Action last; it may delete the door/clonk/etc.
146 		if (right_action)
147 			UserAction->EvaluateAction(right_action, this, clonk);
148 	}
149 	else
150 	{
151 		// Open/close should be aligned to vertical component of direction
152 		SetSwitchState(GetR() >= 0, clonk); // switch off if rotation < 0
153 		// Action last; it may delete the door/clonk/etc.
154 		if (left_action)
155 			UserAction->EvaluateAction(left_action, this, clonk);
156 	}
157 	return false;
158 }
159 
SwitchingLeft()160 private func SwitchingLeft() { return SwitchingTimer(-1); }
SwitchingRight()161 private func SwitchingRight() { return SwitchingTimer(+1); }
162 
163 local ActMap = {
164 	SwitchLeft = {
165 		Prototype = Action,
166 		Name = "SwitchLeft",
167 		Procedure = DFA_NONE,
168 		Length = 1,
169 		Delay = 1,
170 		NextAction = "SwitchLeft",
171 		StartCall = "SwitchingLeft",
172 		FacetBase = 1,
173 	},
174 	SwitchRight = {
175 		Prototype = Action,
176 		Name = "SwitchRight",
177 		Procedure = DFA_NONE,
178 		Length = 1,
179 		Delay = 1,
180 		NextAction = "SwitchRight",
181 		StartCall = "SwitchingRight",
182 		FacetBase = 1,
183 	},
184 };
185 
186 local Name = "$Name$";
187 local Description = "$Description$";
188 local Touchable = 2;
189 local Plane = 270;
190 local MaxHandleAngle = 45;
191 local HandleSpeed = 6;
192 local Components = { Rock = 3, Metal=1 };
193 local left_action, right_action; // Custom editor-selected actions on switch handling
194 
195 local EditorActions = {
196 };
197 
Definition(def)198 func Definition(def)
199 {
200 	// Graphics
201 	SetProperty("PictureTransformation", Trans_Mul(Trans_Scale(800), Trans_Translate(0,0,0),Trans_Rotate(-20,1,0,0),Trans_Rotate(-30,0,1,0)), def);
202 	SetProperty("MeshTransformation", Trans_Rotate(-13,0,1,0), def);
203 	// Editor properties
204 	if (!def.EditorProps) def.EditorProps = {};
205 	def.EditorProps.left_action = new UserAction.Prop { Name="$LeftAction$" };
206 	def.EditorProps.right_action = new UserAction.Prop { Name="$RightAction$" };
207 	// Actions
208 	if (!def.EditorActions) def.EditorActions = {};
209 	def.EditorActions.SwitchLeft = { Name = "$SwitchLeft$", Command = "ControlSwitchDir(nil, -1)" };
210 	def.EditorActions.SwitchRight = { Name = "$SwitchRight$", Command = "ControlSwitchDir(nil, +1)" };
211 	def.EditorActions.Rotate = { Name = "$Rotate$", Command = "SetR((GetR()+135)/90*90)" };
212 	return _inherited(def, ...);
213 }
214