1 #include <SDL/SDL.h>
2 #include <SDL/SDL_image.h>
3 #include <SDL/SDL_ttf.h>
4 #include <SDL/SDL_mixer.h>
5 #include <math.h>
6 #include "tile.h"
7 #include "thing.h"
8 #include "physics.h"
9 #include "game.h"
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #ifndef __WIN32__
14 #include <sys/stat.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <pwd.h>
18 #endif
19 
20 #define CAMSCROLL 15
21 #define SCR_WIDTH 780
22 #define SCR_HEIGHT 600
23 #define SCR_BPP 32
24 #define SPR_SIZE 30
25 #define CAMSENSE 60
26 #define LVLMAX 500
27 #define THINGMAX 250
28 #define BOSSMAX 10
29 #define PARTICLEMAX 2500
30 #define ROOMMAX 100
31 #define TELERADIUS 8
32 #define TELEMODIF 0.175
33 #define TELECAP 17.5
34 #define TELEDELAY 12
35 #define TELEACCEL 3
36 #define TELEFAILSPARKS 5
37 #define DEATHDELAY 25
38 #define SNAPSIZE 3
39 #define SNAPVEL 2
40 #define TEXTSHADOW -1
41 #define BKGSCROLL 0.5
42 #define CURSORRADIUS 4
43 #define SOUNDDIST 150
44 #define MFRAGTOTAL 100
45 
46 #define WALK_SPEED 2.5
47 #define RUN_SPEED 4
48 
49 #define WMEDPOS 75
50 #define OMEDPOS 165
51 #define BMEDPOS 255
52 #define RMEDPOS 375
53 #define GMEDPOS 510
54 #define PMEDPOS 675
55 #define MEDINFODIST 45
56 
57 #define GREENFLAG 0x1
58 #define REDFLAG 0x2
59 #define CREATORFLAG 0x4
60 #define NOCOLLIDEFLAG 0x8
61 #define KEYPRESSED 0x10
62 #define REINCARNATE 0x20
63 
64 #define GAMEMAX 25
65 #define MEDALMAX 600
66 #define BKG_MAX 15
67 #define MUSIC_MAX 14
68 #define NUM_CHANNELS 8
69 
70 #define FPS_LIMIT 60
71 
72 #define GAME_TITLE "Beret"
73 
74 #define LAST_LEVEL 67
75 
76 #define A_ 0x1
77 #define B_ 0x2
78 #define C_ 0x4
79 #define D_ 0x8
80 #define E_ 0x10
81 #define F_ 0x20
82 #define G_ 0x40
83 #define H_ 0x80
84 
85 #ifdef __WIN32__
86 #define DIRSEP "\\"
87 #else
88 #define DIRSEP "/"
89 #endif
90 
91 #ifndef RESOURCE_PATH
92 #if defined __APPLE__
93 #define RESOURCE_PATH "Beret.app/Contents/Resources/"
94 #elif defined __WIN32__
95 #define RESOURCE_PATH ""
96 #else
97 #define RESOURCE_PATH "/usr/local/share/beret/"
98 #endif
99 #endif
100 
101 #ifndef SUPPORT_PATH
102 #if defined __APPLE__
103 #define SUPPORT_PATH "Library/Application Support/Beret/"
104 #elif defined __WIN32__
105 #else
106 #define SUPPORT_PATH ".beret/"
107 #endif
108 #endif
109 
110 #define QUITMOD_WIN KMOD_ALT
111 #define QUITKEY_WIN SDLK_F4
112 #define QUITMOD_LIN KMOD_CTRL
113 #define QUITKEY_LIN SDLK_c
114 #define QUITMOD_MAC KMOD_META
115 #define QUITKEY_MAC SDLK_q
116 
117 #define MAX(a,b) ((a)>(b))?(a):(b)
118 
119 #define UNF 1000
120 #define STORYLEN 21
121 #define CREAT1LEN 21
122 #define CREAT2LEN 21
123 #define CREAT3LEN 18
124 #define CONTROLLEN 19
125 #define MSGMAX 60
126 
127 
128 
129 const char* creat1[CREAT1LEN] =
130   {" ",
131    "Escape - Menu (Playtest Room)",
132    "Q - Display this guide",
133    " ",
134    "F1 - Save Room",
135    "F4 - Load Room",
136    "F9 - Set time limit for level, check blue fragments, get enemy counts",
137    "F12 - All of above except set time limit",
138    " ",
139    "Home/End - Decrease/Increase room width",
140    "Page Up/Page Down - Decrease/Increase room height",
141    "- or + - Change background",
142    "[ or ] - Change music",
143    " ",
144    "I - Open object select screen - Click to select object/tile",
145    "U - Place tile at cursor or fill selection with tiles",
146    "Y - Delete tile at cursor or delete tiles in selection",
147    "O - Place object at cursor",
148    "Tab - Change selected object subtype",
149    " ",
150    "Continue to next page..."};
151 
152 const char* creat2[CREAT2LEN] =
153   {" ",
154    "Left click and drag - Move object",
155    "Right click and drag - Select objects",
156    "ASDW/Arrow Keys - Move selected objects",
157    "Shift + ASDW/Arrow Keys - Move selected objects slowly",
158    "Delete/Backspace - Delete selected objects",
159    "V - Copy selected objects",
160    "< or > - Change object direction",
161    "L - Choose link for selected link blocks",
162    " ",
163    "G - Change grid",
164    "H - Toggle snap to grid",
165    "C - Toggle collision detection",
166    "R - Clear room",
167    "P - Set Beret's start position to cursor",
168    " ",
169    "1-0 - Choose number of entrance",
170    "Shift + 1-0 - Choose entrance number to connect to",
171    "E - Set room number to exit to",
172    " ",
173    "Continue to next page..."};
174 
175 const char* creat3[CREAT3LEN] =
176   {" ",
177    "Notes for creating a level:",
178    " ",
179    "Room 0 is the starting room for the level. Set Beret's",
180    "position in room 0 to the desired level entry point.",
181    " ",
182    "Doors and Exit Signs have an entrance number assigned",
183    "to them, as well as an entrance number and a room number",
184    "that they connect to. Make sure these match up between",
185    "rooms or the player will get a \"file not found\" error.",
186    " ",
187    "When a level is finished, use F12 to check that all 100",
188    "Blue Medallion fragments have been placed and to assign",
189    "the fragments indices. This helps keep track of exactly",
190    "which fragments have been collected. F12 also counts the",
191    "enemies in each room so that the Red Medallion can be",
192    "collected. Use F9 to set a time limit for the level on",
193    "top of these checks."};
194 
195 const char* story[STORYLEN] =
196   {"For many years, Beret was a researcher and experiment in",
197    "the Department of Telekinetics at a large research company",
198    "called the Evil Corporation. While studying telekinetics,",
199    "Beret and his research assistants succeeded in granting",
200    "Beret the power of telekinesis. Shortly after this, Beret",
201    "became disgusted with the injustices committed in the name",
202    "of scientific advance by his employers, the Three Evils of",
203    "the Evil Corporation. Beret has decided to single-handedly",
204    "destroy the entire corporation and defeat the Three Evils",
205    "using his remarkable power - a quite daunting task, due to",
206    "the high security of the Evil Corporation. Each Department",
207    "is inaccessible to anyone without the proper clearance level,",
208    "which is determined by the number of Medallions owned by that",
209    "person. Therefore, in order to reach the Three Evils, who",
210    "reside in extremely well-protected Departments, Beret will",
211    "need to collect as many Medallions as possible in each",
212    "Department that he visits in order to gain access to the next.",
213    " ",
214    "Despite his amazing power, Beret has a perilous journey ahead",
215    "of him. Only by exhibiting great cunning as well as skill",
216    "will he be able to accomplish his goal."};
217 const char* controls[CONTROLLEN] =
218   {"Running:  A and D, or left arrow key and right arrow key",
219    "Walking: Hold shift",
220    "Jumping:  W, up arrow key, or spacebar",
221    "Entering Doors/Reading Signs:  S or down arrow key",
222    " ",
223    "Save State:  F1 or 1",
224    "Load State:  F4 or 4 (hold Ctrl for backup)",
225    "Restart Room:  R",
226    "Exit Level:  Escape",
227    "Pause Game:  P",
228    " ",
229    "Telekinesis:  Left mouse button",
230    "Toggle Telekinesis Guide:  G or right mouse button",
231    "Toggle Cursor Movement Mode:  M",
232    " ",
233    "Move Camera:  I, J, K, or L",
234    " ",
235    "Toggle Full Screen:  Alt+Enter",
236    "Toggle Sound: Slash"};
237 const int levelentry[5][20] = {{UNF,UNF,2,0,6,10,UNF,UNF,16,UNF,21,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF},
238                                {UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,0,37,UNF,30,43,UNF,UNF,UNF,48,55},
239                                {UNF,UNF,UNF,UNF,84,80,95,UNF,UNF,UNF,88,UNF,68,UNF,74,UNF,UNF,0,65,UNF},
240                                {UNF,UNF,UNF,UNF,UNF,UNF,UNF,100,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF},
241                                {120,120,120,120,120,120,120,120,120,120,120,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF}};
242 const int levelnums[5][20] = {{UNF,UNF,2,1,3,4,UNF,UNF,5,UNF,6,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF},
243                               {UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,7,9,UNF,8,10,UNF,UNF,UNF,11,12},
244                               {UNF,UNF,UNF,UNF,18,17,20,UNF,UNF,UNF,19,UNF,15,UNF,16,UNF,UNF,13,14,UNF},
245                               {UNF,UNF,UNF,UNF,UNF,UNF,UNF,21,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF},
246                               {1,2,3,4,5,6,7,8,9,10,22,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF,UNF}};
247 const int numofst[22] = {1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1};
248 const int maplastlevel[5] = {10, 39, 46, 67, 99};
249 const char* wingnames[5] = {"West Wing", "North Wing", "East Wing", "South Wing", "Tower"};
250 const char* deptnames[5][20] = {{" "," ","Telekinetics","Telekinetics","Telekinetics","Telekinetics"," "," ","Connectivity"," ",
251 				 "Connectivity"," "," "," "," "," "," "," "," "," "},
252                                 {" "," "," "," "," "," "," "," "," "," ",
253 				 "Detonation","Detonation"," ","Detonation","Detonation"," "," "," ","Infection","Infection"},
254                                 {" "," "," "," ","Parapsychology","Parapsychology","Antimatter"," "," "," ",
255 				 "Antimatter"," ","Gravitation"," ","Gravitation"," "," ","Gravitation","Gravitation"," "},
256                                 {" "," "," "," "," "," "," ","Evil"," "," "," "," "," "," "," "," "," "," "," "," "},
257                                 {"Your Design","Your Design","Your Design","Your Design","Your Design","Your Design",
258 				 "Your Design","Your Design","Your Design","Your Design","Creation"," "," "," "," "," "," "," "," "," "}};
259 const char* divnames[5][20] = {{" "," ","Main","Inner","Experimental","Defense"," "," ","Collaboration"," ",
260 				"Main"," "," "," "," "," "," "," "," "," "},
261                                {" "," "," "," "," "," "," "," "," "," ",
262 				"Metaphysics","Spiky Death"," ","Pyrotechnics","Adhesives"," "," "," ","Asphyxiation","Contagion"},
263                                {" "," "," "," ","Unknown","Spectral","Main"," "," "," ",
264 				"Motion"," ","Localization"," ","Ubiquitous"," "," ","Object Specific","Hub"," "},
265                                {" "," "," "," "," "," "," ","-"," "," "," "," "," "," "," "," "," "," "," "," "},
266                                {"1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th","-"," "," "," "," "," "," "," "," "," "}};
267 const char* msgs[MSGMAX][8] =
268   {{"This is the map screen, on which you can view a",
269     "floor plan of the Evil Corporation. Each level",
270     "that you can access is shown, as is the level that",
271     "you are closest to opening. Move the cursor over any",
272     "level to view information such as which Medallions",
273     "have been collected in the level, or the number of",
274     "Medallions that are required for entry to that level.",
275     "Click on a level to enter it."},
276    {"[ Basic Controls ]"," ",
277     "Walk with A and D or the left and right arrow keys.",
278     "Jump with W, the up arrow key, or the spacebar.",
279     "Read signs and enter doors with S or",
280     "the down arrow key.",
281     "Hold the shift key to walk slowly.",
282     "Pause the game by pressing P."},
283    {"[ Medallions ]"," ",
284     "In each level there are six Medallions to collect.",
285     "Here is an introduction to the first three:"," ",
286     "White Medallion - Collect somewhere in the level",
287     "Orange Medallion - Collect four Medallion Corners",
288     "Blue Medallion - Collect 100 Medallion Fragments"},
289    {"[ Telekinesis ]"," ",
290     "Click and hold down the left mouse button to use",
291     "telekinesis. Most objects can be picked up and moved",
292     "around. Press G or click the right mouse button to",
293     "toggle the telekinesis range guide. Note that Beret must",
294     "be able to see the object he is using telekinesis on,",
295     "and he can not see through solid walls."},
296    {"[ Enemies ]", " ",
297     "Even with the power of telekinesis on your side,",
298     "enemies can still be a large threat. Many enemies,",
299     "such as Robots, will be destroyed if they are",
300     "smashed into a wall or object. You can also destroy",
301     "enemies by smashing an object such as a block",
302     "into them."},
303    {"[ Clearing a Room ]"," ",
304     "You will obtain the Red Medallion once all the rooms",
305     "in the level have been cleared. To clear a room, you",
306     "must destroy all the enemies in the room and then exit",
307     "that room. If you then return to that room, the",
308     "enemies will be back, but the room will still be cleared."," "},
309    {"[ Save States ]", " "," ",
310     "Press F1 to save the the current state of a room.",
311     "This state can then be loaded at any time, even after",
312     "dying, by pressing F4. Once you leave a room, you can",
313     "no longer load save states created in that room.",
314     " "},
315    {"[ Medallions ]", " ",
316     "Here is a description of the rest of the Medallions:",
317     "Red Medallion - Defeat all enemies in each room",
318     "Green Medallion - Reach the exit before the timer on",
319     "the status bar reaches zero",
320     "Purple Medallion - Simply reach the exit",
321     " "},
322    {"[ Block Types ]", " ",
323     "There are several types of blocks that you will",
324     "encounter, but the three that you will see most",
325     "often are Wood Blocks, Ice Blocks, and Stone Blocks.",
326     "Wood Blocks have no special properties, but Ice Blocks",
327     "are very slippery and slide a lot when thrown.",
328     "Stone Blocks are too heavy to be lifted."},
329    {"[ X Only Walls ]", " ", " ",
330     "Certain types of walls only let specific types of",
331     "things through. Beret Only Walls are blue, and only",
332     "let Beret go through them. Object Only Walls are red,",
333     "and they let everything except Beret through.", " "},
334    {"[ Doors ]", " ",
335     "To enter a door, press S or the down arrow key.", " ",
336     "This large door is the level's exit. Go through it",
337     "when you are ready to end the level!",
338     " ", " "},
339    {"[ Clearing a Room ]",
340     "A flashing red X on the status bar indicates that the",
341     "current room has not yet been cleared and that there",
342     "are still enemies in the room. A red circle means that",
343     "there are no enemies left in the room, but that the room",
344     "has not yet been cleared - you will need to leave the room",
345     "to clear it. If there is a green circle on the status bar,",
346     "the current room has already been cleared."},
347    {"[ Restarting and Leaving ]", " ",
348     "If you need to restart a room for any reason, you can",
349     "do so by pressing R. If you need to return to the map",
350     "screen, press Escape. Note that your progress in the",
351     "level will not be saved - in order to save your",
352     "progress you must reach the exit of the level", " "},
353    {"[ Spike Balls ]", " ",
354     "Robots are not the only danger that you will face within",
355     "the Evil Corporation. Another common type of enemy is",
356     "the Spike Ball, which floats back and forth either",
357     "horizontally or vertically. Like Robots, they can be",
358     "smashed into walls or objects and destroyed.", " "},
359    {"[ Telekinesis Blocks ]", " ",
360     "Telekinesis Blocks are blocks that become solid if",
361     "they detect the use of telekinesis, but are otherwise",
362     "insubstantial. Anything trapped inside a Telekinesis",
363     "Block when it materializes will be destroyed. This",
364     "includes Beret, so watch out!", " "},
365    {"[ Teleseekers and Superbots ]",
366     "Unfortunately, the Robots and Spike Balls that you have",
367     "encountered this far are some of the least troublesome of",
368     "the enemies that you will face. Superbots are a speedy",
369     "version of Robots. Red Teleseekers will chase you if they",
370     "detect that you are using telekinesis, and Yellow",
371     "Teleseekers will chase you if they detect that you are not.",
372     "Teleseekers can not be destroyed by smashing."},
373    {"[ Exiting a Level ]"," ",
374     "When you leave a level through the exit door, all of the",
375     "Medallions that you have collected in the level are added",
376     "to your Medallion collection. All other collectables that",
377     "you have gathered in the level will also be saved, and",
378     "they will appear transparent the next time you play the",
379     "level to indicate that they have already been collected."},
380    {"[ Medallion Fragments ]", " ",
381     "Medallion Fragments can be picked up and moved around with",
382     "telekinesis. Try picking up the Fragments in the",
383     "enclosure to the right of this sign.", " ",
384     "A blue circle in the Blue Medallion section on the status",
385     "bar indicates that there are no more Fragments in the room."},
386    {"[ Aura Drops ]"," "," ",
387     "Aura Drops are enemies that create an aura of deadly",
388     "poison around themselves. This aura can kill Beret",
389     "and other enemies - only Aura Drops are immune to it.",
390     "Even if an Aura Drop is killed, its aura will remain in",
391     "the location of its death."},
392    {"[ Continuing Play ]", " ",
393     "If you exit the game in the middle of a level, you can",
394     "choose to continue your game the next time you play.",
395     "If you do, you will start in the same room that you were",
396     "in when you quit.",
397     "You will also be able to load a save state that you",
398     "had made in that room."},
399    {"[ Moving the Camera ]"," "," ",
400     "In some situations you may need to look around the room you",
401     "are in. To do this, press I to move the camera upwards,",
402     "J to move it to the left, K to move it downwards,",
403     "and L to move it to the right."," "},
404    {"[ Spikes and Hoppers ]", " ",
405     "Be very careful around spikes - a single touch will kill",
406     "you. Enemies will also be destroyed if they touch spikes.", " ",
407     "Hoppers are a slightly more dangerous enemy than Robots",
408     "and Spike Balls, since they are a bit smarter and can hop",
409     "over low obstacles and even up stairs."},
410    {"[ Link Blocks ]", " ",
411     "Link Blocks are the main topic of research in the",
412     "Department of Connectivity. These types of blocks link to",
413     "another object and either influence that object's movements",
414     "or are influenced by its movements. You can not use your",
415     "telekinesis to move any object that is being influenced,",
416     "whether that object is a Link Block or some other object."},
417    {"[ Sign Colors ]", " ",
418     "Because of the astounding size of the Evil Corporation,",
419     "it was a common occurrence for a researcher to get dreadfully",
420     "lost in a Department. To remedy this issue, the path to the",
421     "exit of each Department has been marked by signs with green",
422     "arrows on them. Golden arrows on signs mark paths to other",
423     "rooms that are not directly on the path to the exit."},
424    {"[ Fireworks ]", " ",
425     "Researchers in the Department of Detonation study various",
426     "sorts of explosives - in the Pyrotechnics Division several",
427     "dangerous types of Fireworks have been created. Explosions",
428     "can kill any animate object and also destroy Cracked Blocks.",
429     "Orange Fireworks are the least explosive, then Red Fireworks,",
430     "and Green Fireworks are the most explosive."},
431    {"[ Fake Blocks ]", " ",
432     "Fake Blocks are extremely sneaky enemies that disguise",
433     "themselves as normal Blocks until Beret comes too close, and",
434     "then they attack. The Stone Fake Block variety can not be",
435     "picked up, and none of the Fake Block types can be smashed.",
436     "As long as a Fake Block is disguised, it is not dangerous to",
437     "touch or even stand on."},
438    {"[ Choice Only Walls ]", " ", " ",
439     "If a Choice Only Wall is touched by Beret, it will become a",
440     "Beret Only Wall. If a Choice Only Wall is touched by any",
441     "other object, it will become an Object Only Wall."," "," "},
442    {"[ Cursor Movement Modes ]"," ",
443     "The alternate cursor movement mode is that the cursor",
444     "remains stationary relative to the room, even if the",
445     "screen scrolls. Press M you would like to toggle the",
446     "cursor movement mode. You can also change this option",
447     "on the options menu by pressing Escape and",
448     "selecting \"Options\"."},
449    {"[ Save and Load State Shortcuts ]"," "," ",
450     "Since saving and loading your state quickly can",
451     "be crucial while trying to get the Green Medallion,",
452     "you can use F1 or the 1 key to save your state, and",
453     "you can use F4 or the 4 key to load state."," "},
454    {"[ A Few Notes About This Door ]"," "," ",
455     "Don't forget that going through this door will cause all the",
456     "enemies in this room to come back when you return. If you are",
457     "trying to get the Red Medallion, you may want to kill all the",
458     "enemies and then come back to this door.",
459     " "},
460    {"[ Super Hoppers ]"," "," ",
461     "Super Hoppers are an upgraded version of Hoppers",
462     "that have far superior jumping ability. They can",
463     "jump as high as Beret can!",
464     " "," "},
465    {"[ Solidity Blocks and Switches ]", " ",
466     "When you hit a Solidity Switch with an object, all the Solidity",
467     "Blocks in the room will toggle their solidity. Just as with",
468     "Telekinesis Blocks, anything that is caught inside a Solidity",
469     "Block when it becomes solid will be destroyed. Explosions can",
470     "also trigger Solidity Switches."," "},
471    {"[ Sticky Bombs ]",
472     "In the Adhesives Division of the Department of Detonation, a",
473     "type of explosive called Sticky Bombs has been created. Sticky",
474     "Bombs can be attached to any surface, but then they will not",
475     "come off. Once a Sticky Bomb is attached to something, it will",
476     "begin a countdown, and when its timer runs out, it will explode.",
477     "The explosion of a Sticky Bomb is of the same magnitude as",
478     "that of an Orange Firework."},
479    {"[ Annoying Blocks ]", " ",
480     "Annoying Blocks come in two varieties - ones that move",
481     "horizontally and ones that move vertically. Both types",
482     "will try to match Beret's position. So, the horizontal",
483     "Annoying Blocks will try to stay above or below Beret,",
484     "and the vertical Annoying Blocks will try to stay to",
485     "Beret's left or right."},
486    {"[ Infectlings ]", " ", " ",
487     "Infectlings are objects that can change another object",
488     "into an Infectling when they touch it. Black Infectlings",
489     "affect only animate objects, but White Infectlings affect",
490     "all objects."," "},
491    {"[ Pumping Platforms ]"," "," ",
492     "A Platform can be \"pumped\" if you block its movement path",
493     "and jump on it repeatedly. This technique can be used to",
494     "force a Platform to travel a large distance.",
495     " ", " "},
496    {"[ Save States ]"," "," ",
497     "If you haven't tried out save states yet, this is a good spot",
498     "to do so - you wouldn't want to fall all the way down and have",
499     "to climb back up, would you? Save your state using F1, and then",
500     "if you fall to the bottom you can load your state using F4.",
501     " "},
502    {"[ Department of Infection ]"," ",
503     "The Department of Infection is the last department in the North",
504     "Wing of the Evil Corporation, and so the second of the Three",
505     "Evils, Stoney, resides here. The Medallions in this level are",
506     "very tricky to get, so if you get stuck keep in mind that you",
507     "only need to get to the end of the level and defeat Stoney to",
508     "progress to the East Wing of the Evil Corporation."},
509    {"[ Stoney ]"," "," ",
510     "Behind this menacing door awaits the second of the Three",
511     "Evils. Stoney is a worthy foe - you have a difficult battle",
512     "ahead of you. Once you manage to defeat him, though, you will",
513     "be able to continue on to the East Wing of the Evil Corporation.",
514     " "},
515    {"[ Save States ]"," ",
516     "While save states can take a bit of time to get used to, it",
517     "is certainly worth it, as they can make tricky parts of the",
518     "Evil Corporation much less frustrating. If you have not yet",
519     "begun using save states, you may want to try them out here,",
520     "where save stating after you climb parts of this room can",
521     "save you a lot of time if you fall."},
522    {"[ Gravity Blocks and Floating Blocks ]"," ",
523     "In the Object Specific Division of the Department of",
524     "Gravitation, blocks that are affected by gravity in various",
525     "ways have been designed. Gravity Blocks always fall in the",
526     "direction that the arrow on them indicates. Floating Blocks",
527     "are not affected by gravity at all.",
528     " "},
529    {"[ Force Fields ]"," ",
530     "If anything touches a Force Field, that object will be forced",
531     "to move in the direction that the Force Field indicates. It",
532     "can sometimes be very difficult to get an object out of a",
533     "Force Field, so be careful. Beret is also affected by Force",
534     "Fields, which can be helpful at times but sometimes very",
535     "dangerous."},
536    {"[ More Teleseekers ]"," ",
537     "The Red and Yellow Teleseekers that you have now come to",
538     "know so well are only two of the four types of Teleseekers",
539     "that inhabit the Evil Corporation. The other two types are",
540     "the Blue and Purple Teleseekers. Blue Teleseekers will always",
541     "chase Beret, regardless of his use of telekinesis, and Purple",
542     "Teleseekers will always fly away from Beret."},
543    {"[ Gravity Switches ]"," ",
544     "In the Ubiquitous Division of the Department of",
545     "Gravitation, researchers have created a type of switch that",
546     "can change the gravity in a room. These switches can be",
547     "switched in the same ways that Solidity Switches can:",
548     "by hitting them with an object or an explosive. Only",
549     "inanimate objects are affected by the gravity shift."},
550    {"[ Turrets ]"," ",
551     "The enemies that you must face will continue to grow more",
552     "and more fiendish as you delve deeper into the Evil",
553     "Corporation. Turrets are enemies that rapidly shoot bullets",
554     "which can destroy anything that is animate. They can not be",
555     "destroyed by smashing. Beret will not be harmed by touching",
556     "the body of a Turret."},
557    {"[ Reincarnators ]"," ",
558     "Researchers in the Department of Parapsychology have",
559     "created machines called Reincarnators. Once a Reincarnator",
560     "is activated by the touch of a person, it will be able to",
561     "sense that person's death and pull their spirit into a copy",
562     "of their body, which is created at the machine. Only one",
563     "Reincarnator can be active at a time."},
564    {"[ Ghosts ]", " ",
565     "Due to the research on spiritual energy within the",
566     "Department of Parapsychology, the department has become",
567     "infested with Ghosts. Ghosts will not become visible until",
568     "they are fairly close to Beret, but they are still there even",
569     "while invisible. Ghosts can be smashed, as if they move too",
570     "quickly, they will be forced to be solid."},
571    {"[ Ghost Blocks ]", " ", " ",
572     "If they are moved with enough speed, Ghost Blocks become",
573     "immaterial and can pass through anything, even walls. They",
574     "can still be moved with telekinesis even when they are not",
575     "solid.", " "},
576    {"[ Blockster ]", " ",
577     "The last department in each wing of the Evil Corporation is",
578     "home to one of the Three Evils. The Evil in command of the",
579     "West Wing is named Blockster, and his lair is on the other",
580     "side of this door. Once you defeat Blockster, you will be",
581     "able to continue on to the North Wing and the departments it",
582     "contains."},
583   {"[ Antimatter ]", " ",
584    "The researchers in the Department of Antimatter have",
585    "succeeded in creating chunks of Antimatter, as well as walls",
586    "made out of Antimatter. Antimatter destroys anything it",
587    "touches, even walls. It must be handled with caution, or the",
588    "entire department could be destroyed... and you don't want",
589    "that, do you?"},
590   {"[ Platforms ]", " ",
591    "Platforms come in four varieties, one for each orthogonal",
592    "direction. When they detect the presence and then absence of",
593    "pressure from above (for example, if Beret steps on a Platform",
594    "and then jumps off), they will move in the direction associated",
595    "with that type of Platform.",
596    " "},
597   {"[ Fake Fireworks ]", " ",
598    "Similarly to Fake Blocks, Fake Fireworks appear to be normal",
599    "Fireworks until Beret comes close, and then they lunge for the",
600    "kill. They retain the explosive nature of Fireworks, making them",
601    "quite dangerous, as even if they miss Beret they may still",
602    "explode nearby.", " "},
603   {"[ Matterly ]", " ",
604    "The third and final Evil, Matterly, is waiting for you behind",
605    "this door. She is trickier and more fiendish than the Evils",
606    "you have defeated so far, and there are rumors that Matterly",
607    "has been developing experimental weapons that she has kept",
608    "secret even from the other Evils, so be on your guard!", " "},
609    {"[ Enemies ]", " ", " ",
610     "Don't forget to defeat all the enemies in this room",
611     "if you want to get the Red Medallion!", " ", " ", " "},
612    {"[ Top Hat ]", " ",
613     "The Three Evils have now been defeated, but Beret's quest",
614     "is not quite over! His long-lost evil twin brother, Top Hat,",
615     "has been the true power behind the Evil Corporation the",
616     "whole time, using the Evils as pawns in his evil plots! Now",
617     "Beret must stop Top Hat before it's too late!!!!!", " "},
618    {"[ Antiseekers ]", " ", " ",
619     "Antiseekers chase Beret all the time, like Blue",
620     "Teleseekers, and they are made of Antimatter.",
621     "They have fancy visors as well.", " ", " "},
622    {"[ Top Hat ]", " ",
623     "The final battle! Top Hat has protected himself with a",
624     "field of telekinetic energy that will repel any object",
625     "inside it. In other words, nothing can touch him! However,",
626     "the field is sustained by five Field Generators in this",
627     "room. If they can all be destroyed, Beret will have a",
628     "chance to strike at Top Hat and defeat him!"},
629    {"[ The End ]", " ",
630     "Congratulations!",
631     "Top Hat has been defeated, and Beret has finally",
632     "succeeded in destroying the Evil Corporation! You can",
633     "now play as Top Hat by pressing T. When you have all",
634     "of the Medallions, you will open something very special",
635     "in the Tower of the Evil Corporation."},
636    {"[ Alternate Run Controls ]", " ",
637     "If you would like to use double-tapping to run instead",
638     "of holding shift to walk slowly, you can change this",
639     "setting in the options menu, which is accessed by",
640     "pressing Escape and selecting \"Options\". If you use",
641     "the alternate control scheme, double-tap the direction",
642     "you would like to run in."},
643    {"[ Backup Save State ]", " ", " ",
644     "If you ever accidentally save state in a bad spot, such",
645     "as right before Beret dies, you can load the save state",
646     "that you had saved before the current one by pressing",
647     "Ctrl when you load state.", " "}};
648 
649 const int medalsprx[6] = {5,5,5,5,5,3};
650 const int medalspry[6] = {15,18,19,16,17,11};
651 
652 const char *copyright = "Beret v1.2.1 Copyright 2011 Nigel Kilmer";
653 
654 int i, j;
655 int quit=0;
656 int inactive = 0, paused = 0, guides = 0, getinput = 0, statusbar = 1;
657 int istophat = 0;
658 int fullscreenmode = 0;
659 int camx=0, camy=0;
660 int mx, my, telething = -1, canbetelething = -1, touchtele, telestat;
661 int telesource = -1;
662 int cantele = 0, alwaystele = 0, teleon = 0;
663 int mousecammode = 0, runningmode = 0, deathstateload = 0, opencreator = 0, secretcode = 0;
664 int count = 0, svstcount = 0;
665 int deathtime = 0, fadetime=0, fadereason, walkaway;
666 int curparticle = 0;
667 int curmusic = 0, musicon = 1, curbkg = 1;
668 int startx, starty, startdir;
669 
670 int spminutes, spseconds, spframes, bspminutes, bspseconds, bspframes;
671 int enemdead[ROOMMAX], enemdeadhere, enemalldead, benemalldead, bbossvar;
672 int gotmwht, bgotmwht, gotgmed, bgotgmed, gotpmed, bgotpmed;
673 int gotmcrn[4], bgotmcrn[4];
674 int gotmfrag[MFRAGTOTAL], bgotmfrag[MFRAGTOTAL], mfragcount, bmfragcount;
675 int hassavestate, hasbkpsavestate, loadedbkpsavestate, usedsavestate;
676 int hasgmed, haspmed, hasrmed, hasomed, hasbmed, haswmed;
677 
678 int credittime;
679 int creatormode = 0, crtgridsize, crtgridsnap, crtplacetile;
680 int crtselect, crtplacetype, crtplacesubtype, crtlinking, crtinventory;
681 int crtplacedir, crtentrance, crtexit, crtexitroom, crtmessage;
682 int selx, sely;
683 
684 int ingame, ingamereturn, bwalkaway;
685 int cancont, contlvl;
686 int initmapmsg, initgamemsg;
687 int trgentrance, nextopenlevel, trgmap;
688 int hassaved, loaderror, yesno, optselect, optmax, gotallfrags;
689 
690 int mouse_visible;
691 
692 int tiles[LVLMAX][LVLMAX][3], tilebackup[LVLMAX][LVLMAX][3];
693 Thing beret;
694 Thing things[THINGMAX], thingbackup[THINGMAX];
695 int selection[THINGMAX];
696 Particle particles[PARTICLEMAX];
697 
698 int lvlWidth, lvlHeight;
699 int lvlCode, areaCode, gameNum, mapCode, tempAreaCode, msgcode;
700 int mapselect;
701 int gamemedals;
702 char gamename[25];
703 int gotmedals[MEDALMAX];
704 char game_gotfrags[100][MFRAGTOTAL];
705 char game_gotcorners[100][4];
706 char game_enemdead[100][ROOMMAX];
707 int beatlevel[100];
708 int gravdir, switchflags;
709 
710 char support_path[250];
711 
712 char msgline[250];
713 
714 SDL_Surface* screen = NULL;
715 SDL_Surface* background = NULL;
716 SDL_Surface* invbackground = NULL;
717 SDL_Surface* spritesheet = NULL;
718 SDL_Surface* tilesheet = NULL;
719 SDL_Surface* teleguide = NULL;
720 SDL_Surface* title = NULL;
721 SDL_Surface* credits = NULL;
722 SDL_Surface* mapbkg = NULL;
723 SDL_Surface* pit = NULL;
724 SDL_Surface* lvlnumbkg = NULL;
725 SDL_Surface* gameselect = NULL;
726 SDL_Surface* msgback = NULL;
727 SDL_Surface* optback= NULL;
728 SDL_Surface* getinputback = NULL;
729 SDL_Surface* message = NULL;
730 SDL_Surface* fades[5];
731 
732 char messagestr[200], inputstr[25], getinputstr[50];
733 int inputpos, inputlength;
734 
735 FILE* file;
736 FILE* msgfile;
737 
738 Uint32 selcolor;
739 
740 SDL_Event event;
741 int key1=NONE, key2=NONE, key3=NONE;
742 int remkey1;
743 int camxkey=NONE, camykey=NONE;
744 int freecam;
745 
746 TTF_Font *font = NULL, *smfont = NULL, *medfont = NULL;
747 SDL_Color textcolor = {0, 0, 0}, textcolor2 = {220,220,220};
748 Uint32 redColor, whiteColor, greenColor, blueColor;
749 
750 Mix_Music* music[MUSIC_MAX];
751 Mix_Chunk* sound[SOUND_MAX];
752 
753 
f_sqr(float x)754 float f_sqr(float x) {return x*x;}
f_abs(float x)755 float f_abs(float x) {return x<0?-x:x;}
756 
757 
758 void resolve_fade(void);
759 void resolve_input(SDLKey);
760 void start_map(void);
761 
init_fade(int ftype)762 void init_fade(int ftype) {
763   if (fadetime == 0) {
764     fadetime = 14;
765     fadereason = ftype;
766     svstcount = 0;
767   }
768 }
769 
load_img(char * filename)770 SDL_Surface* load_img(char* filename) {
771   SDL_Surface* loadImg = NULL;
772   SDL_Surface* optImg = NULL;
773 
774   loadImg = IMG_Load(filename);
775 
776   // Create the optimized image
777   if (loadImg != NULL) {
778     optImg = SDL_DisplayFormatAlpha(loadImg);
779     SDL_FreeSurface(loadImg);
780   }
781 
782   return optImg;
783 }
784 
785 
apply_surface(int x,int y,SDL_Surface * source,SDL_Surface * dest)786 void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* dest) {
787   SDL_Rect offset;
788   offset.x = x;
789   offset.y = y;
790 
791   SDL_BlitSurface(source, NULL, dest, &offset);
792 }
793 
794 
apply_particle(int x,int y,int color)795 void apply_particle(int x, int y, int color) {
796   SDL_Rect offset;
797   offset.x = x-2;
798   offset.y = y-2;
799 
800   // Create the rectangle specifying area of sprite sheet to apply
801   SDL_Rect clip;
802   clip.x = 19*SPR_SIZE+4*(color%7);
803   clip.y = 4*SPR_SIZE+4*(color/7);
804   clip.w = 4;
805   clip.h = 4;
806 
807   SDL_BlitSurface(spritesheet, &clip, screen, &offset);
808 }
809 
810 
apply_sprite(int x,int y,int sprx,int spry,int sprw,int sprh,SDL_Surface * source,SDL_Surface * dest)811 void apply_sprite(int x, int y, int sprx, int spry, int sprw, int sprh,
812                   SDL_Surface* source, SDL_Surface* dest) {
813   SDL_Rect offset;
814   offset.x = x;
815   offset.y = y;
816 
817   // Create the rectangle specifying area of sprite sheet to apply
818   SDL_Rect clip;
819   clip.x = sprx*SPR_SIZE;
820   clip.y = spry*SPR_SIZE;
821   clip.w = sprw*SPR_SIZE;
822   clip.h = sprh*SPR_SIZE;
823 
824   SDL_BlitSurface(source, &clip, dest, &offset);
825 }
826 
827 
init()828 int init() {
829 
830   #ifdef __WIN32__
831   sprintf(support_path, "");
832   #else
833   char filestr[512];
834   // Get the home directory of the user.
835   struct passwd *pwd = getpwuid(getuid());
836   if (pwd) {
837     // Create the directory for application data.
838     sprintf(support_path, "%s/%s", pwd->pw_dir, SUPPORT_PATH);
839     mkdir(support_path, S_IRWXU);
840     // Create the directory for custom room data.
841     sprintf(filestr, "%s/rooms", support_path);
842     mkdir(filestr, S_IRWXU);
843     // Create the directory for save data.
844     sprintf(filestr, "%s/saves", support_path);
845     mkdir(filestr, S_IRWXU);
846   }
847   #endif
848 
849   if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
850     printf("Error: couldn't initialize SDL\n");
851     return 0;
852   }
853 
854   // For some reason this causes X windows (at least under Awesome WM)
855   // to refuse to accept keyboard input.
856   SDL_Surface* icon = SDL_LoadBMP(RESOURCE_PATH "images" DIRSEP "block.bmp");
857   SDL_WM_SetIcon(icon, NULL);
858 
859   screen = SDL_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, SCR_BPP, SDL_FULLSCREEN);
860   fullscreenmode = 1;
861   if (screen == NULL) {
862     printf("Error: couldn't initialize the screen\n");
863     return 0;
864   }
865 
866   if (TTF_Init() == -1) {
867     printf("Error: couldn't initalize fonts\n");
868     return 0;
869   }
870 
871   if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == -1) {
872     printf("Error: couldn't initalize sound handling\n");
873     return 0;
874   }
875   Mix_AllocateChannels(NUM_CHANNELS);
876 
877   SDL_ShowCursor(SDL_DISABLE);
878   mouse_visible = 0;
879 
880   selcolor = SDL_MapRGB(screen->format, 0, 0xd0, 0x20);
881 
882   SDL_WM_SetCaption(GAME_TITLE, NULL);
883 
884   return 1;
885 }
886 
887 
load_files()888 int load_files() {
889   char bkgstr[512];
890 
891   // Load images
892   tilesheet = load_img(RESOURCE_PATH "images" DIRSEP "tilesheet.png");
893   spritesheet = load_img(RESOURCE_PATH "images" DIRSEP "spritesheet.png");
894   invbackground = load_img(RESOURCE_PATH "images" DIRSEP "inventory.png");
895   background = load_img(RESOURCE_PATH "images" DIRSEP "bkg1.png");
896   teleguide = load_img(RESOURCE_PATH "images" DIRSEP "teleguide.png");
897   title = load_img(RESOURCE_PATH "images" DIRSEP "title.png");
898   credits = load_img(RESOURCE_PATH "images" DIRSEP "credits.png");
899   mapbkg = load_img(RESOURCE_PATH "images" DIRSEP "mapbkg.png");
900   pit = load_img(RESOURCE_PATH "images" DIRSEP "pit.png");
901   lvlnumbkg = load_img(RESOURCE_PATH "images" DIRSEP "lvlnum.png");
902   gameselect = load_img(RESOURCE_PATH "images" DIRSEP "gameselect.png");
903   msgback = load_img(RESOURCE_PATH "images" DIRSEP "msg.png");
904   optback = load_img(RESOURCE_PATH "images" DIRSEP "opt.png");
905   getinputback = load_img(RESOURCE_PATH "images" DIRSEP "getinput.png");
906   for (i=1;i<=5; i++) {
907     sprintf(bkgstr, "%simages%sfade%d.png", RESOURCE_PATH, DIRSEP, i);
908     fades[i-1] = load_img(bkgstr);
909   }
910 
911   // Load fonts
912   font = TTF_OpenFont(RESOURCE_PATH "AveriaSansGWF-Regular.ttf", 24);
913   smfont = TTF_OpenFont(RESOURCE_PATH "AveriaSansGWF-Regular.ttf", 9);
914   medfont = TTF_OpenFont(RESOURCE_PATH "AveriaSansGWF-Regular.ttf", 16);
915 
916   // Load music
917   for (i=0; i<MUSIC_MAX; i++) {
918     sprintf(bkgstr, "%smusic%sberet%d.ogg", RESOURCE_PATH, DIRSEP, i);
919     if (!(music[i] = Mix_LoadMUS(bkgstr)))
920       return 0;
921   }
922 
923   //Load sound effects
924   sound[SND_KNOCK] = Mix_LoadWAV(RESOURCE_PATH "sfx/knock.wav");
925   sound[SND_KNOCK+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/knock2.wav");
926   sound[SND_KNOCK+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/knock3.wav");
927   sound[SND_CLINK] = Mix_LoadWAV(RESOURCE_PATH "sfx/clink.wav");
928   sound[SND_CLINK+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/clink2.wav");
929   sound[SND_CLINK+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/clink3.wav");
930   sound[SND_CRUNCH] = Mix_LoadWAV(RESOURCE_PATH "sfx/crunch.wav");
931   sound[SND_CRUNCH+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/crunch2.wav");
932   sound[SND_CRUNCH+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/crunch3.wav");
933   sound[SND_SQUELCH] = Mix_LoadWAV(RESOURCE_PATH "sfx/squelch.wav");
934   sound[SND_SQUELCH+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/squelch2.wav");
935   sound[SND_SQUELCH+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/squelch3.wav");
936   sound[SND_STEP] = Mix_LoadWAV(RESOURCE_PATH "sfx/step1.wav");
937   sound[SND_STEP+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/step2.wav");
938   sound[SND_STEP+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/step3.wav");
939   sound[SND_JUMP] = Mix_LoadWAV(RESOURCE_PATH "sfx/jump1.wav");
940   sound[SND_JUMP+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/jump2.wav");
941   sound[SND_JUMP+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/jump3.wav");
942   sound[SND_COLLECT] = Mix_LoadWAV(RESOURCE_PATH "sfx/frag1.wav");
943   sound[SND_COLLECT+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/frag2.wav");
944   sound[SND_COLLECT+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/frag3.wav");
945   sound[SND_COLLECT+3] = Mix_LoadWAV(RESOURCE_PATH "sfx/frag4.wav");
946   sound[SND_COLLECT+4] = Mix_LoadWAV(RESOURCE_PATH "sfx/frag5.wav");
947   sound[SND_COLLECT+5] = Mix_LoadWAV(RESOURCE_PATH "sfx/frag6.wav");
948   sound[SND_BOOM] = Mix_LoadWAV(RESOURCE_PATH "sfx/boom1.wav");
949   sound[SND_BOOM+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/boom2.wav");
950   sound[SND_BOOM+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/boom3.wav");
951   sound[SND_TICK] = Mix_LoadWAV(RESOURCE_PATH "sfx/tick1.wav");
952   sound[SND_TICK+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/tick2.wav");
953   sound[SND_TICK+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/tick3.wav");
954   sound[SND_HOP] = Mix_LoadWAV(RESOURCE_PATH "sfx/hop1.wav");
955   sound[SND_HOP+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/hop2.wav");
956   sound[SND_HOP+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/hop3.wav");
957   sound[SND_INFECT] = Mix_LoadWAV(RESOURCE_PATH "sfx/infect.wav");
958   sound[SND_SWITCHGR] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-gr1.wav");
959   sound[SND_SWITCHGR+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-gr2.wav");
960   sound[SND_SWITCHGR+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-gr3.wav");
961   sound[SND_SWITCHRD] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-rd1.wav");
962   sound[SND_SWITCHRD+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-rd2.wav");
963   sound[SND_SWITCHRD+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-rd3.wav");
964   sound[SND_SWITCHGV] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-gv1.wav");
965   sound[SND_SWITCHGV+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-gv2.wav");
966   sound[SND_SWITCHGV+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/switch-gv3.wav");
967   sound[SND_CHOICEBERET] = Mix_LoadWAV(RESOURCE_PATH "sfx/choice-beret1.wav");
968   sound[SND_CHOICEBERET+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/choice-beret2.wav");
969   sound[SND_CHOICEBERET+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/choice-beret3.wav");
970   sound[SND_CHOICEOBJECT] = Mix_LoadWAV(RESOURCE_PATH "sfx/choice-object1.wav");
971   sound[SND_CHOICEOBJECT+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/choice-object2.wav");
972   sound[SND_CHOICEOBJECT+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/choice-object3.wav");
973   sound[SND_TURRET] = Mix_LoadWAV(RESOURCE_PATH "sfx/shot1.wav");
974   sound[SND_TURRET+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/shot2.wav");
975   sound[SND_TURRET+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/shot3.wav");
976   sound[SND_PLATFORM] = Mix_LoadWAV(RESOURCE_PATH "sfx/platform1.wav");
977   sound[SND_PLATFORM+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/platform2.wav");
978   sound[SND_PLATFORM+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/platform3.wav");
979   sound[SND_STICK] = Mix_LoadWAV(RESOURCE_PATH "sfx/stick.wav");
980   sound[SND_ANTIMATTER] = Mix_LoadWAV(RESOURCE_PATH "sfx/antimatter1.wav");
981   sound[SND_ANTIMATTER+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/antimatter2.wav");
982   sound[SND_ANTIMATTER+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/antimatter3.wav");
983   sound[SND_FAKE] = Mix_LoadWAV(RESOURCE_PATH "sfx/fake.wav");
984   sound[SND_FAKE+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/fake2.wav");
985   sound[SND_POP] = Mix_LoadWAV(RESOURCE_PATH "sfx/pop.wav");
986   sound[SND_POP+1] = Mix_LoadWAV(RESOURCE_PATH "sfx/pop2.wav");
987   sound[SND_POP+2] = Mix_LoadWAV(RESOURCE_PATH "sfx/pop3.wav");
988   sound[SND_REGEN] = Mix_LoadWAV(RESOURCE_PATH "sfx/regen.wav");
989   sound[SND_REGENINIT] = Mix_LoadWAV(RESOURCE_PATH "sfx/regeninit.wav");
990   sound[SND_MEDW] = Mix_LoadWAV(RESOURCE_PATH "sfx/med-w.wav");
991   sound[SND_MEDO] = Mix_LoadWAV(RESOURCE_PATH "sfx/med-o.wav");
992   sound[SND_MEDB] = Mix_LoadWAV(RESOURCE_PATH "sfx/med-b.wav");
993   sound[SND_MEDR] = Mix_LoadWAV(RESOURCE_PATH "sfx/med-r.wav");
994   sound[SND_MEDG] = Mix_LoadWAV(RESOURCE_PATH "sfx/med-g.wav");
995   sound[SND_MEDP] = Mix_LoadWAV(RESOURCE_PATH "sfx/med-p.wav");
996   sound[SND_CORNER] = Mix_LoadWAV(RESOURCE_PATH "sfx/corner.wav");
997 
998   return 1;
999 }
1000 
1001 
clean_up()1002 void clean_up() {
1003 
1004   // Free surfaces
1005   SDL_FreeSurface(screen);
1006   SDL_FreeSurface(invbackground);
1007   SDL_FreeSurface(background);
1008   for (i=0; i<5; i++)
1009     SDL_FreeSurface(fades[i]);
1010   SDL_FreeSurface(tilesheet);
1011   SDL_FreeSurface(spritesheet);
1012   SDL_FreeSurface(teleguide);
1013   SDL_FreeSurface(title);
1014   SDL_FreeSurface(credits);
1015   SDL_FreeSurface(mapbkg);
1016   SDL_FreeSurface(pit);
1017   SDL_FreeSurface(lvlnumbkg);
1018   SDL_FreeSurface(gameselect);
1019   SDL_FreeSurface(msgback);
1020   SDL_FreeSurface(optback);
1021   SDL_FreeSurface(getinputback);
1022 
1023   // Free music and sounds
1024   for (i=0; i<SOUND_MAX; i++) Mix_FreeChunk(sound[i]);
1025   for (i=0; i<MUSIC_MAX; i++) Mix_FreeMusic(music[i]);
1026   Mix_CloseAudio();
1027 
1028   // Free fonts
1029   TTF_CloseFont(font);
1030   TTF_CloseFont(smfont);
1031   TTF_CloseFont(medfont);
1032   TTF_Quit();
1033 
1034   SDL_Quit();
1035 }
1036 
1037 
1038 /*
1039  * Taken from the SDL documentation and modified.
1040  * Set the pixel at (x, y) to the given value
1041  * NOTE: The surface must be locked before calling this!
1042  * Set alpha = -1 to invert color
1043  */
putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel,int alpha)1044 void putpixel(SDL_Surface *surface, int x, int y,
1045               Uint32 pixel, int alpha) {
1046   int bpp = surface->format->BytesPerPixel;
1047   /* Here p is the address to the pixel we want to set */
1048   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
1049 
1050   Uint32 oldpix = *(Uint32 *)p;
1051   if (alpha == -1) {
1052     *(Uint32 *)p = ~oldpix;
1053   } else {
1054     float falpha = (float)alpha/0xff;
1055     int r =((pixel&0xff0000)>>16)*falpha+((oldpix&0xff0000)>>16)*(1-falpha);
1056     int g =((pixel&0xff00)>>8)*falpha+((oldpix&0xff00)>>8)*(1-falpha);
1057     int b =(pixel&0xff)*falpha+(oldpix&0xff)*(1-falpha);
1058     *(Uint32 *)p = (r<<16)+(g<<8)+b;
1059   }
1060 }
1061 
1062 
1063 // draws a line from (x1, y1) to (x2, y2)
draw_line(SDL_Surface * surface,int x1,int y1,int x2,int y2,Uint32 pixel)1064 void draw_line(SDL_Surface* surface, int x1, int y1,
1065                int x2, int y2, Uint32 pixel) {
1066   int curx = x1, cury = y1;
1067   float slope = 2;
1068   if (x2 != x1) slope = 1.0*(y2-y1)/(x2-x1);
1069 
1070   while (!((f_abs(slope)>=1 && cury == y2) ||
1071            (f_abs(slope)<1 && curx == x2))) {
1072     if (curx >= 0 && curx < SCR_WIDTH &&
1073         cury >= 0 && cury < SCR_HEIGHT)
1074       putpixel(surface, curx, cury, pixel, 0xff);
1075     if (x1 == x2) {
1076       cury += cury < y2 ? 1 : -1;
1077     } else {
1078       if (slope <= -1 || slope >= 1) {
1079         cury += cury < y2 ? 1 : -1;
1080         curx = x1 + (cury-y1)/slope;
1081       } else {
1082         curx += curx < x2 ? 1 : -1;
1083         cury = y1 + slope*(curx-x1);
1084       }
1085     }
1086   }
1087   if (curx >= 0 && curx < SCR_WIDTH &&
1088       cury >= 0 && cury < SCR_HEIGHT)
1089   putpixel(surface, curx, cury, pixel, 0xff);
1090 }
1091 
1092 
1093 // draws a semi-transparent rectangle to the surface
draw_rect(SDL_Surface * surface,int x1,int y1,int x2,int y2,Uint32 pixel,int alpha)1094 void draw_rect(SDL_Surface* surface, int x1, int y1,
1095                int x2, int y2, Uint32 pixel, int alpha) {
1096   int curx, cury;
1097   if ( SDL_MUSTLOCK(screen) ) {
1098     if ( SDL_LockSurface(screen) < 0 ) {
1099       fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
1100       return;
1101     }
1102   }
1103   for (curx=x1; curx<=x2; curx++) {
1104     for (cury=y1; cury<=y2; cury++) {
1105       putpixel(surface, curx, cury, pixel, alpha);
1106     }
1107   }
1108   draw_line(surface, x1, y1, x2, y1, 0);
1109   draw_line(surface, x1, y1, x1, y2, 0);
1110   draw_line(surface, x2, y1, x2, y2, 0);
1111   draw_line(surface, x1, y2, x2, y2, 0);
1112   if (SDL_MUSTLOCK(screen)) {
1113     SDL_UnlockSurface(screen);
1114   }
1115 }
1116 
1117 
1118 // draws the screen fading out
draw_fade()1119 void draw_fade() {
1120   apply_surface(0,0,fades[fadereason==0?(fadetime-2)/3:5-((fadetime+2)/3)],screen);
1121 }
1122 
1123 
1124 // draws the particles to the screen
draw_particles()1125 void draw_particles() {
1126   for (i=0; i<PARTICLEMAX; i++) {
1127     if (particles[i].time > 0) {
1128       apply_particle(particles[i].x-camx,particles[i].y-camy,particles[i].color);
1129     }
1130   }
1131 }
1132 
1133 
create_particle(int x,int y,float vx,float vy,int color,int time)1134 void create_particle(int x, int y, float vx, float vy,
1135                      int color, int time) {
1136   particles[curparticle].x = x;
1137   particles[curparticle].y = y;
1138   particles[curparticle].vx = vx/1.5;
1139   particles[curparticle].vy = vy/1.5;
1140   particles[curparticle].color = color;
1141   particles[curparticle].time = time*1.5;
1142   curparticle++;
1143   curparticle %= PARTICLEMAX;
1144 }
1145 
1146 
stop_telekinesis()1147 void stop_telekinesis() {
1148   things[telething].telething = 0;
1149   things[telething].teledelay = TELEDELAY;
1150   telething = -1;
1151   canbetelething = -1;
1152   teleon = 0;
1153 }
1154 
1155 
display_message(int x,int y,TTF_Font * mfont,const char * msg,int bktext)1156 void display_message(int x, int y, TTF_Font *mfont, const char* msg, int bktext) {
1157   message = TTF_RenderText_Blended(mfont, msg?msg:messagestr, textcolor);
1158   apply_surface(x-message->w/2,y-message->h/2,message,screen);
1159   SDL_FreeSurface(message);
1160   if (!bktext) {
1161     message = TTF_RenderText_Blended(mfont, msg?msg:messagestr, textcolor2);
1162     apply_surface(x-message->w/2+TEXTSHADOW,y-message->h/2+TEXTSHADOW,message,screen);
1163     SDL_FreeSurface(message);
1164   }
1165 }
1166 
1167 
start_telekinesis()1168 void start_telekinesis() {
1169   if (((alwaystele && teleon) || (!alwaystele && cantele)) &&
1170       telething == -1 && canbetelething != -1) {
1171     telething = canbetelething;
1172     things[telething].telething = 1;
1173   }
1174 }
1175 
1176 
fix_camera()1177 void fix_camera() {
1178   if (camx < 0) camx = 0;
1179   if (camx > lvlWidth-SCR_WIDTH) camx = lvlWidth-SCR_WIDTH;
1180   if (camy < 0) camy = 0;
1181   if (camy > lvlHeight-SCR_HEIGHT) camy = lvlHeight-SCR_HEIGHT;
1182 }
1183 
1184 
clear_room()1185 void clear_room() {
1186   // clear position
1187   startx = 0;
1188   starty = 0;
1189 
1190   // clear things
1191   for (i=0; i<THINGMAX; i++) {
1192     things[i].type = NOTYPE;
1193     thingbackup[i].type = NOTYPE;
1194     things[i].islinked = -1;
1195     thingbackup[i].islinked = -1;
1196   }
1197 
1198   // clear tiles
1199   for (i=0; i<LVLMAX; i++)
1200     for (j=0; j<LVLMAX; j++) {
1201       tiles[i][j][0] = EMPTY;
1202       tilebackup[i][j][0] = EMPTY;
1203     }
1204 
1205   lvlWidth = SCR_WIDTH;
1206   lvlHeight = SCR_HEIGHT;
1207   fix_camera();
1208 
1209   hassaved = 0;
1210 }
1211 
1212 
center_camera()1213 void center_camera() {
1214   int curcamx = camx, curcamy = camy;
1215   camx = beret.x+beret.width/2-SCR_WIDTH/2;
1216   camy = beret.y+beret.height/2-SCR_HEIGHT/2;
1217   fix_camera();
1218   mx -= curcamx-camx;
1219   my -= curcamy-camy;
1220 }
1221 
1222 
load_backups()1223 void load_backups() {
1224   // reset flags
1225   switchflags &= ~GREENFLAG;
1226   switchflags &= ~REDFLAG;
1227   switchflags &= ~REINCARNATE;
1228   gravdir = DOWN;
1229 
1230   // reset collectibles
1231   mfragcount = bmfragcount;
1232   for (i=0; i<MFRAGTOTAL; i++) gotmfrag[i] = bgotmfrag[i];
1233   for (i=0; i<4; i++) gotmcrn[i] = bgotmcrn[i];
1234   gotmwht = bgotmwht;
1235   gotgmed = bgotgmed;
1236   gotpmed = bgotpmed;
1237   spframes = bspframes;
1238   spseconds = bspseconds;
1239   spminutes = bspminutes;
1240   enemalldead = benemalldead;
1241 
1242   // set up vars
1243   deathtime = 0;
1244   curparticle = 0;
1245   gotallfrags = 0;
1246   int* bossvars = get_bossvars();
1247   for (i=0; i<BOSSMAX-1; i++) bossvars[i] = 0;
1248   bossvars[BOSSMAX-1] = bbossvar;
1249 
1250 
1251   // reset particles
1252   for (i=0; i<PARTICLEMAX; i++) particles[i].time = 0;
1253 
1254   // set up beret
1255   if (telething > -1) stop_telekinesis();
1256   int bspeed = beret.speed;
1257   make_beret(&beret, BERET, istophat, startx, starty, 1, things);
1258   beret.speed = bspeed?bspeed:WALK_SPEED;
1259   beret.dir = startdir;
1260   walkaway = bwalkaway;
1261   center_camera();
1262 
1263   // load things
1264   enemdeadhere = enemdead[areaCode] || game_enemdead[lvlCode][areaCode];
1265   for (i=0; i<THINGMAX; i++) {
1266     if (thingbackup[i].type == NOTYPE) things[i].type = NOTYPE;
1267     else copy_thing(&thingbackup[i], things, i);
1268   }
1269 
1270   // load tiles
1271   int k;
1272   for (i=0; i<LVLMAX; i++)
1273     for (j=0; j<LVLMAX; j++)
1274       for (k=0; k<3; k++)
1275         tiles[i][j][k] = tilebackup[i][j][k];
1276 }
1277 
1278 
check_room_dead()1279 void check_room_dead() {
1280   int foundone = 0;
1281   int iter;
1282   for (iter=0; iter<THINGMAX; iter++) {
1283     if (things[iter].type != NOTYPE && !things[iter].dead && things[iter].animate) {
1284       foundone = 1;
1285       break;
1286     }
1287   }
1288   if (!foundone) {
1289     enemdeadhere = 1;
1290     for (iter=0; iter<ROOMMAX; iter++) {
1291       if (iter != areaCode && !enemdead[iter] && !game_enemdead[lvlCode][iter]) {foundone = 1; break;}
1292     }
1293     if (!foundone && !enemalldead && !hasrmed) {
1294       enemalldead = 1;
1295       play_sound(SND_MEDR);
1296       if (statusbar) {
1297         make_expl(RMEDPOS+15+camx,30+camy,0,0,RED,6,100);
1298         make_expl(RMEDPOS+15+camx,30+camy,0,0,PINK,6,40);
1299       }
1300     }
1301   }
1302 }
1303 
1304 
go_to_room(int entr,int roomnum)1305 void go_to_room(int entr, int roomnum) {
1306   trgentrance = entr;
1307   tempAreaCode = roomnum;
1308   init_fade(5);
1309 }
1310 
1311 
switch_music(int newmusic,int forceswitch)1312 void switch_music(int newmusic, int forceswitch) {
1313   if (newmusic != curmusic || forceswitch || (Mix_PlayingMusic() && (musicon == 0 || musicon == 3))) {
1314     curmusic = newmusic;
1315     if (Mix_PlayingMusic()) Mix_HaltMusic();
1316     if (musicon == 1 || musicon == 2) Mix_PlayMusic(music[curmusic], -1);
1317   }
1318 }
1319 
1320 
1321 // saves the state of the room and collectables
1322 // for returning to this point
save_room_return(int flag)1323 void save_room_return(int flag) {
1324   char filestr[512];
1325   sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
1326   file = fopen(filestr, "wb");
1327   if (file) {
1328     fwrite(&lvlCode, sizeof(int), 1, file);
1329     fwrite(&areaCode, sizeof(int), 1, file);
1330     fwrite(&bgotmwht, sizeof(int), 1, file);
1331     fwrite(bgotmcrn, sizeof(int), 4, file);
1332     fwrite(&bmfragcount, sizeof(int), 1, file);
1333     fwrite(bgotmfrag, sizeof(int), MFRAGTOTAL, file);
1334     fwrite(&bgotgmed, sizeof(int), 1, file);
1335     fwrite(&bgotpmed, sizeof(int), 1, file);
1336     fwrite(&trgentrance, sizeof(int), 1, file);
1337     fwrite(&bspminutes, sizeof(int), 1, file);
1338     fwrite(&bspseconds, sizeof(int), 1, file);
1339     fwrite(&bspframes, sizeof(int), 1, file);
1340     fwrite(&benemalldead, sizeof(int), 1, file);
1341     fwrite(enemdead, sizeof(int), ROOMMAX, file);
1342     fwrite(&haswmed, sizeof(int), 1, file);
1343     fwrite(&hasomed, sizeof(int), 1, file);
1344     fwrite(&hasbmed, sizeof(int), 1, file);
1345     fwrite(&hasrmed, sizeof(int), 1, file);
1346     fwrite(&hasgmed, sizeof(int), 1, file);
1347     fwrite(&haspmed, sizeof(int), 1, file);
1348     fwrite(&usedsavestate, sizeof(int), 1, file);
1349     fwrite(&hassavestate, sizeof(int), 1, file);
1350     fwrite(&flag, sizeof(int), 1, file);
1351     fwrite(&((get_bossvars())[BOSSMAX-1]), sizeof(int), 1, file);
1352 
1353     fclose(file);
1354   }
1355 }
1356 
1357 
1358 // sets up the room data for playing
init_play(int flag,int savereturn)1359 void init_play(int flag, int savereturn) {
1360   char filestr[512];
1361 
1362   // reset or set collectible backup data
1363   if (flag) {
1364     // Set backups to the current state
1365     bgotmwht = gotmwht;
1366     for (i=0;i<4;i++) bgotmcrn[i] = gotmcrn[i];
1367     bmfragcount = mfragcount;
1368     for (i=0;i<MFRAGTOTAL;i++) bgotmfrag[i] = gotmfrag[i];
1369     bgotgmed = gotgmed;
1370     bgotpmed = gotpmed;
1371   } else {
1372     // Starting the level, reset backups
1373     bgotmwht = 0;
1374     for (i=0;i<4;i++) bgotmcrn[i] = 0;
1375     bmfragcount = 0;
1376     for (i=0;i<MFRAGTOTAL;i++) {
1377       bgotmfrag[i] = 0;
1378       if (game_gotfrags[lvlCode][i]) bmfragcount++;
1379     }
1380     bgotgmed = 0;
1381     bgotpmed = 0;
1382   }
1383 
1384   if (!flag) {
1385     startdir = 1;
1386     bwalkaway = 0;
1387     bbossvar = 0;
1388   }
1389   // set room backups
1390   if (flag && (get_bossvars())[BOSSMAX-1]) bbossvar = 1;
1391   for (i=0; i<THINGMAX; i++) {
1392     if (things[i].type == NOTYPE) thingbackup[i].type = NOTYPE;
1393     else copy_thing(&things[i], thingbackup, i);
1394     if (flag && things[i].type == DOOR && things[i].timer == trgentrance) {
1395       startx = things[i].x+things[i].width/2-10;
1396       starty = things[i].y+things[i].height-24;
1397       bwalkaway = 0;
1398     }
1399     if (flag && things[i].type == SIGN && things[i].timer == trgentrance &&
1400         (things[i].x <= SPR_SIZE*2 || things[i].x >= lvlWidth-SPR_SIZE*3)) {
1401       startx = things[i].subtype%2?lvlWidth-20:0;
1402       starty = things[i].y+things[i].height-24;
1403       startdir = things[i].subtype%2?0:1;
1404       bwalkaway = 2-things[i].subtype%2;
1405     }
1406     if (things[i].type == WHITEMEDAL) {
1407       if (bgotmwht) thingbackup[i].type = NOTYPE;
1408       if (haswmed) thingbackup[i].subtype = 1;
1409     }
1410     if (things[i].type == MEDALCORNER) {
1411       if (bgotmcrn[things[i].subtype]) thingbackup[i].type = NOTYPE;
1412       if (game_gotcorners[lvlCode][things[i].subtype]) thingbackup[i].subtype += 4;
1413     }
1414     if (things[i].type == MEDALFRAGMENT && things[i].dir > -1) {
1415       if (bgotmfrag[things[i].dir]) thingbackup[i].type = NOTYPE;
1416       if (game_gotfrags[lvlCode][things[i].dir]) thingbackup[i].subtype += 2;
1417     }
1418   }
1419   int k;
1420   for (i=0; i<LVLMAX; i++)
1421     for (j=0; j<LVLMAX; j++)
1422       for (k=0; k<3; k++)
1423         tilebackup[i][j][k] = tiles[i][j][k];
1424   hassavestate = 0;
1425   hasbkpsavestate = 0;
1426   loadedbkpsavestate = 0;
1427   if (!flag) usedsavestate = 0;
1428   freecam = 0;
1429 
1430   // load rooms with enemies in them from level metadata
1431   // load timer from level metadata
1432   if (!flag) {
1433     char lcode;
1434     if (lvlCode < 80) {
1435       sprintf(filestr, RESOURCE_PATH "rooms/metas");
1436     } else {
1437       sprintf(filestr, "%srooms%slvl%d.meta", support_path, DIRSEP, lvlCode);
1438     }
1439     file = fopen(filestr, "rb");
1440     if (file) {
1441       do {
1442 	if (lvlCode < 80) fread(&lcode, sizeof(char), 1, file);
1443 	fread(enemdead, sizeof(int), ROOMMAX, file);
1444 	fread(&bspminutes, sizeof(int), 1, file);
1445 	fread(&bspseconds, sizeof(int), 1, file);
1446       } while (lvlCode < 80 && lcode != lvlCode && !feof(file));
1447       fclose(file);
1448     } else {
1449       for (i=0; i<ROOMMAX; i++) enemdead[i] = 0;
1450       bspminutes = 0;
1451       bspseconds = 0;
1452     }
1453     bspframes = 0;
1454     benemalldead = 0;
1455   } else {
1456     bspminutes = spminutes;
1457     bspseconds = spseconds;
1458     bspframes = spframes;
1459     if (enemdeadhere) enemdead[tempAreaCode] = 1;
1460     benemalldead = enemalldead;
1461   }
1462 
1463   if (!flag) {
1464     haswmed = gotmedals[lvlCode*6];
1465     hasomed = gotmedals[lvlCode*6+1];
1466     hasbmed = gotmedals[lvlCode*6+2];
1467     hasrmed = gotmedals[lvlCode*6+3];
1468     hasgmed = gotmedals[lvlCode*6+4];
1469     haspmed = gotmedals[lvlCode*6+5];
1470   }
1471 
1472   if (ingame == 3 && savereturn) save_room_return(flag);
1473 
1474   load_backups();
1475 }
1476 
1477 
game_init()1478 void game_init() {
1479   gravdir = DOWN;
1480   switchflags = 0;
1481   lvlCode = 0;
1482   areaCode = 0;
1483   gameNum = 1;
1484   mfragcount = 0;
1485   enemalldead = 0;
1486   freecam = 0;
1487 
1488   initialize_thingnodes();
1489 
1490   initmapmsg = 0;
1491   initgamemsg = 0;
1492 
1493   mousecammode = 1;
1494 
1495   ingame = 0;
1496   walkaway = 0;
1497 
1498   switch_music(0, 1);
1499 
1500   crtplacetile = PURPLETILE;
1501   crtplacetype = WOODBLOCK;
1502   crtplacesubtype = 0;
1503   crtplacedir = 0;
1504   crtentrance = 0;
1505   crtexit = 0;
1506   crtexitroom = 0;
1507   crtmessage = 0;
1508 
1509   read_level();
1510   init_play(1,1);
1511 }
1512 
1513 
clear_selection()1514 void clear_selection() {
1515   for (i=0; i<THINGMAX; i++) {
1516     selection[i] = 0;
1517   }
1518 }
1519 
1520 
init_creator()1521 void init_creator() {
1522   crtgridsize = 1;
1523   crtgridsnap = 1;
1524   crtlinking = 0;
1525   crtinventory = 0;
1526   switchflags |= NOCOLLIDEFLAG;
1527   crtselect = 0;
1528   clear_selection();
1529   load_backups();
1530 }
1531 
1532 
1533 // removes the room return data
remove_room_return()1534 void remove_room_return() {
1535   char filestr[512];
1536   sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
1537   remove(filestr);
1538 }
1539 
1540 
1541 // reloads room return data
load_room_return()1542 void load_room_return() {
1543   char filestr[512];
1544   int flag, hsvsttemp;
1545   sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
1546   file = fopen(filestr, "rb");
1547 #define daskjdf
1548 
1549   if (file) {
1550     fread(&lvlCode, sizeof(int), 1, file);
1551     fread(&areaCode, sizeof(int), 1, file);
1552     fread(&gotmwht, sizeof(int), 1, file);
1553     fread(gotmcrn, sizeof(int), 4, file);
1554     fread(&mfragcount, sizeof(int), 1, file);
1555     fread(gotmfrag, sizeof(int), MFRAGTOTAL, file);
1556     fread(&gotgmed, sizeof(int), 1, file);
1557     fread(&gotpmed, sizeof(int), 1, file);
1558     fread(&trgentrance, sizeof(int), 1, file);
1559     fread(&spminutes, sizeof(int), 1, file);
1560     fread(&spseconds, sizeof(int), 1, file);
1561     fread(&spframes, sizeof(int), 1, file);
1562     fread(&enemalldead, sizeof(int), 1, file);
1563     fread(enemdead, sizeof(int), ROOMMAX, file);
1564     fread(&haswmed, sizeof(int), 1, file);
1565     fread(&hasomed, sizeof(int), 1, file);
1566     fread(&hasbmed, sizeof(int), 1, file);
1567     fread(&hasrmed, sizeof(int), 1, file);
1568     fread(&hasgmed, sizeof(int), 1, file);
1569     fread(&haspmed, sizeof(int), 1, file);
1570     fread(&usedsavestate, sizeof(int), 1, file);
1571     fread(&hsvsttemp, sizeof(int), 1, file);
1572     fread(&flag, sizeof(int), 1, file);
1573     if (!fread(&((get_bossvars())[BOSSMAX-1]), sizeof(int), 1, file))
1574       (get_bossvars())[BOSSMAX-1] = 0;
1575     mapCode = lvlCode / 20;
1576 
1577     fclose(file);
1578 
1579     read_level();
1580 
1581     if (!loaderror) {
1582       ingame = 3;
1583       init_play(flag, 0);
1584     } else {
1585       ingame = 2;
1586       start_map();
1587     }
1588 
1589     hassavestate = hsvsttemp;
1590     hasbkpsavestate = 0;
1591     loadedbkpsavestate = 0;
1592   }
1593 }
1594 
1595 
switch_background(int newbkg)1596 void switch_background(int newbkg) {
1597   char filestr[512];
1598 
1599   if (newbkg != curbkg) {
1600     curbkg = newbkg;
1601     if (curbkg > 0) {
1602       SDL_FreeSurface(background);
1603       sprintf(filestr, RESOURCE_PATH "images%sbkg%d.png", DIRSEP, curbkg);
1604       background = load_img(filestr);
1605     }
1606   }
1607 }
1608 
1609 
1610 // Plays the sound with the given index once on the first free channel.
play_sound(int index)1611 void play_sound(int index) {
1612   if (musicon == 1 || musicon == 3) Mix_PlayChannel(-1, sound[index], 0);
1613 }
1614 
1615 
1616 // Determines if the object is on the visible screen.
on_screen(int xpos,int ypos,int xsize,int ysize)1617 int on_screen(int xpos, int ypos, int xsize, int ysize) {
1618   return xpos + xsize > camx - SOUNDDIST &&
1619     xpos < camx + SCR_WIDTH + SOUNDDIST &&
1620     ypos + ysize > camy - SOUNDDIST &&
1621     ypos < camy + SCR_HEIGHT + SOUNDDIST;
1622 }
1623 
1624 
set_up_input(char * inptstr,int inputtype,int ilength,int deflt)1625 void set_up_input(char* inptstr, int inputtype,
1626                   int ilength, int deflt) {
1627   sprintf(getinputstr, "%s", inptstr);
1628   getinput = inputtype;
1629   inputlength = ilength;
1630   inputpos = 0;
1631   if (deflt > -1) {
1632     sprintf(inputstr, "%d", deflt);
1633     inputpos = strlen(inputstr);
1634   } else strcpy(inputstr, "-");
1635   yesno = 0;
1636 }
1637 
1638 
1639 /* // Read level data stored in the executable. */
1640 /* void read_intern_level() { */
1641 /*   int k; */
1642 /*   clear_room(); */
1643 /*   char *stp, *enp; */
1644 /*   int musictemp, bkgtemp; */
1645 /*   if (get_intern_ptrs(&stp, &enp)) { */
1646 /*     hassaved = 1; */
1647 /*     bkgtemp = *(int*)stp; */
1648 /*     stp += sizeof(int); */
1649 /*     musictemp = *(int*)stp; */
1650 /*     stp += sizeof(int); */
1651 /*     lvlWidth = *(int*)stp; */
1652 /*     stp += sizeof(int); */
1653 /*     lvlHeight = *(int*)stp; */
1654 /*     stp += sizeof(int); */
1655 /*     startx = *(int*)stp; */
1656 /*     stp += sizeof(int); */
1657 /*     starty = *(int*)stp; */
1658 /*     stp += sizeof(int); */
1659 /*     for (i = 0; i < THINGMAX; i++) { */
1660 /*       memcpy(&things[i], stp, sizeof(Thing)); */
1661 /*       stp += sizeof(Thing); */
1662 /*     } */
1663 /*     for (i = 0; i < lvlWidth/SPR_SIZE; i++) */
1664 /*       for (j = 0; j < lvlHeight/SPR_SIZE; j++) */
1665 /* 	for (k = 0; k < 3; k++) { */
1666 /* 	  tiles[i][j][k] = *(int*)stp; */
1667 /* 	  stp += sizeof(int); */
1668 /* 	} */
1669 /*     if (bkgtemp < 1 || bkgtemp > BKG_MAX - 1) bkgtemp = 1; */
1670 /*     if (musictemp < 0 || musictemp > MUSIC_MAX - 1) musictemp = 0; */
1671 /*     clear_selection(); */
1672 /*     switch_music(musictemp, 0); */
1673 /*     switch_background(bkgtemp); */
1674 /*   } else { */
1675 /*     loaderror = 1; */
1676 /*     set_up_input("The file was not found",1,-1,-1); */
1677 /*   } */
1678 /* } */
1679 
1680 
read_level()1681 void read_level() {
1682   clear_room();
1683   char filestr[512];
1684   int musictemp, bkgtemp, k;
1685   char lcode, acode;
1686   if (lvlCode < 80) {
1687     sprintf(filestr, RESOURCE_PATH "rooms/rooms");
1688   } else {
1689     sprintf(filestr, "%srooms%sl%dr%d.room", support_path, DIRSEP, lvlCode, areaCode);
1690   }
1691   file = fopen(filestr, "rb");
1692   if (file) {
1693     hassaved = 1;
1694     do {
1695       if (lvlCode < 80) {
1696 	fread(&lcode, sizeof(char), 1, file);
1697 	fread(&acode, sizeof(char), 1, file);
1698       }
1699       fread(&bkgtemp, sizeof(int), 1, file);
1700       fread(&musictemp, sizeof(int), 1, file);
1701       fread(&lvlWidth, sizeof(int), 1, file);
1702       fread(&lvlHeight, sizeof(int), 1, file);
1703       fread(&startx, sizeof(int), 1, file);
1704       fread(&starty, sizeof(int), 1, file);
1705       fread(things, sizeof(Thing), THINGMAX, file);
1706       for (i=0;i<lvlWidth/SPR_SIZE;i++)
1707 	for (j=0;j<lvlHeight/SPR_SIZE;j++)
1708 	  for (k=0;k<3;k++)
1709 	    fread(&tilebackup[i][j][k], sizeof(int), 1, file);
1710     } while ((lcode != lvlCode || acode != areaCode) &&
1711 	     !feof(file) && lvlCode < 80);
1712     for (i=0;i<lvlWidth/SPR_SIZE;i++)
1713       for (j=0;j<lvlHeight/SPR_SIZE;j++)
1714 	for (k=0;k<3;k++)
1715 	  tiles[i][j][k] = tilebackup[i][j][k];
1716     if (bkgtemp < 1 || bkgtemp > BKG_MAX - 1) bkgtemp = 1;
1717     if (musictemp < 0 || musictemp > MUSIC_MAX - 1) musictemp = 0;
1718     clear_selection();
1719     switch_music(musictemp, 0);
1720     switch_background(bkgtemp);
1721     fclose(file);
1722   } else {
1723     loaderror = 1;
1724     if (lvlCode < 80 || creatormode)
1725       set_up_input("The file was not found",1,-1,-1);
1726     else set_up_input("Make the level first!",1,-1,-1);
1727   }
1728 }
1729 
1730 
check_level_exists()1731 int check_level_exists() {
1732   char filestr[512];
1733   if (lvlCode < 80) {
1734     sprintf(filestr, RESOURCE_PATH "rooms/rooms");
1735   } else {
1736     sprintf(filestr, "%srooms%sl%dr%d.room", support_path, DIRSEP, lvlCode, areaCode);
1737   }
1738   file = fopen(filestr, "rb");
1739   if (file) {
1740     fclose(file);
1741     return 1;
1742   }
1743   return 0;
1744 }
1745 
1746 
write_level()1747 void write_level() {
1748   char filestr[512];
1749   int k;
1750   sprintf(filestr, "%srooms%sl%dr%d.room", support_path, DIRSEP, lvlCode, areaCode);
1751   file = fopen(filestr, "wb");
1752   if (file) {
1753     hassaved = 1;
1754     fwrite(&curbkg, sizeof(int), 1, file);
1755     fwrite(&curmusic, sizeof(int), 1, file);
1756     fwrite(&lvlWidth, sizeof(int), 1, file);
1757     fwrite(&lvlHeight, sizeof(int), 1, file);
1758     fwrite(&startx, sizeof(int), 1, file);
1759     fwrite(&starty, sizeof(int), 1, file);
1760     fwrite(things, sizeof(Thing), THINGMAX, file);
1761     for (i=0;i<lvlWidth/SPR_SIZE;i++)
1762       for (j=0;j<lvlHeight/SPR_SIZE;j++)
1763 	for (k=0;k<3;k++)
1764 	  fwrite(&tiles[i][j][k], sizeof(int), 1, file);
1765     fclose(file);
1766   } else {
1767     set_up_input("The file was not opened",1,-1,-1);
1768   }
1769 }
1770 
1771 
write_metalevel()1772 void write_metalevel() {
1773   char filestr[512];
1774   int oldroom = areaCode, oldbkg = curbkg, oldmusic = curmusic;
1775   int fragcount=0;
1776   int iter=0;
1777 
1778   for (iter=0; iter<ROOMMAX; iter++) {
1779     enemdead[iter] = 1;
1780     areaCode = iter;
1781     sprintf(filestr, "%srooms%sl%dr%d.room", support_path, DIRSEP, lvlCode, areaCode);
1782     file = fopen(filestr, "rb");
1783     if (file) {
1784       fread(&curbkg, sizeof(int), 1, file);
1785       fread(&curmusic, sizeof(int), 1, file);
1786       fread(&lvlWidth, sizeof(int), 1, file);
1787       fread(&lvlHeight, sizeof(int), 1, file);
1788       fread(&startx, sizeof(int), 1, file);
1789       fread(&starty, sizeof(int), 1, file);
1790       fread(things, sizeof(Thing), THINGMAX, file);
1791       for (i=0;i<lvlWidth/SPR_SIZE;i++)
1792         for (j=0;j<lvlHeight/SPR_SIZE;j++)
1793           fread(tiles[i][j], sizeof(int), 3, file);
1794       for (i=0; i<THINGMAX; i++) {
1795         if (things[i].type != NOTYPE && things[i].animate) {
1796           enemdead[iter] = 0;
1797         }
1798         if (things[i].type == MEDALFRAGMENT) {
1799           things[i].dir = fragcount++;
1800         }
1801       }
1802       fclose(file);
1803       write_level();
1804     }
1805   }
1806 
1807   sprintf(filestr, "%srooms%slvl%d.meta", support_path, DIRSEP, lvlCode);
1808   file = fopen(filestr, "wb");
1809   if (file) {
1810     fwrite(enemdead, sizeof(int), ROOMMAX, file);
1811     fwrite(&spminutes, sizeof(int), 1, file);
1812     fwrite(&spseconds, sizeof(int), 1, file);
1813     fclose(file);
1814   } else {
1815     set_up_input("The file was not opened",1,-1,-1);
1816   }
1817 
1818   if (fragcount != MFRAGTOTAL) {
1819     sprintf(filestr, "%d fragments found", fragcount);
1820     set_up_input(filestr,1,-1,-1);
1821   }
1822 
1823   areaCode = oldroom;
1824   curbkg = oldbkg;
1825   curmusic = oldmusic;
1826   read_level();
1827 }
1828 
1829 
set_start_pos()1830 void set_start_pos() {
1831   int temp1 = mx, temp2 = my;
1832   if (crtgridsnap && crtgridsize > 0) {
1833     if (crtgridsize == 1) {
1834       temp1 = temp1-temp1%SPR_SIZE+SPR_SIZE/2;
1835       temp2 = temp2-temp2%SPR_SIZE+SPR_SIZE-beret.height/2;
1836     } else {
1837       temp1 = temp1-temp1%(SPR_SIZE/2)+SPR_SIZE/4;
1838       temp2 = temp2-temp2%(SPR_SIZE/2)+SPR_SIZE/2-beret.height/2;
1839     }
1840   }
1841   startx = temp1-beret.width/2;
1842   starty = temp2-beret.height/2;
1843 }
1844 
1845 
paste_helper(int leftmost,int topmost,int copyindices[250],int index,int flag)1846 void paste_helper(int leftmost, int topmost,
1847                   int copyindices[250], int index, int flag) {
1848   int copyindex;
1849   if (key3 != SHIFT) selection[index] = 0;
1850   if ((copyindex = copy_thing(&things[index], things, -1)) == -1) return;
1851   copyindices[index] = copyindex;
1852   things[copyindex].x = things[index].x-leftmost+mx;
1853   things[copyindex].y = things[index].y-topmost+my;
1854   if (things[copyindex].x > lvlWidth-things[copyindex].width)
1855     things[copyindex].x = lvlWidth-things[copyindex].width;
1856   if (things[copyindex].y > lvlHeight-things[copyindex].height)
1857     things[copyindex].y = lvlHeight-things[copyindex].height;
1858   things[copyindex].vx = 0.01;
1859   things[copyindex].vy = 0.01;
1860   if (flag && things[index].link > -1) {
1861     if (copyindices[things[index].link] > -1) {
1862       things[copyindex].link = copyindices[things[index].link];
1863       if (things[index].subtype % 2 == 0)
1864         things[copyindices[things[index].link]].islinked = copyindex;
1865       else things[copyindex].islinked = copyindices[things[index].link];
1866     }
1867   }
1868   selection[copyindex] = (copyindex<i?1:-1);
1869 }
1870 
1871 
1872 // copies every selected object and pastes them to the mouse position
paste_objects()1873 void paste_objects() {
1874   int leftmost = lvlWidth, topmost = lvlHeight;
1875   int copyindices[250];
1876   for (i=0; i<THINGMAX; i++) {
1877     if (things[i].type != NOTYPE && selection[i]) {
1878       if (things[i].x < leftmost) leftmost = things[i].x;
1879       if (things[i].y < topmost) topmost = things[i].y;
1880     }
1881     copyindices[i] = -1;
1882   }
1883   for (j=0; j < 2; j++) {
1884     for (i=0; i<THINGMAX; i++) {
1885       if (things[i].type != NOTYPE && (things[i].type == LINKBLOCK) == j) {
1886         if (selection[i] == 1) {
1887           paste_helper(leftmost, topmost, copyindices, i, j);
1888         } else if (selection[i] == -1) selection[i] = 1;
1889       }
1890     }
1891   }
1892 }
1893 
1894 
1895 // creates an instance of the current object
create_object()1896 void create_object() {
1897   if (key3 != SHIFT) clear_selection();
1898   int empty;
1899   if ((empty = find_empty(things)) == -1) return;
1900   make_thing(empty, crtplacetype, crtplacesubtype, mx, my,
1901              get_param(crtplacetype, crtplacedir, -1), things);
1902   if (things[empty].x > lvlWidth-things[empty].width)
1903     things[empty].x = lvlWidth-things[empty].width;
1904   if (things[empty].y > lvlHeight-things[empty].height)
1905     things[empty].y = lvlHeight-things[empty].height;
1906   things[empty].vx = 0.01;
1907   things[empty].vy = 0.01;
1908   things[empty].islinked = -1;
1909   selection[empty] = 1;
1910   if (crtplacetype == DOOR || crtplacetype == SIGN) {
1911     things[empty].timer = crtentrance;
1912     things[empty].status = crtexit;
1913     things[empty].dir = crtexitroom;
1914   }
1915   if (crtplacetype == READSIGN) {
1916     things[empty].dir = crtmessage;
1917   }
1918   hassaved = 0;
1919 }
1920 
1921 
1922 // looks at adjacent tiles and makes the tile borders pretty
fix_tile_borders(int xpos,int ypos,int recurse)1923 void fix_tile_borders(int xpos, int ypos, int recurse) {
1924 
1925   /* ABC
1926      H D
1927      GFE */
1928 
1929   int dirflags = 0;
1930   int b = BLANK;
1931   int type = tiles[xpos][ypos][0];
1932   int rside = lvlWidth/SPR_SIZE-1, bside = lvlHeight/SPR_SIZE-1;
1933 
1934   if (xpos == 0) dirflags |= (A_ | H_ | G_);
1935   if (ypos == 0) dirflags |= (A_ | B_ | C_);
1936   if (xpos == rside) dirflags |= (C_|D_|E_);
1937   if (ypos == bside) dirflags |= (E_|F_|G_);
1938   if (xpos > 0) {
1939     if (tiles[xpos-1][ypos][0] == type) dirflags |= H_;
1940     if (recurse) fix_tile_borders(xpos-1,ypos,0);
1941   }
1942   if (ypos > 0) {
1943     if (tiles[xpos][ypos-1][0] == type) dirflags |= B_;
1944     if (recurse) fix_tile_borders(xpos,ypos-1,0);
1945   }
1946   if (xpos < rside) {
1947     if (tiles[xpos+1][ypos][0] == type) dirflags |= D_;
1948     if (recurse) fix_tile_borders(xpos+1,ypos,0);
1949   }
1950   if (ypos < bside) {
1951     if (tiles[xpos][ypos+1][0] == type) dirflags |= F_;
1952     if (recurse) fix_tile_borders(xpos, ypos+1, 0);
1953   }
1954   if (xpos > 0 && ypos > 0) {
1955     if (tiles[xpos-1][ypos-1][0] == type) dirflags |= A_;
1956     if (recurse) fix_tile_borders(xpos-1,ypos-1,0);
1957   }
1958   if (xpos < rside) {
1959     if (ypos > 0 && tiles[xpos+1][ypos-1][0] == type) dirflags |= C_;
1960     if (recurse) fix_tile_borders(xpos+1,ypos-1,0);
1961   }
1962   if (xpos < rside && ypos < bside) {
1963     if (tiles[xpos+1][ypos+1][0] == type) dirflags |= E_;
1964     if (recurse) fix_tile_borders(xpos+1,ypos+1,0);
1965   }
1966   if (xpos > 0 && ypos < bside) {
1967     if (tiles[xpos-1][ypos+1][0] == type) dirflags |= G_;
1968     if (recurse) fix_tile_borders(xpos-1,ypos+1,0);
1969   }
1970 
1971   if (type == OBJONLY || type == BERETONLY || type == CHOICEONLY ||
1972       (type >= SPIKEU && type <= SPIKEL) || (type >= MOVERU && type <= MOVERL) || type == DARKNESSTILE) {
1973     tiles[xpos][ypos][2] = BLANK;
1974     return;
1975   }
1976 
1977   if (!(dirflags & (B_|D_|F_|H_))) b = ALL;
1978   else if (dirflags == 0xff) b = BLANK;
1979   else if (dirflags == (B_|D_|F_|H_)) b = ALLDOTS;
1980   else if (dirflags == (B_|C_|D_|E_|F_|G_|H_)) b = ULDOT;
1981   else if (dirflags == (A_|B_|D_|E_|F_|G_|H_)) b = URDOT;
1982   else if (dirflags == (A_|B_|C_|D_|E_|F_|H_)) b = DLDOT;
1983   else if (dirflags == (A_|B_|C_|D_|F_|G_|H_)) b = DRDOT;
1984   else if (dirflags == (B_|D_|E_|F_|G_|H_)) b = UDOTS;
1985   else if (dirflags == (A_|B_|D_|F_|G_|H_)) b = RDOTS;
1986   else if (dirflags == (A_|B_|C_|D_|F_|H_)) b = DDOTS;
1987   else if (dirflags == (B_|C_|D_|E_|F_|H_)) b = LDOTS;
1988   else if (dirflags == (A_|B_|D_|F_|H_)) b = NULDOT;
1989   else if (dirflags == (C_|B_|D_|F_|H_)) b = NURDOT;
1990   else if (dirflags == (E_|B_|D_|F_|H_)) b = NDRDOT;
1991   else if (dirflags == (G_|B_|D_|F_|H_)) b = NDLDOT;
1992   else if (dirflags == (B_|C_|D_|F_|G_|H_)) b = ULDRDOTS;
1993   else if (dirflags == (A_|B_|D_|E_|F_|H_)) b = URDLDOTS;
1994   else if ((dirflags | (A_|B_|C_)) == 0xff) b = U;
1995   else if ((dirflags | (C_|D_|E_)) == 0xff) b = R;
1996   else if ((dirflags | (E_|F_|G_)) == 0xff) b = D;
1997   else if ((dirflags | (G_|H_|A_)) == 0xff) b = L;
1998   else if ((dirflags | (A_|B_|C_|E_|G_)) == 0xff) {
1999     if (dirflags & E_) b = UWLDOT;
2000     else if (dirflags & G_) b = UWRDOT;
2001     else b = UW2DOTS;
2002   } else if ((dirflags | (C_|D_|E_|G_|A_)) == 0xff) {
2003     if (dirflags & A_) b = RWDDOT;
2004     else if (dirflags & G_) b = RWUDOT;
2005     else b = RW2DOTS;
2006   } else if ((dirflags | (E_|F_|G_|A_|C_)) == 0xff) {
2007     if (dirflags & A_) b = DWRDOT;
2008     else if (dirflags & C_) b = DWLDOT;
2009     else b = DW2DOTS;
2010   } else if ((dirflags | (G_|H_|A_|C_|E_)) == 0xff) {
2011     if (dirflags & C_) b = LWDDOT;
2012     else if (dirflags & E_) b = LWUDOT;
2013     else b = LW2DOTS;
2014   }
2015   else if ((dirflags | (A_|H_|G_|C_|D_|E_)) == 0xff) b = LR;
2016   else if ((dirflags | (A_|B_|C_|G_|E_|F_)) == 0xff) b = UD;
2017   else if ((dirflags | (A_|B_|C_|D_|E_)) == 0xff) b = UR;
2018   else if ((dirflags | (C_|D_|E_|F_|G_)) == 0xff) b = DR;
2019   else if ((dirflags | (A_|H_|G_|F_|E_)) == 0xff) b = DL;
2020   else if ((dirflags | (A_|B_|C_|G_|H_)) == 0xff) b = UL;
2021   else if ((dirflags | (A_|B_|C_|D_|E_|G_)) == 0xff) b = URWDOT;
2022   else if ((dirflags | (C_|D_|E_|F_|G_|A_)) == 0xff) b = DRWDOT;
2023   else if ((dirflags | (A_|H_|G_|F_|E_|C_)) == 0xff) b = DLWDOT;
2024   else if ((dirflags | (A_|B_|C_|G_|H_|E_)) == 0xff) b = ULWDOT;
2025   else if ((dirflags | (A_|C_|D_|E_|F_|G_|H_)) == 0xff) b = NU;
2026   else if ((dirflags | (A_|B_|C_|E_|F_|G_|H_)) == 0xff) b = NR;
2027   else if ((dirflags | (A_|B_|C_|D_|E_|G_|H_)) == 0xff) b = ND;
2028   else if ((dirflags | (A_|B_|C_|D_|E_|F_|G_)) == 0xff) b = NL;
2029 
2030   tiles[xpos][ypos][2] = b;
2031 
2032   /* ABC
2033      H D
2034      GFE */
2035 }
2036 
2037 
2038 // returns 1 if given type is transparent but not an "only" wall
see_through(int type)2039 int see_through(int type) {
2040   return (type >= WHITEFUZZSEE && type <= GREENFUZZSEE) ||
2041     type == GLASS || (type >= PURPLETILESEE && type <= WHITETILESEE) ||
2042     type == CLEAR || type == REDFUZZSEE || type == BLACKTILESEE ||
2043     (type >= WHITESPRSEE && type <= PURPLESPRSEE) ||
2044     (type >= REDSEECRPT && type <= BLUESEECRPT) || type == ANTITILE ||
2045     (type >= SPIKEU && type <= SPIKEL);
2046 }
2047 
2048 
2049 // creates a grid of the current tile type
create_tiles(int type)2050 void create_tiles(int type) {
2051   int sell, selr, selt, selb;
2052   if (crtselect) {
2053     if (selx < mx) {
2054       sell = selx; selr = mx;
2055     } else {
2056       sell = mx; selr = selx;
2057     }
2058     if (sely < my) {
2059       selt = sely; selb = my;
2060     } else {
2061       selt = my; selb = sely;
2062     }
2063     sell /= SPR_SIZE;
2064     selr /= SPR_SIZE;
2065     selt /= SPR_SIZE;
2066     selb /= SPR_SIZE;
2067   } else {
2068     sell = mx/SPR_SIZE;
2069     selr = mx/SPR_SIZE;
2070     selt = my/SPR_SIZE;
2071     selb = my/SPR_SIZE;
2072   }
2073   for (i=sell;i<=selr;i++) {
2074     for (j=selt;j<=selb;j++) {
2075       tiles[i][j][0] = type;
2076       switch (type) {
2077       case OBJONLY : tiles[i][j][1] = OBJONLYF; break;
2078       case BERETONLY : tiles[i][j][1] = BERETONLYF; break;
2079       case MOVERU : case MOVERR : case MOVERD : case MOVERL :
2080       case CHOICEONLY : case DARKNESSTILE: tiles[i][j][1] = NOTSOLIDF; break;
2081       default : tiles[i][j][1] = SOLID;
2082       }
2083       if (see_through(type)) tiles[i][j][1] = SOLIDF;
2084       fix_tile_borders(i,j,1);
2085     }
2086   }
2087   hassaved = 0;
2088 }
2089 
2090 
load_state(int backup)2091 void load_state(int backup) {
2092   if (hassavestate) {
2093     int trash; // dump unneeded values
2094     int oldcamx = camx, oldcamy = camy;
2095     char filestr[512];
2096     char bkpstr[5];
2097     if (backup && hasbkpsavestate) sprintf(bkpstr, "bkp");
2098     else bkpstr[0] = 0;
2099     sprintf(filestr, "%ssaves%sgame%d.sav%s", support_path, DIRSEP, gameNum, bkpstr);
2100     file = fopen(filestr, "rb");
2101     if (file) {
2102       if (backup && hasbkpsavestate) loadedbkpsavestate = 1;
2103       else loadedbkpsavestate = 0;
2104       fread(&beret, sizeof(Thing), 1, file);
2105       beret.subtype = istophat;
2106       fread(&camx, sizeof(int),1,file);
2107       fread(&camy, sizeof(int),1,file);
2108       fread(things, sizeof(Thing), THINGMAX, file);
2109       for (i=0;i<lvlWidth/SPR_SIZE;i++)
2110         for (j=0;j<lvlHeight/SPR_SIZE;j++)
2111           fread(tiles[i][j], sizeof(int), 3, file);
2112       fread(particles, sizeof(Particle), PARTICLEMAX, file);
2113       fread(&mfragcount, sizeof(int), 1, file);
2114       fread(gotmfrag, sizeof(int), MFRAGTOTAL, file);
2115       fread(gotmcrn, sizeof(int), 4, file);
2116       fread(&gotmwht, sizeof(int), 1, file);
2117       fread(&gotgmed, sizeof(int), 1, file);
2118       fread(&gotpmed, sizeof(int), 1, file);
2119       fread(&enemdeadhere, sizeof(int), 1, file);
2120       fread(&enemalldead, sizeof(int), 1, file);
2121       fread(&spframes, sizeof(int), 1, file);
2122       fread(&spseconds, sizeof(int), 1, file);
2123       fread(&spminutes, sizeof(int), 1, file);
2124       fread(&telething, sizeof(int), 1, file);
2125       fread(&switchflags, sizeof(int), 1, file);
2126       if (!mousecammode) {
2127         fread(&mx, sizeof(int), 1, file);
2128         fread(&my, sizeof(int), 1, file);
2129       } else {
2130         fread(&trash, sizeof(int), 1, file);
2131         fread(&trash, sizeof(int), 1, file);
2132       }
2133       if (!fread(&gotallfrags, sizeof(int), 1, file))
2134         gotallfrags = 0;
2135       fread(get_bossvars(), sizeof(int), BOSSMAX, file);
2136       fread(&gravdir, sizeof(int), 1, file);
2137       int jumpgrace, runmargin;
2138       if (!fread(&jumpgrace, sizeof(int), 1, file)) jumpgrace = 0;
2139       if (!fread(&runmargin, sizeof(int), 1, file)) runmargin = 0;
2140       set_jumpgrace(jumpgrace);
2141       set_runmargin(runmargin);
2142 
2143       if (!mousecammode) SDL_WarpMouse(mx-camx, my-camy);
2144       else {
2145 	mx -= oldcamx - camx;
2146 	my -= oldcamy - camy;
2147       }
2148 
2149       fix_camera();
2150       deathtime = 0;
2151       fadetime = 0;
2152       walkaway = 0;
2153 
2154       if (telething > -1 &&
2155           !(alwaystele && teleon) && !(!alwaystele && cantele))
2156         stop_telekinesis();
2157 
2158       usedsavestate = 1;
2159       svstcount = -30;
2160 
2161       fclose(file);
2162     } else {
2163       set_up_input("The file was not found",1,-1,-1);
2164     }
2165   }
2166 }
2167 
2168 
save_map(int num)2169 void save_map(int num) {
2170   char filestr[512];
2171   sprintf(filestr, "%srooms%sbr%d.mp", support_path, DIRSEP, num);
2172   file = fopen(filestr, "wb");
2173   if (file) {
2174     for (i=0;i<SCR_WIDTH/SPR_SIZE;i++)
2175       for (j=0;j<(SCR_HEIGHT-150)/SPR_SIZE;j++)
2176         fwrite(tiles[i][j], sizeof(int), 3, file);
2177     fclose(file);
2178   } else {
2179     set_up_input("The file was not opened",1,-1,-1);
2180   }
2181 }
2182 
2183 
load_map(int num)2184 void load_map(int num) {
2185   char filestr[512];
2186   char mcode;
2187   //  sprintf(filestr, "rooms%sbr%d.mp", DIRSEP, num);
2188   sprintf(filestr, RESOURCE_PATH "rooms/maps");
2189   file = fopen(filestr, "rb");
2190   if (file) {
2191     do {
2192       fread(&mcode, sizeof(char), 1, file);
2193       for (i=0;i<SCR_WIDTH/SPR_SIZE;i++)
2194 	for (j=0;j<(SCR_HEIGHT-150)/SPR_SIZE;j++)
2195 	  fread(tiles[i][j], sizeof(int), 3, file);
2196     } while (mcode != num && !feof(file));
2197     fclose(file);
2198   } else {
2199     set_up_input("The file was not found",1,-1,-1);
2200   }
2201 
2202   sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
2203   file = fopen(filestr, "rb");
2204   if (file) {
2205     fread(&contlvl, sizeof(int), 1, file);
2206     fclose(file);
2207     if (contlvl / 20 == mapCode) cancont = 1;
2208     else cancont = 0;
2209   } else {
2210     cancont = 0;
2211   }
2212 }
2213 
2214 
start_map()2215 void start_map() {
2216   int minabovegm = UNF;
2217   nextopenlevel = -1;
2218   for (i=0; i<20; i++)
2219     if (levelentry[mapCode][i] < minabovegm &&
2220         levelentry[mapCode][i] > gamemedals) {
2221       minabovegm = levelentry[mapCode][i];
2222       nextopenlevel = i;
2223     }
2224   switch_music(1, 0);
2225   mapselect = -1;
2226   int curcamx = camx, curcamy = camy;
2227   camx = 0;
2228   camy = 0;
2229   mx += camx-curcamx;
2230   my += camy-curcamy;
2231   load_map(mapCode);
2232 }
2233 
2234 
check_map_select()2235 void check_map_select() {
2236   if (mx >= 0 && my >= 0) {
2237     mapselect = tiles[mx/SPR_SIZE][my/SPR_SIZE][0] - MAPTILE;
2238     if (mapselect + MAPTILE == CLEAR) {
2239       if (cancont) mapselect = contlvl - mapCode*20;
2240       else mapselect = -1;
2241     } else if (mapselect < 0 ||
2242 	       (levelentry[mapCode][mapselect] > gamemedals && mapselect != nextopenlevel && mapCode < 4))
2243       mapselect = -1;
2244   }
2245 }
2246 
2247 
draw_map_status()2248 void draw_map_status() {
2249   display_message(74,15,smfont,"S TO VIEW THE STORY",1);
2250   display_message(74,30,smfont,"C TO VIEW THE CONTROLS",1);
2251   display_message(74,45,smfont,"A TO VIEW THE CREDITS",1);
2252   display_message(SCR_WIDTH/4,500,font,"Evil Corporation",1);
2253   display_message(SCR_WIDTH/4,530,medfont,wingnames[mapCode],1);
2254   sprintf(messagestr, "%d", gamemedals);
2255   display_message(SCR_WIDTH/4+30, 565, font, 0,1);
2256   apply_sprite(SCR_WIDTH/4-message->w/2-45,565-message->h/2,5,15,
2257                1,1,spritesheet,screen);
2258   if (mapselect > -1) {
2259     apply_surface((SCR_WIDTH-lvlnumbkg->w)/2, 400, lvlnumbkg, screen);
2260     sprintf(messagestr, "%d", levelnums[mapCode][mapselect]);
2261     display_message(SCR_WIDTH/2+numofst[levelnums[mapCode][mapselect]-1],422,font,0,0);
2262     sprintf(messagestr, "Department of %s", deptnames[mapCode][mapselect]);
2263     int deptnameofst = (mapCode==3||(mapCode==4 && mapselect==10))?30:(mapCode==4?15:0);
2264     display_message(3*SCR_WIDTH/4,500+deptnameofst,font,0,1);
2265     if (strcmp(divnames[mapCode][mapselect], "-")) {
2266       sprintf(messagestr, "%s Division", divnames[mapCode][mapselect]);
2267       display_message(3*SCR_WIDTH/4,530+deptnameofst,medfont,0,1);
2268     }
2269     if (levelentry[mapCode][mapselect] > gamemedals) {
2270       sprintf(messagestr, "%d Medallions required for entry", levelentry[mapCode][mapselect]);
2271       display_message(3*SCR_WIDTH/4,565,medfont,0,1);
2272     } else {
2273       if (mapCode < 3) {
2274 	for (i=0; i<6; i++) {
2275 	  if (gotmedals[(mapCode*20+mapselect)*6+i])
2276 	    apply_sprite(3*SCR_WIDTH/4-165+i*60, 547, medalsprx[i],
2277 			 medalspry[i], 1, 1, spritesheet, screen);
2278 	  else
2279 	    apply_sprite(3*SCR_WIDTH/4-165+i*60, 547, 17, 17,
2280 			 1, 1, spritesheet, screen);
2281 	}
2282       }
2283       display_message(3*SCR_WIDTH/4,590,smfont,"CLICK TO ENTER",1);
2284     }
2285   }
2286   if (cancont && contlvl > -1) {
2287     display_message(3*SCR_WIDTH/4, 45,smfont,"CONTINUE LAST PLAY",0);
2288   }
2289   if ((mapCode > 0 || (mapCode == 0 && (opencreator || beatlevel[LAST_LEVEL]))) &&
2290       !(mapCode == 4 && !beatlevel[LAST_LEVEL])) {
2291     if (mx >= 30 && mx <= 120 && my >= 195 && my <= 285)
2292       apply_sprite(30, 195, 3, 10, 3, 3, tilesheet, screen);
2293     else apply_sprite(30, 195, 3, 7, 3, 3, tilesheet, screen);
2294     display_message(75, 240, smfont, "PREVIOUS MAP",0);
2295   }
2296   if ((mapCode < 4 && beatlevel[maplastlevel[mapCode]]) || mapCode == 4) {
2297     if (mx >= SCR_WIDTH-120 && mx <= SCR_WIDTH-30 && my >= 195 && my <= 285)
2298       apply_sprite(SCR_WIDTH-120, 195, 0, 10, 3, 3, tilesheet, screen);
2299     else apply_sprite(SCR_WIDTH-120, 195, 0, 7, 3, 3, tilesheet, screen);
2300     display_message(SCR_WIDTH-75, 240, smfont, "NEXT MAP",0);
2301   }
2302 }
2303 
2304 
draw_creat(int page)2305 void draw_creat(int page) {
2306   apply_surface(0,0,background,screen);
2307   display_message(SCR_WIDTH/2, 30,font,"Creator Guide",1);
2308   for (i=0;i<(page==0?CREAT1LEN:(page==1?CREAT2LEN:CREAT3LEN));i++)
2309     display_message(SCR_WIDTH/2, 60+25*i, medfont,
2310 		    (page==0?creat1[i]:(page==1?creat2[i]:creat3[i])),1);
2311 }
2312 
draw_story()2313 void draw_story() {
2314   apply_surface(0,0,background,screen);
2315   display_message(SCR_WIDTH/2, 30,font,"Story",1);
2316   for (i=0;i<STORYLEN;i++)
2317     display_message(SCR_WIDTH/2, 60+25*i, medfont, story[i],1);
2318 }
2319 
draw_controls()2320 void draw_controls() {
2321   apply_surface(0,0,background,screen);
2322   display_message(SCR_WIDTH/2, 45,font,"Game Controls",1);
2323   for (i=0;i<CONTROLLEN;i++)
2324     display_message(SCR_WIDTH/2, 100+25*i, medfont, controls[i],1);
2325 }
2326 
draw_map()2327 void draw_map() {
2328   int maptype, addto;
2329   apply_surface(0,0,mapbkg,screen);
2330   for (i = 0; i < SCR_WIDTH/SPR_SIZE; i++) {
2331     for (j = 0; j < 15; j++) {
2332       if (tiles[i][j][0] == CLEAR) {
2333         if (cancont) {
2334           apply_sprite(i*SPR_SIZE, j*SPR_SIZE,
2335                        (CLEAR-1)%24,(CLEAR-1)/24,
2336                        1,1,tilesheet,screen);
2337           apply_sprite(i*SPR_SIZE, j*SPR_SIZE,
2338                        tiles[i][j][2]%24,22+tiles[i][j][2]/24,
2339                        1,1,tilesheet,screen);
2340         }
2341       } else if (tiles[i][j][0] == GLASS) {
2342         apply_sprite(i*SPR_SIZE, j*SPR_SIZE,
2343                      (GLASS-1)%24,(GLASS-1)/24,
2344                      1,1,tilesheet,screen);
2345         apply_sprite(i*SPR_SIZE, j*SPR_SIZE,
2346                      tiles[i][j][2]%24,22+tiles[i][j][2]/24,
2347                      1,1,tilesheet,screen);
2348       } else if (tiles[i][j][0] > 0) {
2349         maptype = tiles[i][j][0] - MAPTILE;
2350         if (levelentry[mapCode][maptype] <= gamemedals ||
2351 	    maptype == nextopenlevel || mapCode == 4) {
2352           addto = 0;
2353           if (mapselect == maptype) addto++;
2354           if (levelentry[mapCode][maptype] > gamemedals && !(mapCode == 4 && opencreator)) addto+=2;
2355           apply_sprite(i*SPR_SIZE, j*SPR_SIZE,
2356                        (tiles[i][j][0]-1)%24,(tiles[i][j][0]-1)/24+addto,
2357                        1,1,tilesheet,screen);
2358           if (addto < 2)
2359             apply_sprite(i*SPR_SIZE, j*SPR_SIZE,
2360                          tiles[i][j][2]%24,22+tiles[i][j][2]/24,
2361                          1,1,tilesheet,screen);
2362         }
2363       }
2364     }
2365   }
2366 }
2367 
2368 // Copies the main savestate file to a backup file.
copy_save_state(char * sf1,char * sf2)2369 int copy_save_state(char *sf1, char *sf2) {
2370   char buf[4096];
2371   int cread;
2372   int successcopy = 0;
2373   FILE *sf1h, *sf2h;
2374   sf1h = fopen(sf1, "rb");
2375   sf2h = fopen(sf2, "wb");
2376   if (sf1h && sf2h) {
2377     while (!feof(sf1h)) {
2378       cread = fread(buf, sizeof(char), 4096, sf1h);
2379       fwrite(buf, sizeof(char), cread, sf2h);
2380     }
2381     successcopy = 1;
2382   }
2383   if (sf1h) fclose(sf1h);
2384   if (sf2h) fclose(sf2h);
2385   return successcopy;
2386 }
2387 
2388 
save_state()2389 void save_state() {
2390   if (!beret.dead && !fadetime) {
2391     char filestr[512];
2392     sprintf(filestr, "%ssaves%sgame%d.sav", support_path, DIRSEP, gameNum);
2393     if (hassavestate && !loadedbkpsavestate) {
2394       char filestr2[512];
2395       sprintf(filestr2, "%sbkp", filestr);
2396       hasbkpsavestate = copy_save_state(filestr, filestr2);
2397     }
2398     loadedbkpsavestate = 0;
2399     file = fopen(filestr, "wb");
2400     if (file) {
2401       fwrite(&beret, sizeof(Thing), 1, file);
2402       fwrite(&camx, sizeof(int),1,file);
2403       fwrite(&camy, sizeof(int),1,file);
2404       fwrite(things, sizeof(Thing), THINGMAX, file);
2405       for (i=0;i<lvlWidth/SPR_SIZE;i++)
2406         for (j=0;j<lvlHeight/SPR_SIZE;j++)
2407           fwrite(tiles[i][j], sizeof(int), 3, file);
2408       fwrite(particles, sizeof(Particle), PARTICLEMAX, file);
2409       fwrite(&mfragcount, sizeof(int), 1, file);
2410       fwrite(gotmfrag, sizeof(int), MFRAGTOTAL, file);
2411       fwrite(gotmcrn, sizeof(int), 4, file);
2412       fwrite(&gotmwht, sizeof(int), 1, file);
2413       fwrite(&gotgmed, sizeof(int), 1, file);
2414       fwrite(&gotpmed, sizeof(int), 1, file);
2415       fwrite(&enemdeadhere, sizeof(int), 1, file);
2416       fwrite(&enemalldead, sizeof(int), 1, file);
2417       fwrite(&spframes, sizeof(int), 1, file);
2418       fwrite(&spseconds, sizeof(int), 1, file);
2419       fwrite(&spminutes, sizeof(int), 1, file);
2420       fwrite(&telething, sizeof(int), 1, file);
2421       fwrite(&switchflags, sizeof(int), 1, file);
2422       fwrite(&mx, sizeof(int), 1, file);
2423       fwrite(&my, sizeof(int), 1, file);
2424       fwrite(&gotallfrags, sizeof(int), 1, file);
2425       fwrite(get_bossvars(), sizeof(int), BOSSMAX, file);
2426       fwrite(&gravdir, sizeof(int), 1, file);
2427       int jumpgrace = get_jumpgrace(), runmargin = get_runmargin();
2428       fwrite(&jumpgrace, sizeof(int), 1, file);
2429       fwrite(&runmargin, sizeof(int), 1, file);
2430 
2431       hassavestate = 1;
2432       svstcount = 30;
2433       fclose(file);
2434 
2435       save_room_return(trgentrance == -1 ? 0 : 1);
2436     } else {
2437       set_up_input("The file was not opened",1,-1,-1);
2438     }
2439   }
2440 }
2441 
2442 
load_options()2443 void load_options() {
2444   char filestr[512];
2445   sprintf(filestr, "%ssaves%sgame%d.opt", support_path, DIRSEP, gameNum);
2446   file = fopen(filestr, "rb");
2447   if (file) {
2448     if (!fread(&mousecammode, sizeof(int), 1, file))
2449       mousecammode = 1;
2450     if (!fread(&runningmode, sizeof(int), 1, file))
2451       runningmode = 1;
2452     if (!fread(&deathstateload, sizeof(int), 1, file))
2453       deathstateload = 0;
2454     if (!fread(&opencreator, sizeof(int), 1, file))
2455       opencreator = 0;
2456     if (!fread(&musicon, sizeof(int), 1, file))
2457       musicon = 1;
2458     if (!fread(&guides, sizeof(int), 1, file))
2459       guides = 0;
2460     if (!fread(&istophat, sizeof(int), 1, file))
2461       istophat = 0;
2462     fclose(file);
2463   } else {
2464     mousecammode = 1;
2465     runningmode = 1;
2466     deathstateload = 0;
2467     opencreator = 0;
2468     musicon = 1;
2469   }
2470   if (ingame != 1 && (musicon == 0 || musicon == 3)) {
2471     Mix_HaltMusic();
2472   }
2473 }
2474 
2475 
load_game_data()2476 void load_game_data() {
2477   load_options();
2478   char filestr[512];
2479   sprintf(filestr, "%ssaves%sgame%d.gm", support_path, DIRSEP, gameNum);
2480   file = fopen(filestr, "rb");
2481   if (file) {
2482     fread(gamename, sizeof(char), 25, file);
2483     fread(gotmedals, sizeof(int), MEDALMAX, file);
2484     fread(&mapCode, sizeof(int), 1, file);
2485     fread(&initgamemsg, sizeof(int), 1, file);
2486     if (!fread(beatlevel, sizeof(int), 100, file)) {
2487       for (i=0; i<100; i++) beatlevel[i] = 0;
2488     }
2489     for (i = 0; i < 100; i++) {
2490       if (!fread(game_gotfrags[i], sizeof(char), 100, file)) {
2491 	for (j = 0; j < 100; j++) {
2492 	  if (gotmedals[i*6+2]) game_gotfrags[i][j] = 1;
2493 	  else game_gotfrags[i][j] = 0;
2494 	}
2495       }
2496       if (!fread(game_gotcorners[i], sizeof(char), 4, file)) {
2497 	for (j = 0; j < 4; j++) {
2498 	  if (gotmedals[i*6+1]) game_gotcorners[i][j] = 1;
2499 	  else game_gotcorners[i][j] = 0;
2500 	}
2501       }
2502     }
2503     for (i = 0; i < 100; i++) {
2504       if (!fread(game_enemdead[i], sizeof(char), ROOMMAX, file)) {
2505 	for (j = 0; j < ROOMMAX; j++) {
2506 	  if (gotmedals[i*6+3]) game_enemdead[i][j] = 1;
2507 	  else game_enemdead[i][j] = 0;
2508 	}
2509       }
2510     }
2511     fclose(file);
2512     gamemedals = 0;
2513     for (i=0; i<MEDALMAX; i++)
2514       if (gotmedals[i]) {
2515         gamemedals++;
2516       }
2517     initmapmsg = 1;
2518   } else {
2519     strcpy(gamename, "---New---");
2520     for (i=0; i<MEDALMAX; i++) gotmedals[i] = 0;
2521     for (i=0; i<100; i++) beatlevel[i] = 0;
2522     for (i=0; i<100; i++) {
2523       for (j=0; j<100; j++) game_gotfrags[i][j] = 0;
2524       for (j=0; j<4; j++) game_gotcorners[i][j] = 0;
2525       for (j=0; j<ROOMMAX; j++) game_enemdead[i][j] = 0;
2526     }
2527     gamemedals = 0;
2528     mapCode = 0;
2529     initmapmsg = 0;
2530     initgamemsg = 0;
2531   }
2532 }
2533 
2534 
save_options()2535 void save_options() {
2536   char filestr[512];
2537   sprintf(filestr, "%ssaves%sgame%d.opt", support_path, DIRSEP, gameNum);
2538   file = fopen(filestr, "wb");
2539   if (file) {
2540     fwrite(&mousecammode, sizeof(int), 1, file);
2541     fwrite(&runningmode, sizeof(int), 1, file);
2542     fwrite(&deathstateload, sizeof(int), 1, file);
2543     fwrite(&opencreator, sizeof(int), 1, file);
2544     fwrite(&musicon, sizeof(int), 1, file);
2545     fwrite(&guides, sizeof(int), 1, file);
2546     fwrite(&istophat, sizeof(int), 1, file);
2547     fclose(file);
2548   } else {
2549     set_up_input("The file was not opened",1,-1,-1);
2550   }
2551 }
2552 
2553 
save_game_data()2554 void save_game_data() {
2555   char filestr[512];
2556   sprintf(filestr, "%ssaves%sgame%d.gm", support_path, DIRSEP, gameNum);
2557   file = fopen(filestr, "wb");
2558   if (file) {
2559     fwrite(gamename, sizeof(char), 25, file);
2560     fwrite(gotmedals, sizeof(int), MEDALMAX, file);
2561     fwrite(&mapCode, sizeof(int), 1, file);
2562     fwrite(&initgamemsg, sizeof(int), 1, file);
2563     fwrite(beatlevel, sizeof(int), 100, file);
2564     for (i = 0; i < 100; i++) {
2565       fwrite(game_gotfrags[i], sizeof(char), 100, file);
2566       fwrite(game_gotcorners[i], sizeof(char), 4, file);
2567     }
2568     for (i = 0; i < 100; i++) {
2569       fwrite(game_enemdead[i], sizeof(char), ROOMMAX, file);
2570     }
2571     fclose(file);
2572   } else {
2573     set_up_input("The file was not opened",1,-1,-1);
2574   }
2575 }
2576 
2577 
start_new_game()2578 void start_new_game() {
2579   char filestr[512];
2580   sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
2581   remove(filestr);
2582   strcpy(gamename, inputstr);
2583   save_game_data();
2584 }
2585 
2586 
delete_game()2587 void delete_game() {
2588   char filestr[512];
2589   sprintf(filestr, "%ssaves%sgame%d.gm", support_path, DIRSEP, gameNum);
2590   remove(filestr);
2591   sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
2592   remove(filestr);
2593   sprintf(filestr, "%ssaves%sgame%d.sav", support_path, DIRSEP, gameNum);
2594   remove(filestr);
2595   sprintf(filestr, "%ssaves%sgame%d.savbkp", support_path, DIRSEP, gameNum);
2596   remove(filestr);
2597   sprintf(filestr, "%ssaves%sgame%d.opt", support_path, DIRSEP, gameNum);
2598   remove(filestr);
2599   load_game_data();
2600 }
2601 
2602 
init_leave_select(int getinputset)2603 void init_leave_select(int getinputset) {
2604   getinput = getinputset;
2605   inputlength = -3;
2606   optselect = 0;
2607   optmax = 4;
2608   paused = 0;
2609 }
2610 
2611 
init_opt_select()2612 void init_opt_select() {
2613   getinput = 15;
2614   inputlength = -3;
2615   optselect = 0;
2616   optmax = 5;
2617 }
2618 
2619 
init_sign(int mgnum)2620 void init_sign(int mgnum) {
2621   msgcode = mgnum;
2622   getinput = 3;
2623   inputlength = -1;
2624 }
2625 
2626 
use_door()2627 void use_door() {
2628   Thing door = things[beret.timer];
2629 
2630   if (door.type == FINISHDOOR) {
2631     if (spminutes > 0 || spseconds > 0 || spframes > 0) {
2632       gotgmed = 1;
2633       spminutes = 0;
2634       spseconds = 0;
2635       spframes = 0;
2636       play_sound(SND_MEDG);
2637       if (statusbar) {
2638         make_expl(GMEDPOS+15+camx,30+camy,0,0,GREEN,6,100);
2639         make_expl(GMEDPOS+15+camx,30+camy,0,0,GRYELLOW,6,40);
2640       }
2641     }
2642     if (!gotpmed && lvlCode != LAST_LEVEL) {
2643       gotpmed = 1;
2644       play_sound(SND_MEDP);
2645       if (statusbar) {
2646         make_expl(PMEDPOS+15+camx,30+camy,0,0,PURPLE,6,120);
2647         make_expl(PMEDPOS+15+camx,30+camy,0,0,RED,6,20);
2648       }
2649     }
2650   }
2651   if (door.type == READSIGN && key2 == DOWN && beret.vx == 0) {
2652     init_sign(door.dir);
2653   } else if (door.type == SIGN) {
2654     if (door.subtype%2 == 0 && beret.x <= 0 && !walkaway) {
2655       if (ingame == -1) init_fade(1);
2656       else go_to_room(door.status, door.dir);
2657       walkaway = 1;
2658     }
2659     if (door.subtype%2 == 1 && beret.x+beret.width>=lvlWidth && !walkaway) {
2660       if (ingame == -1) init_fade(1);
2661       else go_to_room(door.status, door.dir);
2662       walkaway = 2;
2663     }
2664   } else if ((door.type == FINISHDOOR || door.type == DOOR) &&
2665       key2 == DOWN && beret.vx == 0 && fadetime == 0) {
2666     if (ingame == -1) {
2667       init_fade(1);
2668     } else {
2669       if (door.type == FINISHDOOR) {
2670         set_up_input("Exit this level?",-14,-2,-1);
2671       } else if (door.type == DOOR) {
2672         go_to_room(door.status, door.dir);
2673       }
2674     }
2675   }
2676 }
2677 
2678 
begin_game()2679 void begin_game() {
2680   if (strcmp(gamename, "---New---") == 0)
2681     set_up_input("Enter your name:",2,13,-1);
2682   else {
2683     char filestr[512];
2684     sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
2685     file = fopen(filestr, "rb");
2686     if (file) {
2687       cancont = 1;
2688       initgamemsg = 1;
2689       fread(&contlvl, sizeof(int), 1, file);
2690       fclose(file);
2691       set_up_input("Continue last play?",-15,-2,-1);
2692       yesno = 1;
2693     } else {
2694       cancont = 0;
2695       init_fade(2);
2696     }
2697   }
2698 }
2699 
2700 
handle_key_down(SDLKey key)2701 void handle_key_down(SDLKey key) {
2702   SDLMod mod = SDL_GetModState();
2703 
2704   if ((key == QUITKEY_WIN && (mod & QUITMOD_WIN)) ||
2705       (key == QUITKEY_LIN && (mod & QUITMOD_LIN)) ||
2706       (key == QUITKEY_MAC && (mod & QUITMOD_MAC))) {
2707     save_options();
2708     quit = 1;
2709   }
2710 
2711   if (ingame == 0) {
2712     if (key == SDLK_ESCAPE) {
2713       quit = 1;
2714       return;
2715     }
2716     ingame = 1;
2717     load_game_data();
2718     return;
2719   } else if (key == SDLK_SLASH) {
2720     musicon = !musicon;
2721     if (musicon) switch_music(curmusic, 1);
2722     else Mix_HaltMusic();
2723     if (ingame != 0 && ingame != 1) save_options();
2724   } else if ((mod & KMOD_ALT) && key == SDLK_RETURN) {
2725     fullscreenmode = !fullscreenmode;
2726     if (fullscreenmode) {
2727       screen = SDL_SetVideoMode(SCR_WIDTH, SCR_HEIGHT,
2728 				SCR_BPP, SDL_FULLSCREEN);
2729     } else {
2730       screen = SDL_SetVideoMode(SCR_WIDTH, SCR_HEIGHT,
2731 				SCR_BPP, SDL_SWSURFACE);
2732     }
2733   }
2734 
2735   if (ingame == 4 || ingame == 5) {
2736     ingame = (ingame == 4 ? ingamereturn : 2);
2737     return;
2738   }
2739 
2740   if (ingame == 10 || ingame == 11) {
2741     ingame++;
2742     return;
2743   }
2744 
2745   if (ingame == 12) {
2746     ingame = -1;
2747     return;
2748   }
2749 
2750   if (ingame == 13 && key == SDLK_ESCAPE) {
2751     init_fade(4);
2752     return;
2753   }
2754 
2755   if (ingame == 2 && !getinput) {
2756     if (key == SDLK_e) {
2757       if (secretcode % 4 == 0) secretcode++;
2758     } else if (key == SDLK_v) {
2759       if (secretcode % 4 == 1) secretcode++;
2760     } else if (key == SDLK_i) {
2761       if (secretcode % 4 == 2) secretcode++;
2762     } else if (key == SDLK_l) {
2763       if (secretcode % 4 == 3) secretcode++;
2764       if (secretcode == 12) {
2765 	opencreator = 1;
2766 	play_sound(SND_SWITCHGV);
2767 	save_options();
2768       }
2769     } else secretcode = 0;
2770     if (key == SDLK_ESCAPE) init_leave_select(13);
2771     else if (key == SDLK_c) {
2772       ingamereturn = ingame;
2773       ingame = 4;
2774       switch_background(2);
2775     } else if (key == SDLK_s) {
2776       ingame = 5;
2777       switch_background(2);
2778     } else if (key == SDLK_a) {
2779       init_fade(9);
2780     }
2781     return;
2782   }
2783 
2784   if (ingame == 1 && !getinput) {
2785     if ((key == SDLK_RIGHT || key == SDLK_d) && gameNum < GAMEMAX) {
2786       gameNum++;
2787       load_game_data();
2788     }
2789     if ((key == SDLK_LEFT || key == SDLK_a) && gameNum > 1) {
2790       gameNum--;
2791       load_game_data();
2792     }
2793     if (key == SDLK_RETURN || key == SDLK_SPACE || key == SDLK_LSHIFT ||
2794         key == SDLK_RSHIFT) {
2795       begin_game();
2796       return;
2797     }
2798     if (key == SDLK_ESCAPE) {
2799       ingame = 0;
2800       return;
2801     }
2802     if ((key == SDLK_DELETE || key == SDLK_BACKSPACE) &&
2803         strcmp(gamename, "---New---")) {
2804       set_up_input("Delete this game?",-10,-2,-1);
2805     }
2806   }
2807 
2808   if (getinput) {
2809     if (key == SDLK_RETURN || key == SDLK_ESCAPE ||
2810         ((getinput == 1 || getinput == 3) &&
2811          (key == SDLK_w || key == SDLK_s || key == SDLK_a || key == SDLK_d ||
2812          key == SDLK_UP || key == SDLK_DOWN || key == SDLK_LEFT || key == SDLK_RIGHT))) {
2813       // take care of different reasons for getting input
2814       resolve_input(key);
2815     } else if (inputpos < inputlength && ((key >= SDLK_0 && key <= SDLK_9) ||
2816                (getinput > 0 && key >= SDLK_a && key <= SDLK_z))) {
2817       if (!(mod & KMOD_SHIFT) || (key >= SDLK_0 && key <= SDLK_9))
2818         inputstr[inputpos++] = key;
2819       else inputstr[inputpos++] = key - 'a' + 'A';
2820       inputstr[inputpos] = '\0';
2821     } else if (inputlength != -1 && inputpos > 0 && key == SDLK_BACKSPACE) {
2822       inputstr[--inputpos] = '\0';
2823       if (inputpos == 0) sprintf(inputstr, "-");
2824     } else if (inputlength == -2) {
2825       if (key == SDLK_a || key == SDLK_LEFT) yesno = 0;
2826       if (key == SDLK_d || key == SDLK_RIGHT) yesno = 1;
2827     } else if (inputlength == -3) {
2828       if (key == SDLK_s || key ==SDLK_DOWN) optselect++;
2829       if (key == SDLK_w || key ==SDLK_UP) optselect--;
2830       if (optselect >= optmax) optselect = optmax-1;
2831       if (optselect < 0) optselect = 0;
2832       if (getinput == 15) {
2833 	if (key == SDLK_a || key == SDLK_LEFT ||
2834 	    key == SDLK_d || key == SDLK_RIGHT) {
2835 	  if (optselect == 0) {
2836 	    if (key == SDLK_a || key == SDLK_LEFT) musicon--;
2837 	    else musicon++;
2838 	    if (musicon == 4) musicon = 0;
2839 	    else if (musicon == -1) musicon = 3;
2840 	    if (musicon == 1 || musicon == 2) switch_music(curmusic, 1);
2841 	    else Mix_HaltMusic();
2842 	  }
2843 	  if (optselect == 1) mousecammode = !mousecammode;
2844 	  if (optselect == 2) runningmode = !runningmode;
2845 	  if (optselect == 3) deathstateload = !deathstateload;
2846 	  if (optselect == 4) statusbar = !statusbar;
2847 	  save_options();
2848 	}
2849       }
2850     }
2851   } else {
2852     if (key == SDLK_LSHIFT || key == SDLK_RSHIFT) {
2853       key3 = SHIFT;
2854     } else if (key == SDLK_ESCAPE && ingame < 0) {
2855       if (creatormode) init_leave_select(16);
2856       else init_leave_select(17);
2857     }
2858 
2859     if (creatormode) {
2860       // creator keys
2861       if (key == SDLK_g) {
2862         crtgridsize++;
2863         crtgridsize %= 3;
2864       } else if (key == SDLK_c) {
2865         switchflags ^= NOCOLLIDEFLAG;
2866       } else if (key == SDLK_q) {
2867 	ingame = 10;
2868       } else if (key == SDLK_h) {
2869         crtgridsnap = !crtgridsnap;
2870       } else if (key == SDLK_l) {
2871         crtlinking = !crtlinking;
2872         hassaved = 0;
2873       } else if (key == SDLK_p) {
2874         set_start_pos();
2875       } else if (key == SDLK_EQUALS) {
2876         if (curbkg < BKG_MAX-1) switch_background(curbkg+1);
2877         if (curbkg == 0) switch_background(1);
2878         hassaved = 0;
2879       } else if (key == SDLK_MINUS) {
2880         if (curbkg > 1) switch_background(curbkg-1);
2881         else switch_background(-1);
2882         hassaved = 0;
2883       } else if (key == SDLK_RIGHTBRACKET) {
2884         if (curmusic < MUSIC_MAX-1) switch_music(curmusic+1, 0);
2885         hassaved = 0;
2886       } else if (key == SDLK_LEFTBRACKET) {
2887         if (curmusic > 0) switch_music(curmusic-1, 0);
2888         hassaved = 0;
2889       } else if (key == SDLK_i) {
2890         crtinventory = !crtinventory;
2891         crtlinking = 0;
2892         crtselect = 0;
2893       } else if (key == SDLK_COMMA) {
2894         crtplacedir = 0;
2895       } else if (key == SDLK_PERIOD) {
2896         crtplacedir = 1;
2897       } else if (key == SDLK_TAB) {
2898         crtplacesubtype++;
2899         crtplacesubtype %= subtype_count(crtplacetype);
2900       } else if (key == SDLK_o) {
2901         create_object();
2902       } else if (key == SDLK_u) {
2903         create_tiles(crtplacetile);
2904       } else if (key == SDLK_y) {
2905         create_tiles(EMPTY);
2906       } else if (key == SDLK_e) {
2907         set_up_input("Doors exit to room:", -1, 4, -1);
2908        } else if (key == SDLK_m) {
2909          set_up_input("Set sign message:", -13, 4, -1);
2910       } else if (key == SDLK_r) {
2911 	set_up_input("Really clear the room?",-20,-2,-1);
2912 	yesno = 0;
2913       } else if (key == SDLK_F1) {
2914         set_up_input("Save room in division:", -2, 2, lvlCode - 79);
2915       } else if (key == SDLK_F4) {
2916         set_up_input("Load room in division:", -3, 2, lvlCode - 79);
2917 /*       } else if (key == SDLK_F5) { */
2918 /*         set_up_input("Save map number:", -6, 4, -1); */
2919 /*       } else if (key == SDLK_F8) { */
2920 /*         set_up_input("Load map number:", -7, 4, -1); */
2921       } else if (key == SDLK_F9) {
2922         if (hassaved) set_up_input("Set timer minutes:", -8, 2, -1);
2923         else set_up_input("First save the room", 1, -1, -1);
2924       } else if (key == SDLK_F12) {
2925         if (hassaved) {
2926           spminutes = 0;
2927           spseconds = 0;
2928           write_metalevel();
2929         } else set_up_input("First save the room", 1, -1, -1);
2930       } else if (key >= SDLK_0 && key <= SDLK_9) {
2931         if (key3 == SHIFT) crtexit = key - '0';
2932         else crtentrance = key - '0';
2933       } else if (key == SDLK_END) {
2934         if (lvlWidth < 19*SCR_WIDTH) {
2935           lvlWidth += SCR_WIDTH;
2936           for (i=0; i< lvlHeight/SPR_SIZE; i++)
2937             fix_tile_borders((lvlWidth-SCR_WIDTH)/SPR_SIZE-1,i,0);
2938         }
2939         hassaved = 0;
2940       } else if (key == SDLK_HOME) {
2941         if (lvlWidth > SCR_WIDTH) {
2942           lvlWidth -= SCR_WIDTH;
2943           for (i=0; i< lvlHeight/SPR_SIZE; i++)
2944             fix_tile_borders(lvlWidth/SPR_SIZE-1,i,0);
2945         }
2946         fix_camera();
2947         hassaved = 0;
2948       } else if (key == SDLK_PAGEDOWN) {
2949         if (lvlHeight < 25*SCR_HEIGHT) {
2950           lvlHeight += SCR_HEIGHT;
2951           for (i=0; i< lvlWidth/SPR_SIZE; i++)
2952             fix_tile_borders(i,(lvlHeight-SCR_HEIGHT)/SPR_SIZE-1,0);
2953         }
2954         hassaved = 0;
2955       } else if (key == SDLK_PAGEUP) {
2956         if (lvlHeight > SCR_HEIGHT) {
2957           lvlHeight -= SCR_HEIGHT;
2958           for (i=0; i< lvlWidth/SPR_SIZE; i++)
2959             fix_tile_borders(i,lvlHeight/SPR_SIZE-1,0);
2960         }
2961         fix_camera();
2962         hassaved = 0;
2963       } else if (key == SDLK_v) {
2964         paste_objects();
2965         hassaved = 0;
2966       } else if (key == SDLK_DELETE || key == SDLK_BACKSPACE) {
2967         for (i=0; i<THINGMAX; i++) {
2968           if (selection[i] || telething == i) {
2969             destroy_thing(&things[i], PIT, things, i);
2970             things[i].type = NOTYPE;
2971             selection[i]=0;
2972           }
2973         }
2974         hassaved = 0;
2975       }
2976     } else if (ingame == 3 || ingame == -1) {
2977       if (key == SDLK_F4 || key == SDLK_4) {
2978 	load_state(mod & KMOD_CTRL);
2979       } else if (!fadetime) {
2980 
2981         // standard keys
2982 	if (key == SDLK_F1 || key == SDLK_1) {
2983           save_state();
2984         } else if (key == SDLK_p && !fadetime) {
2985           paused = !paused;
2986         } else if (key == SDLK_m) {
2987           mousecammode = !mousecammode;
2988 	  save_options();
2989         } else if (key == SDLK_g) {
2990           guides = !guides;
2991         } else if (ingame == 3 && key == SDLK_ESCAPE) {
2992           init_leave_select(14);
2993 	  save_options();
2994         } else if (key == SDLK_i) {
2995           camykey = UP;
2996 	  freecam = 1;
2997         } else if (key == SDLK_j) {
2998           camxkey = LEFT;
2999 	  freecam = 1;
3000         } else if (key == SDLK_k) {
3001           camykey = DOWN;
3002 	  freecam = 1;
3003         } else if (key == SDLK_l) {
3004           camxkey = RIGHT;
3005 	  freecam = 1;
3006 	} else if (key == SDLK_r) {
3007 	  init_fade(6);
3008         } else if (key == SDLK_t && !beret.dead && beatlevel[LAST_LEVEL]) {
3009 	  istophat = !istophat;
3010 	  beret.subtype = istophat;
3011 	  float cx = beret.x+beret.width/2, cy = beret.y+beret.height/2;
3012 	  make_expl(cx,cy,0,0,beret.subtype?PURPLE:GREEN,8,150);
3013 	  make_expl(cx,cy,0,0,beret.subtype?GRAY:SKY,6,75);
3014 	  make_expl(cx,cy,0,0,BROWN,7,25);
3015 	}
3016       }
3017     }
3018 
3019     if (key == SDLK_w || key == SDLK_UP || key == SDLK_SPACE) {
3020       key2 = UP;
3021       freecam = 0;
3022     } else if (key == SDLK_a || key == SDLK_LEFT) {
3023       remkey1 = key1;
3024       key1 = LEFT;
3025       freecam = 0;
3026     } else if (key == SDLK_s || key == SDLK_DOWN) {
3027       key2 = DOWN;
3028       freecam = 0;
3029     } else if (key == SDLK_d || key == SDLK_RIGHT) {
3030       remkey1 = key1;
3031       key1 = RIGHT;
3032       freecam = 0;
3033     }
3034   }
3035 }
3036 
3037 
handle_key_up(SDLKey key)3038 void handle_key_up(SDLKey key) {
3039   if (key == SDLK_w || key == SDLK_UP || key == SDLK_SPACE) {
3040     if (key2 == UP) key2 = NONE;
3041   } else if (key == SDLK_a || key == SDLK_LEFT) {
3042     if (key1 == LEFT) {key1 = remkey1; remkey1 = NONE;}
3043     if (remkey1 == LEFT) remkey1 = NONE;
3044   } else if (key == SDLK_s || key == SDLK_DOWN) {
3045     if (key2 == DOWN) key2 = NONE;
3046   } else if (key == SDLK_d || key == SDLK_RIGHT) {
3047     if (key1 == RIGHT) {key1 = remkey1; remkey1 = NONE;};
3048     if (remkey1 == RIGHT) remkey1 = NONE;
3049   } else if (key == SDLK_LSHIFT || key == SDLK_RSHIFT) {
3050     key3 = NONE;
3051   } else if (key == SDLK_i) {
3052     if (camykey == UP) camykey = NONE;
3053   } else if (key == SDLK_j) {
3054     if (camxkey == LEFT) camxkey = NONE;
3055   } else if (key == SDLK_k) {
3056     if (camykey == DOWN) camykey = NONE;
3057   } else if (key == SDLK_l) {
3058     if (camxkey == RIGHT) camxkey = NONE;
3059   }
3060 }
3061 
3062 
handle_tele_check()3063 void handle_tele_check() {
3064   int cursrad;
3065   canbetelething = -1;
3066   for (cursrad = 0; cursrad <= CURSORRADIUS; cursrad += CURSORRADIUS) {
3067     if (canbetelething == -1) {
3068       for (i=THINGMAX-1; i >= 0; i--) {
3069         if (things[i].type != NOTYPE && things[i].infront &&
3070             (creatormode || (things[i].pickup && !things[i].teledelay)) &&
3071             mx+cursrad >= things[i].x && mx-cursrad < things[i].x+things[i].width &&
3072             my+cursrad >= things[i].y && my-cursrad < things[i].y+things[i].height) {
3073           canbetelething = i;
3074           break;
3075         }
3076       }
3077     } else break;
3078     if (canbetelething == -1) {
3079       for (i=THINGMAX-1; i >= 0; i--) {
3080         if (things[i].type != NOTYPE && !things[i].infront &&
3081             (creatormode || (things[i].pickup && !things[i].teledelay)) &&
3082             mx+cursrad >= things[i].x && mx-cursrad < things[i].x+things[i].width &&
3083             my+cursrad >= things[i].y && my-cursrad < things[i].y+things[i].height) {
3084           canbetelething = i;
3085           break;
3086         }
3087       }
3088     } else break;
3089   }
3090 }
3091 
3092 
3093 // handle the setup of a link from selected Link Blocks to the
3094 // object that the mouse is over
set_link()3095 void set_link() {
3096   crtlinking = 0;
3097   for (i=0; i<THINGMAX; i++)
3098     if (things[i].type == LINKBLOCK && selection[i]) {
3099       things[i].link = canbetelething;
3100       if (things[i].subtype % 2 == 1)
3101         things[i].islinked = canbetelething;
3102       else if (canbetelething > -1) things[canbetelething].islinked = i;
3103     }
3104 }
3105 
3106 
handle_mouse_down(int x,int y,int button)3107 void handle_mouse_down(int x, int y, int button) {
3108   if (ingame == 0) {
3109     ingame = 1;
3110     load_game_data();
3111     return;
3112   } else if (ingame == 1 && !getinput) {
3113     begin_game();
3114     return;
3115   }
3116 
3117   if (ingame == 4 || ingame == 5) {
3118     ingame = (ingame == 4 ? ingamereturn : 2);
3119     return;
3120   }
3121 
3122   if (ingame == 10 || ingame == 11) {
3123     ingame++;
3124     return;
3125   }
3126 
3127   if (ingame == 12) {
3128     ingame = -1;
3129     return;
3130   }
3131 
3132   if (getinput) {
3133     resolve_input(0);
3134     return;
3135   }
3136 
3137   if (ingame== 2 && !getinput) {
3138     if (mapselect > -1 &&
3139         (button == SDL_BUTTON_LEFT || button == SDL_BUTTON_RIGHT) &&
3140         (levelentry[mapCode][mapselect] <= gamemedals || (mapCode == 4 && opencreator))) {
3141       if (my/SPR_SIZE == 1) init_fade(7);
3142       else if (mapCode == 4 && mapselect == 10) {
3143 	char filestr[512];
3144 	sprintf(filestr, "%ssaves%sgame%d.ret", support_path, DIRSEP, gameNum);
3145 	remove(filestr);
3146 	init_fade(1);
3147       }
3148       else init_fade(3);
3149 
3150     }
3151     if ((mapCode > 0 || (mapCode == 0 && (opencreator || beatlevel[LAST_LEVEL]))) &&
3152 	!(mapCode == 4 && !beatlevel[LAST_LEVEL])) {
3153       if (mx >= 30 && mx <= 120 && my >= 195 && my <= 285) {
3154         trgmap = mapCode-1;
3155 	if (trgmap < 0) trgmap = 4;
3156         init_fade(8);
3157       }
3158     }
3159     if ((mapCode < 4 && beatlevel[maplastlevel[mapCode]]) || mapCode == 4) {
3160       if (mx >= SCR_WIDTH-120 && mx <= SCR_WIDTH-30 && my >= 195 && my <= 285) {
3161         trgmap = mapCode+1;
3162 	if (trgmap > 4) trgmap = 0;
3163         init_fade(8);
3164       }
3165     }
3166     return;
3167   }
3168 
3169   if (creatormode &&
3170       (button == SDL_BUTTON_LEFT || button == SDL_BUTTON_RIGHT)) {
3171     if (crtlinking) {
3172       set_link();
3173     } else if (crtinventory) {
3174       int xtile = x/SPR_SIZE-1, ytile = y/SPR_SIZE-1, tindex;
3175       if (xtile > -1 && xtile < 24 && ytile > -1 && ytile < 18) {
3176         if (ytile < 10) {
3177           tindex = xtile+ytile*24+2;
3178           if ((tindex < TOPHAT && tindex != STONEY &&
3179 	      tindex != BLOCKSTER && tindex != MATTERLY) || tindex == MATTERFRAG) {
3180             crtplacetype = tindex;
3181             crtplacesubtype = 0;
3182           }
3183         } else {
3184           tindex = xtile+(ytile-10)*24;
3185           if (tindex < TILEMAX) crtplacetile = tindex;
3186         }
3187       }
3188       crtinventory = 0;
3189     }
3190   }
3191   if (!paused && !getinput) {
3192     if (button == SDL_BUTTON_RIGHT) {
3193       if (creatormode) {
3194         crtselect = 1;
3195         selx = mx;
3196         sely = my;
3197       }
3198       else guides = !guides;
3199     } else if (button == SDL_BUTTON_LEFT) {
3200       cantele = 1;
3201       teleon = !teleon;
3202       if (alwaystele && !teleon && telething > -1) {
3203         stop_telekinesis();
3204       }
3205       if (creatormode) clear_selection();
3206     }
3207   }
3208 }
3209 
3210 
handle_mouse_up(int x,int y,int button)3211 void handle_mouse_up(int x, int y, int button) {
3212   if (button == SDL_BUTTON_LEFT) {
3213     cantele = 0;
3214     if (!alwaystele && telething > -1) {
3215       stop_telekinesis();
3216     }
3217   }
3218   if (button == SDL_BUTTON_RIGHT && creatormode && crtselect) {
3219     if (key3 != SHIFT) clear_selection();
3220     crtselect = 0;
3221     int sell, selr, selt, selb;
3222     if (selx < mx) {
3223       sell = selx; selr = mx;
3224     } else {
3225       sell = mx; selr = selx;
3226     }
3227     if (sely < my) {
3228       selt = sely; selb = my;
3229     } else {
3230       selt = my; selb = sely;
3231     }
3232     for (i=0; i<THINGMAX; i++) {
3233       if (things[i].type != NOTYPE && things[i].x <= selr &&
3234           things[i].y <= selb && things[i].x+things[i].width > sell &&
3235           things[i].y+things[i].height > selt)
3236         selection[i] = 1;
3237     }
3238   }
3239 }
3240 
3241 
draw_sprites(int front)3242 void draw_sprites(int front) {
3243   int args[6];
3244 
3245   for (i=0; i<THINGMAX; i++) {
3246     if (things[i].type != NOTYPE &&
3247         ((things[i].infront == front && !things[i].inback) ||
3248          (things[i].inback && front == -1))) {
3249       draw_thing(&things[i], args);
3250       if (args[2] > -1) {
3251         apply_sprite(args[0]-camx,args[1]-camy,args[2],args[3],
3252                      args[4],args[5],spritesheet,screen);
3253 	// Handle Top Hat's glowing eyes
3254 	if (things[i].type == TOPHAT) {
3255 	    // If Top Hat is telekineticize-o-rama-ing something
3256 	  if ((get_bossvars())[6] > 0)
3257 	    apply_sprite(args[0]-camx+(things[i].dir?14:5),
3258 			 args[1]-camy+((things[i].jump && things[i].vy>0)?14:13),
3259 			 18,5-things[i].dir,1,1,spritesheet,screen);
3260 	}
3261       }
3262       if (creatormode) {
3263         if (things[i].type == LINKBLOCK && selection[i] &&
3264             (things[i].link > -1 || crtlinking)) {
3265           draw_line(screen,things[i].x+things[i].width/2-camx,
3266                     things[i].y+things[i].height/2-camy,
3267                     (crtlinking?mx:(things[things[i].link].x+things[things[i].link].width/2))-camx,
3268                     (crtlinking?my:(things[things[i].link].y+things[things[i].link].height/2))-camy, 0);
3269         }
3270         if (things[i].type == SPIKEBALL)
3271           apply_sprite(things[i].startx-camx,things[i].starty-camy,
3272                        19-things[i].subtype, 16+things[i].dir,1,1,
3273                        spritesheet, screen);
3274         else if (things[i].type == FAKEBLOCK) {
3275           things[i].anim = 3;
3276           draw_thing(&things[i], args);
3277           apply_sprite(args[0]-camx,args[1]-camy,args[2],args[3],
3278                        args[4],args[5],spritesheet,screen);
3279           things[i].anim = 0;
3280         } else if ((things[i].type == DOOR || things[i].type == SIGN ||
3281                     things[i].type == READSIGN) && selection[i]) {
3282             if (things[i].type == READSIGN) sprintf(messagestr, "%d", things[i].dir);
3283           else sprintf(messagestr, "%d, %d, %d", things[i].timer, things[i].status, things[i].dir);
3284           message = TTF_RenderText_Blended(smfont, messagestr, textcolor);
3285           apply_surface(things[i].x-camx, things[i].y-12-camy, message, screen);
3286           SDL_FreeSurface(message);
3287         }
3288         if (selection[i]) {
3289           apply_sprite(things[i].startx+things[i].width-SPR_SIZE-camx,
3290                        things[i].starty-camy,
3291                        19,9,1,1,spritesheet,screen);
3292           apply_sprite(things[i].startx-camx,
3293                        things[i].starty+things[i].height-SPR_SIZE-camy,
3294                        19,10,1,1,spritesheet,screen);
3295         }
3296       }
3297     }
3298   }
3299 }
3300 
3301 
draw_beret()3302 void draw_beret() {
3303   if (creatormode) {
3304     apply_sprite(startx-5-camx,starty-6-camy,
3305                  0,0,1,1,spritesheet,screen);
3306   } else {
3307     int args[6];
3308     draw_thing(&beret, args);
3309     apply_sprite(args[0]-camx,args[1]-camy,args[2],args[3],
3310                  args[4],args[5],spritesheet,screen);
3311     if (telething > -1)
3312       apply_sprite(args[0]-camx+(beret.dir?14:5),
3313 		   args[1]-camy+((beret.jump && beret.vy>0)?(istophat?14:13):(istophat?13:11)),
3314 		   18,5-beret.dir,1,1,spritesheet,screen);
3315   }
3316 }
3317 
3318 
3319 // draws a haze showing that the bottom of the screen is
3320 // a deadly pit of doom
draw_pit()3321 void draw_pit() {
3322   apply_surface(0,lvlHeight-15-camy,pit,screen);
3323 }
3324 
3325 
draw_tiles(int infront)3326 void draw_tiles(int infront) {
3327   int tl = camx/SPR_SIZE, tr = (camx+SCR_WIDTH)/SPR_SIZE;
3328   int tt = camy/SPR_SIZE, tb = (camy+SCR_HEIGHT)/SPR_SIZE;
3329   for (i = tl; i <= tr; i++) {
3330     for (j = tt; j <= tb; j++) {
3331       if (tiles[i][j][0] > 0 && (tiles[i][j][1] > 0) == infront) {
3332         apply_sprite((i-tl)*SPR_SIZE-(camx%SPR_SIZE),
3333                      (j-tt)*SPR_SIZE-(camy%SPR_SIZE),
3334                      (tiles[i][j][0]-1)%24,(tiles[i][j][0]-1)/24,1,1,
3335                      tilesheet,screen);
3336         apply_sprite((i-tl)*SPR_SIZE-(camx%SPR_SIZE),
3337                      (j-tt)*SPR_SIZE-(camy%SPR_SIZE),
3338                      tiles[i][j][2]%24,22-(see_through(tiles[i][j][0])?2:0)+
3339                      tiles[i][j][2]/24,1,1,tilesheet,screen);
3340       }
3341     }
3342   }
3343   if (creatormode && crtgridsize > 0 && !getinput) {
3344     for (i = tl; i <= tr; i++) {
3345       for (j = tt; j <= tb; j++) {
3346         apply_sprite((i-tl)*SPR_SIZE-(camx%SPR_SIZE),
3347                      (j-tt)*SPR_SIZE-(camy%SPR_SIZE),
3348                      19,4+crtgridsize,1,1,
3349                      spritesheet,screen);
3350       }
3351     }
3352   }
3353 }
3354 
3355 
draw_bkg(int flag)3356 void draw_bkg(int flag) {
3357   if (curbkg > -1 && !flag) {
3358     apply_surface(0,0,background,screen);
3359   } else if (flag) apply_surface(0,0,invbackground,screen);
3360   else apply_surface(0,0,mapbkg,screen);
3361 }
3362 
3363 
3364 // determines if one point can see another point
check_vision(int x1,int y1,int x2,int y2,int * r_tilecx,int * r_tilecy)3365 int check_vision(int x1, int y1, int x2, int y2, int *r_tilecx, int *r_tilecy) {
3366   int tilecx = x1/SPR_SIZE, tilecy = y1/SPR_SIZE;
3367   int tilemx = x2/SPR_SIZE, tilemy = y2/SPR_SIZE;
3368   float slope = 1.0*(y2-y1)/(x2-x1);
3369 
3370   // y = slope*(x-x1) + y1
3371   int checked = 0;
3372   while (!checked) {
3373     checked = tilecx == tilemx && tilecy == tilemy;
3374 
3375     int tt=tilecy*SPR_SIZE, tb=(tilecy+1)*SPR_SIZE;
3376     int tl=tilecx*SPR_SIZE, tr=(tilecx+1)*SPR_SIZE;
3377 
3378     if (tiles[tilecx][tilecy][0] && ((tiles[tilecx][tilecy][1] == SOLID || tiles[tilecx][tilecy][0] == DARKNESSTILE) &&
3379 				     !(tiles[tilecx][tilecy][0] >= SPIKEU && tiles[tilecx][tilecy][0] <= SPIKEL))) {
3380       *r_tilecx = tilecx;
3381       *r_tilecy = tilecy;
3382       return 0;
3383     }
3384 
3385     if (tilecx == tilemx) {
3386       tilecy += tilecy<tilemy?1:-1;
3387     } else {
3388       float y;
3389       if (tilecx < tilemx) {
3390         y = (tr-x1)*slope+y1;
3391       } else {
3392         y = (tl-x1)*slope+y1;
3393       }
3394       if (y < tt) tilecy--;
3395       else if (y > tb) tilecy++;
3396       else tilecx += tilecx<tilemx?1:-1;
3397       }
3398   }
3399   return 1;
3400 }
3401 
3402 
3403 // set that the last level has been beaten
set_beat_last_level()3404 void set_beat_last_level() {
3405   beatlevel[LAST_LEVEL] = 1;
3406 }
3407 
3408 
3409 // note that a fragment has been destroyed
kill_fragment()3410 void kill_fragment() {
3411   gotallfrags = -1;
3412 }
3413 
3414 
3415 // store that the given object has been collected
collect_thing(Thing * this)3416 void collect_thing(Thing* this) {
3417   if (this->type == MEDALFRAGMENT) {
3418     play_sound(SND_COLLECT+rand_to(6));
3419     if (this->dir > -1) gotmfrag[this->dir] = 1;
3420     if (this->subtype < 2) {
3421       mfragcount++;
3422       if (mfragcount == MFRAGTOTAL) {
3423 	play_sound(SND_MEDB);
3424 	if (statusbar) {
3425 	  make_expl(BMEDPOS+15+camx,30+camy,0,0,AQUA,6,150);
3426 	  make_expl(BMEDPOS+15+camx,30+camy,0,0,BLUE,6,60);
3427 	}
3428       }
3429     }
3430   } else if (this->type == MEDALCORNER) {
3431     gotmcrn[this->subtype % 4] = 1;
3432     play_sound(SND_CORNER);
3433     if (this->subtype < 4) {
3434       if ((gotmcrn[0] || game_gotcorners[lvlCode][0]) && (gotmcrn[1] || game_gotcorners[lvlCode][1]) &&
3435 	  (gotmcrn[2] || game_gotcorners[lvlCode][2]) && (gotmcrn[3] || game_gotcorners[lvlCode][3])) {
3436 	play_sound(SND_MEDO);
3437 	if (statusbar) {
3438 	  make_expl(OMEDPOS+15+camx,30+camy,0,0,ORANGE,6,150);
3439 	  make_expl(OMEDPOS+15+camx,30+camy,0,0,BROWN,6,60);
3440 	}
3441       }
3442     }
3443   } else if (this->type == WHITEMEDAL) {
3444     gotmwht = 1;
3445     play_sound(SND_MEDW);
3446     if (statusbar && this->subtype == 0) {
3447       make_expl(WMEDPOS+15+camx,30+camy,0,0,WHITE,6,150);
3448       make_expl(WMEDPOS+15+camx,30+camy,0,0,YELLOW,6,60);
3449     }
3450   }
3451 }
3452 
3453 
3454 // determine if a point is within telekinesis range of another point
check_range(int x1,int y1,int x2,int y2,int cx,int cy)3455 int check_range(int x1, int y1, int x2, int y2, int cx, int cy) {
3456   int tilecx = 0, tilecy = 0, check_vision_ret = 0;
3457   check_vision_ret = check_vision(x1, y1, x2, y2, &tilecx, &tilecy);
3458   return sqrt(f_sqr(x2-cx) + f_sqr(y2-cy)) <= SPR_SIZE*TELERADIUS &&
3459     check_vision_ret;
3460 }
3461 
3462 
3463 // helper functions for check_can_see
get_x_see_coord(Thing this,int which)3464 int get_x_see_coord(Thing this, int which) {
3465   switch (which) {
3466   case 0: return this.x;
3467   case 1: return this.x+this.width/2;
3468   case 2: return this.x+this.width;
3469   default : return 0;
3470   }
3471 }
get_y_see_coord(Thing this,int which)3472 int get_y_see_coord(Thing this, int which) {
3473   switch (which) {
3474   case 0: return this.y;
3475   case 1: return this.y+this.height/2;
3476   case 2: return this.y+this.height;
3477   default : return 0;
3478   }
3479 }
3480 
3481 
3482 // determine if the first thing can see the second thing
check_can_see(Thing t1,Thing t2)3483 int check_can_see(Thing t1, Thing t2) {
3484   int coordi, coordj;
3485   for (coordi=0; coordi<9; coordi++)
3486     for (coordj=0; coordj<9; coordj++)
3487       if (check_range(get_x_see_coord(t1,coordi/3),
3488                       get_y_see_coord(t1,coordi%3),
3489                       get_x_see_coord(t2,coordj/3),
3490                       get_y_see_coord(t2,coordj%3),
3491                       t1.x+t1.width/2, t1.y+t1.height/2))
3492         return 1;
3493   return 0;
3494 }
3495 
3496 
3497 // necessary functionality for moving objects, checking
3498 // telekinesis bounds
do_telekinesis()3499 void do_telekinesis() {
3500   //  int part_x = beret.dir?14:5, part_y = (beret.jump && beret.vy>0)?13:11;
3501   telestat = 0;
3502   if (telething > -1) {
3503     // Make particles stream from Beret's eyes
3504     /* if (rand_to(5) == 0) */
3505     /*   create_particle(beret.x+part_x-3+rand_to(3), beret.y+part_y-3+rand_to(3), 0, 0, (rand_to(3) == 0?BLUE:WHITE),  */
3506     /* 		      3+rand_to(4)); */
3507     /* if (rand_to(5) == 0) */
3508     /*   create_particle(beret.x+part_x+2+rand_to(3), beret.y+part_y+(beret.dir?-1:1)-3+rand_to(3), 0, 0,  */
3509     /* 		      (rand_to(3) == 0?BLUE:WHITE), 3+rand_to(4)); */
3510     // Do the actual telekinesis
3511     Thing ttt = things[telething];
3512     if (!creatormode && (ttt.type == NOTYPE || !ttt.pickup ||
3513         !check_can_see(beret, ttt))) {
3514       telestat = 1;
3515       stop_telekinesis();
3516     } else if (creatormode || (!things[telething].infront && things[telething].islinked == -1)) {
3517       float cx = things[telething].x+things[telething].width/2;
3518       float cy = things[telething].y+things[telething].height/2;
3519       things[telething].vx = approach(things[telething].vx,
3520                                       cap_val((mx-cx)*TELEMODIF,TELECAP),
3521                                       TELEACCEL);
3522       things[telething].vy = approach(things[telething].vy,
3523                                       cap_val((my-cy)*TELEMODIF,TELECAP),
3524                                       TELEACCEL);
3525       if (things[telething].solid) {
3526         if ((things[telething].collide[LEFT] && things[telething].vx < 0) ||
3527             (things[telething].collide[RIGHT] && things[telething].vx > 0))
3528           things[telething].vx = 0;
3529         if ((things[telething].collide[UP] && things[telething].vy < 0) ||
3530             (things[telething].collide[DOWN] && things[telething].vy > 0))
3531           things[telething].vy = 0;
3532       }
3533     }
3534   } else if (canbetelething > -1) {
3535     Thing cbtt = things[canbetelething];
3536     if (!creatormode && !check_can_see(beret, cbtt)) {
3537       telestat = 1;
3538     }
3539   } else if (!creatormode && !check_range(beret.x+beret.width/2, beret.y+beret.height/2, mx, my,
3540                                           beret.x+beret.width/2, beret.y+beret.height/2)) {
3541     telestat = 1;
3542   }
3543   if (!telestat)
3544     telestat = telething > -1 ? 0 : (canbetelething > -1 ? 3 : 2);
3545 }
3546 
3547 
draw_cursor()3548 void draw_cursor() {
3549   apply_sprite(mx-camx-7,my-camy-7,19,telestat,1,1,spritesheet,screen);
3550 }
3551 
3552 
draw_inventory()3553 void draw_inventory() {
3554   Thing displthing;
3555   int xpos, ypos;
3556   int args[6];
3557   displthing.subtype = 0;
3558   displthing.dir = 0;
3559   displthing.status = 0;
3560   displthing.anim = 0;
3561   displthing.telething = 0;
3562 
3563   for (i=2; i <= TOPHATSHIELD; i++) {
3564     if (i == STONEY || i == BLOCKSTER || i == MATTERLY ||
3565 	i == TOPHAT || i == SHIELDGEN ||
3566 	(i >= TYPEMAX && i <= SPIKEBLOCK) || i == TOPHATSHIELD) continue;
3567     displthing.type = i;
3568     displthing.x = ((i-2)%24)*SPR_SIZE+SPR_SIZE;
3569     displthing.y = ((i-2)/24)*SPR_SIZE+SPR_SIZE;
3570     displthing.startx = displthing.x;
3571     displthing.starty = displthing.y;
3572 
3573     draw_thing(&displthing, args);
3574     apply_sprite(args[0],args[1],args[2],args[3],
3575                  1,1,spritesheet,screen);
3576   }
3577 
3578   apply_sprite(30,330,19,7,1,1,spritesheet,screen);
3579   for (i=1; i < TILEMAX; i++) {
3580     xpos = (i%24)*SPR_SIZE+SPR_SIZE;
3581     ypos = (i/24)*SPR_SIZE+SPR_SIZE+SCR_HEIGHT/2;
3582     apply_sprite(xpos,ypos,(i-1)%24,(i-1)/24,1,1,tilesheet,screen);
3583   }
3584 
3585   for (i = 0; i < SCR_WIDTH; i+=SPR_SIZE) {
3586     for (j = 0; j < SCR_HEIGHT; j+=SPR_SIZE) {
3587       apply_sprite(i,j,19,5,1,1,spritesheet,screen);
3588     }
3589   }
3590 }
3591 
3592 
draw_get_input()3593 void draw_get_input() {
3594   if (getinput == 3) { // Show message
3595     apply_surface(SCR_WIDTH/2-msgback->w/2, SCR_HEIGHT/2-msgback->h/2,msgback,screen);
3596     if(lvlCode < 80){
3597       for (i=0; i<8; i++) {
3598         display_message(SCR_WIDTH/2,SCR_HEIGHT/2-msgback->h/2+24+20*i,medfont,msgs[msgcode][i],1);
3599       }
3600     } else {
3601       char filestr[250];
3602       char msgstr[250];
3603       int msglen;
3604       sprintf(filestr, "%srooms%ssign%d-%d.txt", support_path, DIRSEP, lvlCode, msgcode);
3605       msgfile = fopen(filestr, "r");
3606       if(msgfile){
3607         for (i=0; i<8; i++) {
3608           if(fgets(msgstr, sizeof msgstr, msgfile) != NULL) {
3609             msglen = strlen(msgstr);
3610             if(msglen > 0 && (msgstr[msglen - 1] == '\r' || msgstr[msglen - 1] == '\n')){
3611               msgstr[msglen - 1] = '\0';
3612               msglen--;
3613             }
3614             if(msglen > 0 && msgstr[msglen - 1] == '\r'){
3615               msgstr[msglen - 1] = '\0';
3616               msglen--;
3617             }
3618             if(msglen > 0){
3619               display_message(SCR_WIDTH/2,SCR_HEIGHT/2-msgback->h/2+24+20*i,medfont,msgstr,1);
3620             }
3621           }
3622         }
3623         fclose(msgfile);
3624       }
3625     }
3626     display_message(SCR_WIDTH/2,SCR_HEIGHT/2+msgback->h/2-14,smfont,"PRESS ENTER TO CONTINUE",1);
3627   } else if (getinput == 14 || getinput == 13 || getinput == 17) { // Get input to go back to map screen
3628     apply_surface(SCR_WIDTH/2-getinputback->w/2, SCR_HEIGHT/2-getinputback->h/2,getinputback,screen);
3629     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+20,font,"Return to game",0);
3630     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+50,font,
3631 		    (getinput == 14 ? "Exit to map" : (getinput == 17 ? "Exit to creator" : "Exit to title")),0);
3632     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+80,font,"Options",0);
3633     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+110,font,"Controls",0);
3634     apply_sprite(SCR_WIDTH/2-125,SCR_HEIGHT/2-getinputback->h/2+20+30*optselect-7,0,15,1,1,spritesheet,screen);
3635     apply_sprite(SCR_WIDTH/2+110,SCR_HEIGHT/2-getinputback->h/2+20+30*optselect-7,0,15,1,1,spritesheet,screen);
3636     display_message(SCR_WIDTH/2,SCR_HEIGHT/2+getinputback->h/2-14,smfont,"PRESS ENTER TO CONTINUE",0);
3637   } else if (getinput == 16) { // Get input in level creator
3638     apply_surface(SCR_WIDTH/2-getinputback->w/2, SCR_HEIGHT/2-getinputback->h/2,getinputback,screen);
3639     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+20,font,"Return to creator",0);
3640     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+50,font,"Test room",0);
3641     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+80,font,"Exit to map",0);
3642     display_message(SCR_WIDTH/2,SCR_HEIGHT/2-getinputback->h/2+110,font,"Controls",0);
3643     apply_sprite(SCR_WIDTH/2-125,SCR_HEIGHT/2-getinputback->h/2+20+30*optselect-7,0,15,1,1,spritesheet,screen);
3644     apply_sprite(SCR_WIDTH/2+110,SCR_HEIGHT/2-getinputback->h/2+20+30*optselect-7,0,15,1,1,spritesheet,screen);
3645     display_message(SCR_WIDTH/2,SCR_HEIGHT/2+getinputback->h/2-14,smfont,"PRESS ENTER TO CONTINUE",0);
3646   } else if (getinput == 15) { // Options screen
3647     apply_surface(SCR_WIDTH/2-optback->w/2, SCR_HEIGHT/2-optback->h/2,optback,screen);
3648     display_message(SCR_WIDTH/2-70,SCR_HEIGHT/2-optback->h/2+20,font,"Sound on:",0);
3649     if (musicon == 0) {
3650       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+20,font,"No",0);
3651     } else if (musicon == 1) {
3652       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+20,font,"Yes",0);
3653     } else if (musicon == 2) {
3654       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+20,font,"Music Only",0);
3655     } else if (musicon == 3) {
3656       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+20,font,"SFX Only",0);
3657     }
3658     display_message(SCR_WIDTH/2-70,SCR_HEIGHT/2-optback->h/2+55,font,"Mouse mode:",0);
3659     if (mousecammode) {
3660       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+55,font,"Normal",0);
3661     } else {
3662       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+55,font,"Alternate",0);
3663     }
3664     display_message(SCR_WIDTH/2-70,SCR_HEIGHT/2-optback->h/2+90,font,"Running mode:",0);
3665     if (runningmode) {
3666       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+90,font,"Walk with shift",0);
3667     } else {
3668       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+90,font,"Double tap",0);
3669     }
3670     display_message(SCR_WIDTH/2-50,SCR_HEIGHT/2-optback->h/2+125,font,"Load state on death:",0);
3671     if (deathstateload) {
3672       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+125,font,"Yes",0);
3673     } else {
3674       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+125,font,"No",0);
3675     }
3676     display_message(SCR_WIDTH/2-50,SCR_HEIGHT/2-optback->h/2+160,font,"Display status bar:",0);
3677     if (statusbar) {
3678       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+160,font,"Yes",0);
3679     } else {
3680       display_message(SCR_WIDTH/2+110,SCR_HEIGHT/2-optback->h/2+160,font,"No",0);
3681     }
3682     apply_sprite(SCR_WIDTH/2-215,SCR_HEIGHT/2-optback->h/2+20+35*optselect-7,0,15,1,1,spritesheet,screen);
3683     apply_sprite(SCR_WIDTH/2+200,SCR_HEIGHT/2-optback->h/2+20+35*optselect-7,0,15,1,1,spritesheet,screen);
3684     display_message(SCR_WIDTH/2,SCR_HEIGHT/2+optback->h/2-14,smfont,"PRESS ENTER TO CONTINUE",0);
3685   } else { // Other
3686     apply_surface(SCR_WIDTH/2-getinputback->w/2, SCR_HEIGHT/2-90,getinputback,screen);
3687     display_message(SCR_WIDTH/2, SCR_HEIGHT/2-60, font,getinputstr,0);
3688     if (inputlength > -1) {
3689       display_message(SCR_WIDTH/2,SCR_HEIGHT/2-15, font,inputstr,0);
3690     }
3691     if (inputlength == -2) {
3692       display_message(SCR_WIDTH/2-60,SCR_HEIGHT/2-15,font,"No",0);
3693       if (!yesno) {
3694         apply_sprite(SCR_WIDTH/2-60-message->w/2-20, SCR_HEIGHT/2-22,0,15,1,1,spritesheet,screen);
3695         apply_sprite(SCR_WIDTH/2-60+message->w/2+5, SCR_HEIGHT/2-22,0,15,1,1,spritesheet,screen);
3696       }
3697       display_message(SCR_WIDTH/2+60,SCR_HEIGHT/2-15,font,"Yes",0);
3698       if (yesno) {
3699         apply_sprite(SCR_WIDTH/2+60-message->w/2-20, SCR_HEIGHT/2-22,0,15,1,1,spritesheet,screen);
3700         apply_sprite(SCR_WIDTH/2+60+message->w/2+5, SCR_HEIGHT/2-22,0,15,1,1,spritesheet,screen);
3701       }
3702     }
3703     display_message(SCR_WIDTH/2,SCR_HEIGHT/2+30,smfont,"PRESS ENTER TO CONTINUE",0);
3704   }
3705 }
3706 
3707 
draw_paused()3708 void draw_paused() {
3709   apply_surface(SCR_WIDTH/2-getinputback->w/2, SCR_HEIGHT/2-75,getinputback,screen);
3710   message = TTF_RenderText_Blended(font, "Paused", textcolor);
3711   apply_surface(SCR_WIDTH/2-message->w/2, SCR_HEIGHT/2-30, message, screen);
3712   SDL_FreeSurface(message);
3713   message = TTF_RenderText_Blended(smfont, "PRESS P TO CONTINUE", textcolor);
3714   apply_surface(SCR_WIDTH/2-message->w/2, SCR_HEIGHT/2+15, message, screen);
3715   SDL_FreeSurface(message);
3716   message = TTF_RenderText_Blended(font, "Paused", textcolor2);
3717   apply_surface(SCR_WIDTH/2-message->w/2+TEXTSHADOW, SCR_HEIGHT/2-30+TEXTSHADOW, message, screen);
3718   SDL_FreeSurface(message);
3719   message = TTF_RenderText_Blended(smfont, "PRESS P TO CONTINUE", textcolor2);
3720   apply_surface(SCR_WIDTH/2-message->w/2+TEXTSHADOW, SCR_HEIGHT/2+15+TEXTSHADOW, message, screen);
3721   SDL_FreeSurface(message);
3722 }
3723 
3724 
draw_svst_msg()3725 void draw_svst_msg() {
3726   if (svstcount > 0) {
3727     svstcount--;
3728     display_message(150, SCR_HEIGHT-60,font,"Saved State",0);
3729   } else if (svstcount < 0) {
3730     svstcount++;
3731     display_message(150, SCR_HEIGHT-60,font,"Loaded State",0);
3732   }
3733 }
3734 
3735 
draw_status_bar()3736 void draw_status_bar() {
3737 
3738   if (gotmwht || haswmed) apply_sprite(WMEDPOS,15,5,15,1,1,spritesheet,screen);
3739   else apply_sprite(WMEDPOS,15,17,17,1,1,spritesheet,screen);
3740 
3741   if (!hasomed && !((gotmcrn[0] || game_gotcorners[lvlCode][0]) && (gotmcrn[1] || game_gotcorners[lvlCode][1]) &&
3742 		    (gotmcrn[2] || game_gotcorners[lvlCode][2]) && (gotmcrn[3] || game_gotcorners[lvlCode][3]))) {
3743     apply_sprite(OMEDPOS,15,17,17,1,1,spritesheet,screen);
3744     for (i=0;i<4;i++)
3745       if (gotmcrn[i] || game_gotcorners[lvlCode][i])
3746         apply_sprite(OMEDPOS+(i>0&&i<3?15:0),15+(i>1?15:0),
3747                      (i<3?4:3),(i<3?8+i:10),1,1,spritesheet,screen);
3748   }
3749   else apply_sprite(OMEDPOS,15,5,18,1,1,spritesheet,screen);
3750 
3751   if (mfragcount < MFRAGTOTAL && !hasbmed) {
3752     if (gotallfrags == 1) apply_sprite(BMEDPOS-2,15,18,9,1,1,spritesheet,screen);
3753     apply_sprite(BMEDPOS+2,17,2,6,1,1,spritesheet,screen);
3754     apply_sprite(BMEDPOS+18,25,2,6,1,1,spritesheet,screen);
3755     apply_sprite(BMEDPOS+2,33,2,7,1,1,spritesheet,screen);
3756     if (gotallfrags == -1) apply_sprite(BMEDPOS-2,15,18,15,1,1,spritesheet,screen);
3757   } else apply_sprite(BMEDPOS,15,5,19,1,1,spritesheet,screen);
3758   sprintf(messagestr, "%d", mfragcount);
3759   message = TTF_RenderText_Blended(font, messagestr, textcolor);
3760   apply_surface(BMEDPOS+MEDINFODIST,15,message,screen);
3761   SDL_FreeSurface(message);
3762   message = TTF_RenderText_Blended(font, messagestr, textcolor2);
3763   apply_surface(BMEDPOS+MEDINFODIST+TEXTSHADOW,15+TEXTSHADOW,message,screen);
3764   SDL_FreeSurface(message);
3765 
3766   if (!enemalldead && !hasrmed) apply_sprite(RMEDPOS,15,17,17,1,1,spritesheet,screen);
3767   else apply_sprite(RMEDPOS,15,5,16,1,1,spritesheet,screen);
3768   if (enemdead[areaCode] || game_enemdead[lvlCode][areaCode]) apply_sprite(RMEDPOS+MEDINFODIST,15,18,8,1,1,spritesheet,screen);
3769   else if (enemdeadhere) apply_sprite(RMEDPOS+MEDINFODIST,15,19,8,1,1,spritesheet,screen);
3770   else {
3771     int cmod = (count/3)%8;
3772     apply_sprite(RMEDPOS+MEDINFODIST,15,19,(cmod<5?11+cmod:20-cmod),1,1,spritesheet,screen);
3773   }
3774 
3775   if (hasgmed || gotgmed) apply_sprite(GMEDPOS,15,5,17,1,1,spritesheet,screen);
3776   else apply_sprite(GMEDPOS,15,17,17,1,1,spritesheet,screen);
3777   if (!(spframes < 10 && spminutes == 0 && spseconds > 0 && spseconds < 15)) {
3778     sprintf(messagestr, "%d : %02d", spminutes, spseconds);
3779     message = TTF_RenderText_Blended(font, messagestr, textcolor);
3780     apply_surface(GMEDPOS+MEDINFODIST,15,message,screen);
3781     SDL_FreeSurface(message);
3782     message = TTF_RenderText_Blended(font, messagestr, textcolor2);
3783     apply_surface(GMEDPOS+MEDINFODIST+TEXTSHADOW,15+TEXTSHADOW,message,screen);
3784     SDL_FreeSurface(message);
3785   }
3786 
3787   if (haspmed || gotpmed) apply_sprite(PMEDPOS,15,3,11,1,1,spritesheet,screen);
3788   else {
3789     apply_sprite(PMEDPOS,15,17,17,1,1,spritesheet,screen);
3790     //    if (usedsavestate) apply_sprite(PMEDPOS,15,19,7,1,1,spritesheet,screen);
3791   }
3792 }
3793 
3794 
draw_guides()3795 void draw_guides() {
3796   int midx = (telesource == -1 ? beret.x+beret.width/2 :
3797               things[telesource].x+things[telesource].width/2);
3798   int midy = (telesource == -1 ? beret.y+beret.height/2 :
3799               things[telesource].y+things[telesource].height/2);
3800   apply_surface(midx-camx-SPR_SIZE*TELERADIUS,
3801                 midy-camy-SPR_SIZE*TELERADIUS,
3802                 teleguide, screen);
3803 }
3804 
3805 
draw_select_game()3806 void draw_select_game() {
3807   apply_surface(SCR_WIDTH/2-gameselect->w/2, 330, gameselect, screen);
3808   sprintf(messagestr, "Game %d", gameNum);
3809   display_message(SCR_WIDTH/2, 355, font, 0,0);
3810   display_message(SCR_WIDTH/2, 400, font, gamename,0);
3811   display_message(SCR_WIDTH/2, 470, smfont, "PRESS ENTER TO BEGIN",0);
3812   if (gamemedals > -1) {
3813     sprintf(messagestr, "%d", gamemedals);
3814     display_message(SCR_WIDTH/2+30, 435, font, 0,0);
3815     apply_sprite(SCR_WIDTH/2-message->w/2-35,435-message->h/2,5,15,
3816                  1,1,spritesheet,screen);
3817   }
3818   if (gameNum > 1) apply_sprite(SCR_WIDTH/2-gameselect->w/2+15, 445,
3819                      19,16,1,1,spritesheet,screen);
3820   if (gameNum < GAMEMAX) apply_sprite(SCR_WIDTH/2+gameselect->w/2-30, 445,
3821                      19,17,1,1,spritesheet,screen);
3822 }
3823 
3824 
draw_title_screen()3825 void draw_title_screen() {
3826   apply_surface(0,0,title,screen);
3827   sprintf(messagestr, "Beret 1.2.1");
3828   display_message(45, 15, smfont, 0, 1);
3829 }
3830 
3831 
square(int x)3832 int square(int x) {
3833   return x * x;
3834 }
3835 
3836 
cube(int x)3837 int cube(int x) {
3838   return x * x * x;
3839 }
3840 
3841 
credit_square(int shift,int div)3842 int credit_square(int shift, int div) {
3843   return square(credittime-shift)/div;
3844 }
3845 
3846 
credit_cube(int shift,int div)3847 int credit_cube(int shift, int div) {
3848   return cube(credittime-shift)/div;
3849 }
3850 
3851 
draw_credits()3852 void draw_credits() {
3853   apply_surface(0,0,fades[4],screen);
3854   apply_surface(0,0,credits,screen);
3855   // Game By: - Nigel Kilmer
3856   display_message(300 - credit_square(150,200), 350 - credit_cube(150,4000), font, "Game by:", 0);
3857   display_message(SCR_WIDTH - 300 + credit_square(170,200), 400 + credit_cube(170,4000), font, "Nigel Kilmer", 0);
3858   display_message(SCR_WIDTH - 250 + credit_square(170,200), 450 + credit_cube(170,4000), smfont, "(Kiwisauce)", 0);
3859   /*
3860   // Design
3861   display_message(320 - credit_square(350,200), 350 - credit_cube(350,4000), font, "Design:", 0);
3862   display_message(SCR_WIDTH - 320 + credit_square(370,200), 400 + credit_cube(370,4000), font, "Nigel Kilmer", 0);
3863   // Programming
3864   display_message(300 - credit_square(550,200), 370 - credit_cube(550,4000), font, "Programming:", 0);
3865   display_message(SCR_WIDTH - 300 + credit_square(570,200), 420 + credit_cube(570,4000), font, "Nigel Kilmer", 0);
3866   // Level Design
3867   display_message(350 - credit_square(750,200), 440 - credit_cube(750,4000), font, "Level Design:", 0);
3868   display_message(SCR_WIDTH - 300 + credit_square(770,200), 490 + credit_cube(770,4000), font, "Nigel Kilmer", 0);
3869   // Art
3870   display_message(310 - credit_square(950,200), 340 - credit_cube(950,4000), font, "Art:", 0);
3871   display_message(SCR_WIDTH - 310 + credit_square(970,200), 390 + credit_cube(970,4000), font, "Nigel Kilmer", 0);
3872   // Music
3873   display_message(270 - credit_square(1150,200), 320 - credit_cube(1150,4000), font, "Music:", 0);
3874   display_message(SCR_WIDTH - 400 + credit_square(1170,200), 370 + credit_cube(1170,4000), font, "Nigel Kilmer", 0);
3875   // Sound
3876   display_message(330 - credit_square(1350,200), 440 - credit_cube(1350,4000), font, "Sound:", 0);
3877   display_message(SCR_WIDTH - 350 + credit_square(1370,200), 490 + credit_cube(1370,4000), font, "Nigel Kilmer", 0);
3878   */
3879   // Playtesters
3880   display_message(250 - credit_square(350,200), 350 - credit_cube(350,4000), font, "Thanks to all playtesters!", 0);
3881   display_message(SCR_WIDTH - 250 - credit_square(500,200), 490 + credit_cube(500,4000), font, "Stefan Roger", 0);
3882   display_message(SCR_WIDTH - 550 + credit_square(575,200), 400 + credit_cube(575,4000), font, "Bret Sepulveda", 0);
3883   display_message(SCR_WIDTH - 450 + credit_square(650,200), 320 + credit_cube(650,4000), font, "Kyle Kilmer", 0);
3884   display_message(SCR_WIDTH - 320 + credit_square(725,200), 280 + credit_cube(725,4000), font, "Nathan Weizenbaum", 0);
3885   display_message(SCR_WIDTH - 350 + credit_square(800,200), 420 + credit_cube(800,4000), font, "KC Gidewall", 0);
3886   display_message(SCR_WIDTH - 470 + credit_square(875,200), 300 + credit_cube(875,4000), font, "Alyssa Gidewall", 0);
3887   display_message(SCR_WIDTH - 450 + credit_square(950,200), 410 + credit_cube(950,4000), font, "Ivan Kozlov", 0);
3888   display_message(SCR_WIDTH - 350 + credit_square(1025,200), 500 + credit_cube(1025,4000), font, "Daniel Mills", 0);
3889   display_message(SCR_WIDTH - 250 + credit_square(1100,200), 430 + credit_cube(1100,4000), font, "Raymond Zhang", 0);
3890   display_message(SCR_WIDTH - 550 + credit_square(1175,200), 290 + credit_cube(1175,4000), font, "Jonathan Kane", 0);
3891   display_message(SCR_WIDTH - 300 + credit_square(1250,200), 400 + credit_cube(1250,4000), font, "Tristan Pearson", 0);
3892   display_message(SCR_WIDTH - 460 + credit_square(1325,200), 250 + credit_cube(1325,4000), font, "Beau Pearson", 0);
3893   // Special thanks
3894   display_message(340 - credit_square(1450,200), 350 - credit_cube(1450, 4000), font, "Special thanks to:", 0);
3895   display_message(380 - credit_square(1525,200), 420 - credit_cube(1525, 4000), font, "Mitchell", 0);
3896   display_message(300 - credit_square(1600,200), 400 - credit_cube(1600, 4000), font, "Isocitration", 0);
3897   display_message(340 - credit_square(1675,200), 330 - credit_cube(1675, 4000), font, "FrostyFish88", 0);
3898   // Copyright
3899   if (credittime < 1800) {
3900     display_message(300, 420 + credit_square(1800,200), font, "Copyright 2012 Nigel Kilmer", 0);
3901   } else {
3902     display_message(300, 420, font, "Copyright 2012 Nigel Kilmer", 0);
3903     display_message(400, 470, smfont, "Thanks for playing!", 0);
3904     display_message(400, 500, smfont, "kiwisauce.com", 0);
3905   }
3906 }
3907 
3908 
draw_creator_guides()3909 void draw_creator_guides() {
3910   Thing displthing;
3911   int args[6];
3912 
3913   if (crtselect) {
3914     int sell, selr, selt, selb;
3915     if (selx < mx) {
3916       sell = selx-camx; selr = mx-camx;
3917     } else {
3918       sell = mx-camx; selr = selx-camx;
3919     }
3920     if (sely < my) {
3921       selt = sely-camy; selb = my-camy;
3922     } else {
3923       selt = my-camy; selb = sely-camy;
3924     }
3925     if (sell < 0) sell = 0;
3926     if (sell >= SCR_WIDTH) sell = SCR_WIDTH-1;
3927     if (selt < 0) selt = 0;
3928     if (selt >= SCR_HEIGHT) selt = SCR_HEIGHT-1;
3929     if (selr < 0) selr = 0;
3930     if (selr >= SCR_WIDTH) selr = SCR_WIDTH-1;
3931     if (selb < 0) selb = 0;
3932     if (selb >= SCR_HEIGHT) selb = SCR_HEIGHT-1;
3933     draw_rect(screen, sell, selt, selr, selb, selcolor, 0x50);
3934   }
3935   int xoffst=15, yoffst=15;
3936   if (mx-camx < SCR_WIDTH/4 && my-camy < SCR_HEIGHT/4)
3937     xoffst = SCR_WIDTH/3;
3938   apply_sprite(xoffst,yoffst,16,18,4,2,spritesheet,screen);
3939   if (crtplacetile > 0)
3940     apply_sprite(xoffst+15,yoffst+15,(crtplacetile-1)%24,
3941                  (crtplacetile-1)/24,1,1,tilesheet,screen);
3942   displthing.type = crtplacetype;
3943   displthing.subtype = crtplacesubtype;
3944   displthing.dir = crtplacedir;
3945   displthing.status = 0;
3946   displthing.anim = 0;
3947   displthing.telething = 0;
3948   displthing.x = xoffst+60;
3949   displthing.y = yoffst+15;
3950   displthing.startx = displthing.x;
3951   displthing.starty = displthing.y;
3952   draw_thing(&displthing, args);
3953   apply_sprite(args[0],args[1],args[2],args[3],
3954                1,1,spritesheet,screen);
3955   if (crtplacetype == SPIKEBALL)
3956     apply_sprite(displthing.x,displthing.y,
3957                  19-crtplacesubtype, 16+crtplacedir,1,1,
3958                  spritesheet, screen);
3959   else if (crtplacetype == DOOR || crtplacetype == SIGN || crtplacetype == READSIGN) {
3960     if (crtplacetype == READSIGN) sprintf(messagestr, "%d", crtmessage);
3961     else sprintf(messagestr, "%d, %d, %d", crtentrance, crtexit, crtexitroom);
3962     message = TTF_RenderText_Blended(smfont, messagestr, textcolor);
3963     apply_surface(displthing.x, displthing.y-12, message, screen);
3964     SDL_FreeSurface(message);
3965   }
3966 }
3967 
3968 
handle_grid_snap()3969 void handle_grid_snap() {
3970   int gridsize = crtgridsize == 1 ? SPR_SIZE : SPR_SIZE/2;
3971   int tx, ty, cx, cy;
3972   for (i=0; i<THINGMAX; i++) {
3973     if (things[i].type != NOTYPE) {
3974       if (abs(things[i].vx) < SNAPVEL && !things[i].nomove) {
3975         tx = things[i].x;
3976         cx = things[i].x+things[i].width/2;
3977         if (tx % gridsize < SNAPSIZE)
3978           things[i].x -= tx % gridsize;
3979         else if (tx % gridsize >= gridsize - SNAPSIZE)
3980           things[i].x += gridsize - tx % gridsize;
3981         else if ((tx+things[i].width) % gridsize < SNAPSIZE)
3982           things[i].x -= (tx+things[i].width) % gridsize;
3983         else if (crtgridsize == 1) {
3984           if ((tx+things[i].width) % gridsize >= gridsize - SNAPSIZE)
3985             things[i].x += gridsize - (tx+things[i].width) % gridsize;
3986           else if (cx % gridsize < gridsize/2+SNAPSIZE &&
3987                    cx % gridsize >= gridsize/2-SNAPSIZE)
3988             things[i].x += gridsize/2 - cx % gridsize;
3989         }
3990       }
3991       if (abs(things[i].vy) < SNAPVEL && !things[i].nomove) {
3992         ty = things[i].y;
3993         cy = things[i].y+things[i].height/2;
3994         if (ty % gridsize < SNAPSIZE)
3995           things[i].y -= ty % gridsize;
3996         else if (ty % gridsize >= gridsize - SNAPSIZE)
3997           things[i].y += gridsize - ty % gridsize;
3998         else if ((ty+things[i].height) % gridsize < SNAPSIZE)
3999           things[i].y -= (ty+things[i].height) % gridsize;
4000         else if (crtgridsize == 1) {
4001           if ((ty+things[i].height) % gridsize >= gridsize - SNAPSIZE)
4002             things[i].y += gridsize - (ty+things[i].height) % gridsize;
4003           else if (cy % gridsize < gridsize/2+SNAPSIZE &&
4004                    cy % gridsize >= gridsize/2-SNAPSIZE)
4005             things[i].y += gridsize/2 - cy % gridsize;
4006         }
4007       }
4008     }
4009 
4010     things[i].x = (int)things[i].x;
4011     things[i].y = (int)things[i].y;
4012   }
4013 }
4014 
4015 
handle_start_pos()4016 void handle_start_pos() {
4017   for (i=0; i<THINGMAX; i++) {
4018     if (things[i].type != NOTYPE) {
4019       things[i].startx = things[i].x;
4020       things[i].starty = things[i].y;
4021     }
4022   }
4023 }
4024 
4025 
handle_mvmt()4026 void handle_mvmt() {
4027   for (i=0;i<THINGMAX;i++) {
4028     if (things[i].type != NOTYPE && selection[i]) {
4029       if (key1 == LEFT) things[i].vx = (key3==SHIFT?-1:-8);
4030       else if (key1 == RIGHT) things[i].vx = (key3==SHIFT?1:8);
4031       if (key2 == UP) things[i].vy = (key3==SHIFT?-1:-8);
4032       else if (key2 == DOWN) things[i].vy = (key3==SHIFT?1:8);
4033     }
4034   }
4035 }
4036 
4037 
update_timer()4038 void update_timer() {
4039   if (spframes > 0 || spseconds > 0 || spminutes > 0) {
4040     spframes--;
4041     if (spframes < 0) {spseconds--; spframes=59;}
4042     if (spseconds < 0) {spminutes--; spseconds=59;}
4043   }
4044 }
4045 
4046 
update_fade()4047 void update_fade() {
4048   if (fadetime > 0) {
4049     draw_fade();
4050     fadetime--;
4051     if (fadetime == 0) resolve_fade();
4052   }
4053 }
4054 
4055 
reincarnate_beret(int bdir,int bspeed)4056 void reincarnate_beret(int bdir, int bspeed) {
4057   for (i = 0; i < THINGMAX; i++) {
4058     if (things[i].type == REINCARNATOR && things[i].subtype == 1) {
4059       make_beret(&beret, BERET, istophat, things[i].x+5,
4060                  things[i].y+66, 1, things);
4061       beret.dir = bdir;
4062       beret.speed = bspeed;
4063       play_sound(SND_REGEN);
4064       make_expl(beret.x+beret.width/2,beret.y+beret.height/2,
4065                 0,0,ORANGE,5,125);
4066       make_expl(beret.x+beret.width/2,beret.y+beret.height/2,
4067                 0,0,WHITE,5,125);
4068       center_camera();
4069       deathtime = 0;
4070       break;
4071     }
4072   }
4073 }
4074 
4075 
main(int argc,char * argv[])4076 int main(int argc, char* argv[]) {
4077 
4078   // Initalize and load necessary files
4079   if (!init()) {printf("Initialization error\n"); return 1;}
4080   if (!load_files()) {printf("File load error\n"); return 1;}
4081 
4082   game_init();
4083 
4084   Uint32 curTime, nextTime = SDL_GetTicks();
4085 
4086   // Enter main game loop
4087   while (!quit) {
4088     curTime = SDL_GetTicks();
4089     if (curTime >= nextTime) {
4090       nextTime = SDL_GetTicks() + (1000 / FPS_LIMIT);
4091 
4092       // Fixes mouse-in-corner bug - no it doesn't, silly
4093       /* if (SDL_GetAppState() & SDL_APPMOUSEFOCUS) { */
4094       /* 	if (mouse_visible) { */
4095       /* 	  SDL_ShowCursor(SDL_DISABLE); */
4096       /* 	  mouse_visible = 0; */
4097       /* 	} */
4098       /* } else { */
4099       /* 	if (!mouse_visible) { */
4100       /* 	  SDL_ShowCursor(SDL_ENABLE); */
4101       /* 	  mouse_visible = 1; */
4102       /* 	} */
4103       /* } */
4104 
4105       // Check events
4106       while (SDL_PollEvent(&event)) {
4107         if (event.type == SDL_QUIT) {
4108           creatormode = 0;
4109           clean_up();
4110           return 0;
4111         } else if (event.type == SDL_ACTIVEEVENT) {
4112             inactive = !event.active.gain;
4113         } else if (event.type == SDL_KEYDOWN) {
4114           handle_key_down(event.key.keysym.sym);
4115         } else if (event.type == SDL_KEYUP) {
4116           handle_key_up(event.key.keysym.sym);
4117         } else if (event.type == SDL_MOUSEMOTION) {
4118           mx = event.motion.x+camx;
4119           my = event.motion.y+camy;
4120 	  // Adjust the mouse position near the edge of the screen to make smashing enemies easier.
4121 	  if (event.motion.x <= 0) mx -= SPR_SIZE * 2;
4122 	  else if (event.motion.x >= SCR_WIDTH - 1) mx += SPR_SIZE * 2;
4123 	  if (event.motion.y <= 0) my -= SPR_SIZE * 2;
4124 	  else if (event.motion.y >= SCR_HEIGHT - 1) my += SPR_SIZE * 2;
4125 	  // Handle option choices
4126 	  if (getinput) {
4127 	    if (inputlength == -2) {
4128 	      yesno = (event.motion.x > SCR_WIDTH / 2);
4129 	    } else if (inputlength == -3 && getinput != 15) {
4130 	      if (optmax == 4) optselect = (event.motion.y - (SCR_HEIGHT/2-getinputback->h/2+10)) / 30;
4131 	      else optselect = (event.motion.y - (SCR_HEIGHT/2-getinputback->h/2+15)) / 40;
4132 	      if (optselect < 0) optselect = 0;
4133 	      else if (optselect >= optmax) optselect = optmax - 1;
4134 	    }
4135 	  }
4136         } else if (event.type == SDL_MOUSEBUTTONDOWN) {
4137           handle_mouse_down(event.button.x, event.button.y,
4138                             event.button.button);
4139         } else if (event.type == SDL_MOUSEBUTTONUP) {
4140           handle_mouse_up(event.button.x, event.button.y,
4141                           event.button.button);
4142         }
4143       }
4144 
4145       if (ingame == 4 || ingame == 5 || (ingame >= 10 && ingame <= 12)) {
4146         telestat = 2;
4147         if (ingame == 4) draw_controls();
4148         else if (ingame == 5) draw_story();
4149 	else draw_creat(ingame - 10);
4150         if (!inactive) draw_cursor();
4151         if (SDL_Flip(screen) == -1) return 1;
4152         continue;
4153       }
4154 
4155       if (ingame == 13) {
4156 	credittime++;
4157 	draw_credits();
4158 	if (credittime >= 1950) init_fade(4);
4159         if (!inactive) draw_cursor();
4160         update_fade();
4161         if (SDL_Flip(screen) == -1) return 1;
4162 	continue;
4163       }
4164 
4165       if (ingame == 0 || ingame == 1) {
4166         telestat = 2;
4167         for (i=0; i < THINGMAX; i++) {
4168           if (things[i].type != NOTYPE)
4169             update_thing(&things[i], tiles, things, &beret,
4170                          lvlWidth, lvlHeight, i, &gravdir,
4171                          &switchflags, telething > -1);
4172 	}
4173         draw_bkg(0);
4174         draw_pit();
4175         draw_tiles(0);
4176         draw_sprites(-1);
4177         draw_sprites(0);
4178         draw_sprites(1);
4179         draw_tiles(1);
4180         draw_title_screen();
4181         if (ingame == 1) draw_select_game();
4182         if (getinput) draw_get_input();
4183         if (!inactive) draw_cursor();
4184         update_fade();
4185         if (SDL_Flip(screen) == -1) return 1;
4186         continue;
4187       }
4188 
4189       if (ingame == 2) {
4190         telestat = 2;
4191         draw_map();
4192         draw_map_status();
4193         if (!getinput && !fadetime) check_map_select();
4194         if (getinput) draw_get_input();
4195         if (!inactive) draw_cursor();
4196         update_fade();
4197         if (SDL_Flip(screen) == -1) return 1;
4198         continue;
4199       }
4200 
4201       if (!crtinventory) handle_tele_check();
4202 
4203       //      int curbx = beret.x, curby = beret.y;
4204 
4205       if (!paused && !creatormode && !getinput) {
4206         beret.timer = -1;
4207         if (gotallfrags > -1) gotallfrags = 1;
4208         update_timer();
4209         update_particles(particles, gravdir);
4210         for (i=0; i < THINGMAX; i++) {
4211           if (things[i].type != NOTYPE && !things[i].infront)
4212             update_thing(&things[i], tiles, things, &beret,
4213                          lvlWidth, lvlHeight, i, &gravdir,
4214                          &switchflags, telething > -1);
4215           if (things[i].type == MEDALFRAGMENT && things[i].subtype < 2 && gotallfrags > -1)
4216             gotallfrags = 0;
4217         }
4218         if (!beret.dead) handle_key_input(key1, key2, key3, &beret,
4219 					  tiles, things, lvlWidth,
4220 					  lvlHeight, &gravdir,
4221 					  &switchflags, walkaway,
4222 					  runningmode);
4223         for (i=0; i < THINGMAX; i++) {
4224           if (things[i].type != NOTYPE && things[i].infront)
4225             update_thing(&things[i], tiles, things, &beret,
4226                          lvlWidth, lvlHeight, i, &gravdir,
4227                          &switchflags, telething > -1);
4228         }
4229         for (i=0; i < THINGMAX; i++) {
4230           if (things[i].type != NOTYPE && things[i].dead)
4231             things[i].type = NOTYPE;
4232         }
4233         if (beret.dead) {
4234           if (telething > -1) stop_telekinesis();
4235           if (deathtime == DEATHDELAY - 5 && switchflags & REINCARNATE)
4236             reincarnate_beret(beret.dir, beret.speed);
4237           else if (deathtime < DEATHDELAY) deathtime++;
4238           else if (deathstateload && hassavestate) load_state(0);
4239 	  else if (!fadetime) init_fade(6);
4240         }
4241         if (beret.ontele == 1) {
4242           things[telething].teledelay = TELEDELAY;
4243           stop_telekinesis();
4244         }
4245         if (beret.timer > -1 && !beret.jump) use_door();
4246         start_telekinesis();
4247         if (!beret.dead) do_telekinesis();
4248       } else if (creatormode && !getinput) {
4249         if (!crtinventory) {
4250           start_telekinesis();
4251           do_telekinesis();
4252           handle_mvmt();
4253           for (i=0; i < THINGMAX; i++) {
4254             if (things[i].type != NOTYPE)
4255               update_thing(&things[i], tiles, things, &beret,
4256                            lvlWidth, lvlHeight, i, &gravdir,
4257                            &switchflags, telething > -1);
4258           }
4259           if (crtgridsnap && crtgridsize) handle_grid_snap();
4260           handle_start_pos();
4261         } else {
4262           telestat = 2;
4263         }
4264       }
4265 
4266       // Update camera
4267       if (!creatormode) {
4268         int curcamx = camx, curcamy = camy;
4269 	if (freecam) {
4270 	  if (!paused) {
4271 	    if (camxkey == LEFT) camx -= CAMSCROLL;
4272 	    if (camxkey == RIGHT) camx += CAMSCROLL;
4273 	    if (camykey == UP) camy -= CAMSCROLL;
4274 	    if (camykey == DOWN) camy += CAMSCROLL;
4275 	  }
4276 	} else {
4277 	  int midx = (telesource == -1 ? beret.x+beret.width/2 :
4278 		      things[telesource].x+things[telesource].width/2);
4279 	  int midy = (telesource == -1 ? beret.y+beret.height/2 :
4280 		      things[telesource].y+things[telesource].height/2);
4281 	  int bvy = abs(beret.vy);
4282 	  if (midx < camx + 2*SCR_WIDTH/5)
4283 	    camx = approach(camx, midx-2*SCR_WIDTH/5, MAX(CAMSCROLL*2,bvy));
4284 	  if (midx > camx + 3*SCR_WIDTH/5)
4285 	    camx = approach(camx, midx-3*SCR_WIDTH/5, MAX(CAMSCROLL*2,bvy));
4286 	  if (midy < camy + 2*SCR_HEIGHT/5)
4287 	    camy = approach(camy, midy-2*SCR_HEIGHT/5, MAX(CAMSCROLL*2,bvy));
4288 	  if (midy > camy + 3*SCR_HEIGHT/5)
4289 	    camy = approach(camy, midy-3*SCR_HEIGHT/5, MAX(CAMSCROLL*2,bvy));
4290 	}
4291 
4292         fix_camera();
4293 /*         if (!inactive &&  */
4294 /*             ((!mousecammode && (curcamx != camx || curcamy != camy)) ||  */
4295 /*             (mousecammode && (curbx != (int)beret.x || curby != (int)beret.y)))) { */
4296         if (!inactive && (curcamx != camx || curcamy != camy)) {
4297 /*           if (mousecammode) { */
4298 /*             mx -= curbx - (int)beret.x; */
4299 /*             my -= curby - (int)beret.y; */
4300 /*             SDL_WarpMouse(mx-camx,my-camy); */
4301 /*           } else { */
4302             if (!mousecammode) {
4303               int tox = mx-camx, toy = my-camy;
4304               if (tox < 0) tox = 0;
4305               if (toy < 0) toy = 0;
4306               if (tox > SCR_WIDTH-1) tox = SCR_WIDTH-1;
4307               if (toy > SCR_HEIGHT-1) toy = SCR_HEIGHT-1;
4308               SDL_WarpMouse(tox,toy);
4309             } else {
4310               mx -= curcamx - camx;
4311               my -= curcamy - camy;
4312             }
4313 /*           } */
4314         }
4315       } else if (!crtinventory && !getinput) {
4316         int cammove;
4317         if (mx < camx+CAMSENSE) {
4318           cammove = (CAMSENSE-(mx-camx))/2;
4319           camx -= cammove; mx -= cammove;
4320         }
4321         if (mx > camx+SCR_WIDTH-CAMSENSE) {
4322           cammove = (CAMSENSE-(camx+SCR_WIDTH-mx))/2;
4323           camx += cammove; mx += cammove;
4324         }
4325         if (my < camy+CAMSENSE) {
4326           cammove = (CAMSENSE-(my-camy))/2;
4327           camy -= cammove; my -= cammove;
4328         }
4329         if (my > camy+SCR_HEIGHT-CAMSENSE) {
4330           cammove = (CAMSENSE-(camy+SCR_HEIGHT-my))/2;
4331           camy += cammove; my +=cammove;
4332         }
4333         int dmx = camx, dmy = camy;
4334         fix_camera();
4335         dmx = dmx-camx, dmy = dmy-camy;
4336         mx -= dmx;
4337         my -= dmy;
4338       }
4339 
4340       // Draw sprites and background
4341       if (!creatormode || !crtinventory) {
4342         draw_bkg(0);
4343         if (camy+SCR_HEIGHT >= lvlHeight - 30) draw_pit();
4344         draw_tiles(0);
4345         draw_sprites(-1);
4346         draw_sprites(0);
4347         if (!beret.dead || creatormode) draw_beret();
4348         draw_sprites(1);
4349         draw_tiles(1);
4350       } else {
4351         draw_bkg(1);
4352         draw_inventory();
4353       }
4354 
4355       if (paused) draw_paused();
4356 
4357       // Draw the mouse cursor and guides
4358       if (!creatormode && guides) draw_guides();
4359       if (!creatormode && statusbar && lvlCode != LAST_LEVEL)
4360 	draw_status_bar();
4361       if (!creatormode && svstcount != 0) draw_svst_msg();
4362       if (creatormode && !crtinventory && !getinput)
4363         draw_creator_guides();
4364       if (!creatormode) draw_particles();
4365 
4366       if (getinput) draw_get_input();
4367       if (!inactive) draw_cursor();
4368 
4369       update_fade();
4370 
4371       // Draw the screen
4372       if (SDL_Flip(screen) == -1) return 1;
4373 
4374       // update the misc counter
4375       count++;
4376     } else {
4377       SDL_Delay(nextTime - curTime);
4378     }
4379   }
4380 
4381   clean_up();
4382   return 0;
4383 }
4384 
4385 
4386 // takes necessary action after fading has finished
resolve_fade()4387 void resolve_fade() {
4388   int temp, tempcx, tempcy;
4389 
4390   switch (fadereason) {
4391   case 1 :
4392     if (ingame >= 0) {
4393       hassaved = 0;
4394       creatormode = 1;
4395       clear_room();
4396     } else {
4397       creatormode = !creatormode;
4398     }
4399     ingame = -1;
4400     switchflags ^= CREATORFLAG;
4401     if (creatormode) init_creator();
4402     else init_play(0,1);
4403     break;
4404   case 2 :
4405     ingame = 2;
4406     creatormode = 0;
4407     switchflags &= ~CREATORFLAG;
4408     start_map();
4409     if (!initmapmsg) {
4410       init_sign(0);
4411       initmapmsg = 1;
4412     }
4413     break;
4414   case 3 :
4415     lvlCode = mapCode*20+mapselect;
4416     areaCode = 0;
4417     (get_bossvars())[BOSSMAX-1] = 0;
4418     trgentrance = -1;
4419     loaderror = 0;
4420     read_level();
4421     if (!loaderror) {
4422       ingame = 3;
4423       init_play(0,1);
4424       if (!initgamemsg) {
4425         init_sign(1);
4426         initgamemsg = 1;
4427       }
4428     } else {
4429       load_map(mapCode);
4430     }
4431     break;
4432   case 4:
4433     game_init();
4434     break;
4435   case 5 :
4436     temp = areaCode;
4437     areaCode = tempAreaCode;
4438     tempAreaCode = temp;
4439     tempcx = camx;
4440     tempcy = camy;
4441     read_level();
4442     camx = tempcx;
4443     camy = tempcy;
4444     init_play(1,1);
4445     break;
4446   case 6 :
4447     load_backups();
4448     break;
4449   case 7 :
4450     loaderror = 0;
4451     load_room_return();
4452     break;
4453   case 8 :
4454     mapCode = trgmap;
4455     start_map();
4456     break;
4457   case 9 :
4458     credittime = 0;
4459     ingame = 13;
4460     switch_music(13, 0);
4461     break;
4462   }
4463   if (fadereason > 0) init_fade(0);
4464   else walkaway = 0;
4465 }
4466 
resolve_input(SDLKey key)4467 void resolve_input(SDLKey key) {
4468   int temp = getinput;
4469   getinput = 0;
4470   if ((key == SDLK_RETURN || key == SDLK_SPACE || !key) &&
4471       inputlength == -2 && !yesno && temp == -15)
4472     init_fade(2);
4473   if ((key == SDLK_RETURN || key == SDLK_SPACE || !key) &&
4474       (inputstr[0] != '-' || (inputlength == -2 && yesno) || inputlength == -3)) {
4475     switch (temp) {
4476     case -13 :
4477       crtmessage = atoi(inputstr);
4478       if (crtmessage >= MSGMAX) crtmessage = 0;
4479       break;
4480     case -15 :
4481       init_fade(7);
4482       break;
4483     case -20 :
4484       areaCode = -1;
4485       clear_room();
4486       break;
4487     case -21 :
4488       write_level();
4489       break;
4490     case -1 :
4491       crtexitroom = atoi(inputstr);
4492       if (crtexitroom >= ROOMMAX) crtexitroom = 0;
4493       break;
4494     case -2 :
4495       lvlCode = atoi(inputstr) + 79;
4496       if (lvlCode >= 80 && lvlCode <= 89)
4497 	set_up_input("Save room number:",-4,2,areaCode);
4498       else set_up_input("Must be 1 - 10",1,-1,-1);
4499       break;
4500     case -3 :
4501       lvlCode = atoi(inputstr) + 79;
4502       if (lvlCode >= 80 && lvlCode <= 89)
4503 	set_up_input("Load room number:",-5,2,-1);
4504       else set_up_input("Must be 1 - 10",1,-1,-1);
4505       break;
4506     case -4 :
4507       areaCode = atoi(inputstr);
4508       if (check_level_exists()) {
4509 	set_up_input("Overwrite existing room?",-21,-2,-1);
4510 	yesno = 0;
4511 	break;
4512       }
4513       write_level();
4514       break;
4515     case -5 :
4516       areaCode = atoi(inputstr);
4517       read_level();
4518       break;
4519     case -6 :
4520       save_map(atoi(inputstr));
4521       break;
4522     case -7 :
4523       load_map(atoi(inputstr));
4524       break;
4525     case -8 :
4526       spminutes = atoi(inputstr);
4527       set_up_input("Set timer seconds:", -9, 2, -1);
4528       break;
4529     case -9 :
4530       spseconds = atoi(inputstr);
4531       if (spseconds >= 60) spseconds = 0;
4532       write_metalevel();
4533       break;
4534     case -10 :
4535       delete_game();
4536       break;
4537     case -11 :
4538       init_fade(4);
4539       break;
4540     case 13 :
4541     case 14 :
4542     case 17 :
4543       // Go back to map screen, title, or creator.
4544       if (optselect == 1) init_fade(temp == 13 ? 4 : (temp == 17 ? 1 : 2));
4545       else if (optselect == 2) init_opt_select();
4546       else if (optselect == 3) {
4547 	if (ingame == 2) switch_background(2);
4548 	ingamereturn = ingame;
4549 	ingame = 4;
4550       }
4551       break;
4552     case 16 :
4553       if (optselect == 1) init_fade(1);
4554       else if (optselect == 2) init_fade(2);
4555       break;
4556     case -12 :
4557       init_fade(2);
4558       break;
4559     case -14 :
4560       if (enemdeadhere) enemdead[areaCode] = 1;
4561       beatlevel[lvlCode] = 1;
4562       if (lvlCode < LAST_LEVEL) {
4563 	// Check which medals the player collected.
4564 	if (gotmwht && !haswmed) {gotmedals[lvlCode*6] = 1; gamemedals++;}
4565 	if (!hasomed && (gotmcrn[0] || game_gotcorners[lvlCode][0]) && (gotmcrn[1] || game_gotcorners[lvlCode][1]) &&
4566 	    (gotmcrn[2] || game_gotcorners[lvlCode][2]) && (gotmcrn[3] || game_gotcorners[lvlCode][3])) {
4567 	  gotmedals[lvlCode*6+1] = 1; gamemedals++;
4568 	}
4569 	if (!hasbmed && mfragcount == MFRAGTOTAL) {gotmedals[lvlCode*6+2] = 1; gamemedals++;}
4570 	if (!hasrmed && enemalldead) {gotmedals[lvlCode*6+3] = 1; gamemedals++;}
4571 	if (!hasgmed && gotgmed) {gotmedals[lvlCode*6+4] = 1; gamemedals++;}
4572 	if (!haspmed && gotpmed) {gotmedals[lvlCode*6+5] = 1; gamemedals++;}
4573 	// Check which pieces of medals the player collected.
4574 	for (i = 0; i < 4; i++) {
4575 	  if (gotmcrn[i]) game_gotcorners[lvlCode][i] = 1;
4576 	}
4577 	for (i = 0; i < MFRAGTOTAL; i++) {
4578 	  if (gotmfrag[i]) game_gotfrags[lvlCode][i] = 1;
4579 	}
4580 	for (i = 0; i < ROOMMAX; i++) {
4581 	  if (enemdead[i]) game_enemdead[lvlCode][i] = 1;
4582 	}
4583       }
4584       save_game_data();
4585       remove_room_return();
4586       if (lvlCode != LAST_LEVEL) {
4587 	init_fade(2);
4588       } else {
4589 	init_fade(9);
4590       }
4591       break;
4592     case 2 :
4593       start_new_game();
4594       init_fade(2);
4595       break;
4596     }
4597   }
4598 }
4599