Forum

Engine hack to make coop per player scores

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Engine hack to make coop per player scores

Postby Baker » Mon Aug 11, 2014 7:05 pm

Here is the story.

I'm strongly considering enabling TAB during coop to display the full scoreboard because otherwise in coop it is incredibly difficult:
(1) to see who has connected
(2) to see what there colors are, etc. (to tell them to change color so they don't take friendly fire with "teamplay 1").

However, it is annoying seeing ZERO as everyone's score. I'd prefer per player monster kills (yeah, I know coop isn't competitive).

Since there are HUNDREDS of single player releases with their own QuakeC, I'm thinking of jacking into ClientObituary or Killed (Killed seems more logical) ...
(1) Only do this if deathmatch is 0 and coop is non-zero. Something like DMSP won't be affected, DMSP uses deathmatch scoreboard.
(2) Seeing if the attacker score increased -- if it did, could be some sort of mod.
(3) If it didn't increase, make it increase.

combat.qc:Killed
Code: Select all
void(entity targ, entity attacker) Killed =
{
   local entity oself;

   oself = self;
   self = targ;
   
...
// bump the monster counter
   if (self.flags & FL_MONSTER)
   {
      killed_monsters = killed_monsters + 1;
      WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
   }

   ClientObituary(self, attacker); // <----------- called here
   
   self.takedamage = DAMAGE_NO;
...
};


client.qc:ClientObituary
Code: Select all
void(entity targ, entity attacker) ClientObituary =
{
   local   float rnum;
   local   string deathstring, deathstring2;
   rnum = random();

   if (targ.classname == "player")
   {
      if (attacker.classname == "teledeath")
      {
         bprint (targ.netname);
         bprint (" was telefragged by ");
         bprint (attacker.owner.netname);
         bprint ("\n");

         attacker.owner.frags = attacker.owner.frags + 1;
         return;
      }

      if (attacker.classname == "teledeath2")
      {
         bprint ("Satan's power deflects ");
         bprint (targ.netname);
         bprint ("'s telefrag\n");

         targ.frags = targ.frags - 1;
         return;
      }

      if (attacker.classname == "player")
      {
         if (targ == attacker)
         {
            // killed self
            attacker.frags = attacker.frags - 1;
            bprint (targ.netname);
            
            if (targ.weapon == 64 && targ.waterlevel > 1)
            {
               bprint (" discharges into the water.\n");
               return;
            }
            if (targ.weapon == IT_GRENADE_LAUNCHER)
               bprint (" tries to put the pin back in\n");
            else
               bprint (" becomes bored with life\n");
            return;
         }
         else if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) )
         {
            if (rnum < 0.25)
               deathstring = " mows down a teammate\n";
            else if (rnum < 0.50)
               deathstring = " checks his glasses\n";
            else if (rnum < 0.75)
               deathstring = " gets a frag for the other team\n";
            else
               deathstring = " loses another friend\n";
            bprint (attacker.netname);
            bprint (deathstring);
            attacker.frags = attacker.frags - 1;
            return;
         }
         else
         {
            attacker.frags = attacker.frags + 1;

            rnum = attacker.weapon;
            if (rnum == IT_AXE)
            {
               deathstring = " was ax-murdered by ";
               deathstring2 = "\n";
            }
            if (rnum == IT_SHOTGUN)
            {
               deathstring = " chewed on ";
               deathstring2 = "'s boomstick\n";
            }
            if (rnum == IT_SUPER_SHOTGUN)
            {
               deathstring = " ate 2 loads of ";
               deathstring2 = "'s buckshot\n";
            }
            if (rnum == IT_NAILGUN)
            {
               deathstring = " was nailed by ";
               deathstring2 = "\n";
            }
            if (rnum == IT_SUPER_NAILGUN)
            {
               deathstring = " was punctured by ";
               deathstring2 = "\n";
            }
            if (rnum == IT_GRENADE_LAUNCHER)
            {
               deathstring = " eats ";
               deathstring2 = "'s pineapple\n";
               if (targ.health < -40)
               {
                  deathstring = " was gibbed by ";
                  deathstring2 = "'s grenade\n";
               }
            }
            if (rnum == IT_ROCKET_LAUNCHER)
            {
               deathstring = " rides ";
               deathstring2 = "'s rocket\n";
               if (targ.health < -40)
               {
                  deathstring = " was gibbed by ";
                  deathstring2 = "'s rocket\n" ;
               }
            }
            if (rnum == IT_LIGHTNING)
            {
               deathstring = " accepts ";
               if (attacker.waterlevel > 1)
                  deathstring2 = "'s discharge\n";
               else
                  deathstring2 = "'s shaft\n";
            }
            bprint (targ.netname);
            bprint (deathstring);
            bprint (attacker.netname);
            bprint (deathstring2);
         }
         return;
      }
      else
      {
         targ.frags = targ.frags - 1;
         bprint (targ.netname);

         // killed by a montser?
         if (attacker.flags & FL_MONSTER)
         {
            if (attacker.classname == "monster_army")
               bprint (" was shot by a Grunt\n");
            if (attacker.classname == "monster_demon1")
               bprint (" was eviscerated by a Fiend\n");
            if (attacker.classname == "monster_dog")
               bprint (" was mauled by a Rottweiler\n");
            if (attacker.classname == "monster_dragon")
               bprint (" was fried by a Dragon\n");
            if (attacker.classname == "monster_enforcer")
               bprint (" was blasted by an Enforcer\n");
            if (attacker.classname == "monster_fish")
               bprint (" was fed to the Rotfish\n");
            if (attacker.classname == "monster_hell_knight")
               bprint (" was slain by a Death Knight\n");
            if (attacker.classname == "monster_knight")
               bprint (" was slashed by a Knight\n");
            if (attacker.classname == "monster_ogre")
               bprint (" was destroyed by an Ogre\n");
            if (attacker.classname == "monster_oldone")
               bprint (" became one with Shub-Niggurath\n");
            if (attacker.classname == "monster_shalrath")
               bprint (" was exploded by a Vore\n");
            if (attacker.classname == "monster_shambler")
               bprint (" was smashed by a Shambler\n");
            if (attacker.classname == "monster_tarbaby")
               bprint (" was slimed by a Spawn\n");
            if (attacker.classname == "monster_vomit")
               bprint (" was vomited on by a Vomitus\n");
            if (attacker.classname == "monster_wizard")
               bprint (" was scragged by a Scrag\n");
            if (attacker.classname == "monster_zombie")
               bprint (" joins the Zombies\n");

            return;
         }

         // tricks and traps
         if (attacker.classname == "explo_box")
         {
            bprint (" blew up\n");
            return;
         }
         if (attacker.solid == SOLID_BSP && attacker != world)
         {   
            bprint (" was squished\n");
            return;
         }
         if (attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter")
         {
            bprint (" was spiked\n");
            return;
         }
         if (attacker.classname == "fireball")
         {
            bprint (" ate a lavaball\n");
            return;
         }
         if (attacker.classname == "trigger_changelevel")
         {
            bprint (" tried to leave\n");
            return;
         }

         // in-water deaths
         rnum = targ.watertype;
         if (rnum == -3)
         {
            if (random() < 0.5)
               bprint (" sleeps with the fishes\n");
            else
               bprint (" sucks it down\n");
            return;
         }
         else if (rnum == -4)
         {
            if (random() < 0.5)
               bprint (" gulped a load of slime\n");
            else
               bprint (" can't exist on slime alone\n");
            return;
         }
         else if (rnum == -5)
         {
            if (targ.health < -15)
            {
               bprint (" burst into flames\n");
               return;
            }
            if (random() < 0.5)
               bprint (" turned into hot slag\n");
            else
               bprint (" visits the Volcano God\n");
            return;
         }

         // fell to their death?
         if (targ.deathtype == "falling")
         {
            targ.deathtype = "";
            bprint (" fell to his death\n");
            return;
         }

         // hell if I know; he's just dead!!!
         bprint (" died\n");
      }
   }
};


Caveats/Consideration:
(1) Some monsters aren't killed by players.
(2) A Chthon or Shub? I wonder how that even works for the QuakeC "attacker".

Idea for a rainy day ...
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Engine hack to make coop per player scores

Postby frag.machine » Mon Aug 11, 2014 8:14 pm

Coop scores have very mod-oriented criterias.
I'd suggest instead you simply don't show the individual player points column if coop == 1 (but you may assume that if coop > 1 then it's OK to show the individual scores).
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Re: Engine hack to make coop per player scores

Postby Spike » Mon Aug 11, 2014 9:15 pm

meh, just start killing other players, then it won't be 0.
rewarding players only for killing blows still isn't in the spirit of coop.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Engine hack to make coop per player scores

Postby Baker » Mon Aug 11, 2014 9:19 pm

Spike wrote:meh, just start killing other players, then it won't be 0.


Hadn't thought of that. Also -1 frag per suicide/ lava death / managed to squish self.

Yeah, the killing blow thing but then that is how DM frags work :D
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Engine hack to make coop per player scores

Postby Baker » Wed Aug 13, 2014 12:02 am

And why not?

Image

Goes something like this ...

Code: Select all
#ifdef SUPPORTS_COOP_ENHANCEMENTS
extern dfunction_t *pr_handles_killed;
extern int pr_in_killed;
void PR_In_Killed_Check (dfunction_t *f, int depth);
void PR_In_Killed_Finish (void);
#endif // SUPPORTS_COOP_ENHANCEMENTS

#ifdef SUPPORTS_COOP_ENHANCEMENTS
   if (pr_handles_killed && f == pr_handles_killed && !pr_in_killed)
      PR_In_Killed_Check (f, pr_depth);
#endif // SUPPORTS_COOP_ENHANCEMENTS


#ifdef SUPPORTS_COOP_ENHANCEMENTS
   if (pr_in_killed && pr_depth == pr_in_killed)
      PR_In_Killed_Finish ();
#endif // SUPPORTS_COOP_ENHANCEMENTS

#ifdef SUPPORTS_COOP_ENHANCEMENTS
dfunction_t *pr_handles_killed;      // Baker: coop fake scoreboard

int    pr_in_killed; // Reset this on progs load in case of pr error
int    pr_in_killed_start_frags;
eval_t   *pr_in_killed_murderer_frags_ev;

void PR_In_Killed_Check (dfunction_t *f, int depth)
{
   // Baker: Find out who attacker is and if we need to increment
   // Save attackers score and increment it upon leaving function.

   // parm 1 is target
   // parm 2 is killer
   int the_dead_addy = ((int *)pr_globals)[f->parm_start];
   int the_murderer_addy = ((int *)pr_globals)[f->parm_start + 1];

   // Determine the entity number
   int the_dead_entnum = the_dead_addy / pr_edict_size;
   int the_murderer_entnum = the_murderer_addy / pr_edict_size;
   edict_t *dead_edict = EDICT_NUM(the_dead_entnum);
   edict_t *murderer_edict = EDICT_NUM(the_murderer_entnum);
   eval_t   *dead_flags_ev = GETEDICTFIELDVALUE(dead_edict, eval_flags);
   eval_t   *murderer_frag_ev = GETEDICTFIELDVALUE(murderer_edict, eval_frags);
   qboolean dead_is_monster = dead_flags_ev ? (int)dead_flags_ev->_float & FL_MONSTER : false;
   int murderer_start_frags = murderer_frag_ev ? (int)murderer_frag_ev->_float : 99999;

   // Make sure dead entity is a monster
   if (!dead_is_monster)
      return;

   // Make sure the killer is a player
   if (the_murderer_entnum < 1 || svs.maxclients < the_murderer_entnum)
      return;

   // Make sure isn't a suicide or self-kill
   if (the_murderer_entnum == the_dead_entnum)
      return;

   pr_in_killed = depth;
   pr_in_killed_murderer_frags_ev = murderer_frag_ev;
   pr_in_killed_start_frags = murderer_frag_ev->_float;
   Con_SafePrintf ("Entered killed\n");
}

void PR_In_Killed_Finish (void)
{
   pr_in_killed = 0;
   Con_SafePrintf ("Exit killed\n");

   if (!pr_in_killed_murderer_frags_ev)
      return; // Error?
   
   // Frags didn't change, so we will do it.
   if (pr_in_killed_murderer_frags_ev->_float == pr_in_killed_start_frags)
      pr_in_killed_murderer_frags_ev->_float ++;
}

dfunction_t* PR_Check_Coop_Kills (void)
{
   // Reset this on progs load in case of pr error
   pr_in_killed = 0;
   if (sv_coop_enhancements.value && coop.value /*pr_global_struct->coop hasn't been set yet */)
   {
      dfunction_t* testfunc = PR_FindFunction ("Killed", PRFF_NOBUILTINS);
      if (testfunc && testfunc->numparms == 2 && testfunc->parm_size[0] == 1 && testfunc->parm_size[1])
      {
         Con_SafePrintf ("Per player coop scores initialized\n");
         return testfunc; // Looks good!
      }
   }
   return NULL;
}
#endif // SUPPORTS_COOP_ENHANCEMENTS

#ifdef SUPPORTS_COOP_ENHANCEMENTS
   pr_handles_killed = PR_Check_Coop_Kills ();
#endif // SUPPORTS_COOP_ENHANCEMENTS

#ifdef SUPPORTS_COOP_ENHANCEMENTS
   if (sv_coop_enhancements.value &&  cmd_source != src_command && cl.gametype != GAME_DEATHMATCH && coop.value && teamplay.value && sv.active)
   {
      if (isDedicated)
         bottom = 12; // Dedicated, you get yellow I guess.
      else bottom = (int)cl_color.value & 15;
   }
#endif // SUPPORTS_COOP_ENHANCEMENTS
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest