1 /*-- Elevator --*/
2 
3 #include Library_Structure
4 #include Library_Ownable
5 
6 // used in the elevator case
7 static const Elevator_needed_power = 20;
8 
9 local case, rope;
10 local partner, slave;
11 
12 /* Editing helpers */
13 
14 // Frees a rectangle for the case
CreateShaft(int length)15 public func CreateShaft(int length)
16 {
17 	// Move the case out of the way
18 	case->SetPosition(case->GetX(), GetY()-10);
19 	ClearFreeRect(GetX() + 7 - 38*GetDir(), GetY() + 20, 24, length + 13);
20 	// Move the case back
21 	case->SetPosition(case->GetX(), GetY()+20);
22 }
23 
24 // Move case to specified absolute y position
SetCasePosition(int y)25 public func SetCasePosition(int y)
26 {
27 	if (case) return case->SetPosition(case->GetX(), y);
28 	return false;
29 }
30 
31 // Overloaded to reposition the case
SetDir(new_dir,...)32 public func SetDir(new_dir, ...)
33 {
34 	var r = inherited(new_dir, ...);
35 	// Update position of child objects on direction change
36 	if (case) case->SetPosition(GetX() -19 * GetCalcDir(), case->GetY());
37 	if (rope) rope->SetPosition(GetX() -19 * GetCalcDir(), rope->GetY());
38 
39 	// Set mesh transformation so that the rope on the mesh fits the rope from the elevator case.
40 	if (new_dir == DIR_Left)
41 	{
42 		this.MeshTransformation = Trans_Rotate(-44,0,1,0);
43 	}
44 	else
45 	{
46 		this.MeshTransformation = Trans_Rotate(-47,0,1,0);
47 	}
48 	return r;
49 }
50 
51 // Forward config to case
SetNoPowerNeed(bool to_val)52 public func SetNoPowerNeed(bool to_val)
53 {
54 	if (case) return case->SetNoPowerNeed(to_val);
55 	return false;
56 }
57 
EditCursorMoved()58 private func EditCursorMoved()
59 {
60 	// Move case and rope along with elevator in editor mode
61 	if (case) case->SetPosition(GetX() + GetCaseXOff(), case->GetY());
62 	if (rope) rope->SetPosition(GetX() + GetCaseXOff(), GetY() - 13);
63 	return true;
64 }
65 
66 // return default horizontal offset of case/rope to elevator
GetCaseXOff()67 public func GetCaseXOff() { return -19 * GetCalcDir(); }
68 
69 /* Initialization */
70 
Construction()71 private func Construction()
72 {
73 	// Set default mesh transformation.
74 	SetDir(DIR_Left);
75 	SetAction("Default");
76 	wheel_anim = PlayAnimation("winchSpin", 1, Anim_Const(0), Anim_Const(1000));
77 
78 	return _inherited(...);
79 }
80 
IsHammerBuildable()81 public func IsHammerBuildable() { return true; }
82 
Initialize()83 private func Initialize()
84 {
85 	SetCategory(C4D_StaticBack);
86 	CreateCase();
87 	CreateRope();
88 
89 	if (partner)
90 	{
91 		if (Inside(partner->GetY(), GetY()-3, GetY()+3))
92 		{
93 			partner->LetsBecomeFriends(this);
94 			SetPosition(GetX(), partner->GetY());
95 		}
96 		else
97 			partner = nil;
98 	}
99 	return _inherited(...);
100 }
101 
CreateCase()102 private func CreateCase()
103 {
104 	case = CreateObjectAbove(ElevatorCase, GetCaseXOff(), 33, GetOwner());
105 	if (case) case->Connect(this);
106 }
107 
GetCase()108 public func GetCase()
109 {
110 	return case;
111 }
112 
CreateRope()113 private func CreateRope()
114 {
115 	rope = CreateObjectAbove(ElevatorRope, GetCaseXOff(), -11, GetOwner());
116 	if (rope) rope->SetAction("Be", case.back);
117 }
118 
119 /* Destruction */
120 
Destruction()121 private func Destruction()
122 {
123 	if(rope) rope->RemoveObject();
124 	if(case) case->LostElevator();
125 	if (partner) partner->LoseCombination();
126 }
127 
LostCase()128 public func LostCase()
129 {
130 	if(partner) partner->LoseCombination();
131 	if(rope) rope->RemoveObject();
132 
133 	StopEngine();
134 
135 	// for now: the elevator dies, too
136 	Incinerate();
137 }
138 
139 /* Effects */
140 
141 local wheel_anim, case_speed;
142 
StartEngine(int direction,bool silent)143 public func StartEngine(int direction, bool silent)
144 {
145 	if (!case) return;
146 
147 	if (!silent)
148 	{
149 		Sound("Structures::Elevator::Start", {custom_falloff_distance = 400});
150 		ScheduleCall(this, "EngineLoop", 34);
151 	}
152 	if (wheel_anim == nil) // If for some reason the animation has stopped
153 		wheel_anim = PlayAnimation("winchSpin", 1, Anim_Const(0), Anim_Const(1000));
154 
155 	var begin, end;
156 	if (direction == COMD_Up) // Either that or COMD_Down
157 	{
158 		begin = GetAnimationLength("winchSpin");
159 		end = 0;
160 	}
161 	else
162 	{
163 		begin = 0;
164 		end = GetAnimationLength("winchSpin");
165 	}
166 
167 	case_speed = Abs(case->GetYDir());
168 	var speed = 80 - case_speed * 2;
169 	SetAnimationPosition(wheel_anim, Anim_Linear(GetAnimationPosition(wheel_anim), begin, end, speed, ANIM_Loop));
170 }
171 
EngineLoop()172 public func EngineLoop()
173 {
174 	Sound("Structures::Elevator::Moving", {loop_count = 1, custom_falloff_distance = 400});
175 }
176 
StopEngine(bool silent)177 public func StopEngine(bool silent)
178 {
179 	if (!silent)
180 	{
181 		Sound("Structures::Elevator::Moving", {loop_count = -1});
182 		ClearScheduleCall(this, "EngineLoop");
183 		Sound("Structures::Elevator::Stop", {custom_falloff_distance = 400});
184 	}
185 
186 	if (wheel_anim == nil) return;
187 
188 	case_speed = nil;
189 	SetAnimationPosition(wheel_anim, Anim_Const(GetAnimationPosition(wheel_anim)));
190 }
191 
192 // Adjusting the turning speed of the wheel to the case's speed
UpdateTurnSpeed()193 private func UpdateTurnSpeed()
194 {
195 	if (!case) return;
196 	if (case_speed == nil || wheel_anim == nil) return;
197 
198 	if (Abs(case->GetYDir()) != case_speed)
199 	{
200 		var begin, end;
201 		if (case->GetYDir() < 0) // Either that or COMD_Down
202 		{
203 			begin = GetAnimationLength("winchSpin");
204 			end = 0;
205 		}
206 		else
207 		{
208 			begin = 0;
209 			end = GetAnimationLength("winchSpin");
210 		}
211 		case_speed = Abs(case->GetYDir());
212 		var speed = 80 - case_speed * 2;
213 		SetAnimationPosition(wheel_anim, Anim_Linear(GetAnimationPosition(wheel_anim), begin, end, speed, ANIM_Loop));
214 	}
215 }
216 
217 /* Construction preview */
218 
219 // Definition call by the construction previewer
ConstructionPreview(object previewer,int overlay,int dir)220 public func ConstructionPreview(object previewer, int overlay, int dir)
221 {
222 	if (GetType(this) != C4V_Def) return;
223 
224 	previewer->SetGraphics(nil, Elevator_Case_Front, overlay, GFXOV_MODE_Base);
225 	previewer->SetObjDrawTransform(1000, 0, -19000 * (dir*2-1), 0, 1000, 17000, overlay);
226 	return true;
227 }
228 
229 // Sticking to other elevators
ConstructionCombineWith()230 public func ConstructionCombineWith() { return "CanCombineElevator"; }
ConstructionCombineDirection(object other)231 public func ConstructionCombineDirection(object other)
232 {
233 	if (!other) return CONSTRUCTION_STICK_Left | CONSTRUCTION_STICK_Right;
234 
235 	// Only combine when facing correctly
236 	if (other->GetDir() == DIR_Left)
237 		return CONSTRUCTION_STICK_Right;
238 	return CONSTRUCTION_STICK_Left;
239 }
240 
241 // Called to determine if sticking is possible
CanCombineElevator(object previewer)242 public func CanCombineElevator(object previewer)
243 {
244 	if (!previewer) return true;
245 
246 	if (GetDir() == DIR_Left)
247 	{
248 		if (previewer.direction == DIR_Right && previewer->GetX() > GetX()) return true;
249 	}
250 	else
251 	{
252 		if (previewer.direction == DIR_Left && previewer->GetX() < GetX()) return true;
253 	}
254 	return false;
255 }
256 
257 // Called when the elevator construction site is created
CombineWith(object other)258 public func CombineWith(object other)
259 {
260 	// Save for use in Initialize
261 	partner = other;
262 }
263 
264 // Special requirements for the basement of the elevator.
GetBasementWidth()265 public func GetBasementWidth() { return 36; }
GetBasementOffset()266 public func GetBasementOffset() { return [11 * (2 * GetDir() - 1), 0]; }
267 
268 
269 /* Combination */
270 
271 // Called by a new elevator next to this one
272 // The other elevator will be the slave
LetsBecomeFriends(object other)273 public func LetsBecomeFriends(object other)
274 {
275 	partner = other;
276 	other.slave = true; // Note: This is liberal slavery
277 	if (case) case->StartConnection(other.case);
278 }
279 
280 // Partner was destroyed or moved
LoseCombination()281 public func LoseCombination()
282 {
283 	partner = nil;
284 	slave = false;
285 	if (case) case->LoseConnection();
286 }
287 
288 // Called by our case because the case has a timer anyway
CheckSlavery()289 public func CheckSlavery()
290 {
291 	// Check if somehow we moved away from our fellow
292 	if (ObjectDistance(partner) > 62 || !Inside(partner->GetY(), GetY()-1, GetY()+1))
293 	{
294 		LoseCombination();
295 		partner->LoseCombination();
296 	}
297 }
298 
299 /* Scenario saving */
300 
SaveScenarioObject(props)301 public func SaveScenarioObject(props)
302 {
303 	if (!inherited(props, ...)) return false;
304 	props->Remove("Category");
305 	if (partner && slave)
306 	{
307 		props->AddCall("Friends", partner, "LetsBecomeFriends", this);
308 	}
309 	if (case && case->GetY() > GetY() + 20)
310 	{
311 		props->AddCall("Shaft", this, "CreateShaft", case->GetY() - GetY() - 20);
312 		props->AddCall("Shaft", this, "SetCasePosition", case->GetY());
313 	}
314 	return true;
315 }
316 
317 local ActMap = {
318 		Default = {
319 			Prototype = Action,
320 			Name = "Default",
321 			Procedure = DFA_NONE,
322 			Directions = 2,
323 			FlipDir = 1,
324 			Length = 1,
325 			Delay = 3,
326 			FacetBase = 1,
327 			NextAction = "Default",
328 			EndCall = "UpdateTurnSpeed",
329 		},
330 };
331 
Definition(def)332 private func Definition(def) {
333 	SetProperty("PictureTransformation", Trans_Mul(Trans_Rotate(-20,1,0), Trans_Rotate(-20, 0, 1, 0)));
334 	return _inherited(def, ...);
335 }
336 local Name = "$Name$";
337 local Description = "$Description$";
338 local BlastIncinerate = 100;
339 local HitPoints = 70;
340 local Plane = 249;
341 local Components = {Wood = 3, Metal = 1};
342