📄 ch29.htm
字号:
}
}
void __fastcall
TGameForm::SpriteScene1DrawScene(TObject *Sender)
{
if (FFightClass)
FFightClass->ShowData();
}
void __fastcall TGameForm::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift,
int X, int Y)
{
if (ByzGame->CurrentScene == mrFightMap)
{
if (BadQueen1->IsHit(X, Y))
FFightClass->PerformHit(this);
}
}
void __fastcall TGameForm::Scene1DrawScene(TObject *Sender)
{
Scene1->WriteXY(375, 405,
"Press Alt-B to Start");
Scene1->WriteXY(375, 430, "Press Alt-X to Exit");
}
void __fastcall TGameForm::HermesChart1HeroMove(TObject *Sender,
const tagPOINT &NewPos, int NewType, bool &MoveOk)
{
switch
(TMapType(NewType))
{
case mtGrass:
MoveOk = True;
break;
case mtCreature:
MoveOk = False;
ByzGame->BadGuy->Creature =
ByzGame->CreatureList->CreatureFromLocation(NewPos.x, NewPos.y);
if (ByzGame->BadGuy->Creature->Kind == "Food")
{
dynamic_cast<TByzCharacter*>(ByzGame->Hero)->Hunger += 3;
dynamic_cast<TByzCharacter*>(ByzGame->BadGuy)->SetVisible(False);
}
else if (ByzGame->BadGuy->Creature->Kind == "Medicine")
{
dynamic_cast<TByzCharacter*>(ByzGame->Hero)->HitPoints += 3;
dynamic_cast<TByzCharacter*>(ByzGame->BadGuy)->SetVisible(False);
}
else
{
ByzGame->CurrentScene = mrFightMap;
Run();
}
break;
default:
MoveOk = False;
}
}
</FONT></PRE>
<H3 ALIGN="CENTER"><FONT COLOR="#0066FF"></FONT></H3>
<P><A NAME="Heading15"></A><FONT COLOR="#000077"><B>Listing 29.7. A header file containing
some global declarations.</B></FONT></P>
<PRE><FONT
COLOR="#0066FF">///////////////////////////////////////
// Globals.h
// Byzantium Project
// Copyright (c) 1997 by Charlie Calvert
//
#ifndef GlobalsH
#define GlobalsH
#define mrHitCreature 0x5001
#define mrGameOver 0x5002
#define
mrWorldMap 0x6001
#define mrFightMap 0x6002
#define mrIntroMap 0x6003
#define WM_NEXTSCENE WM_USER + 1
#define WM_STARTSHOW WM_USER + 2
enum TMapType {mtGrass, mtWater, mtMountain, mtRoad, mtWater2,
mtFootHill,
mtNorthShore, mtWestShore, mtSouthShore, mtEastShore,
mtSWShore, mtSEShore, mtNWShore, mtNEShore,
mtWNWShore, mtWSEShore, mtESEShore, mtENEShore,
mtBlank1, mtBlank2, mtBlank3, mtBlank4, mtAllSnow,
mtSnowyMountain, mtSouthMtn, mtWestMtn, mtNorthMtn, mtEastMtn,
mtSEMtn, mtSWMtn, mtNWMtn, mtNEMtn, mtNWFootHill,
mtNEFootHill, mtSEFootHill, mtSWFootHill,
mtNorthFootHill, mtEastFootHill,
mtSouthFootHill,
mtWestFootHill, mtNEDiagShore, mtSEDiagShore,
mtSWDiagShore, mtNWDiagShore, mtSWBendShore, mtSEBendShore,
mtNWBendShore, mtNEBendShore, mtENBendShore, mtWNBendShore,
mtWSBendShore, mtESBendShore, mtCity, mtCreature};
#endif
</FONT></PRE>
<H3 ALIGN="CENTER"><FONT COLOR="#0066FF"></FONT></H3>
<P><A NAME="Heading16"></A><FONT COLOR="#000077"><B>Listing 29.8. The header file
for the fight class.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Fightclass.h
// Project: Byzantium
// Copyright (c) 1997 by Charlie Calvert
//
#ifndef FightClass1H
#define FightClass1H
#include "Mercury1.hpp"
class
TFightClass
{
private:
AnsiString FBadGuyName;
AnsiString FDisplayString;
HWND FHandle;
TScene *FScene;
bool FHitInProcess;
void Button1Click(void);
bool BadGuyAttacks(void);
bool CheckCharacters(void);
bool
HeroAttacks(void);
void DisplayData(AnsiString S);
public:
TFightClass(HWND AHandle, TScene *AScene);
void PerformHit(TObject *Sender);
void ShowData();
__property AnsiString BadGuyName={read=FBadGuyName};
};
#endif
</FONT></PRE>
<H3 ALIGN="CENTER"><FONT COLOR="#0066FF"></FONT></H3>
<P><A NAME="Heading17"></A><FONT COLOR="#000077"><B>Listing 29.9. The main source
file for the fight class.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
//
Fightclass.cpp
// Project: Byzantium
// Copyright (c) 1997 by Charlie Calvert
//
#include <vcl\vcl.h>
#include <time.h>
#pragma hdrstop
#include "Creatures1.hpp"
#include "FightClass1.h"
#include
"ByzEngine1.h"
#include "Mercury2.h"
TFightClass::TFightClass(HWND AHandle, TScene *AScene)
{
FHandle = AHandle;
FHitInProcess = False;
FScene = AScene;
FBadGuyName = ByzGame->BadGuy->Name;
}
void
TFightClass::DisplayData(AnsiString S)
{
TCustomValue *CustomValue;
AnsiString DisplayValue;
CustomValue = ByzGame->Hero->Creature->FindCustomByName("Hit Points");
DisplayValue = CustomValue->CurrentValue;
FScene->WriteXY(270, 405, DisplayValue);
CustomValue = ByzGame->BadGuy->Creature->FindCustomByName("Hit Points");
DisplayValue = CustomValue->CurrentValue;
FScene->WriteXY(270, 440, DisplayValue);
FScene->WriteXY(375, 410, FDisplayString);
}
void TFightClass::ShowData()
{
DisplayData("Hit Points");
if (ByzGame->BadGuy->Creature)
DisplayData("Hit Points");
}
void TFightClass::Button1Click()
{
ShowMessage(ByzGame->Hero->Name + " retreats. Receives 5 points damage.");
dynamic_cast<TByzCharacter *>(ByzGame->Hero)->HitPoints -= 5;
if (CheckCharacters());
}
void WaitTime(int Delay)
{
time_t t1, t2;
t1 = time(NULL);
while (True)
{
Application->ProcessMessages();
t2 = time(NULL);
if (t2 - t1 >= Delay)
return;
}
}
bool TFightClass::CheckCharacters(void)
{
TBadGuy *B =
dynamic_cast<TBadGuy*>(ByzGame->BadGuy);
if (B->HitPoints <= 0)
{
ByzGame->CreatureList->HideCreature(B->Name, False);
FDisplayString = "Victory is sweet!";
WaitTime(1);
ByzGame->CurrentScene = mrWorldMap;
PostMessage(FHandle, WM_STARTSHOW, 0, 0);
return False;
}
if (dynamic_cast<THero*>(ByzGame->Hero)->HitPoints <= 0)
{
FDisplayString = "Defeat is bitter ashes!";
WaitTime(1);
ByzGame->CurrentScene = mrIntroMap;
PostMessage(FHandle, WM_STARTSHOW, 0, 0);
return False;
}
return True;
}
bool TFightClass::BadGuyAttacks(void)
{
THero *H = dynamic_cast<THero*>(ByzGame->Hero);
TBadGuy *B = dynamic_cast<TBadGuy*>(ByzGame->BadGuy);
FDisplayString = H->Name + " Under attack!";
WaitTime(1);
if (H->DefendYourself(B))
{
FDisplayString = H->Name + ": No damage!";
}
else
FDisplayString = H->Name + " is hit!";
WaitTime(1);
return CheckCharacters();
}
bool TFightClass::HeroAttacks(void)
{
TBadGuy *B = dynamic_cast<TBadGuy*>(ByzGame->BadGuy);
THero *H =
dynamic_cast<THero*>(ByzGame->Hero);
FDisplayString = B->Name + " Under attack!";
WaitTime(1);
if (B->DefendYourself(H))
FDisplayString = B->Name + ": No damage!";
else
FDisplayString =
B->Name + " is hit!";
WaitTime(1);
return CheckCharacters();
}
void TFightClass::PerformHit(TObject *Sender)
{
if (FHitInProcess)
return;
FHitInProcess = True;
if (HeroAttacks())
BadGuyAttacks();
FHitInProcess = False;
FDisplayString = "Waiting...";
}
</FONT></PRE>
<P>As it is implemented here, Byzantium is a very simple game. When the program is
first launched, you see a main form with a picture of a bucolic landscape. A window
in the form states that you can start the game by pressing Alt+B, or you can press
Alt+X to exit.</P>
<P>If you press Alt+B, then you can see the hero standing on a tiled world map. You
can use the arrow keys to move the hero, pushing the Insert key
to toggle back and
forth between moving the hero alone, or moving the entire landscape.</P>
<P>The hero can interact with various objects on the tiled surface. For example,
the hero can eat bits of food, thereby alleviating his hunger. He also can
pick up
medical kits to restore the hit points or his health.</P>
<P>The hero can also encounter various bad guys, most of whom live in castles or
stone turrets. If you bump into a bad guy, you will be switched to a third scene
where the hero can
engage in combat with the bad guy.</P>
<P>When in fight mode, the hero, dressed as a monk, appears on the left. The villain,
who is always a wicked queen, is standing on the right. If you move the mouse cursor
over the queen, the cursor changes shape;
that is, it moves into attack mode. When
the cursor is in attack mode, you can left-click the wicked queen to attack her.
The hero gets a chance to do some damage to her, and she in turn will have a chance
to attack the hero.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>I feel the need to defend myself
against possible charges of sexism. As I develop this game further, I will give the
user a chance to choose whether the main character is a man
or woman. It was perhaps
not wise of me to pick the word "Hero" as a field of the <TT>TGame</TT>
object, but it seemed to me more concise and easy to understand than a phrase like
"MainCharacter." <BR>
<BR>
The fact that the
villain is a queen is mostly a function of my artist's inclination
when she produced her first evil character for me to use. As the game matures, it
will have more evil characters, some male, some female. <BR>
<BR>
In short, the game is not
intended to contain any political messages about sexuality,
and a more egalitarian world view will emerge as the game matures.
<HR>
</BLOCKQUOTE>
<P>The game is designed so that the hero can easily withstand several fights with
the bad guys.
Eventually, however, he will be worn down and will need to find more
food or medical kits or else perish. The condition for losing the game is to run
out of hit points before killing all the bad guys.</P>
<P>As I stated earlier, this game is not
complete. My goal was just to give you enough
pieces so that you could begin to construct your own game with its own rules. Where
you take the game from the point at which I have left it is up to you. You might,
however, want to check my Web site to
see whether I have found time to actually complete
a full game.
<H3><A NAME="Heading19"></A><FONT COLOR="#000077">Understanding the Technology Behind
Byzantium</FONT></H3>
<P>Now you can take a closer look at Byzantium. In the next few sections of the
book,
I help you examine the technology behind the game, showing the way it was put together
and giving hints about ways in which the game could be expanded.</P>
<P>This program uses all the graphics engine components introduced in the preceding
chapter. I lay them out on the main form, as shown in Figure 29.4. The properties
of these objects are filled out almost exactly as they were in Chapter 28, "Game
Programming," only this time I'm using all the components at once. To see the
details of which properties are connected to which values, you should launch the
game and study the main form.<BR>
<BR>
<A NAME="Heading20"></A><A HREF="29ebu04.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/29/29ebu04.jpg">FIGURE 29.4.</A><FONT COLOR="#000077">
</FONT><I>The graphics components used
in the Byzantium program as they appear on
the main form at design time.</I></P>
<P><BR>
On top of the graphics components I lay a game engine that consists of two main objects
called <TT>TCharacter</TT> and <TT>TGame</TT>. These objects are meant to
be base
classes from which you can make descendants of the characters and games that you
want to create.</P>
<P>The key fact to understand about <TT>TGame</TT> and <TT>TCharacter</TT> is that
they know how to work with the graphics engine and shield
the user from the graphics
engine's complexity. In short, the user should feel most of the time as though he
or she is manipulating a game or character that simply knows how to draw him, her,
or itself to the screen. In short, the programmer can stay
inside the problem domain
defined by the game itself and can ignore the problems inherent in implementing a
graphics engine.</P>
<P>For example, the user can simply ask the character to move, hide, state its name,
or keep track of its health, hit
points, and so on. The technical implementation
of all these traits should not be a concern to the programmer. It doesn't matter
how a character moves, hides, or is drawn to the screen. When you're writing a game,
you don't want to have to think about
those kinds of issues. You just want to design
a game.</P>
<P>Furthermore, you want to be sure that problems with the graphics engine can occur
only if a mistake is made in <TT>Mercury1.pas</TT> or in <TT>Creatures1.pas</TT>.
Graphics-based problems
should never be caused by errors in the game engine because
the game engine shouldn't contain any graphics-based code. Conversely, problems with
the logic of the game should not ever occur in the graphics engine because it should
contain no game
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -