Game Development Community

MMORPG Tutorial Article 4 Server Side Melee Redux!

by Dreamer · 04/12/2005 (9:25 pm) · 38 comments

If you haven't already done so please start here...
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7514

I really wanted to go back to the old school days of rolling dice to determine hit and miss in melee (think pen and paper RPGs), so here goes.

The heart of our Roll Based Server Side melee is this

server/scripts/commands.cs
function ServerCmdAutoAttack(%client){
	
	if(%client.getClassName() !$="AIPlayer"){
		%tempObj = findObject(%client,$TypeMasks::PlayerObjectType,10,1,0);
		%targetObject = %client.getSelectedObject();
		
		%client.Player.AttackOn =1;
		echo("Aquiring target");
		
		if(!%client.getSelectedObject()){
				MessageClient(%client,'No target','You have no target to attack!');
				%client.Player.AttackOn = 0;
		}else{
			if(%tempObj && %tempObj.getId() == %targetObject.getId()){
				if(%targetObject.getDamageLevel() < 100 && %client.Player.getDamageLevel() < 100 && %client.Player.AttackOn == 1){
					echo("Recieved order for AutoAttack");
					%Msg = "You attack "@%targetObject.getShapeName();
					MessageClient(%client,'Attack',%Msg);
					echo("Rolling!");
					
					%clientRoll = getRandom(1,32) + %client.Player.Atk;
					echo(%client.Player.getShapeName()@": "@%clientRoll);
				
					%targetRoll = getRandom(1,32) + %targetObject.AC;
					echo(%targetObject.getShapeName()@": "@%targetRoll);
					Melee::Attack(%client.Player);
					
					if(%clientRoll >= %targetRoll){
						%BaseDamage = %client.Player.getMountedImage(0).BaseDamage;
						if(%BaseDamage < 1){
							%BaseDamage = 1;
						}
						%targetObject.applyDamage(%BaseDamage);
						%Msg = "You hit "@%targetObject.getShapeName()@" for "@%client.Player.getMountedImage(0).BaseDamage;
						MessageClient(%client,'Attack',%Msg);
					}else{
						%Msg = "You missed "@%targetObject.getShapeName();
						MessageClient(%client,'Missed',%Msg);
					}
				
				}
				if(%targetObject.getClassName() $="AIPlayer"){
					if( %targetObject.getAimObject() != %client.Player){
						echo("Target is AI, make sure to setup return of Damage");
						%targetObject.setAimObject(%client.Player);
						schedule(1000,%targetObject,ServerCmdAutoAttack,%targetObject);
					}
				}else{
					if(!%targetObject.getSelectedObject()){
						echo("Target is not AI and does not currently hold a target, setting target");
						%targetObject.setSelectedObject(%client);
						CommandToClient(%TargetObject,'UpdateTargetDialog','NewTarget',%Client.getshapeName());
					}
				}
				schedule(1000,%targetObject,ServerCmdAutoAttack,%Client);
				//%Msg = "You are at "@%client.player.getPosition()@" and your target is at "@%targetObject.getPosition(); 
				//MessageClient(%client,'Location',%Msg);
			}else{
				MessageClient(%client,'OutOfRange','You are too far away to strike your target');
			}
		}
	}else{
		//Functions for AI autoattack!
		%tempObj = findObject(%client,$TypeMasks::PlayerObjectType,10,1,1);
		%targetObject = %client.getAimObject();
		if(%tempObj && %tempObj.getId() == %targetObject.getId()){
			if(%targetObject.getDamageLevel() < 100 && %client.getDamageLevel() < 100){
				%Msg = %client.getShapeName()@' is attacking you!';
				MessageClient(%targetObject,'Attack',%Msg);
				
				Melee::Attack(%client);
				
				%clientRoll = getRandom(1,32) + %client.Atk;
				echo(%client.getShapeName()@" Rolled "@%clientRoll);
				
				%targetRoll = getRandom(1,32) + %targetObject.AC;
				echo(%targetObject.getShapeName()@" Rolled "@%targetRoll);
				
				
				if(%clientRoll >= %targetRoll){
					%BaseDamage = %client.getMountedImage(0).BaseDamage;
					if(%BaseDamage < 1){
						%BaseDamage = 1;
					}
					%targetObject.applyDamage(%BaseDamage);
					MessageClient(%targetObject,'Attack',%client.getShapeName()@' hits you for '@%client.getMountedImage(0).BaseDamage);
				}else{
					MessageClient(%targetObject,'Attack',%client.getShapeName()@' missed!');
				}
				schedule(2000,%targetObject,ServerCmdAutoAttack,%client);
			}
		}else{
			echo("Too far away from client, moving!");
			%client.setMoveDestination(%targetObject.getTransform());
			schedule(5000,%targetObject,ServerCmdAutoAttack,%client);
		}	
		
	}
	echo("Leaving AutoAttack");
}
Make sure in weapons.cs you add the following
function Melee::Attack(%obj){
	%actnum = getRandom(1,3);
	if(%actnum ==1){
		%action = "h1swing";
	}
	if(%actnum ==2){
		%action = "h1slice";
	}
	if(%actnum == 3){
		%action = "h1thrust";
	}
	if(%actnum < 1 || %actnum >3){
		%action = "h1swing";
	}
	 %obj.setActionThread(%action);
}
function WeaponImage::onFireHandToHand(%this, %obj, %slot)
{
	Melee::Attack(%obj);
}

And of course the sword.cs from our previous tutorial. add the following to the SwordImage
BaseDamage = 5;

Now you want to be able to melee with your target so the mouseFire in default.bind.cs this
function mouseFire(%val)
{
   $mvTriggerCount0++;
   CommandToServer('AutoAttack');
}

Thats it for Melee, next up will be a couple an example of a very simple DirectDamage(DD) spell and then we will add a database and start work on the MM part of our MMORPG!
Enjoy!
Page «Previous 1 2
#1
04/12/2005 (11:15 pm)
It looks to me that this resource builds off others, you might want to link us to those so we know.
#2
04/13/2005 (12:23 am)
all i can say is YAY
#3
04/13/2005 (7:51 am)
Umm, this resource is part of a 7 part (so far) tutorial, they all build on eachother, so far parts 4 and 5 have posted but 1-3 have not yet been approved, I'm not quite sure why.

*Update! The dependant parts finally got approved!
Start here
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7514
#4
04/13/2005 (9:05 am)
Next Tutorial...
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7584
#6
04/14/2005 (6:23 pm)
just a quick question, what file do we place the first part of the code?
thanks
#7
04/14/2005 (7:21 pm)
im guessing thats starter.fps/server/commands.cs
#8
04/14/2005 (11:07 pm)
Fixed it!
#9
04/15/2005 (1:32 pm)
Great thanks, ill check it out as soon as i get home.
Thanks
#10
05/05/2005 (10:56 am)
keyboard0 input device acquired.
We are doing raycast and player =1621
 /// RayCast Search /// 
 Object =  2010 435.1 -94.0683 237.127 -0.997493 0.0401098 0.0582988
 Object Position =  435.814 -94.1651 235.738
 Object Id =  2010
 Object Name =  
 /// --------------- /// 
Aquiring target
system/server/scripts/commands.cs (239): Unable to find object: '1' attempting to call function 'getId'
Leaving AutoAttack
We are doing raycast and player =1621
 /// RayCast Search /// 
 Object =  2010 435.092 -94.0779 237.122 -0.995862 0.0599572 0.0682989
 Object Position =  435.814 -94.1651 235.738
 Object Id =  2010
 Object Name =  
 /// --------------- /// 
Aquiring target
system/server/scripts/commands.cs (239): Unable to find object: '1' attempting to call function 'getId'
Leaving AutoAttack
keyboard0 input device unacquired.
keyboard0 input device acquired.
*** ENDING MISSION
CDROP: 1566 IPX:61726553:697A65005369:26478
Exporting server prefs...
Exporting client prefs
Exporting client config
Exporting server prefs
Exporting client prefs
Exporting server prefs
Shutting down the OpenGL display device...
Making the GL rendering context not current...
Deleting the GL rendering context...
Releasing the device context...
Restoring the desktop display settings (1024x768x32)...
keyboard0 input device unacquired.

This is my console output when I atempt to attack an NPC using the same datablock as the player. Also note this is after targeting him. In my chat output I get that he is out of range even though I am basicly standing on top of his head!

I have done my best to debug this but I am not really sure whats wrong it all looks ok to me. If you have any ideas please let me know!
#11
05/05/2005 (3:34 pm)
From the looks of it, it appears you are not aquiring the target first, go into targeting mode and make sure his Health and Energy bars are displayed in the targetingHUD, then attack.

Just a side note you will get an out of range message if he is not directly in your line of site, I really need to modify this to do a radius search for range, and then do a line of site check to return something like "You need to face your opponent", I'll place that on my TODO list. Also remember that range is limited to 10 at present if you want to increase your melee attack range
%tempObj = findObject(%client,$TypeMasks::PlayerObjectType,10,1,0);

Modify the line above, I've had pretty good luck with values up to about 15 anything more, and you lose alot of the effect.
#12
05/06/2005 (9:47 am)
The npc is targeted. I have to click him about 100 times to find a "Sweet spot" but his health and energy are being showed with his name. After that I am close enough that the two orcs could probaly kiss and facing him perfectly, and still get the message.

I will try adjusting the range and we will see what happens from there!
#13
05/06/2005 (10:01 am)
Just a note changing the range did no good, all my scripts and everything compile with no errors. I am not really sure whats the problem here...
#14
05/06/2005 (11:41 am)
@Issac, run the game, execute trace(1); in the console, target the Orc, get close and left click once, wait about 30 seconds, then exit the game and email your log to...
smorey@gmail.com

I'll look at it and see what I can find.
#15
05/06/2005 (12:30 pm)
@dreamy, alright did what you said. Thanks for the help!

Let me know what you come up with please
#16
05/06/2005 (4:15 pm)
It's been over an hour since you posted this, could you please resend the log?
I've never recieved it.
#17
05/06/2005 (4:59 pm)
Thats why I hate gmail, I will resend it tommorrw why I am at the office.

Sorry
#18
05/07/2005 (5:29 pm)
Alright its been resent sorry for your wait

Thanks alot
#19
05/24/2005 (1:06 pm)
Wouldn't you want the range for the sword in the sword.cs file so you can accomidate various weapon ranges in the future? Unless I'm reading it wrong all melee weapons end up with the same range currently. I'm thinking that if you added a polearm or halbred in the future you'd need to be able to set the range further out than for the sword.

Please let me know if I'm miss reading this. Thanks!
#20
05/24/2005 (1:44 pm)
@Alan: This is a tutorial and not intended to cover all the bases with implementation and is instead more of a guide or working illustration of the concept. There are many many many aspects of these tutorials that are left as an excercise for the reader. Perhaps the advanced tutorial series that Dreamer is working on will provide more of the concrete work you're looking for. Now to make an attempt at answering your question...

Quote:Wouldn't you want the range for the sword in the sword.cs file so you can accomidate various weapon ranges in the future? Unless I'm reading it wrong all melee weapons end up with the same range currently. I'm thinking that if you added a polearm or halbred in the future you'd need to be able to set the range further out than for the sword.

This is something that I believe would be specific to your game implimentation. In other games (NwN for instance) a melee weapon is a melee weapon. They may be longer or shorter but the attack distance required for melee attacks is kept the same. If you were to have the range adjust on a per-melee-weapon basis I would guess that the players would figure this out very quickly and switch to their halibard the moment they see their opponent wielding a sword as the halibard would obvious connect with greater range which in effect would render the "shorter" melee weapons "not-as-good". This is a balance that must be chosen for your game and is not something that can be stated across-the-board as being benificial to everyone.
Page «Previous 1 2