Developer Store
Support
Member Forums

Screenshots
FAQ
Documentation
License
Known Issues
Downloads

MMOWorkshop BACK!

PyTorque
TGB Web Browser


karvermimic

hallsofvalhalla - Been A while
xapken - Wizards and Champions
J.C. Smith - The Repopulation - 0.5.2 Build Notes
EmpireGames - Elementals in Rise of Heroes
Empire Games - WarPath.
jaidurn - Warcall 0.0.1.0 build notes
medafor - Final detailing of models
Thamior - Presence system GONE! [May 25th 2009)]
... MORE BLOGS!

Building a MMO
Wow I had no idea....
Quest Remnants of Chaos
Checking to see if anyone is sti...
Well here I am again :)
T3D and MMOKit
afx 2.0
Terrain specularity [video]
Torque T3D MMO Kit
[3dFoin] Dragon Bug

Introduction

This resource was first made possible by Stephen Lujan and some additional resources made available by Graustern. The original resource can be found here : http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=13246. You will actually want to use this if you are using TGE since the version I am going to post is for TGEA 1.7.1. You will need to replace this function.

You can find the original thread on this forum here: http://www.mmoworkshop.com/trac/mom/phpbb?page=viewtopic.php&t=230&start=15. There is also a picture of the resource in action so you can see what it should do.

ConsoleMethod( GuiShapeDamageHud, addPopup, bool, 6, 6, "int time, ColorF color, int object, string text")

With the one posted for TGEA, but the rest is perfectly fine since the TGEA version only has some conversions to work with the new graphics layer. So lets get started.

Making the required C++ changes

If you are using TGE follow the directions for the file guiShapeDamageHud.cc at the very beginning of this resource. Add it to your project as mentioned and compile to make sure it works.

http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=13246

Also, if you are using TGE make sure to put in this fix at the beginning of this thread.

http://www.mmoworkshop.com/trac/mom/phpbb?page=viewtopic.php&t=230&postdays=0&postorder=asc&start=15

If you are using TGEA you will want to use this version and place it in engine/gui/game and name it guiShapeDamageHud.cpp

//-----------------------------------------------------------------------------
// gui/game/guiShapeDamageHud.cc
// by Stephen Lujan
// Converted to TGEA by Xerves with a fix by DBS
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "console/console.h"
#include "gui/core/guiControl.h"
#include "gui/3d/guiTSControl.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "T3D/shapeBase.h"
#include "T3D/gameConnection.h"
#include "gfx/gfxDevice.h"

//----------------------------------------------------------------------------
/// Temporarily displays damage or other text above shape objects.
///
/// This GUI control must be a child of a TSControl, and a server connection
/// and control object must be present.
///
/// This is a stand-alone control and relies only on the standard base GuiControl.
class GuiShapeDamageHud : public GuiControl 
{
   typedef GuiControl Parent;
   bool onInputEvent(const InputEventInfo &evt){return getParent()->onInputEvent(evt);}
   bool pointInControl(const Point2I& parentCoordPoint){return false;} 
   bool cursorInControl(){return false;}
public:
	struct damagePopup
	{
		ShapeBase*			mShape;
		S32					mTime;
		S32					mStartTime;
		ColorF				mTextColor;
		StringTableEntry	mText;
	};

	Vector<damagePopup> mPopups;

protected:
	// field data
   ColorF   mFillColor;
   ColorF   mFrameColor;
   F32      mStartVerticalOffset;
	F32      mEndVerticalOffset;
   F32      mDistanceFade;
   bool     mShowFrame;
   bool     mShowFill;
	U32		mLastTime;

   void drawDamage( Point2I offset, damagePopup *popup, F32 opacity);

public:
   GuiShapeDamageHud();

   // GuiControl
   virtual void onRender(Point2I offset, const RectI &updateRect);

   static void initPersistFields();
   DECLARE_CONOBJECT( GuiShapeDamageHud );
};


//-----------------------------------------------------------------------------

IMPLEMENT_CONOBJECT(GuiShapeDamageHud);

/// Default distance for object's information to be displayed.
static const F32 cDefaultVisibleDistance = 500.0f;

GuiShapeDamageHud::GuiShapeDamageHud()
{
   mFillColor.set( 0.25, 0.25, 0.25, 0.25 );
   mFrameColor.set( 0, 1, 0, 1 );
   mShowFrame = mShowFill = true;
   mStartVerticalOffset = 1;
	mEndVerticalOffset = 40;
   mDistanceFade = 0.1f;
	mLastTime = Platform::getVirtualMilliseconds();
}

void GuiShapeDamageHud::initPersistFields()
{
   Parent::initPersistFields();
   addGroup("Colors");
   addField( "fillColor",  TypeColorF, Offset( mFillColor, GuiShapeDamageHud ) );
   addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeDamageHud ) );
   endGroup("Colors");

   addGroup("Misc");
   addField( "showFill",   TypeBool, Offset( mShowFill, GuiShapeDamageHud ) );
   addField( "showFrame",  TypeBool, Offset( mShowFrame, GuiShapeDamageHud ) );
   addField( "startingVerticalOffset", TypeF32, Offset( mStartVerticalOffset, GuiShapeDamageHud ) );
   addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeDamageHud ) );
   endGroup("Misc");
}

//----------------------------------------------------------------------------
/// Core rendering method for this control.
///
///
/// Information is offset from the center of the object's bounding box,
/// unless the object is a PlayerObjectType, in which case the eye point
/// is used.
///
/// @param   updateRect   Extents of control.
void GuiShapeDamageHud::onRender( Point2I, const RectI &updateRect)
{
	U32 time = Platform::getVirtualMilliseconds();
	U32 tmask;
	S32 deltaTime= time - mLastTime;
	//Con::errorf("%d = %d - %d", deltaTime, time, mLastTime);
	mLastTime = time;

	//no impossibilities extending life of popups
	if (deltaTime < 0)
		deltaTime = 0;
	//if something is stuck lets not delete popups all at once
	if (deltaTime > 500)
		deltaTime = 500;

   // Background fill first
   if (mShowFill)
      GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor);

   // Must be in a TS Control
   GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
   if (!parent) return;

   // Must have a connection and control object
   GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return;

   ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
   if (!control)
      return;

   // Get control camera info
   MatrixF cam;
   Point3F camPos;
   VectorF camDir;
   conn->getControlCameraTransform(0,&cam);
   cam.getColumn(3, &camPos);
   cam.getColumn(1, &camDir);

   F32 camFov;
   conn->getControlCameraFov(&camFov);
   camFov = mDegToRad(camFov) / 2;
	// the next line is optional just to widen the viewcone a little bit for when 40% of a player is visible
   // but their center isn't. Increase the viewcone by 5 degrees:
   camFov += 0.08f; //mDegToRad(5) / 2;
   F32 cosCamFov = mCos(camFov);

   // Visible distance info & name fading
   F32 visDistance = gClientSceneGraph->getVisibleDistance();
   F32 visDistanceSqr = visDistance * visDistance;
   F32 fadeDistance = visDistance * mDistanceFade;

   // Collision info. We're going to be running LOS tests and we
   // don't want to collide with the control object.
   static U32 losMask = TerrainObjectType | InteriorObjectType; //| ShapeBaseObjectType;
   control->disableCollision();

	Vector<damagePopup>::iterator i;
   for(i = mPopups.begin(); i != mPopups.end(); i++)
   {
      damagePopup *current = &(*i);
      if( !current )
		{
			//Con::errorf("Damage popup doesn't exist!");
			continue;
		}

		if (current->mTime < 0)
		{
			//Con::errorf("deleting expired popup at time %d", time);
			mPopups.erase(i);
			i--;
			continue;
		}

		if( !current->mShape )
		{	
			//Con::errorf("Damage popup has a shape that doesn't exist.");
			//AssertFatal(current->mShape, "this shape doesn't exist");
			continue;
		}
        tmask = current->mShape->getType();
		if( !(current->mShape->getType() & ShapeBaseObjectType))
		{	
			Con::errorf("Damage popup has a shape that isn't derived from shapeBase.");
			continue;
		} 
		
		//Con::errorf("decreasing popup life %d - %d", current->mTime, deltaTime);
		current->mTime -= deltaTime;
		// Target pos to test, if it's a player run the LOS to his eye
		// point, otherwise we'll grab the generic box center.
		Point3F shapePos;
		if (current->mShape->getType() & PlayerObjectType) 
		{
			MatrixF eye;

			// Use the render eye transform, otherwise we'll see jittering
			// Stephen: why always access violations here? grrrrrr
			current->mShape->getRenderEyeTransform(&eye);
			eye.getColumn(3, &shapePos);
		}
		else 
		{
			 // Use the render transform instead of the box center
			 // otherwise it'll jitter.
			MatrixF srtMat = current->mShape->getRenderTransform();
			srtMat.getColumn(3, &shapePos);
		}
		VectorF shapeDir = shapePos - camPos;

		// test early to see if it's behind us.
      // no need to normalize shapeDir for this,
      // we'll normalize it later if needed.
      F32 dot = mDot(shapeDir, camDir);
      if (dot < 0)
		{
			//Con::errorf("Damage popup was behind camera.");
         continue;
		}

      // Test to see if it's in range
      F32 shapeDist = shapeDir.lenSquared();
      if (shapeDist == 0 || shapeDist > visDistanceSqr)
		{
			//Con::errorf("Damage popup was not in range.");
         continue;
		}
      shapeDist = mSqrt(shapeDist);

      // Test to see if it's within our viewcone, this test doesn't
      // actually match the viewport very well, should consider
      // projection and box test.
      dot /= shapeDist;
      if (dot < cosCamFov)
		{
			//Con::errorf("Damage popup was not in viewcone.");
         continue;
		}

      // Test to see if it's behind something, and we want to
      // ignore anything it's mounted on when we run the LOS.
      RayInfo info;
      current->mShape->disableCollision();
      ShapeBase *mount = current->mShape->getObjectMount();

      if (mount)
         mount->disableCollision();

      bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info);
      current->mShape->enableCollision();

      if (mount)
         mount->enableCollision();

      if (!los)
		{
			//Con::errorf("Damage popup did not have line of sight.");
         continue;
		}


      // Project the shape pos into screen space and calculate
      // the distance opacity used to fade the labels into the
      // distance.
      Point3F projPnt;
      //shapePos.z += mVerticalOffset;

		//perhaps instantiating these as guimembers instead of every message every frame would be better
		F32 heightMultiplier= ((F32)current->mTime / (F32)current->mStartTime);
		F32 pixelHeight= ((1.0-heightMultiplier)*mEndVerticalOffset) + ((heightMultiplier)*mStartVerticalOffset);
		//Con::errorf("before projPnt.y = %.2f", projPnt.y);
		
      if (!parent->project(shapePos, &projPnt))
		{
			//Con::errorf("Damage popup could not project to screen coordinates.");
         continue;
		}
		projPnt.y -= pixelHeight;
		if (projPnt.y < 1)
			projPnt.y = 1;
		/*
		projPnt.x -= 0.6*pixelHeight;
		if (projPnt.x < 1)
			projPnt.x = 1;
		*/
		//Con::errorf("heightMultiplier = %.2f  pixelHeight = %.2f after projPnt.y = %.2f", heightMultiplier, pixelHeight, projPnt.y);
      F32 opacity = (shapeDist < fadeDistance)? 1.0:
         1.0 - (shapeDist - fadeDistance) / (visDistance - fadeDistance);
		//spend last half of life fading out
		F32 ageOpacity = (2.0*(F32)current->mTime / (F32)current->mStartTime);
		if (ageOpacity < opacity)
			opacity= ageOpacity;
      // Render the text
      drawDamage(Point2I((S32)projPnt.x, (S32)projPnt.y),current,opacity);
   }

   // Restore control object collision
   control->enableCollision();

}

//----------------------------------------------------------------------------
/// Render popup text.
///
/// Helper function for GuiShapeDamageHud::onRender
///
/// @param   offset  Screen coordinates to render name label. (Text is centered
///                  horizontally about this location, with bottom of text at
///                  specified y position.)
/// @param   popup	the damage popup struct
/// @param   opacity Opacity of name (a fraction).
void GuiShapeDamageHud::drawDamage(Point2I offset, damagePopup *popup, F32 opacity)
{
	//Con::errorf("executing GuiShapeDamageHud::drawDamage() at shape %d at time %d",popup->mShape->getId(), Platform::getVirtualMilliseconds());
   // Center the name
   offset.x -= mProfile->mFont->getStrWidth((const UTF8 *)popup->mText) / 2;
   offset.y -= mProfile->mFont->getHeight();

   // Deal with opacity and draw.
   popup->mTextColor.alpha = opacity;
   GFX->getDrawUtil()->setBitmapModulation(popup->mTextColor);
   GFX->getDrawUtil()->drawText(mProfile->mFont, offset, popup->mText);
   GFX->getDrawUtil()->clearBitmapModulation();
}

//Modified to work with the kit.  This function should work in both TGE and TGEA
ConsoleMethod( GuiShapeDamageHud, addPopup, bool, 6, 6, "int time, ColorF color, int object, string text")
{
	S32 ghostID = dAtoi(argv[4]);
	GameConnection* conn = GameConnection::getConnectionToServer();
    if (!conn)
      return false;
	NetObject* shape = conn->resolveGhost(ghostID);


	// Allocate a new popup.s
	GuiShapeDamageHud::damagePopup newPopup;
	// PARSE SHIZZLE
	newPopup.mTime = dAtoi(argv[2]);
	newPopup.mStartTime = newPopup.mTime;
	dSscanf(argv[3], "%f %f %f", &newPopup.mTextColor.red, &newPopup.mTextColor.green, &newPopup.mTextColor.blue);
	newPopup.mShape = static_cast<ShapeBase*> (shape); //(conn->findObject(argv[4]));
	if (!newPopup.mShape)
	{
		Con::errorf("New damage popup couldn't find the shape below! Check guiShapeDamageHud.cc");
		Con::errorf(argv[4]);
		return false;
	}
	newPopup.mText= StringTable->insert(argv[5]);

	// Store it in the list at the back to get rendered last
	object->mPopups.push_back(newPopup);
	//for now i'll assume it worked
	return true;
}

After you are done adding that to TGEA compile and make sure there are no errors. If you are using TGE replace the addPopup ConsoleMethod? with the one posted above. Make sure the TGE version compiled and if it does everything should be fine.

Python Changes

simmind.py

Before getWayPoints_r add this function

    #Gets the ghost ID of the target (checks the player connection on both entries)
    def remote_getGhostID(self,id,target): 
        conn = self.playerConnections.get(id,None)
        if not conn:
            conn = self.playerConnections.get(target,None)
        if not conn:
            return -1
            
        return conn.getGhostIndex(target)

This is called by the player's world connection to get the ghost index from the zone by passing the zone's ID for that target.

combat.py

Find the following code

                # If the attacker is a Player, then inform the attacker that the
                # attack was blocked.
                if attacker.player:
                    attacker.player.sendGameText(RPG_MSG_GAME_YELLOW,"%s blocks %s's attack!\\n"%(defender.name,attacker.name))

                # If the defender was a Player, then inform the defender of the
                # successful block.
                if defender.player:
                    defender.player.sendGameText(RPG_MSG_GAME_YELLOW,"%s blocks %s's attack!\\n"%(defender.name,attacker.name))

After it add the following

                #Send TextPopups 
	        if attacker and attacker.player and attacker.player.mind: 
		    attacker.player.sendTextPopup(attacker, defender,"Block")	
                if defender and defender.player and defender.player.mind: 
		    defender.player.sendTextPopup(attacker, defender,"Block") 
	        #End TextPopups 

A few more lines down find the following

                # If the attacker is a Player, then inform the attacker that the
                # attack was dodged.
                if attacker.player:
                    attacker.player.sendGameText(RPG_MSG_GAME_YELLOW,"%s dodges %s's attack!\\n"%(defender.name,attacker.name))

                # If the defender was a Player, then inform the defender of the
                # successful dodge.
                if defender.player:
                    defender.player.sendGameText(RPG_MSG_GAME_YELLOW,"%s dodges %s's attack!\\n"%(defender.name,attacker.name))

After it add

                #Send TextPopups 
	        if attacker and attacker.player and attacker.player.mind: 
		    attacker.player.sendTextPopup(attacker, defender,"Dodge")	
                if defender and defender.player and defender.player.mind: 
		    defender.player.sendTextPopup(attacker, defender,"Dodge") 
	        #End TextPopups 

A few more lines down find the following

                        # If the attacker is a Player, then inform the attacker
                        # that the attack was shielded.
                        if attacker.player:
                            attacker.player.sendGameText(RPG_MSG_GAME_YELLOW,"%s shields %s from %s's attack!\\n"%(defender.name,sex,attacker.name))

                        # If the defender was a Player, then inform the defender
                        # of the successful shielding.
                        if defender.player:
                            defender.player.sendGameText(RPG_MSG_GAME_YELLOW,"%s shields %s from %s's attack!\\n"%(defender.name,sex,attacker.name))

After it add

                        #Send TextPopups 
	                if attacker and attacker.player and attacker.player.mind: 
		            attacker.player.sendTextPopup(attacker, defender,"Shield Block")	
                        if defender and defender.player and defender.player.mind: 
		            defender.player.sendTextPopup(attacker, defender,"Shield Block") 
	                #End TextPopups 

damage.py

Find the follow bit of code

    # Cancel feign death and sleep conditions.
    # TWS: Should probaby check for conditions before itearting over processes
    # and trying to cancel the conditions.  This should probably be handled by
    # mob.cancel calls.
    mob.cancelStatProcess("feignDeath","$tgt is obviously not dead!\\n")
    mob.cancelSleep()

After it add the following

    #Send TextPopups 
    if inflictor and inflictor.player and inflictor.player.mind: 
        inflictor.player.sendTextPopup(inflictor, mob,amount)	
    if mob and mob.player and mob.player.mind: 
        mob.player.sendTextPopup(inflictor, mob,amount) 
    #End TextPopups

player.py

Find the function collapseMoney and before it add this.

    #Return function of the call that passes the ghostID to the client
    def textPopup(self,ghostid,text): 
	if ghostid > 0:
	    self.mind.callRemote("newTextPopup",ghostid,text)
	    
    #Calls for the GhostID from the zone server and then passes it back to the client
    def sendTextPopup(self,attacker, defender, text):
	self.zone.simAvatar.mind.callRemote("getGhostID",attacker.simObject.id, defender.simObject.id).addCallback(self.textPopup,text) 		    

playermind.py

Before onSpellSlot add the following function

    #Adds a damage popup
    def remote_newTextPopup(self,ghostid,message): 
        eval = 'DamageHud.addPopup(2000, "1 1 1", %s, %s);'%(ghostid,message) 
        TGEEval(eval) 

playgui.gui

Add the following control to the playgui file.

new GuiShapeDamageHud(DamageHud) { 
      canSaveDynamicFields = "0"; 
      Profile = "ShapeNameHudProfile"; 
      HorizSizing = "width"; 
      VertSizing = "height"; 
      position = "0 0"; 
      Extent = "1392 1290"; 
      MinExtent = "8 2"; 
      canSave = "1"; 
      Visible = "1"; 
      renderTooltip = "0"; 
      hovertime = "1000"; 
      fillColor = "0.25 0.25 0.25 0.25"; 
      frameColor = "0 1 0 1"; 
      showFill = "0"; 
      showFrame = "0"; 
      startingVerticalOffset = "1"; 
      endVerticalOffset = "100"; 
      distanceFade = "0.1"; 
   };