PageRenderTime 51ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/DR.World/Ablast20/Source/ABMAIN.PAS

https://code.google.com/p/d-edit/
Pascal | 745 lines | 628 code | 71 blank | 46 comment | 0 complexity | e71906455e91d7039425b29975e4a3f7 MD5 | raw file
  1. {-= Main part of Alien Blaster =-}
  2. Uses ABGraf, Dos, Crt;
  3. {$I abconst.pas}
  4. {$I abmisc.pas}
  5. Type
  6. RecordPointer = ^LinkedList; {predefined record for a pointer-linked array (PLA)}
  7. LinkedList = Record
  8. X, Y : Real; {coordinates}
  9. Frame : ^Real; {only used by explosions}
  10. Next, Previous : RecordPointer; {nodes}
  11. End;
  12. LevelSettingsRecord = Record {header part of a level-file}
  13. RandomShoot : Word; {the bigger the value the more often enemies will shoot}
  14. EnemysHorizOffset, EnemysVertOffset : Real; {offsets}
  15. End;
  16. GameSettingsRecord = Record {configuration}
  17. Left, Right, Shoot : Byte; {keys}
  18. PlayersHorizOffset, BulletOffset, {offsets}
  19. FrameDelay : Real; {delay between frames}
  20. End;
  21. SpriteRecord = Record {sprites}
  22. Width, Height : Word;
  23. Data : Pointer; {buffer}
  24. End;
  25. ExplosionRecord = Record {explosions}
  26. FirstExplosion : RecordPointer; {pointer to first element}
  27. ExplosionSprite : SpriteRecord;
  28. End;
  29. EnemyRecord = Record
  30. FirstEnemy, FirstBullet : RecordPointer; {pointer to first element}
  31. HorizDirection, VertDirection : ShortInt; {directions (-1=left, 0=none, 1=right}
  32. SpaceShip, BulletSprite : SpriteRecord; {sprites}
  33. End;
  34. PlayerRecord = Record
  35. X, Y : Real; {coordinates}
  36. FirstBullet : RecordPointer; {pointer to first element}
  37. Spaceship, BulletSprite : SpriteRecord; {sprites}
  38. Lifes : Word; {lifes}
  39. End;
  40. Var OldTimerHandler, OldKbdHandler : Procedure; {pointers to hooked interrupts}
  41. Player : PlayerRecord;
  42. Enemies : EnemyRecord;
  43. Explosions : ExplosionRecord;
  44. Palette : PalettePointer; {save VGA palette}
  45. Font : SpriteRecord; {font of the game}
  46. OldExitProc, {old on-exit handler}
  47. VirtualScreen1, VirtualScreen2 : Pointer; {virtual screen buffers}
  48. LevelSettings : LevelSettingsRecord;
  49. GameSettings : GameSettingsRecord;
  50. Shooting, {indicates if the fire button is beeing pressed}
  51. Bussy : Boolean; {indiactes if the new tiemr interrupt is not idle}
  52. CurrentLevel : Word; {current level (first level is 1)}
  53. CurrentRecord : RecordPointer;
  54. Event : EventType; {will equal the id of the occured event and thus terminating the game}
  55. Pressed : Byte; {hold the value of a pressed key}
  56. Pause, {only used by the new keyboard handler}
  57. Paused : Boolean; {indicates the state of the game}
  58. Procedure Create(Var FirstRecord : RecordPointer); Forward;
  59. Procedure SetBullet(X1, Y1 : Real; Var FirstRecord : RecordPointer); Forward;
  60. Procedure NewTimerHandler; Interrupt; Forward;
  61. Procedure Quit(ExitCode : Byte; Message : String); Forward;
  62. {*** GRAPHICS HANDLING ***}
  63. {Show a text string}
  64. Procedure OutPut(X, Y : Word; Message : String; Buffer : Pointer);
  65. Var Run, Char : Byte;
  66. Begin
  67. With Font Do
  68. For Run:= 1 to Length(Message) Do
  69. Begin
  70. Case Message[Run] Of {get letter order}
  71. 'A'..'Z' : Char:= Ord(Message[run]) - 65;
  72. '0'..'9' : Char:= 26 + Ord(Message[run]) - 48;
  73. '?' : Char:= 36; '!' : Char:= 37; '(' : Char:= 38; ')' : Char:= 39; '@' : Char:= 40;
  74. '.' : Char:= 41; ':' : Char:= 42; ',' : Char:= 43; '/' : Char:= 44; #39 : Char:= 45;
  75. '-' : Char:= 46; '+' : Char:= 47; #32 : Continue;
  76. End;
  77. PutSprite(X + (Run - 1) * Width, Y, Width, Width, Ptr(Seg(Font.Data^), Ofs(Font.Data^) + Width * Width * Char), Buffer);
  78. End;
  79. End;
  80. {Pop up a message}
  81. Function Message(Message1 : String) : char;
  82. Begin
  83. Flip(VirtualScreen2^, Display^);
  84. With Font Do OutPut((ScreenWidth - Length(Message1) * Width) Div 2, (ScreenHeight - Width) Div 2, Message1, Display);
  85. Message:= UpCase(ReadKey); {wait for a keypress and return it's value}
  86. End;
  87. {Set new 256 color palette}
  88. Procedure SetPalette;
  89. Var Run : Byte;
  90. Begin
  91. Clear(0, Vga);
  92. For Run:= 0 to 255 Do
  93. With Palette^[Run] Do SetRGB(Run, Red, Green, Blue);
  94. End;
  95. {Load a PCX image}
  96. Function LoadImage(Path : String; Var Width, Height : Word; Var Palette : PalettePointer; Var Buffer : Pointer) : ShortInt;
  97. Var F : File;
  98. Run : Word;
  99. begin
  100. If OpenFile(F, Path) <> Success Then Quit(CantOpenFile, 'Error! Can''t load "' + Path + '"'); {can't open?}
  101. GetPcxDimensions(F, Width, Height); {get width and height}
  102. If MaxAvail < Width * Height Then Quit(CantOpenFile, 'Error! Not enough memory'); {not enough memory}
  103. GetMem(Buffer, Width * Height); {allocate memory for new buffer and put the image into it}
  104. LoadPcxPalette(F, Palette);
  105. LoadPcx(F, Width, Height, Buffer);
  106. Close(F);
  107. LoadImage:= Success;
  108. End;
  109. {Load font}
  110. Procedure LoadFont;
  111. Begin
  112. With Font Do LoadImage(FontPcx, Width, Height, Palette, Data);
  113. End;
  114. {Load background}
  115. Procedure LoadBackground;
  116. Var Width, Height : Word;
  117. Begin
  118. LoadImage(BackgroundPcx, Width, Height, Palette, VirtualScreen2);
  119. End;
  120. {Load all sprites}
  121. Procedure LoadSprites;
  122. Begin
  123. With Enemies Do
  124. Begin
  125. With SpaceShip Do LoadImage(Enemypcx, Width, Height, Palette, Data);
  126. With BulletSprite Do LoadImage(EnemyBulletPcx, Width, Height, Palette, Data);
  127. End;
  128. With Player Do
  129. Begin
  130. With SpaceShip Do LoadImage(PlayerPcx, Width, Height, Palette, Data);
  131. With BulletSprite Do LoadImage(PlayerBulletPcx, Width, Height, Palette, Data);
  132. End;
  133. With Explosions, ExplosionSprite Do
  134. LoadImage(ExplosionPcx, Width, Height, Palette, Data);
  135. End;
  136. {*** POINTER AND PLA HANDLING ***}
  137. {Link new record to PLA}
  138. Procedure Create(Var FirstRecord : RecordPointer);
  139. Var TempRecord : RecordPointer;
  140. Begin
  141. New(Temprecord);
  142. With TempRecord^ do
  143. Begin {setup nodes}
  144. Next:= FirstRecord;
  145. FirstRecord^.Previous:= TempRecord;
  146. FirstRecord:= TempRecord;
  147. End;
  148. End;
  149. {Erase an element from PLA}
  150. Procedure Erase(Var CurrentRecord, FirstRecord : RecordPointer);
  151. Var TempRecord : RecordPointer;
  152. Begin
  153. TempRecord:= CurrentRecord;
  154. With CurrentRecord^ Do
  155. Begin {setup nodes...}
  156. If CurrentRecord = FirstRecord Then FirstRecord:= FirstRecord^.Next
  157. Else
  158. Begin
  159. Previous^.Next:= Next;
  160. Next^.Previous:= Previous;
  161. End;
  162. CurrentRecord:= Next;
  163. Dispose(TempRecord); {erase}
  164. End;
  165. End;
  166. {Erase player's bullets}
  167. Procedure ErasePlayersBullets;
  168. Var TempRecord : RecordPointer;
  169. Begin
  170. With Player Do
  171. Begin
  172. TempRecord:= FirstBullet;
  173. While TempRecord <> Nil Do Erase(TempRecord, FirstBullet);
  174. End;
  175. End;
  176. {Erase all enemies}
  177. Procedure EraseEnemies;
  178. Var TempRecord : RecordPointer;
  179. Begin
  180. With Enemies Do
  181. Begin
  182. TempRecord:= FirstBullet;
  183. While TempRecord <> Nil Do Erase(TempRecord, FirstBullet); {erase bullets}
  184. TempRecord:= FirstEnemy;
  185. While TempRecord <> Nil Do Erase(TempRecord, FirstEnemy); {erase spaceships}
  186. End;
  187. End;
  188. {Erase all explosions}
  189. Procedure EraseExplosions;
  190. Var TempRecord : RecordPointer;
  191. Begin
  192. With Explosions Do
  193. Begin
  194. TempRecord:= FirstExplosion;
  195. While TempRecord <> Nil Do
  196. Begin
  197. Dispose(TempRecord^.Frame); {erase...}
  198. Erase(TempRecord, FirstExplosion);
  199. End;
  200. End;
  201. End;
  202. {Release all PLAs and the rest used memory for the game}
  203. Procedure EraseAll;
  204. Begin
  205. If VirtualScreen1 <> Nil Then
  206. Begin
  207. FreeMem(VirtualScreen1, BufferSize);
  208. VirtualScreen1:= Nil;
  209. End;
  210. If VirtualScreen2 <> Nil Then
  211. Begin
  212. FreeMem(VirtualScreen2, BufferSize);
  213. VirtualScreen2:= Nil;
  214. End;
  215. With Font Do
  216. If Data <> Nil Then
  217. Begin
  218. FreeMem(Data, Width * Height);
  219. Data:= Nil;
  220. End;
  221. With Explosions.ExplosionSprite Do
  222. If Data <> Nil Then
  223. Begin
  224. FreeMem(Data, Width * Height);
  225. Data:= Nil;
  226. End;
  227. With Player Do
  228. Begin
  229. With Spaceship Do
  230. If Data <> Nil Then
  231. Begin
  232. FreeMem(Data, Width * Height);
  233. Data:= Nil;
  234. End;
  235. With BulletSprite Do
  236. If Data <> Nil Then
  237. Begin
  238. FreeMem(Data, Width * Height);
  239. Data:= Nil;
  240. End;
  241. End;
  242. With Enemies, SpaceShip Do
  243. Begin
  244. With SpaceShip Do
  245. If Data <> Nil Then
  246. Begin
  247. FreeMem(Data, Width * Height);
  248. Data:= Nil;
  249. End;
  250. With BulletSprite Do
  251. If Data <> Nil Then
  252. Begin
  253. FreeMem(Data, Width * Height);
  254. Data:= Nil;
  255. End;
  256. End;
  257. ErasePlayersBullets;
  258. EraseEnemies;
  259. EraseExplosions;
  260. Dispose(Palette);
  261. If Palette <> Nil Then Palette:= Nil;
  262. End;
  263. {*** SPRITE HANDLING ***}
  264. {Link new bullet to the rest}
  265. Procedure SetBullet(X1, Y1 : Real; Var FirstRecord : RecordPointer);
  266. Begin
  267. Create(FirstRecord);
  268. With FirstRecord^ Do
  269. Begin {setup it's coordinates}
  270. X:= X1;
  271. Y:= Y1;
  272. End;
  273. End;
  274. {Put a sprite in the specified buffer at the specified coordinates}
  275. Procedure PutSprites(FirstRecord : RecordPointer; Sprite : SpriteRecord);
  276. Var TempRecord : RecordPointer;
  277. begin
  278. TempRecord:= FirstRecord;
  279. While TempRecord <> Nil Do
  280. With TempRecord^, Sprite Do
  281. Begin
  282. Putsprite(Trunc(X), Trunc(Y), Width, Height, Data, VirtualScreen1);
  283. TempRecord:= Next;
  284. End;
  285. End;
  286. {Set all explosions in the specified buffer}
  287. Procedure PutExplosions;
  288. Var Explosion : RecordPointer;
  289. Begin
  290. Explosion:= Explosions.FirstExplosion;
  291. While Explosion <> Nil Do
  292. With Explosion^, Explosions.ExplosionSprite Do
  293. Begin
  294. PutSprite(Trunc(X), Trunc(Y), Width, Width, Ptr(Seg(Data^), Ofs(data^) + Width * Width * Trunc(Frame^)), VirtualScreen1);
  295. Explosion:= Next;
  296. End;
  297. End;
  298. {Link new explosion to the rest}
  299. Procedure SetExplosion(X1, Y1 : Real);
  300. Begin
  301. Create(Explosions.FirstExplosion);
  302. With Explosions.FirstExplosion^ Do
  303. Begin {setup the new explosion}
  304. X:= X1;
  305. Y:= Y1;
  306. New(Frame);
  307. Frame^:= 0;
  308. End;
  309. End;
  310. {*** EVENTS AND INTERRUPT HANDLING ***}
  311. {Handle player-died event}
  312. Procedure KillPlayer;
  313. Begin
  314. With Player Do
  315. Begin
  316. If Lifes > 0 Then Dec(Lifes);
  317. SetExplosion(X, Y);
  318. End;
  319. End;
  320. {Handle enemy-destoried event}
  321. Procedure KillEnemy(Var Enemy : RecordPointer);
  322. Begin
  323. With Enemy^ Do SetExplosion(X, Y);
  324. With Enemies Do
  325. Begin
  326. Erase(Enemy, FirstEnemy);
  327. If FirstEnemy = Nil Then {no more enemies left?}
  328. Begin {simply clear all objects on the screen}
  329. ErasePlayersBullets;
  330. EraseEnemies;
  331. End;
  332. End;
  333. End;
  334. {New keyboard handler}
  335. Procedure NewKeyboardHandler; Interrupt;
  336. var Input : Byte;
  337. {Pop up usage informaton}
  338. Procedure ShowHelp;
  339. Begin
  340. With Font Do
  341. Begin {misc. information}
  342. Flip(VirtualScreen2^, Display^);
  343. OutPut((ScreenWidth - Length(GameTitle) * Width) Div 2, Width * 0, UpperStr(GameTitle), Display);
  344. OutPut((ScreenWidth - (3 + Length(Programmer) + Length(EMail) + 2) * Width) Div 2, Width * 1, 'BY ' +
  345. UpperStr(Programmer) + '(' + UpperStr(EMail) + ')', Display);
  346. OutPut((ScreenWidth - 8 * Width) Div 2, Width * 3, '- HELP -', Display);
  347. OutPut((ScreenWidth - 8 * Width) Div 2, Width * 5, 'ESC:QUIT', Display);
  348. With GameSettings Do
  349. Begin {keys}
  350. OutPut((ScreenWidth - (Length(Key(Left)) + 10) * Width) Div 2, Width * 6,
  351. UpperStr(Key(Left)) + ':MOVE LEFT', Display);
  352. OutPut((ScreenWidth - (Length(Key(Right)) + 11) * Width) Div 2, Width * 7,
  353. UpperStr(Key(Right)) + ':MOVE RIGHT', Display);
  354. OutPut((ScreenWidth - (Length(Key(Shoot)) + 5) * Width) Div 2, Width * 8,
  355. UpperStr(Key(Shoot)) + ':SHOOT', Display);
  356. End;
  357. End;
  358. ReadKbdPort;
  359. End;
  360. Begin
  361. Input:= Port[$60]; {read directly from the port}
  362. If Input = 225 Then {pause pressed?}
  363. Begin
  364. Pause:= Not Pause;
  365. If Not Pause Then {interrupt executed for second time?}
  366. Paused:= Not Paused; {change state}
  367. Port[$20]:= $20; {end of interrupt}
  368. End Else
  369. Begin
  370. With GameSettings Do
  371. Begin
  372. If Input = Shoot Then Shooting:= True; {shoot if key is pressed}
  373. If Input = Shoot + 128 Then Shooting:= False; {don't shoot in key is released}
  374. If Input in [Left, Left + 128, Right, Right + 128, 1, Ord(kF1)] Then Pressed:= Input; {get input}
  375. End;
  376. Inline($9C); {pushf}
  377. OldKbdHandler;
  378. If Input = Ord(kF1) Then ShowHelp;
  379. FlushKbdBuffer;
  380. End;
  381. End;
  382. {Return to OS}
  383. Procedure Quit(ExitCode : Byte; Message : String);
  384. Begin
  385. SetMode(TextMode);
  386. WriteLn(Message);
  387. Halt(Byte(ExitCode));
  388. End;
  389. {Determine if collision occured between two objects}
  390. Function Collision(X1, Y1, Width1, Height1, X2, Y2, Width2, Height2 : Word) : Boolean;
  391. Begin
  392. If (X1 + Width1 - 1 >= x2) And (X1 <= X2 + Width2 - 1) And (Y1 + Height1 - 1 >= y2) And (Y1 <= Y2 + Height2 - 1) Then
  393. Collision:= True Else Collision:= False;
  394. End;
  395. {New exit handler}
  396. Procedure NewExitProc;
  397. Begin
  398. SetDefaultSpeed; {restore state}
  399. SetIntVec($1C, @OldTimerHandler);
  400. SetIntVec($9, @OldKbdHandler);
  401. EraseAll;
  402. If ExitCode <> Success Then {run-time error occured?}
  403. Begin {acknowledge user}
  404. SetMode(TextMode);
  405. WriteLn('Run-time error ', ExitCode, ' at ', Seg(ErrorAddr^), ':', Ofs(ErrorAddr^));
  406. End;
  407. ExitProc:= OldExitProc;
  408. If ExitCode <> Success Then Halt(255) Else Halt;
  409. End;
  410. {New timer handler}
  411. Procedure NewTimerHandler;
  412. {Determine if player was killed}
  413. Function PlayerKilled : Boolean;
  414. Begin
  415. PlayerKilled:= False;
  416. With CurrentRecord^ Do
  417. If Collision(Trunc(X), Trunc(Y), Enemies.BulletSprite.Width, Enemies.BulletSprite.Height,
  418. Trunc(Player.X), Trunc(Player.Y), Player.SpaceShip.Width, Player.SpaceShip.Height) Then
  419. Begin {handle event}
  420. PlayerKilled:= True;
  421. Erase(CurrentRecord, Enemies.FirstBullet);
  422. KillPlayer;
  423. End;
  424. End;
  425. {Determine if an enemy was killed}
  426. Function EnemyKilled : Boolean;
  427. Var Enemy : RecordPointer;
  428. Label ByPass;
  429. Begin
  430. Enemy:= Enemies.FirstEnemy;
  431. While Enemy <> Nil Do
  432. With CurrentRecord^ Do
  433. If Collision(Trunc(X), trunc(Y), Player.BulletSprite.Width, Player.BulletSprite.Height,
  434. Trunc(Enemy^.X), Trunc(Enemy^.Y), Enemies.SpaceShip.Width, Enemies.SpaceShip.Height) Then
  435. Begin {handle event}
  436. Erase(CurrentRecord, Player.FirstBullet);
  437. KillEnemy(Enemy);
  438. EnemyKilled:= True;
  439. Goto ByPass;
  440. End Else Enemy:= Enemy^.Next;
  441. EnemyKilled:= False;
  442. ByPass:
  443. End;
  444. {React on keypress}
  445. Procedure HandleKbdEvents;
  446. Begin
  447. With Player, GameSettings Do
  448. Begin
  449. If Pressed = 1{esc} Then Event:= GameHalted;
  450. If Enemies.FirstEnemy <> Nil Then {still enemies left?}
  451. Begin
  452. If Pressed = Left Then
  453. If X - PlayersHorizOffset >= 0 Then X:= X - Trunc(PlayersHorizOffset) Else X:= 0; {move left}
  454. If Pressed = Right Then If X + PlayersHorizOffset <= ScreenWidth - SpaceShip.Width Then {move right}
  455. X:= X + Trunc(PlayersHorizOffset) Else X:= ScreenWidth - SpaceShip.Width;
  456. If Shooting Then {shooting?}
  457. If FirstBullet = Nil Then SetBullet(X + (SpaceShip.Width - BulletSprite.Width) Div 2,
  458. Y - BulletSprite.Height, Player.FirstBullet){fire if no bullets are already flying}
  459. Else If FirstBullet^.Y < ScreenHeight Div 2 Then SetBullet(X + (SpaceShip.Width - BulletSprite.Width) Div 2,
  460. {check the distance between two bullets and fire} Y - BulletSprite.Height, Player.FirstBullet);
  461. End Else If Lifes > 0 Then Y:= Y - 1; {move player's spaceship to the top of the screen (if alive)}
  462. End;
  463. End;
  464. {Simulate enemy's behaveour}
  465. Procedure HandleEnemies;
  466. Var XOutOfRange, YOutOfRange : Boolean;
  467. Label GoBack;
  468. Begin
  469. GoBack:
  470. XOutOfRange:= False;
  471. YOutOfRange:= False;
  472. CurrentRecord:= Enemies.FirstEnemy;
  473. While CurrentRecord <> Nil Do
  474. With CurrentRecord^, Enemies.SpaceShip Do
  475. Begin
  476. X:= X + LevelSettings.EnemysHorizOffset * Enemies.HorizDirection; {move to the right and backwards...}
  477. If (X + Width > ScreenWidth) or (X < 0) Then XOutOfRange:= True;
  478. Y:= Y + LevelSettings.EnemysVertOffset * Enemies.VertDirection; {move to the bottom and backwards...}
  479. If (Y + Height > ScreenHeight) or (Y < 0) Then YOutOfRange:= True;
  480. If Random(LevelSettings.RandomShoot) = 0 Then {shoot at a random time}
  481. SetBullet(Trunc(X) + (Width - Enemies.BulletSprite.Width) Div 2, Y + Height, Enemies.FirstBullet);
  482. If Collision(Trunc(X), Trunc(Y), Enemies.SpaceShip.Width, Enemies.SpaceShip.Height,
  483. Trunc(Player.X), Trunc(Player.Y), Player.SpaceShip.Width, Player.SpaceShip.Height) Then
  484. Begin {player hitted}
  485. KillPlayer;
  486. KillEnemy(CurrentRecord);
  487. End Else CurrentRecord:= Next;
  488. End;
  489. If (XOutOfRange) or (YOutOfRange) Then {screen range exceeded?}
  490. Begin
  491. If XOutOfRange Then Enemies.HorizDirection:= -Enemies.HorizDirection
  492. Else Enemies.VertDirection:= -Enemies.VertDirection;
  493. Goto GoBack;
  494. End;
  495. End;
  496. {Update all active explosions}
  497. Procedure HandleExplosions;
  498. Begin
  499. CurrentRecord:= Explosions.FirstExplosion;
  500. While CurrentRecord <> Nil Do
  501. With CurrentRecord^, Explosions.ExplosionSprite Do
  502. Begin
  503. Frame^:= Frame^ + GameSettings.FrameDelay;
  504. If Frame^ > Height Div Width Then {explosion done?}
  505. Begin {erase it and handle results}
  506. Dispose(Frame);
  507. Erase(CurrentRecord, Explosions.FirstExplosion);
  508. If Enemies.FirstEnemy = Nil Then Event:= LevelCompleted;
  509. If Player.Lifes = 0 Then Event:= GameOver;
  510. End Else CurrentRecord:= Next;
  511. End;
  512. End;
  513. {Update all active enemy's bullets}
  514. Procedure HandleEnemysBullets;
  515. Begin
  516. CurrentRecord:= Enemies.FirstBullet;
  517. While CurrentRecord <> Nil Do
  518. Begin
  519. With Enemies.BulletSprite Do
  520. PutSprite(Trunc(CurrentRecord^.X), Trunc(CurrentRecord^.Y), Width, Height, Data, VirtualScreen1);
  521. CurrentRecord^.Y:= CurrentRecord^.Y + GameSettings.BulletOffset;
  522. If CurrentRecord^.Y > ScreenHeight Then Erase(CurrentRecord, Enemies.FirstBullet) {out of range}
  523. Else If Not PlayerKilled Then CurrentRecord:= CurrentRecord^.Next;
  524. End;
  525. End;
  526. {Update all active player's bullets}
  527. Procedure HandlePlayersBullets;
  528. Begin
  529. CurrentRecord:= Player.FirstBullet;
  530. While CurrentRecord <> Nil Do
  531. Begin
  532. CurrentRecord^.Y:= CurrentRecord^.Y - GameSettings.BulletOffset;
  533. If CurrentRecord^.Y + Player.BulletSprite.Height < 0 Then Erase(CurrentRecord, Player.FirstBullet) {out of range}
  534. Else If Not EnemyKilled Then CurrentRecord:= CurrentRecord^.Next;
  535. End;
  536. End;
  537. Begin
  538. If (Not Bussy) And (Not Paused) Then {not already bussy and game isn't paused?}
  539. Begin {move objects and handle events...}
  540. Bussy:= True;
  541. HandleKbdEvents;
  542. HandleEnemies;
  543. HandleExplosions;
  544. HandleEnemysBullets;
  545. HandlePlayersBullets;
  546. Bussy:= False;
  547. End;
  548. Inline($9C); {pushf}
  549. OldTimerHandler;
  550. End;
  551. {*** INITALLIZATION AND SETUP ***}
  552. {Setup player}
  553. Procedure SetupPlayer;
  554. Begin
  555. With Player, SpaceShip Do
  556. Begin
  557. X:= (ScreenWidth - Width) Div 2; {set in the middle at the bottom of the screen...}
  558. Y:= ScreenHeight - Height;
  559. FirstBullet:= Nil;
  560. End;
  561. End;
  562. {Setup enemies}
  563. Procedure SetupEnemies;
  564. Begin
  565. With Enemies Do
  566. Begin
  567. FirstEnemy:= Nil;
  568. FirstBullet:= Nil;
  569. HorizDirection:= 1; {direction=left}
  570. VertDirection:= 1; {direction=down}
  571. End;
  572. End;
  573. {Load the requested level and set new settings if asked}
  574. Function LoadLevel(Level : Word; LoadSettings : Boolean) : Byte;
  575. Var Enemy : RecordPointer;
  576. Result : Byte;
  577. CurrentTimerHandler, CurrentKbdHandler : Procedure; {current interrupts}
  578. F : File;
  579. Begin
  580. GetIntVec($9, @CurrentKbdHandler); {save current interrupts and restore the old ones...}
  581. GetIntVec($1C, @CurrentTimerHandler);
  582. SetIntVec($9, @OldKbdHandler);
  583. SetIntVec($1C, @OldTimerHandler);
  584. Result:= OpenFile(F, LevelFile + IntToStr(Level)); {try to open file}
  585. If Result <> Success Then {failed?}
  586. Begin {return...}
  587. LoadLevel:= Result;
  588. SetIntVec($9, @CurrentKbdHandler);
  589. SetIntVec($1C, @CurrentTimerHandler);
  590. Exit;
  591. End;
  592. ErasePlayersBullets; {erase everything and prepare the level...}
  593. EraseEnemies;
  594. EraseExplosions;
  595. SetupPlayer;
  596. SetupEnemies;
  597. If LoadSettings Then BlockRead(F, LevelSettings, SizeOf(LevelSettingsRecord)) {load settings if requested}
  598. Else Seek(F, SizeOf(LevelSettings)); {go to spaceships-position section}
  599. While Not Eof(F) Do
  600. With Enemies, Enemy^ Do
  601. Begin {create and setup the enemies}
  602. Create(Enemies.FirstEnemy);
  603. BlockRead(F, FirstEnemy^.X, SizeOf(X));
  604. BlockRead(F, FirstEnemy^.Y, SizeOf(Y));
  605. End;
  606. Close(F); {restore old game state...}
  607. SetIntVec($9, @CurrentKbdHandler);
  608. SetIntVec($1C, @CurrentTimerHandler);
  609. LoadLevel:= Success;
  610. End;
  611. {Initallize game}
  612. Procedure Initallization;
  613. {Load setup file if avelable}
  614. Procedure Loadsetup;
  615. Var F : File;
  616. Begin
  617. If OpenFile(F, SettingsFile) <> Success Then Exit;
  618. Blockread(F, GameSettings, SizeOf(GameSettingsRecord));
  619. Close(F);
  620. End;
  621. Begin
  622. GetIntVec($9, @OldKbdHandler);
  623. GetIntVec($1C, @OldTimerHandler);
  624. OldExitProc:= ExitProc;
  625. ExitProc:= @NewExitProc;
  626. Randomize;
  627. With GameSettings Do {set defaults}
  628. Begin
  629. Left:= Ord(kLeft);
  630. Right:= Ord(kRight);
  631. Shoot:= Ord(kCtrl);
  632. BulletOffset:= 1;
  633. PlayersHorizOffset:= 1;
  634. FrameDelay:= 0.1;
  635. End;
  636. With LevelSettings Do {set defaults}
  637. Begin
  638. RandomShoot:= 800;
  639. EnemysHorizOffset:= 0.8;
  640. EnemysVertOffset:= 0.005;
  641. End;
  642. LoadSetup;
  643. SetSpeed(Speed); {change timer frequency}
  644. TypeMatic(0, 0); {change typematic rate}
  645. End;
  646. Procedure STARTGAME;
  647. Begin
  648. Event:= NoEvent; {reset default state...}
  649. Pressed:= 129;
  650. SetIntVec($1C, @NewTimerHandler); {setup new handlers...}
  651. SetIntVec($9, @NewKeyboardHandler);
  652. Repeat
  653. Flip(VirtualScreen2^, VirtualScreen1^); {animation step 1 - set background}
  654. If Not Paused Then
  655. OutPut(0, ScreenHeight - Font.Width + 1, 'LIFES:' + IntToStr(Player.Lifes) + ' LEVEL:' + IntToStr(CurrentLevel),
  656. VirtualScreen1) Else OutPut(0, ScreenHeight - Font.Width + 1, 'PAUSED', VirtualScreen1); {current state}
  657. {animation step 2 - set all active objects}
  658. With Player, SpaceShip Do
  659. Begin
  660. PutSprite(Trunc(X), Trunc(Y), Width, Height, Data, VirtualScreen1);
  661. PutSprites(FirstBullet, BulletSprite);
  662. End;
  663. With Enemies Do
  664. Begin
  665. PutSprites(FirstEnemy, Spaceship);
  666. PutSprites(FirstBullet, BulletSprite);
  667. End;
  668. PutExplosions;
  669. Flip(VirtualScreen1^, Display^); {animation step 3 - show the result on the screen}
  670. Until ((Event = LevelCompleted) And (Player.Y < -Player.SpaceShip.Height)) or
  671. ((Event = GameOver) And (Explosions.FirstExplosion = Nil)) or (Event = GameHalted);
  672. SetIntVec($1C, @OldTimerHandler); {restore hooked interrupts...}
  673. SetIntVec($9, @OldKbdHandler);
  674. End;