/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/Dmfc/dmfchudmessages.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003/08/26 03:57:20 $ * $Author: kevinb $ * * HUD Message (Death Message's etc) functions * * $Log: dmfchudmessages.cpp,v $ * Revision 1.1.1.1 2003/08/26 03:57:20 kevinb * initial 1.5 import * * * 20 7/09/99 2:53p Jeff * handle gametime better (pause it when needed) if the server is 'waiting * for players' * * 19 6/11/99 5:37p Jeff * removed ai_info #ifdefs (better way of doing it) * * 18 6/10/99 12:41p Jeff * no ai_info in non-Outrage builds * * 17 4/19/99 3:54a Jeff * added needed includes for Linux * * 16 3/17/99 12:24p Jeff * converted DMFC to be COM interface * * 15 2/11/99 12:50a Jeff * changed names of exported variables * * 14 2/02/99 8:43a Chris * I made buildings with AI work correctly (ie really big robots should be * buildings) * anim to and from states are now shorts instead of bytes * * 13 1/31/99 7:26p Matt * Renamed a bunch of functions to have HUD capitalized * * 12 1/19/99 3:55a Jeff * all strings localized out * * 10 10/30/98 12:47p Jeff * cut down a couple bytes on memory usage * * 9 10/20/98 2:10p Jeff * fixed bug where if death messages were turned off, you wouldn't get any * statistical messages * * 8 10/20/98 12:16p Jeff * added death message filter, hud callsign filter * * 7 10/13/98 2:15a Jeff * created new event for when a player leaves a multiplayer game. Fixed * some 'english' bugs in the network games. * * 6 10/05/98 2:49p Jeff * * 5 10/01/98 2:47p Jeff * added revenge message * * 4 9/28/98 5:05p Jeff * made the statisitical death messages an option in the menu * * 3 9/28/98 10:49a Jeff * fixed 2 of the statistical messages so they don't print if the time is * 0 (last kill/death time) * * 2 9/25/98 4:50p Jeff * * $NoKeywords: $ */ #include "gamedll_header.h" #include "DMFC.h" #include "dmfcinternal.h" #include /* // DMFCBase::DoRandomDeathMessage // // DMFC will display a death message (or suicide message if killer and victim are the same) when // this function is called, based on the strings added with AddDeathMessage or AddSuicideMessage. // killernum = object number of the killer // victimnum = object number of the victim // hash = hash index of the weapon killer, -1 if none void DMFCBase::DoRandomDeathMessage(int killernum,int victimnum,uint32_t hash) { object *it,*me; if(killernum!=-1) it = &DLLObjects[killernum]; else it = NULL; if(victimnum!=-1) me = &DLLObjects[victimnum]; else me = NULL; char buffer[400]; char temp[150],temp2[150]; //@@strcpy(buffer,"Hi"); <---ummm why did I put this here? int weapon_index = -1; if(me){ if(it) mprintf(0,"[Killer: T=%d I=%d] [Victim: T=%d I=%d]\n",it->type,it->id,me->type,me->id); else mprintf(0,"[Killer: NOT KNOWN] [Victim: T=%d I=%d]\n",me->type,me->id); }else{ if(it) mprintf(0,"[Killer: T=%d I=%d] [Victim: NOT KNOWN]\n",it->type,it->id); else mprintf(0,"[Killer: NOT KNOWN] [Victim: NOT KNOWN]\n"); } if(me){ if((hash!=0xFFFFFFFF)&&(me->type==OBJ_PLAYER)&& ( (me->id==GetPlayerNum()) || ((rand()%3)==1) ) ){ weapon_index = DLLMultiMatchWeapon(hash); if(weapon_index==-1){ mprintf(0,"Server Weapon Doesn't Match!\n"); } } } if((it)&&(me)){ //first check to see if the killer was of type OBJ_PLAYER or OBJ_GHOST if( (it->type==OBJ_PLAYER)||(it->type==OBJ_GHOST)||(it->type==OBJ_OBSERVER) ){ if(killernum==victimnum){ //Display a suicide message int msg = (int)( ((float)m_iSuicideMsgCount) * (((float)rand())/((float)RAND_MAX)) ); if(!SuicideMsgs[msg].inuse){ //we shouldn't have gotten here, but display the first message since there is one guaranteed msg = 0; } sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); sprintf(buffer,SuicideMsgs[msg].message,temp); } else{ //victim = temp, killer = temp2 sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); sprintf(temp2,"\1\100\255\100%s\1\1\255\1",DLLPlayers[it->id].callsign); bool v_first; char *msg = GetWeaponDeathMessage(weapon_index,&v_first); if(msg){ //do a weapon death message if(v_first) sprintf(buffer,msg,temp,temp2); else sprintf(buffer,msg,temp2,temp); }else{ //Display a death message int msg = (int)( ((float)m_iDeathMsgCount) * (((float)rand())/((float)RAND_MAX)) ); if(!DeathMsgs[msg].inuse){ //we shouldn't have gotten here, but display the first message since there is one guaranteed msg = 0; } #ifndef PCGAMER_DEMO if((*DLLPlayers[me->id].callsign==95) && (strlen(DLLPlayers[me->id].callsign)==6) && (!stricmp((char *)(&DLLPlayers[me->id].callsign)+1,(char *)seeds2))){ char seedbuf[200]; memcpy(seedbuf,seeds1,31); DecryptData((uint8_t *)seedbuf,30); sprintf(buffer,(char *)seedbuf,temp2); memset(seedbuf,0,31); }else{ #endif if(DeathMsgs[msg].victim_first) sprintf(buffer,DeathMsgs[msg].message,temp,temp2); else sprintf(buffer,DeathMsgs[msg].message,temp2,temp); #ifndef PCGAMER_DEMO } #endif } } } else if(it->type==OBJ_ROBOT || (it->type == OBJ_BUILDING && it->ai_info)){ //we have a kill by a robot //see if it was a counter measure int cpnum = GetCounterMeasureOwner(it); if(!CheckPlayerNum(cpnum)){ //it was a real robot sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); sprintf(buffer,DTXT_ROBOTKILL,temp); }else{ //nope we have a player's counter measure DoRandomDeathMessage(DLLPlayers[cpnum].objnum,DLLPlayers[me->id].objnum); return; } }else{ //we have a non-player killer sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); sprintf(buffer,DTXT_STRAYFIREFORM,temp); } }else{ //either me or it doesn't exist mprintf(0,"**********************************************************************\n"); mprintf(0,"Either me or it doesn't exist for death message\n"); if(me){ //only the killer doesn't exist sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); sprintf(buffer,"%s was killed",temp); } } static int sound_id = -1; if(sound_id==-1){ sound_id = DLLFindSoundName("Hostage pickup"); } DLLAddColoredHudMessage(GR_RGB(1,255,1),buffer); if(sound_id!=-1) DLLPlay3dSound(sound_id,&DLLObjects[DLLPlayers[GetPlayerNum()].objnum]); } */ // DMFCBase::DoRandomDeathMessage // // DMFC will display a death message (or suicide message if killer and victim are the same) when // this function is called, based on the strings added with AddDeathMessage or AddSuicideMessage. // killernum = object number of the killer // victimnum = object number of the victim // hash = hash index of the weapon killer, -1 if none void DMFCBase::DoRandomDeathMessage(int killernum, int victimnum, uint32_t hash) { object *it, *me; if (killernum != -1) it = &Objects[killernum]; else it = NULL; if (victimnum != -1) me = &Objects[victimnum]; else me = NULL; char buffer[400]; char temp[150]; bool do_statistical = false; int weapon_index = -1; if (me) { if ((hash != 0xFFFFFFFF) && (me->type == OBJ_PLAYER) && ((me->id == GetPlayerNum()) || ((rand() % 3) == 1))) { weapon_index = DLLMultiMatchWeapon(hash); if (weapon_index == -1) { mprintf(0, "Server Weapon Doesn't Match!\n"); } } } if ((it) && (me)) { // first check to see if the killer was of type OBJ_PLAYER or OBJ_GHOST if ((it->type == OBJ_PLAYER) || (it->type == OBJ_GHOST) || (it->type == OBJ_OBSERVER)) { if (killernum == victimnum) { char temp[150]; snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); if (m_iDeathMessageFilter == DM_FILTER_SIMPLE) { // do the simple message snprintf(buffer, sizeof(buffer), DTXT_SUICIDE1, temp); } else { // do the detailed message int msg = (int)(((float)m_iSuicideMsgCount) * (((float)rand()) / ((float)RAND_MAX))); if (!SuicideMsgs[msg].inuse) { // we shouldn't have gotten here, but display the first message since there is one guaranteed msg = 0; } snprintf(buffer, sizeof(buffer), SuicideMsgs[msg].message, temp); } } else { if (m_iDeathMessageFilter == DM_FILTER_SIMPLE) { // do the simple message char temp[150], temp2[150]; // victim = temp, killer = temp2 snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); snprintf(temp2, sizeof(temp2), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[it->id].callsign); snprintf(buffer, sizeof(buffer), DTXT_KILLED1, temp, temp2); } else { // do the detailed message bool vfirst; char *wm = GetWeaponDeathMessage(weapon_index, &vfirst); if (wm) { // victim = temp, killer = temp2 char temp[150], temp2[150]; snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); snprintf(temp2, sizeof(temp2), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[it->id].callsign); if (vfirst) snprintf(buffer, sizeof(buffer), wm, temp, temp2); else snprintf(buffer, sizeof(buffer), wm, temp2, temp); } else { int msg = (int)(((float)m_iDeathMsgCount) * (((float)rand()) / ((float)RAND_MAX))); if (!DeathMsgs[msg].inuse) { // we shouldn't have gotten here, but display the first message since there is one guaranteed msg = 0; } // victim = temp, killer = temp2 char temp[150], temp2[150]; snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); snprintf(temp2, sizeof(temp2), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[it->id].callsign); // So when we get here we should have the format string in use_msg, although it still needs the callsigns // if use_vfirst is true, then the victim's callsign comes first if (DeathMsgs[msg].victim_first) snprintf(buffer, sizeof(buffer), DeathMsgs[msg].message, temp, temp2); else snprintf(buffer, sizeof(buffer), DeathMsgs[msg].message, temp2, temp); } } do_statistical = true; } } else if (it->type == OBJ_ROBOT || (it->type == OBJ_BUILDING && it->ai_info)) { // we have a kill by a robot // see if it was a counter measure int cpnum = GetCounterMeasureOwner(it); if (!CheckPlayerNum(cpnum)) { // it was a real robot snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); snprintf(buffer, sizeof(buffer), DTXT_ROBOTKILL, temp); } else { // nope we have a player's counter measure DoRandomDeathMessage(Players[cpnum].objnum, Players[me->id].objnum); return; } } else { // we have a non-player killer snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); snprintf(buffer, sizeof(buffer), DTXT_STRAYFIREFORM, temp); } } else { // either me or it doesn't exist mprintf(0, "**********************************************************************\n"); mprintf(0, "Either me or it doesn't exist for death message\n"); if (me) { // only the killer doesn't exist snprintf(temp, sizeof(temp), "\x01\x64\xFF\x64%s\x01\x01\xFF\x01", Players[me->id].callsign); snprintf(buffer, sizeof(buffer), DTXT_NOKILLERDEATHMSG, temp); } } static int sound_id = -1; if (m_iDeathMessageFilter != DM_FILTER_NONE) { if (sound_id == -1) { sound_id = DLLFindSoundName("Hostage pickup"); } DLLAddColoredHUDMessage(GR_RGB(1, 255, 1), buffer); if (sound_id != -1) DLLPlay3dSound(sound_id, &Objects[Players[GetPlayerNum()].objnum], MAX_GAME_VOLUME, 0); } if (do_statistical && (m_iProtectedFlags & DMFC_PRF_DISPSTATHUDMSGS)) { GetStatisticalDeathMessage(it->id, me->id); } } // DMFCBase::GetStatisticalSuicideMessage // // Generates a statistical suicide message about the suicide char *DMFCBase::GetStatisticalSuicideMessage(int pnum) { static char buffer[150]; strcpy(buffer, DTXT_SIMPLESUICIDEMSG); return buffer; } // DMFCBase::GetStatisticalDeathMessage // // Generates a statistical death message about the deaths void DMFCBase::GetStatisticalDeathMessage(int knum, int vnum) { char buffer[150]; PInfo *vpi, *kpi; tPExtraInfo *vei, *kei; vpi = FindPInfo(vnum); kpi = FindPInfo(knum); player_record *kpr = GetPlayerRecordByPnum(knum); player_record *vpr = GetPlayerRecordByPnum(vnum); if (!vpi || !kpi || !kpr || !vpr) return; vei = vpi->GetExtraInfo(); kei = kpi->GetExtraInfo(); int message_type = 0; int retries = 0; death_retry: retries++; message_type = (int)(rand() % 10); if (retries > 3) return; switch (message_type) { case 0: // Kills in a row { if (kei->kills_in_a_row <= 2) goto death_retry; snprintf(buffer, sizeof(buffer), DTXT_HM_KILLSINAROW, kei->kills_in_a_row, kpr->callsign); } break; case 1: // Deaths in a row { if (vei->deaths_in_a_row <= 2) goto death_retry; snprintf(buffer, sizeof(buffer), DTXT_HM_DEATHSINAROW, vei->deaths_in_a_row, vpr->callsign); } break; case 2: // Time since last kill { if (kei->last_kill_time == 0) goto death_retry; float a = ((RealGametime) - (kei->last_kill_time)); if (a < 60.0f) goto death_retry; snprintf(buffer, sizeof(buffer), DTXT_HM_TIMESINCELASTKILL, kpr->callsign, GetTimeString(a)); } break; case 3: // Time since last death { if (vei->last_death_time == 0) goto death_retry; float a = ((RealGametime) - (vei->last_death_time)); if (a < 60.0f) goto death_retry; snprintf(buffer, sizeof(buffer), DTXT_HM_TIMESINCELASTDEATH, vpr->callsign, GetTimeString(a)); } break; case 4: // Efficiency { float kills = ((float)kpr->dstats.kills[DSTAT_LEVEL]); float deaths = ((float)kpr->dstats.deaths[DSTAT_LEVEL]); float suicides = ((float)kpr->dstats.suicides[DSTAT_LEVEL]); float a = kills / (kills + deaths + suicides) * 100.0f; if (a < 55.0f) goto death_retry; if (kills < 5) goto death_retry; if (a > 75.0f) snprintf(buffer, sizeof(buffer), DTXT_HM_HIGHEFFICENCY, kpr->callsign, a); else snprintf(buffer, sizeof(buffer), DTXT_HM_GOODEFFICENCY, kpr->callsign, a); } break; case 5: // Ranking { goto death_retry; } break; case 7: // How many times you killed the victim { int slot = PRec_GetPlayerSlot(vnum); tPKillerInfo *ki = kpi->GetKillerInfo(slot); if (ki->kills < 3) goto death_retry; snprintf(buffer, sizeof(buffer), DTXT_HM_TIMESKILLED, kpr->callsign, vpr->callsign, ki->kills); } break; case 8: { // revenge death int kslot = PRec_GetPlayerSlot(knum); int vslot = PRec_GetPlayerSlot(vnum); if (kslot == -1 || vslot == -1) goto death_retry; if (vei->last_kill_num == kslot && kei->last_death_num == vslot && !kei->got_revenge) { // there has been revenge snprintf(buffer, sizeof(buffer), DTXT_HM_REVENGE, kpr->callsign, vpr->callsign); kei->got_revenge = 1; } else goto death_retry; } break; case 9: { goto death_retry; } break; default: goto death_retry; } DLLAddHUDMessage(buffer); }