ularn_win.c 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258
  1. /* =============================================================================
  2. * PROGRAM: ularn
  3. * FILENAME: ularn_win.c
  4. *
  5. * DESCRIPTION:
  6. * This module contains all operating system dependant code for input and
  7. * display update.
  8. * Each version of ularn should provide a different implementation of this
  9. * module.
  10. *
  11. * This is the Windows 32 window display and input module.
  12. *
  13. * =============================================================================
  14. * EXPORTED VARIABLES
  15. *
  16. * nonap : Set to true if no time delays are to be used.
  17. * nosignal : Set if ctrl-C is to be trapped to prevent exit.
  18. * enable_scroll : Probably superfluous
  19. * yrepcount : Repeat count for input commands.
  20. *
  21. * =============================================================================
  22. * EXPORTED FUNCTIONS
  23. *
  24. * init_app : Initialise the app
  25. * close_app : Close the app and free resources
  26. * get_normal_input : Get the next command input
  27. * get_prompt_input : Get input in response to a question
  28. * get_password_input : Get a password
  29. * get_num_input : Geta number
  30. * get_dir_input : Get a direction
  31. * set_display : Set the display mode
  32. * UpdateStatus : Update the status display
  33. * UpdateEffects : Update the effects display
  34. * UpdateStatusAndEffects : Update both status and effects display
  35. * ClearText : Clear the text output area
  36. * UlarnBeep : Make a beep
  37. * Cursor : Set the cursor location
  38. * Printc : Print a single character
  39. * Print : Print a string
  40. * Printf : Print a formatted string
  41. * Standout : Print a string is standout format
  42. * SetFormat : Set the output text format
  43. * ClearEOL : Clear to end of line
  44. * ClearEOPage : Clear to end of page
  45. * show1cell : Show 1 cell on the map
  46. * showplayer : Show the player on the map
  47. * showcell : Show the area around the player
  48. * drawscreen : Redraw the screen
  49. * draws : Redraw a section of the screen
  50. * mapeffect : Draw a directional effect
  51. * magic_effect_frames : Get the number of animation frames in a magic fx
  52. * magic_effect : Draw a frame in a magic fx
  53. * nap : Delay for a specified number of milliseconds
  54. * GetUser : Get the username and user id.
  55. *
  56. * =============================================================================
  57. */
  58. #include <stdio.h>
  59. #include <windows.h> // include important windows stuff
  60. #include "header.h"
  61. #include "ularn_game.h"
  62. #include "config.h"
  63. #include "dungeon.h"
  64. #include "itm.h"
  65. #include "monster.h"
  66. #include "player.h"
  67. #include "ularn_win.h"
  68. #include "ularnpc.rh"
  69. //
  70. // Defines for windows
  71. //
  72. #define WINDOW_CLASS_NAME "ULARN_WINCLASS" // class name
  73. // Default size of the ularn window in characters
  74. #define WINDOW_WIDTH 80
  75. #define WINDOW_HEIGHT 25
  76. #define SEPARATOR_WIDTH 8
  77. #define SEPARATOR_HEIGHT 8
  78. #define BORDER_SIZE 8
  79. /* =============================================================================
  80. * Exported variables
  81. */
  82. int nonap = 0;
  83. int nosignal = 0;
  84. char enable_scroll = 0;
  85. int yrepcount = 0;
  86. /* =============================================================================
  87. * Local variables
  88. */
  89. #define M_NONE 0
  90. #define M_SHIFT 1
  91. #define M_CTRL 2
  92. #define M_ASCII 255
  93. #define MAX_KEY_BINDINGS 3
  94. struct KeyCodeType {
  95. int VirtKey;
  96. int ModKey;
  97. };
  98. #define NUM_DIRS 8
  99. static ActionType DirActions[NUM_DIRS] = {
  100. ACTION_MOVE_WEST, ACTION_MOVE_EAST, ACTION_MOVE_SOUTH,
  101. ACTION_MOVE_NORTH, ACTION_MOVE_NORTHEAST, ACTION_MOVE_NORTHWEST,
  102. ACTION_MOVE_SOUTHEAST, ACTION_MOVE_SOUTHWEST};
  103. /* Default keymap */
  104. /* Allow up to MAX_KEY_BINDINGS per action */
  105. static struct KeyCodeType KeyMap[ACTION_COUNT][MAX_KEY_BINDINGS] = {
  106. {{0, 0}, {0, 0}, {0, 0}}, // ACTION_NULL
  107. {{'~', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_DIAG
  108. {{'h', M_ASCII},
  109. {VK_NUMPAD4, M_NONE},
  110. {VK_LEFT, M_NONE}}, // ACTION_MOVE_WEST
  111. {{'H', M_ASCII}, {VK_LEFT, M_SHIFT}, {0, 0}}, // ACTION_RUN_WEST
  112. {{'l', M_ASCII},
  113. {VK_NUMPAD6, M_NONE},
  114. {VK_RIGHT, M_NONE}}, // ACTION_MOVE_EAST,
  115. {{'L', M_ASCII}, {VK_RIGHT, M_SHIFT}, {0, 0}}, // ACTION_RUN_EAST,
  116. {{'j', M_ASCII},
  117. {VK_NUMPAD2, M_NONE},
  118. {VK_DOWN, M_NONE}}, // ACTION_MOVE_SOUTH,
  119. {{'J', M_ASCII}, {VK_DOWN, M_SHIFT}, {0, 0}}, // ACTION_RUN_SOUTH,
  120. {{'k', M_ASCII},
  121. {VK_NUMPAD8, M_NONE},
  122. {VK_UP, M_NONE}}, // ACTION_MOVE_NORTH,
  123. {{'K', M_ASCII}, {VK_UP, M_SHIFT}, {0, 0}}, // ACTION_RUN_NORTH,
  124. {{'u', M_ASCII},
  125. {VK_NUMPAD9, M_NONE},
  126. {VK_PRIOR, M_NONE}}, // ACTION_MOVE_NORTHEAST,
  127. {{'U', M_ASCII}, {VK_PRIOR, M_SHIFT}, {0, 0}}, // ACTION_RUN_NORTHEAST,
  128. {{'y', M_ASCII},
  129. {VK_NUMPAD7, M_NONE},
  130. {VK_HOME, M_NONE}}, // ACTION_MOVE_NORTHWEST,
  131. {{'Y', M_ASCII}, {VK_HOME, M_SHIFT}, {0, 0}}, // ACTION_RUN_NORTHWEST,
  132. {{'n', M_ASCII},
  133. {VK_NUMPAD3, M_NONE},
  134. {VK_NEXT, M_NONE}}, // ACTION_MOVE_SOUTHEAST,
  135. {{'N', M_ASCII}, {VK_NEXT, M_SHIFT}, {0, 0}}, // ACTION_RUN_SOUTHEAST,
  136. {{'b', M_ASCII},
  137. {VK_NUMPAD1, M_NONE},
  138. {VK_END, M_NONE}}, // ACTION_MOVE_SOUTHWEST,
  139. {{'B', M_ASCII}, {VK_END, M_SHIFT}, {0, 0}}, // ACTION_RUN_SOUTHWEST,
  140. {{'.', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_WAIT,
  141. {{' ', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_NONE,
  142. {{'w', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_WIELD,
  143. {{'W', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_WEAR,
  144. {{'r', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_READ,
  145. {{'q', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_QUAFF,
  146. {{'d', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_DROP,
  147. {{'c', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_CAST_SPELL,
  148. {{'o', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_OPEN_DOOR,
  149. {{'C', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_CLOSE_DOOR,
  150. {{'O', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_OPEN_CHEST,
  151. {{'i', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_INVENTORY,
  152. {{'e', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_EAT_COOKIE,
  153. {{'\\', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_LIST_SPELLS,
  154. {{'?', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_HELP,
  155. {{'S', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_SAVE,
  156. {{'Z', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_TELEPORT,
  157. {{'^', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_IDENTIFY_TRAPS,
  158. {{'_', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_BECOME_CREATOR,
  159. {{'+', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_CREATE_ITEM,
  160. {{'-', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_TOGGLE_WIZARD,
  161. {{'`', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_DEBUG_MODE,
  162. {{'T', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_REMOVE_ARMOUR,
  163. {{'g', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_PACK_WEIGHT,
  164. {{'v', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_VERSION,
  165. {{'Q', M_ASCII}, {0, 0}, {0, 0}}, // ACTION_QUIT,
  166. {{'R', M_CTRL}, {0, 0}, {0, 0}}, // ACTION_REDRAW_SCREEN,
  167. {{'P', M_ASCII}, {0, 0}, {0, 0}} // ACTION_SHOW_TAX
  168. };
  169. static struct KeyCodeType RunKeyMap = {VK_NUMPAD5, M_NONE};
  170. //
  171. // Variables for windows
  172. //
  173. #define INITIAL_WIDTH 400
  174. #define INITIAL_HEIGHT 300
  175. static HINSTANCE my_instance = 0;
  176. static HWND frame_window_handle = NULL; // save the window handle
  177. static HINSTANCE main_instance = NULL; // save the instance
  178. static HDC frame_dc = 0;
  179. static LOGFONT LogFont;
  180. static HFONT LarnFont = 0;
  181. static int use_palette = 0;
  182. static int peused[256];
  183. static PALETTEENTRY pe[256];
  184. static LOGPALETTE *palette = NULL;
  185. static HPALETTE HPalette;
  186. static int CaretActive = 0;
  187. static int TileWidth = 32;
  188. static int TileHeight = 32;
  189. static int CharHeight;
  190. static int CharWidth;
  191. static int LarnWindowLeft = 0;
  192. static int LarnWindowTop = 0;
  193. static int LarnWindowWidth = INITIAL_WIDTH;
  194. static int LarnWindowHeight = INITIAL_HEIGHT;
  195. static int LarnWindowMaximized = 0;
  196. static int MinWindowWidth;
  197. static int MinWindowHeight;
  198. static int Runkey;
  199. static ActionType Event;
  200. static int GotChar;
  201. static char EventChar;
  202. //
  203. // player id file
  204. //
  205. static char *PIDName = LIBDIR "\\vlarn.pid";
  206. #define FIRST_PID 1001
  207. //
  208. // ularn.ini file for window position & font selection
  209. //
  210. static char *IniName = "vlarn.ini";
  211. //
  212. // Bitmaps for tiles
  213. //
  214. static char *TileBMName = LIBDIR "\\vlarn_gfx.bmp";
  215. static HBITMAP TileBitmap = NULL;
  216. static HDC TileDC;
  217. /* Tiles for different character classes, (female, male) */
  218. static int PlayerTiles[8][2] = {
  219. {165, 181}, /* Ogre */
  220. {166, 182}, /* Wizard */
  221. {167, 183}, /* Klingon */
  222. {168, 184}, /* Elf */
  223. {169, 185}, /* Rogue */
  224. {170, 186}, /* Adventurer */
  225. {171, 187}, /* Dwarf */
  226. {172, 188} /* Rambo */
  227. };
  228. #define TILE_CURSOR1 174
  229. #define TILE_CURSOR2 190
  230. #define WALL_TILES 352
  231. /* Tiles for directional effects */
  232. static int EffectTile[EFFECT_COUNT][9] = {
  233. {191, 198, 196, 194, 192, 195, 193, 197, 199},
  234. {191, 206, 204, 202, 200, 203, 201, 205, 207},
  235. {191, 214, 212, 210, 208, 211, 209, 213, 215},
  236. {191, 222, 220, 218, 216, 219, 217, 221, 223},
  237. {191, 230, 228, 226, 224, 227, 225, 229, 231}};
  238. #define MAX_MAGICFX_FRAME 8
  239. struct MagicEffectDataType {
  240. int Overlay; /* 0 = no overlay, 1 = overlay */
  241. int Frames; /* Number of frames in the effect */
  242. int Tile1[MAX_MAGICFX_FRAME]; /* The primary tile for this frame */
  243. int Tile2[MAX_MAGICFX_FRAME]; /* Only used for overlay effects */
  244. };
  245. static struct MagicEffectDataType magicfx_tile[MAGIC_COUNT] = {
  246. /* Sparkle */
  247. {1, /* Overlay this on current tile */
  248. 8,
  249. {240, 241, 242, 243, 244, 245, 246, 247},
  250. {248, 249, 250, 251, 252, 253, 254, 255}},
  251. /* Sleep */
  252. {0, 6, {256, 272, 288, 304, 320, 336, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  253. /* Web */
  254. {0, 6, {257, 273, 289, 305, 321, 337, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  255. /* Phantasmal forces */
  256. {0, 6, {258, 274, 290, 306, 322, 338, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  257. /* Cloud kill */
  258. {0, 6, {259, 275, 291, 307, 323, 339, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  259. /* Vaporize rock */
  260. {0, 6, {260, 276, 292, 308, 324, 340, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  261. /* Dehydrate */
  262. {0, 6, {261, 277, 293, 309, 325, 341, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  263. /* Drain life */
  264. {0, 6, {262, 278, 294, 310, 326, 342, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  265. /* Flood */
  266. {0, 6, {263, 279, 295, 311, 327, 343, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  267. /* Finger of death */
  268. {0, 6, {264, 280, 296, 312, 328, 344, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  269. /* Teleport away */
  270. {0, 6, {265, 281, 297, 313, 329, 345, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  271. /* Magic fire */
  272. {0, 6, {266, 282, 298, 314, 330, 346, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  273. /* Make wall */
  274. {0, 6, {267, 283, 299, 315, 331, 347, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  275. /* Summon demon */
  276. {0, 6, {268, 284, 300, 316, 332, 348, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
  277. /* Annihilate (scroll) */
  278. {0, 6, {269, 285, 301, 317, 333, 349, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}};
  279. //
  280. // Current display mode
  281. //
  282. DisplayModeType CurrentDisplayMode = DISPLAY_TEXT;
  283. //
  284. // Map window position and size
  285. //
  286. static int MapLeft;
  287. static int MapTop;
  288. static int MapWidth;
  289. static int MapHeight;
  290. static int MapTileLeft = 0;
  291. static int MapTileTop = 0;
  292. static int MapTileWidth;
  293. static int MapTileHeight;
  294. //
  295. // Status lines window position and size
  296. //
  297. static int StatusLeft;
  298. static int StatusTop;
  299. static int StatusWidth;
  300. static int StatusHeight;
  301. //
  302. // Effects window position and size
  303. //
  304. static int EffectsLeft;
  305. static int EffectsTop;
  306. static int EffectsWidth;
  307. static int EffectsHeight;
  308. //
  309. // Message window position and size
  310. //
  311. static int MessageLeft;
  312. static int MessageTop;
  313. static int MessageWidth;
  314. static int MessageHeight;
  315. //
  316. // Text window position, size
  317. //
  318. static int TextLeft;
  319. static int TextTop;
  320. static int TextWidth;
  321. static int TextHeight;
  322. static int ShowTextBorder;
  323. // =============================================================================
  324. // Text mode stuff
  325. //
  326. #define LINE_LENGTH 80
  327. typedef char TextLine[LINE_LENGTH + 1];
  328. typedef FormatType FormatLine[LINE_LENGTH + 1];
  329. //
  330. // Messages
  331. //
  332. #define MAX_MSG_LINES 5
  333. static TextLine MessageChr[MAX_MSG_LINES];
  334. static FormatLine MessageFmt[MAX_MSG_LINES];
  335. static FormatType CurrentMsgFormat;
  336. static int MsgCursorX = 1;
  337. static int MsgCursorY = 1;
  338. //
  339. // Text
  340. //
  341. #define MAX_TEXT_LINES 25
  342. #define TEXT_LINE_LENGTH 80
  343. static TextLine TextChr[MAX_TEXT_LINES];
  344. static FormatLine TextFmt[MAX_TEXT_LINES];
  345. static FormatType CurrentTextFormat;
  346. static int TextCursorX = 1;
  347. static int TextCursorY = 1;
  348. //
  349. // Generalised text buffer
  350. // Top left corner is x=1, y=1
  351. //
  352. static TextLine *Text;
  353. static FormatLine *Format;
  354. static FormatType CurrentFormat;
  355. static int CursorX = 1;
  356. static int CursorY = 1;
  357. static int MaxLine;
  358. static int TTop;
  359. static int TLeft;
  360. static int TWidth;
  361. static int THeight;
  362. //
  363. // The monster to use for showing mimics. Changes every 10 turns.
  364. //
  365. static MonsterIdType mimicmonst = MIMIC;
  366. /* =============================================================================
  367. * Local functions
  368. */
  369. /* =============================================================================
  370. * FUNCTION: ReadIniFile
  371. *
  372. * DESCRIPTION:
  373. * Read the ularn.ini file that specifies the window position and font to use.
  374. * The file is written on exit so that the ularn window position and font will
  375. * are restored the next time the game is played.
  376. *
  377. * PARAMETERS:
  378. *
  379. * None.
  380. *
  381. * RETURN VALUE:
  382. *
  383. * 0 => Read of ini file failed
  384. * 1 => Successfully read ini file.
  385. */
  386. int ReadIniFile(void) {
  387. FILE *fp;
  388. char Line[256];
  389. char String[256];
  390. int len;
  391. int f1, f2, f3, f4, f5, f6, f7, f8;
  392. fp = fopen(IniName, "r");
  393. if (fp == NULL)
  394. /*
  395. * .ini file doesn't exist yet
  396. */
  397. return 0;
  398. fgets(Line, 256, fp);
  399. if (strncmp(Line, "[ULARN WINDOW]", 14) != 0)
  400. /*
  401. * Not what we expect here so exit. The file will be re-created
  402. * when the program exits
  403. */
  404. return 0;
  405. fscanf(fp, "%d %d %d %d %s\n", &LarnWindowLeft, &LarnWindowTop,
  406. &LarnWindowWidth, &LarnWindowHeight, String);
  407. LarnWindowMaximized = strncmp(String, "MAXIMIZED", 9) == 0;
  408. fgets(Line, 256, fp);
  409. if (strncmp(Line, "[ULARN FONT]", 12) != 0)
  410. /*
  411. * Not what we expect here so exit. The file will be re-created
  412. * when the program exits.
  413. */
  414. return 0;
  415. fgets(Line, 256, fp);
  416. len = strlen(Line) - 1;
  417. while ((len > 0) && (!isprint(Line[len]))) {
  418. Line[len] = 0;
  419. len--;
  420. }
  421. strncpy(LogFont.lfFaceName, Line, LF_FACESIZE);
  422. fscanf(fp, "%ld %ld %ld %ld %ld\n", &LogFont.lfWidth, &LogFont.lfHeight,
  423. &LogFont.lfEscapement, &LogFont.lfOrientation, &LogFont.lfWeight);
  424. fscanf(fp, "%d %d %d %d %d %d %d %d\n", &f1, &f2, &f3, &f4, &f5, &f6, &f7,
  425. &f8);
  426. LogFont.lfItalic = (BYTE)f1;
  427. LogFont.lfUnderline = (BYTE)f2;
  428. LogFont.lfStrikeOut = (BYTE)f3;
  429. LogFont.lfCharSet = (BYTE)f4;
  430. LogFont.lfOutPrecision = (BYTE)f5;
  431. LogFont.lfClipPrecision = (BYTE)f6;
  432. LogFont.lfQuality = (BYTE)f7;
  433. LogFont.lfPitchAndFamily = (BYTE)f8;
  434. return 1;
  435. }
  436. /* =============================================================================
  437. * FUNCTION: WriteIniFile
  438. *
  439. * DESCRIPTION:
  440. * This function writes the ini file that specifies the window position and
  441. * font to use for ularn.
  442. * This is to be called on program exit so that ularn will remember the player's
  443. * preferred window position and font size.
  444. *
  445. * PARAMETERS:
  446. *
  447. * None.
  448. *
  449. * RETURN VALUE:
  450. *
  451. * None.
  452. */
  453. void WriteIniFile(void) {
  454. FILE *fp;
  455. int Top, Left;
  456. int Width, Height;
  457. RECT WRect;
  458. WINDOWPLACEMENT wp;
  459. int maximized;
  460. fp = fopen(IniName, "w");
  461. /* Outputthe window position */
  462. GetWindowRect(frame_window_handle, &WRect);
  463. Left = WRect.left;
  464. Top = WRect.top;
  465. Width = (WRect.right - WRect.left) - 1;
  466. Height = (WRect.bottom - WRect.top) - 1;
  467. wp.length = sizeof(WINDOWPLACEMENT);
  468. GetWindowPlacement(frame_window_handle, &wp);
  469. maximized = (wp.showCmd == SW_SHOWMAXIMIZED);
  470. fprintf(fp, "[ULARN WINDOW]\n");
  471. fprintf(fp, "%d %d %d %d %s\n", Left, Top, Width, Height,
  472. (maximized) ? "MAXIMIZED" : "NORMAL");
  473. /* Output the font specification */
  474. GetObject(LarnFont, sizeof(LOGFONT), &LogFont);
  475. fprintf(fp, "[ULARN FONT]\n");
  476. fprintf(fp, "%s\n", LogFont.lfFaceName);
  477. fprintf(fp, "%ld %ld %ld %ld %ld\n", LogFont.lfWidth, LogFont.lfHeight,
  478. LogFont.lfEscapement, LogFont.lfOrientation, LogFont.lfWeight);
  479. fprintf(fp, "%d %d %d %d %d %d %d %d\n", LogFont.lfItalic,
  480. LogFont.lfUnderline, LogFont.lfStrikeOut, LogFont.lfCharSet,
  481. LogFont.lfOutPrecision, LogFont.lfClipPrecision, LogFont.lfQuality,
  482. LogFont.lfPitchAndFamily);
  483. fclose(fp);
  484. }
  485. /* =============================================================================
  486. * FUNCTION: AboutDialogProc
  487. *
  488. * DESCRIPTION:
  489. * Windows callback for the about dialog.
  490. *
  491. * PARAMETERS:
  492. *
  493. * hwnd : The window handle for the message
  494. *
  495. * msg : The message received
  496. *
  497. * wparam : The wparam for the message
  498. *
  499. * lparam : The lparam for the message.
  500. *
  501. * RETURN VALUE:
  502. *
  503. * 0 if the message was handled by this procedure.
  504. */
  505. static BOOL CALLBACK AboutDialogProc(HWND hwnd, UINT msg, WPARAM wparam,
  506. LPARAM lparam) {
  507. RECT WRect;
  508. RECT DRect;
  509. int WindowWidth;
  510. int WindowHeight;
  511. int DialogWidth;
  512. int DialogHeight;
  513. int DialogX;
  514. int DialogY;
  515. //
  516. // What is the message
  517. //
  518. switch (msg) {
  519. case WM_INITDIALOG:
  520. // hwndFocus = (HWND) wparam; // handle of control to receive focus
  521. // lInitParam = lparam;
  522. //
  523. // Do initialization stuff here
  524. //
  525. GetWindowRect(frame_window_handle, &WRect);
  526. WindowWidth = (WRect.right - WRect.left) + 1;
  527. WindowHeight = (WRect.bottom - WRect.top) + 1;
  528. GetWindowRect(hwnd, &DRect);
  529. DialogWidth = (DRect.right - DRect.left) + 1;
  530. DialogHeight = (DRect.bottom - DRect.top) + 1;
  531. DialogX = WRect.left + (WindowWidth - DialogWidth) / 2;
  532. DialogY = WRect.top + (WindowHeight - DialogHeight) / 2;
  533. SetWindowPos(hwnd, NULL, DialogX, DialogY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  534. return TRUE;
  535. case WM_COMMAND: {
  536. // int wNotifyCode = HIWORD(wparam); // notification code
  537. int wID = LOWORD(wparam); // item, control, or accelerator identifier
  538. // HWND hwndCtl = (HWND) lparam; // handle of control
  539. switch (wID) {
  540. case IDOK:
  541. EndDialog(hwnd, IDOK);
  542. return TRUE;
  543. default:
  544. break;
  545. }
  546. break;
  547. }
  548. default:
  549. break;
  550. }
  551. return FALSE;
  552. }
  553. /* =============================================================================
  554. * FUNCTION: DoAbout
  555. *
  556. * DESCRIPTION:
  557. * SHow the about dialog.
  558. *
  559. * PARAMETERS:
  560. *
  561. * None.
  562. *
  563. * RETURN VALUE:
  564. *
  565. * None.
  566. */
  567. void DoAbout(void) {
  568. DialogBox(main_instance, MAKEINTRESOURCE(IDD_ABOUT), frame_window_handle,
  569. AboutDialogProc);
  570. }
  571. /* =============================================================================
  572. * FUNCTION: calc_scroll
  573. *
  574. * DESCRIPTION:
  575. * Calculate the new scroll position of the map based on the player's current
  576. * position.
  577. *
  578. * PARAMETERS:
  579. *
  580. * None.
  581. *
  582. * RETURN VALUE:
  583. *
  584. * true if the new scroll position differs from the previous scroll position.
  585. */
  586. static int calc_scroll(void) {
  587. int ox, oy;
  588. ox = MapTileLeft;
  589. oy = MapTileTop;
  590. if (MapTileHeight < MAXY) {
  591. MapTileTop = playery - MapTileHeight / 2;
  592. if (MapTileTop < 0)
  593. MapTileTop = 0;
  594. if ((MapTileTop + MapTileHeight) > MAXY)
  595. MapTileTop = MAXY - MapTileHeight;
  596. } else
  597. MapTileTop = 0;
  598. if (MapTileWidth < MAXX) {
  599. MapTileLeft = playerx - MapTileWidth / 2;
  600. if (MapTileLeft < 0)
  601. MapTileLeft = 0;
  602. if ((MapTileLeft + MapTileWidth) > MAXX)
  603. MapTileLeft = MAXX - MapTileWidth;
  604. } else
  605. MapTileLeft = 0;
  606. //
  607. // return true if the map requires scrolling
  608. //
  609. return (MapTileLeft != ox) || (MapTileTop != oy);
  610. }
  611. /* =============================================================================
  612. * FUNCTION: CalcMinWindowSize
  613. *
  614. * DESCRIPTION:
  615. * Calculate the minimum window size.
  616. * The new minimum windows isze is stored in MinWindowWidth and MinWindowHeight.
  617. * If the current window size is smaller than this then it is resized.
  618. *
  619. * PARAMETERS:
  620. *
  621. * DC : The device context for the window.
  622. *
  623. * RETURN VALUE:
  624. *
  625. * None.
  626. */
  627. static void CalcMinWindowSize(HDC DC) {
  628. TEXTMETRIC tm;
  629. //
  630. // Calculate the new minimum window size
  631. //
  632. GetTextMetrics(DC, &tm);
  633. CharWidth = tm.tmAveCharWidth;
  634. CharHeight = tm.tmHeight;
  635. MinWindowWidth = WINDOW_WIDTH * CharWidth + 2 * GetSystemMetrics(SM_CXFRAME);
  636. MinWindowHeight = WINDOW_HEIGHT * CharHeight + 2 * SEPARATOR_HEIGHT +
  637. 2 * GetSystemMetrics(SM_CYFRAME) +
  638. GetSystemMetrics(SM_CYMENU) +
  639. GetSystemMetrics(SM_CYCAPTION);
  640. //
  641. // Update the window size
  642. //
  643. if (MinWindowWidth > LarnWindowWidth)
  644. LarnWindowWidth = MinWindowWidth;
  645. if (MinWindowHeight > LarnWindowHeight)
  646. LarnWindowHeight = MinWindowHeight;
  647. SetWindowPos(frame_window_handle, NULL, 0, 0, LarnWindowWidth,
  648. LarnWindowHeight, SWP_NOMOVE | SWP_NOZORDER);
  649. InvalidateRect(frame_window_handle, NULL, 1);
  650. }
  651. /*
  652. * Repaint flag to force redraw of everything, not just deltas
  653. */
  654. static int Repaint = 0;
  655. /* =============================================================================
  656. * FUNCTION: PaintStatus
  657. *
  658. * DESCRIPTION:
  659. * Paint the status area.
  660. *
  661. * PARAMETERS:
  662. *
  663. * DC : The device context for the painting
  664. *
  665. * RETURN VALUE:
  666. *
  667. * None.
  668. */
  669. static void PaintStatus(HDC DC) {
  670. char Line[81];
  671. char Buf[81];
  672. int i;
  673. RECT StatusRect;
  674. if (Repaint) {
  675. StatusRect.left = StatusLeft;
  676. StatusRect.top = StatusTop;
  677. StatusRect.right = StatusLeft + StatusWidth;
  678. StatusRect.bottom = StatusTop + StatusHeight;
  679. FillRect(DC, &StatusRect, GetStockObject(WHITE_BRUSH));
  680. }
  681. //
  682. // Build the top status line
  683. //
  684. Line[0] = 0;
  685. /* Spells */
  686. if (c[SPELLMAX] > 99)
  687. sprintf(Buf, "Spells:%3ld(%3ld)", c[SPELLS], c[SPELLMAX]);
  688. else
  689. sprintf(Buf, "Spells:%3ld(%2ld) ", c[SPELLS], c[SPELLMAX]);
  690. strcat(Line, Buf);
  691. /* AC, WC */
  692. sprintf(Buf, " AC: %-3ld WC: %-3ld Level", c[AC], c[WCLASS]);
  693. strcat(Line, Buf);
  694. /* Level */
  695. if (c[LEVEL] > 99)
  696. sprintf(Buf, "%3ld", c[LEVEL]);
  697. else
  698. sprintf(Buf, " %-2ld", c[LEVEL]);
  699. strcat(Line, Buf);
  700. /* Exp, class */
  701. sprintf(Buf, " Exp: %-9ld %s", c[EXPERIENCE], class[c[LEVEL] - 1]);
  702. strcat(Line, Buf);
  703. TextOut(DC, StatusLeft, StatusTop, Line, strlen(Line));
  704. //
  705. // Format the second line of the status
  706. //
  707. sprintf(Buf, "%ld (%ld)", c[HP], c[HPMAX]);
  708. sprintf(Line,
  709. "HP: %11s STR=%-2ld INT=%-2ld WIS=%-2ld CON=%-2ld DEX=%-2ld "
  710. "CHA=%-2ld LV:",
  711. Buf, c[STRENGTH] + c[STREXTRA], c[INTELLIGENCE], c[WISDOM],
  712. c[CONSTITUTION], c[DEXTERITY], c[CHARISMA]);
  713. if ((level == 0) || (wizard))
  714. c[TELEFLAG] = 0;
  715. if (c[TELEFLAG])
  716. strcat(Line, " ?");
  717. else
  718. strcat(Line, levelname[level]);
  719. sprintf(Buf, " Gold: %-8ld", c[GOLD]);
  720. strcat(Line, Buf);
  721. TextOut(DC, StatusLeft, StatusTop + CharHeight, Line, strlen(Line));
  722. //
  723. // Mark all character values as displayed.
  724. //
  725. c[TMP] = c[STRENGTH] + c[STREXTRA];
  726. for (i = 0; i < 100; i++)
  727. cbak[i] = c[i];
  728. }
  729. /* Effects strings */
  730. static struct bot_side_def {
  731. int typ;
  732. char *string;
  733. } bot_data[] = {{STEALTH, "Stealth "}, {UNDEADPRO, "Undead Pro"},
  734. {SPIRITPRO, "Spirit Pro"}, {CHARMCOUNT, "Charm "},
  735. {TIMESTOP, "Time Stop "}, {HOLDMONST, "Hold Monst"},
  736. {GIANTSTR, "Giant Str "}, {FIRERESISTANCE, "Fire Resit"},
  737. {DEXCOUNT, "Dexterity "}, {STRCOUNT, "Strength "},
  738. {SCAREMONST, "Scare "}, {HASTESELF, "Haste Self"},
  739. {CANCELLATION, "Cancel "}, {INVISIBILITY, "Invisible "},
  740. {ALTPRO, "Protect 3 "}, {PROTECTIONTIME, "Protect 2 "},
  741. {WTW, "Wall-Walk "}};
  742. /* =============================================================================
  743. * FUNCTION: PaintEffects
  744. *
  745. * DESCRIPTION:
  746. * Paint the effects display.
  747. *
  748. * PARAMETERS:
  749. *
  750. * DC : The DC to be painted.
  751. *
  752. * RETURN VALUE:
  753. *
  754. * None.
  755. */
  756. static void PaintEffects(HDC DC) {
  757. int i, idx;
  758. int WasSet;
  759. int IsSet;
  760. RECT EffectsRect;
  761. if (Repaint) {
  762. EffectsRect.left = EffectsLeft;
  763. EffectsRect.top = EffectsTop;
  764. EffectsRect.right = EffectsLeft + EffectsWidth;
  765. EffectsRect.bottom = EffectsTop + EffectsHeight;
  766. FillRect(DC, &EffectsRect, GetStockObject(WHITE_BRUSH));
  767. }
  768. for (i = 0; i < 17; i++) {
  769. idx = bot_data[i].typ;
  770. WasSet = (cbak[idx] != 0);
  771. IsSet = (c[idx] != 0);
  772. if ((Repaint) || (IsSet != WasSet)) {
  773. if (IsSet)
  774. TextOut(DC, EffectsLeft, EffectsTop + i * CharHeight,
  775. bot_data[i].string, strlen(bot_data[i].string));
  776. else
  777. TextOut(DC, EffectsLeft, EffectsTop + i * CharHeight, " ", 10);
  778. }
  779. cbak[idx] = c[idx];
  780. }
  781. }
  782. /* =============================================================================
  783. * FUNCTION: GetTile
  784. *
  785. * DESCRIPTION:
  786. * Get the tile to be displayed for a location on the map.
  787. *
  788. * PARAMETERS:
  789. *
  790. * x : The x coordinate for the tile
  791. *
  792. * y : The y coordiante for the tile
  793. *
  794. * TileId : This is set to the tile to be displayed for (x, y).
  795. *
  796. * RETURN VALUE:
  797. *
  798. * None.
  799. */
  800. static void GetTile(int x, int y, int *TileId) {
  801. MonsterIdType k;
  802. if ((x == playerx) && (y == playery) && (c[BLINDCOUNT] == 0)) {
  803. //
  804. // This is the square containing the player and the players isn't
  805. // blind, so return the player tile.
  806. //
  807. *TileId = PlayerTiles[class_num][(int)sex];
  808. return;
  809. }
  810. //
  811. // Work out what is here
  812. //
  813. if (know[x][y] == OUNKNOWN) {
  814. //
  815. // The player doesn't know what is at this position.
  816. //
  817. *TileId = objtilelist[OUNKNOWN];
  818. } else {
  819. k = mitem[x][y].mon;
  820. if (k != 0) {
  821. if ((c[BLINDCOUNT] == 0) && (((stealth[x][y] & STEALTH_SEEN) != 0) ||
  822. ((stealth[x][y] & STEALTH_AWAKE) != 0))) {
  823. //
  824. // There is a monster here and the player is not blind and the
  825. // monster is seen or awake.
  826. //
  827. if (k == MIMIC) {
  828. if ((gtime % 10) == 0)
  829. while ((mimicmonst = rnd(MAXMONST)) == INVISIBLESTALKER)
  830. ;
  831. *TileId = monsttilelist[mimicmonst];
  832. } else if ((k == INVISIBLESTALKER) && (c[SEEINVISIBLE] == 0))
  833. *TileId = objtilelist[(int)know[x][y]];
  834. else if ((k >= DEMONLORD) && (k <= LUCIFER) && (c[EYEOFLARN] == 0))
  835. /* demons are invisible if not have the eye */
  836. *TileId = objtilelist[(int)know[x][y]];
  837. else
  838. *TileId = monsttilelist[k];
  839. } /* can see monster */
  840. else
  841. /*
  842. * The monster at this location is not known to the player, so show
  843. * the tile for the item at this location
  844. */
  845. *TileId = objtilelist[(int)know[x][y]];
  846. } /* monster here */
  847. else {
  848. k = know[x][y];
  849. *TileId = objtilelist[k];
  850. }
  851. }
  852. /* Handle walls */
  853. if (*TileId == objtilelist[OWALL])
  854. *TileId = WALL_TILES + iarg[x][y];
  855. }
  856. /* =============================================================================
  857. * FUNCTION: PaintMap
  858. *
  859. * DESCRIPTION:
  860. * Repaint the map.
  861. *
  862. * PARAMETERS:
  863. *
  864. * DC : The device context to be painted.
  865. *
  866. * RETURN VALUE:
  867. *
  868. * None.
  869. */
  870. static void PaintMap(HDC DC) {
  871. int x, y;
  872. int sx, sy;
  873. int mx, my;
  874. int TileId;
  875. int TileX;
  876. int TileY;
  877. mx = MapTileLeft + MapTileWidth;
  878. my = MapTileTop + MapTileHeight;
  879. if (my > MAXY)
  880. my = MAXY;
  881. if (mx > MAXX)
  882. mx = MAXX;
  883. sx = 0;
  884. for (x = MapTileLeft; x < mx; x++) {
  885. sy = 0;
  886. for (y = MapTileTop; y < my; y++) {
  887. GetTile(x, y, &TileId);
  888. TileX = (TileId % 16) * TileWidth;
  889. TileY = (TileId / 16) * TileHeight;
  890. BitBlt(DC, MapLeft + sx * TileWidth, MapTop + sy * TileHeight, TileWidth,
  891. TileHeight, TileDC, TileX, TileY, SRCCOPY);
  892. sy++;
  893. }
  894. sx++;
  895. }
  896. sx = playerx - MapTileLeft;
  897. sy = playery - MapTileTop;
  898. if ((sx >= 0) && (sx < MapTileWidth) && (sy >= 0) && (sy < MapTileHeight)) {
  899. TileId = TILE_CURSOR1;
  900. TileX = (TileId % 16) * TileWidth;
  901. TileY = (TileId / 16) * TileHeight;
  902. BitBlt(DC, MapLeft + sx * TileWidth, MapTop + sy * TileHeight, TileWidth,
  903. TileHeight, TileDC, TileX, TileY, SRCAND);
  904. TileId = TILE_CURSOR2;
  905. TileX = (TileId % 16) * TileWidth;
  906. TileY = (TileId / 16) * TileHeight;
  907. BitBlt(DC, MapLeft + sx * TileWidth, MapTop + sy * TileHeight, TileWidth,
  908. TileHeight, TileDC, TileX, TileY, SRCPAINT);
  909. }
  910. }
  911. /* =============================================================================
  912. * FUNCTION: PaintTextWindow
  913. *
  914. * DESCRIPTION:
  915. * Repaint the window in text mode.
  916. *
  917. * PARAMETERS:
  918. *
  919. * DC : The device contect to be painted.
  920. *
  921. * RETURN VALUE:
  922. *
  923. * None.
  924. */
  925. static void PaintTextWindow(HDC DC) {
  926. int sx, ex, y;
  927. FormatType Fmt;
  928. RECT TextRect;
  929. HPEN Pen;
  930. TextRect.left = TLeft;
  931. TextRect.top = TTop;
  932. TextRect.right = TLeft + TWidth;
  933. TextRect.bottom = TTop + THeight;
  934. if (CurrentDisplayMode == DISPLAY_TEXT) {
  935. if (ShowTextBorder) {
  936. Pen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
  937. SelectObject(DC, Pen);
  938. SelectObject(DC, GetStockObject(NULL_BRUSH));
  939. RoundRect(DC, TLeft - 8, TTop - 8, TLeft + TWidth + 9, TTop + THeight + 9,
  940. 16, 16);
  941. DeleteObject(Pen);
  942. } else
  943. GetClientRect(frame_window_handle, &TextRect);
  944. }
  945. FillRect(DC, &TextRect, GetStockObject(WHITE_BRUSH));
  946. for (y = 0; y < MaxLine; y++) {
  947. sx = 0;
  948. while (sx < LINE_LENGTH) {
  949. Fmt = Format[y][sx];
  950. ex = sx;
  951. while ((ex < LINE_LENGTH) && (Format[y][ex] == Fmt))
  952. ex++;
  953. switch (Fmt) {
  954. case FORMAT_NORMAL:
  955. SetTextColor(DC, RGB(0, 0, 0));
  956. break;
  957. case FORMAT_STANDOUT:
  958. SetTextColor(DC, RGB(255, 0, 0));
  959. break;
  960. case FORMAT_STANDOUT2:
  961. SetTextColor(DC, RGB(0, 127, 0));
  962. break;
  963. case FORMAT_STANDOUT3:
  964. SetTextColor(DC, RGB(0, 0, 255));
  965. break;
  966. default:
  967. break;
  968. }
  969. TextOut(DC, TLeft + sx * CharWidth, TTop + y * CharHeight, Text[y] + sx,
  970. ex - sx);
  971. sx = ex;
  972. }
  973. }
  974. }
  975. /* =============================================================================
  976. * FUNCTION: PaintMapWindow
  977. *
  978. * DESCRIPTION:
  979. * Repaint the window in map mode
  980. *
  981. * PARAMETERS:
  982. *
  983. * DC : The device context to be painted.
  984. *
  985. * RETURN VALUE:
  986. *
  987. * None.
  988. */
  989. static void PaintMapWindow(HDC DC) {
  990. RECT Rect;
  991. HBRUSH LightBrush;
  992. HBRUSH MidBrush;
  993. HBRUSH DarkBrush;
  994. //
  995. // Draw separators between the different window areas
  996. //
  997. LightBrush = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
  998. MidBrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
  999. DarkBrush = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
  1000. //
  1001. // Message area
  1002. //
  1003. Rect.left = MessageLeft;
  1004. Rect.top = MessageTop - SEPARATOR_HEIGHT;
  1005. Rect.right = MessageLeft + MessageWidth;
  1006. Rect.bottom = Rect.top + 2;
  1007. FillRect(DC, &Rect, LightBrush);
  1008. Rect.top = Rect.bottom;
  1009. Rect.bottom = Rect.top + 4;
  1010. FillRect(DC, &Rect, MidBrush);
  1011. Rect.top = Rect.bottom;
  1012. Rect.bottom = Rect.top + 2;
  1013. FillRect(DC, &Rect, DarkBrush);
  1014. //
  1015. // Status area
  1016. //
  1017. Rect.left = StatusLeft;
  1018. Rect.top = StatusTop - SEPARATOR_HEIGHT;
  1019. Rect.right = StatusLeft + StatusWidth;
  1020. Rect.bottom = Rect.top + 2;
  1021. FillRect(DC, &Rect, LightBrush);
  1022. Rect.top = Rect.bottom;
  1023. Rect.bottom = Rect.top + 4;
  1024. FillRect(DC, &Rect, MidBrush);
  1025. Rect.top = Rect.bottom;
  1026. Rect.bottom = Rect.top + 2;
  1027. FillRect(DC, &Rect, DarkBrush);
  1028. //
  1029. // Effects area
  1030. //
  1031. Rect.left = EffectsLeft - SEPARATOR_WIDTH;
  1032. Rect.top = EffectsTop;
  1033. Rect.right = Rect.left + 2;
  1034. Rect.bottom = EffectsTop + EffectsHeight;
  1035. FillRect(DC, &Rect, LightBrush);
  1036. Rect.left = Rect.right;
  1037. Rect.right = Rect.left + 4;
  1038. Rect.bottom = EffectsTop + EffectsHeight + 2;
  1039. FillRect(DC, &Rect, MidBrush);
  1040. Rect.left = Rect.right;
  1041. Rect.right = Rect.left + 2;
  1042. Rect.bottom = EffectsTop + EffectsHeight;
  1043. FillRect(DC, &Rect, DarkBrush);
  1044. PaintStatus(DC);
  1045. PaintEffects(DC);
  1046. PaintMap(DC);
  1047. PaintTextWindow(DC);
  1048. DeleteObject(LightBrush);
  1049. DeleteObject(MidBrush);
  1050. DeleteObject(DarkBrush);
  1051. }
  1052. /* =============================================================================
  1053. * FUNCTION: PaintWindow
  1054. *
  1055. * DESCRIPTION:
  1056. * Repaint the window.
  1057. *
  1058. * PARAMETERS:
  1059. *
  1060. * DC : The device context to be painted
  1061. *
  1062. * RETURN VALUE:
  1063. *
  1064. * None.
  1065. */
  1066. static void PaintWindow(HDC DC) {
  1067. SetMapMode(DC, MM_TEXT);
  1068. SelectObject(DC, LarnFont);
  1069. if (use_palette) {
  1070. SelectPalette(DC, HPalette, 0);
  1071. RealizePalette(DC);
  1072. }
  1073. Repaint = 1;
  1074. if (CurrentDisplayMode == DISPLAY_MAP)
  1075. PaintMapWindow(DC);
  1076. else
  1077. PaintTextWindow(DC);
  1078. Repaint = 0;
  1079. }
  1080. /* =============================================================================
  1081. * FUNCTION: Resize
  1082. *
  1083. * DESCRIPTION:
  1084. * This procedure handles resizing the window in response to any event that
  1085. * requires the sub-window size and position to be recalculated.
  1086. *
  1087. * PARAMETERS:
  1088. *
  1089. * hwnd : The handle of the window being resized
  1090. *
  1091. * RETURN VALUE:
  1092. *
  1093. * None.
  1094. */
  1095. static void Resize(HWND hwnd) {
  1096. RECT CRect;
  1097. RECT WRect;
  1098. int ClientWidth;
  1099. int ClientHeight;
  1100. GetWindowRect(hwnd, &WRect);
  1101. LarnWindowWidth = (WRect.right - WRect.left) + 1;
  1102. LarnWindowHeight = (WRect.bottom - WRect.top) + 1;
  1103. GetClientRect(hwnd, &CRect);
  1104. ClientWidth = (CRect.right - CRect.left);
  1105. ClientHeight = (CRect.bottom - CRect.top);
  1106. //
  1107. // Calculate the message window size and position
  1108. //
  1109. MessageWidth = ClientWidth;
  1110. MessageHeight = CharHeight * MAX_MSG_LINES;
  1111. MessageLeft = 0;
  1112. MessageTop = ClientHeight - MessageHeight - 1;
  1113. //
  1114. // Calculate the Status window size and position
  1115. //
  1116. StatusLeft = 0;
  1117. StatusTop = (MessageTop - SEPARATOR_HEIGHT) - CharHeight * 2;
  1118. StatusWidth = ClientWidth;
  1119. StatusHeight = CharHeight * 2;
  1120. //
  1121. // Calculate the Effects window size and position
  1122. //
  1123. EffectsLeft = ClientWidth - CharWidth * 10;
  1124. EffectsTop = 0;
  1125. EffectsWidth = CharWidth * 10;
  1126. EffectsHeight = StatusTop - SEPARATOR_HEIGHT;
  1127. //
  1128. // Calculate the size and position of the map window
  1129. //
  1130. MapLeft = 0;
  1131. MapTop = 0;
  1132. MapWidth = EffectsLeft - SEPARATOR_WIDTH;
  1133. MapHeight = StatusTop - SEPARATOR_HEIGHT;
  1134. MapTileWidth = MapWidth / TileWidth;
  1135. MapTileHeight = MapHeight / TileHeight;
  1136. //
  1137. // Calculate the size and position of the text window
  1138. //
  1139. TextWidth = CharWidth * LINE_LENGTH;
  1140. TextHeight = CharHeight * MAX_TEXT_LINES;
  1141. TextLeft = (ClientWidth - TextWidth) / 2;
  1142. TextTop = (ClientHeight - TextHeight) / 2;
  1143. //
  1144. // Check if should draw a border around the text page when it is displayed
  1145. //
  1146. ShowTextBorder = (TextLeft >= BORDER_SIZE) && (TextTop >= BORDER_SIZE);
  1147. //
  1148. // If the map window is bigger than required to display the map, then centre
  1149. // the map in the window.
  1150. //
  1151. if (MapTileWidth > MAXX) {
  1152. MapTileWidth = MAXX;
  1153. MapLeft = (MapWidth - MapTileWidth * TileWidth) / 2;
  1154. }
  1155. if (MapTileHeight > MAXY) {
  1156. MapTileHeight = MAXY;
  1157. MapTop = (MapHeight - MapTileHeight * TileHeight) / 2;
  1158. }
  1159. if (CurrentDisplayMode == DISPLAY_MAP) {
  1160. TLeft = MessageLeft;
  1161. TTop = MessageTop;
  1162. TWidth = MessageWidth;
  1163. THeight = MessageHeight;
  1164. } else {
  1165. TLeft = TextLeft;
  1166. TTop = TextTop;
  1167. TWidth = TextWidth;
  1168. THeight = TextHeight;
  1169. }
  1170. //
  1171. // calculate the map scroll position for the current player position
  1172. //
  1173. calc_scroll();
  1174. //
  1175. // Force the window to redraw
  1176. //
  1177. InvalidateRect(hwnd, NULL, 1);
  1178. //
  1179. // Show the cursor if required
  1180. //
  1181. if (CaretActive) {
  1182. CreateCaret(frame_window_handle, NULL, 2, CharHeight);
  1183. ShowCaret(frame_window_handle);
  1184. SetCaretPos(TLeft + (CursorX - 1) * CharWidth,
  1185. TTop + (CursorY - 1) * CharHeight);
  1186. }
  1187. }
  1188. /* =============================================================================
  1189. * FUNCTION: WindowProc
  1190. *
  1191. * DESCRIPTION:
  1192. * This is the main message handler of the system.
  1193. *
  1194. * PARAMETERS:
  1195. *
  1196. * hwnd : The window handle for the message
  1197. *
  1198. * msg : The message received
  1199. *
  1200. * wparam : The wparam for the message
  1201. *
  1202. * lparam : The lparam for the message.
  1203. *
  1204. * RETURN VALUE:
  1205. *
  1206. * 0 If the message was handled by this function.
  1207. */
  1208. static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam,
  1209. LPARAM lparam) {
  1210. PAINTSTRUCT ps; // used in WM_PAINT
  1211. HDC hdc; // handle to a device context
  1212. //
  1213. // What is the message
  1214. //
  1215. switch (msg) {
  1216. case WM_CREATE:
  1217. //
  1218. // Do initialization stuff here
  1219. //
  1220. return 0;
  1221. case WM_INITMENU: {
  1222. HMENU hMenu = (HMENU)wparam;
  1223. CheckMenuItem(hMenu, IDM_DISPLAY_BEEP,
  1224. (nobeep) ? MF_UNCHECKED : MF_CHECKED);
  1225. break;
  1226. }
  1227. case WM_PAINT:
  1228. //
  1229. // Start painting
  1230. //
  1231. hdc = BeginPaint(hwnd, &ps);
  1232. PaintWindow(hdc);
  1233. //
  1234. // End painting
  1235. //
  1236. EndPaint(hwnd, &ps);
  1237. return 0;
  1238. case WM_CLOSE:
  1239. Event = ACTION_QUIT;
  1240. return 0;
  1241. case WM_DESTROY:
  1242. //
  1243. // kill the application
  1244. //
  1245. Event = ACTION_QUIT;
  1246. return 0;
  1247. case WM_GETMINMAXINFO: {
  1248. MINMAXINFO *mmi;
  1249. mmi = (MINMAXINFO *)lparam;
  1250. mmi->ptMinTrackSize.x = MinWindowWidth;
  1251. mmi->ptMinTrackSize.y = MinWindowHeight;
  1252. break;
  1253. }
  1254. case WM_SIZE:
  1255. Resize(hwnd);
  1256. break;
  1257. case WM_COMMAND: {
  1258. // int wNotifyCode = HIWORD(wparam); // notification code
  1259. int wID = LOWORD(wparam); // item, control, or accelerator identifier
  1260. // HWND hwndCtl = (HWND) lparam; // handle of control
  1261. switch (wID) {
  1262. case IDM_GAME_SAVE:
  1263. Event = ACTION_SAVE;
  1264. break;
  1265. case IDM_GAME_QUIT:
  1266. Event = ACTION_QUIT;
  1267. break;
  1268. case IDM_COMMANDS_WAIT:
  1269. Event = ACTION_WAIT;
  1270. break;
  1271. case IDM_COMMANDS_WIELD:
  1272. Event = ACTION_WIELD;
  1273. break;
  1274. case IDM_COMMANDS_WEAR:
  1275. Event = ACTION_WEAR;
  1276. break;
  1277. case IDM_COMMANDS_TAKEOFF:
  1278. Event = ACTION_REMOVE_ARMOUR;
  1279. break;
  1280. case IDM_COMMANDS_QUAFF:
  1281. Event = ACTION_QUAFF;
  1282. break;
  1283. case IDM_COMMANDS_READ:
  1284. Event = ACTION_READ;
  1285. break;
  1286. case IDM_COMMANDS_CAST:
  1287. Event = ACTION_CAST_SPELL;
  1288. break;
  1289. case IDM_COMMANDS_EAT:
  1290. Event = ACTION_EAT_COOKIE;
  1291. break;
  1292. case IDM_COMMANDS_DROP:
  1293. Event = ACTION_DROP;
  1294. break;
  1295. case IDM_COMMAND_CLOSE:
  1296. Event = ACTION_CLOSE_DOOR;
  1297. break;
  1298. case IDM_SHOW_DISCOVERIES:
  1299. Event = ACTION_LIST_SPELLS;
  1300. break;
  1301. case IDM_SHOW_INV:
  1302. Event = ACTION_INVENTORY;
  1303. break;
  1304. case IDM_SHOW_TAX:
  1305. Event = ACTION_SHOW_TAX;
  1306. break;
  1307. case IDM_SHOW_PACKWEIGHT:
  1308. Event = ACTION_PACK_WEIGHT;
  1309. break;
  1310. case IDM_DISPLAY_REDRAW:
  1311. Event = ACTION_REDRAW_SCREEN;
  1312. break;
  1313. case IDM_DISPLAY_BEEP:
  1314. nobeep = !nobeep;
  1315. break;
  1316. case IDM_DISPLAY_FONT: {
  1317. CHOOSEFONT cf;
  1318. HFONT newfont;
  1319. //
  1320. // Get the current font data
  1321. //
  1322. GetObject(LarnFont, sizeof(LOGFONT), &LogFont);
  1323. memset(&cf, 0, sizeof(CHOOSEFONT));
  1324. cf.lStructSize = sizeof(CHOOSEFONT);
  1325. cf.hwndOwner = frame_window_handle;
  1326. cf.hDC = NULL;
  1327. cf.lpLogFont = &LogFont;
  1328. cf.iPointSize = 0;
  1329. cf.Flags =
  1330. CF_SCREENFONTS | CF_FORCEFONTEXIST | CF_EFFECTS | CF_FIXEDPITCHONLY;
  1331. cf.rgbColors = 0;
  1332. cf.lCustData = 0;
  1333. cf.lpfnHook = 0;
  1334. cf.lpTemplateName = 0;
  1335. cf.hInstance = main_instance;
  1336. cf.lpszStyle = 0;
  1337. cf.nFontType = 0;
  1338. cf.nSizeMin = 6;
  1339. cf.nSizeMax = 24;
  1340. if (ChooseFont(&cf)) {
  1341. newfont = CreateFontIndirect(&LogFont);
  1342. SelectObject(frame_dc, newfont);
  1343. DeleteObject(LarnFont);
  1344. LarnFont = newfont;
  1345. CalcMinWindowSize(frame_dc);
  1346. }
  1347. break;
  1348. }
  1349. case IDM_HELP_HELP:
  1350. Event = ACTION_HELP;
  1351. break;
  1352. case IDM_HELP_VERSION:
  1353. Event = ACTION_VERSION;
  1354. break;
  1355. case IDM_HELP_ABOUT:
  1356. DoAbout();
  1357. break;
  1358. default:
  1359. break;
  1360. }
  1361. break;
  1362. }
  1363. case WM_KEYDOWN: {
  1364. ActionType Action;
  1365. int ModKey = 0;
  1366. int Found = 0;
  1367. int i;
  1368. if ((GetKeyState(VK_SHIFT) & 0xFFFE) != 0)
  1369. ModKey |= M_SHIFT;
  1370. if ((GetKeyState(VK_CONTROL) & 0xFFFE) != 0)
  1371. ModKey |= M_CTRL;
  1372. Action = ACTION_NULL;
  1373. while ((Action < ACTION_COUNT) && (!Found)) {
  1374. for (i = 0; i < MAX_KEY_BINDINGS; i++) {
  1375. if ((wparam == KeyMap[Action][i].VirtKey) &&
  1376. (KeyMap[Action][i].ModKey == ModKey))
  1377. Found = 1;
  1378. }
  1379. if (!Found)
  1380. Action++;
  1381. }
  1382. if (Found)
  1383. Event = Action;
  1384. else {
  1385. /* check run key */
  1386. if ((wparam == RunKeyMap.VirtKey) && (RunKeyMap.ModKey == ModKey))
  1387. Runkey = 1;
  1388. }
  1389. break;
  1390. }
  1391. case WM_CHAR: {
  1392. int chCharCode = (TCHAR)wparam; // character code
  1393. // int lKeyData = lparam; // key data
  1394. ActionType Action;
  1395. int Found = 0;
  1396. int i;
  1397. Action = ACTION_NULL;
  1398. while ((Action < ACTION_COUNT) && (!Found)) {
  1399. for (i = 0; i < MAX_KEY_BINDINGS; i++) {
  1400. if ((chCharCode == KeyMap[Action][i].VirtKey) &&
  1401. (KeyMap[Action][i].ModKey == M_ASCII))
  1402. Found = 1;
  1403. }
  1404. if (!Found)
  1405. Action++;
  1406. }
  1407. if (Found)
  1408. Event = Action;
  1409. else {
  1410. /* check run key */
  1411. if ((chCharCode == RunKeyMap.VirtKey) && (RunKeyMap.ModKey == M_ASCII))
  1412. Runkey = 1;
  1413. }
  1414. EventChar = (char)chCharCode;
  1415. GotChar = 1;
  1416. return 0;
  1417. }
  1418. default:
  1419. break;
  1420. }
  1421. //
  1422. // Process any messages that we didn't take care of
  1423. //
  1424. return DefWindowProc(hwnd, msg, wparam, lparam);
  1425. }
  1426. /* =============================================================================
  1427. * FUNCTION: calculate_palette
  1428. *
  1429. * DESCRIPTION:
  1430. * Calculate an palette for mapping from a 24 bpp bitmap to an 8 bpp bitmap.
  1431. * This sets the pe used and pe defined above.
  1432. *
  1433. * PARAMETERS:
  1434. *
  1435. * HSrcBitmap : A handle to the source 24 bit bitmap
  1436. *
  1437. * RETURN VALUE:
  1438. *
  1439. * None.
  1440. */
  1441. static void calculate_palette(HBITMAP HSrcBmp) {
  1442. int *Cube;
  1443. int ro, go, bo;
  1444. int r, g, b;
  1445. int ci;
  1446. int x, y;
  1447. int NumColours;
  1448. int MaxC, MaxI;
  1449. BITMAP SrcBitmap;
  1450. unsigned char *SrcAddr;
  1451. GetObject(HSrcBmp, sizeof(BITMAP), &SrcBitmap);
  1452. //
  1453. // Stage 1: Quantize 8x8x8 colours to 5x5x5 colours and fill in the 5x5x5
  1454. // colour cube.
  1455. //
  1456. Cube = malloc(32768 * sizeof(int));
  1457. ci = 0;
  1458. for (ro = 0; ro < 32; ro++) {
  1459. for (go = 0; go < 32; go++)
  1460. for (bo = 0; bo < 32; bo++)
  1461. Cube[ci++] = 0;
  1462. }
  1463. for (y = 0; y < SrcBitmap.bmHeight; y++) {
  1464. for (x = 0; x < SrcBitmap.bmWidth; x++) {
  1465. SrcAddr = ((unsigned char *)SrcBitmap.bmBits) +
  1466. SrcBitmap.bmWidthBytes * y + x * 3;
  1467. r = SrcAddr[2];
  1468. g = SrcAddr[1];
  1469. b = SrcAddr[0];
  1470. ro = r / 8;
  1471. go = g / 8;
  1472. bo = b / 8;
  1473. ci = ((ro * 32) + go) * 32 + bo;
  1474. Cube[ci]++;
  1475. }
  1476. }
  1477. //
  1478. // Mark all palette entries as unused.
  1479. //
  1480. for (ci = 0; ci < 256; ci++)
  1481. peused[ci] = 0;
  1482. //
  1483. // Set the entry for black and white to 0 and 255 respectively
  1484. //
  1485. peused[0] = 1;
  1486. pe[0].peRed = 0;
  1487. pe[0].peGreen = 0;
  1488. pe[0].peBlue = 0;
  1489. peused[255] = 1;
  1490. pe[255].peRed = 255;
  1491. pe[255].peGreen = 255;
  1492. pe[255].peBlue = 255;
  1493. //
  1494. // Take the most popular colours.
  1495. //
  1496. NumColours = 0;
  1497. while (NumColours < 236) {
  1498. MaxC = 0;
  1499. MaxI = 0;
  1500. //
  1501. // Traverse the colour cube, ignoring black (0) and white (32767) as these
  1502. // have already been locked into the palette.
  1503. //
  1504. for (ci = 1; ci < 32767; ci++) {
  1505. if (Cube[ci] > MaxC) {
  1506. MaxC = Cube[ci];
  1507. MaxI = ci;
  1508. }
  1509. }
  1510. if (MaxC > 0) {
  1511. peused[NumColours + 8] = 1;
  1512. pe[NumColours + 8].peRed = (unsigned char)(8 * ((MaxI & 0x7c00) >> 10));
  1513. pe[NumColours + 8].peGreen = (unsigned char)(8 * ((MaxI & 0x03e0) >> 5));
  1514. pe[NumColours + 8].peBlue = (unsigned char)(8 * (MaxI & 0x1f));
  1515. Cube[MaxI] = 0;
  1516. NumColours++;
  1517. } else
  1518. NumColours = 236;
  1519. }
  1520. palette = malloc(sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 256);
  1521. palette->palVersion = 0x300;
  1522. palette->palNumEntries = 256;
  1523. for (ci = 0; ci < 256; ci++)
  1524. palette->palPalEntry[ci] = pe[ci];
  1525. HPalette = CreatePalette(palette);
  1526. free(Cube);
  1527. }
  1528. /* =============================================================================
  1529. * Exported functions
  1530. */
  1531. /* =============================================================================
  1532. * FUNCTION: init_app
  1533. */
  1534. int init_app(HINSTANCE hinstance) {
  1535. WNDCLASS winclass; // this will hold the class we create
  1536. HWND hwnd; // generic window handle
  1537. int x, y;
  1538. HBITMAP TmpBitmap;
  1539. //
  1540. // First fill in the window class stucture
  1541. //
  1542. winclass.style = CS_OWNDC;
  1543. winclass.lpfnWndProc = WindowProc;
  1544. winclass.cbClsExtra = 0;
  1545. winclass.cbWndExtra = 0;
  1546. winclass.hInstance = hinstance;
  1547. winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  1548. winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  1549. winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  1550. winclass.lpszMenuName = NULL;
  1551. winclass.lpszClassName = WINDOW_CLASS_NAME;
  1552. //
  1553. // Register the window class
  1554. //
  1555. if (!RegisterClass(&winclass))
  1556. return 0;
  1557. //
  1558. // Store the instance for future reference
  1559. //
  1560. my_instance = hinstance;
  1561. //
  1562. // Create the window, note the use of WS_POPUP
  1563. //
  1564. hwnd = CreateWindow(
  1565. WINDOW_CLASS_NAME, // class
  1566. "ULarn Win32", // title
  1567. WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE | WS_THICKFRAME |
  1568. WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
  1569. 0, 0, // x,y
  1570. INITIAL_WIDTH, // width
  1571. INITIAL_HEIGHT, // height
  1572. NULL, // handle to parent
  1573. LoadMenu(hinstance, (const char *)IDM_MENU1), // handle to menu
  1574. hinstance, // instance
  1575. NULL);
  1576. if (hwnd == NULL)
  1577. return 0;
  1578. //
  1579. // Save the window handle and instance in a global
  1580. //
  1581. frame_window_handle = hwnd;
  1582. main_instance = hinstance;
  1583. frame_dc = GetDC(hwnd);
  1584. SetMapMode(frame_dc, MM_TEXT);
  1585. if (ReadIniFile()) {
  1586. LarnFont = CreateFontIndirect(&LogFont);
  1587. SelectObject(frame_dc, LarnFont);
  1588. CalcMinWindowSize(frame_dc);
  1589. if (LarnWindowMaximized) {
  1590. ShowWindow(frame_window_handle, SW_SHOWMAXIMIZED);
  1591. Resize(frame_window_handle);
  1592. } else {
  1593. SetWindowPos(frame_window_handle, NULL, LarnWindowLeft, LarnWindowTop,
  1594. LarnWindowWidth, LarnWindowHeight, SWP_NOZORDER);
  1595. }
  1596. } else {
  1597. //
  1598. // Get the default fixed font
  1599. //
  1600. LarnFont = GetStockObject(ANSI_FIXED_FONT);
  1601. SelectObject(frame_dc, LarnFont);
  1602. //
  1603. // Calculate the minimum window size and set the window to be that size
  1604. //
  1605. CalcMinWindowSize(frame_dc);
  1606. }
  1607. if (GetDeviceCaps(frame_dc, BITSPIXEL) <= 8) {
  1608. BITMAPINFOHEADER bi;
  1609. LPVOID lpDIBBits;
  1610. BITMAP SrcBitmap;
  1611. //
  1612. // Display is not true colour or high colour.
  1613. // The tiles will need to be remapped into fewer colours.
  1614. //
  1615. use_palette = 1;
  1616. //
  1617. // Load the tile images as a DIB into a temporory bitmap
  1618. //
  1619. TmpBitmap =
  1620. LoadImage(hinstance, TileBMName, IMAGE_BITMAP, 0, 0,
  1621. LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
  1622. if (TmpBitmap == NULL) {
  1623. MessageBox(NULL, "Failed to load tiles bitmap", "Error", MB_OK);
  1624. return 0;
  1625. }
  1626. //
  1627. // Calculate the palette to use
  1628. //
  1629. calculate_palette(TmpBitmap);
  1630. //
  1631. // Select the palette into the main window DC and realize the palette
  1632. // into the display device.
  1633. //
  1634. SelectPalette(frame_dc, HPalette, 0);
  1635. RealizePalette(frame_dc);
  1636. //
  1637. // Get info about the 24 bit tiles bitmap
  1638. //
  1639. GetObject(TmpBitmap, sizeof(BITMAP), &SrcBitmap);
  1640. //
  1641. // Create info about the tiles bitmap to use for blitting from info about
  1642. // the 24 bit version of the tiles.
  1643. //
  1644. bi.biSize = sizeof(BITMAPINFOHEADER);
  1645. bi.biWidth = SrcBitmap.bmWidth;
  1646. bi.biHeight = SrcBitmap.bmHeight;
  1647. bi.biPlanes = SrcBitmap.bmPlanes;
  1648. bi.biBitCount = SrcBitmap.bmBitsPixel;
  1649. bi.biCompression = BI_RGB;
  1650. bi.biSizeImage = 0;
  1651. bi.biXPelsPerMeter = 0;
  1652. bi.biYPelsPerMeter = 0;
  1653. bi.biClrUsed = 0;
  1654. bi.biClrImportant = 0;
  1655. lpDIBBits = (LPVOID)(SrcBitmap.bmBits);
  1656. //
  1657. // Create a device dependant version of the original bitmap using the
  1658. // palette that has been selected to the main window.
  1659. //
  1660. TileBitmap = CreateDIBitmap(frame_dc, (LPBITMAPINFOHEADER)&bi, CBM_INIT,
  1661. lpDIBBits, (LPBITMAPINFO)&bi, DIB_RGB_COLORS);
  1662. DeleteObject(TmpBitmap);
  1663. } else {
  1664. //
  1665. // Load the tile bitmap as a DDB and create device contexts for blitting
  1666. //
  1667. TileBitmap = LoadImage(hinstance, TileBMName, IMAGE_BITMAP, 0, 0,
  1668. LR_LOADFROMFILE | LR_DEFAULTSIZE);
  1669. if (TileBitmap == NULL) {
  1670. MessageBox(NULL, "Failed to load tiles bitmap", "Error", MB_OK);
  1671. return 0;
  1672. }
  1673. }
  1674. TileDC = CreateCompatibleDC(frame_dc);
  1675. SelectObject(TileDC, TileBitmap);
  1676. if (use_palette)
  1677. SelectPalette(TileDC, HPalette, 0);
  1678. //
  1679. // Clear the text buffers
  1680. //
  1681. for (y = 0; y < MAX_MSG_LINES; y++) {
  1682. for (x = 0; x < LINE_LENGTH; x++) {
  1683. MessageChr[y][x] = ' ';
  1684. MessageFmt[y][x] = FORMAT_NORMAL;
  1685. }
  1686. MessageChr[y][LINE_LENGTH] = 0;
  1687. }
  1688. for (y = 0; y < MAX_TEXT_LINES; y++) {
  1689. for (x = 0; x < LINE_LENGTH; x++) {
  1690. TextChr[y][x] = ' ';
  1691. TextFmt[y][x] = FORMAT_NORMAL;
  1692. }
  1693. TextChr[y][LINE_LENGTH] = 0;
  1694. }
  1695. return 1;
  1696. }
  1697. /* =============================================================================
  1698. * FUNCTION: close_app
  1699. */
  1700. void close_app(void) {
  1701. if (frame_window_handle != NULL)
  1702. WriteIniFile();
  1703. if (palette != NULL)
  1704. free(palette);
  1705. if (frame_dc != NULL)
  1706. ReleaseDC(frame_window_handle, frame_dc);
  1707. if (TileDC != NULL)
  1708. DeleteDC(TileDC);
  1709. if (TileBitmap != NULL)
  1710. DeleteObject(TileBitmap);
  1711. if (frame_window_handle != NULL)
  1712. DestroyWindow(frame_window_handle);
  1713. if (my_instance != NULL)
  1714. UnregisterClass(WINDOW_CLASS_NAME, my_instance);
  1715. }
  1716. /* =============================================================================
  1717. * FUNCTION: get_normal_input
  1718. */
  1719. ActionType get_normal_input(void) {
  1720. MSG msg; // generic message
  1721. int idx;
  1722. int got_dir;
  1723. Event = ACTION_NULL;
  1724. Runkey = 0;
  1725. while (Event == ACTION_NULL) {
  1726. if (GetMessage(&msg, NULL, 0, 0)) {
  1727. //
  1728. // Test if this is a quit
  1729. //
  1730. if (msg.message == WM_QUIT)
  1731. break;
  1732. //
  1733. // Translate any accelerator keys
  1734. //
  1735. TranslateMessage(&msg);
  1736. //
  1737. // Send the message to the window proc
  1738. //
  1739. DispatchMessage(&msg);
  1740. }
  1741. //
  1742. // Clear enhanced interface events in enhanced interface is not active
  1743. //
  1744. if (!enhance_interface) {
  1745. if ((Event == ACTION_OPEN_DOOR) || (Event == ACTION_OPEN_CHEST))
  1746. Event = ACTION_NULL;
  1747. }
  1748. }
  1749. if (Runkey) {
  1750. idx = 0;
  1751. got_dir = 0;
  1752. while ((idx < NUM_DIRS) && (!got_dir)) {
  1753. if (DirActions[idx] == Event)
  1754. got_dir = 1;
  1755. else
  1756. idx++;
  1757. }
  1758. if (got_dir)
  1759. /* modify into a run event */
  1760. Event = Event + 1;
  1761. }
  1762. return Event;
  1763. }
  1764. /* =============================================================================
  1765. * FUNCTION: get_prompt_input
  1766. */
  1767. char get_prompt_input(char *prompt, char *answers, int ShowCursor) {
  1768. MSG msg; // generic message
  1769. char *ch;
  1770. //
  1771. // Display the prompt at the current position
  1772. //
  1773. Print(prompt);
  1774. //
  1775. // Show the cursor if required
  1776. //
  1777. if (ShowCursor) {
  1778. CreateCaret(frame_window_handle, NULL, 2, CharHeight);
  1779. ShowCaret(frame_window_handle);
  1780. SetCaretPos(TLeft + (CursorX - 1) * CharWidth,
  1781. TTop + (CursorY - 1) * CharHeight);
  1782. CaretActive = 1;
  1783. }
  1784. //
  1785. // Process events until a character in answers has been pressed.
  1786. //
  1787. GotChar = 0;
  1788. while (!GotChar) {
  1789. if (GetMessage(&msg, NULL, 0, 0)) {
  1790. //
  1791. // Test if this is a quit
  1792. //
  1793. if (msg.message == WM_QUIT)
  1794. break;
  1795. //
  1796. // Translate any accelerator keys
  1797. //
  1798. TranslateMessage(&msg);
  1799. //
  1800. // Send the message to the window proc
  1801. //
  1802. DispatchMessage(&msg);
  1803. }
  1804. if (GotChar) {
  1805. //
  1806. // Search for the input character in the answers string
  1807. //
  1808. ch = strchr(answers, EventChar);
  1809. if (ch == NULL) {
  1810. //
  1811. // Not an answer we want
  1812. //
  1813. GotChar = 0;
  1814. }
  1815. }
  1816. }
  1817. if (ShowCursor) {
  1818. HideCaret(frame_window_handle);
  1819. CaretActive = 0;
  1820. }
  1821. return EventChar;
  1822. }
  1823. /* =============================================================================
  1824. * FUNCTION: get_password_input
  1825. */
  1826. void get_password_input(char *password, int Len) {
  1827. char ch;
  1828. char inputchars[256];
  1829. int Pos;
  1830. int value;
  1831. /* get the printable characters on this system */
  1832. Pos = 0;
  1833. for (value = 0; value < 256; value++) {
  1834. if (isprint(value)) {
  1835. inputchars[Pos] = (char)value;
  1836. Pos++;
  1837. }
  1838. }
  1839. /* add CR, BS and null terminator */
  1840. inputchars[Pos++] = '\010';
  1841. inputchars[Pos++] = '\015';
  1842. inputchars[Pos] = '\0';
  1843. Pos = 0;
  1844. do {
  1845. ch = get_prompt_input("", inputchars, 1);
  1846. if (isprint(ch) && (Pos < Len)) {
  1847. password[Pos] = ch;
  1848. Pos++;
  1849. Printc('*');
  1850. } else if (ch == '\010') {
  1851. //
  1852. // Backspace
  1853. //
  1854. if (Pos > 0) {
  1855. CursorX--;
  1856. Printc(' ');
  1857. CursorX--;
  1858. Pos--;
  1859. }
  1860. }
  1861. } while (ch != '\015');
  1862. password[Pos] = 0;
  1863. }
  1864. /* =============================================================================
  1865. * FUNCTION: get_num_input
  1866. */
  1867. int get_num_input(int defval) {
  1868. char ch;
  1869. int Pos = 0;
  1870. int value = 0;
  1871. int neg = 0;
  1872. do {
  1873. ch = get_prompt_input("", "-*0123456789\010\015", 1);
  1874. if ((ch == '-') && (Pos == 0)) {
  1875. //
  1876. // Minus
  1877. //
  1878. neg = 1;
  1879. Printc(ch);
  1880. Pos++;
  1881. }
  1882. if (ch == '*')
  1883. return defval;
  1884. else if (ch == '\010') {
  1885. //
  1886. // Backspace
  1887. //
  1888. if (Pos > 0) {
  1889. if ((Pos == 1) && neg)
  1890. neg = 0;
  1891. else
  1892. value = value / 10;
  1893. CursorX--;
  1894. Printc(' ');
  1895. CursorX--;
  1896. Pos--;
  1897. }
  1898. } else if ((ch >= '0') && (ch <= '9')) {
  1899. //
  1900. // digit
  1901. //
  1902. value = value * 10 + (ch - '0');
  1903. Printc(ch);
  1904. Pos++;
  1905. }
  1906. } while (ch != '\015');
  1907. if (Pos == 0)
  1908. return defval;
  1909. else {
  1910. if (neg)
  1911. value = -value;
  1912. return value;
  1913. }
  1914. }
  1915. /* =============================================================================
  1916. * FUNCTION: get_dir_input
  1917. */
  1918. ActionType get_dir_input(char *prompt, int ShowCursor) {
  1919. MSG msg; // generic message
  1920. int got_dir;
  1921. int idx;
  1922. //
  1923. // Display the prompt at the current position
  1924. //
  1925. Print(prompt);
  1926. //
  1927. // Show the cursor if required
  1928. //
  1929. if (ShowCursor) {
  1930. CreateCaret(frame_window_handle, NULL, 2, CharHeight);
  1931. ShowCaret(frame_window_handle);
  1932. SetCaretPos(TLeft + (CursorX - 1) * CharWidth,
  1933. TTop + (CursorY - 1) * CharHeight);
  1934. CaretActive = 1;
  1935. }
  1936. Event = ACTION_NULL;
  1937. got_dir = 0;
  1938. while (!got_dir) {
  1939. if (GetMessage(&msg, NULL, 0, 0)) {
  1940. //
  1941. // Test if this is a quit
  1942. //
  1943. if (msg.message == WM_QUIT)
  1944. break;
  1945. //
  1946. // Translate any accelerator keys
  1947. //
  1948. TranslateMessage(&msg);
  1949. //
  1950. // Send the message to the window proc
  1951. //
  1952. DispatchMessage(&msg);
  1953. }
  1954. idx = 0;
  1955. while ((idx < NUM_DIRS) && (!got_dir)) {
  1956. if (DirActions[idx] == Event)
  1957. got_dir = 1;
  1958. else
  1959. idx++;
  1960. }
  1961. }
  1962. if (ShowCursor) {
  1963. HideCaret(frame_window_handle);
  1964. CaretActive = 0;
  1965. }
  1966. return Event;
  1967. }
  1968. /* =============================================================================
  1969. * FUNCTION: UpdateStatus
  1970. */
  1971. void UpdateStatus(void) {
  1972. if (CurrentDisplayMode == DISPLAY_TEXT)
  1973. /* Don't redisplay if in text mode */
  1974. return;
  1975. PaintStatus(frame_dc);
  1976. }
  1977. /* =============================================================================
  1978. * FUNCTION: UpdateEffects
  1979. */
  1980. void UpdateEffects(void) {
  1981. if (CurrentDisplayMode == DISPLAY_TEXT)
  1982. /* Don't redisplay if in text mode */
  1983. return;
  1984. PaintEffects(frame_dc);
  1985. }
  1986. /* =============================================================================
  1987. * FUNCTION: UpdateStatusAndEffects
  1988. */
  1989. void UpdateStatusAndEffects(void) {
  1990. if (CurrentDisplayMode == DISPLAY_TEXT)
  1991. /* Don't redisplay if in text mode */
  1992. return;
  1993. //
  1994. // Do effects first as update status will mark all effects as current
  1995. //
  1996. PaintEffects(frame_dc);
  1997. PaintStatus(frame_dc);
  1998. }
  1999. /* =============================================================================
  2000. * FUNCTION: set_display
  2001. */
  2002. void set_display(DisplayModeType Mode) {
  2003. //
  2004. // Save the current settings
  2005. //
  2006. if (CurrentDisplayMode == DISPLAY_MAP) {
  2007. MsgCursorX = CursorX;
  2008. MsgCursorY = CursorY;
  2009. CurrentMsgFormat = CurrentFormat;
  2010. } else if (CurrentDisplayMode == DISPLAY_TEXT) {
  2011. TextCursorX = CursorX;
  2012. TextCursorY = CursorY;
  2013. CurrentTextFormat = CurrentFormat;
  2014. }
  2015. CurrentDisplayMode = Mode;
  2016. //
  2017. // Set the text buffer settings for the new display mode
  2018. //
  2019. if (CurrentDisplayMode == DISPLAY_MAP) {
  2020. CursorX = MsgCursorX;
  2021. CursorY = MsgCursorY;
  2022. CurrentFormat = CurrentMsgFormat;
  2023. Text = MessageChr;
  2024. Format = MessageFmt;
  2025. MaxLine = MAX_MSG_LINES;
  2026. TLeft = MessageLeft;
  2027. TTop = MessageTop;
  2028. TWidth = MessageWidth;
  2029. THeight = MessageHeight;
  2030. } else if (CurrentDisplayMode == DISPLAY_TEXT) {
  2031. CursorX = TextCursorX;
  2032. CursorY = TextCursorY;
  2033. CurrentFormat = CurrentTextFormat;
  2034. Text = TextChr;
  2035. Format = TextFmt;
  2036. MaxLine = MAX_TEXT_LINES;
  2037. TLeft = TextLeft;
  2038. TTop = TextTop;
  2039. TWidth = TextWidth;
  2040. THeight = TextHeight;
  2041. }
  2042. InvalidateRect(frame_window_handle, NULL, 1);
  2043. }
  2044. /* =============================================================================
  2045. * FUNCTION: IncCursorY
  2046. *
  2047. * DESCRIPTION:
  2048. * Increae the cursor y position, scrolling the text window if requried.
  2049. *
  2050. * PARAMETERS:
  2051. *
  2052. * Count : The number of lines to increase the cursor y position
  2053. *
  2054. * RETURN VALUE:
  2055. *
  2056. * None.
  2057. */
  2058. static void IncCursorY(int Count) {
  2059. int Scroll;
  2060. int inc;
  2061. int Line;
  2062. int x;
  2063. inc = Count;
  2064. Scroll = 0;
  2065. while (inc > 0) {
  2066. CursorY = CursorY + 1;
  2067. if (CursorY > MaxLine) {
  2068. Scroll = 1;
  2069. for (Line = 0; Line < (MaxLine - 1); Line++) {
  2070. for (x = 0; x < LINE_LENGTH; x++) {
  2071. Text[Line][x] = Text[Line + 1][x];
  2072. Format[Line][x] = Format[Line + 1][x];
  2073. }
  2074. }
  2075. CursorY--;
  2076. for (x = 0; x < LINE_LENGTH; x++) {
  2077. Text[MaxLine - 1][x] = ' ';
  2078. Format[MaxLine - 1][x] = FORMAT_NORMAL;
  2079. }
  2080. }
  2081. inc--;
  2082. }
  2083. if (Scroll)
  2084. PaintTextWindow(frame_dc);
  2085. }
  2086. /* =============================================================================
  2087. * FUNCTION: IncCursorX
  2088. *
  2089. * DESCRIPTION:
  2090. * Increase the cursor x position, handling line wrap.
  2091. *
  2092. * PARAMETERS:
  2093. *
  2094. * Count : The amount to increase the cursor x position.
  2095. *
  2096. * RETURN VALUE:
  2097. *
  2098. * None.
  2099. */
  2100. static void IncCursorX(int Count) {
  2101. CursorX = CursorX + Count;
  2102. if (CursorX > LINE_LENGTH) {
  2103. CursorX = 1;
  2104. IncCursorY(1);
  2105. }
  2106. }
  2107. /* =============================================================================
  2108. * FUNCTION: ClearText
  2109. */
  2110. void ClearText(void) {
  2111. int x, y;
  2112. //
  2113. // Clear the text buffer
  2114. //
  2115. for (y = 0; y < MaxLine; y++) {
  2116. for (x = 0; x < LINE_LENGTH; x++) {
  2117. Text[y][x] = ' ';
  2118. Format[y][x] = FORMAT_NORMAL;
  2119. }
  2120. Text[y][LINE_LENGTH] = 0;
  2121. }
  2122. CursorX = 1;
  2123. CursorY = 1;
  2124. //
  2125. // Clear the text area
  2126. //
  2127. PaintTextWindow(frame_dc);
  2128. }
  2129. /* =============================================================================
  2130. * FUNCTION: UlarnBeep
  2131. */
  2132. void UlarnBeep(void) {
  2133. if (!nobeep) {
  2134. //
  2135. // Middle C, 1/4 second
  2136. //
  2137. Beep(440, 250);
  2138. }
  2139. }
  2140. /* =============================================================================
  2141. * FUNCTION: MoveCursor
  2142. */
  2143. void MoveCursor(int x, int y) {
  2144. CursorX = x;
  2145. CursorY = y;
  2146. if (CursorX < 1)
  2147. CursorX = 1;
  2148. if (CursorY < 1)
  2149. CursorY = 1;
  2150. if (CursorX > LINE_LENGTH)
  2151. CursorX = LINE_LENGTH;
  2152. if (CursorY > MaxLine)
  2153. CursorY = MaxLine;
  2154. }
  2155. /* =============================================================================
  2156. * FUNCTION: Printc
  2157. */
  2158. void Printc(char c) {
  2159. int incx;
  2160. switch (c) {
  2161. case '\t':
  2162. incx = ((((CursorX - 1) / 8) + 1) * 8 + 1) - CursorX;
  2163. IncCursorX(incx);
  2164. break;
  2165. case '\n':
  2166. CursorX = 1;
  2167. IncCursorY(1);
  2168. break;
  2169. default:
  2170. Text[CursorY - 1][CursorX - 1] = c;
  2171. Format[CursorY - 1][CursorX - 1] = CurrentFormat;
  2172. switch (CurrentFormat) {
  2173. case FORMAT_NORMAL:
  2174. SetTextColor(frame_dc, RGB(0, 0, 0));
  2175. break;
  2176. case FORMAT_STANDOUT:
  2177. SetTextColor(frame_dc, RGB(255, 0, 0));
  2178. break;
  2179. case FORMAT_STANDOUT2:
  2180. SetTextColor(frame_dc, RGB(0, 127, 0));
  2181. break;
  2182. case FORMAT_STANDOUT3:
  2183. SetTextColor(frame_dc, RGB(0, 0, 255));
  2184. break;
  2185. default:
  2186. break;
  2187. }
  2188. TextOut(frame_dc, TLeft + (CursorX - 1) * CharWidth,
  2189. TTop + (CursorY - 1) * CharHeight, &c, 1);
  2190. IncCursorX(1);
  2191. break;
  2192. }
  2193. }
  2194. /* =============================================================================
  2195. * FUNCTION: Print
  2196. */
  2197. void Print(char *string) {
  2198. int Len;
  2199. int pos;
  2200. if (string == NULL)
  2201. return;
  2202. Len = strlen(string);
  2203. if (Len == 0)
  2204. return;
  2205. for (pos = 0; pos < Len; pos++)
  2206. Printc(string[pos]);
  2207. }
  2208. /* =============================================================================
  2209. * FUNCTION: Printf
  2210. */
  2211. void Printf(char *fmt, ...) {
  2212. char buf[2048];
  2213. va_list argptr;
  2214. va_start(argptr, fmt);
  2215. vsprintf(buf, fmt, argptr);
  2216. va_end(argptr);
  2217. Print(buf);
  2218. }
  2219. /* =============================================================================
  2220. * FUNCTION: Standout
  2221. */
  2222. void Standout(char *String) {
  2223. CurrentFormat = FORMAT_STANDOUT;
  2224. Print(String);
  2225. CurrentFormat = FORMAT_NORMAL;
  2226. }
  2227. /* =============================================================================
  2228. * FUNCTION: SetFormat
  2229. */
  2230. void SetFormat(FormatType format) { CurrentFormat = format; }
  2231. /* =============================================================================
  2232. * FUNCTION: ClearToEOL
  2233. */
  2234. void ClearToEOL(void) {
  2235. int x;
  2236. for (x = CursorX; x <= LINE_LENGTH; x++) {
  2237. Text[CursorY - 1][x - 1] = ' ';
  2238. Format[CursorY - 1][x - 1] = FORMAT_NORMAL;
  2239. }
  2240. TextOut(frame_dc, TLeft + (CursorX - 1) * CharWidth,
  2241. TTop + (CursorY - 1) * CharHeight, &(Text[CursorY - 1][CursorX - 1]),
  2242. (LINE_LENGTH - CursorX) + 1);
  2243. }
  2244. /* =============================================================================
  2245. * FUNCTION: ClearToEOPage
  2246. */
  2247. void ClearToEOPage(int x, int y) {
  2248. int tx, ty;
  2249. for (tx = x; tx <= LINE_LENGTH; tx++) {
  2250. Text[y - 1][tx - 1] = ' ';
  2251. Format[y - 1][tx - 1] = FORMAT_NORMAL;
  2252. }
  2253. TextOut(frame_dc, TLeft + (x - 1) * CharWidth, TTop + (y - 1) * CharHeight,
  2254. &(Text[y - 1][x - 1]), (LINE_LENGTH - x) + 1);
  2255. for (ty = y + 1; ty <= MaxLine; ty++) {
  2256. for (tx = 1; tx <= LINE_LENGTH; tx++) {
  2257. Text[ty - 1][tx - 1] = ' ';
  2258. Format[ty - 1][tx - 1] = FORMAT_NORMAL;
  2259. }
  2260. TextOut(frame_dc, TLeft, TTop + (ty - 1) * CharHeight, Text[ty - 1],
  2261. LINE_LENGTH);
  2262. }
  2263. }
  2264. /* =============================================================================
  2265. * FUNCTION: show1cell
  2266. */
  2267. void show1cell(int x, int y) {
  2268. int TileId;
  2269. int sx, sy;
  2270. int TileX, TileY;
  2271. /* see nothing if blind */
  2272. if (c[BLINDCOUNT])
  2273. return;
  2274. /* we end up knowing about it */
  2275. know[x][y] = item[x][y];
  2276. if (mitem[x][y].mon != MONST_NONE)
  2277. stealth[x][y] |= STEALTH_SEEN;
  2278. sx = x - MapTileLeft;
  2279. sy = y - MapTileTop;
  2280. if ((sx < 0) || (sx >= MapTileWidth) || (sy < 0) || (sy >= MapTileHeight)) {
  2281. //
  2282. // Tile is not currently in the visible part of the map,
  2283. // so don't draw anything
  2284. //
  2285. return;
  2286. }
  2287. GetTile(x, y, &TileId);
  2288. TileX = (TileId % 16) * TileWidth;
  2289. TileY = (TileId / 16) * TileHeight;
  2290. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2291. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2292. }
  2293. /* =============================================================================
  2294. * FUNCTION: showplayer
  2295. */
  2296. void showplayer(void) {
  2297. int sx, sy;
  2298. int TileId;
  2299. int TileX, TileY;
  2300. int scroll;
  2301. //
  2302. // Determine if we need to scroll the map
  2303. //
  2304. scroll = calc_scroll();
  2305. if (scroll)
  2306. PaintMap(frame_dc);
  2307. else {
  2308. sx = playerx - MapTileLeft;
  2309. sy = playery - MapTileTop;
  2310. if ((sx >= 0) && (sx < MapTileWidth) && (sy >= 0) && (sy < MapTileHeight)) {
  2311. if (c[BLINDCOUNT] == 0)
  2312. TileId = PlayerTiles[class_num][(int)sex];
  2313. else
  2314. GetTile(playerx, playery, &TileId);
  2315. TileX = (TileId % 16) * TileWidth;
  2316. TileY = (TileId / 16) * TileHeight;
  2317. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2318. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2319. TileId = TILE_CURSOR1;
  2320. TileX = (TileId % 16) * TileWidth;
  2321. TileY = (TileId / 16) * TileHeight;
  2322. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2323. TileWidth, TileHeight, TileDC, TileX, TileY, SRCAND);
  2324. TileId = TILE_CURSOR2;
  2325. TileX = (TileId % 16) * TileWidth;
  2326. TileY = (TileId / 16) * TileHeight;
  2327. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2328. TileWidth, TileHeight, TileDC, TileX, TileY, SRCPAINT);
  2329. } /* If player on visible map area */
  2330. }
  2331. }
  2332. /* =============================================================================
  2333. * FUNCTION: showcell
  2334. */
  2335. void showcell(int x, int y) {
  2336. int minx, maxx;
  2337. int miny, maxy;
  2338. int mx, my;
  2339. int sx, sy;
  2340. int TileX, TileY;
  2341. int TileId;
  2342. int scroll;
  2343. //
  2344. // Determine if we need to scroll the map
  2345. //
  2346. scroll = calc_scroll();
  2347. /*
  2348. * Decide how much the player knows about around him/her.
  2349. */
  2350. if (c[AWARENESS]) {
  2351. minx = x - 3;
  2352. maxx = x + 3;
  2353. miny = y - 3;
  2354. maxy = y + 3;
  2355. } else {
  2356. minx = x - 1;
  2357. maxx = x + 1;
  2358. miny = y - 1;
  2359. maxy = y + 1;
  2360. }
  2361. if (c[BLINDCOUNT]) {
  2362. minx = x;
  2363. maxx = x;
  2364. miny = y;
  2365. maxy = y;
  2366. //
  2367. // Redraw the last player position to remove the cursor
  2368. //
  2369. if (!scroll) {
  2370. //
  2371. // Only redraw if the map is not going to be completely redrawn.
  2372. //
  2373. sx = lastpx - MapTileLeft;
  2374. sy = lastpy - MapTileTop;
  2375. if ((sx >= 0) && (sx < MapTileWidth) && (sy >= 0) &&
  2376. (sy < MapTileHeight)) {
  2377. //
  2378. // Tile is currently visible, so draw it
  2379. //
  2380. GetTile(lastpx, lastpy, &TileId);
  2381. TileX = (TileId % 16) * TileWidth;
  2382. TileY = (TileId / 16) * TileHeight;
  2383. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2384. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2385. }
  2386. }
  2387. }
  2388. /*
  2389. * Limit the area to the map extents
  2390. */
  2391. if (minx < 0)
  2392. minx = 0;
  2393. if (maxx > MAXX - 1)
  2394. maxx = MAXX - 1;
  2395. if (miny < 0)
  2396. miny = 0;
  2397. if (maxy > MAXY - 1)
  2398. maxy = MAXY - 1;
  2399. for (my = miny; my <= maxy; my++) {
  2400. for (mx = minx; mx <= maxx; mx++) {
  2401. if ((mx == playerx) && (my == playery)) {
  2402. know[mx][my] = item[mx][my];
  2403. if (!scroll) {
  2404. //
  2405. // Only draw if the entire map is not going to be scrolled
  2406. //
  2407. showplayer();
  2408. }
  2409. } else if ((know[mx][my] != item[mx][my]) || /* item changed */
  2410. ((mx == lastpx) && (my == lastpy)) || /* last player pos */
  2411. ((mitem[mx][my].mon != MONST_NONE) && /* unseen monster */
  2412. ((stealth[mx][my] & STEALTH_SEEN) == 0))) {
  2413. //
  2414. // Only draw areas not already known (and hence displayed)
  2415. //
  2416. know[mx][my] = item[mx][my];
  2417. if (mitem[mx][my].mon != MONST_NONE)
  2418. stealth[mx][my] |= STEALTH_SEEN;
  2419. if (!scroll) {
  2420. //
  2421. // Only draw the tile if the map is not going to be scrolled
  2422. //
  2423. sx = mx - MapTileLeft;
  2424. sy = my - MapTileTop;
  2425. if ((sx >= 0) && (sx < MapTileWidth) && (sy >= 0) &&
  2426. (sy < MapTileHeight)) {
  2427. //
  2428. // Tile is currently visible, so draw it
  2429. //
  2430. GetTile(mx, my, &TileId);
  2431. TileX = (TileId % 16) * TileWidth;
  2432. TileY = (TileId / 16) * TileHeight;
  2433. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2434. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2435. }
  2436. }
  2437. } // if not known
  2438. }
  2439. }
  2440. if (scroll)
  2441. /* scrolling the map window, so repaint everything and return */
  2442. PaintMap(frame_dc);
  2443. }
  2444. /* =============================================================================
  2445. * FUNCTION: drawscreen
  2446. */
  2447. void drawscreen(void) { InvalidateRect(frame_window_handle, NULL, 1); }
  2448. /* =============================================================================
  2449. * FUNCTION: draws
  2450. */
  2451. void draws(int minx, int miny, int maxx, int maxy) {
  2452. InvalidateRect(frame_window_handle, NULL, 1);
  2453. }
  2454. /* =============================================================================
  2455. * FUNCTION: mapeffect
  2456. */
  2457. void mapeffect(int x, int y, DirEffectsType effect, int dir) {
  2458. int TileId;
  2459. int sx, sy;
  2460. int TileX, TileY;
  2461. /* see nothing if blind */
  2462. if (c[BLINDCOUNT])
  2463. return;
  2464. sx = x - MapTileLeft;
  2465. sy = y - MapTileTop;
  2466. if ((sx < 0) || (sx >= MapTileWidth) || (sy < 0) || (sy >= MapTileHeight)) {
  2467. //
  2468. // Tile is not currently in the visible part of the map,
  2469. // so don't draw anything
  2470. //
  2471. return;
  2472. }
  2473. TileId = EffectTile[effect][dir];
  2474. TileX = (TileId % 16) * TileWidth;
  2475. TileY = (TileId / 16) * TileHeight;
  2476. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2477. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2478. }
  2479. /* =============================================================================
  2480. * FUNCTION: magic_effect_frames
  2481. */
  2482. int magic_effect_frames(MagicEffectsType fx) { return magicfx_tile[fx].Frames; }
  2483. /* =============================================================================
  2484. * FUNCTION: magic_effect
  2485. */
  2486. void magic_effect(int x, int y, MagicEffectsType fx, int frame) {
  2487. int TileId;
  2488. int sx, sy;
  2489. int TileX, TileY;
  2490. if (frame > magicfx_tile[fx].Frames)
  2491. return;
  2492. /*
  2493. * draw the tile that is at this location
  2494. */
  2495. /* see nothing if blind */
  2496. if (c[BLINDCOUNT])
  2497. return;
  2498. sx = x - MapTileLeft;
  2499. sy = y - MapTileTop;
  2500. if ((sx < 0) || (sx >= MapTileWidth) || (sy < 0) || (sy >= MapTileHeight)) {
  2501. //
  2502. // Tile is not currently in the visible part of the map,
  2503. // so don't draw anything
  2504. //
  2505. return;
  2506. }
  2507. if (magicfx_tile[fx].Overlay) {
  2508. GetTile(x, y, &TileId);
  2509. TileX = (TileId % 16) * TileWidth;
  2510. TileY = (TileId / 16) * TileHeight;
  2511. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2512. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2513. TileId = magicfx_tile[fx].Tile1[frame];
  2514. TileX = (TileId % 16) * TileWidth;
  2515. TileY = (TileId / 16) * TileHeight;
  2516. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2517. TileWidth, TileHeight, TileDC, TileX, TileY, SRCAND);
  2518. TileId = magicfx_tile[fx].Tile2[frame];
  2519. TileX = (TileId % 16) * TileWidth;
  2520. TileY = (TileId / 16) * TileHeight;
  2521. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2522. TileWidth, TileHeight, TileDC, TileX, TileY, SRCPAINT);
  2523. } else {
  2524. TileId = magicfx_tile[fx].Tile1[frame];
  2525. TileX = (TileId % 16) * TileWidth;
  2526. TileY = (TileId / 16) * TileHeight;
  2527. BitBlt(frame_dc, MapLeft + sx * TileWidth, MapTop + sy * TileHeight,
  2528. TileWidth, TileHeight, TileDC, TileX, TileY, SRCCOPY);
  2529. }
  2530. }
  2531. /* =============================================================================
  2532. * FUNCTION: nap
  2533. */
  2534. void nap(int delay) {
  2535. MSG msg; // generic message
  2536. int time_left;
  2537. time_left = delay;
  2538. while (time_left > 0) {
  2539. if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  2540. //
  2541. // Test if this is a quit
  2542. //
  2543. if (msg.message == WM_QUIT)
  2544. break;
  2545. //
  2546. // Translate any accelerator keys
  2547. //
  2548. TranslateMessage(&msg);
  2549. //
  2550. // Send the message to the window proc
  2551. //
  2552. DispatchMessage(&msg);
  2553. }
  2554. if (time_left > 100)
  2555. Sleep(100);
  2556. else
  2557. Sleep(time_left);
  2558. time_left -= 100;
  2559. }
  2560. }
  2561. //
  2562. //
  2563. //
  2564. static char *UserName;
  2565. /* =============================================================================
  2566. * FUNCTION: NameDialogProc
  2567. *
  2568. * DESCRIPTION:
  2569. * Windows callback for the enter name dialog.
  2570. *
  2571. * PARAMETERS:
  2572. *
  2573. * hwnd : The window handle for the message
  2574. *
  2575. * msg : The message received
  2576. *
  2577. * wparam : The wparam for the message
  2578. *
  2579. * lparam : The lparam for the message.
  2580. *
  2581. * RETURN VALUE:
  2582. *
  2583. * 0 if the message was handled by this procedure.
  2584. */
  2585. static BOOL CALLBACK NameDialogProc(HWND hwnd, UINT msg, WPARAM wparam,
  2586. LPARAM lparam) {
  2587. int len;
  2588. RECT WRect;
  2589. RECT DRect;
  2590. int WindowWidth;
  2591. int WindowHeight;
  2592. int DialogWidth;
  2593. int DialogHeight;
  2594. int DialogX;
  2595. int DialogY;
  2596. //
  2597. // What is the message
  2598. //
  2599. switch (msg) {
  2600. case WM_INITDIALOG:
  2601. // hwndFocus = (HWND) wparam; // handle of control to receive focus
  2602. // lInitParam = lparam;
  2603. //
  2604. // Do initialization stuff here
  2605. //
  2606. GetWindowRect(frame_window_handle, &WRect);
  2607. WindowWidth = (WRect.right - WRect.left) + 1;
  2608. WindowHeight = (WRect.bottom - WRect.top) + 1;
  2609. GetWindowRect(hwnd, &DRect);
  2610. DialogWidth = (DRect.right - DRect.left) + 1;
  2611. DialogHeight = (DRect.bottom - DRect.top) + 1;
  2612. DialogX = WRect.left + (WindowWidth - DialogWidth) / 2;
  2613. DialogY = WRect.top + (WindowHeight - DialogHeight) / 2;
  2614. SetWindowPos(hwnd, NULL, DialogX, DialogY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  2615. return TRUE;
  2616. case WM_COMMAND: {
  2617. // int wNotifyCode = HIWORD(wparam); // notification code
  2618. int wID = LOWORD(wparam); // item, control, or accelerator identifier
  2619. // HWND hwndCtl = (HWND) lparam; // handle of control
  2620. switch (wID) {
  2621. case IDOK:
  2622. //
  2623. // Get the number of characeters entered.
  2624. //
  2625. len = (WORD)SendDlgItemMessage(hwnd, IDC_EDIT_NAME, EM_LINELENGTH,
  2626. (WPARAM)0, (LPARAM)0);
  2627. if (len > USERNAME_LENGTH)
  2628. len = USERNAME_LENGTH;
  2629. else if (len == 0) {
  2630. EndDialog(hwnd, IDCANCEL);
  2631. return TRUE;
  2632. }
  2633. //
  2634. // Put the number of characters into first word of buffer.
  2635. //
  2636. *((LPWORD)UserName) = (WORD)len;
  2637. /* Get the characters. */
  2638. SendDlgItemMessage(hwnd, IDC_EDIT_NAME, EM_GETLINE,
  2639. (WPARAM)0, /* line 0 */
  2640. (LPARAM)UserName);
  2641. /* Null-terminate the string. */
  2642. UserName[len] = 0;
  2643. EndDialog(hwnd, IDOK);
  2644. return TRUE;
  2645. case IDCANCEL:
  2646. EndDialog(hwnd, IDCANCEL);
  2647. return TRUE;
  2648. default:
  2649. break;
  2650. }
  2651. break;
  2652. }
  2653. default:
  2654. break;
  2655. }
  2656. return FALSE;
  2657. }
  2658. /* =============================================================================
  2659. * FUNCTION: GetUser
  2660. */
  2661. void GetUser(char *username, int *uid) {
  2662. int result;
  2663. FILE *fp;
  2664. char TmpName[80];
  2665. int TmpPid;
  2666. int Found;
  2667. int n;
  2668. /* Set the buffer the name dialog is to use to store the input name */
  2669. UserName = username;
  2670. /* uid = -1 indicated failure to determine uid */
  2671. *uid = -1;
  2672. if (username[0] == 0) {
  2673. //
  2674. // Name is not yet specified, so ask player for the name
  2675. //
  2676. result = DialogBox(main_instance, MAKEINTRESOURCE(IDD_DIALOG1),
  2677. frame_window_handle, NameDialogProc);
  2678. if (result == IDCANCEL)
  2679. strcpy(UserName, "Anon");
  2680. }
  2681. /* get the Player Id */
  2682. fp = fopen(PIDName, "rb");
  2683. if (fp == NULL) {
  2684. /* Need to create the PID file. */
  2685. fp = fopen(PIDName, "wb");
  2686. if (fp != NULL) {
  2687. *uid = FIRST_PID;
  2688. fwrite(username, USERNAME_LENGTH + 1, 1, fp);
  2689. fwrite(uid, sizeof(int), 1, fp);
  2690. fclose(fp);
  2691. }
  2692. } else {
  2693. /* search the PID file for this player id */
  2694. Found = 0;
  2695. TmpPid = FIRST_PID;
  2696. while (!feof(fp) && !Found) {
  2697. n = fread(TmpName, USERNAME_LENGTH + 1, 1, fp);
  2698. if (n == 1)
  2699. n = fread(&TmpPid, sizeof(int), 1, fp);
  2700. if (n == 1) {
  2701. if (strcmp(TmpName, username) == 0) {
  2702. *uid = TmpPid;
  2703. Found = 1;
  2704. }
  2705. }
  2706. }
  2707. fclose(fp);
  2708. if (!Found) {
  2709. *uid = TmpPid + 1;
  2710. fp = fopen(PIDName, "ab");
  2711. if (fp != NULL) {
  2712. fwrite(username, USERNAME_LENGTH + 1, 1, fp);
  2713. fwrite(uid, sizeof(int), 1, fp);
  2714. fclose(fp);
  2715. }
  2716. }
  2717. }
  2718. }