View Javadoc

1   package erland.game.tileadventure;
2   /*
3    * Copyright (C) 2004 Erland Isaksson (erland_i@hotmail.com)
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18   *
19   */
20  
21  import erland.game.GameEnvironmentInterface;
22  import erland.game.tileadventure.isodiamond.IsoDiamondDrawMap;
23  import erland.game.tileadventure.rect.RectDrawMap;
24  
25  import java.util.*;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  /***
31   * Implementation of the main game model that handles all the logic in the game
32   */
33  public class TileAdventureModel implements GameObjectMapActionInterface, ActionHandlerInterface {
34      /*** Logging instance */
35      private static Log LOG = LogFactory.getLog(TileAdventureModel.class);
36      /*** Block container for the game area */
37      protected IrregularBlockContainerInterface cont;
38      /*** Game environment object */
39      protected GameEnvironmentInterface environment;
40      /*** Room map */
41      protected MapObjectContainerInterface roomMap;
42      /*** Object list */
43      protected Vector objectList;
44      /*** A list of all players in the game */
45      private GameObject[] players;
46      /*** A map with mapping between the object maps and rooms */
47      private Map roomByBlockMap;
48      /*** A map with mapping between the object maps and moving maps */
49      private Map movingMapByBlockMap;
50      /*** Mapping between a player and current room */
51      private Map playerRoomMap;
52      /*** A list of all ongoing actions */
53      private Map actionMap = new HashMap();
54  
55      /***
56       * Initiate the model
57       * @param environment Game environment
58       * @param cont Block container for the track
59       */
60      public void init(GameEnvironmentInterface environment, IrregularBlockContainerInterface cont) {
61          this.environment = environment;
62          this.cont = cont;
63          movingMapByBlockMap = new HashMap();
64          playerRoomMap = new HashMap();
65          roomByBlockMap = new HashMap();
66          LevelManager levelManager = new WorldGameLevelManager();
67          levelManager.init(environment);
68          levelManager.setContainer(cont);
69          LevelInfoInterface levelInfo = levelManager.getLevel(1);
70          roomMap = levelInfo.getObjects();
71          initRooms(roomMap);
72      }
73      private void initRooms(MapObjectContainerInterface rooms) {
74          this.objectList = new Vector();
75          for (int x = 0; x < rooms.getSizeX(); x++) {
76              for (int y = 0; y < rooms.getSizeY(); y++) {
77                  for (int z = 0; z < rooms.getSizeZ(); z++) {
78                      RoomObject room = (RoomObject) rooms.getBlock(x,y,z);
79                      if(room!=null) {
80                          initRoom(room,objectList);
81                      }
82                  }
83              }
84          }
85      }
86      private void initPlayer(RoomObject room, GameObject player) {
87          DrawMap map = ((TileGameEnvironmentInterface)(environment.getCustomEnvironment())).createBlockMap();
88          map.setContainer(cont);
89          map.addObjectMap(room.getBlocks());
90          //map.addObjectMap((MapObjectContainerInterface) movingMapByBlockMap.get(room.getBlocks()));
91          playerRoomMap.put(player,map);
92      }
93      private void initRoom(RoomObject room,List objectList) {
94          MapObjectContainerInterface movingMap = new MapBlockContainer(room.getBlocks().getSizeX(),room.getBlocks().getSizeY(),room.getBlocks().getSizeZ());
95          roomByBlockMap.put(room.getBlocks(),room);
96          movingMapByBlockMap.put(room.getBlocks(),movingMap);
97          MapObjectContainerInterface map = room.getBlocks();
98          for (int x = 0; x < map.getSizeX(); x++) {
99              for (int y = 0; y < map.getSizeY(); y++) {
100                 for (int z = 0; z < map.getSizeZ(); z++) {
101                     GameObject o = (GameObject) map.getBlock(x,y,z);
102                     if(o instanceof GameObjectUpdateInterface) {
103                         o.setActionMap(this);
104                         objectList.add(o);
105                     }
106                 }
107             }
108         }
109     }
110 
111     private RoomObject findStartRoom() {
112         for (int x = 0; x < roomMap.getSizeX(); x++) {
113             for (int y = 0; y < roomMap.getSizeY(); y++) {
114                 for (int z = 0; z < roomMap.getSizeZ(); z++) {
115                     RoomObject room = (RoomObject) roomMap.getBlock(x,y,z);
116                     if(room!=null) {
117                         return room;
118                     }
119                 }
120             }
121         }
122         return null;
123     }
124     private void removePlayers(GameObject[] players) {
125         if(players!=null) {
126             for(int i=0;i<players.length;i++) {
127                 objectList.removeElement(players[i]);
128                 players[i].getObjectMap().removeBlock(players[i],(int)players[i].getPosX(),(int)players[i].getPosY(),(int)players[i].getPosZ());
129                 MapObjectContainerInterface movingMap = (MapObjectContainerInterface) movingMapByBlockMap.get(players[i].getObjectMap());
130                 movingMap.removeBlock(players[i]);
131             }
132         }
133     }
134     public void initPlayers(GameObject[] players) {
135         removePlayers(this.players);
136         this.players = players;
137         RoomObject room = findStartRoom();
138         MapObjectContainerInterface blockMap = room.getBlocks();
139         for(int i=0;i<players.length;i++) {
140             players[i].setObjectMap(blockMap);
141             players[i].setActionMap(this);
142             players[i].setPos(1,1,1);
143             objectList.addElement(players[i]);
144             blockMap.addObject(players[i],(int)players[i].getPosX(),(int)players[i].getPosY(),(int)players[i].getPosZ());
145             initPlayer(room,players[i]);
146         }
147     }
148     /***
149      * Update model, this will move all cars, check for collisions and apply frictions
150      */
151     public void update() {
152         if(objectList!=null) {
153             for(int i=0;i<objectList.size();i++) {
154                 GameObjectUpdateInterface obj = (GameObjectUpdateInterface) objectList.elementAt(i);
155                 obj.update();
156             }
157         }
158         for (Iterator it = actionMap.values().iterator(); it.hasNext();) {
159             List list = (List) it.next();
160             for(Iterator it2 = list.iterator();it2.hasNext();) {
161                 ActionInterface action = (ActionInterface) it2.next();
162                 action.start();
163                 if(action.perform()) {
164                     action.stop();
165                     it2.remove();
166                 }
167             }
168         }
169     }
170 
171     public MapDrawInterface getMap(GameObject player) {
172         return (MapDrawInterface) playerRoomMap.get(player);
173     }
174     private boolean isInsideMap(MapObjectContainerInterface map, float x, float y, float z) {
175         if(x<map.getSizeX()&& y<map.getSizeY() && z<map.getSizeZ() && x>=0 && y>=0 && z>=0) {
176             return true;
177         }else {
178             return false;
179         }
180     }
181 
182     public boolean isFree(MapObjectContainerInterface map,GameObject obj,float x, float y, float z) {
183         if(isInsideMap(map,x,y,z)) {
184             MapObjectInterface block = map.getBlock((int)x,(int)y,(int)z);
185             if((block==null||block==obj)) {
186                 return true;
187             }
188         }
189         return false;
190     }
191 
192     public boolean isFree(GameObject obj,float x, float y, float z) {
193         if(isInsideMap(obj.getObjectMap(),x,y,z)) {
194             MapObjectInterface block = obj.getObjectMap().getBlock((int)x,(int)y,(int)z);
195             //TODO: clean up
196             //MapObjectContainerInterface movingMap = (MapObjectContainerInterface) movingMapByBlockMap.get(obj.getObjectMap());
197             //MapObjectInterface movingObject = movingMap.getBlock((int)x,(int)y,(int)z);
198             //if((block==null||block==obj)&&(movingObject==null||movingObject==obj)) {
199             if(block==null || block==obj || !block.getBoundingBox().contains(x,y,z)) {
200                 return true;
201             }
202         }
203         return false;
204     }
205 
206     public boolean isFree(GameObject obj,Box3D boundingBox) {
207         return isFree(obj,boundingBox.getMinX(),boundingBox.getMinY(),boundingBox.getMinZ()) &&
208                isFree(obj,boundingBox.getMinX(),boundingBox.getMinY(),boundingBox.getMaxZ()) &&
209                isFree(obj,boundingBox.getMinX(),boundingBox.getMaxY(),boundingBox.getMinZ()) &&
210                isFree(obj,boundingBox.getMinX(),boundingBox.getMaxY(),boundingBox.getMaxZ()) &&
211                isFree(obj,boundingBox.getMaxX(),boundingBox.getMinY(),boundingBox.getMinZ()) &&
212                isFree(obj,boundingBox.getMaxX(),boundingBox.getMinY(),boundingBox.getMaxZ()) &&
213                isFree(obj,boundingBox.getMaxX(),boundingBox.getMaxY(),boundingBox.getMinZ()) &&
214                isFree(obj,boundingBox.getMaxX(),boundingBox.getMaxY(),boundingBox.getMaxZ());
215     }
216 
217     public boolean move(GameObject obj, float x, float y, float z) {
218         Box3D b = new Box3D();
219         obj.fillBoundingBox(x,y,z,b);
220         if(isFree(obj,b)) {
221             Box3D c = obj.getBoundingBox();
222             obj.getObjectMap().removeObject(obj,(int)c.getMinX(),(int)c.getMinY(),(int)c.getMinZ());
223             obj.getObjectMap().removeObject(obj,(int)c.getMinX(),(int)c.getMinY(),(int)c.getMaxZ());
224             obj.getObjectMap().removeObject(obj,(int)c.getMinX(),(int)c.getMaxY(),(int)c.getMinZ());
225             obj.getObjectMap().removeObject(obj,(int)c.getMinX(),(int)c.getMaxY(),(int)c.getMaxZ());
226             obj.getObjectMap().removeObject(obj,(int)c.getMaxX(),(int)c.getMinY(),(int)c.getMinZ());
227             obj.getObjectMap().removeObject(obj,(int)c.getMaxX(),(int)c.getMinY(),(int)c.getMaxZ());
228             obj.getObjectMap().removeObject(obj,(int)c.getMaxX(),(int)c.getMaxY(),(int)c.getMinZ());
229             obj.getObjectMap().removeObject(obj,(int)c.getMaxX(),(int)c.getMaxY(),(int)c.getMaxZ());
230             obj.setPos(x,y,z);
231             c = obj.getBoundingBox();
232             obj.getObjectMap().addObject(obj,(int)c.getMinX(),(int)c.getMinY(),(int)c.getMinZ());
233             obj.getObjectMap().addObject(obj,(int)c.getMinX(),(int)c.getMinY(),(int)c.getMaxZ());
234             obj.getObjectMap().addObject(obj,(int)c.getMinX(),(int)c.getMaxY(),(int)c.getMinZ());
235             obj.getObjectMap().addObject(obj,(int)c.getMinX(),(int)c.getMaxY(),(int)c.getMaxZ());
236             obj.getObjectMap().addObject(obj,(int)c.getMaxX(),(int)c.getMinY(),(int)c.getMinZ());
237             obj.getObjectMap().addObject(obj,(int)c.getMaxX(),(int)c.getMinY(),(int)c.getMaxZ());
238             obj.getObjectMap().addObject(obj,(int)c.getMaxX(),(int)c.getMaxY(),(int)c.getMinZ());
239             obj.getObjectMap().addObject(obj,(int)c.getMaxX(),(int)c.getMaxY(),(int)c.getMaxZ());
240             return true;
241         }
242         return false;
243     }
244 
245     private float getMovingPosX(float x, float y, float z, Direction direction) {
246         if(direction == Direction.WEST) {
247             x--;
248         }else if(direction == Direction.EAST) {
249             x++;
250         }
251         return x;
252     }
253     private float getMovingPosY(float x, float y, float z, Direction direction) {
254         if(direction == Direction.NORTH) {
255             y--;
256         }else if(direction == Direction.SOUTH) {
257             y++;
258         }
259         return y;
260     }
261     private float getMovingPosZ(float x, float y, float z, Direction direction) {
262         if(direction == Direction.DOWN) {
263             z--;
264         }else if(direction == Direction.UP) {
265             z++;
266         }
267         return z;
268     }
269 
270     private Action getPushActionForDirection(Direction direction) {
271         if(direction==Direction.WEST) {
272             return Action.PUSH_WEST;
273         }else if(direction==Direction.EAST) {
274             return Action.PUSH_EAST;
275         }else if(direction==Direction.NORTH) {
276             return Action.PUSH_NORTH;
277         }else if(direction==Direction.SOUTH) {
278             return Action.PUSH_SOUTH;
279         }else {
280             return Action.NONE;
281         }
282     }
283 
284     public ActionInterface isActionPossibleOnObject(GameObject obj, Action action) {
285         MapObjectContainerInterface movingMap = (MapObjectContainerInterface) movingMapByBlockMap.get(obj.getObjectMap());
286         MapObjectContainerInterface blockMap = (MapObjectContainerInterface) obj.getObjectMap();
287         if(action.isMove()||action.isPush()) {
288             Direction direction = action.getDirection();
289             int newX=(int)getMovingPosX(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
290             int newY=(int)getMovingPosY(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
291             int newZ=(int)getMovingPosZ(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
292             if(isInsideMap(blockMap,newX,newY,newZ)) {
293                 if(isFree(obj,newX,newY,newZ)) {
294                     return action;
295                 }else {
296                     GameObject nextObj = (GameObject)blockMap.getBlock(newX,newY,newZ);
297                     if(nextObj!=null && nextObj.isMovable(action.getDirection())) {
298                         //TODO: Push when move was requested, do we need to do anything special ?
299                         return getPushActionForDirection(action.getDirection());
300                     }
301                 }
302             }else if(action.isMove()) {
303                 LOG.debug("Try changing room "+newX+","+newY+","+newZ);
304                 MapObjectContainerInterface currentRoomMap = obj.getObjectMap();
305                 RoomObject currentRoom = (RoomObject) roomByBlockMap.get(currentRoomMap);
306                 if(isFreeInNextRoom(currentRoom,newX,newY,newZ) && isAllowedToChangeRoom(obj)) {
307                     return action;
308                 }
309             }
310         }else if(action==Action.JUMP) {
311             if(isInsideMap(blockMap,obj.getPosX(),obj.getPosY(),obj.getPosZ()+1)) {
312                 if(isFree(obj,obj.getPosX(),obj.getPosY(),obj.getPosZ()+1)) {
313                     return action;
314                 }
315             }
316         }else if(action==Action.DROP) {
317             if(isInsideMap(blockMap,obj.getPosX(),obj.getPosY(),obj.getPosZ()-1)) {
318                 if(isFree(obj,obj.getPosX(),obj.getPosY(),obj.getPosZ()-1)) {
319                     return action;
320                 }
321             }
322         }
323         return Action.NONE;
324     }
325     public ActionInterface startActionOnObject(GameObject obj, Action action) {
326         MapObjectContainerInterface movingMap = (MapObjectContainerInterface) movingMapByBlockMap.get(obj.getObjectMap());
327         MapObjectContainerInterface blockMap = (MapObjectContainerInterface) obj.getObjectMap();
328         if(action.isMove()||action.isPush()) {
329             Direction direction = action.getDirection();
330             int newX=(int)getMovingPosX(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
331             int newY=(int)getMovingPosY(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
332             int newZ=(int)getMovingPosZ(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
333             if(isInsideMap(blockMap,newX,newY,newZ)) {
334                 if(isFree(obj,newX,newY,newZ)) {
335                     movingMap.setBlock(obj,newX,newY,newZ);
336                     return action;
337                 }else {
338                     GameObject nextObj = (GameObject)blockMap.getBlock(newX,newY,newZ);
339                     if(nextObj!=null && nextObj.isMovable(action.getDirection())) {
340                         nextObj.action(getPushActionForDirection(action.getDirection()));
341                         movingMap.setBlock(obj,newX,newY,newZ);
342                         return getPushActionForDirection(action.getDirection());
343                     }
344                 }
345             }else if(action.isMove()) {
346                 LOG.debug("Try changing room "+newX+","+newY+","+newZ);
347                 MapObjectContainerInterface currentRoomMap = obj.getObjectMap();
348                 RoomObject currentRoom = (RoomObject) roomByBlockMap.get(currentRoomMap);
349                 if(isFreeInNextRoom(currentRoom,newX,newY,newZ) && isAllowedToChangeRoom(obj)) {
350                     LOG.debug("Start changing room "+newX+","+newY+","+newZ);
351                     movingMap.setBlock(obj,newX,newY,newZ);
352                     return action;
353                 }
354             }
355         }else if(action==Action.JUMP) {
356             if(isInsideMap(blockMap,obj.getPosX(),obj.getPosY(),obj.getPosZ()+1)) {
357                 if(isFree(obj,obj.getPosX(),obj.getPosY(),obj.getPosZ()+1)) {
358                     movingMap.setBlock(obj,(int)obj.getPosX(),(int)obj.getPosY(),(int)obj.getPosZ()+1);
359                     return action;
360                 }
361             }
362         }else if(action==Action.DROP) {
363             if(isInsideMap(blockMap,obj.getPosX(),obj.getPosY(),obj.getPosZ()-1)) {
364                 if(isFree(obj,obj.getPosX(),obj.getPosY(),obj.getPosZ()-1)) {
365                     movingMap.setBlock(obj,(int)obj.getPosX(),(int)obj.getPosY(),(int)obj.getPosZ()-1);
366                     return action;
367                 }
368             }
369         }
370         return Action.NONE;
371     }
372 
373     public void endActionOnObject(GameObject obj, Action action) {
374         MapObjectContainerInterface movingMap = (MapObjectContainerInterface) movingMapByBlockMap.get(obj.getObjectMap());
375         MapObjectContainerInterface blockMap = (MapObjectContainerInterface) obj.getObjectMap();
376         if(action.isMove() || action.isPush()) {
377             Direction direction = action.getDirection();
378             int newX = (int)getMovingPosX(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
379             int newY = (int)getMovingPosY(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
380             int newZ = (int)getMovingPosZ(obj.getPosX(),obj.getPosY(),obj.getPosZ(),direction);
381             if(isInsideMap(blockMap,newX,newY,newZ)) {
382                 if(movingMap.getBlock(newX,newY,newZ)==obj) {
383                     movingMap.removeBlock(obj,newX,newY,newZ);
384                     obj.setPos(newX,newY,newZ);
385                 }
386             }else {
387                 LOG.debug("Ending changing room "+newX+","+newY+","+newZ);
388                 MapObjectContainerInterface currentRoomMap = obj.getObjectMap();
389                 RoomObject currentRoom = (RoomObject) roomByBlockMap.get(currentRoomMap);
390                 if(isFreeInNextRoom(currentRoom,newX,newY,newZ) && isAllowedToChangeRoom(obj)) {
391                     obj.setPos(newX,newY,newZ);
392                     RoomObject room = findRoom(currentRoom,newX, newY, newZ);
393                     if(newX<0) {
394                         newX=room.getBlocks().getSizeX()-1;
395                     }else if(newX>=currentRoomMap.getSizeX()) {
396                         newX = 0;
397                     }
398                     if(newY<0) {
399                         newY=room.getBlocks().getSizeY()-1;
400                     }else if(newY>=currentRoomMap.getSizeY()) {
401                         newY = 0;
402                     }
403                     if(newZ<0) {
404                         newZ=room.getBlocks().getSizeZ()-1;
405                     }else if(newZ>=currentRoomMap.getSizeZ()) {
406                         newZ = 0;
407                     }
408                     initPlayer(room,obj);
409                     obj.setObjectMap(room.getBlocks());
410                     obj.setPos(newX,newY,newZ);
411                     //objectList.add(obj);
412                     LOG.debug("Changed room "+newX+","+newY+","+newZ);
413                 }
414             }
415         }else if(action==Action.JUMP) {
416             if(isInsideMap(blockMap,obj.getPosX(),obj.getPosY(),obj.getPosZ()+1)) {
417                 LOG.debug("isInsideMap "+obj.getPosZ());
418                 if(movingMap.getBlock((int)obj.getPosX(),(int)obj.getPosY(),(int)obj.getPosZ()+1)==obj) {
419                     LOG.debug("getBlock "+obj.getPosZ());
420                     movingMap.removeBlock(obj,(int)obj.getPosX(),(int)obj.getPosY(),(int)obj.getPosZ()+1);
421                     obj.setPos(obj.getPosX(),obj.getPosY(),obj.getPosZ()+1);
422                 }
423             }
424         }else if(action==Action.DROP) {
425             if(isInsideMap(blockMap,obj.getPosX(),obj.getPosY(),obj.getPosZ()-1)) {
426                 LOG.debug("isInsideMap "+obj.getPosZ());
427                 if(movingMap.getBlock((int)obj.getPosX(),(int)obj.getPosY(),(int)obj.getPosZ()-1)==obj) {
428                     LOG.debug("getBlock "+obj.getPosZ());
429                     movingMap.removeBlock(obj,(int)obj.getPosX(),(int)obj.getPosY(),(int)obj.getPosZ()-1);
430                     obj.setPos(obj.getPosX(),obj.getPosY(),obj.getPosZ()-1);
431                 }
432             }
433         }
434     }
435     private RoomObject findRoom(RoomObject currentRoom, float x, float y, float z) {
436         int roomX = (int)currentRoom.getPosX();
437         int roomY = (int)currentRoom.getPosY();
438         int roomZ = (int)currentRoom.getPosZ();
439         if(x<0) {
440             roomX--;
441         }else if(x>=currentRoom.getBlocks().getSizeX()) {
442             roomX++;
443         }
444         if(y<0) {
445             roomY--;
446         }else if(y>=currentRoom.getBlocks().getSizeY()) {
447             roomY++;
448         }
449         if(z<0) {
450             roomZ--;
451         }else if(z>=currentRoom.getBlocks().getSizeZ()) {
452             roomZ++;
453         }
454         return (RoomObject) roomMap.getBlock(roomX,roomY,roomZ);
455     }
456 
457     private boolean isFreeInNextRoom(RoomObject currentRoom, float x, float y, float z) {
458         RoomObject room = findRoom(currentRoom, x,y,z);
459         if(room!=null) {
460             if(currentRoom.getPosX()>room.getPosX()) {
461                 x = room.getBlocks().getSizeX()-1;
462             }else if(currentRoom.getPosX()<room.getPosX()) {
463                 x = 0;
464             }
465             if(currentRoom.getPosY()>room.getPosY()) {
466                 y = room.getBlocks().getSizeY()-1;
467             }else if(currentRoom.getPosY()<room.getPosY()) {
468                 y = 0;
469             }
470             if(currentRoom.getPosZ()>room.getPosZ()) {
471                 z = room.getBlocks().getSizeZ()-1;
472             }else if(currentRoom.getPosZ()<room.getPosZ()) {
473                 z = 0;
474             }
475             return isFree(room.getBlocks(),null,x,y,z);
476         }
477         return false;
478     }
479     private boolean isAllowedToChangeRoom(GameObject obj) {
480         return obj instanceof LivingObject;
481     }
482 
483     public void register(ActionInterface action) {
484         List list = (List) actionMap.get(action.getController());
485         if(list==null) {
486             list = new ArrayList(10);
487             actionMap.put(action.getController(),list);
488         }
489         for(Iterator it=list.iterator();it.hasNext();) {
490             ActionInterface a = (ActionInterface) it.next();
491             if(a.getClass().equals(action.getClass())) {
492                 a.stop();
493                 it.remove();
494             }
495         }
496         list.add(action);
497     }
498 
499     public void unregister(ActionInterface action) {
500         List list = (List) actionMap.get(action.getController());
501         if(list!=null) {
502             list.remove(action);
503         }
504     }
505 }