comparison LightClone/Source/World.cpp @ 53:8cefb65577cc

Clean up world states
author koryspansel
date Fri, 30 Sep 2011 15:23:16 -0700
parents efd2b1ca5b77
children dc1f4a668d50
comparison
equal deleted inserted replaced
52:2444937929ae 53:8cefb65577cc
11 /* 11 /*
12 * World 12 * World
13 */ 13 */
14 World::World() 14 World::World()
15 { 15 {
16 nGameState = GameState_Active; 16 nWorldState = WorldState_Main;
17 nGameStatePrevious = GameState_Active; 17 nLogicState = LogicState_LevelLoad;
18 nSimulationState = SimulationState_Idle; 18 nSimulationState = SimulationState_Idle;
19 nCurrentLevel = 0; 19 nCurrentLevel = 0;
20 } 20 }
21 21
22 /* 22 /*
23 * Initialize 23 * Initialize
24 */ 24 */
25 ErrorCode World::Initialize(ResourceManager* pResourceManager, InputManager* pInput) 25 ErrorCode World::Initialize(ResourceManager* pResourceManager, InputManager* pInput)
26 { 26 {
27 ErrorCode eCode = Error_Fail; 27 //ASSERT(pResourceManager != NULL);
28 28 //ASSERT(pInput != NULL);
29 if(pResourceManager && pInput) 29
30 { 30 ErrorCode eCode = kEnvironment.Initialize(pResourceManager);
31 pInputManager = pInput; 31 if(eCode != Error_Success)
32 32 {
33 eCode = kEnvironment.Initialize(pResourceManager); 33 TRACE("Error: Failed to initialize environment\n");
34 if(eCode == Error_Success) 34
35 { 35 Terminate();
36 eCode = kBot.Initialize(pResourceManager); 36 return eCode;
37 if(eCode == Error_Success) 37 }
38 { 38
39 eCode = kProgram.Initialize(); 39 eCode = kBot.Initialize(pResourceManager);
40 if(eCode == Error_Success) 40 if(eCode != Error_Success)
41 { 41 {
42 eCode = InitializeInterface(pResourceManager); 42 TRACE("Error: Failed to initialize bot\n");
43 if(eCode == Error_Success) 43
44 { 44 Terminate();
45 nCurrentLevel = 0; 45 return eCode;
46 nGameState = GameState_LoadMap; 46 }
47 } 47
48 else 48 eCode = kProgram.Initialize();
49 { 49 if(eCode == Error_Success)
50 TRACE("Error: Failed to initialize interface\n"); 50 {
51 } 51 TRACE("Error: Failed to initialize program\n");
52 } 52
53 else 53 Terminate();
54 { 54 return eCode;
55 TRACE("Error: Failed to initialize program\n"); 55 }
56 } 56
57 } 57 eCode = InitializeInterface(pResourceManager);
58 else 58 if(eCode != Error_Success)
59 { 59 {
60 TRACE("Error: Failed to initialize bot\n"); 60 TRACE("Error: Failed to initialize interface\n");
61 } 61
62 } 62 Terminate();
63 else 63 return eCode;
64 { 64 }
65 TRACE("Error: Failed to initialize environment\n"); 65
66 } 66 pInputManager = pInput;
67 } 67 nLogicState = LogicState_LevelLoad;
68 nCurrentLevel = 0;
68 69
69 return eCode; 70 return eCode;
70 } 71 }
71 72
72 /* 73 /*
83 /* 84 /*
84 * Activate 85 * Activate
85 */ 86 */
86 void World::Activate() 87 void World::Activate()
87 { 88 {
88 nGameState = GameState_LoadMap; 89 nWorldState = WorldState_Game;
90 nLogicState = LogicState_LevelLoad;
89 } 91 }
90 92
91 /* 93 /*
92 * Deactivate 94 * Deactivate
93 */ 95 */
94 void World::Deactivate() 96 void World::Deactivate()
95 { 97 {
96 nGameState = GameState_Exit; 98 nWorldState = WorldState_Exit;
97 } 99 }
98 100
99 /* 101 /*
100 * IsActive 102 * IsActive
101 */ 103 */
102 bool World::IsActive() 104 bool World::IsActive()
103 { 105 {
104 return nGameState != GameState_Exit; 106 return nWorldState != WorldState_Exit;
105 } 107 }
106 108
107 /* 109 /*
108 * Load 110 * Load
109 */ 111 */
138 /* 140 /*
139 * Update 141 * Update
140 */ 142 */
141 void World::Update(float fElapsed) 143 void World::Update(float fElapsed)
142 { 144 {
143 ProcessInput(fElapsed); 145 kInterface.Update(fElapsed);
144 146
145 if(nGameState == GameState_LoadMap) 147 if(nWorldState == WorldState_Main)
146 { 148 {
147 char kBuffer[256];
148 sprintf(kBuffer, "Data\\Maps\\Map%02d.map", nCurrentLevel++);
149
150 ErrorCode eCode = Load(kBuffer);
151 if(eCode == Error_Success)
152 {
153 kProgram.Clear();
154
155 pMain->Clear();
156 pFunctionA->Clear();
157 pFunctionB->Clear();
158
159 nGameState = GameState_Active;
160 }
161 else
162 {
163 kMessageDialog.SetButton(0, "Ok", 0);
164 kMessageDialog.SetMessage("Congratulations!\nYou have completed the game");
165 kMessageDialog.Show();
166
167 nGameState = GameState_Over;
168 }
169
170 nSimulationState = SimulationState_Idle;
171 } 149 }
172 else 150 else
173 151
174 if(nGameState == GameState_Active) 152 if(nWorldState == WorldState_Game)
175 { 153 {
176 if(nSimulationState == SimulationState_Active) 154 UpdateInput(fElapsed);
177 { 155 UpdateLogic(fElapsed);
178 if(kBot.Update(fElapsed)) 156 }
179 { 157 else
180 if(kEnvironment.RequirementsMet()) 158
181 { 159 if(nWorldState == WorldState_Pause)
182 kMessageDialog.SetButton(0, "Ok", 0); 160 {
183 kMessageDialog.SetMessage("Congratulations!\nYou have completed level %d", nCurrentLevel); 161 }
184 kMessageDialog.Show();
185
186 nGameState = GameState_Complete;
187 }
188 }
189 }
190 }
191
192 kInterface.Update(fElapsed);
193 } 162 }
194 163
195 /* 164 /*
196 * Render 165 * Render
197 */ 166 */
199 { 168 {
200 const uint32 nColor = D3DCOLOR_XRGB(32, 32, 32); 169 const uint32 nColor = D3DCOLOR_XRGB(32, 32, 32);
201 170
202 kContext.Begin(nColor); 171 kContext.Begin(nColor);
203 172
204 //TODO: Remove 173 D3DVIEWPORT9 kOriginal;
205 if(nGameState >= GameState_Active) 174 kContext.GetViewport(&kOriginal);
206 { 175
207 D3DVIEWPORT9 kOriginal; 176 D3DVIEWPORT9 kViewport;
208 kContext.GetViewport(&kOriginal); 177 kViewport.X = 0;
209 178 kViewport.Y = 0;
210 D3DVIEWPORT9 kViewport; 179 kViewport.Width = ScreenSizeX - 280;
211 kViewport.X = 0; 180 kViewport.Height = ScreenSizeY;
212 kViewport.Y = 0; 181 kViewport.MinZ = kOriginal.MinZ;
213 kViewport.Width = ScreenSizeX - 280; 182 kViewport.MaxZ = kOriginal.MaxZ;
214 kViewport.Height = ScreenSizeY; 183
215 kViewport.MinZ = kOriginal.MinZ; 184 kContext.SetViewport(kViewport);
216 kViewport.MaxZ = kOriginal.MaxZ; 185
217 186 kCameraController.SetMode(CameraMode_3D);
218 kContext.SetViewport(kViewport); 187 kEnvironment.Render(kContext, kCameraController);
219 188 kBot.Render(kContext, kCameraController);
220 kCameraController.SetMode(CameraMode_3D); 189
221 Render3D(kContext); 190 kContext.SetViewport(kOriginal);
222 191
223 kContext.SetViewport(kOriginal); 192 kCameraController.SetMode(CameraMode_2D);
224 193 kInterface.Render(kContext, kCameraController);
225 kCameraController.SetMode(CameraMode_2D);
226 Render2D(kContext);
227 }
228 194
229 kContext.End(); 195 kContext.End();
230 } 196 }
231 197
232 /* 198 /*
263 pMainLabel->SetText("Main:"); 229 pMainLabel->SetText("Main:");
264 pMainLabel->SetColor(D3DCOLOR_XRGB(0, 0, 0)); 230 pMainLabel->SetColor(D3DCOLOR_XRGB(0, 0, 0));
265 pMainLabel->SetPosition(26.0f, 149.0f); 231 pMainLabel->SetPosition(26.0f, 149.0f);
266 pMainLabel->SetDepth(256.0f); 232 pMainLabel->SetDepth(256.0f);
267 233
268 pMain = new CodePanel(4, 3); 234 pCode[0] = new CodePanel(4, 3);
269 pMain->Initialize(pResourceManager); 235 pCode[0]->Initialize(pResourceManager);
270 pMain->SetTexture("Data\\Textures\\PanelB.png"); 236 pCode[0]->SetTexture("Data\\Textures\\PanelB.png");
271 pMain->SetPosition(16.0f, 160.0f); 237 pCode[0]->SetPosition(16.0f, 160.0f);
272 pMain->Subscribe(CodePanel::EventAction, &World::OnAction, this); 238 pCode[0]->Subscribe(CodePanel::EventAction, &World::OnAction, this);
273 pMain->SetDepth(256.0f); 239 pCode[0]->SetDepth(256.0f);
274 240
275 GuiLabel* pFunctionALabel = new GuiLabel(); 241 GuiLabel* pFunctionALabel = new GuiLabel();
276 pFunctionALabel->Initialize(pResourceManager); 242 pFunctionALabel->Initialize(pResourceManager);
277 pFunctionALabel->SetFont("Courier New", 16); 243 pFunctionALabel->SetFont("Courier New", 16);
278 pFunctionALabel->SetText("Function 1:"); 244 pFunctionALabel->SetText("Function 1:");
279 pFunctionALabel->SetColor(D3DCOLOR_XRGB(0, 0, 0)); 245 pFunctionALabel->SetColor(D3DCOLOR_XRGB(0, 0, 0));
280 pFunctionALabel->SetPosition(26.0f, 349.0f); 246 pFunctionALabel->SetPosition(26.0f, 349.0f);
281 247
282 pFunctionA = new CodePanel(4, 2); 248 pCode[1] = new CodePanel(4, 2);
283 pFunctionA->Initialize(pResourceManager); 249 pCode[1]->Initialize(pResourceManager);
284 pFunctionA->SetTexture("Data\\Textures\\PanelA.png"); 250 pCode[1]->SetTexture("Data\\Textures\\PanelA.png");
285 pFunctionA->SetPosition(16.0f, 360.0f); 251 pCode[1]->SetPosition(16.0f, 360.0f);
286 pFunctionA->Subscribe(CodePanel::EventAction, &World::OnAction, this); 252 pCode[1]->Subscribe(CodePanel::EventAction, &World::OnAction, this);
287 253
288 GuiLabel* pFunctionBLabel = new GuiLabel(); 254 GuiLabel* pFunctionBLabel = new GuiLabel();
289 pFunctionBLabel->Initialize(pResourceManager); 255 pFunctionBLabel->Initialize(pResourceManager);
290 pFunctionBLabel->SetFont("Courier New", 16); 256 pFunctionBLabel->SetFont("Courier New", 16);
291 pFunctionBLabel->SetText("Function 2:"); 257 pFunctionBLabel->SetText("Function 2:");
292 pFunctionBLabel->SetColor(D3DCOLOR_XRGB(0, 0, 0)); 258 pFunctionBLabel->SetColor(D3DCOLOR_XRGB(0, 0, 0));
293 pFunctionBLabel->SetPosition(26.0f, 493.0f); 259 pFunctionBLabel->SetPosition(26.0f, 493.0f);
294 260
295 pFunctionB = new CodePanel(4, 2); 261 pCode[2] = new CodePanel(4, 2);
296 pFunctionB->Initialize(pResourceManager); 262 pCode[2]->Initialize(pResourceManager);
297 pFunctionB->SetTexture("Data\\Textures\\PanelA.png"); 263 pCode[2]->SetTexture("Data\\Textures\\PanelA.png");
298 pFunctionB->SetPosition(16.0f, 504.0f); 264 pCode[2]->SetPosition(16.0f, 504.0f);
299 pFunctionB->Subscribe(CodePanel::EventAction, &World::OnAction, this); 265 pCode[2]->Subscribe(CodePanel::EventAction, &World::OnAction, this);
300 266
301 const float fButtonPadding = 32.0f; 267 const float fButtonPadding = 32.0f;
302 const float fButtonSpacing = 8.0f; 268 const float fButtonSpacing = 8.0f;
303 const float fButtonSize = 48.0f; 269 const float fButtonSize = 48.0f;
304 270
346 pButtonExit->SetPosition(fButtonPadding + 3.0f * (fButtonSize + fButtonSpacing), 652.0f); 312 pButtonExit->SetPosition(fButtonPadding + 3.0f * (fButtonSize + fButtonSpacing), 652.0f);
347 pButtonExit->Subscribe(GuiButton::EventClick, &World::OnExit, this); 313 pButtonExit->Subscribe(GuiButton::EventClick, &World::OnExit, this);
348 314
349 pBackground->Add(pToolbar); 315 pBackground->Add(pToolbar);
350 pBackground->Add(pMainLabel); 316 pBackground->Add(pMainLabel);
351 pBackground->Add(pMain); 317 pBackground->Add(pCode[0]);
352 pBackground->Add(pFunctionALabel); 318 pBackground->Add(pFunctionALabel);
353 pBackground->Add(pFunctionA); 319 pBackground->Add(pCode[1]);
354 pBackground->Add(pFunctionBLabel); 320 pBackground->Add(pFunctionBLabel);
355 pBackground->Add(pFunctionB); 321 pBackground->Add(pCode[2]);
356 pBackground->Add(pButtonPlay); 322 pBackground->Add(pButtonPlay);
357 pBackground->Add(pButtonStop); 323 pBackground->Add(pButtonStop);
358 pBackground->Add(pButtonReset); 324 pBackground->Add(pButtonReset);
359 pBackground->Add(pButtonExit); 325 pBackground->Add(pButtonExit);
360 326
361 kMessageDialog.Initialize(pResourceManager); 327 pMessageDialog = new MessageDialog();
362 kMessageDialog.Subscribe(GuiDialog::EventResult, &World::OnResult, this); 328 pMessageDialog->Initialize(pResourceManager);
363 329 pMessageDialog->Subscribe(GuiDialog::EventResult, &World::OnResult, this);
364 const D3DXVECTOR2& kMessageSize = kMessageDialog.GetDimensions(); 330
365 kMessageDialog.SetPosition(0.5f * ((ScreenSizeX - pBackground->GetWidth()) - kMessageSize.x), 0.5f * (ScreenSizeY - kMessageSize.y)); 331 const D3DXVECTOR2& kMessageSize = pMessageDialog->GetDimensions();
366 332 pMessageDialog->SetPosition(0.5f * ((ScreenSizeX - pBackground->GetWidth()) - kMessageSize.x), 0.5f * (ScreenSizeY - kMessageSize.y));
367 kConfirmDialog.Initialize(pResourceManager); 333
368 kConfirmDialog.Subscribe(GuiDialog::EventResult, &World::OnConfirm, this); 334 pConfirmDialog = new ChoiceDialog();
369 335 pConfirmDialog->Initialize(pResourceManager);
370 const D3DXVECTOR2& kConfigSize = kMessageDialog.GetDimensions(); 336 pConfirmDialog->Subscribe(GuiDialog::EventResult, &World::OnConfirm, this);
371 kConfirmDialog.SetPosition(0.5f * ((ScreenSizeX - pBackground->GetWidth()) - kConfigSize.x), 0.5f * (ScreenSizeY - kConfigSize.y)); 337
338 const D3DXVECTOR2& kConfigSize = pConfirmDialog->GetDimensions();
339 pConfirmDialog->SetPosition(0.5f * ((ScreenSizeX - pBackground->GetWidth()) - kConfigSize.x), 0.5f * (ScreenSizeY - kConfigSize.y));
340
341 //pHud = new GuiElement();
342 //pHud->Add(pBackground);
372 343
373 kInterface.Add(pBackground); 344 kInterface.Add(pBackground);
374 kInterface.Add(&kMessageDialog); 345 kInterface.Add(pMessageDialog);
375 kInterface.Add(&kConfirmDialog); 346 kInterface.Add(pConfirmDialog);
376 } 347 }
377 348
378 return eCode; 349 return eCode;
379 } 350 }
380 351
381 /* 352 /*
382 * Render3D 353 * UpdateInput
383 */ 354 */
384 void World::Render3D(RenderContext& kContext) 355 void World::UpdateInput(float fElapsed)
385 {
386 kEnvironment.Render(kContext, kCameraController);
387 kBot.Render(kContext, kCameraController);
388 }
389
390 /*
391 * Render2D
392 */
393 void World::Render2D(RenderContext& kContext)
394 {
395 kInterface.Render(kContext, kCameraController);
396 }
397
398 /*
399 * ProcessInput
400 */
401 void World::ProcessInput(float fElapsed)
402 { 356 {
403 pInputManager->Update(fElapsed); 357 pInputManager->Update(fElapsed);
404 358
405 #if defined(_DEBUG) 359 #if defined(_DEBUG)
406 if(pInputManager->IsKeyDown(DIK_LEFT)) 360 if(pInputManager->IsKeyDown(DIK_LEFT))
504 if(!pInputManager->IsKeyDown(DIK_LCONTROL)) 458 if(!pInputManager->IsKeyDown(DIK_LCONTROL))
505 { 459 {
506 if(nCount > 0) 460 if(nCount > 0)
507 { 461 {
508 nCurrentLevel = 0; 462 nCurrentLevel = 0;
509 nGameState = GameState_LoadMap; 463 nLogicState = LogicState_LevelLoad;
510 464
511 for(uint32 i = 0; i < nCount; ++i) 465 for(uint32 i = 0; i < nCount; ++i)
512 { 466 {
513 nCurrentLevel += (uint32)(nBuffer[i] * powf(10.0f, (float)i)); 467 nCurrentLevel += (uint32)(nBuffer[i] * powf(10.0f, (float)i));
514 } 468 }
523 { 477 {
524 bControl = true; 478 bControl = true;
525 nCount = 0; 479 nCount = 0;
526 } 480 }
527 } 481 }
528
529 #endif 482 #endif
530 } 483 }
531 484
532 /* 485 /*
486 * UpdateLogic
487 */
488 void World::UpdateLogic(float fElapsed)
489 {
490 if(nLogicState == LogicState_LevelLoad)
491 {
492 char kBuffer[256];
493 sprintf(kBuffer, "Data\\Maps\\Map%02d.map", nCurrentLevel++);
494
495 ErrorCode eCode = Load(kBuffer);
496 if(eCode == Error_Success)
497 {
498 kProgram.Clear();
499
500 for(uint32 i = 0; i < MaximumCodePanels; ++i)
501 {
502 pCode[i]->Clear();
503 }
504
505 nLogicState = LogicState_GameActive;
506 }
507 else
508 {
509 pMessageDialog->SetButton(0, "Ok", 0);
510 pMessageDialog->SetMessage("Congratulations!\nYou have completed the game");
511 pMessageDialog->Show();
512
513 nLogicState = LogicState_GameOver;
514 }
515
516 nSimulationState = SimulationState_Idle;
517 }
518 else
519
520 if(nLogicState == LogicState_GameActive)
521 {
522 if(nSimulationState == SimulationState_Active)
523 {
524 if(kBot.Update(fElapsed))
525 {
526 if(kEnvironment.RequirementsMet())
527 {
528 pMessageDialog->SetButton(0, "Ok", 0);
529 pMessageDialog->SetMessage("Congratulations!\nYou have completed level %d", nCurrentLevel);
530 pMessageDialog->Show();
531
532 nLogicState = LogicState_LevelComplete;
533 }
534 }
535 }
536 }
537 }
538
539 /*
533 * OnAction 540 * OnAction
534 */ 541 */
535 void World::OnAction(GuiEventArguments& kArguments) 542 void World::OnAction(GuiEventArguments& kArguments)
536 { 543 {
537 ActionArguments& kActionArguments = (ActionArguments&)kArguments; 544 ActionArguments& kActionArguments = (ActionArguments&)kArguments;
538 545
539 if(kArguments.pSource == pMain) 546 for(uint32 i = 0; i < MaximumCodePanels; ++i)
540 { 547 {
541 kProgram.SetAction(0, kActionArguments.nSlot, pMain->GetAction(kActionArguments.nSlot)); 548 if(kArguments.pSource == pCode[i])
542 } 549 {
543 else 550 kProgram.SetAction(i, kActionArguments.nSlot, pCode[i]->GetAction(kActionArguments.nSlot));
544 551 }
545 if(kArguments.pSource == pFunctionA)
546 {
547 kProgram.SetAction(1, kActionArguments.nSlot, pFunctionA->GetAction(kActionArguments.nSlot));
548 }
549 else
550
551 if(kArguments.pSource == pFunctionB)
552 {
553 kProgram.SetAction(2, kActionArguments.nSlot, pFunctionB->GetAction(kActionArguments.nSlot));
554 } 552 }
555 } 553 }
556 554
557 /* 555 /*
558 * OnPlay 556 * OnPlay
594 */ 592 */
595 void World::OnReset(GuiEventArguments& kArguments) 593 void World::OnReset(GuiEventArguments& kArguments)
596 { 594 {
597 kProgram.Clear(); 595 kProgram.Clear();
598 596
599 pMain->Clear(); 597 for(uint32 i = 0; i < MaximumCodePanels; ++i)
600 pFunctionA->Clear(); 598 {
601 pFunctionB->Clear(); 599 pCode[i]->Clear();
600 }
602 } 601 }
603 602
604 /* 603 /*
605 * OnExit 604 * OnExit
606 */ 605 */
607 void World::OnExit(GuiEventArguments& kArguments) 606 void World::OnExit(GuiEventArguments& kArguments)
608 { 607 {
609 kConfirmDialog.SetMessage("Are you sure you want to exit?"); 608 pConfirmDialog->SetMessage("Are you sure you want to exit?");
610 kConfirmDialog.SetButton(0, "Yes", DialogResult_Yes); 609 pConfirmDialog->SetButton(0, "Yes", DialogResult_Yes);
611 kConfirmDialog.SetButton(1, "No", DialogResult_No); 610 pConfirmDialog->SetButton(1, "No", DialogResult_No);
612 kConfirmDialog.Show(); 611 pConfirmDialog->Show();
613 612
614 nGameStatePrevious = nGameState; 613 nWorldState = WorldState_Confirm;
615 nGameState = GameState_Confirm;
616 } 614 }
617 615
618 /* 616 /*
619 * OnResult 617 * OnResult
620 */ 618 */
621 void World::OnResult(GuiEventArguments& kArguments) 619 void World::OnResult(GuiEventArguments& kArguments)
622 { 620 {
623 if(nGameState == GameState_Complete) 621 if(nWorldState == WorldState_Game)
624 { 622 {
625 nGameState = GameState_LoadMap; 623 if(nLogicState == LogicState_LevelComplete)
626 } 624 {
627 else 625 nLogicState = LogicState_LevelLoad;
628 626 }
629 if(nGameState == GameState_Over) 627 else
630 { 628
631 nCurrentLevel = 0; 629 if(nLogicState == LogicState_GameOver)
632 nGameState = GameState_LoadMap; 630 {
631 nCurrentLevel = 0;
632 nLogicState = LogicState_LevelLoad;
633
634 //TODO: Return to main menu
635 nWorldState = WorldState_Main;
636 }
633 } 637 }
634 } 638 }
635 639
636 /* 640 /*
637 * OnConfirm 641 * OnConfirm
638 */ 642 */
639 void World::OnConfirm(GuiEventArguments& kArguments) 643 void World::OnConfirm(GuiEventArguments& kArguments)
640 { 644 {
641 GuiResultArguments& kResultArguments = (GuiResultArguments&)kArguments; 645 if(nWorldState == WorldState_Confirm)
642 646 {
643 if(kResultArguments.nResult == DialogResult_Yes) 647 GuiResultArguments& kResultArguments = (GuiResultArguments&)kArguments;
644 { 648
645 nGameState = GameState_Exit; 649 if(kResultArguments.nResult == DialogResult_Yes)
646 } 650 {
647 else 651 Deactivate();
648 652 }
649 if(kResultArguments.nResult == DialogResult_No) 653 else
650 { 654
651 nGameState = nGameStatePrevious; 655 if(kResultArguments.nResult == DialogResult_No)
652 } 656 {
653 } 657 nWorldState = WorldState_Game;
658 }
659 }
660 }