Changeset 10181 for code/branches/presentationHS14/src/modules/towerdefense
- Timestamp:
- Dec 15, 2014, 3:58:14 PM (10 years ago)
- Location:
- code/branches/presentationHS14
- Files:
-
- 12 edited
- 8 copied
Legend:
- Unmodified
- Added
- Removed
-
code/branches/presentationHS14
- Property svn:mergeinfo changed
/code/branches/towerdefenseHS14 (added) merged: 10086,10091,10105-10106,10109,10123,10125-10126,10132,10134,10140-10142,10144,10146-10147,10151,10158-10159,10172
- Property svn:mergeinfo changed
-
code/branches/presentationHS14/src/modules/towerdefense/CMakeLists.txt
r9958 r10181 1 1 SET_SOURCE_FILES(TOWERDEFENSE_SRC_FILES 2 2 TowerDefense.cc 3 Tower.cc 3 TowerDefenseTower.cc 4 TowerTurret.cc 4 5 TowerDefenseCenterpoint.cc 5 6 TowerDefenseHUDController.cc 6 7 TowerDefensePlayerStats.cc 8 TDCoordinate.cc 9 TowerDefenseEnemy.cc 7 10 8 11 ) -
code/branches/presentationHS14/src/modules/towerdefense/TDEnemy.h
r9272 r10181 5 5 * Author: weigeltm 6 6 */ 7 7 /* 8 8 #ifndef TDENEMY_H_ 9 9 #define TDENEMY_H_ … … 24 24 25 25 26 #endif /*TDENEMY_H_ */26 #endif TDENEMY_H_ */ -
code/branches/presentationHS14/src/modules/towerdefense/Tower.cc
r9667 r10181 40 40 { 41 41 static int ori; 42 orxout() << "orientation " << ++ori << endl;42 //orxout() << "orientation " << ++ori << endl; 43 43 } 44 44 … … 46 46 { 47 47 static int yaw; 48 orxout() << "rotateYaw " << ++yaw << endl;48 //orxout() << "rotateYaw " << ++yaw << endl; 49 49 } 50 50 … … 52 52 { 53 53 static int pitch; 54 orxout() << "rotatePitch " << ++pitch << endl;54 //orxout() << "rotatePitch " << ++pitch << endl; 55 55 } 56 56 … … 58 58 { 59 59 static int roll; 60 orxout() << "rotateRoll" << ++roll << endl;60 //orxout() << "rotateRoll" << ++roll << endl; 61 61 } 62 62 -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefense.cc
r9667 r10181 20 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 21 * 22 * 22 * Author: 23 23 * 24 24 * Co-authors: … … 73 73 * 74 74 */ 75 76 75 #include "TowerDefense.h" 77 #include "Tower .h"76 #include "TowerDefenseTower.h" 78 77 #include "TowerDefenseCenterpoint.h" 79 78 //#include "TDCoordinate.h" 79 #include "TowerTurret.h" 80 80 #include "worldentities/SpawnPoint.h" 81 81 #include "worldentities/pawns/Pawn.h" 82 82 #include "worldentities/pawns/SpaceShip.h" 83 83 #include "controllers/WaypointController.h" 84 85 84 #include "graphics/Model.h" 86 85 #include "infos/PlayerInfo.h" 87 88 86 #include "chat/ChatManager.h" 89 87 #include "core/CoreIncludes.h" 90 91 88 /* Part of a temporary hack to allow the player to add towers */ 92 89 #include "core/command/ConsoleCommand.h" … … 99 96 { 100 97 RegisterObject(TowerDefense); 98 /* 99 for (int i=0; i < 16 ; i++){ 100 for (int j = 0; j< 16 ; j++){ 101 towermatrix[i][j] = NULL; 102 } 103 }*/ 101 104 102 105 this->setHUDTemplate("TowerDefenseHUD"); 103 106 104 this->stats_ = new TowerDefensePlayerStats();105 106 /* Temporary hack to allow the player to add towers */107 //this->stats_ = new TowerDefensePlayerStats(); 108 109 /* Temporary hack to allow the player to add towers and upgrade them */ 107 110 this->dedicatedAddTower_ = createConsoleCommand( "addTower", createExecutor( createFunctor(&TowerDefense::addTower, this) ) ); 111 this->dedicatedUpgradeTower_ = createConsoleCommand( "upgradeTower", createExecutor( createFunctor(&TowerDefense::upgradeTower, this) ) ); 108 112 } 109 113 … … 127 131 void TowerDefense::start() 128 132 { 133 129 134 Deathmatch::start(); 130 135 131 const int kInitialTowerCount = 3; 132 Coordinate initialTowerCoordinates[kInitialTowerCount] = {{3,2}, {8,5}, {12,10}}; 133 134 for (int i = 0; i < kInitialTowerCount; i++) 135 { 136 Coordinate coordinate = initialTowerCoordinates[i]; 137 addTower(coordinate.x, coordinate.y); 138 } 139 140 ChatManager::message("Use the console command addTower x y to add towers"); 141 142 //TODO: let the player control his controllable entity && TODO: create a new ControllableEntity for the player 143 } 136 // Waypoints: [1,3] [10,3] [10,11] [13,11] -> add the points to a matrix so the player cant place towers on the path 137 for (int i=0; i < 16 ; i++){ 138 for (int j = 0; j< 16 ; j++){ 139 towermatrix[i][j] = false; 140 } 141 } 142 143 for (int k=0; k<3; k++) 144 towermatrix[1][k]=true; 145 for (int l=1; l<11; l++) 146 towermatrix[l][3]=true; 147 for (int m=3; m<12; m++) 148 towermatrix[10][m]=true; 149 for (int n=10; n<14; n++) 150 towermatrix[n][11]=true; 151 for (int o=13; o<16; o++) 152 towermatrix[13][o]=true; 153 154 //set initial credits, lifes and WaveNumber 155 this->setCredit(200); 156 this->setLifes(50); 157 this->setWaveNumber(0); 158 time=0.0; 159 160 //adds initial towers 161 for (int i=0; i <7; i++){ 162 addTower(i+3,4); 163 }/* 164 for (int j=0; j < 7; j++){ 165 addTower(9,j+5); 166 }*/ 167 } 168 // Generates a TowerDefenseEnemy. Uses Template "enemytowerdefense". Sets position at first waypoint of path. 169 void TowerDefense::addTowerDefenseEnemy(std::vector<TDCoordinate*> path, int templatenr){ 170 171 172 TowerDefenseEnemy* en1 = new TowerDefenseEnemy(this->center_->getContext()); 173 174 switch(templatenr) 175 { 176 case 1 : 177 en1->addTemplate("enemytowerdefense1"); 178 en1->setScale(3); 179 en1->setHealth(en1->getHealth() + this->getWaveNumber()*4); 180 181 break; 182 case 2 : 183 en1->addTemplate("enemytowerdefense2"); 184 en1->setScale(2); 185 en1->setHealth(en1->getHealth() + this->getWaveNumber()*4); 186 // en1->setShieldHealth(en1->getShield() = this->getWaveNumber()*2)) 187 188 break; 189 case 3 : 190 en1->addTemplate("enemytowerdefense3"); 191 en1->setScale(1); 192 en1->setHealth(en1->getHealth() + this->getWaveNumber()*4); 193 break; 194 } 195 196 en1->getController(); 197 en1->setPosition(path.at(0)->get3dcoordinate()); 198 TowerDefenseEnemyvector.push_back(en1); 199 200 for(unsigned int i = 0; i < path.size(); ++i) 201 { 202 en1->addWaypoint((path.at(i))); 203 } 204 } 205 144 206 145 207 void TowerDefense::end() 146 { 208 209 { 210 147 211 Deathmatch::end(); 148 149 ChatManager::message("Match is over"); 150 } 151 212 ChatManager::message("Match is over! Gameover!"); 213 214 } 215 216 //not working yet 217 void TowerDefense::upgradeTower(int x,int y) 218 {/* 219 const int upgradeCost = 20; 220 221 if (!this->hasEnoughCreditForTower(upgradeCost)) 222 { 223 orxout() << "not enough credit: " << (this->getCredit()) << " available, " << upgradeCost << " needed."; 224 return; 225 } 226 227 if (towermatrix [x][y] == NULL) 228 { 229 orxout() << "no tower on this position" << endl; 230 return; 231 } 232 233 else 234 { 235 (towermatrix [x][y])->upgradeTower(); 236 }*/ 237 } 238 239 /*adds Tower at Position (x,y) and reduces credit and adds the point to the towermatrix. template ("towerturret") 240 so towers have ability if the turrets 241 242 */ 152 243 void TowerDefense::addTower(int x, int y) 153 244 { 154 const TowerCost towerCost = TDDefaultTowerCost;245 const int towerCost = 20; 155 246 156 247 if (!this->hasEnoughCreditForTower(towerCost)) 157 248 { 158 orxout() << "not enough credit: " << (this-> stats_->getCredit()) << " available, " << TDDefaultTowerCost << " needed.";249 orxout() << "not enough credit: " << (this->getCredit()) << " available, " << towerCost << " needed."; 159 250 return; 160 251 } 161 252 162 if (t his->towerExists(x,y))163 { 164 orxout() << " tower exists!!" << endl;253 if (towermatrix [x][y]==true) 254 { 255 orxout() << "not possible to put tower here!!" << endl; 165 256 return; 166 257 } 167 258 168 259 /* 169 260 unsigned int width = this->center_->getWidth(); 170 261 unsigned int height = this->center_->getHeight(); 171 262 */ 172 263 173 264 int tileScale = (int) this->center_->getTileScale(); … … 182 273 orxout() << "Will add tower at (" << (x-8) * tileScale << "," << (y-8) * tileScale << ")" << endl; 183 274 184 // Add tower to coordinatesStack 185 Coordinate newTowerCoordinates = {x, y}; 186 addedTowersCoordinates_.push_back(newTowerCoordinates); 187 188 // Reduce credit 189 this->stats_->buyTower(towerCost); 190 191 // Create tower 192 Tower* newTower = new Tower(this->center_->getContext()); 193 newTower->addTemplate(this->center_->getTowerTemplate()); 194 195 newTower->setPosition(static_cast<float>((x-8) * tileScale), static_cast<float>((y-8) * tileScale), 75); 196 newTower->setGame(this); 197 } 198 199 bool TowerDefense::hasEnoughCreditForTower(TowerCost towerCost) 200 { 201 return ((this->stats_->getCredit()) >= towerCost); 202 } 203 204 bool TowerDefense::towerExists(int x, int y) 205 { 206 for(std::vector<Coordinate>::iterator it = addedTowersCoordinates_.begin(); it != addedTowersCoordinates_.end(); ++it) 207 { 208 Coordinate currentCoordinates = (Coordinate) (*it); 209 if (currentCoordinates.x == x && currentCoordinates.y == y) 210 return true; 211 } 212 213 return false; 214 } 215 216 275 //Reduce credit 276 this->buyTower(towerCost); 277 towermatrix [x][y]=true; 278 279 //Creates tower 280 TowerDefenseTower* towernew = new TowerDefenseTower(this->center_->getContext()); 281 towernew->addTemplate("towerturret"); 282 towernew->setPosition(static_cast<float>((x-8) * tileScale), static_cast<float>((y-8) * tileScale), 75); 283 towernew->setGame(this); 284 } 285 286 bool TowerDefense::hasEnoughCreditForTower(int towerCost) 287 { 288 return ((this->getCredit()) >= towerCost); 289 } 290 291 292 bool TowerDefense::hasEnoughCreditForUpgrade() 293 { 294 return true; 295 } 296 297 217 298 void TowerDefense::tick(float dt) 218 299 { 219 300 SUPER(TowerDefense, tick, dt); 301 time +=dt; 302 303 TDCoordinate* coord1 = new TDCoordinate(1,1); 304 std::vector<TDCoordinate*> path; 305 path.push_back(coord1); 306 if(time>1 && TowerDefenseEnemyvector.size() < 30) 307 { 308 //adds different types of enemys depending on the WaveNumber 309 addTowerDefenseEnemy(path, this->getWaveNumber() % 3 +1 ); 310 time = time-1; 311 } 312 313 Vector3* endpoint = new Vector3(500, 700, 150); 314 //if ships are at the end they get destroyed 315 for(unsigned int i =0; i < TowerDefenseEnemyvector.size(); ++i) 316 { 317 if(TowerDefenseEnemyvector.at(i) != NULL && TowerDefenseEnemyvector.at(i)->isAlive()) 318 { 319 //destroys enemys at the end of teh path and reduces the life by 1. no credits gifted 320 321 Vector3 ship = TowerDefenseEnemyvector.at(i)->getRVWorldPosition(); 322 float distance = ship.distance(*endpoint); 323 324 if(distance <50){ 325 TowerDefenseEnemyvector.at(i)->destroy(); 326 this->reduceLifes(1); 327 this->buyTower(1); 328 if (this->getLifes()==0) 329 { 330 this->end(); 331 } 332 } 333 } 334 } 335 //goes thorugh vector to see if an enemy is still alive. if not next wave is launched 336 int count= 0; 337 for(unsigned int i =0; i < TowerDefenseEnemyvector.size(); ++i) 338 { 339 if(TowerDefenseEnemyvector.at(i)!= NULL) 340 { 341 ++count; 342 } 343 } 344 345 if(count== 0) 346 { 347 time2 +=dt; 348 if(time2 > 10) 349 { 350 TowerDefenseEnemyvector.clear(); 351 this->nextwave(); 352 time=0; 353 time2=0; 354 } 355 } 356 357 220 358 } 221 359 -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefense.h
r9667 r10181 37 37 #ifndef _TowerDefense_H__ 38 38 #define _TowerDefense_H__ 39 39 #include "TDCoordinate.h" 40 40 #include "towerdefense/TowerDefensePrereqs.h" 41 41 #include "gametypes/Deathmatch.h" 42 43 #include "TowerDefensePlayerStats.h" 42 #include "TowerDefenseEnemy.h" 43 #include "util/Output.h" 44 #include "core/object/WeakPtr.h" 44 45 45 46 namespace orxonox … … 51 52 virtual ~TowerDefense(); 52 53 54 std::vector<orxonox::WeakPtr<TowerDefenseEnemy> > TowerDefenseEnemyvector; 55 bool towermatrix[16][16]; 56 void addTowerDefenseEnemy(std::vector<TDCoordinate*> path, int templatenr); 53 57 virtual void start(); //<! The function is called when the gametype starts 54 58 virtual void end(); … … 56 60 //virtual void playerEntered(PlayerInfo* player); 57 61 //virtual bool playerLeft(PlayerInfo* player); 62 //Player Stats (set,get, reduce) 63 int getCredit(){ return this->credit_; } 64 int getLifes(){ return this->lifes_; } 65 int getWaveNumber(){ return this->waves_; } 66 void setCredit(int credit){ credit_ = credit; } 67 void setLifes(int lifes){ lifes_ = lifes; } 68 void setWaveNumber(int wavenumber){waves_=wavenumber; } 69 void buyTower(int cost){ credit_ -= cost;} 70 void addCredit(int credit) { credit_+=credit; } 71 void nextwave(){ waves_++;} 72 int reduceLifes(int NumberofLifes){ return lifes_-=NumberofLifes; } 58 73 59 74 //virtual void pawnKilled(Pawn* victim, Pawn* killer = 0); … … 69 84 void addTower(int x, int y); 70 85 86 void upgradeTower(int x, int y); 71 87 /* Part of a temporary hack to allow the player to add towers */ 72 88 ConsoleCommand* dedicatedAddTower_; 89 ConsoleCommand* dedicatedUpgradeTower_; 73 90 74 91 //TODO: void spawnNewWave() … … 79 96 private: 80 97 TowerDefenseCenterpoint *center_; 98 float time; 99 float time2; 100 int credit_; 101 int waves_; 102 int lifes_; 81 103 82 104 /* handles stats */ 83 TowerDefensePlayerStats *stats_;84 bool hasEnoughCreditFor Tower(TowerCost towerCost);105 bool hasEnoughCreditForTower(int towerCost); 106 bool hasEnoughCreditForUpgrade(); 85 107 86 bool towerExists(int x, int y);87 108 88 typedef struct {89 int x;90 int y;91 } Coordinate;92 109 93 std::vector<Coordinate> addedTowersCoordinates_; 94 std::vector<Tower*> towers_; 110 std::vector<TowerTurret*> towers_; 95 111 }; 96 112 } -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefenseController.cc
r9667 r10181 33 33 #include "core/XMLPort.h" 34 34 #include "worldentities/pawns/Pawn.h" 35 #include "controllers/WaypointController.h" 35 36 36 37 namespace orxonox … … 53 54 XMLPortParam(TowerDefenseController, "alertnessradius", setAlertnessRadius, getAlertnessRadius, xmlelement, mode).defaultValues(500.0f); 54 55 } 56 55 57 56 58 void TowerDefenseController::tick(float dt) -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefenseEnemy.cc
r10180 r10181 30 30 //needed to keep track of the PlayerStats coded in TowerDefense.h 31 31 this->td = orxonox_cast<TowerDefense*>(this->getGametype().get()); 32 once_=false; 32 33 33 34 } 34 35 //add credit if enemy is destroyed 35 36 TowerDefenseEnemy::~TowerDefenseEnemy(){ 36 this->td->addCredit(1);37 //this->td->addCredit(1); 37 38 } 38 39 … … 48 49 } 49 50 51 WeakPtr<TowerDefense> TowerDefenseEnemy::getGame() 52 { 53 if (game == NULL) 54 { 55 for (ObjectList<TowerDefense>::iterator it = ObjectList<TowerDefense>::begin(); it != ObjectList<TowerDefense>::end(); ++it) 56 game = *it; 57 } 58 return game; 59 } 60 61 void TowerDefenseEnemy::damage(float damage, float healthdamage, float shielddamage, Pawn* originator) 62 { 63 Pawn::damage(damage, healthdamage, shielddamage, originator); 64 if (getGame() && once_ == false && getHealth() <= 0) 65 { 66 getGame()->addCredit(1); 67 once_ = true; 68 } 69 } 50 70 /* 51 71 void TowerDefenseEnemy::popWaypoint() -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefenseEnemy.h
r10180 r10181 43 43 TDCoordinate peekWaypoint(); 44 44 45 virtual void damage(float damage, float healthdamage, float shielddamage, Pawn* originator); 45 46 46 private: 47 48 protected: 49 50 WeakPtr<TowerDefense> getGame(); 51 WeakPtr<TowerDefense> game; 47 52 TowerDefense* td; 48 53 bool once_; -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefenseHUDController.cc
r9667 r10181 32 32 #include "util/Convert.h" 33 33 34 35 34 namespace orxonox 36 35 { … … 40 39 { 41 40 RegisterObject(TowerDefenseHUDController); 41 this->td = 0; 42 42 } 43 43 … … 50 50 { 51 51 SUPER(TowerDefenseHUDController, tick, dt); 52 const std::string& lifes = multi_cast<std::string>(this->td->getLifes()); 53 const std::string& credits = multi_cast<std::string>(this->td->getCredit()); 54 const std::string& wave = multi_cast<std::string>(this->td->getWaveNumber()); 55 56 if(showlives == true) 57 this->setCaption(multi_cast<std::string>(lifes)); 58 else if(showcredits == true) 59 this->setCaption(multi_cast<std::string>(credits)); 60 else if(showwaves == true) 61 this->setCaption(multi_cast<std::string>(wave)); 62 63 52 64 } 53 65 … … 55 67 { 56 68 SUPER(TowerDefenseHUDController, XMLPort, xmlelement, mode); 69 70 XMLPortParam(TowerDefenseHUDController, "showlives", setShowlives, getShowlives, xmlelement, mode); 71 XMLPortParam(TowerDefenseHUDController, "showcredits", setShowcredits, getShowcredits, xmlelement, mode); 72 XMLPortParam(TowerDefenseHUDController, "showwaves", setShowwaves, getShowwaves, xmlelement, mode); 73 57 74 } 58 75 59 76 void TowerDefenseHUDController::changedOwner() 60 { 61 SUPER(TowerDefenseHUDController, changedOwner); 62 /* 63 if (this->getOwner() != NULL && this->getOwner()->getGametype()) 64 this->owner_ = orxonox_cast<Pong*>(this->getOwner()->getGametype().get()); 65 else 66 this->owner_ = 0; 67 */ 68 } 77 { 78 SUPER(TowerDefenseHUDController, changedOwner); 69 79 70 } 80 if (this->getOwner() && this->getOwner()->getGametype()) 81 { 82 this->td = orxonox_cast<TowerDefense*>(this->getOwner()->getGametype().get()); 83 } 84 else 85 { 86 this->td = 0; 87 } 88 } 89 } 90 -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefenseHUDController.h
r9667 r10181 39 39 40 40 #include "towerdefense/TowerDefensePrereqs.h" 41 41 #include "TowerDefensePlayerStats.h" 42 42 #include "tools/interfaces/Tickable.h" 43 43 #include "overlays/OverlayText.h" 44 44 #include "TowerDefense.h" 45 45 46 46 namespace orxonox … … 52 52 virtual ~TowerDefenseHUDController(); 53 53 54 54 55 virtual void tick(float dt); 55 56 virtual void XMLPort(Element& xmlelement, XMLPort::Mode mode); 56 57 virtual void changedOwner(); 58 void setShowlives(bool temp) 59 { this->showlives = temp; } 60 unsigned int getShowlives(void) const 61 { return this->showlives; } 62 63 void setShowcredits(bool temp) 64 { this->showcredits = temp; } 65 unsigned int getShowcredits(void) const 66 { return this->showcredits; } 67 68 void setShowwaves(bool temp) 69 { this->showwaves = temp; } 70 unsigned int getShowwaves(void) const 71 { return this->showwaves; } 72 73 private: 74 TowerDefense* td; 75 bool showcredits; 76 bool showlives; 77 bool showwaves; 57 78 }; 58 79 } -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefensePlayerStats.cc
r9272 r10181 25 25 * ... 26 26 */ 27 27 /* 28 28 #include "TowerDefensePlayerStats.h" 29 29 … … 31 31 { 32 32 const int kDefaultCredit = 200; 33 33 const int kDefaultLifes=20; 34 34 TowerDefensePlayerStats::TowerDefensePlayerStats() 35 35 { 36 36 this->credit_ = kDefaultCredit; 37 37 this->waveNumber_ = 0; 38 this->lifes_=kDefaultLifes; 38 39 } 39 /* 40 40 41 TowerDefensePlayerStats::~TowerDefensePlayerStats() 41 42 { 42 43 } 43 */ 44 44 45 } 46 */ -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefensePlayerStats.h
r9272 r10181 27 27 */ 28 28 29 /* *29 /* 30 30 @brief 31 31 This manages the stats of the player. It is used by 'TowerDefense', the gametype 32 32 33 33 @ingroup TowerDefense 34 */ 34 35 35 36 36 … … 44 44 typedef enum _TowerCosts { 45 45 // Set to 0 for debug... 46 TDDefaultTowerCost = 0 46 TDDefaultTowerCost = 20 , 47 TDDefaultUpgradeCost = 15 47 48 } TowerCost; 48 49 … … 67 68 { waveNumber_++; } 68 69 70 inline int getLifes() 71 {return lifes_; } 72 73 inline void reduceLifes(int NumberofLifes) 74 {lifes_-=NumberofLifes; } 75 76 inline void upgradeTower(int upgradecost) 77 { credit_ -= upgradecost;} 78 79 80 69 81 private: 70 82 int credit_; 71 83 int waveNumber_; 84 int upgradecost; 85 int lifes_; 72 86 //int baseHealth_; 73 87 }; 74 88 } 75 89 76 #endif /*_TowerDefensePlayerStats_H__ */90 #endif _TowerDefensePlayerStats_H__ */ -
code/branches/presentationHS14/src/modules/towerdefense/TowerDefensePrereqs.h
r9272 r10181 66 66 { 67 67 class TowerDefense; 68 class Tower; 68 69 class TowerDefenseTower; 70 69 71 class TowerDefenseCenterpoint; 70 72 class TowerDefenseHUDController; 71 73 class TowerDefensePlayerStats; 74 class TowerDefenseEnemy; 75 class TDCoordinate; 76 class TowerTurret; 77 class TowerDefenseTower; 72 78 } 73 79
Note: See TracChangeset
for help on using the changeset viewer.