dw.c 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. dw.c
  5. Abstract:
  6. This module implements a utility to pass idle time.
  7. Author:
  8. Evan Green 23-Jul-2015
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <assert.h>
  17. #include <ctype.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <getopt.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <time.h>
  24. #include <unistd.h>
  25. #include "swlib.h"
  26. //
  27. // --------------------------------------------------------------------- Macros
  28. //
  29. #define DwRedrawCash(_Context) \
  30. DwDrawStandardStat(DwString(DwsCash), 5, (_Context)->Cash, TRUE, FALSE)
  31. #define DwRedrawWeapons(_Context) \
  32. DwDrawStandardStat(DwString(DwsGuns), \
  33. 7, \
  34. (_Context)->WeaponCount, \
  35. FALSE, \
  36. FALSE)
  37. #define DwRedrawHealth(_Context) \
  38. DwDrawStandardStat(DwString(DwsHealth), \
  39. 9, \
  40. (_Context)->Health, \
  41. FALSE, \
  42. (_Context)->Health < 50)
  43. #define DwRedrawBank(_Context) \
  44. DwDrawStandardStat(DwString(DwsBank), 11, (_Context)->Bank, TRUE, FALSE)
  45. #define DwRedrawDebt(_Context) \
  46. DwDrawStandardStat(DwString(DwsDebt), 13, (_Context)->Debt, TRUE, TRUE)
  47. #define DW_HEALTH_COLOR(_Context) \
  48. (((_Context)->Health < 50) ? ConsoleColorDarkRed : ConsoleColorBlack)
  49. #define DwRedrawHighlightedHealth(_Context) \
  50. DwDrawStat(DwString(DwsHealth), \
  51. 9, \
  52. (_Context)->Health, \
  53. FALSE, \
  54. DW_HEALTH_COLOR(_Context), \
  55. ConsoleColorGray)
  56. #define DwDrawStandardStat(_Name, _Row, _Value, _Money, _Bad) \
  57. DwDrawStat((_Name), \
  58. (_Row), \
  59. (_Value), \
  60. (_Money), \
  61. (_Bad) ? ConsoleColorDarkRed : ConsoleColorGray, \
  62. (_Bad) ? ConsoleColorGray : ConsoleColorDarkBlue)
  63. #define DwClearLowerRegion() \
  64. SwClearRegion(ConsoleColorGray, ConsoleColorBlack, 0, 18, 80, 7)
  65. #define DwGoodName(_Index) DwString(DwsGoodsNames + (_Index))
  66. #define DwLocationName(_Index) DwString(DwsLocations + (_Index))
  67. #define DwWeaponName(_Index) DwString(DwsWeapons + (_Index))
  68. #define DwString(_Index) DwStrings[Context->Language][_Index]
  69. //
  70. // ---------------------------------------------------------------- Definitions
  71. //
  72. #define DW_VERSION_MAJOR 1
  73. #define DW_VERSION_MINOR 0
  74. #define DW_LANGUAGE_COUNT 2
  75. #define DW_INITIAL_CASH 2000
  76. #define DW_INITIAL_WEAPON_COUNT 0
  77. #define DW_INITIAL_WEAPON_DAMAGE 0
  78. #define DW_INITIAL_HEALTH 100
  79. #define DW_INITIAL_BANK 0
  80. #define DW_INITIAL_DEBT 5500
  81. #define DW_INITIAL_SPACE 100
  82. #define DW_INITIAL_LOCATION 0
  83. #define DW_INITIAL_DAY 1
  84. #define DW_GOOD_COUNT 12
  85. #define DW_LOCATION_COUNT 6
  86. #define DW_WEAPON_COUNT 4
  87. #define DW_GAME_TIME 31
  88. #define DW_SUBWAY_SAYINGS_COUNT 31
  89. #define DW_SONG_COUNT 18
  90. #define DW_PASSIVE_ACTIVITY_COUNT 5
  91. #define DW_FINANCIAL_DISTRICT 0
  92. #define DW_MORE_SPACE 10
  93. #define DW_MIN_SPACE_PRICE 200
  94. #define DW_MAX_SPACE_PRICE 300
  95. #define DW_FLASH_FAST_MICROSECONDS 150000
  96. #define DW_FLASH_SLOW_MICROSECONDS 200000
  97. //
  98. // Define the type of goods that brownies are made of.
  99. //
  100. #define DW_BROWNIE_GOOD1 2
  101. #define DW_BROWNIE_GOOD2 11
  102. //
  103. // Define the factors by which extreme prices fluctuate.
  104. //
  105. #define DW_SURGE_FACTOR 4
  106. #define DW_SALE_FACTOR 4
  107. //
  108. // Define the interest rates. Loans are brutal.
  109. //
  110. #define DW_LOAN_INTEREST_RATE 10
  111. #define DW_BANK_INTEREST_RATE 5
  112. //
  113. // Define high score information.
  114. //
  115. #define DW_HIGH_SCORE_MAGIC 0x65706F44
  116. #define DW_HIGH_SCORE_NAME_SIZE 22
  117. #define DW_HIGH_SCORE_VALID 0x00000001
  118. #define DW_HIGH_SCORE_ALIVE 0x00000002
  119. #define DW_HIGH_SCORE_YOU 0x80000000
  120. #define DW_HIGH_SCORE_COUNT 18
  121. //
  122. // ------------------------------------------------------ Data Type Definitions
  123. //
  124. /*++
  125. Structure Description:
  126. This structure stores the context for the dw application.
  127. Members:
  128. Backspace - Stores the backspace character.
  129. Cash - Stores the amount of cash the player has.
  130. WeaponCount - Stores the number of weapons the player has.
  131. WeaponDamage - Stores the sum of the damage the player's weapons can do.
  132. Health - Stores the amount of health the player has.
  133. Bank - Stores the amount the player has in the bank.
  134. Debt - Stores the amount of debt the player is carrying.
  135. Space - Stores the empty space the player has for more goods.
  136. Day - Stores the current (game) day of the month.
  137. Location - Stores the player's location.
  138. Inventory - Stores the player's inventory of goods, indexed by good.
  139. Market - Stores today's market prices.
  140. ExitRequested - Stores a boolean indicating that an exit was requested.
  141. Language - Stores the language index in use.
  142. --*/
  143. typedef struct _DW_CONTEXT {
  144. char Backspace;
  145. INT Cash;
  146. INT WeaponCount;
  147. INT WeaponDamage;
  148. INT Health;
  149. INT Bank;
  150. INT Debt;
  151. INT Space;
  152. INT Day;
  153. INT Location;
  154. INT Inventory[DW_GOOD_COUNT];
  155. INT Market[DW_GOOD_COUNT];
  156. BOOL ExitRequested;
  157. INT Language;
  158. } DW_CONTEXT, *PDW_CONTEXT;
  159. /*++
  160. Structure Description:
  161. This structure stores the data for a good.
  162. Members:
  163. MinPrice - Stores the minimum price of the good.
  164. MaxPrice - Stores the maximum price of the good.
  165. Sales - Stores a boolean indicating if the good goes on sale.
  166. Surges - Stores a boolean indicating if the good undergoes shortages.
  167. --*/
  168. typedef struct _DW_GOOD {
  169. INT MinPrice;
  170. INT MaxPrice;
  171. BOOL Sales;
  172. BOOL Surges;
  173. } DW_GOOD, *PDW_GOOD;
  174. /*++
  175. Structure Description:
  176. This structure stores the data for a location.
  177. Members:
  178. PolicePresence - Stores the likelihood that the player will have to deal
  179. with the popo when going here.
  180. MinGoods - Stores the minimum number of goods at this market.
  181. MaxGoods - Stores the maximum number of goods at this market.
  182. --*/
  183. typedef struct _DW_LOCATION {
  184. INT PolicePresence;
  185. INT MinGoods;
  186. INT MaxGoods;
  187. } DW_LOCATION, *PDW_LOCATION;
  188. /*++
  189. Structure Description:
  190. This structure stores the data for a weapon.
  191. Members:
  192. Price - Stores the price of the weapon.
  193. Space - Stores the amount of space the weapon takes up.
  194. Damage - Stores the amount of damage the weapon does.
  195. --*/
  196. typedef struct _DW_WEAPON {
  197. INT Price;
  198. INT Space;
  199. INT Damage;
  200. } DW_WEAPON, *PDW_WEAPON;
  201. /*++
  202. Structure Description:
  203. This structure stores a single high score entry.
  204. Members:
  205. Flags - Stores a bitfield of flags about this entry.
  206. Year - Stores the year of the high score.
  207. Month - Stores the month of the high score, 1-12.
  208. Day - Stores the day of the high score, 1-31.
  209. Amount - Stores the amount they had.
  210. Name - Stores the high scorer's name.
  211. --*/
  212. typedef struct _DW_HIGH_SCORE_ENTRY {
  213. ULONG Flags;
  214. USHORT Year;
  215. UCHAR Month;
  216. UCHAR Day;
  217. LONG Amount;
  218. CHAR Name[DW_HIGH_SCORE_NAME_SIZE];
  219. } DW_HIGH_SCORE_ENTRY, *PDW_HIGH_SCORE_ENTRY;
  220. /*++
  221. Structure Description:
  222. This structure stores the complete high score data.
  223. Members:
  224. Magic - Stores the magic value DW_HIGH_SCORE_MAGIC.
  225. Checksum - Stores the checksum of the table, assuming the checksum field
  226. itself is zero.
  227. Entries - Stores the array of high score entries.
  228. --*/
  229. typedef struct _DW_HIGH_SCORES {
  230. ULONG Magic;
  231. ULONG Checksum;
  232. DW_HIGH_SCORE_ENTRY Entries[DW_HIGH_SCORE_COUNT];
  233. } DW_HIGH_SCORES, *PDW_HIGH_SCORES;
  234. typedef enum _DW_STRING_VALUE {
  235. DwsIntroTitle,
  236. DwsIntroText,
  237. DwsHorizontalLine,
  238. DwsTwoColumnLine,
  239. DwsColumnTitles,
  240. DwsSubway,
  241. DwsMarketGreeting,
  242. DwsSurgeFormat1,
  243. DwsSurgeFormat2,
  244. DwsPressSpace,
  245. DwsBuyOrJet,
  246. DwsBuySellJet,
  247. DwsWhatToBuy,
  248. DwsWhatToSell,
  249. DwsHowManyToBuy,
  250. DwsHowManyToSell,
  251. DwsWhereTo,
  252. DwsSubwayLadyFormat,
  253. DwsSubwayQualifier,
  254. DwsHearSongFormat,
  255. DwsProductOfferFormat,
  256. DwsProductMoreSpace,
  257. DwsMugged,
  258. DwsReceiveGiftFormat,
  259. DwsSendGiftFormat,
  260. DwsLostGoodsFormat,
  261. DwsFoundGoodsFormat,
  262. DwsSharedGoodsFormat,
  263. DwsSirenSong,
  264. DwsSirenPrompt,
  265. DwsSirenResult,
  266. DwsPassiveActivityFormat,
  267. DwsFightThreatFormat,
  268. DwsRunOrFight,
  269. DwsRunOption,
  270. DwsFight,
  271. DwsRun,
  272. DwsPlayerFire,
  273. DwsPlayerMissed,
  274. DwsPlayerHit,
  275. DwsPlayerUnderFire,
  276. DwsFled,
  277. DwsFailedToFlee,
  278. DwsNotFleeing,
  279. DwsTheyMissed,
  280. DwsTheyHit,
  281. DwsKilled,
  282. DwsFightVictoryFormat,
  283. DwsDoctorOffer,
  284. DwsVisitLoanShark,
  285. DwsYes,
  286. DwsLoanRepaymentAmount,
  287. DwsVisitBank,
  288. DwsDepositOrWithdraw,
  289. DwsHowMuchMoney,
  290. DwsHighScoresTitle,
  291. DwsHighScoreDead,
  292. DwsHighScoreFormat,
  293. DwsPlayAgain,
  294. DwsMadeHighScores,
  295. DwsNamePrompt,
  296. DwsAnonymous,
  297. DwsYou,
  298. DwsGoodsNames,
  299. DwsGoodsSales = DwsGoodsNames + DW_GOOD_COUNT,
  300. DwsLocations = DwsGoodsSales + DW_GOOD_COUNT,
  301. DwsWeapons = DwsLocations + DW_LOCATION_COUNT,
  302. DwsSubwaySayings = DwsWeapons + DW_WEAPON_COUNT,
  303. DwsSongs = DwsSubwaySayings + DW_SUBWAY_SAYINGS_COUNT,
  304. DwsPassiveActivities = DwsSongs + DW_SONG_COUNT,
  305. DwsCash = DwsPassiveActivities + DW_PASSIVE_ACTIVITY_COUNT,
  306. DwsGuns,
  307. DwsHealth,
  308. DwsBank,
  309. DwsDebt,
  310. DwsAccess,
  311. DwsNoAccess,
  312. DwsStringCount
  313. } DW_STRING_VALUE, *PDW_STRING_VALUE;
  314. //
  315. // ----------------------------------------------- Internal Function Prototypes
  316. //
  317. INT
  318. DwDecodeStrings (
  319. PDW_CONTEXT Context
  320. );
  321. VOID
  322. DwDrawIntro (
  323. PDW_CONTEXT Context
  324. );
  325. VOID
  326. DwDrawGameBoard (
  327. PDW_CONTEXT Context
  328. );
  329. VOID
  330. DwResetGame (
  331. PDW_CONTEXT Context
  332. );
  333. VOID
  334. DwPlay (
  335. PDW_CONTEXT Context
  336. );
  337. VOID
  338. DwDoDailyEvents (
  339. PDW_CONTEXT Context
  340. );
  341. VOID
  342. DwReceiveOffer (
  343. PDW_CONTEXT Context
  344. );
  345. VOID
  346. DwPerformActOfGod (
  347. PDW_CONTEXT Context
  348. );
  349. VOID
  350. DwEncounterPolice (
  351. PDW_CONTEXT Context
  352. );
  353. VOID
  354. DwVisitFinancialDistrict (
  355. PDW_CONTEXT Context
  356. );
  357. VOID
  358. DwGenerateMarket (
  359. PDW_CONTEXT Context
  360. );
  361. VOID
  362. DwParticipateInMarket (
  363. PDW_CONTEXT Context
  364. );
  365. VOID
  366. DwDrawMarket (
  367. PDW_CONTEXT Context
  368. );
  369. VOID
  370. DwRedrawInventory (
  371. PDW_CONTEXT Context
  372. );
  373. VOID
  374. DwPresentNotification (
  375. PDW_CONTEXT Context,
  376. PSTR Notification
  377. );
  378. VOID
  379. DwDrawBottomPrompt (
  380. PSTR Prompt
  381. );
  382. VOID
  383. DwDrawStat (
  384. PSTR Name,
  385. INT Row,
  386. INT Value,
  387. BOOL Money,
  388. CONSOLE_COLOR Foreground,
  389. CONSOLE_COLOR Background
  390. );
  391. VOID
  392. DwDrawLocation (
  393. PSTR Location
  394. );
  395. VOID
  396. DwRedrawSpace (
  397. PDW_CONTEXT Context
  398. );
  399. VOID
  400. DwFlashText (
  401. PDW_CONTEXT Context,
  402. PSTR String,
  403. INT XPosition,
  404. INT YPosition,
  405. CONSOLE_COLOR Background,
  406. CONSOLE_COLOR Foreground,
  407. CONSOLE_COLOR FlashForeground,
  408. BOOL Fast
  409. );
  410. INT
  411. DwDisplayHighScores (
  412. PDW_CONTEXT Context
  413. );
  414. VOID
  415. DwLoadHighScores (
  416. PDW_CONTEXT Context,
  417. PDW_HIGH_SCORES Scores
  418. );
  419. INT
  420. DwHighScoresFileIo (
  421. PDW_HIGH_SCORES Scores,
  422. BOOL Load
  423. );
  424. int
  425. DwCompareHighScores (
  426. const void *LeftPointer,
  427. const void *RightPointer
  428. );
  429. VOID
  430. DwFormatMoney (
  431. PSTR String,
  432. UINTN StringSize,
  433. INT Value
  434. );
  435. INT
  436. DwReadCharacterSet (
  437. PDW_CONTEXT Context,
  438. PSTR Set
  439. );
  440. INT
  441. DwReadQuantity (
  442. PDW_CONTEXT Context
  443. );
  444. INT
  445. DwReadString (
  446. PDW_CONTEXT Context,
  447. PSTR String,
  448. INT StringSize
  449. );
  450. INT
  451. DwReadYesNoAnswer (
  452. PDW_CONTEXT Context,
  453. PSTR Exposition,
  454. PSTR Prompt
  455. );
  456. INT
  457. DwReadCharacter (
  458. PDW_CONTEXT Context
  459. );
  460. INT
  461. DwRandom (
  462. INT Minimum,
  463. INT Maximum
  464. );
  465. ULONG
  466. DwChecksum (
  467. PVOID Buffer,
  468. ULONG Size
  469. );
  470. //
  471. // -------------------------------------------------------------------- Globals
  472. //
  473. //
  474. // Store the menu of available goods and how they behave. Don't look!
  475. //
  476. DW_GOOD DwGoods[DW_GOOD_COUNT] = {
  477. {1000, 4000, TRUE, FALSE},
  478. {15000, 29000, FALSE, TRUE},
  479. {480, 1280, TRUE, FALSE},
  480. {5500, 13000, FALSE, TRUE},
  481. {11, 60, TRUE, FALSE},
  482. {1500, 4400, FALSE, FALSE},
  483. {540, 1250, FALSE, TRUE},
  484. {1000, 2500, FALSE, FALSE},
  485. {220, 700, FALSE, FALSE},
  486. {630, 1300, FALSE, FALSE},
  487. {90, 250, FALSE, TRUE},
  488. {315, 890, TRUE, FALSE}
  489. };
  490. DW_LOCATION DwLocations[DW_LOCATION_COUNT] = {
  491. {10, (DW_GOOD_COUNT / 2) + 2, DW_GOOD_COUNT},
  492. {5, (DW_GOOD_COUNT / 2) + 3, DW_GOOD_COUNT},
  493. {15, (DW_GOOD_COUNT / 2) + 1, DW_GOOD_COUNT},
  494. {90, (DW_GOOD_COUNT / 2), DW_GOOD_COUNT - 2},
  495. {20, (DW_GOOD_COUNT / 2) + 1, DW_GOOD_COUNT},
  496. {70, (DW_GOOD_COUNT / 2), DW_GOOD_COUNT - 1},
  497. };
  498. DW_WEAPON DwWeapons[DW_WEAPON_COUNT] = {
  499. {300, 4, 5},
  500. {350, 4, 9},
  501. {290, 4, 4},
  502. {310, 4, 7},
  503. };
  504. //
  505. // Define all the strings used in the game, localized. Do a very minimal effort
  506. // to hide from prying "strings".
  507. //
  508. PSTR DwStrings[DW_LANGUAGE_COUNT][DwsStringCount] = {
  509. {
  510. "E!P!Q!F!!!X!B!S!T",
  511. "!Cbtfe!po!Kpio!F/!Efmm(t!pme!Esvh!Xbst!hbnf-!Epqf!Xbst!jt!b!" \
  512. "tjnvmbujpo!pg!bo\n!jnbhjobsz!esvh!nbslfu/!!Epqf!xbst!jt!bo!" \
  513. "Bmm.Bnfsjdbo!hbnf!xijdi!gfbuvsft\n!cvzjoh-!tfmmjoh-!boe!uszjoh!" \
  514. "up!hfu!qbtu!uif!dpqt\"\n\n!Uif!gjstu!uijoh!zpv!offe!up!ep!jt!" \
  515. "qbz!pgg!zpvs!efcu!up!uif!Mpbo!Tibsl/!!Bgufs\n!uibu-!zpvs!hpbm!" \
  516. "jt!up!nblf!bt!nvdi!npofz!bt!qpttjcmf!)boe!tubz!bmjwf*\"!!Zpv\n!" \
  517. "ibwf!pof!npoui!pg!hbnf!ujnf!up!nblf!zpvs!gpsuvof/\n\n!Epqf!Xbst!" \
  518. "ibt!cffo!cspvhiu!up!zpv!dpvsuftz!pg!uif!Ibqqz!Ibdlfs!Gpvoebujpo/",
  519. ",>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" \
  520. ">>>>>>>>>,",
  521. "}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" \
  522. "!!!!!!!!!}",
  523. "}!!!!!!!!!!!!Tubut!!!!!!!!!!!!}!Dfousbm!Qbsl!!}!!!!!!!!!!Usfodidpbu!" \
  524. "!!!!!!!!!}",
  525. "!T!V!C!X!B!Z",
  526. "Ifz!evef-!uif!qsjdft!pg!esvht!ifsf!bsf;",
  527. "Dpqt!nbef!b!cjh!&t!cvtu\"!!Qsjdft!bsf!pvusbhfpvt\"",
  528. "Beejdut!bsf!cvzjoh!&t!bu!pvusbhfpvt!qsjdft\"",
  529. "Qsftt!TQBDF!up!dpoujovf",
  530. "Xjmm!zpv!C?vz-!ps!K?fu@!",
  531. "Xjmm!zpv!C?vz-!T?fmm-!ps!K?fu@!",
  532. "Xibu!ep!zpv!xjti!up!cvz@!",
  533. "Xibu!ep!zpv!xjui!up!tfmm@!",
  534. "Zpv!dbo!bggpse!&e/!!Ipx!nboz!ep!zpv!cvz@!",
  535. "Zpv!ibwf!&e/!!Ipx!nboz!ep!zpv!tfmm@!",
  536. "Xifsf!up-!evef@",
  537. "Uif!mbez!ofyu!up!zpv!po!uif!tvcxbz!tbje-\n!!!!!#&t#///\n!&t",
  538. ")bu!mfbtu-!zpv!.uijol.!uibu(t!xibu!tif!tbje*/",
  539. "Zpv!ifbs!tpnfpof!qmbzjoh!&t",
  540. "Xpvme!zpv!mjlf!up!cvz!b!&t!gps!%&e@!",
  541. "cjhhfs!usfodidpbu",
  542. "Zpv!xfsf!nvhhfe!jo!uif!tvcxbz\"",
  543. "Zpv!nffu!b!gsjfoe\"!!If!mbzt!tpnf!&t!po!zpv/",
  544. "Zpv!nffu!b!gsjfoe\"!!Zpv!hjwf!ifs!tpnf!&t/",
  545. "Qpmjdf!epht!dibtfe!zpv!gps!&e!cmpdlt\"\n!Zpv!espqqfe!tpnf!esvht\"!!" \
  546. "Uibu(t!b!esbh-!nbo/",
  547. "Zpv!gjoe!&e!vojut!pg!&t!po!b!efbe!evef!jo!uif!tvcxbz/",
  548. "Zpvs!nbnb!nbef!cspxojft!xjui!tpnf!pg!zpvs!&t\"!!Uifz!xfsf!hsfbu\"",
  549. "Uifsf!jt!tpnf!xffe!uibu!tnfmmt!mjlf!qbsbrvbu!ifsf\"!!Ju!mpplt!hppe\"",
  550. "Xjmm!zpv!tnplf!ju@!",
  551. "Zpv!ibmmvdjobufe!gps!uisff!ebzt!po!uif!xjmeftu!usjq!zpv!fwfs!" \
  552. "jnbhjofe\"\n!Uifo!zpv!ejfe!cfdbvtf!zpvs!csbjo!ejtjoufhsbufe\"",
  553. "Zpv!tupqqfe!up!&t/",
  554. "Pggjdfs!Ibsebtt!boe!&e!pg!ijt!efqvujft!bsf!dibtjoh!zpv\"",
  555. "Xjmm!zpv!S?vo!ps!G?jhiu@!",
  556. "Xjmm!zpv!svo@!",
  557. "Gjhiu",
  558. "Svo",
  559. "Zpv(sf!gjsjoh!po!uifn\"!!",
  560. "Zpv!njttfe\"",
  561. "Zpv!ljmmfe!pof\"",
  562. "Uifz!bsf!gjsjoh!po!zpv-!nbo\"!!",
  563. "Zpv!mptu!uifn!jo!uif!bmmfzt/",
  564. "Zpv!dbo(u!mptf!uifn\"",
  565. "Zpv!tuboe!uifsf!mjlf!bo!jejpu/",
  566. "Uifz!njttfe\"",
  567. "Zpv(wf!cffo!iju\"",
  568. "Uifz!xbtufe!zpv!nbo\"!!Xibu!b!esbh\"",
  569. "Zpv!ljmmfe!bmm!pg!uifn\"\n!Zpv!gjoe!%&e!po!Pggjdfs!Ibsebtt(!" \
  570. "dbsdbtt\"\n!",
  571. "Xjmm!zpv!qbz!%&e!up!ibwf!b!epdups!tfx!zpv!vq@!",
  572. "Xpvme!zpv!mjlf!up!wjtju!uif!Mpbo!Tibsl@!",
  573. "Zft",
  574. "Ipx!nvdi!ep!zpv!hjwf!ijn@!",
  575. "Xpvme!zpv!mjlf!up!wjtju!uif!Cbol@!",
  576. "Ep!zpv!xbou!up!E?fqptju!ps!X?juiesbx@!",
  577. "Ipx!nvdi!npofz@!",
  578. "I!J!H!I!!!T!D!P!S!F!T",
  579. ")S/J/Q/*",
  580. "&23t!!!!!!!!!!&13e.&13e.&15e!!!!!!!!!!&.31t&t",
  581. "Qmbz!bhbjo@",
  582. "Dpohsbuvmbujpot\"!!Zpv!nbef!uif!Ijhi!Tdpsft!mjtu\"\n!",
  583. "Qmfbtf!foufs!zpvs!obnf;!",
  584. "Nztufsz!Efbmfs",
  585. "+++!ZPV!+++",
  586. "Bdje",
  587. "Dpdbjof",
  588. "Ibtijti",
  589. "Ifspjo",
  590. "Mveft",
  591. "NEB",
  592. "Pqjvn",
  593. "QDQ",
  594. "Qfzpuf",
  595. "Tisppnt",
  596. "Tqffe",
  597. "Xffe",
  598. "Uif!nbslfu!ibt!cffo!gmppefe!xjui!difbq!ipnf.nbef!bdje\"",
  599. NULL,
  600. "Uif!Nbssblfti!Fyqsftt!ibt!bssjwfe\"",
  601. NULL,
  602. "Sjwbm!esvh!efbmfst!sbjefe!b!qibsnbdz!boe!bsf!tfmmjoh!difbq!mveft\"",
  603. NULL,
  604. NULL,
  605. NULL,
  606. NULL,
  607. NULL,
  608. NULL,
  609. "Dpmvncjbo!gsfjhiufs!evtufe!uif!Dpbtu!Hvbse\"\n!Xffe!qsjdft!ibwf!" \
  610. "cpuupnfe!pvu\"",
  611. "Cspoy",
  612. "Hifuup",
  613. "Dfousbm!Qbsl",
  614. "Nboibuubo",
  615. "Dpofz!Jtmboe",
  616. "Cspplmzo",
  617. "Cbsfuub",
  618. "/49!Tqfdjbm",
  619. "Svhfs",
  620. "Tbuvsebz!Ojhiu!Tqfdjbm",
  621. "Xpvmeo(u!ju!cf!gvooz!jg!fwfszpof!tveefomz!rvbdlfe!bu!podf@",
  622. "Uif!Qpqf!xbt!podf!Kfxjti-!zpv!lopx",
  623. "J(mm!cf!zpv!ibwf!tpnf!sfbmmz!joufsftujoh!esfbnt",
  624. "Tp!J!uijol!J(n!hpjoh!up!Bntufsebn!uijt!zfbs",
  625. "Tpo-!zpv!offe!b!zfmmpx!ibjsdvu",
  626. "J!uijol!ju(t!xpoefsgvm!xibu!uifz(sf!epjoh!xjui!jodfotf!uiftf!ebzt",
  627. "J!xbto(u!bmxbzt!b!xpnbo-!zpv!lopx",
  628. "Epft!zpvs!npuifs!lopx!zpv(sf!b!epqf!efbmfs@",
  629. "Bsf!zpv!ijhi!po!tpnfuijoh@",
  630. "Pi-!zpv!nvtu!cf!gspn!Dbmjgpsojb",
  631. "J!vtfe!up!cf!b!ijqqjf-!nztfmg",
  632. "Uifsf(t!opuijoh!mjlf!ibwjoh!mput!pg!npofz",
  633. "Zpv!mppl!mjlf!bo!bbsewbsl\"",
  634. "J!epo(u!cfmjfwf!jo!Spobme!Sfbhbo",
  635. "Dpvsbhf\"!!Cvti!jt!b!oppemf\"",
  636. "Ibwfo(u!J!tffo!zpv!po!UW@",
  637. "J!uijol!ifnpssipje!dpnnfsdjbmt!bsf!sfbmmz!ofbu\"",
  638. "Xf(sf!xjoojoh!uif!xbs!gps!esvht\"",
  639. "B!ebz!xjuipvu!epqf!jt!mjlf!ojhiu",
  640. "Xf!pomz!vtf!31&!pg!pvs!csbjot-!tp!xiz!opu!cvso!pvu!uif!puifs!91&",
  641. "J(n!tpmjdjujoh!dpousjcvujpot!gps![pncjft!gps!Disjtu",
  642. "J(e!mjlf!up!tfmm!zpv!bo!fejcmf!qppemf",
  643. "Xjoofst!epo(u!ep!esvht///!vomftt!uifz!ep",
  644. "Ljmm!b!dpq!gps!Disjtu\"",
  645. "J!bn!uif!xbmsvt\"",
  646. "Kftvt!mpwft!zpv!npsf!uibo!zpv!xjmm!lopx",
  647. "J!gffm!bo!vobddpvoubcmf!vshf!up!ezf!nz!ibjs!cmvf",
  648. "Xbto(u!Kbof!Gpoeb!xpoefsgvm!jo!Cbscbsfmmb",
  649. "Kvtu!tbz!Op//!xfmm-!nbzcf///!pl-!xibu!uif!ifmm\"",
  650. "Xpvme!zpv!mjlf!b!kfmmz!cbcz@",
  651. "Esvht!dbo!cf!zpvs!gsjfoe\"",
  652. "#Bsf!Zpv!Fyqfsjfodfe#!cz!Kjnj!Ifoesjy",
  653. "#Diffcb!Diffcb#!cz!Upof!Mpd",
  654. "#Dpnjo(!jo!up!Mpt!Bohfmft#!cz!Bsmp!Hvuisjf",
  655. "#Dpnnfsdjbm#!cz!Tqbolz!boe!Pvs!Hboh",
  656. "#Mbuf!jo!uif!Fwfojoh#!cz!Qbvm!Tjnpo",
  657. "#Mjhiu!Vq#!cz!Tuzy",
  658. "#Nfyjdp#!cz!Kfggfstpo!Bjsqmbof",
  659. "#Pof!Uplf!Pwfs!uif!Mjof#!cz!Csfxfs!'!Tijqmfz",
  660. "#Uif!Tnplfpvu#!cz!Tifm!Tjmwfstufjo",
  661. "#Xijuf!Sbccju#!cz!Kfggfstpo!Bjsqmbof",
  662. "#Judizdpp!Qbsl#!cz!Tnbmm!Gbdft",
  663. "#Xijuf!Qvolt!po!Epqf#!cz!Uif!Uvcft",
  664. "#Mfhfoe!pg!b!Njoe#!cz!Uif!Nppez!Cmvft",
  665. "#Fjhiu!Njmft!Ijhi#!cz!Uif!Czset",
  666. "#Bdbqvmdp!Hpme#!cz!Sjefst!pg!uif!Qvsqmf!Tbhf",
  667. "#Ljdlt#!cz!Qbvm!Sfwfsf!'!uif!Sbjefst",
  668. "uif!Ojypo!ubqft",
  669. "#Mfhbmj{f!Ju#!cz!Npkp!Ojypo!'!Tlje!Spqfs",
  670. "ibwf!b!cffs",
  671. "tnplf!b!kpjou",
  672. "tnplf!b!djhbs",
  673. "tnplf!b!Ekbsvn",
  674. "tnplf!b!djhbsfuuf",
  675. "Dbti",
  676. "Hvot",
  677. "Ifbmui",
  678. "Cbol",
  679. "Efcu",
  680. "EX`QMBZ`PME`TDIPPM",
  681. "ti;!ex;!Dpnnboe!opu!gpvoe/\n",
  682. },
  683. {
  684. "D!B!O!E!Z!!!X!B!S!T",
  685. "!Cbtfe!po!Kpio!F/!Efmm(t!pme!usbejoh!hbnf-!Dboez!Xbst!jt!b!" \
  686. "tjnvmbujpo!pg!bo\n!jnbhjobsz!dboez!nbslfu/!!Dboez!Xbst!jt!bo!" \
  687. "Bmm.Bnfsjdbo!hbnf!xijdi!gfbuvsft\n!cvzjoh-!tfmmjoh-!boe!uszjoh!" \
  688. "up!hfu!qbtu!uif!wfhhjf!qpmjdf\"\n\n!Uif!gjstu!uijoh!zpv!offe!" \
  689. "up!ep!jt!qbz!pgg!zpvs!efcu!up!uif!Mpbo!Tibsl/!!Bgufs\n!uibu-!" \
  690. "zpv!hpbm!jt!up!nblf!bt!nvdi!npofz!bt!qpttjcmf!)boe!tubz!" \
  691. "bmjwf*\"!!Zpv\n!ibwf!pof!npoui!pg!hbnf!ujnf!up!nblf!zpvs!" \
  692. "gpsuvof/\n\n",
  693. ",>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" \
  694. ">>>>>>>>>>>>,",
  695. "}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}!!!!!!!!!!!!!!!!!!!!!!!!!!" \
  696. "!!!!!!!!!!!!}",
  697. "}!!!!!!!!!!!!Tubut!!!!!!!!!!!!}!Dfousbm!Qbsl!!}!!!!!!!!!!Usfodidpbu" \
  698. "!!!!!!!!!!}",
  699. "!!C!!!V!!!T!",
  700. "Ifz!evef-!uif!qsjdft!pg!dboez!ifsf!bsf;",
  701. "Uif!GEB!nbef!b!cjh!&t!cvtu\"!!Qsjdft!bsf!pvusbhfpvt\"",
  702. "Beejdut!bsf!cvzjoh!&t!bu!pvusbhfpvt!qsjdft\"",
  703. "Qsftt!TQBDF!up!dpoujovf",
  704. "Xjmm!zpv!C?vz-!ps!K?fu@!",
  705. "Xjmm!zpv!C?vz-!T?fmm-!ps!K?fu@!",
  706. "Xibu!ep!zpv!xjti!up!cvz@!",
  707. "Xibu!ep!zpv!xjui!up!tfmm@!",
  708. "Zpv!dbo!bggpse!&e/!!Ipx!nboz!ep!zpv!cvz@!",
  709. "Zpv!ibwf!&e/!!Ipx!nboz!ep!zpv!tfmm@!",
  710. "Xifsf!up-!evef@",
  711. "Uif!mbez!ofyu!up!zpv!po!uif!cvt!tbje-\n!!!!!#&t#///\n!&t",
  712. ")bu!mfbtu-!zpv!.uijol.!uibu(t!xibu!tif!tbje*/",
  713. "Zpv!ifbs!tpnfpof!qmbzjoh!&t",
  714. "Xpvme!zpv!mjlf!up!cvz!b!&t!gps!%&e@!",
  715. "cjhhfs!usfodidpbu",
  716. "Zpv!xfsf!nvhhfe!jo!uif!cvt!tubujpo\"",
  717. "Zpv!nffu!b!gsjfoe\"!!If!mbzt!tpnf!&t!po!zpv/",
  718. "Zpv!nffu!b!gsjfoe\"!!Zpv!hjwf!ifs!tpnf!&t/",
  719. "Dsb{fe!dijmesfo!dibtfe!zpv!gps!&e!cmpdlt\"\n!Zpv!espqqfe!tpnf!" \
  720. "dboez\"!!Uibu(t!b!esbh-!nbo/",
  721. "Zpv!gjoe!&e!vojut!pg!&t!bu!uif!cpuupn!pg!b!wfoejoh!nbdijof/",
  722. "Zpvs!nbnb!nbef!cspxojft!xjui!tpnf!pg!zpvs!&t\"!!Uifz!xfsf!hsfbu\"",
  723. "Uifsf!jt!tpnf!tpeb!ifsf!uibu!mpplt!mjlf!Ofx!Dplf\"!!Ju!mpplt!mfhju\"",
  724. "Xjmm!zpv!esjol!ju@!",
  725. "Zpv!ibmmvdjobufe!gps!uisff!ebzt!po!uif!xjmeftu!usjq!zpv!fwfs!" \
  726. "jnbhjofe\"\n!Uifo!zpv!ejfe!cfdbvtf!zpvs!csbjo!ejtjoufhsbufe\"",
  727. "Zpv!tupqqfe!up!&t/",
  728. "Dbqubjo!Wfhfubcmft!boe!&e!pg!ijt!dspojft!bsf!dibtjoh!zpv\"",
  729. "Xjmm!zpv!S?vo!ps!G?jhiu@!",
  730. "Xjmm!zpv!svo@!",
  731. "Gjhiu",
  732. "Svo",
  733. "Zpv(sf!gjsjoh!po!uifn\"!!",
  734. "Zpv!njttfe\"",
  735. "Zpv!hpu!pof\"",
  736. "Uifz!bsf!gjsjoh!po!zpv-!nbo\"!!",
  737. "Zpv!mptu!uifn!jo!uif!tvqfsnbslfu/",
  738. "Zpv!dbo(u!mptf!uifn\"",
  739. "Zpv!tuboe!uifsf!mjlf!bo!jejpu/",
  740. "Uifz!njttfe\"",
  741. "Zpv(wf!cffo!iju\"",
  742. "Uifz!dpowfsufe!zpv!up!b!wfhbo\"!!Xibu!b!esbh\"",
  743. "Zpv!hpu!bmm!pg!uifn\"\n!Zpv!gjoe!%&e!po!Dbqubjo!Wfhfubcmft(!" \
  744. "usvol\"\n!",
  745. "Xjmm!zpv!qbz!%&e!up!ibwf!b!epdups!gjy!zpv!vq@!",
  746. "Xpvme!zpv!mjlf!up!wjtju!uif!Mpbo!Tibsl@!",
  747. "Zft",
  748. "Ipx!nvdi!ep!zpv!hjwf!ijn@!",
  749. "Xpvme!zpv!mjlf!up!wjtju!uif!Cbol@!",
  750. "Ep!zpv!xbou!up!E?fqptju!ps!X?juiesbx@!",
  751. "Ipx!nvdi!npofz@!",
  752. "I!J!H!I!!!T!D!P!S!F!T",
  753. ")wfhbo*",
  754. "&23t!!!!!!!!!!&13e.&13e.&15e!!!!!!!!!!&.31t&t",
  755. "Qmbz!bhbjo@",
  756. "Dpohsbuvmbujpot\"!!Zpv!nbef!uif!Ijhi!Tdpsft!mjtu\"\n!",
  757. "Qmfbtf!foufs!zpvs!obnf;!",
  758. "Nztufsz!Efbmfs",
  759. "+++!ZPV!+++",
  760. "BjsIfbet",
  761. "Dbecvsz",
  762. "Ljttft",
  763. "Tljuumft",
  764. "Ujd!Ubdt",
  765. "Nbmu!Cbmmt",
  766. "Qjyjf!Tujy",
  767. "Qpq!Spdlt",
  768. "Qf{",
  769. "Cmpx!Qpqt",
  770. "Tubscvstu",
  771. "N'Nt",
  772. "Uif!nbslfu!ibt!cffo!gmppefe!xjui!difbq!ipnf.nbef!BjsIfbet\"",
  773. NULL,
  774. "Ju(t!Wbmfoujof(t!Ebz\"!Ljttft!bsf!po!tbmf\"",
  775. NULL,
  776. "Sjwbm!hspdfsz!tupsft!bsf!tfmmjoh!difbq!pgg.csboe!Ujd.Ubdt\"",
  777. NULL,
  778. NULL,
  779. NULL,
  780. NULL,
  781. NULL,
  782. NULL,
  783. "B!Ifstifz(t!usvdl!dsbtife!po!uif!ijhixbz\"\nN'Nt!bsf!fwfszxifsf-!" \
  784. "qsjdft!ibwf!cpuupnfe!pvu\"",
  785. "Njttjpo",
  786. "Dbtusp",
  787. "Qsftjejp",
  788. "Tvotfu",
  789. "Svttjbo!Ijmm",
  790. "Qpusfsp",
  791. "Tvqfs!Tpblfs",
  792. "Qfmmfu!Hvo",
  793. "Sfe!Szefs",
  794. "Dbq!Hvo",
  795. "Xpvmeo(u!ju!cf!gvooz!jg!fwfszpof!tveefomz!rvbdlfe!bu!podf@",
  796. "Uif!Qpqf!xbt!podf!Kfxjti-!zpv!lopx",
  797. "J(mm!cf!zpv!ibwf!tpnf!sfbmmz!joufsftujoh!esfbnt",
  798. "Tp!J!uijol!J(n!hpjoh!up!Qfootzmwbojb!uijt!zfbs",
  799. "Tpo-!zpv!offe!b!zfmmpx!ibjsdvu",
  800. "J!uijol!ju(t!xpoefsgvm!xibu!uifz(sf!epjoh!xjui!nbqmf!tzsvq!uiftf!ebzt",
  801. "J!xbto(u!bmxbzt!b!xpnbo-!zpv!lopx",
  802. "Epft!zpvs!npuifs!lopx!zpv!fbu!tp!nvdi!dboez@",
  803. "Zpv(sf!tvsf!cpvodz-!bsfo(u!zpv@",
  804. "Pi-!zpv!nvtu!cf!gspn!Dbmjgpsojb",
  805. "J!vtfe!up!cf!b!ijqqjf-!nztfmg",
  806. "Uifsf(t!opuijoh!mjlf!ibwjoh!mput!pg!npofz",
  807. "Zpv!mppl!mjlf!bo!bbsewbsl\"",
  808. "J!epo(u!cfmjfwf!jo!dmbttjdbm!qiztjdt",
  809. "Dpvsbhf\"!!Tjsj!jt!b!oppemf\"",
  810. "Ibwfo(u!J!tffo!zpv!po!UW@",
  811. "J!uijol!Hfpshf!Gpsfnbo!hsjmmt!bsf!sfbmmz!ofbu\"",
  812. "Xf(sf!xjoojoh!uif!xbs!gps!dpuupo!dboez\"",
  813. "B!ebz!xjuipvu!dboez!jt!mjlf!ojhiu",
  814. "J!dbo(u!cfmjfwf!J(n!opu!cvuufs\"",
  815. "J(n!tpmjdjujoh!dpousjcvujpot!gps![pncjft!gps!Cveeib",
  816. "J(e!mjlf!up!tfmm!zpv!fejcmf!upjmfu!qbqfs",
  817. "Ofwfs!Yfspy!cvccmf!hvn",
  818. "Nz!Lju.Lbu!cbst!dpnf!jo!gjwft\"",
  819. "J!bn!uif!xbmsvt\"",
  820. "Kftvt!mpwft!zpv!npsf!uibo!zpv!xjmm!lopx",
  821. "J!gffm!bo!vobddpvoubcmf!vshf!up!ezf!nz!ibjs!cmvf",
  822. "Xbto(u!Kbof!Gpoeb!xpoefsgvm!jo!Cbscbsfmmb",
  823. "Opuijoh!siznft!xjui!psbohf!///!fydfqu!cpsbohf\"",
  824. "Xpvme!zpv!mjlf!b!kfmmz!cbcz@",
  825. "Dboez!dbo!cf!zpvs!gsjfoe\"",
  826. "#J!Xbou!Dboez#!cz!Cpx!Xpx!Xpx",
  827. "#Npuifs!Qpqdpso#!cz!Kbnft!Cspxo",
  828. "#J!Dbo(u!Ifmq!Nztfmg#!cz!Uif!Gpvs!Upqt",
  829. "#Tvhbs-!Tvhbs#!cz!Uif!Bsdijft",
  830. "#Dvqt!boe!Dblft#!cz!TqjobmUbq",
  831. "#D!jt!gps!Dppljf#!cz!uif!Dppljf!Npotufs",
  832. "#Mft!Tvdfuuft#!cz!Gsbodf!Hbmmf",
  833. "#Dboeznbo#!cz!Brvb-!Brvb",
  834. "#Tbwpz!Usvggmf#!cz!Uif!Cfbumft",
  835. "#Dipdpmbuf!Kftvt#!cz!Upn!Xbjut",
  836. "#Tusbxcfssz!Gjfmet!Gpsfwfs#!cz!Uif!Cfbumft",
  837. "#Tvhbs!Nbhopmjb#!cz!Uif!Hsbufgvm!Efbe",
  838. "#Cspxo!Tvhbs#!cz!Uif!Spmmjoh!Tupoft",
  839. "#Qfbdift#!cz!Uif!Qsftjefout!pg!uif!Vojufe!Tubuft",
  840. "#Xjme!Ipofz!Qjf#!cz!Uif!Cfbumft",
  841. "#Ebodf!pg!uif!Tvhbs!Qmvn!Gbjsz#!cz!Udibjlpwtlz",
  842. "#B!Tqppogvm!pg!Tvhbs#!cz!Kvmjf!Boesfxt",
  843. "#Qpvs!Tpnf!Tvhbs!Po!Nf#!cz!Efg!Mfqqbse",
  844. "ibwf!b!cvshfs",
  845. "ibwf!tpnf!dpggff",
  846. "hsbc!b!rvjdl!gjy!zpvstfmg",
  847. "hsbc!tpnf!mvodi",
  848. "ibwf!b!dboez!djhbsfuuf",
  849. "Npofz",
  850. "Xfbqpot",
  851. "Ifbmui",
  852. "Cbol",
  853. "Efcu",
  854. "EX`QMBZ",
  855. "ti;!ex;!Dpnnboe!opu!gpvoe/\n",
  856. },
  857. };
  858. BOOL DwStringsDecoded;
  859. INT DwRandomSource;
  860. //
  861. // ------------------------------------------------------------------ Functions
  862. //
  863. INT
  864. DwMain (
  865. INT ArgumentCount,
  866. CHAR **Arguments
  867. )
  868. /*++
  869. Routine Description:
  870. This routine is the main entry point for the dw utility.
  871. Arguments:
  872. ArgumentCount - Supplies the number of command line arguments the program
  873. was invoked with.
  874. Arguments - Supplies a tokenized array of command line arguments.
  875. Return Value:
  876. Returns an integer exit code. 0 for success, nonzero otherwise.
  877. --*/
  878. {
  879. DW_CONTEXT Context;
  880. INT PlayAgain;
  881. INT Result;
  882. //
  883. // Seed the bad randomizer, and try to open a decent random source.
  884. //
  885. DwRandomSource = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
  886. if (DwRandomSource < 0) {
  887. DwRandomSource = open("/dev/random", O_RDONLY | O_NONBLOCK);
  888. }
  889. srand(time(NULL));
  890. memset(&Context, 0, sizeof(Context));
  891. Result = DwDecodeStrings(&Context);
  892. if (Result != 0) {
  893. goto MainEnd;
  894. }
  895. SwSetRawInputMode(NULL, NULL);
  896. DwDrawIntro(&Context);
  897. if (DwReadCharacterSet(&Context, " ") == -1) {
  898. goto MainEnd;
  899. }
  900. //
  901. // Shall we play a game?
  902. //
  903. do {
  904. DwResetGame(&Context);
  905. DwPlay(&Context);
  906. PlayAgain = DwDisplayHighScores(&Context);
  907. } while (PlayAgain == 1);
  908. SwClearRegion(ConsoleColorDefault, ConsoleColorDefault, 0, 0, 80, 25);
  909. SwMoveCursor(stdout, 0, 0);
  910. Result = 0;
  911. MainEnd:
  912. if (DwRandomSource >= 0) {
  913. close(DwRandomSource);
  914. }
  915. SwRestoreInputMode();
  916. return Result;
  917. }
  918. //
  919. // --------------------------------------------------------- Internal Functions
  920. //
  921. INT
  922. DwDecodeStrings (
  923. PDW_CONTEXT Context
  924. )
  925. /*++
  926. Routine Description:
  927. This routine decodes all the strings used the app.
  928. Arguments:
  929. Context - Supplies a pointer to the application context.
  930. Return Value:
  931. 0 on success.
  932. ENOMEM on allocation failure.
  933. 127 if the proper conditions were not met.
  934. --*/
  935. {
  936. PSTR Buffer;
  937. ULONG Language;
  938. PSTR NewString;
  939. BOOL Print;
  940. size_t Size;
  941. PSTR Source;
  942. DW_STRING_VALUE StringIndex;
  943. if (DwStringsDecoded != FALSE) {
  944. return 0;
  945. }
  946. Print = FALSE;
  947. Size = 0;
  948. for (Language = 0; Language < DW_LANGUAGE_COUNT; Language += 1) {
  949. for (StringIndex = 0; StringIndex < DwsStringCount; StringIndex += 1) {
  950. Source = DwStrings[Language][StringIndex];
  951. if (Source != NULL) {
  952. Size += strlen(Source) + 1;
  953. }
  954. }
  955. }
  956. Buffer = malloc(Size);
  957. if (Buffer == NULL) {
  958. return ENOMEM;
  959. }
  960. for (Language = 0; Language < DW_LANGUAGE_COUNT; Language += 1) {
  961. if (Print != FALSE) {
  962. printf(" {\n");
  963. }
  964. for (StringIndex = 0; StringIndex < DwsStringCount; StringIndex += 1) {
  965. Source = DwStrings[Language][StringIndex];
  966. if (Source != NULL) {
  967. NewString = Buffer;
  968. if (Print != FALSE) {
  969. printf(" \"");
  970. }
  971. while (*Source != '\0') {
  972. if (*Source == '\n') {
  973. *Buffer = '\n';
  974. } else {
  975. *Buffer = *Source - 1;
  976. }
  977. if (Print != FALSE) {
  978. if (*Buffer == '\n') {
  979. printf("\\n");
  980. } else if (*Buffer == '"') {
  981. printf("\\\"");
  982. } else {
  983. printf("%c", *Buffer);
  984. }
  985. }
  986. Source += 1;
  987. Buffer += 1;
  988. }
  989. if (Print != FALSE) {
  990. printf("\",\n");
  991. }
  992. *Buffer = '\0';
  993. Buffer += 1;
  994. DwStrings[Language][StringIndex] = NewString;
  995. } else {
  996. if (Print != FALSE) {
  997. printf(" NULL,\n");
  998. }
  999. }
  1000. }
  1001. if (Print != FALSE) {
  1002. printf(" },\n");
  1003. }
  1004. }
  1005. DwStringsDecoded = TRUE;
  1006. for (Language = 0; Language < DW_LANGUAGE_COUNT; Language += 1) {
  1007. if (getenv(DwStrings[Language][DwsAccess]) != NULL) {
  1008. Context->Language = Language;
  1009. break;
  1010. }
  1011. }
  1012. if (Language == DW_LANGUAGE_COUNT) {
  1013. fprintf(stderr, "%s", DwString(DwsNoAccess));
  1014. return 127;
  1015. }
  1016. return 0;
  1017. }
  1018. VOID
  1019. DwDrawIntro (
  1020. PDW_CONTEXT Context
  1021. )
  1022. /*++
  1023. Routine Description:
  1024. This routine prepares the console and draws the introductory screen.
  1025. Arguments:
  1026. Context - Supplies a pointer to the application context.
  1027. Return Value:
  1028. None.
  1029. --*/
  1030. {
  1031. SwClearRegion(ConsoleColorGray, ConsoleColorDefault, 0, 0, 80, 25);
  1032. SwMoveCursor(stdout, 30, 0);
  1033. SwPrintInColor(ConsoleColorGray,
  1034. ConsoleColorDarkBlue,
  1035. DwString(DwsIntroTitle));
  1036. SwMoveCursor(stdout, 0, 5);
  1037. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, DwString(DwsIntroText));
  1038. DwDrawBottomPrompt(DwString(DwsPressSpace));
  1039. return;
  1040. }
  1041. VOID
  1042. DwResetGame (
  1043. PDW_CONTEXT Context
  1044. )
  1045. /*++
  1046. Routine Description:
  1047. This routine resets the game context.
  1048. Arguments:
  1049. Context - Supplies a pointer to the application context.
  1050. Return Value:
  1051. None.
  1052. --*/
  1053. {
  1054. INT Index;
  1055. Context->Backspace = 0x7F;
  1056. Context->Cash = DW_INITIAL_CASH;
  1057. Context->WeaponCount = DW_INITIAL_WEAPON_COUNT;
  1058. Context->WeaponDamage = DW_INITIAL_WEAPON_DAMAGE;
  1059. Context->Health = DW_INITIAL_HEALTH;
  1060. Context->Bank = DW_INITIAL_BANK;
  1061. Context->Debt = DW_INITIAL_DEBT;
  1062. Context->Space = DW_INITIAL_SPACE;
  1063. Context->Day = DW_INITIAL_DAY;
  1064. Context->Location = DW_INITIAL_LOCATION;
  1065. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  1066. Context->Inventory[Index] = 0;
  1067. }
  1068. DwDrawGameBoard(Context);
  1069. return;
  1070. }
  1071. VOID
  1072. DwDrawGameBoard (
  1073. PDW_CONTEXT Context
  1074. )
  1075. /*++
  1076. Routine Description:
  1077. This routine draws the static and initial components of the game board.
  1078. Arguments:
  1079. Context - Supplies a pointer to the application context.
  1080. Return Value:
  1081. None.
  1082. --*/
  1083. {
  1084. INT Row;
  1085. SwClearRegion(ConsoleColorGray, ConsoleColorDefault, 0, 0, 80, 25);
  1086. SwClearRegion(ConsoleColorDarkBlue, ConsoleColorDefault, 1, 1, 78, 16);
  1087. SwMoveCursor(stdout, 3, 0);
  1088. SwPrintInColor(ConsoleColorGray,
  1089. ConsoleColorDarkBlue,
  1090. "%02d-01-19%02d",
  1091. DwRandom(1, 12),
  1092. DwRandom(60, 91));
  1093. SwMoveCursor(stdout, 65, 0);
  1094. SwPrintInColor(ConsoleColorGray, ConsoleColorDarkBlue, "Space 100");
  1095. SwMoveCursor(stdout, 1, 1);
  1096. SwPrintInColor(ConsoleColorDarkBlue,
  1097. ConsoleColorGray,
  1098. DwString(DwsHorizontalLine));
  1099. SwMoveCursor(stdout, 1, 2);
  1100. SwPrintInColor(ConsoleColorDarkBlue,
  1101. ConsoleColorGray,
  1102. DwString(DwsColumnTitles));
  1103. SwMoveCursor(stdout, 1, 3);
  1104. SwPrintInColor(ConsoleColorDarkBlue,
  1105. ConsoleColorGray,
  1106. DwString(DwsHorizontalLine));
  1107. for (Row = 4; Row < 16; Row += 1) {
  1108. SwMoveCursor(stdout, 1, Row);
  1109. SwPrintInColor(ConsoleColorDarkBlue,
  1110. ConsoleColorGray,
  1111. DwString(DwsTwoColumnLine));
  1112. }
  1113. SwMoveCursor(stdout, 1, 16);
  1114. SwPrintInColor(ConsoleColorDarkBlue,
  1115. ConsoleColorGray,
  1116. DwString(DwsHorizontalLine));
  1117. DwRedrawCash(Context);
  1118. DwRedrawWeapons(Context);
  1119. DwRedrawHealth(Context);
  1120. DwRedrawBank(Context);
  1121. DwRedrawDebt(Context);
  1122. return;
  1123. }
  1124. VOID
  1125. DwPlay (
  1126. PDW_CONTEXT Context
  1127. )
  1128. /*++
  1129. Routine Description:
  1130. This routine implements the main game loop.
  1131. Arguments:
  1132. Context - Supplies a pointer to the application context.
  1133. Return Value:
  1134. None.
  1135. --*/
  1136. {
  1137. //
  1138. // Live day by day.
  1139. //
  1140. while ((Context->Health > 0) && (Context->Day <= DW_GAME_TIME) &&
  1141. (Context->ExitRequested == FALSE)) {
  1142. SwMoveCursor(stdout, 6, 0);
  1143. SwPrintInColor(ConsoleColorGray,
  1144. ConsoleColorDarkBlue,
  1145. "%02d",
  1146. Context->Day);
  1147. DwDrawLocation(DwLocationName(Context->Location));
  1148. DwDoDailyEvents(Context);
  1149. if (Context->Health <= 0) {
  1150. break;
  1151. }
  1152. DwParticipateInMarket(Context);
  1153. Context->Day += 1;
  1154. }
  1155. return;
  1156. }
  1157. VOID
  1158. DwDoDailyEvents (
  1159. PDW_CONTEXT Context
  1160. )
  1161. /*++
  1162. Routine Description:
  1163. This routine performs the daily routine, such as fetching market prices,
  1164. and meeting people on the subway.
  1165. Arguments:
  1166. Context - Supplies a pointer to the application context.
  1167. Return Value:
  1168. None.
  1169. --*/
  1170. {
  1171. INT Chance;
  1172. INT Index;
  1173. CHAR Line[240];
  1174. INT Price;
  1175. PSTR Qualifier;
  1176. PSTR SurgeFormat;
  1177. INT Worth;
  1178. //
  1179. // Go through the regular chores every day except the first.
  1180. //
  1181. if (Context->Day != 1) {
  1182. //
  1183. // Update the loan shark and bank on another day.
  1184. //
  1185. if (Context->Debt != 0) {
  1186. Context->Debt =
  1187. (Context->Debt * (DW_LOAN_INTEREST_RATE + 100)) / 100;
  1188. DwRedrawDebt(Context);
  1189. }
  1190. if (Context->Bank != 0) {
  1191. Context->Bank =
  1192. (Context->Bank * (DW_BANK_INTEREST_RATE + 100)) / 100;
  1193. DwRedrawBank(Context);
  1194. }
  1195. //
  1196. // Determine if something interesting is going to happen.
  1197. //
  1198. Worth = Context->Cash - Context->Debt;
  1199. Chance = 100;
  1200. if (Worth > 3000000) {
  1201. Chance = 130;
  1202. } else if (Worth > 1000000) {
  1203. Chance = 115;
  1204. }
  1205. if (DwRandom(0, Chance) > 75) {
  1206. Chance = 80 + DwLocations[Context->Location].PolicePresence;
  1207. Chance = DwRandom(0, Chance);
  1208. if (Chance < 33) {
  1209. DwReceiveOffer(Context);
  1210. } else if (Chance < 50) {
  1211. DwPerformActOfGod(Context);
  1212. } else {
  1213. DwEncounterPolice(Context);
  1214. }
  1215. }
  1216. if (Context->Health <= 0) {
  1217. return;
  1218. }
  1219. //
  1220. // Sometimes the lady on the subway pipes up.
  1221. //
  1222. if (DwRandom(0, 100) < 15) {
  1223. if (DwRandom(0, 100) < 50) {
  1224. Qualifier = "";
  1225. if (DwRandom(0, 100) < 30) {
  1226. Qualifier = DwString(DwsSubwayQualifier);
  1227. }
  1228. Index = DwRandom(0, DW_SUBWAY_SAYINGS_COUNT);
  1229. snprintf(Line,
  1230. sizeof(Line),
  1231. DwString(DwsSubwayLadyFormat),
  1232. DwString(DwsSubwaySayings + Index),
  1233. Qualifier);
  1234. } else {
  1235. Index = DwRandom(0, DW_SONG_COUNT);
  1236. snprintf(Line,
  1237. sizeof(Line),
  1238. DwString(DwsHearSongFormat),
  1239. DwString(DwsSongs + Index));
  1240. }
  1241. }
  1242. //
  1243. // In a certain part of town, it's possible to visit some special folks.
  1244. //
  1245. if (Context->Location == DW_FINANCIAL_DISTRICT) {
  1246. DwVisitFinancialDistrict(Context);
  1247. }
  1248. }
  1249. //
  1250. // Go get today's market prices, and note any large price fluctuations.
  1251. //
  1252. DwGenerateMarket(Context);
  1253. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  1254. Price = Context->Market[Index];
  1255. //
  1256. // If the good is not in the market or has a normal price, then ignore
  1257. // it.
  1258. //
  1259. if ((Price == 0) ||
  1260. ((Price >= DwGoods[Index].MinPrice) &&
  1261. (Price < DwGoods[Index].MaxPrice))) {
  1262. continue;
  1263. }
  1264. //
  1265. // Everyone loves a sale.
  1266. //
  1267. if (Price < DwGoods[Index].MinPrice) {
  1268. DwPresentNotification(Context, DwString(DwsGoodsSales + Index));
  1269. //
  1270. // A surge: help a brother out.
  1271. //
  1272. } else {
  1273. if (DwRandom(0, 100) < 50) {
  1274. SurgeFormat = DwString(DwsSurgeFormat1);
  1275. } else {
  1276. SurgeFormat = DwString(DwsSurgeFormat2);
  1277. }
  1278. snprintf(Line, sizeof(Line), SurgeFormat, DwGoodName(Index));
  1279. DwPresentNotification(Context, Line);
  1280. }
  1281. }
  1282. return;
  1283. }
  1284. VOID
  1285. DwReceiveOffer (
  1286. PDW_CONTEXT Context
  1287. )
  1288. /*++
  1289. Routine Description:
  1290. This routine gives the player a special product offer.
  1291. Arguments:
  1292. Context - Supplies a pointer to the application context.
  1293. Return Value:
  1294. None.
  1295. --*/
  1296. {
  1297. INT Answer;
  1298. CHAR Line[80];
  1299. INT Price;
  1300. INT Product;
  1301. //
  1302. // Potentially offer the player more space for goods.
  1303. //
  1304. if (DwRandom(0, 100) < 50) {
  1305. Price = DwRandom(DW_MIN_SPACE_PRICE, DW_MAX_SPACE_PRICE);
  1306. if (Price <= Context->Cash) {
  1307. snprintf(Line,
  1308. sizeof(Line),
  1309. DwString(DwsProductOfferFormat),
  1310. DwString(DwsProductMoreSpace),
  1311. Price);
  1312. Answer = DwReadYesNoAnswer(Context, NULL, Line);
  1313. if (Answer == 1) {
  1314. Context->Space += DwRandom(1, 2) * DW_MORE_SPACE;
  1315. Context->Cash -= Price;
  1316. DwRedrawSpace(Context);
  1317. DwRedrawCash(Context);
  1318. }
  1319. }
  1320. //
  1321. // Offer the player a weapon.
  1322. //
  1323. } else {
  1324. Product = DwRandom(0, DW_WEAPON_COUNT);
  1325. Price = DwWeapons[Product].Price;
  1326. if ((Price <= Context->Cash) &&
  1327. (Context->Space >= DwWeapons[Product].Space)) {
  1328. snprintf(Line,
  1329. sizeof(Line),
  1330. DwString(DwsProductOfferFormat),
  1331. DwWeaponName(Product),
  1332. DwWeapons[Product].Price);
  1333. Answer = DwReadYesNoAnswer(Context, NULL, Line);
  1334. if (Answer == 1) {
  1335. Context->Space -= DwWeapons[Product].Space;
  1336. Context->WeaponCount += 1;
  1337. Context->WeaponDamage += DwWeapons[Product].Damage;
  1338. Context->Cash -= Price;
  1339. DwRedrawCash(Context);
  1340. DwRedrawSpace(Context);
  1341. DwRedrawWeapons(Context);
  1342. }
  1343. }
  1344. }
  1345. return;
  1346. }
  1347. VOID
  1348. DwPerformActOfGod (
  1349. PDW_CONTEXT Context
  1350. )
  1351. /*++
  1352. Routine Description:
  1353. This routine performs a non-interactive and random action on the user.
  1354. Arguments:
  1355. Context - Supplies a pointer to the application context.
  1356. Return Value:
  1357. None.
  1358. --*/
  1359. {
  1360. INT Action;
  1361. INT Amount;
  1362. INT Answer;
  1363. INT Good;
  1364. INT Index;
  1365. CHAR Line[160];
  1366. //
  1367. // Some actions require a good that the player currently has in a certain
  1368. // quantity.
  1369. //
  1370. Amount = DwRandom(3, 7);
  1371. for (Index = 0; Index < 5; Index += 1) {
  1372. Good = DwRandom(0, DW_GOOD_COUNT);
  1373. if (Context->Inventory[Good] >= Amount) {
  1374. break;
  1375. }
  1376. }
  1377. if (Index == 5) {
  1378. Good = -1;
  1379. }
  1380. Action = DwRandom(0, 100);
  1381. //
  1382. // Sometimes there are muggers in the subway.
  1383. //
  1384. if (Action < 10) {
  1385. DwPresentNotification(Context, DwString(DwsMugged));
  1386. Context->Cash = (Context->Cash * DwRandom(80, 95)) / 100;
  1387. DwRedrawCash(Context);
  1388. //
  1389. // Sometimes gifts are given or received.
  1390. //
  1391. } else if (Action < 30) {
  1392. if (Good == -1) {
  1393. //
  1394. // Players lose out if they don't have space to receive gifts.
  1395. //
  1396. if (Amount > Context->Space) {
  1397. Amount = 0;
  1398. }
  1399. Good = DwRandom(0, DW_GOOD_COUNT);
  1400. snprintf(Line,
  1401. sizeof(Line),
  1402. DwString(DwsReceiveGiftFormat),
  1403. DwGoodName(Good));
  1404. Context->Inventory[Good] += Amount;
  1405. Context->Space -= Amount;
  1406. //
  1407. // Well heeled players give gifts to others.
  1408. //
  1409. } else {
  1410. snprintf(Line,
  1411. sizeof(Line),
  1412. DwString(DwsSendGiftFormat),
  1413. DwGoodName(Good));
  1414. Context->Inventory[Good] -= Amount;
  1415. Context->Space += Amount;
  1416. }
  1417. if (Amount != 0) {
  1418. DwPresentNotification(Context, Line);
  1419. DwRedrawSpace(Context);
  1420. DwRedrawInventory(Context);
  1421. }
  1422. //
  1423. // Sometimes people just lose things, or find things.
  1424. //
  1425. } else if (Action < 50) {
  1426. if (Good != -1) {
  1427. snprintf(Line,
  1428. sizeof(Line),
  1429. DwString(DwsLostGoodsFormat),
  1430. DwRandom(3, 7));
  1431. Context->Inventory[Good] -= Amount;
  1432. Context->Space += Amount;
  1433. } else {
  1434. if (Amount > Context->Space) {
  1435. Amount = 0;
  1436. }
  1437. Good = DwRandom(0, DW_GOOD_COUNT);
  1438. snprintf(Line,
  1439. sizeof(Line),
  1440. DwString(DwsFoundGoodsFormat),
  1441. Amount,
  1442. DwGoodName(Good));
  1443. Context->Inventory[Good] += Amount;
  1444. Context->Space -= Amount;
  1445. }
  1446. if (Amount != 0) {
  1447. DwPresentNotification(Context, Line);
  1448. DwRedrawSpace(Context);
  1449. DwRedrawInventory(Context);
  1450. }
  1451. //
  1452. // Sometimes other people share your items.
  1453. //
  1454. } else if ((Action < 60) &&
  1455. ((Context->Inventory[DW_BROWNIE_GOOD1] != 0) ||
  1456. (Context->Inventory[DW_BROWNIE_GOOD2] != 0))) {
  1457. Good = DW_BROWNIE_GOOD1;
  1458. if (Context->Inventory[DW_BROWNIE_GOOD2] >
  1459. Context->Inventory[DW_BROWNIE_GOOD1]) {
  1460. Good = DW_BROWNIE_GOOD2;
  1461. }
  1462. Amount = DwRandom(2, 6);
  1463. if (Amount > Context->Inventory[Good]) {
  1464. Amount = Context->Inventory[Good];
  1465. }
  1466. snprintf(Line,
  1467. sizeof(Line),
  1468. DwString(DwsSharedGoodsFormat),
  1469. DwGoodName(Good));
  1470. Context->Inventory[Good] -= Amount;
  1471. Context->Space += Amount;
  1472. DwPresentNotification(Context, Line);
  1473. DwRedrawSpace(Context);
  1474. DwRedrawInventory(Context);
  1475. //
  1476. // Sometimes there's that offer that's just too good to be true.
  1477. //
  1478. } else if (Action < 65) {
  1479. Answer = DwReadYesNoAnswer(Context,
  1480. DwString(DwsSirenSong),
  1481. DwString(DwsSirenPrompt));
  1482. if (Answer == 1) {
  1483. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, "Y");
  1484. SwMoveCursor(stdout, 1, 21);
  1485. SwPrintInColor(ConsoleColorGray,
  1486. ConsoleColorBlack,
  1487. DwString(DwsSirenResult));
  1488. DwPresentNotification(Context, NULL);
  1489. Context->Health = 0;
  1490. }
  1491. //
  1492. // Occasionally the player just needs to take a break from the hustle to
  1493. // sit and think.
  1494. //
  1495. } else {
  1496. Index = DwRandom(0, DW_PASSIVE_ACTIVITY_COUNT);
  1497. snprintf(Line,
  1498. sizeof(Line),
  1499. DwString(DwsPassiveActivityFormat),
  1500. DwString(DwsPassiveActivities + Index));
  1501. DwPresentNotification(Context, Line);
  1502. Amount = DwRandom(1, 10);
  1503. if (Context->Cash >= Amount) {
  1504. Context->Cash -= Amount;
  1505. }
  1506. DwRedrawCash(Context);
  1507. }
  1508. return;
  1509. }
  1510. VOID
  1511. DwEncounterPolice (
  1512. PDW_CONTEXT Context
  1513. )
  1514. /*++
  1515. Routine Description:
  1516. This routine is called when the player has a brush with the law.
  1517. Arguments:
  1518. Context - Supplies a pointer to the application context.
  1519. Return Value:
  1520. None.
  1521. --*/
  1522. {
  1523. INT Answer;
  1524. PSTR Choices;
  1525. INT Damage;
  1526. INT Defence;
  1527. INT DoctorCost;
  1528. INT Enemies;
  1529. INT FancyFeet;
  1530. INT MaxEnemies;
  1531. INT Offense;
  1532. PSTR Prompt;
  1533. INT Row;
  1534. PSTR ShotResult;
  1535. INT VictoryCash;
  1536. //
  1537. // How bad could this be?
  1538. //
  1539. MaxEnemies = 4;
  1540. if (Context->Day >= 15) {
  1541. MaxEnemies = 6;
  1542. if (Context->Day > 23) {
  1543. MaxEnemies = 9;
  1544. }
  1545. }
  1546. Enemies = DwRandom(2, MaxEnemies);
  1547. MaxEnemies = Enemies;
  1548. //
  1549. // Loop handlin' bad guys.
  1550. //
  1551. while (Enemies != 0) {
  1552. DwClearLowerRegion();
  1553. Row = 18;
  1554. SwMoveCursor(stdout, 1, Row);
  1555. SwPrintInColor(ConsoleColorGray,
  1556. ConsoleColorBlack,
  1557. DwString(DwsFightThreatFormat),
  1558. Enemies - 1);
  1559. Row += 1;
  1560. SwMoveCursor(stdout, 1, Row);
  1561. if (Context->WeaponCount != 0) {
  1562. Prompt = DwString(DwsRunOrFight);
  1563. Choices = "rf";
  1564. } else {
  1565. Prompt = DwString(DwsRunOption);
  1566. Choices = "yn";
  1567. }
  1568. SwPrintInColor(ConsoleColorGray, ConsoleColorDarkMagenta, Prompt);
  1569. Answer = DwReadCharacterSet(Context, Choices);
  1570. if (Answer == EOF) {
  1571. return;
  1572. }
  1573. //
  1574. // Fight on, player.
  1575. //
  1576. if (Answer == 'f') {
  1577. SwPrintInColor(ConsoleColorGray,
  1578. ConsoleColorBlack,
  1579. DwString(DwsFight));
  1580. Row += 1;
  1581. DwFlashText(Context,
  1582. DwString(DwsPlayerFire),
  1583. 1,
  1584. Row,
  1585. ConsoleColorGray,
  1586. ConsoleColorBlack,
  1587. ConsoleColorWhite,
  1588. FALSE);
  1589. Offense = 50 + Context->WeaponDamage;
  1590. ShotResult = DwString(DwsPlayerMissed);
  1591. if (DwRandom(0, 100) < Offense) {
  1592. Enemies -= 1;
  1593. ShotResult = DwString(DwsPlayerHit);
  1594. }
  1595. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, ShotResult);
  1596. //
  1597. // Flee from the police!
  1598. //
  1599. } else if ((Answer == 'y') || (Answer == 'r')) {
  1600. SwPrintInColor(ConsoleColorGray,
  1601. ConsoleColorBlack,
  1602. DwString(DwsRun));
  1603. Row += 1;
  1604. SwMoveCursor(stdout, 1, Row);
  1605. FancyFeet = 65 - (Enemies * 5);
  1606. if (DwRandom(0, 100) < FancyFeet) {
  1607. SwPrintInColor(ConsoleColorGray,
  1608. ConsoleColorBlack,
  1609. DwString(DwsFled));
  1610. DwPresentNotification(Context, NULL);
  1611. break;
  1612. } else {
  1613. SwPrintInColor(ConsoleColorGray,
  1614. ConsoleColorBlack,
  1615. DwString(DwsFailedToFlee));
  1616. }
  1617. //
  1618. // Just stand around and hope nothing happens.
  1619. //
  1620. } else {
  1621. Row += 1;
  1622. SwMoveCursor(stdout, 1, Row);
  1623. SwPrintInColor(ConsoleColorGray,
  1624. ConsoleColorBlack,
  1625. DwString(DwsNotFleeing));
  1626. }
  1627. Row += 1;
  1628. //
  1629. // If there are still enemies, they fire at our hero.
  1630. //
  1631. if (Enemies != 0) {
  1632. SwMoveCursor(stdout, 1, Row);
  1633. DwFlashText(Context,
  1634. DwString(DwsPlayerUnderFire),
  1635. 1,
  1636. Row,
  1637. ConsoleColorGray,
  1638. ConsoleColorBlack,
  1639. ConsoleColorWhite,
  1640. FALSE);
  1641. Defence = 60 - (Enemies * 5);
  1642. if (DwRandom(0, 100) < Defence) {
  1643. SwPrintInColor(ConsoleColorGray,
  1644. ConsoleColorBlack,
  1645. DwString(DwsTheyMissed));
  1646. } else {
  1647. SwPrintInColor(ConsoleColorGray,
  1648. ConsoleColorBlack,
  1649. DwString(DwsTheyHit));
  1650. Damage = DwRandom(5, 10);
  1651. if (Damage >= Context->Health) {
  1652. Context->Health = 0;
  1653. } else {
  1654. Context->Health -= Damage;
  1655. }
  1656. }
  1657. DwRedrawHighlightedHealth(Context);
  1658. if (Context->Health <= 0) {
  1659. Row += 1;
  1660. SwMoveCursor(stdout, 1, Row);
  1661. SwPrintInColor(ConsoleColorGray,
  1662. ConsoleColorBlack,
  1663. DwString(DwsKilled));
  1664. }
  1665. DwPresentNotification(Context, NULL);
  1666. DwRedrawHealth(Context);
  1667. if (Context->Health <= 0) {
  1668. break;
  1669. }
  1670. //
  1671. // Victory!
  1672. //
  1673. } else {
  1674. DwPresentNotification(Context, NULL);
  1675. SwClearRegion(ConsoleColorGray, ConsoleColorDefault, 0, 24, 80, 1);
  1676. VictoryCash = DwRandom(1000, 1500 + (MaxEnemies * 200));
  1677. SwMoveCursor(stdout, 1, Row);
  1678. SwPrintInColor(ConsoleColorGray,
  1679. ConsoleColorBlack,
  1680. DwString(DwsFightVictoryFormat),
  1681. VictoryCash);
  1682. Context->Cash += VictoryCash;
  1683. DoctorCost = DwRandom(100, 200) *
  1684. (DW_INITIAL_HEALTH - Context->Health) / 5;
  1685. if (DoctorCost > Context->Cash) {
  1686. DoctorCost = Context->Cash;
  1687. }
  1688. if (DoctorCost != 0) {
  1689. SwPrintInColor(ConsoleColorGray,
  1690. ConsoleColorDarkMagenta,
  1691. DwString(DwsDoctorOffer),
  1692. DoctorCost);
  1693. Answer = DwReadYesNoAnswer(Context, NULL, NULL);
  1694. if (Answer == EOF) {
  1695. return;
  1696. }
  1697. if (Answer == 1) {
  1698. Context->Cash -= DoctorCost;
  1699. Context->Health = DW_INITIAL_HEALTH;
  1700. }
  1701. } else {
  1702. DwPresentNotification(Context, NULL);
  1703. }
  1704. DwRedrawCash(Context);
  1705. DwRedrawHealth(Context);
  1706. break;
  1707. }
  1708. }
  1709. return;
  1710. }
  1711. VOID
  1712. DwVisitFinancialDistrict (
  1713. PDW_CONTEXT Context
  1714. )
  1715. /*++
  1716. Routine Description:
  1717. This routine is called to prompt the user and handle visits to the bank and
  1718. potentially the loan shark.
  1719. Arguments:
  1720. Context - Supplies a pointer to the application context.
  1721. Return Value:
  1722. None.
  1723. --*/
  1724. {
  1725. INT Action;
  1726. INT Answer;
  1727. INT Value;
  1728. //
  1729. // Visit the loan shark if the player has a loan.
  1730. //
  1731. if (Context->Debt != 0) {
  1732. Answer = DwReadYesNoAnswer(Context, NULL, DwString(DwsVisitLoanShark));
  1733. if (Answer == -1) {
  1734. return;
  1735. }
  1736. if (Answer == 1) {
  1737. SwPrintInColor(ConsoleColorGray,
  1738. ConsoleColorBlack,
  1739. DwString(DwsYes));
  1740. SwMoveCursor(stdout, 1, 20);
  1741. SwPrintInColor(ConsoleColorGray,
  1742. ConsoleColorDarkMagenta,
  1743. DwString(DwsLoanRepaymentAmount));
  1744. Value = DwReadQuantity(Context);
  1745. if (Value > 0) {
  1746. if ((Value <= Context->Cash) && (Value <= Context->Debt)) {
  1747. Context->Cash -= Value;
  1748. Context->Debt -= Value;
  1749. DwRedrawCash(Context);
  1750. if (Context->Debt == 0) {
  1751. SwClearRegion(ConsoleColorDarkBlue,
  1752. ConsoleColorDefault,
  1753. 9,
  1754. 13,
  1755. 20,
  1756. 1);
  1757. } else {
  1758. DwRedrawDebt(Context);
  1759. }
  1760. }
  1761. }
  1762. }
  1763. }
  1764. //
  1765. // Visit the bank.
  1766. //
  1767. Answer = DwReadYesNoAnswer(Context, NULL, DwString(DwsVisitBank));
  1768. if (Answer == -1) {
  1769. return;
  1770. }
  1771. if (Answer == 1) {
  1772. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, DwString(DwsYes));
  1773. SwMoveCursor(stdout, 1, 19);
  1774. SwPrintInColor(ConsoleColorGray,
  1775. ConsoleColorDarkMagenta,
  1776. DwString(DwsDepositOrWithdraw));
  1777. Action = DwReadCharacterSet(Context, "dw");
  1778. if (Action == -1) {
  1779. return;
  1780. }
  1781. SwPrintInColor(ConsoleColorGray,
  1782. ConsoleColorBlack,
  1783. "%c",
  1784. toupper(Action));
  1785. SwMoveCursor(stdout, 1, 20);
  1786. SwPrintInColor(ConsoleColorGray,
  1787. ConsoleColorDarkMagenta,
  1788. DwString(DwsHowMuchMoney));
  1789. Value = DwReadQuantity(Context);
  1790. if (Value > 0) {
  1791. if (Action == 'd') {
  1792. if (Value <= Context->Cash) {
  1793. Context->Cash -= Value;
  1794. Context->Bank += Value;
  1795. }
  1796. } else if (Action == 'w') {
  1797. if (Value <= Context->Bank) {
  1798. Context->Cash += Value;
  1799. Context->Bank -= Value;
  1800. }
  1801. }
  1802. DwRedrawBank(Context);
  1803. DwRedrawCash(Context);
  1804. }
  1805. }
  1806. return;
  1807. }
  1808. VOID
  1809. DwGenerateMarket (
  1810. PDW_CONTEXT Context
  1811. )
  1812. /*++
  1813. Routine Description:
  1814. This routine generates a new market's worth of goods and prices.
  1815. Arguments:
  1816. Context - Supplies a pointer to the application context.
  1817. Return Value:
  1818. None.
  1819. --*/
  1820. {
  1821. INT EventCount;
  1822. INT GoodsCount;
  1823. INT Index;
  1824. INT TotalGoodsCount;
  1825. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  1826. Context->Market[Index] = 0;
  1827. }
  1828. //
  1829. // Determine how many special events will occur today.
  1830. //
  1831. EventCount = 0;
  1832. if (DwRandom(0, 100) < 70) {
  1833. EventCount = 1;
  1834. if (DwRandom(0, 100) < 40) {
  1835. EventCount = 2;
  1836. if (DwRandom(0, 100) < 5) {
  1837. EventCount = 3;
  1838. }
  1839. }
  1840. }
  1841. //
  1842. // Determine what those special events are.
  1843. //
  1844. GoodsCount = 0;
  1845. while (EventCount > 0) {
  1846. Index = DwRandom(0, DW_GOOD_COUNT);
  1847. if (Context->Market[Index] != 0) {
  1848. continue;
  1849. }
  1850. if ((DwGoods[Index].Surges == FALSE) &&
  1851. (DwGoods[Index].Sales == FALSE)) {
  1852. continue;
  1853. }
  1854. GoodsCount += 1;
  1855. EventCount -= 1;
  1856. Context->Market[Index] = DwRandom(DwGoods[Index].MinPrice,
  1857. DwGoods[Index].MaxPrice);
  1858. if (DwGoods[Index].Surges != FALSE) {
  1859. Context->Market[Index] *= DW_SURGE_FACTOR;
  1860. } else if (DwGoods[Index].Sales != FALSE) {
  1861. Context->Market[Index] /= DW_SALE_FACTOR;
  1862. }
  1863. }
  1864. //
  1865. // Determine how many goods will be in the market.
  1866. //
  1867. TotalGoodsCount = DwRandom(DwLocations[Context->Location].MinGoods,
  1868. DwLocations[Context->Location].MaxGoods);
  1869. assert(TotalGoodsCount <= DW_GOOD_COUNT);
  1870. GoodsCount = TotalGoodsCount - GoodsCount;
  1871. while (GoodsCount > 0) {
  1872. Index = DwRandom(0, DW_GOOD_COUNT);
  1873. if (Context->Market[Index] != 0) {
  1874. continue;
  1875. }
  1876. Context->Market[Index] = DwRandom(DwGoods[Index].MinPrice,
  1877. DwGoods[Index].MaxPrice);
  1878. GoodsCount -= 1;
  1879. }
  1880. return;
  1881. }
  1882. VOID
  1883. DwParticipateInMarket (
  1884. PDW_CONTEXT Context
  1885. )
  1886. /*++
  1887. Routine Description:
  1888. This routine is where the player buys and sells goods.
  1889. Arguments:
  1890. Context - Supplies a pointer to the application context.
  1891. Return Value:
  1892. None.
  1893. --*/
  1894. {
  1895. INT Action;
  1896. INT Choice;
  1897. CHAR ChoiceBuffer[DW_GOOD_COUNT + 1];
  1898. PSTR Choices;
  1899. INT Index;
  1900. INT MaxQuantity;
  1901. PSTR Prompt;
  1902. INT Quantity;
  1903. INT SelectedGood;
  1904. while (Context->ExitRequested == FALSE) {
  1905. DwDrawMarket(Context);
  1906. //
  1907. // Determine if the user has anything in inventory, and can therefore
  1908. // sell.
  1909. //
  1910. Choices = "bj";
  1911. Prompt = DwString(DwsBuyOrJet);
  1912. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  1913. if (Context->Inventory[Index] != 0) {
  1914. Choices = "bsj";
  1915. Prompt = DwString(DwsBuySellJet);
  1916. break;
  1917. }
  1918. }
  1919. DwDrawBottomPrompt(Prompt);
  1920. Action = DwReadCharacterSet(Context, Choices);
  1921. if (Action == EOF) {
  1922. break;
  1923. }
  1924. if ((Action == 'b') || (Action == 's')) {
  1925. SwClearRegion(ConsoleColorGray, ConsoleColorDefault, 0, 24, 80, 1);
  1926. SwMoveCursor(stdout, 1, 23);
  1927. if (Action == 'b') {
  1928. SwPrintInColor(ConsoleColorGray,
  1929. ConsoleColorDarkMagenta,
  1930. DwString(DwsWhatToBuy));
  1931. } else {
  1932. SwPrintInColor(ConsoleColorGray,
  1933. ConsoleColorDarkMagenta,
  1934. DwString(DwsWhatToSell));
  1935. }
  1936. //
  1937. // Create the set of possibilities. For buying, anything in the
  1938. // market is a valid choice. For selling, anything in inventory
  1939. // is a valid choice.
  1940. //
  1941. Choice = 'a';
  1942. Choices = ChoiceBuffer;
  1943. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  1944. if (Context->Market[Index] != 0) {
  1945. if ((Action == 'b') || (Context->Inventory[Index] != 0)) {
  1946. *Choices = Choice;
  1947. Choices += 1;
  1948. }
  1949. Choice += 1;
  1950. }
  1951. }
  1952. *Choices = '\0';
  1953. Choice = DwReadCharacterSet(Context, ChoiceBuffer);
  1954. if (Choice == EOF) {
  1955. break;
  1956. }
  1957. //
  1958. // Translate back to figure out which good they were talking about.
  1959. //
  1960. Choice -= 'a';
  1961. SelectedGood = 0;
  1962. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  1963. if (Context->Market[Index] != 0) {
  1964. if (Choice == SelectedGood) {
  1965. SelectedGood = Index;
  1966. break;
  1967. }
  1968. SelectedGood += 1;
  1969. }
  1970. }
  1971. SwPrintInColor(ConsoleColorGray,
  1972. ConsoleColorBlack,
  1973. DwGoodName(SelectedGood));
  1974. //
  1975. // Ask how many they'd like to purchase or sell.
  1976. //
  1977. SwMoveCursor(stdout, 1, 24);
  1978. if (Action == 'b') {
  1979. MaxQuantity = Context->Cash / Context->Market[Index];
  1980. SwPrintInColor(ConsoleColorGray,
  1981. ConsoleColorDarkMagenta,
  1982. DwString(DwsHowManyToBuy),
  1983. MaxQuantity);
  1984. } else {
  1985. MaxQuantity = Context->Inventory[Index];
  1986. SwPrintInColor(ConsoleColorGray,
  1987. ConsoleColorDarkMagenta,
  1988. DwString(DwsHowManyToSell),
  1989. MaxQuantity);
  1990. }
  1991. Quantity = DwReadQuantity(Context);
  1992. //
  1993. // Do the deal. The check for space is only needed when buying.
  1994. //
  1995. if ((Quantity > 0) && (Quantity <= MaxQuantity) &&
  1996. ((Action == 's') || (Quantity <= Context->Space))) {
  1997. //
  1998. // Selling is really just buying negative quantities. Chew on
  1999. // that.
  2000. //
  2001. if (Action == 's') {
  2002. Quantity = -Quantity;
  2003. }
  2004. Context->Inventory[SelectedGood] += Quantity;
  2005. Context->Cash -= Quantity * Context->Market[SelectedGood];
  2006. Context->Space -= Quantity;
  2007. DwRedrawCash(Context);
  2008. DwRedrawInventory(Context);
  2009. DwRedrawSpace(Context);
  2010. }
  2011. //
  2012. // They're outta here.
  2013. //
  2014. } else if (Action == 'j') {
  2015. DwClearLowerRegion();
  2016. for (Index = 0; Index < DW_LOCATION_COUNT; Index += 1) {
  2017. SwMoveCursor(stdout, 4 + ((Index % 3) * 28), 18 + (Index / 3));
  2018. SwPrintInColor(ConsoleColorGray,
  2019. ConsoleColorBlack,
  2020. "%d. %s",
  2021. Index + 1,
  2022. DwLocationName(Index));
  2023. }
  2024. DwDrawBottomPrompt(DwString(DwsWhereTo));
  2025. Choice = DwReadCharacterSet(Context, "123456");
  2026. if (Choice == EOF) {
  2027. return;
  2028. }
  2029. //
  2030. // If they want a different place than they are, then ride the
  2031. // subway away from here.
  2032. //
  2033. Choice -= '1';
  2034. if (Choice != Context->Location) {
  2035. assert(Choice < DW_LOCATION_COUNT);
  2036. Context->Location = Choice;
  2037. DwClearLowerRegion();
  2038. //
  2039. // Ride the subway.
  2040. //
  2041. DwDrawLocation("");
  2042. DwFlashText(Context,
  2043. DwString(DwsSubway),
  2044. 33,
  2045. 2,
  2046. ConsoleColorDarkBlue,
  2047. ConsoleColorWhite,
  2048. ConsoleColorDarkBlue,
  2049. TRUE);
  2050. DwDrawLocation(DwLocationName(Choice));
  2051. break;
  2052. }
  2053. }
  2054. }
  2055. return;
  2056. }
  2057. VOID
  2058. DwDrawMarket (
  2059. PDW_CONTEXT Context
  2060. )
  2061. /*++
  2062. Routine Description:
  2063. This routine redraws the current daily market contents.
  2064. Arguments:
  2065. Context - Supplies a pointer to the application context.
  2066. Return Value:
  2067. None.
  2068. --*/
  2069. {
  2070. INT ColumnIndex;
  2071. INT Index;
  2072. CHAR Line[16];
  2073. CHAR Price[11];
  2074. INT Row;
  2075. CHAR Selector;
  2076. DwClearLowerRegion();
  2077. Selector = 'A';
  2078. SwMoveCursor(stdout, 1, 18);
  2079. SwPrintInColor(ConsoleColorGray,
  2080. ConsoleColorBlack,
  2081. DwString(DwsMarketGreeting));
  2082. Row = 19;
  2083. ColumnIndex = 0;
  2084. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  2085. if (Context->Market[Index] == 0) {
  2086. continue;
  2087. }
  2088. SwMoveCursor(stdout, 4 + (ColumnIndex * 26), Row);
  2089. snprintf(Line, sizeof(Line), "%c> %s", Selector, DwGoodName(Index));
  2090. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, Line);
  2091. DwFormatMoney(Price, sizeof(Price), Context->Market[Index]);
  2092. SwPrintInColor(ConsoleColorGray,
  2093. ConsoleColorBlack,
  2094. "%*s",
  2095. 20 - strlen(Line),
  2096. Price);
  2097. Selector += 1;
  2098. if (ColumnIndex == 2) {
  2099. ColumnIndex = 0;
  2100. Row += 1;
  2101. } else {
  2102. ColumnIndex += 1;
  2103. }
  2104. }
  2105. return;
  2106. }
  2107. VOID
  2108. DwRedrawInventory (
  2109. PDW_CONTEXT Context
  2110. )
  2111. /*++
  2112. Routine Description:
  2113. This routine redraws the player's current inventory.
  2114. Arguments:
  2115. Context - Supplies a pointer to the application context.
  2116. Return Value:
  2117. None.
  2118. --*/
  2119. {
  2120. INT Index;
  2121. INT Quantity;
  2122. INT Row;
  2123. SwClearRegion(ConsoleColorDarkBlue,
  2124. ConsoleColorDefault,
  2125. 47,
  2126. 4,
  2127. 24,
  2128. DW_GOOD_COUNT);
  2129. Row = 4;
  2130. for (Index = 0; Index < DW_GOOD_COUNT; Index += 1) {
  2131. Quantity = Context->Inventory[Index];
  2132. if (Quantity == 0) {
  2133. continue;
  2134. }
  2135. SwMoveCursor(stdout, 47, Row);
  2136. SwPrintInColor(ConsoleColorDarkBlue,
  2137. ConsoleColorGray,
  2138. DwGoodName(Index));
  2139. SwMoveCursor(stdout, 47 + 18, Row);
  2140. SwPrintInColor(ConsoleColorDarkBlue,
  2141. ConsoleColorGray,
  2142. "%6d",
  2143. Quantity);
  2144. Row += 1;
  2145. }
  2146. return;
  2147. }
  2148. VOID
  2149. DwPresentNotification (
  2150. PDW_CONTEXT Context,
  2151. PSTR Notification
  2152. )
  2153. /*++
  2154. Routine Description:
  2155. This routine presents a notification to the user in the bottom area, and
  2156. waits for them to press space.
  2157. Arguments:
  2158. Context - Supplies a pointer to the application context.
  2159. Notification - Supplies an pointer to the notification. If NULL, then the
  2160. lower screen isn't cleared, as it's assumed the notification is already
  2161. set up.
  2162. Return Value:
  2163. None.
  2164. --*/
  2165. {
  2166. if (Notification != NULL) {
  2167. DwClearLowerRegion();
  2168. SwMoveCursor(stdout, 1, 18);
  2169. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, Notification);
  2170. }
  2171. DwDrawBottomPrompt(DwString(DwsPressSpace));
  2172. DwReadCharacterSet(Context, " ");
  2173. return;
  2174. }
  2175. VOID
  2176. DwDrawBottomPrompt (
  2177. PSTR Prompt
  2178. )
  2179. /*++
  2180. Routine Description:
  2181. This routine prints a string centered in purple at the bottom of the
  2182. screen. This routine assumes the bottom line is already clear.
  2183. Arguments:
  2184. Prompt - Supplies a pointer to the string containing the prompt to print.
  2185. Return Value:
  2186. None.
  2187. --*/
  2188. {
  2189. UINTN Column;
  2190. UINTN Length;
  2191. Length = strlen(Prompt);
  2192. if (Length >= 80) {
  2193. Column = 0;
  2194. } else {
  2195. Column = 40 - (Length / 2);
  2196. }
  2197. SwMoveCursor(stdout, Column, 24);
  2198. SwPrintInColor(ConsoleColorGray, ConsoleColorDarkMagenta, Prompt);
  2199. return;
  2200. }
  2201. VOID
  2202. DwDrawStat (
  2203. PSTR Name,
  2204. INT Row,
  2205. INT Value,
  2206. BOOL Money,
  2207. CONSOLE_COLOR Foreground,
  2208. CONSOLE_COLOR Background
  2209. )
  2210. /*++
  2211. Routine Description:
  2212. This routine redraws one of the statistics on the left pane.
  2213. Arguments:
  2214. Name - Supplies a pointer to the name of the statistic.
  2215. Row - Supplies the starting row of the statistic.
  2216. Value - Supplies the value of the stat.
  2217. Money - Supplies a boolean indicating if this is a monetary value or not.
  2218. Foreground - Supplies the text color to use.
  2219. Background - Supplies the background color to use.
  2220. Return Value:
  2221. None.
  2222. --*/
  2223. {
  2224. CHAR Line[21];
  2225. CHAR ValueString[21];
  2226. if (Money != FALSE) {
  2227. DwFormatMoney(ValueString, sizeof(ValueString), Value);
  2228. } else {
  2229. snprintf(ValueString, sizeof(ValueString), "%d", Value);
  2230. }
  2231. snprintf(Line, sizeof(Line), "%s%*s", Name, 20 - strlen(Name), ValueString);
  2232. SwMoveCursor(stdout, 9, Row);
  2233. SwPrintInColor(Background, Foreground, Line);
  2234. return;
  2235. }
  2236. VOID
  2237. DwDrawLocation (
  2238. PSTR Location
  2239. )
  2240. /*++
  2241. Routine Description:
  2242. This routine redraws the current location.
  2243. Arguments:
  2244. Location - Supplies the name of the location.
  2245. Return Value:
  2246. None.
  2247. --*/
  2248. {
  2249. INT Column;
  2250. INT Length;
  2251. SwClearRegion(ConsoleColorDarkBlue, ConsoleColorGray, 33, 2, 13, 1);
  2252. //
  2253. // Center the text.
  2254. //
  2255. Length = strlen(Location);
  2256. if (Length <= 14) {
  2257. Column = 39 - (Length / 2);
  2258. } else {
  2259. Column = 33;
  2260. }
  2261. SwMoveCursor(stdout, Column, 2);
  2262. SwPrintInColor(ConsoleColorDarkBlue, ConsoleColorWhite, Location);
  2263. return;
  2264. }
  2265. VOID
  2266. DwRedrawSpace (
  2267. PDW_CONTEXT Context
  2268. )
  2269. /*++
  2270. Routine Description:
  2271. This routine redraws the players remaining inventory capacity.
  2272. Arguments:
  2273. Context - Supplies a pointer to the application context.
  2274. Return Value:
  2275. None.
  2276. --*/
  2277. {
  2278. SwMoveCursor(stdout, 73, 0);
  2279. SwPrintInColor(ConsoleColorGray,
  2280. ConsoleColorDarkBlue,
  2281. "%4d",
  2282. Context->Space);
  2283. return;
  2284. }
  2285. VOID
  2286. DwFlashText (
  2287. PDW_CONTEXT Context,
  2288. PSTR String,
  2289. INT XPosition,
  2290. INT YPosition,
  2291. CONSOLE_COLOR Background,
  2292. CONSOLE_COLOR Foreground,
  2293. CONSOLE_COLOR FlashForeground,
  2294. BOOL Fast
  2295. )
  2296. /*++
  2297. Routine Description:
  2298. This routine flashes text for a little while, for an exciting visual
  2299. experience.
  2300. Arguments:
  2301. Context - Supplies a pointer to the application context.
  2302. String - Supplies a pointer to the string to flash.
  2303. XPosition - Supplies the column to draw the text at.
  2304. YPosition - Supplies the row to draw the text at.
  2305. Background - Supplies the background color.
  2306. Foreground - Supplies the initial and final foreground color.
  2307. FlashForeground - Supplies the accent foreground color.
  2308. Fast - Supplies a boolean indicating whether to flash fast or slow.
  2309. Return Value:
  2310. None.
  2311. --*/
  2312. {
  2313. INT Count;
  2314. CONSOLE_COLOR CurrentColor;
  2315. INT Delay;
  2316. INT Index;
  2317. if (Fast != FALSE) {
  2318. Delay = DW_FLASH_FAST_MICROSECONDS;
  2319. } else {
  2320. Delay = DW_FLASH_SLOW_MICROSECONDS;
  2321. }
  2322. Count = DwRandom(5, 7) | 1;
  2323. for (Index = 0; Index < Count; Index += 1) {
  2324. SwMoveCursor(stdout, XPosition, YPosition);
  2325. if ((Index & 0x1) != 0) {
  2326. CurrentColor = FlashForeground;
  2327. } else {
  2328. CurrentColor = Foreground;
  2329. }
  2330. SwPrintInColor(Background, CurrentColor, "%s", String);
  2331. SwSleep(Delay);
  2332. }
  2333. return;
  2334. }
  2335. INT
  2336. DwDisplayHighScores (
  2337. PDW_CONTEXT Context
  2338. )
  2339. /*++
  2340. Routine Description:
  2341. This routine displays the "high" scores. Oh yeah.
  2342. Arguments:
  2343. Context - Supplies a pointer to the player context.
  2344. Return Value:
  2345. 0 if the caller does not want to play again.
  2346. 1 if the caller does want to play again (yay).
  2347. -1 if the answer could not be read.
  2348. --*/
  2349. {
  2350. CHAR Amount[16];
  2351. INT Answer;
  2352. CONSOLE_COLOR Color;
  2353. struct tm *CurrentTime;
  2354. PSTR Dead;
  2355. PDW_HIGH_SCORE_ENTRY Entry;
  2356. INT Index;
  2357. CHAR Line[81];
  2358. PSTR Name;
  2359. INT PlayerValue;
  2360. INT Result;
  2361. INT Row;
  2362. BOOL Save;
  2363. DW_HIGH_SCORES Scores;
  2364. time_t Time;
  2365. Save = FALSE;
  2366. DwLoadHighScores(Context, &Scores);
  2367. //
  2368. // If the player made the high score list, ask for their name and remember
  2369. // to save the high scores list.
  2370. //
  2371. PlayerValue = Context->Cash + Context->Bank - Context->Debt;
  2372. Entry = &(Scores.Entries[DW_HIGH_SCORE_COUNT - 1]);
  2373. Name = (PSTR)(Entry->Name);
  2374. if (((Entry->Flags & DW_HIGH_SCORE_VALID) == 0) ||
  2375. (PlayerValue > Entry->Amount)) {
  2376. DwClearLowerRegion();
  2377. SwMoveCursor(stdout, 1, 18);
  2378. SwPrintInColor(ConsoleColorGray,
  2379. ConsoleColorBlack,
  2380. DwString(DwsMadeHighScores));
  2381. SwPrintInColor(ConsoleColorGray,
  2382. ConsoleColorDarkMagenta,
  2383. DwString(DwsNamePrompt));
  2384. Result = DwReadString(Context, Name, DW_HIGH_SCORE_NAME_SIZE);
  2385. if (Result != 0) {
  2386. strncpy(Name, DwString(DwsAnonymous), DW_HIGH_SCORE_NAME_SIZE);
  2387. }
  2388. Save = TRUE;
  2389. } else {
  2390. strncpy(Name, DwString(DwsYou), DW_HIGH_SCORE_NAME_SIZE);
  2391. }
  2392. //
  2393. // Add the remainder of the player's details, always to the last score.
  2394. //
  2395. Entry->Flags = DW_HIGH_SCORE_VALID | DW_HIGH_SCORE_YOU;
  2396. if (Context->Health != 0) {
  2397. Entry->Flags |= DW_HIGH_SCORE_ALIVE;
  2398. }
  2399. Entry->Amount = PlayerValue;
  2400. Time = time(NULL);
  2401. CurrentTime = localtime(&Time);
  2402. if (CurrentTime != NULL) {
  2403. Entry->Month = CurrentTime->tm_mon + 1;
  2404. Entry->Day = CurrentTime->tm_mday;
  2405. Entry->Year = CurrentTime->tm_year + 1900;
  2406. }
  2407. //
  2408. // Sort the scores now.
  2409. //
  2410. qsort(&(Scores.Entries[0]),
  2411. DW_HIGH_SCORE_COUNT,
  2412. sizeof(DW_HIGH_SCORE_ENTRY),
  2413. DwCompareHighScores);
  2414. //
  2415. // Print the high scores screen.
  2416. //
  2417. SwClearRegion(ConsoleColorDarkBlue, ConsoleColorDefault, 0, 0, 80, 25);
  2418. SwMoveCursor(stdout, 28, 0);
  2419. SwPrintInColor(ConsoleColorDarkBlue,
  2420. ConsoleColorYellow,
  2421. DwString(DwsHighScoresTitle));
  2422. Row = 4;
  2423. for (Index = 0; Index < DW_HIGH_SCORE_COUNT; Index += 1) {
  2424. Entry = &(Scores.Entries[Index]);
  2425. if ((Entry->Flags & DW_HIGH_SCORE_VALID) == 0) {
  2426. continue;
  2427. }
  2428. DwFormatMoney(Amount, sizeof(Amount), Entry->Amount);
  2429. Entry->Name[DW_HIGH_SCORE_NAME_SIZE - 1] = '\0';
  2430. Dead = "";
  2431. if ((Entry->Flags & DW_HIGH_SCORE_ALIVE) == 0) {
  2432. Dead = DwString(DwsHighScoreDead);
  2433. }
  2434. snprintf(Line,
  2435. sizeof(Line),
  2436. DwString(DwsHighScoreFormat),
  2437. Amount,
  2438. Entry->Month,
  2439. Entry->Day,
  2440. Entry->Year,
  2441. Entry->Name,
  2442. Dead);
  2443. Color = ConsoleColorGray;
  2444. if ((Entry->Flags & DW_HIGH_SCORE_YOU) != 0) {
  2445. Color = ConsoleColorWhite;
  2446. Entry->Flags &= ~DW_HIGH_SCORE_YOU;
  2447. }
  2448. SwMoveCursor(stdout, 6, Row);
  2449. SwPrintInColor(ConsoleColorDarkBlue, Color, "%s", Line);
  2450. Row += 1;
  2451. }
  2452. //
  2453. // Save the high scores file.
  2454. //
  2455. if (Save != FALSE) {
  2456. Scores.Checksum = 0;
  2457. Scores.Checksum = DwChecksum(&Scores, sizeof(DW_HIGH_SCORES));
  2458. DwHighScoresFileIo(&Scores, FALSE);
  2459. }
  2460. //
  2461. // Let's do it all again!
  2462. //
  2463. DwDrawBottomPrompt(DwString(DwsPlayAgain));
  2464. Answer = DwReadYesNoAnswer(Context, NULL, NULL);
  2465. return Answer;
  2466. }
  2467. VOID
  2468. DwLoadHighScores (
  2469. PDW_CONTEXT Context,
  2470. PDW_HIGH_SCORES Scores
  2471. )
  2472. /*++
  2473. Routine Description:
  2474. This routine loads the high scores, or initializes a new set.
  2475. Arguments:
  2476. Context - Supplies a pointer to the player context.
  2477. Scores - Supplies a pointer where the loaded stores will be returned.
  2478. Return Value:
  2479. None. If high scores could not be read, new ones will be initialized.
  2480. --*/
  2481. {
  2482. ULONG Checksum;
  2483. INT Result;
  2484. //
  2485. // Try to load the high scores and validate the CRC32.
  2486. //
  2487. Result = DwHighScoresFileIo(Scores, TRUE);
  2488. if ((Result == 0) && (Scores->Magic == DW_HIGH_SCORE_MAGIC)) {
  2489. Checksum = Scores->Checksum;
  2490. Scores->Checksum = 0;
  2491. if (DwChecksum(Scores, sizeof(DW_HIGH_SCORES)) == Checksum) {
  2492. Scores->Checksum = Checksum;
  2493. return;
  2494. }
  2495. }
  2496. //
  2497. // Either the scores didn't load or the CRC didn't match, so initialize a
  2498. // new set.
  2499. //
  2500. memset(Scores, 0, sizeof(DW_HIGH_SCORES));
  2501. Scores->Magic = DW_HIGH_SCORE_MAGIC;
  2502. return;
  2503. }
  2504. INT
  2505. DwHighScoresFileIo (
  2506. PDW_HIGH_SCORES Scores,
  2507. BOOL Load
  2508. )
  2509. /*++
  2510. Routine Description:
  2511. This routine attempts to load or save the high scores file.
  2512. Arguments:
  2513. Scores - Supplies a pointer to the high scores structure.
  2514. Load - Supplies a boolean indicating whether to load (TRUE) or store
  2515. (FALSE) the file.
  2516. Return Value:
  2517. 0 on success.
  2518. Returns an error number on failure.
  2519. --*/
  2520. {
  2521. PSTR Access;
  2522. PUCHAR Bytes;
  2523. FILE *File;
  2524. PSTR Home;
  2525. INT Index;
  2526. UCHAR Pad;
  2527. CHAR Path[256];
  2528. ssize_t Size;
  2529. Home = getenv("HOME");
  2530. if (Home == NULL) {
  2531. Home = ".";
  2532. }
  2533. snprintf(Path, sizeof(Path), "%s/.dwsco", Home);
  2534. Access = "wb";
  2535. if (Load != FALSE) {
  2536. Access = "rb";
  2537. }
  2538. File = fopen(Path, Access);
  2539. if (File == NULL) {
  2540. return errno;
  2541. }
  2542. Bytes = (PUCHAR)Scores;
  2543. if (Load != FALSE) {
  2544. Size = fread(Scores, 1, sizeof(DW_HIGH_SCORES), File);
  2545. //
  2546. // When the stakes are this high it's important to have truly
  2547. // bulletproof security.
  2548. //
  2549. Pad = 0x56;
  2550. for (Index = 0; Index < sizeof(DW_HIGH_SCORES); Index += 1) {
  2551. Bytes[Index] ^= Pad;
  2552. Pad += 1;
  2553. }
  2554. } else {
  2555. Pad = 0x56;
  2556. for (Index = 0; Index < sizeof(DW_HIGH_SCORES); Index += 1) {
  2557. Bytes[Index] ^= Pad;
  2558. Pad += 1;
  2559. }
  2560. Size = fwrite(Scores, 1, sizeof(DW_HIGH_SCORES), File);
  2561. }
  2562. fclose(File);
  2563. if (Size != sizeof(DW_HIGH_SCORES)) {
  2564. if (errno == 0) {
  2565. return -1;
  2566. }
  2567. return errno;
  2568. }
  2569. return 0;
  2570. }
  2571. int
  2572. DwCompareHighScores (
  2573. const void *LeftPointer,
  2574. const void *RightPointer
  2575. )
  2576. /*++
  2577. Routine Description:
  2578. This routine compares two high score entries.
  2579. Arguments:
  2580. LeftPointer - Supplies the left high score pointer to compare.
  2581. RightPointer - Supplies the right pointer to compare.
  2582. Return Value:
  2583. <0 if the left is less than the right.
  2584. 0 if the left is equal to the right.
  2585. >0 if the left is greater than the right.
  2586. --*/
  2587. {
  2588. PDW_HIGH_SCORE_ENTRY Left;
  2589. LONG LeftValue;
  2590. PDW_HIGH_SCORE_ENTRY Right;
  2591. LONG RightValue;
  2592. Left = (PDW_HIGH_SCORE_ENTRY)LeftPointer;
  2593. LeftValue = Left->Amount;
  2594. Right = (PDW_HIGH_SCORE_ENTRY)RightPointer;
  2595. RightValue = Right->Amount;
  2596. //
  2597. // First compare invalid entries.
  2598. //
  2599. if ((Left->Flags & DW_HIGH_SCORE_VALID) == 0) {
  2600. LeftValue = -999999999;
  2601. }
  2602. if ((Right->Flags & DW_HIGH_SCORE_VALID) == 0) {
  2603. RightValue = -999999999;
  2604. }
  2605. //
  2606. // Higher scores should be first in the list.
  2607. //
  2608. if (LeftValue < RightValue) {
  2609. return 1;
  2610. } else if (LeftValue > RightValue) {
  2611. return -1;
  2612. }
  2613. return 0;
  2614. }
  2615. VOID
  2616. DwFormatMoney (
  2617. PSTR String,
  2618. UINTN StringSize,
  2619. INT Value
  2620. )
  2621. /*++
  2622. Routine Description:
  2623. This routine prints a money value, with thousands separators.
  2624. Arguments:
  2625. String - Supplies a pointer to the output buffer.
  2626. StringSize - Supplies the size of the output buffer string in bytes.
  2627. Value - Supplies the value to print.
  2628. Return Value:
  2629. None.
  2630. --*/
  2631. {
  2632. INT Billions;
  2633. INT Millions;
  2634. INT Ones;
  2635. INT Thousands;
  2636. if (Value < 0) {
  2637. Value = -Value;
  2638. *String = '-';
  2639. String += 1;
  2640. StringSize -= 1;
  2641. }
  2642. Ones = Value;
  2643. Billions = Value / 1000000000;
  2644. Ones = Value % 1000000000;
  2645. Millions = Ones / 1000000;
  2646. Ones = Ones % 1000000;
  2647. Thousands = Ones / 1000;
  2648. Ones = Ones % 1000;
  2649. if (Billions != 0) {
  2650. snprintf(String,
  2651. StringSize,
  2652. "$%d,%03d,%03d,%03d",
  2653. Billions,
  2654. Millions,
  2655. Thousands,
  2656. Ones);
  2657. } else if (Millions != 0) {
  2658. snprintf(String,
  2659. StringSize,
  2660. "$%d,%03d,%03d",
  2661. Millions,
  2662. Thousands,
  2663. Ones);
  2664. } else if (Thousands != 0) {
  2665. snprintf(String, StringSize, "$%d,%03d", Thousands, Ones);
  2666. } else {
  2667. snprintf(String, StringSize, "$%d", Ones);
  2668. }
  2669. return;
  2670. }
  2671. INT
  2672. DwReadCharacterSet (
  2673. PDW_CONTEXT Context,
  2674. PSTR Set
  2675. )
  2676. /*++
  2677. Routine Description:
  2678. This routine reads a character from a given permissible set.
  2679. Arguments:
  2680. Context - Supplies a pointer to the application context.
  2681. Set - Supplies a pointer to a null terminated string containing the set of
  2682. acceptable characters.
  2683. Return Value:
  2684. Returns the character received on success.
  2685. -1 on read failure.
  2686. --*/
  2687. {
  2688. INT Character;
  2689. PSTR Current;
  2690. while (TRUE) {
  2691. Character = DwReadCharacter(Context);
  2692. if (Character == -1) {
  2693. break;
  2694. }
  2695. if (isupper(Character)) {
  2696. Character = tolower(Character);
  2697. }
  2698. //
  2699. // See if the character received was in the allowed set.
  2700. //
  2701. Current = Set;
  2702. while (*Current != '\0') {
  2703. if (*Current == Character) {
  2704. return Character;
  2705. }
  2706. Current += 1;
  2707. }
  2708. }
  2709. return Character;
  2710. }
  2711. INT
  2712. DwReadQuantity (
  2713. PDW_CONTEXT Context
  2714. )
  2715. /*++
  2716. Routine Description:
  2717. This routine reads a numeric quantity in from standard input.
  2718. Arguments:
  2719. Context - Supplies a pointer to the application context.
  2720. Return Value:
  2721. Returns the numeric value read in on success.
  2722. -1 on failure.
  2723. --*/
  2724. {
  2725. CHAR Buffer[9];
  2726. INT Character;
  2727. INT Index;
  2728. INT ScanIndex;
  2729. INT Value;
  2730. Index = 0;
  2731. while (TRUE) {
  2732. Character = DwReadCharacter(Context);
  2733. if (Character == EOF) {
  2734. return -1;
  2735. }
  2736. if (isdigit(Character)) {
  2737. if (Index < 8) {
  2738. Buffer[Index] = Character;
  2739. Index += 1;
  2740. SwPrintInColor(ConsoleColorGray,
  2741. ConsoleColorBlack,
  2742. "%c",
  2743. Character);
  2744. }
  2745. } else if ((Character == Context->Backspace) || (Character == '\b')) {
  2746. if (Index > 0) {
  2747. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, "\b \b");
  2748. Index -= 1;
  2749. }
  2750. } else if ((Character == '\r') || (Character == '\n')) {
  2751. break;
  2752. }
  2753. }
  2754. if (Index == 0) {
  2755. return -1;
  2756. }
  2757. Value = 0;
  2758. for (ScanIndex = 0; ScanIndex < Index; ScanIndex += 1) {
  2759. Value = (Value * 10) + (Buffer[ScanIndex] - '0');
  2760. }
  2761. return Value;
  2762. }
  2763. INT
  2764. DwReadString (
  2765. PDW_CONTEXT Context,
  2766. PSTR String,
  2767. INT StringSize
  2768. )
  2769. /*++
  2770. Routine Description:
  2771. This routine reads a generic string from standard input.
  2772. Arguments:
  2773. Context - Supplies a pointer to the application context.
  2774. String - Supplies a pointer where the string will be returned on success.
  2775. StringSize - Supplies string buffer size.
  2776. Return Value:
  2777. 0 on success.
  2778. -1 on failure.
  2779. --*/
  2780. {
  2781. INT Character;
  2782. INT Index;
  2783. Index = 0;
  2784. while (TRUE) {
  2785. Character = DwReadCharacter(Context);
  2786. if (Character == EOF) {
  2787. return -1;
  2788. }
  2789. if ((Character == Context->Backspace) || (Character == '\b')) {
  2790. if (Index > 0) {
  2791. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, "\b \b");
  2792. Index -= 1;
  2793. }
  2794. } else if ((Character == '\r') || (Character == '\n')) {
  2795. break;
  2796. } else {
  2797. if (Index < StringSize - 1) {
  2798. String[Index] = Character;
  2799. Index += 1;
  2800. SwPrintInColor(ConsoleColorGray,
  2801. ConsoleColorBlack,
  2802. "%c",
  2803. Character);
  2804. }
  2805. }
  2806. }
  2807. if (Index == 0) {
  2808. return -1;
  2809. }
  2810. String[Index] = '\0';
  2811. return 0;
  2812. }
  2813. INT
  2814. DwReadYesNoAnswer (
  2815. PDW_CONTEXT Context,
  2816. PSTR Exposition,
  2817. PSTR Prompt
  2818. )
  2819. /*++
  2820. Routine Description:
  2821. This routine reads a yes/no answer from the user.
  2822. Arguments:
  2823. Context - Supplies a pointer to the application context.
  2824. Exposition - Supplies an optional pointer to a line of black text to put
  2825. first. Most questions don't have this.
  2826. Prompt - Supplies an optional pointer to the question to ask.
  2827. Return Value:
  2828. 0 if the user said no.
  2829. 1 if the user said yes.
  2830. -1 on failure.
  2831. --*/
  2832. {
  2833. INT Answer;
  2834. INT Row;
  2835. if (Prompt != NULL) {
  2836. DwClearLowerRegion();
  2837. }
  2838. Row = 18;
  2839. if (Exposition != NULL) {
  2840. SwMoveCursor(stdout, 1, Row);
  2841. Row = 19;
  2842. SwPrintInColor(ConsoleColorGray, ConsoleColorBlack, Exposition);
  2843. }
  2844. if (Prompt != NULL) {
  2845. SwMoveCursor(stdout, 1, Row);
  2846. SwPrintInColor(ConsoleColorGray, ConsoleColorDarkMagenta, Prompt);
  2847. }
  2848. Answer = DwReadCharacterSet(Context, "yn");
  2849. if (Answer == EOF) {
  2850. return Answer;
  2851. }
  2852. if (Answer == 'y') {
  2853. return 1;
  2854. }
  2855. return 0;
  2856. }
  2857. INT
  2858. DwReadCharacter (
  2859. PDW_CONTEXT Context
  2860. )
  2861. /*++
  2862. Routine Description:
  2863. This routine reads a key from standard in.
  2864. Arguments:
  2865. Context - Supplies a pointer to the application context.
  2866. Return Value:
  2867. Returns the character received on success.
  2868. EOF on read failure or end of input.
  2869. --*/
  2870. {
  2871. INT Character;
  2872. Character = SwReadInputCharacter();
  2873. if (Character == 0x3) {
  2874. Context->ExitRequested = TRUE;
  2875. return -1;
  2876. }
  2877. return Character;
  2878. }
  2879. INT
  2880. DwRandom (
  2881. INT Minimum,
  2882. INT Maximum
  2883. )
  2884. /*++
  2885. Routine Description:
  2886. This routine gets a random value within the given range.
  2887. Arguments:
  2888. Minimum - Supplies the minimum random value.
  2889. Maximum - Supplies the maximum random value, exclusive.
  2890. Return Value:
  2891. Returns a random value between the specified minimum and maximum.
  2892. --*/
  2893. {
  2894. ssize_t BytesRead;
  2895. INT Value;
  2896. //
  2897. // Prefer the random source if it exists.
  2898. //
  2899. if (DwRandomSource >= 0) {
  2900. do {
  2901. BytesRead = read(DwRandomSource, &Value, 2);
  2902. } while ((BytesRead < 0) && (errno == EINTR));
  2903. if (BytesRead == sizeof(Value)) {
  2904. return Minimum + (Value % (Maximum - Minimum));
  2905. }
  2906. }
  2907. return Minimum + (rand() % (Maximum - Minimum));
  2908. }
  2909. ULONG
  2910. DwChecksum (
  2911. PVOID Buffer,
  2912. ULONG Size
  2913. )
  2914. /*++
  2915. Routine Description:
  2916. This routine gets a checksum value for the high scores file.
  2917. Arguments:
  2918. Buffer - Supplies a pointer to the buffer to checksum.
  2919. Size - Supplies the size in bytes of the checksum.
  2920. Return Value:
  2921. Returns the checksum of the file.
  2922. --*/
  2923. {
  2924. PUCHAR Bytes;
  2925. ULONG Sum;
  2926. //
  2927. // Borrow an algorithm from ELF: Sum = (Sum * 33) + Byte for each byte.
  2928. //
  2929. Bytes = Buffer;
  2930. Sum = 0;
  2931. while (Size != 0) {
  2932. Sum = ((Sum << 5) + Sum) + *Bytes;
  2933. Bytes += 1;
  2934. Size -= 1;
  2935. }
  2936. return Sum;
  2937. }