Develop and Download Open Source Software

Browse Subversion Repository

Contents of /users/dbug/UpgradeTime/Encounter/FloppyBuilderVersion/code/game_main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1716 - (show annotations) (download) (as text)
Sun Nov 26 13:22:24 2023 UTC (5 months, 2 weeks ago) by dbug
File MIME type: text/x-csrc
File size: 30554 byte(s)
Started migrating the bytestream commands into separate functions to implement them more efficiently in a separate assembler module (bytestream.s).
Added a COMMAND_STOP_BREAKPOINT command to make debugging easier
Moved the stream pointer in zero page to facilitate the assembler implementation.

Free memory: 2914 bytes 
1 //
2 // EncounterHD - Main Game
3 // (c) 2020-2023 Dbug / Defence Force
4 //
5 #include <lib.h>
6
7 #include "common.h"
8
9 #include "game_defines.h"
10
11 char gAskQuestion;
12 char gInputBuffer[40];
13 char gInputBufferPos;
14
15 char gWordCount; // How many tokens/word did we find in the input buffer
16 char gWordBuffer[10]; // One byte identifier of each of the identified words
17 char gWordPosBuffer[10]; // Actual offset in the original input buffer, can be used to print the unrecognized words
18
19 char gTextBuffer[80]; // Temp
20
21 int gScore;
22
23
24 typedef WORDS (*AnswerProcessingFun)();
25
26 extern WORDS AskInput(const char* inputMessage,AnswerProcessingFun callback, char checkTockens);
27
28 void ResetInput()
29 {
30 gAskQuestion = 1;
31 gInputBufferPos=0;
32 gInputBuffer[gInputBufferPos]=0;
33 }
34
35
36 // At the end of the parsing, each of the words is terminated by a zero so it can be printed individually
37 unsigned char ParseInputBuffer()
38 {
39 unsigned char wordId;
40 char car;
41 char done;
42 keyword* keywordPtr;
43 char* separatorPtr;
44 char* inputPtr = gInputBuffer;
45
46 memset(gWordBuffer,e_WORD_COUNT_,sizeof(gWordBuffer));
47 memset(gWordPosBuffer,0,sizeof(gWordPosBuffer));
48
49 gWordCount=0;
50 done = 0;
51
52 // While we have not reached the null terminator
53 while ((!done) && (car=*inputPtr))
54 {
55 if (car==' ')
56 {
57 // We automagically filter out the spaces
58 inputPtr++;
59 }
60 else
61 {
62 // This is not a space, so we assume it is the start of a word
63 gWordPosBuffer[gWordCount] = inputPtr-gInputBuffer;
64
65 // Search the end
66 separatorPtr=inputPtr;
67 while (*separatorPtr && (*separatorPtr!=' '))
68 {
69 separatorPtr++;
70 }
71 if (*separatorPtr == 0)
72 {
73 done = 1;
74 }
75 else
76 {
77 *separatorPtr=0;
78 }
79
80 // Now that we have identified the begining and end of the word, check if it's in our vocabulary list
81 gWordBuffer[gWordCount]=e_WORD_COUNT_;
82 keywordPtr = gWordsArray;
83 while (keywordPtr->word) // The list is terminated by a null pointer entry
84 {
85 // Right now we do a full comparison of the words, but technically we could restrict to only a few significant characters.
86 if (strcmp(inputPtr,keywordPtr->word)==0)
87 {
88 // Found the word in the list, we mark down the token id and continue searching
89 gWordBuffer[gWordCount] = keywordPtr->id;
90 gWordCount++;
91 break;
92 }
93 ++keywordPtr;
94 }
95 inputPtr = separatorPtr+1;
96 }
97 }
98
99 return gWordCount;
100 }
101
102
103
104 void PrintStatusMessage(char color,const char* message)
105 {
106 char* ptrScreen=(char*)0xbb80+40*22;
107 memset(ptrScreen+1,32,39);
108 sprintf(ptrScreen+1,"%c%s",color,message);
109 }
110
111
112 void PrintErrorMessage(const char* message)
113 {
114 PrintStatusMessage(1,message);
115 PlaySound(PingData);
116 WaitFrames(75);
117 }
118
119
120 void PrintInformationMessage(const char* message)
121 {
122 PrintStatusMessage(3,message);
123 //PlaySound(PingData);
124 WaitFrames(75);
125 }
126
127
128 void PrintSceneDirections()
129 {
130 unsigned char* directions = gCurrentLocationPtr->directions;
131 int direction;
132
133 gFlagDirections = 0;
134 for (direction=0;direction<e_DIRECTION_COUNT_;direction++)
135 {
136 if (directions[direction]!=e_LOCATION_NONE)
137 {
138 gFlagDirections|= (1<<direction);
139 }
140 }
141 }
142
143
144 // Very basic version of the inventory, does not check anything,
145 // displays a maximum of 7 items before it starts overwriting the clock
146 void PrintInventory()
147 {
148 int itemId;
149 int inventoryCell =0;
150 item* itemPtr = gItems;
151 memset((char*)0xbb80+40*24,32,40*4-8); // 8 characters at the end of the inventory for the clock
152 for (itemId=0;itemId<e_ITEM_COUNT_;itemId++)
153 {
154 if (itemPtr->location == e_LOCATION_INVENTORY)
155 {
156 int descriptionLength = strlen(itemPtr->description);
157 char* screenPtr = (char*)0xbb80+40*(24+inventoryCell/2)+(inventoryCell&1)*20;
158 memcpy(screenPtr,itemPtr->description , descriptionLength);
159 #if 0
160 // Ideally it would be nice to be able to display things like
161 // - An empty plastic bag
162 // - A tin box full of dust
163 // - A bucket filled with petrol
164 //
165 // This is technically not very difficult to do, but we only have 3.5 lines of 40 characters
166 // So probably should wait a bit until the whole UI layout is 100% fixed
167 if (itemPtr->flags & ITEM_FLAG_IS_CONTAINER)
168 {
169 screenPtr += descriptionLength;
170 if (itemPtr->associated_item == 255)
171 {
172 // Empty container
173 memcpy(screenPtr," (empty)",7);
174 }
175 else
176 {
177 // Contains something
178 memcpy(screenPtr," ->",2);
179 }
180 }
181 #endif
182
183 inventoryCell++;
184 }
185 ++itemPtr;
186 }
187 }
188
189 void PrintSceneObjects()
190 {
191 int itemCount = 0;
192 int item;
193
194 for (item=0;item<e_ITEM_COUNT_;item++)
195 {
196 if (gItems[item].location == gCurrentLocation)
197 {
198 itemCount++;
199 }
200 }
201
202 // Print any item in the location
203 if (itemCount)
204 {
205 const char* ptrMessage=gTextCanSee;
206 char* ptrScreen=(char*)0xbb80+40*18;
207 for (item=0;item<e_ITEM_COUNT_;item++)
208 {
209 if (gItems[item].location == gCurrentLocation)
210 {
211 sprintf(ptrScreen+1,"%c%s%s",3,ptrMessage,gItems[item].description); // "I can see"
212 ptrMessage="";
213 ptrScreen+=40;
214 }
215 }
216 }
217 else
218 {
219 sprintf((char*)0xbb80+40*18+1,"%c%s",3,gTextNothingHere); // "There is nothing of interest here"
220 }
221 }
222
223
224 WORDS ProcessPlayerNameAnswer()
225 {
226 // We accept anything, it's the player name so...
227 return e_WORD_QUIT;
228 }
229
230 void HandleHighScore()
231 {
232 int entry;
233 int score;
234 score_entry* ptrScore=gHighScores;
235
236 // Load the highscores from the disk
237 LoadFileAt(LOADER_HIGH_SCORES,gHighScores);
238
239 for (entry=0;entry<SCORE_COUNT;entry++)
240 {
241 // Check if our score is higher than the next one in the list
242 score=ptrScore->score-32768;
243 if (gScore>score)
244 {
245 // First, we scroll down the rest, but since we don't have "memmov" we need to manually start from the end
246 // else the result will be corrupted because of overwrites.
247 score_entry* ptrScoreCopy = gHighScores+SCORE_COUNT-1;
248 while (ptrScoreCopy>ptrScore)
249 {
250 --ptrScoreCopy;
251 ptrScoreCopy[1] = ptrScoreCopy[0];
252 }
253
254 // Ask the player their name
255 AskInput(gTextHighScoreAskForName,ProcessPlayerNameAnswer, 0); // "New highscore! Your name please?"
256 ptrScore->score = gScore+32768;
257 ptrScore->condition = e_SCORE_GAVE_UP; // Need to get that from the game
258 memset(ptrScore->name,' ',15); // Fill the entry with spaces
259 if (gInputBufferPos>15)
260 {
261 // Just copy the first 16 characters if it's too long
262 gInputBufferPos=15;
263 }
264 // Force the name to the right to be formatted like the rest of the default scores
265 memcpy(ptrScore->name+15-gInputBufferPos,gInputBuffer,gInputBufferPos);
266
267 // Save back the highscores in the slot
268 SaveFileAt(LOADER_HIGH_SCORES,gHighScores);
269 return;
270 }
271 ++ptrScore;
272 }
273 }
274
275 void PrintTopDescription(const char* message)
276 {
277 int messageLength = messageLength=strlen(message);
278 memset((char*)0xbb80+17*40+1,' ',39);
279 strcpy((char*)0xbb80+17*40+20-messageLength/2,message);
280 }
281
282 void PrintSceneInformation()
283 {
284 // Print the description of the place at the top (centered)
285 PrintTopDescription(gCurrentLocationPtr->description);
286
287 // The redefined charcters to draw the bottom part of the directional arrows \v/
288 poke(0xbb80+16*40+16,9); // ALT charset
289 memcpy((char*)0xbb80+16*40+17,";<=>?@",6);
290
291 // Display the score
292 sprintf((char*)0xbb80+16*40+1,"%c%s%d%c",4,gTextScore,gScore,7); // "Score:"
293
294 PrintSceneDirections();
295
296 PrintSceneObjects();
297
298 PrintInventory();
299 }
300
301
302 void LoadScene()
303 {
304 gCurrentLocationPtr = &gLocations[gCurrentLocation];
305
306 ClearMessageWindow(16+4);
307
308 #if 1
309 LoadFileAt(LOADER_PICTURE_LOCATIONS_START+gCurrentLocation+1,ImageBuffer);
310 #else
311 memset(ImageBuffer,64+1,40*128);
312 #endif
313
314
315
316 #if 0 // Sprite test
317
318 #if 1
319 LoadFileAt(LOADER_SPRITE_DOG,SecondImageBuffer); // The dog is a 240x128 image with all the current dog graphics
320
321 gDrawWidth = 40;
322 gDrawHeight = 128;
323 gSourceStride = 40;
324 gDrawSourceAddress = SecondImageBuffer;
325 gDrawAddress = ImageBuffer;
326 //gDrawAddress = (unsigned char*)0xa000;
327
328 BlitSprite();
329 #else
330 LoadFileAt(LOADER_SPRITE_THE_END,SecondImageBuffer); // The End is a 120x95 image -> 95 lines of 20 bytes
331
332 gDrawWidth = 20;
333 gDrawHeight = 95;
334 gSourceStride = 20;
335 gDrawSourceAddress = SecondImageBuffer;
336 gDrawAddress = ImageBuffer+10+40*16;
337 //gDrawAddress = (unsigned char*)0xa000+10+40*16;
338
339 BlitSprite();
340 #endif
341 #endif
342
343 PrintSceneInformation();
344
345 #if 1
346 // Set the byte stream pointer
347 SetByteStream(gCurrentLocationPtr->script);
348
349 // And run the first set of commands for this scene
350 HandleByteStream();
351 #endif
352
353 BlitBufferToHiresWindow();
354
355 //TrashFreeMemory();
356 }
357
358
359 void PlayerMove(unsigned char direction)
360 {
361 unsigned char requestedScene = gCurrentLocationPtr->directions[direction];
362 if (requestedScene==e_LOCATION_NONE)
363 {
364 PrintErrorMessage(gTextErrorInvalidDirection); // "Impossible to move in that direction"
365 }
366 else
367 {
368 PlaySound(KeyClickHData);
369 gCurrentLocation=requestedScene;
370 LoadScene();
371 }
372 }
373
374
375 WORDS ProcessContainerAnswer()
376 {
377 // Check the first word: We expects a container id
378 switch (gWordBuffer[0])
379 {
380 case e_ITEM_TobaccoTin: // an empty tobacco tin
381 case e_ITEM_Bucket: // a wooden bucket
382 case e_ITEM_CardboardBox: // a cardboard box
383 case e_ITEM_FishingNet: // a fishing net
384 case e_ITEM_PlasticBag: // a plastic bag
385 case e_ITEM_SmallBottle: // a small bottle
386 // We return the name of the object
387 return gWordBuffer[0];
388
389 default:
390 // No idea what that is, but definitely not a container
391 return e_WORD_QUIT;
392 }
393 // Continue
394 return e_WORD_CONTINUE;
395 }
396
397
398 void TakeItem(unsigned char itemId)
399 {
400 if (itemId>=e_ITEM_COUNT_)
401 {
402 PrintErrorMessage(gTextErrorCantTakeNoSee); // "You can only take something you see"
403 }
404 else
405 {
406 // The item is in the scene
407 item* itemPtr=&gItems[itemId];
408 item* itemAliasPtr=itemPtr;
409
410 if (itemPtr->location == e_LOCATION_INVENTORY)
411 {
412 PrintErrorMessage(gTextErrorAlreadyHaveItem); // "You already have this item"
413 return;
414 }
415
416 if (itemPtr->location != gCurrentLocation)
417 {
418 int itemAlias;
419 for (itemAlias=0;itemAlias<e_ITEM_COUNT_;itemAlias++)
420 {
421 itemAliasPtr=&gItems[itemAlias];
422 if ( (itemAliasPtr->location==gCurrentLocation) && (itemAliasPtr->flags & ITEM_FLAG_ALIAS_ITEM) && (itemAliasPtr->associated_item == itemId) )
423 {
424 break;
425 }
426 }
427 if (itemAlias == e_ITEM_COUNT_)
428 {
429 PrintErrorMessage(gTextErrorCantTakeNoSee); // "I don't see this item here");
430 return;
431 }
432 }
433
434
435 if (itemPtr->flags & ITEM_FLAG_HEAVY)
436 {
437 PrintErrorMessage(gTextErrorTooHeavy); // "This is too heavy");
438 }
439 else
440 if (itemPtr->usable_containers)
441 {
442 // Requires a container
443 WORDS containerId = AskInput(gTextCarryInWhat,ProcessContainerAnswer,1 ); // "Carry it in what?"
444 if (containerId == e_WORD_QUIT)
445 {
446 PrintErrorMessage(gTextErrorRidiculous); // "Don't be ridiculous"
447 }
448 else
449 {
450 item* containerPtr=&gItems[containerId];
451 if (containerPtr->location != e_LOCATION_INVENTORY)
452 {
453 PrintErrorMessage(gTextErrorMissingContainer); // "You don't have this container" - Technically could be optimized at a later stage to automatically pick the item if it's in the scene
454 }
455 else
456 if (containerPtr->associated_item != 255)
457 {
458 PrintErrorMessage(gTextErrorAlreadyFull); // "Sorry, that's full already"
459 }
460 else
461 {
462 // Looks like we have both the item and an empty container!
463 itemPtr->location = e_LOCATION_INVENTORY;
464 itemPtr->associated_item = containerId;
465
466 containerPtr->associated_item = itemId;
467
468 LoadScene();
469 }
470 }
471 }
472 else
473 {
474 // Can just be picked-up
475 itemAliasPtr->location = e_LOCATION_NONE; // Can actually be the same pointer, literaly aliasing!
476 itemPtr->location = e_LOCATION_INVENTORY; // Which means that one should be done after
477 LoadScene();
478 }
479 }
480 }
481
482
483 void DropItem(unsigned char itemId)
484 {
485 if ( (itemId>e_ITEM_COUNT_) || (gItems[itemId].location!=e_LOCATION_INVENTORY) )
486 {
487 PrintErrorMessage(gTextErrorDropNotHave); // "You can only drop something you have"
488 }
489 else
490 {
491 item* itemPtr=&gItems[itemId];
492 if (itemPtr->flags & ITEM_FLAG_IS_CONTAINER)
493 {
494 // When the item is a container we need to drop both the container and the content
495 item* containerPtr=itemPtr;
496 itemId = containerPtr->associated_item;
497 itemPtr = &gItems[itemId];
498
499 // Put the container on the ground
500 containerPtr->location = gCurrentLocation;
501 }
502
503 if (itemPtr->associated_item != 255)
504 {
505 // Remove the item from the container
506 item* containerPtr=&gItems[itemPtr->associated_item];
507 containerPtr->associated_item = 255;
508 itemPtr->associated_item = 255;
509 }
510
511 if (itemPtr->flags & ITEM_FLAG_EVAPORATES)
512 {
513 // Special items like water of petrol go back to where they were or disappear
514 if (itemId == e_ITEM_Water)
515 {
516 itemPtr->location = e_LOCATION_WELL;
517 PrintInformationMessage(gTextWaterDrainsAways); // "The water drains away"
518 }
519 else
520 {
521 itemPtr->location = 99;
522 PrintInformationMessage(gTextPetrolEvaporates); // "The petrol evaporates"
523 }
524 }
525 else
526 {
527 // Normal items stay on the same location
528 itemPtr->location = gCurrentLocation;
529 }
530 LoadScene();
531 }
532 }
533
534 char ItemCheck(unsigned char itemId)
535 {
536 if (itemId<e_ITEM_COUNT_)
537 {
538 if ( (gItems[itemId].location==e_LOCATION_INVENTORY) || (gItems[itemId].location==gCurrentLocation) )
539 {
540 return 1;
541 }
542 else
543 {
544 PrintErrorMessage(gTextErrorItemNotPresent); // "This item does not seem to be present"
545 }
546 }
547 else
548 {
549 PrintErrorMessage(gTextErrorUnknownItem); // "I do not know what this item is"
550 }
551 return 0; // Cannot use
552 }
553
554 void ReadItem(unsigned char itemId)
555 {
556 if (ItemCheck(itemId))
557 {
558 switch (itemId)
559 {
560 case e_ITEM_Newspaper:
561 PlayStream(gSceneActionReadNewsPaper);
562 LoadScene();
563 break;
564
565 case e_ITEM_HandWrittenNote:
566 PlayStream(gSceneActionReadHandWrittenNote);
567 LoadScene();
568 break;
569
570 case e_ITEM_ChemistryBook:
571 PlayStream(gSceneActionReadChemistryBook);
572 if (gItems[e_ITEM_ChemistryRecipes].location==e_LOCATION_NONE)
573 {
574 // If the recipes were not yet found, they now appear at the current location
575 gItems[e_ITEM_ChemistryRecipes].location = gCurrentLocation;
576 }
577 LoadScene();
578 break;
579
580 case e_ITEM_ChemistryRecipes:
581 PlayStream(gSceneActionReadChemistryRecipes);
582 LoadScene();
583 break;
584
585 case e_ITEM_PlasticBag:
586 PrintErrorMessage(gTextAGenericWhiteBag); // "It's just a white generic bag"
587 break;
588
589 default:
590 PrintErrorMessage(gTextErrorCannotRead); // "I can't read that"
591 break;
592 }
593 }
594 }
595
596
597 void ActionClimbLadder()
598 {
599 char ladderLocation = gItems[e_ITEM_Ladder].location;
600 if (gCurrentLocation == e_LOCATION_INSIDEHOLE)
601 {
602 gItems[e_ITEM_Ladder].location = e_LOCATION_INSIDEHOLE; // The ladder stays inside the hole
603 gItems[e_ITEM_LadderInTheHole].location = e_LOCATION_OUTSIDE_PIT; // The ladder stays inside the hole
604 if (ladderLocation == e_LOCATION_INVENTORY)
605 {
606 PrintInformationMessage(gTextPositionLadder); // "You position the ladder properly"
607 LoadScene();
608 WaitFrames(50*1);
609 }
610 PrintInformationMessage(gTextClimbUpLadder); // "You climb up the ladder"
611 WaitFrames(50*1);
612 gCurrentLocation = e_LOCATION_OUTSIDE_PIT;
613 LoadScene();
614 }
615 else
616 if (gCurrentLocation == e_LOCATION_OUTSIDE_PIT)
617 {
618 gItems[e_ITEM_Ladder].location = e_LOCATION_INSIDEHOLE; // The ladder stays inside the hole
619 gItems[e_ITEM_LadderInTheHole].location = e_LOCATION_OUTSIDE_PIT; // The ladder stays inside the hole
620 if (ladderLocation == e_LOCATION_INVENTORY)
621 {
622 PrintInformationMessage(gTextPositionLadder); // "You position the ladder properly"
623 LoadScene();
624 WaitFrames(50*1);
625 }
626 PrintInformationMessage(gTextClimbDownLadder); // "You climb down the ladder"
627 WaitFrames(50*1);
628 gCurrentLocation = e_LOCATION_INSIDEHOLE;
629 LoadScene();
630 }
631 else
632 {
633 PrintErrorMessage(gTextErrorCannotUseHere); // "I can't use it here"
634 }
635 }
636
637
638 void ActionClimbRope()
639 {
640 char ropeLocation = gItems[e_ITEM_Rope].location;
641 if (gCurrentLocation == e_LOCATION_INSIDEHOLE)
642 {
643 if (gItems[e_ITEM_RopeAttachedToATree].location == e_LOCATION_OUTSIDE_PIT)
644 {
645 gItems[e_ITEM_Rope].location = e_LOCATION_INSIDEHOLE;
646 gItems[e_ITEM_RopeAttachedToATree].location = e_LOCATION_OUTSIDE_PIT;
647 PrintInformationMessage(gTextClimbUpRope); // "You climb up the rope"
648 WaitFrames(50*1);
649 gCurrentLocation = e_LOCATION_OUTSIDE_PIT;
650 LoadScene();
651 }
652 else
653 {
654 PrintErrorMessage(gTextErrorCannotAttachRope); // "You can't attach the rope"
655 }
656 }
657 else
658 if (gCurrentLocation == e_LOCATION_OUTSIDE_PIT)
659 {
660 gItems[e_ITEM_Rope].location = e_LOCATION_INSIDEHOLE;
661 gItems[e_ITEM_RopeAttachedToATree].location = e_LOCATION_OUTSIDE_PIT;
662 if (ropeLocation == e_LOCATION_INVENTORY)
663 {
664 PrintInformationMessage(gTextAttachRopeToTree); // "You attach the rope to the tree"
665 LoadScene();
666 WaitFrames(50*1);
667 }
668 PrintInformationMessage(gTextClimbDownRope); // "You climb down the rope"
669 WaitFrames(50*1);
670 gCurrentLocation = e_LOCATION_INSIDEHOLE;
671 LoadScene();
672 }
673 else
674 {
675 PrintErrorMessage(gTextErrorCannotUseHere); // "I can't use it here"
676 }
677 }
678
679 void UseItem(unsigned char itemId)
680 {
681 if (ItemCheck(itemId))
682 {
683 switch (itemId)
684 {
685 case e_ITEM_Ladder:
686 case e_ITEM_LadderInTheHole:
687 {
688 if ( (gCurrentLocation == e_LOCATION_OUTSIDE_PIT) || (gCurrentLocation == e_LOCATION_INSIDEHOLE) )
689 {
690 char ladderLocation = gItems[e_ITEM_Ladder].location;
691 if (ladderLocation == e_LOCATION_INVENTORY)
692 {
693 PrintInformationMessage(gTextPositionLadder); // "You position the ladder properly"
694 gItems[e_ITEM_Ladder].location = e_LOCATION_INSIDEHOLE; // The ladder stays inside the hole
695 gItems[e_ITEM_LadderInTheHole].location = e_LOCATION_OUTSIDE_PIT; // The ladder stays inside the hole
696 LoadScene();
697 }
698 else
699 if (ladderLocation == e_LOCATION_INSIDEHOLE)
700 {
701 PrintErrorMessage(gTextErrorLadderInHole); // "The ladder is already in the hole"
702 }
703 }
704 else
705 {
706 PrintErrorMessage(gTextErrorCannotUseHere); // "I can't use it here"
707 }
708 }
709 break;
710
711 case e_ITEM_Rope:
712 case e_ITEM_RopeAttachedToATree:
713 //ActionClimbRope();
714 break;
715
716 default:
717 PrintErrorMessage(gTextErrorDontKnowUsage); // "I don't know how to use that"
718 break;
719 }
720 }
721 }
722
723
724 void ClimbItem(unsigned char itemId)
725 {
726 if (ItemCheck(itemId))
727 {
728 switch (itemId)
729 {
730 case e_ITEM_Ladder:
731 case e_ITEM_LadderInTheHole:
732 {
733 if ( (gCurrentLocation == e_LOCATION_OUTSIDE_PIT) || (gCurrentLocation == e_LOCATION_INSIDEHOLE) )
734 {
735 char ladderLocation = gItems[e_ITEM_Ladder].location;
736 if (ladderLocation == e_LOCATION_INVENTORY)
737 {
738 PrintErrorMessage(gTextErrorNeedPositionned); // "It needs to be positionned first"
739 }
740 else
741 if (ladderLocation == e_LOCATION_INSIDEHOLE)
742 {
743 gCurrentLocation = e_LOCATION_OUTSIDE_PIT;
744 LoadScene();
745 }
746 }
747 else
748 {
749 PrintErrorMessage(gTextErrorCannotUseHere); // "I can't use it here"
750 }
751 }
752 //ActionClimbLadder();
753 break;
754
755 case e_ITEM_Rope:
756 case e_ITEM_RopeAttachedToATree:
757 ActionClimbRope();
758 break;
759
760 default:
761 PrintErrorMessage(gTextErrorCantClimbThat); // "I don't know how to climb that"
762 break;
763 }
764 }
765 }
766
767
768 void InspectItem(unsigned char itemId)
769 {
770 if (ItemCheck(itemId))
771 {
772 switch (itemId)
773 {
774 case e_ITEM_UnitedKingdomMap:
775 PlayStream(gSceneActionInspectMap);
776 LoadScene();
777 break;
778
779 case e_ITEM_ChemistryBook:
780 PrintInformationMessage(gTextThickBookBookmarks); // "A thick book with some boomarks"
781 break;
782
783 default:
784 PrintErrorMessage(gTextErrorNothingSpecial); // "Nothing special"
785 break;
786 }
787 }
788 }
789
790
791
792
793
794 WORDS ProcessAnswer()
795 {
796 // Check the first word
797 switch (gWordBuffer[0])
798 {
799 //
800 // Movement
801 //
802 case e_WORD_NORTH:
803 PlayerMove(e_DIRECTION_NORTH);
804 break;
805 case e_WORD_SOUTH:
806 PlayerMove(e_DIRECTION_SOUTH);
807 break;
808 case e_WORD_EAST:
809 PlayerMove(e_DIRECTION_EAST);
810 break;
811 case e_WORD_WEST:
812 PlayerMove(e_DIRECTION_WEST);
813 break;
814 case e_WORD_UP:
815 PlayerMove(e_DIRECTION_UP);
816 break;
817 case e_WORD_DOWN:
818 PlayerMove(e_DIRECTION_DOWN);
819 break;
820
821 //
822 // Action
823 //
824 case e_WORD_TAKE:
825 TakeItem(gWordBuffer[1]);
826 break;
827 case e_WORD_DROP:
828 DropItem(gWordBuffer[1]);
829 break;
830
831 case e_WORD_READ:
832 ReadItem(gWordBuffer[1]);
833 break;
834
835 case e_WORD_USE:
836 UseItem(gWordBuffer[1]);
837 break;
838
839 case e_WORD_CLIMB:
840 ClimbItem(gWordBuffer[1]);
841 break;
842
843 case e_WORD_LOOK:
844 InspectItem(gWordBuffer[1]);
845 break;
846
847 case e_WORD_KILL:
848 {
849 unsigned char itemId=gWordBuffer[1];
850 item* itemPtr=&gItems[itemId];
851 if (itemPtr->location != gCurrentLocation)
852 {
853 PrintErrorMessage(gTextErrorItsNotHere); // "It's not here"
854 }
855 else
856 if (itemPtr->flags & ITEM_FLAG_DEAD)
857 {
858 PrintErrorMessage(gTextErrorAlreadyDead); // "Already dead"
859 }
860 else
861 {
862 // Normally we should ask how, check the weapon, etc...
863 // Right now it's just to check things
864 switch (itemId)
865 {
866 case e_ITEM_Thug:
867 gScore+=50;
868 itemPtr->flags|=ITEM_FLAG_DEAD;
869 itemPtr->description=gTextDeadThug; // "a dead thug";
870 LoadScene();
871 break;
872
873 case e_ITEM_AlsatianDog:
874 gScore+=50;
875 itemPtr->flags|=ITEM_FLAG_DEAD;
876 itemPtr->description=gTextDeadDog; // "a dead dog";
877 LoadScene();
878 break;
879
880 case e_ITEM_YoungGirl:
881 PrintErrorMessage(gTextErrorShouldSaveGirl); // "You are supposed to save her"
882 break;
883 }
884 }
885 }
886 break;
887
888 case e_WORD_FRISK:
889 case e_WORD_SEARCH:
890 {
891 unsigned char itemId=gWordBuffer[1];
892 item* itemPtr=&gItems[itemId];
893 if (itemPtr->location != gCurrentLocation)
894 {
895 PrintErrorMessage(gTextErrorItsNotHere); // "It's not here"
896 }
897 else
898 {
899 // Eventual list of things we can search
900 switch (itemId)
901 {
902 case e_ITEM_Thug:
903 if (!(itemPtr->flags & ITEM_FLAG_DEAD))
904 {
905 PrintErrorMessage(gTextErrorShouldSubdue); // "I should subdue him first"
906 }
907 else
908 if (gItems[e_ITEM_Pistol].location!=e_LOCATION_NONE)
909 {
910 PrintErrorMessage(gTextErrorAlreadySearched); // "You've already frisked him"
911 }
912 else
913 {
914 gScore+=50;
915 gItems[e_ITEM_Pistol].location = e_LOCATION_MASTERBEDROOM;
916 PrintInformationMessage(gTextFoundSomething); // "You found something interesting"
917 LoadScene();
918 }
919 break;
920 }
921 }
922 }
923 break;
924
925 #ifdef ENABLE_CHEATS
926 case e_WORD_REVIVE:
927 {
928 unsigned char itemId=gWordBuffer[1];
929 item* itemPtr=&gItems[itemId];
930 if (itemPtr->location != gCurrentLocation)
931 {
932 PrintErrorMessage(gTextErrorItsNotHere); // "It's not here"
933 }
934 else
935 if (!(itemPtr->flags & ITEM_FLAG_DEAD))
936 {
937 PrintErrorMessage(gTextNotDead); // "Not dead"
938 }
939 else
940 {
941 // We revive the characters to debug and test more easily
942 switch (itemId)
943 {
944 case e_ITEM_Thug:
945 itemPtr->flags&=~ITEM_FLAG_DEAD;
946 itemPtr->description=gTextThugAsleepOnBed; // "a thug asleep on the bed";
947 LoadScene();
948 break;
949
950 case e_ITEM_AlsatianDog:
951 itemPtr->flags&=~ITEM_FLAG_DEAD;
952 itemPtr->description=gTextDogGrowlingAtYou; // "an alsatian growling at you"
953 LoadScene();
954 break;
955 }
956 }
957 }
958 break;
959
960 case e_WORD_TICKLE:
961 {
962 unsigned char itemId=gWordBuffer[1];
963 item* itemPtr=&gItems[itemId];
964 if (itemPtr->location != gCurrentLocation)
965 {
966 PrintErrorMessage(gTextErrorItsNotHere); // "It's not here"
967 }
968 else
969 if (itemPtr->flags & ITEM_FLAG_DEAD)
970 {
971 PrintErrorMessage(gTextErrorDeadDontMove); // "Dead don't move"
972 }
973 else
974 {
975 // The tickling is just a way to trigger the attack sequence
976 switch (itemId)
977 {
978 case e_ITEM_Thug:
979 gCurrentLocationPtr->script = gDescriptionThugAttacking;
980 itemPtr->description=gTextThugShootingAtMe; // "a thug shooting at me"
981 LoadScene();
982 break;
983
984 case e_ITEM_AlsatianDog:
985 gCurrentLocationPtr->script = gDescriptionDogAttacking;
986 itemPtr->description=gTextDogJumpingAtMe; // "a dog jumping at me"
987 LoadScene();
988 break;
989
990 case e_ITEM_YoungGirl:
991 PrintErrorMessage(gTextErrorInappropriate); // "Probably impropriate"
992 break;
993 }
994 }
995 }
996 break;
997 #endif
998
999 //
1000 // Meta
1001 //
1002 case e_WORD_QUIT:
1003 PlaySound(KeyClickHData);
1004 HandleHighScore();
1005 // Quit
1006 return e_WORD_QUIT;
1007 break;
1008
1009 default:
1010 PlaySound(PingData);
1011 break;
1012 }
1013 // Continue
1014 return e_WORD_CONTINUE;
1015 }
1016
1017
1018 WORDS AskInput(const char* inputMessage,AnswerProcessingFun callback, char checkTockens)
1019 {
1020 int k;
1021 int shift=0;
1022
1023 ResetInput();
1024 while (1)
1025 {
1026 if (gAskQuestion)
1027 {
1028 PrintStatusMessage(2,inputMessage);
1029 memset((char*)0xbb80+40*23+1,' ',39);
1030 gAskQuestion=0;
1031 }
1032
1033 do
1034 {
1035 WaitIRQ();
1036 HandleByteStream();
1037 k=ReadKeyNoBounce();
1038 sprintf((char*)0xbb80+40*23+1,"%c>%s%c ",2,gInputBuffer, ((VblCounter&32)||(k==KEY_RETURN))?32:32|128);
1039 }
1040 while (k==0);
1041
1042 if ((KeyBank[4] & 16)) // SHIFT code
1043 {
1044 shift=1;
1045 }
1046
1047 switch (k)
1048 {
1049 case KEY_DEL: // We use DEL as Backspace
1050 if (gInputBufferPos)
1051 {
1052 gInputBufferPos--;
1053 gInputBuffer[gInputBufferPos]=0;
1054 PlaySound(KeyClickHData);
1055 }
1056 break;
1057
1058 case KEY_RETURN:
1059 if (!checkTockens || ParseInputBuffer())
1060 {
1061 WORDS answer = callback();
1062 if (answer !=e_WORD_CONTINUE)
1063 {
1064 // Quit
1065 return answer;
1066 }
1067 else
1068 {
1069 // Continue
1070 ResetInput();
1071 gAskQuestion = 1;
1072 }
1073 }
1074 else
1075 {
1076 // No word recognized
1077 PlaySound(PingData);
1078 }
1079 break;
1080
1081 default:
1082 if (k>=32)
1083 {
1084 if ( (k>='A') && (k<='Z') && shift)
1085 {
1086 k |= 32;
1087 }
1088
1089 if (gInputBufferPos<20)
1090 {
1091 gInputBuffer[gInputBufferPos++]=k;
1092 gInputBuffer[gInputBufferPos]=0;
1093 PlaySound(KeyClickLData);
1094 }
1095 }
1096 break;
1097 }
1098 }
1099 }
1100
1101
1102 void Initializations()
1103 {
1104 // erase the screen
1105 memset((char*)0xa000,0,8000);
1106
1107 // Install the 50hz IRQ
1108 System_InstallIRQ_SimpleVbl();
1109
1110 // Setup the Hires/Text mixed graphic mode
1111 InitializeGraphicMode();
1112
1113 // Load the charset
1114 LoadFileAt(LOADER_FONT_6x8,0xb500);
1115
1116 // Perform some initializations for the text display system
1117 ComputeFancyFontWidth();
1118 GenerateShiftBuffer();
1119 GenerateMul40Table();
1120
1121 #ifdef TESTING_MODE
1122 // Add here any change to the scenario to easily check things
1123 //gCurrentLocation =e_LOCATION_INSIDEHOLE;
1124 gCurrentLocation =e_LOCATION_OUTSIDE_PIT;
1125 gItems[e_ITEM_Rope].location = e_LOCATION_INVENTORY;
1126 gItems[e_ITEM_Ladder].location = e_LOCATION_INVENTORY;
1127 //gItems[e_ITEM_LadderInTheHole].location = e_LOCATION_OUTSIDE_PIT;
1128 //gCurrentLocation =e_LOCATION_WELL;
1129 //gCurrentLocation =e_LOCATION_ENTRANCEHALL;
1130 //gCurrentLocation =e_LOCATION_LAWN;
1131 //gCurrentLocation =e_LOCATION_MASTERBEDROOM;
1132 ////gCurrentLocation =e_LOCATION_MARKETPLACE;
1133 //gCurrentLocation =e_LOCATION_NARROWPATH;
1134 //gCurrentLocation = e_LOCATION_LIBRARY;
1135 //gItems[e_ITEM_PlasticBag].location = e_LOCATION_INVENTORY;
1136
1137 gItems[e_ITEM_ChemistryBook].location = e_LOCATION_INVENTORY;
1138 gCurrentLocation =e_LOCATION_LIBRARY;
1139
1140 #else
1141 // In normal gameplay, the player starts from the marketplace with an empty inventory
1142 gCurrentLocation = e_LOCATION_MARKETPLACE;
1143 #endif
1144
1145 gScore = 0;
1146 gCurrentStream = 0;
1147 gDelayStream = 0;
1148
1149 LoadScene();
1150 DisplayClock();
1151
1152 ResetInput();
1153 }
1154
1155
1156 void main()
1157 {
1158 Initializations();
1159
1160 AskInput(gTextAskInput,ProcessAnswer,1);
1161
1162 // Just to let the last click sound to keep playing
1163 WaitFrames(4);
1164
1165 System_RestoreIRQ_SimpleVbl();
1166
1167 // Quit and return to the intro
1168 InitializeFileAt(LOADER_INTRO_PROGRAM,0x400);
1169 }
1170

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26