typos
[k8-i-v-a-n.git] / src / game / message.cpp
blobae36845a3e5941cc7493b02338d1713ffe38e484
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 #include <cstdarg>
13 #include <cctype>
15 #ifndef DISABLE_SOUND
16 # include "regex.h"
17 # include <SDL_mixer.h>
18 #endif
20 #include "message.h"
21 #include "festring.h"
22 #include "felist.h"
23 #include "game.h"
24 #include "graphics.h"
25 #include "fesave.h"
26 #include "feparse.h"
27 #include "bitmap.h"
28 #include "igraph.h"
29 #include "iconf.h"
32 felist msgsystem::MessageHistory(CONST_S("Message history"), WHITE, 128);
33 festring msgsystem::LastMessage;
34 festring msgsystem::BigMessage;
35 int msgsystem::Times;
36 v2 msgsystem::Begin, msgsystem::End;
37 truth msgsystem::Enabled = true;
38 truth msgsystem::BigMessageMode = false;
39 truth msgsystem::MessagesChanged = true;
40 bitmap* msgsystem::QuickDrawCache = 0;
41 int msgsystem::LastMessageLines;
44 void msgsystem::AddMessage (cchar *Format, ...) {
45 if (!Enabled) return;
46 if (BigMessageMode && BigMessage.GetSize() >= 512) LeaveBigMessageMode();
47 char Message[1024];
49 va_list AP;
50 va_start(AP, Format);
51 vsnprintf(Message, sizeof(Message)-1, Format, AP);
52 va_end(AP);
54 festring Buffer(Message);
56 if (!Buffer.GetSize()) ABORT("Empty message request!");
58 soundsystem::playSound(Buffer);
60 Buffer.Capitalize();
62 /* Comment the first line and uncomment the second before the release! */
63 if (isalpha(Buffer[Buffer.GetSize()-1]))
64 //Buffer << " (this sentence isn't terminated correctly because Hex doesn't know grammar rules)";
65 Buffer << '.';
67 if (BigMessageMode) {
68 if (BigMessage.GetSize()) BigMessage << ' ';
69 BigMessage << Buffer;
70 return;
73 ivantime Time;
74 game::GetTime(Time);
76 if (Buffer == LastMessage) {
77 for (int c = 0; c < LastMessageLines; ++c) MessageHistory.Pop();
78 ++Times;
79 End = v2(Time.Hour, Time.Min);
80 } else {
81 Times = 1;
82 Begin = End = v2(Time.Hour, Time.Min);
83 LastMessage = Buffer;
84 LastMessage.ensureUniqueOwned();
87 festring Temp;
88 Temp << Begin.X << ':';
90 if(Begin.Y < 10)
91 Temp << '0';
93 Temp << Begin.Y;
95 if(Begin != End)
97 Temp << '-' << End.X << ':';
99 if(End.Y < 10)
100 Temp << '0';
102 Temp << End.Y;
105 if(Times != 1)
106 Temp << " (" << Times << "x)";
108 Temp << ' ';
109 int Marginal = Temp.GetSize();
110 Temp << Buffer;
112 std::vector<festring> Chapter;
113 festring::SplitString(Temp, Chapter, 78, Marginal);
115 for(uInt c = 0; c < Chapter.size(); ++c)
116 MessageHistory.AddEntry(Chapter[c], WHITE);
118 MessageHistory.SetSelected(MessageHistory.GetLastEntryIndex());
119 LastMessageLines = Chapter.size();
120 MessagesChanged = true;
123 void msgsystem::Draw()
125 truth WasInBigMessageMode = BigMessageMode;
126 LeaveBigMessageMode();
128 if(MessagesChanged)
130 MessageHistory.QuickDraw(QuickDrawCache, 8);
131 MessagesChanged = false;
134 v2 Size = QuickDrawCache->GetSize();
135 int Y = RES.Y - 122;
136 blitdata B = { DOUBLE_BUFFER,
137 { 0, 0 },
138 { 13, Y },
139 { Size.X, Size.Y },
140 { 0 },
142 0 };
144 QuickDrawCache->NormalBlit(B);
145 igraph::BlitBackGround(v2(13, Y), v2(1, 1));
146 igraph::BlitBackGround(v2(12 + Size.X, Y), v2(1, 1));
147 igraph::BlitBackGround(v2(13, Y + Size.Y - 1), v2(1, 1));
148 igraph::BlitBackGround(v2(12 + Size.X, Y + Size.Y - 1), v2(1, 1));
150 if(WasInBigMessageMode)
151 EnterBigMessageMode();
154 void msgsystem::DrawMessageHistory()
156 MessageHistory.Draw();
159 void msgsystem::Format()
161 MessageHistory.Empty();
162 LastMessage.Empty();
163 MessagesChanged = true;
164 BigMessageMode = false;
167 void msgsystem::Save(outputfile& SaveFile)
169 MessageHistory.Save(SaveFile);
170 SaveFile << LastMessage << Times << Begin << End;
173 void msgsystem::Load(inputfile& SaveFile)
175 MessageHistory.Load(SaveFile);
176 SaveFile >> LastMessage >> Times >> Begin >> End;
179 void msgsystem::ScrollDown()
181 if(MessageHistory.GetSelected() < MessageHistory.GetLastEntryIndex())
183 MessageHistory.EditSelected(1);
184 MessagesChanged = true;
188 void msgsystem::ScrollUp()
190 if(MessageHistory.GetSelected())
192 MessageHistory.EditSelected(-1);
193 MessagesChanged = true;
197 void msgsystem::LeaveBigMessageMode()
199 BigMessageMode = false;
201 if(BigMessage.GetSize())
203 AddMessage("%s", BigMessage.CStr());
204 BigMessage.Empty();
208 void msgsystem::Init()
210 QuickDrawCache = new bitmap(v2((game::GetScreenXSize() << 4) + 6, 106));
211 QuickDrawCache->ActivateFastFlag();
212 game::SetStandardListAttributes(MessageHistory);
213 MessageHistory.AddFlags(INVERSE_MODE);
216 void msgsystem::ThyMessagesAreNowOld()
218 if(MessageHistory.GetColor(MessageHistory.GetLastEntryIndex()) == WHITE)
219 MessagesChanged = true;
221 for(uInt c = 0; c < MessageHistory.GetLength(); ++c)
222 MessageHistory.SetColor(c, LIGHT_GRAY);
226 /* SOUND SYSTEM */
227 #ifndef DISABLE_SOUND
229 #define SND_CHANNEL_COUNT (16)
231 struct SoundFile {
232 festring filename;
233 Mix_Chunk *chunk;
234 //Mix_Music *music;
238 struct SoundInfo {
239 //pcre *re;
240 //pcre_extra *extra;
241 SEE_RegExpr *re;
242 std::vector<int> sounds;
245 int soundsystem::SoundState = 0;
247 std::vector<SoundFile> soundsystem::files;
248 std::vector<SoundInfo> soundsystem::patterns;
251 int soundsystem::addFile (const festring &filename) {
252 for (int i = 0; i < int(files.size()); i++) if (files[i].filename == filename) return i;
253 SoundFile p;
254 p.filename = filename;
255 p.chunk = NULL;
256 //p.music = NULL;
257 files.push_back(p);
258 return files.size()-1;
261 bool eol = false;
263 static festring getstr (FILE *f, truth word) {
264 if (eol && word) return "";
265 festring s;
266 for (;;) {
267 char c = fgetc(f);
268 if (c == EOF) return s;
269 if (c == 13) continue;
270 if (c == 10 && s != "") return eol = true, s;
271 if (c == 10) continue;
272 if (c == ' ' && word && s != "") return s;
273 s = s+c;
278 void soundsystem::initSound () {
279 //const char *error;
280 //int erroffset;
281 if (SoundState == 0) {
282 //FILE *debf = fopen("snddebug.txt", "wt");
283 FILE *debf = NULL;
284 if (debf) fprintf(debf, "This file can be used to diagnose problems with sound.\n");
285 if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 8000) != 0) {
286 ADD_MESSAGE("Unable to initialize audio: %s", Mix_GetError());
287 if (debf) fprintf(debf, "Unable to initialize audio: %s\n", Mix_GetError());
288 SoundState = -1;
289 return;
291 Mix_AllocateChannels(SND_CHANNEL_COUNT);
292 setVolume(ivanconfig::GetSoundVolume());
293 SoundState = -2;
294 festring cfgfile = game::GetGameDir()+"sound/config.txt";
295 FILE *f = fopen(cfgfile.CStr(), "r");
296 if (!f) SoundState = -1;
297 else {
298 festring Pattern, File;
299 while ((Pattern = getstr(f, false)) != "") {
300 SoundInfo si;
302 si.re = pcre_compile(Pattern.CStr(), 0, &error, &erroffset, NULL);
303 if (!si.re) {
304 if (debf) fprintf(debf, "PCRE compilation failed at expression offset %d: %s\n", erroffset, error);
305 ADD_MESSAGE("PCRE compilation failed at expression offset %d: %s", erroffset, error);
306 } else {
307 si.extra = pcre_study(si.re, 0, &error);
310 si.re = SEE_regex_parse(Pattern.CStr(), Pattern.GetSize(), SEERX_FLAG_IGNORECASE);
311 if (!si.re) {
312 ADD_MESSAGE("Sound RegExpr compilation failed: %s", Pattern.CStr());
314 eol = false;
315 while ((File = getstr(f, true)) != "") si.sounds.push_back(addFile(File));
316 if (si.sounds.size() != 0) patterns.push_back(si);
318 fclose(f);
319 SoundState = 1;
320 //Mix_HookMusicFinished(changeMusic);
322 if (debf) fclose(debf);
327 SoundFile *soundsystem::findMatchingSound (const festring &Buffer) {
328 for (int f = patterns.size()-1; f >= 0; --f) {
329 if (patterns[f].re) {
331 if (pcre_exec(patterns[f].re, patterns[f].extra, Buffer.CStr(), Buffer.GetSize(), 0, 0, NULL, 0) >= 0) {
332 return &files[patterns[f].sounds[rand()%patterns[f].sounds.size()]];
335 if (SEE_regex_match(patterns[f].re, Buffer.CStr(), Buffer.GetSize(), 0, NULL)) {
336 return &files[patterns[f].sounds[rand()%patterns[f].sounds.size()]];
340 return NULL;
344 void soundsystem::setVolume (sLong vol) {
345 if (vol < 0) vol = 0; else if (vol > 128) vol = 128;
346 if (SoundState == 1) {
347 for (int f = 0; f < SND_CHANNEL_COUNT; f++) Mix_Volume(f, vol);
352 void soundsystem::playSound (const festring &Buffer) {
353 if (!ivanconfig::GetPlaySounds()) return;
354 initSound();
355 if (SoundState == 1) {
356 SoundFile *sf = findMatchingSound(Buffer);
357 if (!sf) return;
358 if (!sf->chunk) {
359 festring sndfile = game::GetGameDir()+"sound/"+sf->filename;
360 //fprintf(stderr, "loading sound: '%s'\n", sndfile.CStr());
361 sf->chunk = Mix_LoadWAV(sndfile.CStr());
363 if (sf->chunk) {
364 for (int f = 0; f < SND_CHANNEL_COUNT; ++f) {
365 if (!Mix_Playing(f)) {
366 //fprintf(stderr, "starting sound: '%s'\n", sf->filename.CStr());
367 Mix_Volume(f, ivanconfig::GetSoundVolume());
368 Mix_PlayChannel(f, sf->chunk, 0);
369 //Mix_SetPosition(f, angle, dist);
370 return;
377 #else
379 void soundsystem::initSound () {}
380 int soundsystem::addFile (const festring &filename) { return 0; }
381 SoundFile *soundsystem::findMatchingSound (const festring &Buffer) { return NULL; }
382 void soundsystem::playSound (const festring &Buffer) {}
383 void soundsystem::setVolume (sLong vol) {}
385 #endif