mElite  1.0
An Elite clone based on TextElite by Jan-Philipp Kappmeier and Melanie Schmidt.
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
txtelite.cpp
Go to the documentation of this file.
1 
7 /*jm* * Within this file all comments starting with /*jm are made by
8 Jan-Philipp and Melanie. All other comments are from the
9 original file *** */
10 /*jm* * The original code has been changed like this:
11 - we splitted it into declarations (txtelite.h) and
12 program code (txtelite.cpp)
13 - the code was transformed into a class "TextEliteGame",
14 so that we can construct a textEliteGame-object and use
15 it nicely with our other cpp-classes
16 - all functions that execute user commands (like buy fuel,
17 jump somewhere, ..) were rewritten andextended with
18 more error messages (the original code only provided
19 unsharp error messages like "Can't buy any fuel" instead
20 of "tank is full" or "you don't have enough cash").
21 - all functionality for printing on the screen was deleted
22 and, if necessary, replaced by returning strings
23 - all functionality for user input from keyboard was deleted (like the
24 parser for strings) and replaced by function calls ****************** */
25 
26 
27 #include "txtelite.h"
28 
29 /*jm****************************** * functions ******************************** */
30 
31 /*jm******** * constructor and destructor for new textEliteGame *************** */
32 
33 TextEliteGame::TextEliteGame( void (*vuserErrorFnc)(std::string), void (*vprogErrorFnc)(std::string) ){
34 
35  /*jm * some values couldn't be initialized in txtelite.h; this is done here */
36  initValues();
37 
38  /*jm these functions are for sending error messages to the class that uses
39  the game as an object ************************************************ */
40  userErrorFnc = vuserErrorFnc;
41  progErrorFnc = vprogErrorFnc;
42 
43  /*jm******* * part of the original main-function: starts here ************** */
44 
45  /* 6502 Elite fires up at Lave with fluctuation=00
46  and these prices tally with the NES ones.
47  However, the availabilities reside in the saved game data.
48  Availabilities are calculated (and fluctuation randomised)
49  on hyperspacing
50  I have checked with this code for Zaonce with fluctaution &AB
51  against the SuperVision 6502 code and both prices and availabilities tally.
52  */
53 
54  uint i;
55 
56  nativerand=1;
57 
58  for(i=0;i<=lasttrade;i++) strcpy_s(tradnames[i],commodities[i].name);
59 
60  mysrand(12345);/* Ensure repeatability */
61 
62  galaxynum=1; buildgalaxy(galaxynum);
63 
64  currentplanet=numforLave;/* Don't use jump */
65  localmarket = genmarket(0x00,galaxy[numforLave]);/* Since want seed=0 */
66 
67  fuel=maxfuel;
68 
69  setCargoBay(20);/* Small cargo bay */
70  setCash(1000); /* 100 CR */
71 
72  /*jm******* * part of the original main-function: ends here **************** */
73 
74 }
75 
77 
82 /*jm**** * function returns the number of the system we are currently in ****** */
84 {
85  return currentplanet;
86 }
87 
88 /*jm * function returns an int-vector with the numbers of all systems in range */
89 std::vector<int> * TextEliteGame::getLocalSystems()
90 {planetnum syscount;
91 uint d;
92 std::vector<int> * myLocalSystems = new std::vector<int>;
93 
94 for(syscount=0;syscount<galsize;++syscount)
95 { d=distance(galaxy[syscount],galaxy[currentplanet]);
96 if(d<=maxfuel)
97 {
98  myLocalSystems->push_back(syscount);
99 }
100 }
101 return myLocalSystems;
102 }
103 
104 /*jm * function returns an int-vector with the numbers
105 of all systems the given rectangle */
106 std::vector<int> * TextEliteGame::getSystemsInRectangle(unsigned int left,
107  unsigned int right, unsigned int bottom, unsigned int top)
108 {planetnum syscount;
109 
110 std::vector<int> * mySystems = new std::vector<int>;
111 
112 if (left < 0) left = 0;
113 if (bottom < 0) bottom = 0;
114 if (right < 0) right = 0;
115 if (top < 0) top = 0;
116 
117 for(syscount=0;syscount<galsize;++syscount)
118 {
119  if( (galaxy[syscount].x >= left)
120  && (galaxy[syscount].x <= right)
121  && (galaxy[syscount].y >= bottom)
122  && (galaxy[syscount].y <= top))
123  {
124  mySystems->push_back(syscount);
125  }
126 }
127 return mySystems;
128 }
129 
130 /*jm**** * function returns information about the system with the given number **
131 the structure 'system' from wrappDef.h is used ****************** */
133 {
134  System mySystem;
135  if (number<0 || number >= galsize) {
136  progErrorFnc("The integer in 'number' has to be between 0 and 256! Error occured in getSystemInformation(int number) in txtelite.cpp");
137  mySystem.name = "Error! No System";
138  mySystem.systemnumber = 0;
139  mySystem.posx = 0;
140  mySystem.posy = 0;
141  mySystem.government = Anarchy;
142  mySystem.economy = PoorAgri;
143  mySystem.techLevel = 0;
144  mySystem.population = 0;
145  mySystem.productivity = 0;
146  mySystem.radius = 0;
147  mySystem.description = "An error occured!";
148  return mySystem;
149  }
150  plansys mySys = galaxy[number];
151  mySystem.systemnumber = number;
152  mySystem.posx = mySys.x;
153  mySystem.posy = mySys.y;
154  mySystem.name = mySys.name;
155  mySystem.government = getEnumOfGovernment(mySys.govtype);
156  mySystem.economy = getEnumOfEconomy(mySys.economy);
157  mySystem.techLevel = mySys.techlev;
158  mySystem.population = mySys.population;
159  mySystem.productivity = mySys.productivity;
160  mySystem.radius = mySys.radius;
161 
162  rnd_seed = mySys.goatsoupseed;
163  mySystem.description = goat_soup("\x8F is \x97.",&mySys);
164 
165  return mySystem;
166 }
167 
168 /*jm*** * Returns whether the system 'number' is reachable from the current
169 system with the current fuel ********************************** */
171 {
172  uint d=distance(galaxy[number],galaxy[currentplanet]);
173  return (d <= fuel);
174 }
175 
176 /*jm*** * Returns whether the system 'number' is reachable from the current
177 system if we have maximal amount of fuel **************************** */
179 {
180  uint d=distance(galaxy[number],galaxy[currentplanet]);
181  return (d <= maxfuel);
182 }
183 
184 /*jm**** * function returns the actual amount of fuel ************************* */
186 {
187  return (float)(fuel/10);
188 }
189 
190 /*jm*** * returns the amount of fuel that fits into the tank ****************** */
192 {
193  return (float)(maxfuel/10);
194 }
195 
196 
197 /*jm*** * Gives a list of MarketplaceItems saying how much is available of each
198 of the 17 items and how much it costs (both regarding the market
199 in the current system *********************************************** */
200 std::vector<MarketplaceItem> * TextEliteGame::getCurrentMarketplace()
201 {
202  std::vector<MarketplaceItem> * result = new std::vector<MarketplaceItem>;
203  for (int i=0; i < lasttrade + 1; i++)
204  {
205  MarketplaceItem mi;
206  mi.price = (float)(localmarket.price[i])/10;
207  mi.amount = localmarket.quantity[i];
208  result->push_back(mi);
209  }
210  return result;
211 }
212 
213 /*jm*** * Tells how much we have in our cargo for each of the 17 buyable items. */
214 std::vector<int> * TextEliteGame::getCurrentCargo()
215 {
216  std::vector<int> * result = new std::vector<int>;
217  for (int i=0; i < lasttrade + 1; i++)
218  {
219  result->push_back(shipshold[i]);
220  }
221  return result;
222 }
223 
224 /*jm*** * Tells how much space is free for storing additional items. Only items
225 with unit 't' are taken into account, 'kg' and 'g' make no difference */
227 {
228  return holdspace;
229 }
230 
231 /*jm******* * Returns the size of the cargo bay. Value must be calculated ***** */
233 {
234  uint t = 0;
235  for(int i=0;i<=lasttrade;++i)
236  {
237  if ((commodities[i].units)==tons) t+=shipshold[i];
238  }
239  return (t+holdspace);
240 }
241 
242 /*jm******* * Returns the amount of available cash *************************** */
244 {
245  return cash;
246 }
247 
248 /*jm******* * Returns the price for one unit of fuel ************************* */
250 {
251  return fuelcost;
252 }
253 
254 /*jm this function returns whether a certain tradegood has tonnes as unit. *****
255 * * this is important because only items in tonnes count for the *
256 * * calculation of the free holdspace. *************************************** */
258 {
259  return ((commodities[number].units)==tons);
260 }
261 
262 
263 /*jm****************************************************************************
264 ******************** * new functions: actions ***********************************
265  */
266 
267 /*jm****************** * Sets the amount of fuel that we have ***************** */
269 {
270  if (amount > maxfuel){
271  progErrorFnc("'amount' must be between 0.0 and 7.0. Error occured in setFuel in txtelite.cpp");
272  return;
273  }
274  fuel = amount;
275 }
276 
277 /*jm****************** * Sets the amount of cash that we have ***************** */
278 void TextEliteGame::setCash(signed long amount)
279 {
280  cash = amount;
281 }
282 
283 /*jm************** * Sets the size of the cargo bay that we have ****************
284 Sends an error if we have to much cargo for the new value */
286 {
287  uint t = 0;
288  for(int i=0;i<=lasttrade;++i)
289  {
290  if ((commodities[i].units)==tons) t+=shipshold[i];
291  }
292  if(t>amount) {
293  progErrorFnc("The cargo bay cannot be set to this value because there is too much cargo. Error occured in setCargoBay in txtelite.cpp.");
294  return;
295  }
296  holdspace=amount-t;
297  return;
298 }
299 
300 /*jm**************** * new functions: actions ********************************* */
301 
302 /*jm*** * Attempts to jump to the system with number 'number'. ******************
303 In case of errors the error functions are called ******************** */
305 {
306  if (number < 0 || number > galsize)
307  {
308  progErrorFnc("The integer in 'number' has to be between 0 and 256! Error occured in performJump(int number) in txtelite.cpp"); return; };
309 
310  uint d=distance(galaxy[number],galaxy[currentplanet]);
311 
312  if (d>maxfuel)
313  {
314  userErrorFnc("Jump too far (system not in range, even with full fuel)"); return; };
315 
316  if (d > fuel)
317  {
318  userErrorFnc("Jump too far (system in range, but not enough fuel)"); return; };
319 
320  if (number == currentplanet)
321  { userErrorFnc("Already there!"); return; };
322 
323  fuel-=d;
324 
325  currentplanet=number;
326  localmarket = genmarket(randbyte(),galaxy[number]);
327 
328  /*gamejump(number); */
329  std::string report = "Jumped to ";
330  report += galaxy[number].name;
331  report += ".";
332  userErrorFnc(report);
333 }
334 
335 /*jm*** * Attempts to buy 'amount' items of type 'number'. There are a lot of ***
336 error texts that are returned if the player cannot buy what he wants */
337 std::string TextEliteGame::performPurchase(uint number, uint amount)
338 {
339  uint t = amount;
340  std::string errorreport;
341 
342  if (number >= lasttrade+1) {
343  progErrorFnc("'number' is too high. This error occured in the function performPurchase in the file txtelite.cpp");
344  errorreport = "";
345  return errorreport;
346  }
347 
348  errorreport = "Nothing was bought";
349 
350  if (amount == 0) return errorreport;
351  if (cash <= 0){
352  userErrorFnc("You don't have any cash.");
353  return errorreport;
354  }
355  t = mymin((uint)floor((double)cash/(localmarket.price[number])),t);
356  if (t == 0) {
357  std::string errortext = "You don't have enough cash to buy anything.";
358  userErrorFnc(errortext);
359  return errorreport;
360  }
361  if (localmarket.quantity[number] <= 0){
362  userErrorFnc("The market does not sell this at the moment.");
363  return errorreport;
364  }
365  t = mymin(localmarket.quantity[number],t);
366  if ((commodities[number].units)==tons){
367  if (holdspace == 0){
368  userErrorFnc("Your cargo bay is full! You can only buy items that weigh less than a ton.");
369  return errorreport;
370  }
371  t = mymin(holdspace,t);
372  }
373  shipshold[number]+=t;
374  localmarket.quantity[number]-=t;
375  cash-=t*(localmarket.price[number]);
376  if ((commodities[number].units)==tons) {holdspace-=t;}
377 
378  std::stringstream freport;
379  std::string sreport;
380  freport << (float)t;
381  freport >> sreport;
382  std::string report="Bought ";
383  report += sreport;
384  report += unitnames[commodities[number].units];
385  report += " of ";
386  report += tradnames[number];
387  return report;
388 }
389 
390 /*jm* * Attempts to sell 'amount' items of type 'number'. This does work if
391 the user has enough items to sell, otherwise less or nothing is bought */
392 std::string TextEliteGame::performSale(uint number, uint amount)
393 {
394  uint t = amount;
395 
396  if (number >= lasttrade+1) {
397  progErrorFnc("'number' is too high. This error occured in the function performPurchase in the file txtelite.cpp");
398  return "";
399  }
400 
401  if (shipshold[number] == 0) {
402  userErrorFnc("You don't have any of these.");
403  return "Nothing was selled";
404  }
405  shipshold[number]-=t;
406  localmarket.quantity[number]+=t;
407  if ((commodities[number].units)==tons) {holdspace+=t;}
408  cash+=t*(localmarket.price[number]);
409 
410  std::stringstream freport;
411  std::string sreport;
412  freport << (float)t;
413  freport >> sreport;
414  std::string report="Sold ";
415  report += sreport;
416  report += unitnames[commodities[number].units];
417  report += " of ";
418  report += tradnames[number];
419  return report;
420 }
421 
422 /*jm*** * Attempts to buy 'amount' fuel. When this is not possible, the
423 corrisponding error message is returned. **************************** */
424 void TextEliteGame::buyFuel(float amount)
425 {
426  if (fuel == maxfuel)
427  userErrorFnc("Can't buy any fuel (Tank is already full).");
428  else
429  {
430  uint f = (uint)floor(10*amount);
431  if (f+fuel>maxfuel)
432  {
433  userErrorFnc("Buying less fuel (otherwise it doesn't fit into the tank). ");
434  f = maxfuel - fuel;
435  }
436  if (fuelcost>0)
437  {
438  if ((int)f*fuelcost>cash)
439  {
440  f = (uint)(cash/fuelcost);
441  if (f >0)
442  userErrorFnc("Buying less fuel (not enough cash available to buy whole amount). ");
443  else
444  userErrorFnc("Can't buy any fuel (Not enough cash).");
445  }
446  }
447  if (f > 0)
448  {
449  fuel+=f;
450  cash-=fuelcost*f;
451  std::stringstream freport;
452  std::string sreport;
453  freport << (float)f/10;
454  freport >> sreport;
455  std::string report="Bought ";
456  report += sreport;
457  report += " LY fuel.";
458  userErrorFnc(report);
459  }
460  }
461 }
462 
463 /*jm****************************************************************************
464 **************** * new functions: help functions ********************************
465  */
466 
467 /*jm**** * function to set values that were previously preinitialized ***********
468 ******* * (initialization commands don't work within class definition) ******** */
470  lastrand = 0;
471 
472  strcpy_s(unitnames[0],"t");
473  strcpy_s(unitnames[1],"kg");
474  strcpy_s(unitnames[2],"g");
475 
476  commodities[0].baseprice=0x13;
477  commodities[0].gradient=-0x02;
478  commodities[0].basequant=0x06;
479  commodities[0].maskbyte=0x01;
480  commodities[0].units=0;
481  strcpy_s(commodities[0].name,"Food ");
482 
483  commodities[1].baseprice=0x14;
484  commodities[1].gradient=-0x01;
485  commodities[1].basequant=0x0A;
486  commodities[1].maskbyte=0x03;
487  commodities[1].units=0;
488  strcpy_s(commodities[1].name,"Textiles");
489 
490  commodities[2].baseprice=0x41;
491  commodities[2].gradient=-0x03;
492  commodities[2].basequant=0x02;
493  commodities[2].maskbyte=0x07;
494  commodities[2].units=0;
495  strcpy_s(commodities[2].name,"Radioactives");
496 
497  commodities[3].baseprice=0x28;
498  commodities[3].gradient=-0x05;
499  commodities[3].basequant=0xE2;
500  commodities[3].maskbyte=0x1F;
501  commodities[3].units=0;
502 #if POLITICALLY_CORRECT
503  strcpy_s(commodities[3].name,"Robot Slaves");
504 #else
505  strcpy_s(commodities[3].name,"Slaves");
506 #endif
507 
508  commodities[4].baseprice=0x53;
509  commodities[4].gradient=-0x05;
510  commodities[4].basequant=0xFB;
511  commodities[4].maskbyte=0x0F;
512  commodities[4].units=0;
513 #if POLITICALLY_CORRECT
514  strcpy_s(commodities[4].name,"Beverages ");
515 #else
516  strcpy_s(commodities[4].name,"Liquor/Wines");
517 #endif
518 
519  commodities[5].baseprice=0xC4;
520  commodities[5].gradient=+0x08;
521  commodities[5].basequant=0x36;
522  commodities[5].maskbyte=0x03;
523  commodities[5].units=0;
524  strcpy_s(commodities[5].name,"Luxuries");
525 
526  commodities[6].baseprice=0xEB;
527  commodities[6].gradient=+0x1D;
528  commodities[6].basequant=0x08;
529  commodities[6].maskbyte=0x78;
530  commodities[6].units=0;
531 #if POLITICALLY_CORRECT
532  strcpy_s(commodities[6].name,"Rare Species");
533 #else
534  strcpy_s(commodities[6].name,"Narcotics ");
535 #endif
536 
537  commodities[7].baseprice=0x9A;
538  commodities[7].gradient=+0x0E;
539  commodities[7].basequant=0x38;
540  commodities[7].maskbyte=0x03;
541  commodities[7].units=0;
542  strcpy_s(commodities[7].name,"Computers ");
543 
544  commodities[8].baseprice=0x75;
545  commodities[8].gradient=+0x06;
546  commodities[8].basequant=0x28;
547  commodities[8].maskbyte=0x07;
548  commodities[8].units=0;
549  strcpy_s(commodities[8].name,"Machinery ");
550 
551  commodities[9].baseprice=0x4E;
552  commodities[9].gradient=+0x01;
553  commodities[9].basequant=0x11;
554  commodities[9].maskbyte=0x1F;
555  commodities[9].units=0;
556  strcpy_s(commodities[9].name,"Alloys");
557 
558  commodities[10].baseprice=0x7C;
559  commodities[10].gradient=+0x0d;
560  commodities[10].basequant=0x1D;
561  commodities[10].maskbyte=0x07;
562  commodities[10].units=0;
563  strcpy_s(commodities[10].name,"Firearms");
564 
565  commodities[11].baseprice=0xB0;
566  commodities[11].gradient=-0x09;
567  commodities[11].basequant=0xDC;
568  commodities[11].maskbyte=0x3F;
569  commodities[11].units=0;
570  strcpy_s(commodities[11].name,"Furs ");
571 
572  commodities[12].baseprice=0x20;
573  commodities[12].gradient=-0x01;
574  commodities[12].basequant=0x35;
575  commodities[12].maskbyte=0x03;
576  commodities[12].units=0;
577  strcpy_s(commodities[12].name,"Minerals");
578 
579  commodities[13].baseprice=0x61;
580  commodities[13].gradient=-0x01;
581  commodities[13].basequant=0x42;
582  commodities[13].maskbyte=0x07;
583  commodities[13].units=1;
584  strcpy_s(commodities[13].name,"Gold ");
585 
586  commodities[14].baseprice=0xAB;
587  commodities[14].gradient=-0x02;
588  commodities[14].basequant=0x37;
589  commodities[14].maskbyte=0x1F;
590  commodities[14].units=1;
591  strcpy_s(commodities[14].name,"Platinum");
592 
593  commodities[15].baseprice=0x2D;
594  commodities[15].gradient=-0x01;
595  commodities[15].basequant=0xFA;
596  commodities[15].maskbyte=0x0F;
597  commodities[15].units=2;
598  strcpy_s(commodities[15].name,"Gem-Strones ");
599 
600  commodities[16].baseprice=0x35;
601  commodities[16].gradient=+0x0F;
602  commodities[16].basequant=0xC0;
603  commodities[16].maskbyte=0x07;
604  commodities[16].units=0;
605  strcpy_s(commodities[16].name,"Alien Items ");
606 
607  for (int i = 0; i < lasttrade+1; i++) { shipshold[i] = 0; }
608 
609  cash=0;
610 }
611 
612 /*jm***** * Converts an uint governmentnumber into the appropriate enum value * */
614 {
615  switch (gov) {
616 case 0: return Anarchy;
617 case 1: return Feudal;
618 case 2: return MultiGov;
619 case 3: return Dictatorship;
620 case 4: return Communist;
621 case 5: return Confederacy;
622 case 6: return Democracy;
623 case 7: return CorporateState;
624 default: return Anarchy;
625  }
626 }
627 
628 /*jm****** * Converts an uint economynumber into the appropriate enum value *** */
630 {
631  switch(econ) {
632  case 0: return RichInd;
633  case 1: return AverageInd;
634  case 2: return PoorInd;
635  case 3: return MainlyInd;
636  case 4: return MainlyAgri;
637  case 5: return RichAgri;
638  case 6: return AverageAgri;
639  case 7: return PoorAgri;
640  default: return PoorAgri;
641  }
642 }
643 
644 /*jm****************************************************************************
645 /*************************** * original functions *******************************
646  */
647 
648 /*jm************ * functions for random generation (from original code) ******* */
649 
652 void TextEliteGame::mysrand(unsigned int seed)
653 { srand(seed);
654 lastrand = seed - 1;
655 }
656 
657 int TextEliteGame::myrand(void)
658 { int r;
659 if(nativerand) r=rand();
660 else
661 { // As supplied by D McDonnell from SAS Insititute C
662  r = (((((((((((lastrand << 3) - lastrand) << 3)
663  + lastrand) << 1) + lastrand) << 4)
664  - lastrand) << 1) - lastrand) + 0xe60)
665  & 0x7fffffff;
666  lastrand = r - 1;
667 }
668 return(r);
669 }
670 
671 char TextEliteGame::randbyte(void)
672 {
673  return (char)(myrand()&0xFF);
674 }
675 
676 /*jm******************* * help functions (original code) ********************** */
677 
678 uint TextEliteGame::mymin(uint a,uint b)
679 {
680  if(a<b) return(a);
681  else return(b);
682 }
683 
685 signed int TextEliteGame::ftoi(double value)
686 { return ((signed int)floor(value+0.5));
687 }
688 
689 void TextEliteGame::tweakseed(seedtype *s)
690 { uint16 temp;
691 temp = ((*s).w0)+((*s).w1)+((*s).w2); /* 2 byte aritmetic */
692 (*s).w0 = (*s).w1;
693 (*s).w1 = (*s).w2;
694 (*s).w2 = temp;
695 }
696 
697 /*jm*************** * string functions (original code) ************************ */
700 void TextEliteGame::stripout(char *s,const char c) /* Remove all c's from string s */
701 { size_t i=0,j=0;
702 while(i<strlen(s))
703 { if(s[i]!=c) { s[j]=s[i]; j++;}
704 i++;
705 }
706 s[j]=0;
707 }
708 
709 int TextEliteGame::toupper(char c)
710 { if((c>='a')&&(c<='z')) return(c+'A'-'a');
711 return((int)c);
712 }
713 
714 int TextEliteGame::tolower(char c)
715 { if((c>='A')&&(c<='Z')) return(c+'a'-'A');
716 return((int)c);
717 }
718 
719 
720 
721 markettype TextEliteGame::genmarket(uint fluct, plansys p)
722 /* Prices and availabilities are influenced by the planet's economy type
723 (0-7) and a random "fluctuation" byte that was kept within the saved
724 commander position to keep the market prices constant over gamesaves.
725 Availabilities must be saved with the game since the player alters them
726 by buying (and selling(?))
727 
728 Almost all operations are one byte only and overflow "errors" are
729 extremely frequent and exploited.
730 
731 Trade Item prices are held internally in a single byte=true value/4.
732 The decimal point in prices is introduced only when printing them.
733 Internally, all prices are integers.
734 The player's cash is held in four bytes.
735  */
736 
737 { markettype market;
738 unsigned short i;
739 for(i=0;i<=lasttrade;i++)
740 { signed int q;
741 signed int product = (p.economy)*(commodities[i].gradient);
742 signed int changing = fluct & (commodities[i].maskbyte);
743 q =(commodities[i].basequant) + changing - product;
744 q = q&0xFF;
745 if(q&0x80) {q=0;};/* Clip to positive 8-bit */
746 
747 market.quantity[i] = (uint16)(q & 0x3F); /* Mask to 6 bits */
748 
749 q =(commodities[i].baseprice) + changing + product;
750 q = q & 0xFF;
751 market.price[i] = (uint16) (q*4);
752 }
753 market.quantity[AlienItems] = 0; /* Override to force nonavailability */
754 return market;
755 }
756 
759 plansys TextEliteGame::makesystem(seedtype *s)
760 {
761 
762  char pairs[] = "..lexegezacebiso"// LEXEGEZACEBISO"
763  "usesarmaindirea."// USESARMAINDIREA."
764  "eratenberalaveti"// ERATENBERALAVETI"
765  "edorquanteisrion"; // EDORQUANTEISRION"; /* Dots should be nullprint characters */
766 
767  plansys thissys;
768  uint pair1,pair2,pair3,pair4;
769  uint16 longnameflag=((*s).w0)&64;
770 
771  thissys.x=(((*s).w1)>>8);
772  thissys.y=(((*s).w0)>>8);
773 
774  thissys.govtype =((((*s).w1)>>3)&7); /* bits 3,4 &5 of w1 */
775 
776  thissys.economy =((((*s).w0)>>8)&7); /* bits 8,9 &A of w0 */
777  if (thissys.govtype <=1)
778  { thissys.economy = ((thissys.economy)|2);
779  }
780 
781  thissys.techlev =((((*s).w1)>>8)&3)+((thissys.economy)^7);
782  thissys.techlev +=((thissys.govtype)>>1);
783  if (((thissys.govtype)&1)==1) thissys.techlev+=1;
784  /* C simulation of 6502's LSR then ADC */
785 
786  thissys.population = 4*(thissys.techlev) + (thissys.economy);
787  thissys.population +=(thissys.govtype) + 1;
788 
789  thissys.productivity = (((thissys.economy)^7)+3)*((thissys.govtype)+4);
790  thissys.productivity *= (thissys.population)*8;
791 
792  thissys.radius = 256*(((((*s).w2)>>8)&15)+11) + thissys.x;
793 
794  thissys.goatsoupseed.a = (*s).w1 & 0xFF;;
795  thissys.goatsoupseed.b = (*s).w1 >>8;
796  thissys.goatsoupseed.c = (*s).w2 & 0xFF;
797  thissys.goatsoupseed.d = (*s).w2 >> 8;
798 
799  pair1=2*((((*s).w2)>>8)&31);tweakseed(s);
800  pair2=2*((((*s).w2)>>8)&31);tweakseed(s);
801  pair3=2*((((*s).w2)>>8)&31);tweakseed(s);
802  pair4=2*((((*s).w2)>>8)&31); tweakseed(s);
803  /* Always four iterations of random number */
804 
805  (thissys.name)[0]=pairs[pair1];
806  (thissys.name)[1]=pairs[pair1+1];/*jm we changed the planet */
807  (thissys.name)[2]=pairs[pair2]; /* names to lowercase*/
808  (thissys.name)[3]=pairs[pair2+1];
809  (thissys.name)[4]=pairs[pair3];
810  (thissys.name)[5]=pairs[pair3+1];
811 
812  if(longnameflag) /* bit 6 of ORIGINAL w0 flags a four-pair name */
813  {
814  (thissys.name)[6]=pairs[pair4];
815  (thissys.name)[7]=pairs[pair4+1];
816  (thissys.name)[8]=0;
817  }
818  else (thissys.name)[6]=0;
819  stripout(thissys.name,'.');
820 
821  if (thissys.name[0] != 0)
822  thissys.name[0] = toupper(thissys.name[0]);
823 
824  return thissys;
825 }
826 
827 
831 /* Functions for galactic hyperspace */
832 
833 uint16 TextEliteGame::rotatel(uint16 x) /* rotate 8 bit number leftwards */
834 /* (tried to use chars but too much effort persuading this braindead
835 language to do bit operations on bytes!) */
836 { uint16 temp = x&128;
837 return (2*(x&127))+(temp>>7);
838 }
839 
840 uint16 TextEliteGame::twist(uint16 x)
841 { return (uint16)((256*rotatel(x>>8))+rotatel(x&255));
842 }
843 
844 void TextEliteGame::nextgalaxy(seedtype *s) /* Apply to base seed; once for galaxy 2*/
845 { (*s).w0 = twist((*s).w0);/* twice for galaxy 3, etc. */
846  (*s).w1 = twist((*s).w1);/* Eighth application gives galaxy 1 again */
847  (*s).w2 = twist((*s).w2);
848 }
849 
850 /* Original game generated from scratch each time info needed */
851 void TextEliteGame::buildgalaxy(uint galaxynum)
852 { uint syscount,galcount;
853  seed.w0=base0; seed.w1=base1; seed.w2=base2; /* Initialise seed for galaxy 1 */
854  for(galcount=1;galcount<galaxynum;++galcount) nextgalaxy(&seed);
855  /* Put galaxy data into array of structures */
856  for(syscount=0;syscount<galsize;++syscount) galaxy[syscount]=makesystem(&seed);
857 }
858 
862 uint TextEliteGame::distance(plansys a,plansys b)
863 /* Seperation between two planets (4*sqrt(X*X+Y*Y/4)) */
864 { return (uint)ftoi(4*sqrt((float)((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)/4)));
865 }
866 
867 myboolean TextEliteGame::dogalhyp(char *s) /* Jump to next galaxy */
868 /* Preserve planetnum (eg. if leave 7th planet
869 arrive at 7th planet) */
870 { (void)(&s);/* Discard s */
871 galaxynum++;
872 if(galaxynum==9) {galaxynum=1;}
873 buildgalaxy(galaxynum);
874 return true;
875 }
876 
877 /* "Goat Soup" planetary description string code - adapted from Christian Pinder's
878 reverse engineered sources. */
879 
880 /* B0 = <planet name>
881 B1 = <planet name>ian
882 B2 = <random name>
883  */
884 
885 int TextEliteGame::gen_rnd_number (void)
886 { int a,x;
887 x = (rnd_seed.a * 2) & 0xFF;
888 a = x + rnd_seed.c;
889 if (rnd_seed.a > 127) a++;
890 rnd_seed.a = a & 0xFF;
891 rnd_seed.c = x;
892 
893 a = a / 256; /* a = any carry left from above */
894 x = rnd_seed.b;
895 a = (a + x + rnd_seed.d) & 0xFF;
896 rnd_seed.b = a;
897 rnd_seed.d = x;
898 return a;
899 }
900 
901 std::string TextEliteGame::goat_soup(const char *source,plansys * psy)
902 {
903  static struct desc_choice desc_list[] =
904  {
905  /* 81 */ {"fabled", "notable", "well known", "famous", "noted"},
906  /* 82 */ {"very", "mildly", "most", "reasonably", ""},
907  /* 83 */ {"ancient", "\x95", "great", "vast", "pink"},
908  /* 84 */ {"\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"},
909  /* 85 */ {"shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"},
910  /* 86 */ {"food blenders", "tourists", "poetry", "discos", "\x8E"},
911  /* 87 */ {"talking tree", "crab", "bat", "lobst", "\xB2"},
912  /* 88 */ {"beset", "plagued", "ravaged", "cursed", "scourged"},
913  /* 89 */ {"\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"},
914  /* 8A */ {"its \x83 \x84", "the \xB1 \x98 \x99","its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"},
915  /* 8B */ {"juice", "brandy", "water", "brew", "gargle blasters"},
916  /* 8C */ {"\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"},
917  /* 8D */ {"fabulous", "exotic", "hoopy", "unusual", "exciting"},
918  /* 8E */ {"cuisine", "night life", "casinos", "sit coms", " \xA1 "},
919  /* 8F */ {"\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"},
920  /* 90 */ {"n unremarkable", " boring", " dull", " tedious", " revolting"},
921  /* 91 */ {"planet", "world", "place", "little planet", "dump"},
922  /* 92 */ {"wasp", "moth", "grub", "ant", "\xB2"},
923  /* 93 */ {"poet", "arts graduate", "yak", "snail", "slug"},
924  /* 94 */ {"tropical", "dense", "rain", "impenetrable", "exuberant"},
925  /* 95 */ {"funny", "wierd", "unusual", "strange", "peculiar"},
926  /* 96 */ {"frequent", "occasional", "unpredictable", "dreadful", "deadly"},
927  /* 97 */ {"\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89","a\x90 \x91"},
928  /* 98 */ {"\x9B", "mountain", "edible", "tree", "spotted"},
929  /* 99 */ {"\x9F", "\xA0", "\x87oid", "\x93", "\x92"},
930  /* 9A */ {"ancient", "exceptional", "eccentric", "ingrained", "\x95"},
931  /* 9B */ {"killer", "deadly", "evil", "lethal", "vicious"},
932  /* 9C */ {"parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"},
933  /* 9D */ {"plant", "tulip", "banana", "corn", "\xB2weed"},
934  /* 9E */ {"\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"},
935  /* 9F */ {"shrew", "beast", "bison", "snake", "wolf"},
936  /* A0 */ {"leopard", "cat", "monkey", "goat", "fish"},
937  /* A1 */ {"\x8C \x8B", "\xB1 \x9F \xA2","its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"},
938  /* A2 */ {"meat", "cutlet", "steak", "burgers", "soup"},
939  /* A3 */ {"ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"},
940  /* A4 */ {"hockey", "cricket", "karate", "polo", "tennis"}
941  };
942  char pairs0[]="ABOUSEITILETSTONLONUTHNO";
943 
944  std::string result = "";
946  /* must continue into .. */
947  for(;;)
948  { uint8 c=*(source++);
949  if(c=='\0') break;
950  if(c<0x80) {
951  result += c;
952  }
953  else
954  { if (c <=0xA4)
955  { int rnd = gen_rnd_number();
956  result += goat_soup(desc_list[c-0x81].option[(rnd >= 0x33)+(rnd >= 0x66)+(rnd >= 0x99)+(rnd >= 0xCC)],psy);
957  }
958  else switch(c)
959  { case 0xB0: /* planet name */
960  { int i=1;
961  result += psy->name[0];
962  while(psy->name[i]!='\0')
963  {
964  result += tolower(psy->name[i++]);
965  }
966  } break;
967  case 0xB1: /* <planet name>ian */
968  { int i=1;
969  result += psy->name[0];
970  while(psy->name[i]!='\0')
971  { if((psy->name[i+1]!='\0') || ((psy->name[i]!='E') && (psy->name[i]!='I')))
972  {
973  result += tolower(psy->name[i]);
974  }
975  i++;
976  }
977  result += "ian";
978  } break;
979  case 0xB2: /* random name */
980  { int i;
981  int len = gen_rnd_number() & 3;
982  for(i=0;i<=len;i++)
983  { int x = gen_rnd_number() & 0x3e;
984  if(pairs0[x]!='.')
985  {
986  result += pairs0[x];
987  }
988  if(i && (pairs0[x+1]!='.'))
989  {
990  result += pairs0[x+1];
991  }
992  }
993  } break;
994  default: return "<bad char in data [%X]>";
995  } /* endswitch */
996  } /* endelse */
997  } /* endwhile */
998  return result;
999 } /* endfunc */
1000