DEADSOFTWARE

Game: Add chat sounds for promotion
[d2df-sdl.git] / src / game / g_game.pas
index 569d02ef2743535caf1f2eff25d495c8a4533577..ebb1387e79153cebaeeb33d2382545a2d2c44ebe 100644 (file)
@@ -49,6 +49,12 @@ type
     DEStr: String;
   end;
 
+  TChatSound = record
+    Sound: TPlayableSound;
+    Tags: Array of String;
+    FullWord: Boolean;
+  end;
+
   TPlayerSettings = record
     Name: String;
     Model: String;
@@ -114,6 +120,7 @@ procedure g_Game_PauseAllSounds(Enable: Boolean);
 procedure g_Game_StopAllSounds(all: Boolean);
 procedure g_Game_UpdateTriggerSounds();
 function  g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
+procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
 procedure g_Game_Announce_KillCombo(Param: Integer);
 procedure g_Game_StartVote(Command, Initiator: string);
@@ -303,6 +310,7 @@ var
   gVotesEnabled: Boolean = True;
   gEvents: Array of TGameEvent;
   gDelayedEvents: Array of TDelayedEvent;
+  gChatSounds: Array of TChatSound;
 
   // move button values:
   // bits 0-1: l/r state:
@@ -344,11 +352,15 @@ uses
   e_input, e_log, g_console, g_items, g_map, g_panel,
   g_playermodel, g_gfx, g_options, g_weapons, Math,
   g_triggers, g_monsters, e_sound, CONFIG,
-  g_language, g_net, SDL,
+  g_language, g_net,
   ENet, e_msg, g_netmsg, g_netmaster, GL, GLExt,
   sfs, wadreader, g_holmes;
 
 
+var
+  hasPBarGfx: Boolean = false;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end;
 
@@ -513,6 +525,7 @@ type
     ShowCount: Integer;
     Msgs: Array of String;
     NextMsg: Word;
+    PBarWasHere: Boolean; // did we draw a progress bar for this message?
   end;
 
   TParamStrValue = record
@@ -2042,7 +2055,69 @@ begin
   end;
 end;
 
+procedure g_Game_LoadChatSounds(Resource: string);
+var
+  WAD: TWADFile;
+  FileName, Snd: string;
+  p: Pointer;
+  len, cnt, tags, i, j: Integer;
+  cfg: TConfig;
+begin
+  FileName := g_ExtractWadName(Resource);
+
+  WAD := TWADFile.Create();
+  WAD.ReadFile(FileName);
+
+  if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
+  begin
+    gChatSounds := nil;
+    WAD.Free();
+    Exit;
+  end;
+
+  cfg := TConfig.CreateMem(p, len);
+  cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
+
+  SetLength(gChatSounds, cnt);
+  for i := 0 to Length(gChatSounds) - 1 do
+  begin
+    gChatSounds[i].Sound := nil;
+    Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
+    tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
+    if (Snd = '') or (Tags <= 0) then
+      continue;
+    g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
+    gChatSounds[i].Sound := TPlayableSound.Create();
+    gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
+    SetLength(gChatSounds[i].Tags, tags);
+    for j := 0 to tags - 1 do
+      gChatSounds[i].Tags[j] := LowerCase(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
+    gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
+  end;
+
+  cfg.Free();
+  WAD.Free();
+end;
+
+procedure g_Game_FreeChatSounds();
+var
+  i: Integer;
+begin
+  for i := 0 to Length(gChatSounds) - 1 do
+  begin
+    gChatSounds[i].Sound.Free();
+    g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
+  end;
+  SetLength(gChatSounds, 0);
+  gChatSounds := nil;
+end;
+
 procedure g_Game_LoadData();
+var
+  wl, hl: Integer;
+  wr, hr: Integer;
+  wb, hb: Integer;
+  wm, hm: Integer;
 begin
   if DataLoaded then Exit;
 
@@ -2062,6 +2137,29 @@ begin
   g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
   g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
   g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
+
+  hasPBarGfx := true;
+  if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
+  if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
+  if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
+  if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
+
+  if hasPBarGfx then
+  begin
+    g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
+    g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
+    g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
+    g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
+    if (wl > 0) and (hl > 0) and (wr > 0) and (hr = hl) and (wb > 0) and (hb = hl) and (wm > 0) and (hm > 0) and (hm <= hl) then
+    begin
+      // yay!
+    end
+    else
+    begin
+      hasPBarGfx := false;
+    end;
+  end;
+
   g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
   g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
   g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
@@ -2103,6 +2201,8 @@ begin
   killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
   killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
 
+  g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
+
   g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
   g_Items_LoadData();
 
@@ -2168,6 +2268,8 @@ begin
   g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
   g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
 
+  g_Game_FreeChatSounds();
+
   DataLoaded := False;
 end;
 
@@ -2515,41 +2617,105 @@ procedure DrawLoadingStat();
     glEnd();
   end;
 
-  procedure drawPBar (cur, total: Integer);
+  function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
   var
     rectW, rectH: Integer;
     x0, y0: Integer;
     wdt: Integer;
-  begin
+    wl, hl: Integer;
+    wr, hr: Integer;
+    wb, hb: Integer;
+    wm, hm: Integer;
+    idl, idr, idb, idm: LongWord;
+    f, my: Integer;
+  begin
+    result := false;
     if (total < 1) then exit;
     if (cur < 1) then exit; // don't blink
-    if (cur >= total) then exit; // don't blink
+    if (not washere) and (cur >= total) then exit; // don't blink
     //if (cur < 0) then cur := 0;
     //if (cur > total) then cur := total;
+    result := true;
 
-    rectW := gScreenWidth-64;
-    rectH := 16;
+    if (hasPBarGfx) then
+    begin
+      g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
+      g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
+      g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
+      g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
+      g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
+      g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
+      g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
+      g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
 
-    x0 := (gScreenWidth-rectW) div 2;
-    y0 := gScreenHeight-rectH-64;
-    if (y0 < 2) then y0 := 2;
+      //rectW := gScreenWidth-360;
+      rectW := trunc(624.0*gScreenWidth/1024.0);
+      rectH := hl;
 
-    glDisable(GL_BLEND);
-    glDisable(GL_TEXTURE_2D);
+      x0 := (gScreenWidth-rectW) div 2;
+      y0 := gScreenHeight-rectH-64;
+      if (y0 < 2) then y0 := 2;
+
+      glEnable(GL_SCISSOR_TEST);
+
+      // left and right
+      glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
+      e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
+      e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
+
+      // body
+      glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
+      f := x0+wl;
+      while (f < x0+rectW) do
+      begin
+        e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
+        f += wb;
+      end;
+
+      // filled part
+      wdt := (rectW-wl-wr)*cur div total;
+      if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
+      if (wdt > 0) then
+      begin
+        my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
+        glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
+        f := x0+wl;
+        while (wdt > 0) do
+        begin
+          e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
+          f += wm;
+          wdt -= wm;
+        end;
+      end;
+
+      glScissor(0, 0, gScreenWidth, gScreenHeight);
+    end
+    else
+    begin
+      rectW := gScreenWidth-64;
+      rectH := 16;
+
+      x0 := (gScreenWidth-rectW) div 2;
+      y0 := gScreenHeight-rectH-64;
+      if (y0 < 2) then y0 := 2;
+
+      glDisable(GL_BLEND);
+      glDisable(GL_TEXTURE_2D);
 
-    //glClearColor(0, 0, 0, 0);
-    //glClear(GL_COLOR_BUFFER_BIT);
+      //glClearColor(0, 0, 0, 0);
+      //glClear(GL_COLOR_BUFFER_BIT);
 
-    glColor4ub(127, 127, 127, 255);
-    drawRect(x0-2, y0-2, rectW+4, rectH+4);
+      glColor4ub(127, 127, 127, 255);
+      drawRect(x0-2, y0-2, rectW+4, rectH+4);
 
-    glColor4ub(0, 0, 0, 255);
-    drawRect(x0-1, y0-1, rectW+2, rectH+2);
+      glColor4ub(0, 0, 0, 255);
+      drawRect(x0-1, y0-1, rectW+2, rectH+2);
 
-    glColor4ub(127, 127, 127, 255);
-    wdt := rectW*cur div total;
-    if (wdt > rectW) then wdt := rectW;
-    drawRect(x0, y0, wdt, rectH);
+      glColor4ub(127, 127, 127, 255);
+      wdt := rectW*cur div total;
+      if (wdt > rectW) then wdt := rectW;
+      drawRect(x0, y0, wdt, rectH);
+    end;
   end;
 
 var
@@ -2575,7 +2741,7 @@ begin
 
       e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
       yy := yy + LOADING_INTERLINE;
-      drawPBar(CurValue, MaxValue);
+      PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
     end;
   end;
 end;
@@ -3785,6 +3951,7 @@ end;
 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
 var
   i, nPl: Integer;
+  tmps: AnsiString;
 begin
   g_Game_Free();
 
@@ -3840,7 +4007,8 @@ begin
 // Çàãðóçêà è çàïóñê êàðòû:
   if not g_Game_StartMap(MAP, True) then
   begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
+    if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
+    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
     Exit;
   end;
 
@@ -6789,6 +6957,57 @@ begin
   MessageTime := Time;
 end;
 
+procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
+var
+  i, j: Integer;
+  ok: Boolean;
+  fpText: String;
+
+  function FilterPunctuation(S: String): String;
+  begin
+    S := StringReplace(S, '.', ' ', [rfReplaceAll]);
+    S := StringReplace(S, ',', ' ', [rfReplaceAll]);
+    S := StringReplace(S, ':', ' ', [rfReplaceAll]);
+    S := StringReplace(S, ';', ' ', [rfReplaceAll]);
+    S := StringReplace(S, '!', ' ', [rfReplaceAll]);
+    S := StringReplace(S, '?', ' ', [rfReplaceAll]);
+    Result := S;
+  end;
+begin
+  g_Sound_PlayEx('SOUND_GAME_RADIO');
+
+  if Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
+  begin
+    // remove player name
+    Delete(Text, 1, Pos(': ', Text) + 2 - 1);
+    // for FullWord check
+    Text := LowerCase(' ' + Text + ' ');
+    fpText := FilterPunctuation(Text);
+
+    for i := 0 to Length(gChatSounds) - 1 do
+    begin
+      ok := True;
+      for j := 0 to Length(gChatSounds[i].Tags) - 1 do
+      begin
+        if gChatSounds[i].FullWord and
+        (gChatSounds[i].Tags[j] <> '.') and (gChatSounds[i].Tags[j] <> ',') and
+        (gChatSounds[i].Tags[j] <> ':') and (gChatSounds[i].Tags[j] <> ';') and
+        (gChatSounds[i].Tags[j] <> '!') and (gChatSounds[i].Tags[j] <> '?') then
+          ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
+        else
+          ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
+        if not ok then
+          break;
+      end;
+      if ok then
+      begin
+        gChatSounds[i].Sound.Play();
+        break;
+      end;
+    end;
+  end;
+end;
+
 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
 var
   a: Integer;
@@ -7002,6 +7221,7 @@ begin
     CurValue := 0;
     MaxValue := Max;
     ShowCount := 0;
+    PBarWasHere := false;
   end;
 
   g_ActiveWindow := nil;
@@ -7038,6 +7258,7 @@ begin
     for len := Low(Msgs) to High(Msgs) do
       Msgs[len] := '';
     NextMsg := 0;
+    PBarWasHere := false;
   end;
 end;