Author Topic: My TDM Idea  (Read 1561 times)

Mmnom.  I am putting together a team deathmatch.  I've started constructing bases and a map.  But I'd like to outline my ideas here.

Weapons
Old School weapon pack.  The version I have has some console spam w/the baseball bat.  I need to check if the latest version fixes that or if I have to fix it myself.
Default Weapons.
I'd consider Teir+Tactical but I want this to run on lower end systems.  Also there's the reload time to consider.  I kinda miss having infinite ammo, and the old school weapon pack has that.

Map
The map was made via a randomly generated cave.  Made of 64x cubes.  Each cell is 64x64x128.  Chances of placing a wall instead of an open area are 3/10.  The map size is 12 cells by 12 cells.  The entire area is enclosed.  There are no exits from the cave.
I picked 5 locations for bases, and placed hills between bases that had a straight shot to each other.
Players will spawn in a map room, from which they can teleport to any of their bases.
I will definitely put a timer on each teleport button.  Something like 3 seconds between each teleport in.
This is to make it easier for a base to be captured, and reduce the kill rate of spawnkilling.
Each base will have a tank.

Teams
I started making the map before I decided on how many teams I'm making.  The result is 5.
Blue, Red, Yellow, Orange, and White.  The Floor of the map is green, so no green team.  The walls are black.  So no black team.
Each round, each team gets a random set of weapons.  The weapons are weighted by damage per second.  I tested dps ingame by hooking into the function that damages targets and recording how much damage was done and what time it was.
I think the random weapons will affect the results of each round.  But should allow unskilled players to get some kills if they get a good weapon.

If a team loses all of their bases, the players in that team will be split up among the remaining teams.
I am unsure how this will play out, but I imagine it will be fun.  And if your team loses- there's no downtime.  You can just keep playing.

Server
I plan to use HammerHost.  I have no idea of how good their service is, but it is considerably cheaper than blocknet.  If you know of any other hosting services with a good service record, hook me up.

The modlist is very small.  There's the mod to handle a team losing, and old school weapons.  I believe there will be some events I'll need to add.  Stuff like shifteventenabled.

As far as administration goes- there isn't much that needs to be done.  The rules consist of, don't glitch through the 64xcube walls to kill people.
So- the admins only need to spy on people occasionally.
I may end up not needing even that.  I could modify the 64x cubes to have a zone in them which checks for vehicles and players, and kills them.  However that is ALOT of checks.  A better idea would be to force the vehicles and players to check if the brick above them is a part of the 64x wall.

Adding killzones on the outer border of the map is a good idea too.

I think you should work on reducing the number of teams. Especially with teams being able to be eliminated. 2 or 3 should be alright.

Cause think of it. If you manage to get 10 players into this server, you have a 2v2v2v2v2.

Valid point.  I could lock areas off depending on the player count.  3-4 people per team should be good.

It gets a bit tricky to figure who has won when I change the amount of bases.  Instead of using default events I'll have to make some events for tracking how many bases a team owns and changing how many bases are required for a win.

I guess I could do it with default events.  But the logic behind that gets complicated.

So for 3 bases
Basewindec -> printcount = 9
33 onminigameroundstart > self > firerelay
33 onrelay > self > firerelay
33 onrelay > named_brick:Basewincount > firerelay
38 onprintcountunderflow > self > cancelevents

Basewincount ->printcount = 2
33 onrelay > self > decprintcount > 1
33 onrelay > named_brick: countsetter > firerelay
0 onprintcountunderflow > self > seteventenabled 1 [  ]
0 onminigameroundstart > self > seteventenabled 1 [-]

Countsetter
onrelay > named_brick:orange_basecountmax > decprintcount 1
"" > "":yellow_basecountmax > "" ""
"" > "":white_basecountmax > "" ""

Orange_Basecountmax
onminigameroundstart > self > setprintcount 1
onprintcountoverflow > minigame > win team(brick)

Orange_basecountmin
onminigameroundstart > self > setprintcount 0
onprintcountunderflow > iforgettheeventswhichsplitupa team

Orange_capturepoint
oncapture >

...
How do I determine which team owns the base with just slayer & default events.  I can't decrease and increase the proper team's basecount without that.
Well whatever.  With these events, by adjusting the printcount on basewincount (via another brick which keeps track of the amount of players), the amount of bases needed to win on Orange_basecountmax will change.

I guess that's why I should just make some custom events.


Here are the DPS  results from my tests:
Code: (dpsdata.txt) [Select]
Machine Gun 181.302 6.33464
Baseball Bat 71.25 0.75
Bow 56.1313 1.87104
Crossbow 67.4894 1.54261
Disintegrator 88.8831 6.73219
Flamethrower 188.578 25.0998
Grenade L. 137.871 3.67454
Gun 109.712 3.65705
Guns Akimbo 232.21 7.74032
Longbow 60.5473 2.42189
Machine Gun 185.426 6.18087
Mortar 225.127 3.26543
Revolvers A. 192.678 4.28174
Rocket L. 124.329 2.51625
Shotgun 166.521 6.51058
Sniper Rifle 163.674 0.935279
Spear 69.6409 2.13781
Sword 151.362 4.32464
Tranquilizer 138.249 1.81539
Attached is the script I used to get the data.

Place dpstest.cs in Blockland/base.
discoverfile("base/dpstest.cs");
exec("base/dpstest.cs");
Place 9 vehicle spawns.
Spawn 9 horses.
Place a 2x2.
Name the 2x2 "Item".
Spawn the desired item from the 2x2.
Hit the horses with the item, they're just there to absorb the damage.
Stop hitting the horses.
Say /getdata.
Observe that the data is what you want.
Say /publish or /publish [itemname].  If you did not name the 2x2 you will need to enter in a name yourself.
Say /cleardata.
Say /respawnvehicles.
Spawn the next item you wish to test and repeat.

Upon completion you can look at the data in Blockland/base/dpsdata.txt.

Can't judge by just dps though. mortar is very hard to hit with. sword requires close range. sniper rifle and tranquilizer require precise aim.
Using horses is also not such a good idea. They'll take reduced damage from explosives, due to their center point being further away from the explosion than a regular players'. It's the same reason why people say to shoot under vehicles when using rockets. Does more damage than hitting the front most of the time.

I didn't know that about horses.  I think the dps data is accurate enough for what I'm doing.

I got the killzones around the walls set up.
And I made a loop to check if a player is inside of a wall.  It only checks 1 player on the client list every fifth of a second.  So it shouldn't be any more resource intensive than 1 killzone.

Quote from: mapdeathstuff.cs
datablock TriggerData( DeathTrigger )
{
   // tickPeriodMS is a value is used to control how often the console
   // onTriggerTick callback is called while there are any objects
   // in the trigger. The default value is 100 MS.
   tickPeriodMS = 200;
};

function DeathTrigger::onEnterTrigger( %this, %trigger, %object )
{
   if(%object.getClassName() $= "Player")
   {
      %object.kill();
   }
   if(%object.getClassName() $= "WheeledVehicle")
   {
      if(%object.getMountedObjectCount()>0)
      {
         %mounted = 1;
      }
      %i=0;
      while(%mounted)
      {
         if(%object.getMountedObjectCount()<=0||%x>16)
         {
            %mounted = 0;
         }
         %object.getmountedobject(%i).kill();
         %i++;
         %x++;
      }
      %object.damage(%object.getDatablock(),%object,10000);
   }
   Parent::onEnterTrigger( %this, %trigger, %object );
}

function createdeathtriggers()
{
   if(isObject(DTNorth))
   {
      DTNorth.delete();
      DTSouth.delete();
      DTEast.delete();
      DTWest.delete();
      DTLoop.delete();
   }
   new Trigger(DTNorth)
   {
      position = "-66 482 0";
      rotation = "1 0 0 0";
      scale = "498 50 500";
      dataBlock = "DeathTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
   new Trigger(DTSouth)
   {
      position = "-16 -16 0";
      rotation = "1 0 0 0";
      scale = "498 50 500";
      dataBlock = "DeathTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
   new Trigger(DTEast)
   {
      position = "432 482 0";
      rotation = "1 0 0 0";
      scale = "50 498 500";
      dataBlock = "DeathTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
   new Trigger(DTWest)
   {
      position = "-66 432 0";
      rotation = "1 0 0 0";
      scale = "50 498 500";
      dataBlock = "DeathTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
   new ScriptObject(DTLoop);
   MissionCleanup.add(DTNorth);
   MissionCleanup.add(DTSouth);
   MissionCleanup.add(DTEast);
   MissionCleanup.add(DTWest);
   MissionCleanup.add(DTLoop);
   DTLoop.loop();
}

function DTLoop::loop(%this)
{
   %this.schedule(200,"loop");
   %count = ClientGroup.getCount();
   if(%count)
   {
      if(%this.count>%count||%this.count$="")
      {
         %this.count = %count;
      }
      %client = ClientGroup.getObject(%this.count-1);
      if(isObject(%client.player))
      {
         %player = %client.player;
         if(isObject(%player.getObjectMount()))
         {
            %mount = %player.getObjectMount();
            %raycast = Containerraycast(%mount.position, Vectoradd("0 0 64",%mount.position), $TypeMasks::FxBrickObjectType, %mount);
            if(getWord(getWord(%raycast,0).position,2)==48)
            {
               %mount.damage(%mount.getDatablock(),%mount,10000);
            }
         }
         %raycast = Containerraycast(%player.position, Vectoradd("0 0 64",%player.position), $TypeMasks::FxBrickObjectType, %player);
         if(getWord(getWord(%raycast,0).position,2)==48)
         {
            messageclient(%client,0,"\c0You suffocated inside of a wall.");
            %player.kill();
         }
      }
   }
}
I pulled the code for the deathzones from a map I made 02/2011.  Archives are awesome. :D

I have the basic structure of 4 bases done.  And I purchased a server from HammerHost.  So no one else has to deal with my stuffty connection when I host it.

Also I figured out what to do about the extra bases.
If a team has their last base captured, they are split up among the rest of the teams as normal.
After that is done, the game will check the amount of people on the team who captured the base.
If that value is less than 7, the base will be turned grey, and be locked up.

In this way a team will not be stretched thin when they capture a base, and yet we keep the randomness as to which teams are going to participate in the final battle.

Wait I thought hammerhost was taken down?

But on topic, this sounds like a really good idea, and the fact you get random weapons is good too, what I suggest adding is a few places to snipe people, like maybe a bunker, just a suggestion.

It mentioned something like that on the site.
Quote
DDoS protection: The C.A. node has DDoS protection up to 160 Gbps. This ensures HammerHost will never collapse like RTB, Lug-host, or HammerHost 1.0.

I'm on a node in Canada which also has DDoS protection.

I could add sniper nests up on the walls.  Though they will be larely ineffective when there are tanks roaming around.  And then the mortars.  And the crossbow has an explosion effect too....
I'll figure something out.

I made two events.  One to display a message when a base is captured, and another to change the color of various things such as flags and vehicles when a base is captured.

The intended use is as such:
onCapture > self > displaycapturemessage > [<color:ASDFGH>basename]
onCapture > Named_Brick(Base_Flag) > setSameColorAs > [Capture_Point_Name]
These are not meant to be used in any application other than this tdm.
Quote from: server.cs
//We want to:
//Display a message when a point is captured
//Change the color of a brick to the color of the calling brick

package CPlistoldteam
{
   function FxDtsBrick::setCPControl(%this, %color, %reset, %client)
   {
      %mini = getMinigameFromObject(%this);
      if(!isSlayerMinigame(%mini))
         return;
      if(isObject(%client))
      {
         if(!minigameCstar fishe(%client, %this))
            return;
      }
      %this.oldteamname = getField(%this.getTeamControlList(),0).name;
      %this.oldteamnamecolor = getField(%this.getTeamControlList(),0).colorHex;
      Parent::setCPControl(%this, %color, %reset, %client);
   }
};
activatepackage(CPlistoldteam);

registerOutputEvent(FxDtsBrick, "displayCaptureMessage", "string 50 80", 1);
function FxDtsBrick::displayCaptureMessage(%this, %basename, %client)
{
   if(isObject(%client))
   {
      if(isObject(%client.minigame))
      {
         %client.minigame.chatMsgAll("<color:"@getField(%this.getTeamControlList(),0).colorHex@">"@getField(%this.getTeamControlList(),0).name@"\c6 has captured "@%basename@"\c6 from <color:"@%this.oldteamnamecolor@">"@%this.oldteamname);
      }
   }
}
registerOutputEvent(FxDtsBrick, "setSameColorAs", "string 50 80", 1);
function FxDtsBrick::setSameColorAs(%this, %brickname, %client)
{
   %brickname = "_"@%brickname;
   if(isObject(%brickname))
   {
      if(%brickname.getClassName()$="fxDTSBrick")
      {
         %this.setColor(%brickname.getColorID());
      }
   }
}

Get obstructradiusdamage by Port so you can't use explosives to hurt people through walls. Makes towers and bases are useful obstructions to hide behind.

Got it.  I'll test it tomorrow.

Solid walls are super important.

Obstruct Radius Damage works.  If a brick gets blown up, the damage still goes through, so I think I'll leave brick damage off.  This is also a good idea since I'm trying to keep the brick count low, and blowing up buildings made in that style isn't very fun as most of them don't blow up.

I got the team dissolving part working.  And a base will be locked if the team that captured it doesn't have enough people to defend it.

I considered adjusting the autosort weight in favor of a team which captures a base.  But doing so would allow the first team which captures a base to quickly gain an advantage and wipe the rest of the teams off of the map.

I'd consider Teir+Tactical but I want this to run on lower end systems.  Also there's the reload time to consider.  I kinda miss having infinite ammo, and the old school weapon pack has that.
I believe all you have to do is $Pref::Server::TTAmmo = 2; to make the guns have infinite ammo.

But without that incentive to raid, people would rather camp and hope to get the spills from others' work.

Maybe instead of getting more players, they get base upgrades.