Commit fc2215d3 authored by TANIGUCHI Takaki's avatar TANIGUCHI Takaki

New upstream version 0.9

parent e34bec84
......@@ -169,8 +169,8 @@ usage:
@echo "$(GOAL_DEBUG) build the executable with debug options"
@echo "$(GOAL_PROF) build the executable with profiling options"
@echo "clean remove all built files"
@echo "install remove all built files"
@echo "uninstall remove all built files"
@echo "install install executable, manual, and high scores files"
@echo "uninstall remove executable and manual files"
# If source files exist then build the EXE file.
.PHONY: $(GOAL_EXE)
......
......@@ -8,7 +8,7 @@ Home page: http://marc.mongenet.ch/OSS/XGalaga/
COPYRIGHT
2003, 2004, 2008, 2009, 2010, 2013 Marc Mongenet
2003, 2004, 2008, 2009, 2010, 2013, 2017 Marc Mongenet
Xgalaga++ is free software, available under the terms of the GNU General
Public License.
......@@ -38,6 +38,9 @@ http://marc.mongenet.ch/OSS/GenericMakefiles/.
VERSIONS
(2017-12-10) v0.9: Added key to change screen refresh rate
Invisible mouse pointer
One xlib call per frame optimized out
(2013-10-15) v0.8.4: Fix missing include (thanks to nemysis)
(2012-04-20) v0.8.3: Fix to compile on BSD (thanks to nemysis)
(2010-12-09) v0.8.2: Fix to compile on 64-bit systems (thanks to Zbigniew Baniewski)
......
......@@ -67,6 +67,7 @@ Config & Config::Instance()
Config::Config()
: details_level_ (max_details)
, refresh_rate_ (50)
, player_name_ (PlayerName())
, score_file_name_(ScoreFileName())
{
......@@ -81,6 +82,10 @@ void Config::AddDetailsLevel(int i) {
}
int Config::NextRefreshRate(void) const {
return refresh_rate_ >= 120 ? 50 : refresh_rate_ + 5;
}
/*
*
......
......@@ -10,6 +10,7 @@ class Config {
enum { min_details = 0, max_details = 4 };
int details_level_;
int refresh_rate_;
const std::string player_name_;
const std::string score_file_name_;
......@@ -24,6 +25,9 @@ public:
int ScaleDetails(int val) const
{ return val * details_level_ / max_details; }
int MaxDetails() const { return max_details; }
int RefreshRate(void) const { return refresh_rate_; }
int NextRefreshRate(void) const;
void SetNextRefreshRate(void) { refresh_rate_ = NextRefreshRate(); }
const std::string & GetPlayerName() const { return player_name_; }
const std::string & GetScoreFileName() const { return score_file_name_; }
};
......
......@@ -9,8 +9,5 @@ enum { g_alien_width = 19,
g_aliens_vspacing = 24
};
// Vertical refresh rate
const int vfreq (50);
#endif
......@@ -171,3 +171,19 @@ void X11::AllocColorAlways(XColor * color) const
*color = colors[best_index];
AllocColor(color);
}
void X11::SetInvisibleCursor(void) {
Cursor invisibleCursor;
Pixmap bitmap;
XColor black;
static char noData[] = { 0,0,0,0,0,0,0,0 };
black.red = black.green = black.blue = 0;
bitmap = XCreateBitmapFromData(display_, window_, noData, 8, 8);
invisibleCursor = XCreatePixmapCursor(display_, bitmap, bitmap,
&black, &black, 0, 0);
XDefineCursor(display_, window_, invisibleCursor);
XFreeCursor(display_, invisibleCursor);
XFreePixmap(display_, bitmap);
}
......@@ -109,6 +109,8 @@ public:
int WindowWidth() const;
int WindowHeight() const;
void SetInvisibleCursor(void);
Color GetBlack() const;
Color GetWhite() const;
void SetForeground(Color color) const;
......
......@@ -38,7 +38,7 @@ void HighScores::Update()
Load(file);
file.clear();
if (last_score_ != -1) {
high_scores_[last_size_].insert(HighScore(last_score_));
high_scores_[last_rate_][last_size_].insert(HighScore(last_score_));
last_score_ = -1;
if (filebuf.Truncate() != 0)
throw std::runtime_error ("Cannot update high scores file '"+score_file_name+"'!");
......@@ -48,24 +48,35 @@ void HighScores::Update()
}
const std::multiset<HighScore> * HighScores::Get(Coord window_size) const
const std::multiset<HighScore> * HighScores::Get(Coord window_size, int refresh_rate) const
{
const Ctn::const_iterator it (high_scores_.find(window_size));
return it == high_scores_.end() ? 0 : &(it->second);
const MapRateCoordScore::const_iterator it1 (high_scores_.find(refresh_rate));
if (it1 == high_scores_.end()) return 0;
const MapCoordScore::const_iterator it2 (it1->second.find(window_size));
return it2 == it1->second.end() ? 0 : &(it2->second);
}
void HighScores::Load(std::istream & scores_file)
{
high_scores_.clear();
int score;
Coord window_size;
string name;
std::time_t score_date = 0;
std::time_t score_date;
int refresh_rate;
while (scores_file.peek() != EOF) {
if (scores_file.peek() == 'T') {
score_date = 0;
refresh_rate = 50;
if (scores_file.peek() == '@') {
scores_file.ignore();
if (!(scores_file >> refresh_rate >> score_date >> score >> window_size.x >> window_size.y && std::getline(scores_file, name))) {
break;
}
}
else if (scores_file.peek() == 'T') {
scores_file.ignore();
if (!(scores_file >> score_date >> score >> window_size.x >> window_size.y && std::getline(scores_file, name))) {
break;
......@@ -80,7 +91,8 @@ void HighScores::Load(std::istream & scores_file)
if (!name.empty()) {
name.erase(0, 1);
}
high_scores_[window_size].insert(HighScore(score, name, score_date));
high_scores_[refresh_rate][window_size].insert(HighScore(score, name, score_date));
}
if (!scores_file.eof())
......@@ -90,18 +102,22 @@ void HighScores::Load(std::istream & scores_file)
void HighScores::Save(std::ostream & scores_file) const
{
for (Ctn::const_iterator mapit (high_scores_.begin());
mapit != high_scores_.end(); ++mapit) {
for (std::multiset<HighScore>::const_iterator setit (mapit->second.begin());
setit != mapit->second.end(); ++setit) {
scores_file << 'T'
<< setit->Date() << ' '
<< setit->Value() << ' '
<< mapit->first.x << ' '
<< mapit->first.y << ' '
<< setit->Name() << '\n';
if (!scores_file.good()) {
throw std::runtime_error ("Error while saving high scores file '"+Config::Instance().GetScoreFileName()+"'!");
for (MapRateCoordScore::const_iterator rateit (high_scores_.begin());
rateit != high_scores_.end(); ++rateit) {
for (MapCoordScore::const_iterator coordit (rateit->second.begin());
coordit != rateit->second.end(); ++coordit) {
for (std::multiset<HighScore>::const_iterator setit (coordit->second.begin());
setit != coordit->second.end(); ++setit) {
scores_file << '@'
<< rateit->first << ' '
<< setit->Date() << ' '
<< setit->Value() << ' '
<< coordit->first.x << ' '
<< coordit->first.y << ' '
<< setit->Name() << '\n';
if (!scores_file.good()) {
throw std::runtime_error ("Error while saving high scores file '"+Config::Instance().GetScoreFileName()+"'!");
}
}
}
}
......
......@@ -35,22 +35,25 @@ inline bool operator<(const HighScore & lhs, const HighScore & rhs)
class HighScores {
static HighScores * singleton_;
typedef std::map<Coord, std::multiset<HighScore> > Ctn;
Ctn high_scores_;
typedef std::map<Coord, std::multiset<HighScore> > MapCoordScore;
typedef std::map<int, MapCoordScore> MapRateCoordScore;
MapRateCoordScore high_scores_;
int last_score_;
Coord last_size_;
int last_rate_;
HighScores() : last_score_(-1) {}
public:
static HighScores & Instance();
static void DestroyInstance() { delete singleton_; singleton_ = 0; }
void Add(int score, Coord window_size) {
void Add(int score, Coord window_size, int refresh_rate) {
last_score_ = score;
last_size_ = window_size;
last_rate_ = refresh_rate;
}
void Update();
const std::multiset<HighScore> * Get(Coord window_size) const;
const std::multiset<HighScore> * Get(Coord window_size, int refresh_rate) const;
private:
void Load(std::istream &);
void Save(std::ostream &) const;
......
......@@ -4,16 +4,17 @@
Input::Input()
: quit_ (false)
, move_ (0)
, left_ (false), right_ (false), last_is_left_(false)
, up_ (false), down_ (false), last_is_up_(false)
, fire_ (false), firing_(false)
, start_ (false)
, pause_ (false)
, cheat_ (false)
, details_ (0)
, window_size_(0)
: quit_ (false)
, move_ (0)
, left_ (false), right_ (false), last_is_left_(false)
, up_ (false), down_ (false), last_is_up_(false)
, fire_ (false), firing_(false)
, start_ (false)
, pause_ (false)
, cheat_ (false)
, details_ (0)
, window_size_ (0)
, inc_refresh_rate_(false)
{}
......@@ -27,13 +28,15 @@ void Input::Update()
highscores_ = false;
details_ = 0;
window_size_ = 0;
inc_refresh_rate_ = false;
while (X11::Inst().Pending()) {
XEvent e;
X11::Inst().NextEvent(&e);
switch (e.type) {
X11::Inst().NextEvent(&e);
switch (e.type) {
case KeyPress:
switch (XLookupKeysym(&e.xkey, 0)) {
switch (XLookupKeysym(&e.xkey, 0)) {
case XK_Left:
left_ = true;
last_is_left_ = true;
......@@ -53,7 +56,7 @@ void Input::Update()
case XK_space:
fire_ = firing_ = true;
break;
default: {
default:
char c (0);
XLookupString(&e.xkey, &c, 1, 0, 0);
switch (c) {
......@@ -63,6 +66,9 @@ void Input::Update()
case '-':
details_ = -1;
break;
case 'r':
inc_refresh_rate_ = true;
break;
case '1':
window_size_ = 1;
break;
......@@ -86,19 +92,18 @@ void Input::Update()
break;
case 'q':
quit_ = true;
return;
return;
case 's':
start_ = true;
return;
return;
case 'C':
cheat_ = true;
return;
}
}
}
break;
case KeyRelease:
switch (XLookupKeysym(&e.xkey, 0)) {
case KeyRelease:
switch (XLookupKeysym(&e.xkey, 0)) {
case XK_Left:
left_ = false;
break;
......
......@@ -15,19 +15,21 @@ class Input {
bool highscores_;
int details_;
int window_size_;
bool inc_refresh_rate_;
public:
Input();
void Update();
bool Quit() const { return quit_; }
int Move() const { return move_; }
int VMove() const { return vmove_; }
bool Fire() const { return fire_; }
bool Start() const { return start_; }
bool Pause() const { return pause_; }
bool Cheat() const { return cheat_; }
bool HighScores() const { return highscores_; }
int Details() const { return details_; }
int WindowSize() const { return window_size_; }
bool Quit() const { return quit_; }
int Move() const { return move_; }
int VMove() const { return vmove_; }
bool Fire() const { return fire_; }
bool Start() const { return start_; }
bool Pause() const { return pause_; }
bool Cheat() const { return cheat_; }
bool HighScores() const { return highscores_; }
int Details() const { return details_; }
int WindowSize() const { return window_size_; }
bool IncRefreshRate() const { return inc_refresh_rate_; }
};
......
......@@ -300,13 +300,21 @@ void Play()
int fast_star_scrolling_time (0);
bool cheated (false);
bool window_resized (false);
bool rate_changed (false);
Coord last_window_size (X11::Inst().WindowWidth(), X11::Inst().WindowHeight());
double frame_time (0);
for (;;) {
input.Update();
if (input.Quit()) break;
if (!input.Pause()) {
Config::Instance().AddDetailsLevel(input.Details());
if (input.IncRefreshRate()) {
Config::Instance().SetNextRefreshRate();
rate_changed = true;
}
SetStandardWindowSize(input.WindowSize());
X11::Inst().GetWindowAttributes();
if (last_window_size != Coord(X11::Inst().WindowWidth(), X11::Inst().WindowHeight())) {
......@@ -329,8 +337,8 @@ void Play()
Score::Instance().SetShield(player.Shield());
if (player.Shield() < 0) {
if (!cheated && !window_resized) {
HighScores::Instance().Add(Score::Instance().Value(), last_window_size);
if (!cheated && !window_resized && !rate_changed) {
HighScores::Instance().Add(Score::Instance().Value(), last_window_size, Config::Instance().RefreshRate());
}
break;
}
......@@ -362,7 +370,7 @@ void Play()
Score::Instance().Refresh();
}
X11::Inst().Sync(False);
frame_time = SleepTimeInterval(frame_time, 1.0 / vfreq);
frame_time = SleepTimeInterval(frame_time, 1.0 / Config::Instance().RefreshRate());
}
}
......@@ -371,6 +379,7 @@ void MainLoop()
{
X11::CreateInstance();
X11::Inst().SetWindowTitle("XGalaga++");
X11::Inst().SetInvisibleCursor();
while (StartMenu::Instance().Display()) {
Play();
}
......
......@@ -244,7 +244,7 @@ void AliensManager::Creation()
case new_convoy:
convoy_alien_idx_ = 0;
next_creation_ = new_alien;
// continue with new_alien
// Falls through.
case new_alien: {
const ConvoyData *const convoy (convoys_ + convoy_idx_);
const Coord delta_cruise ((convoy_alien_idx_ + (max_convoy_size_ - convoy->ConvoySize()) / 2) * g_aliens_hspacing,
......
......@@ -29,14 +29,13 @@ StartMenu & StartMenu::Instance() {
const char *const menu_lines[] = {
"XGalaga++ v0.8.4 (2013-10-15)",
"2003-2013 Marc Mongenet, graphics from XGalaga 2.0.34",
"XGalaga++ v0.9 (2017-12-10)",
"2003-2017 Marc Mongenet, graphics from XGalaga 2.0.34",
"This game is free software, covered by the GPL.",
"",
"Welcome player %p!",
"",
"Key Action",
" ",
"KEY ACTION",
"s start game",
"q quit",
"h toggle high scores / menu",
......@@ -44,12 +43,13 @@ const char *const menu_lines[] = {
"1,2,3,4,5 set window size",
"+ increase details",
"- decrease details",
"r refresh rate %r1->%r2 Hz",
"<space bar> fire",
"<left/right arrow> move left/right",
"<up/down arrow> scroll high scores up/down",
"",
"Bonus",
"",
"BONUS",
"",
"extra shield every 1000 points"
};
......@@ -86,28 +86,43 @@ bool StartMenu::Display()
Input input;
bool updated (false);
double frame_time (0);
for (;;) {
for (;;) {
input.Update();
if (input.Quit()) return false;
if (input.Start()) {
print_scores_ = true;
wait_scores_ = 5000;
return true;
}
Config::Instance().AddDetailsLevel(input.Details());
if (input.IncRefreshRate()) {
Config::Instance().SetNextRefreshRate();
X11::Inst().ClearWindow();
}
SetStandardWindowSize(input.WindowSize());
X11::Inst().GetWindowAttributes();
if (--wait_scores_ < 0 || input.HighScores()) {
print_scores_ = !print_scores_;
wait_scores_ = 1000;
X11::Inst().ClearWindow();
}
StarsFields::Instance().Scroll();
Score::Instance().Refresh();
if (last_window_size_ != Coord(X11::Inst().WindowWidth(), X11::Inst().WindowHeight())) {
last_window_size_ = Coord(X11::Inst().WindowWidth(), X11::Inst().WindowHeight());
scroll_scores_ = 0;
}
if (print_scores_) {
try {
if (!updated) {
......@@ -128,8 +143,10 @@ bool StartMenu::Display()
}
}
else PrintHelp(NextColor());
X11::Inst().Sync(False);
frame_time = SleepTimeInterval(frame_time, 1.0 / vfreq);
frame_time = SleepTimeInterval(frame_time, 1.0 / Config::Instance().RefreshRate());
}
}
......@@ -162,13 +179,29 @@ void StartMenu::PrintHelp(XColor color)
{
Coord pos (std::max(0, (last_window_size_.x - text_width_) / 2), 20);
SetFontContext(color);
// Draw help text
for (size_t i (0); i < sizeof menu_lines / sizeof menu_lines[0]; ++i) {
std::string line (menu_lines[i]);
while (line.find("%p") != std::string::npos) {
line.replace(line.find("%p"), 2, Config::Instance().GetPlayerName());
}
std::ostringstream refresh_rate;
refresh_rate << Config::Instance().RefreshRate();
while (line.find("%r1") != std::string::npos) {
line.replace(line.find("%r1"), 3, refresh_rate.str().c_str());
}
std::ostringstream next_refresh_rate;
next_refresh_rate << Config::Instance().NextRefreshRate();
while (line.find("%r2") != std::string::npos) {
line.replace(line.find("%r2"), 3, next_refresh_rate.str().c_str());
}
X11::Inst().DrawString(pos, line);
pos.y += LineHeight();
if (line.empty()) {
color.red -= 8192;
......@@ -197,7 +230,13 @@ void StartMenu::PrintScores(XColor color)
SetFontContext(color);
// Draw high scores title
std::ostringstream ost;
ost <<"High scores for "<<last_window_size_.x<<''<<last_window_size_.y<<" window";
ost <<"High scores for "
<<last_window_size_.x
<<''
<<last_window_size_.y
<<'@'
<<Config::Instance().RefreshRate()
<<" window";
X11::Inst().DrawString(pos, ost.str().c_str());
pos.y += LineHeight();
ost.str(std::string(ost.str().size(), ''));
......@@ -205,7 +244,7 @@ void StartMenu::PrintScores(XColor color)
pos.y += LineHeight();
// get high scores for current window size
const std::multiset<HighScore> *const high_scores (HighScores::Instance().Get(last_window_size_));
const std::multiset<HighScore> *const high_scores (HighScores::Instance().Get(last_window_size_, Config::Instance().RefreshRate()));
if (!high_scores) {
score_lines_.clear();
ost.str("None.");
......
......@@ -25,28 +25,29 @@ void StarsFields::StarsField::Scroll()
{
static unsigned count = 0;
// Scroll existing stars, destroying out of window ones.
// Erase existing stars.
X11::Inst().SetClipMask(None);
X11::Inst().SetForeground(X11::Inst().GetBlack());
DrawAll();
for (FieldCtn::size_type v = 0; v < field_.Size(); ++v) {
field_[v].y += speed_;
// Scroll existing stars, destroying out of window ones.
for (FieldCtn::size_type i = 0; i < field_.Size(); ++i) {
field_[i].y += speed_;
}
while (field_.Size() > 0 && field_[0].YOut()) {
field_.Pop();
}
X11::Inst().SetForeground(color_.pixel);
DrawAll();
// Create one star in an (empty) top line on pair 'count'.
if (count++ & 1) {
const Coord star (std::rand() % X11::Inst().WindowWidth(),
std::rand() % speed_);
field_.Push(star);
X11::Inst().DrawPoint(star);
}
// Draw all stars
X11::Inst().SetForeground(color_.pixel);
DrawAll();
}
......
.TH XGALAGA++ 6 "21-MAY-2008"
.TH XGALAGA++ 6 "10-DEC-2017"
.SH NAME
XGalaga++ \- X11 classic vertical scrolling shoot'em up
.SH SYNOPSIS
......@@ -35,6 +35,9 @@ Scroll high scores up/down.
.TP
.BI -/+
Decrease/increase details (use a bit less CPU and memory).
.TP
.BI r
Increment video refresh rate by 5 Hz in range 50 - 120 Hz.
.SH ENVIRONMENT VARIABLES
XGAL_PSEUDO - Player pseudonym, used for high scores.
.SH "SEE ALSO"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment