Alright I want this stuff out there. But I wanna do it with s t y l e. Prepare your star fish big boys because I'm going to slam this goddamn thread with some hella development concepts and run down. Do I suck at it, maybe, lets see who has the big developer richard and criticize what I've done yeah? So finally, here it is. BBB:R: Exposed.
Alright what the forget do I code in? I code in Sublime Text 3 and use the TS highlighting. Or used to. I don't code much anymore. Whatever.
This is the baller ass folder set up. Why the forget am I doing this? Let's look at the
server.cs okay boys? Here's the whole loving server.cs, but this is what we need. I don't give a stuff about what I'm doing elsewhere, we'll get there in a second.
$BBB::Path = "Add-Ons/Gamemode_BBBRedux/";
function BBB_Init(%dir)
{
%rdir = $BBB::Path @ %dir @ "/";
echo(" *Loading scripts ("@%dir@").");
for(%file=findFirstFile(%rdir @ "*.cs"); %file !$= ""; %file=findNextFile(%rdir @ "*.cs"))
{
if(!isFile(%file)) continue;
exec(%file);
}
echo(" *Finished.");
}
BBB_Init("Systems");
BBB_Init("PlayerScripts");
BBB_Init("Equipment");
See this stuff? I don't remember who the forget wrote it. I sure as hell didn't. See that BBB_Init? It goes spider on those folder's asses and runs any *.cs file it can find, that's some spicy stuff ain't it? Systems contains all my scripts relating to making the gamemode game. The PlayerScripts folder is hella big richard, it contains the player type, top ten anime shocks, and Equipment contains all code relating to any equipment I have. Alright let's get to the goddamn coolaid stuff of the server.cs.
if(!isObject(botSimGroup))
{
new SimSet(botSimGroup);
}
exec("./Sounds/Sounds.cs");
exec("./Equipment/Particles.cs");
//Creates BBB's heart if it doesn't exist already for whatever reason.
if(!isObject(BBB_System))
{
%BBB = new scriptObject(BBB_System)
{
match = false;
traitorList = "";
TraitorCount = 0;
InnocentCount = 0;
minutes = 0;
seconds = 0;
maxSeconds = 0;
timeDisplay = "0:00";
totalEquipment = 0;
currentDeathID = 0;
};
for(%i = 0; %i < $Pref::Server::MaxPlayers; %i++)
%BBB.DeathDetails[%i] = new ScriptObject(){class = "DeathObject";};
%BBB.timer(0,30);
%BBB.MarkList[10] = "||||||||||";
%BBB.MarkList[9] = "|||||||||<color:ff0000>|";
%BBB.MarkList[8] = "||||||||<color:ff0000>||";
%BBB.MarkList[7] = "|||||||<color:ff0000>|||";
%BBB.MarkList[6] = "||||||<color:ff0000>||||";
%BBB.MarkList[5] = "|||||<color:ff0000>|||||";
%BBB.MarkList[4] = "||||<color:ff0000>||||||";
%BBB.MarkList[3] = "|||<color:ff0000>|||||||";
%BBB.MarkList[2] = "||<color:ff0000>||||||||";
%BBB.MarkList[1] = "|<color:ff0000>|||||||||";
%BBB.RoleColor["Traitor"] = "\c0";
%BBB.RoleColor["Innocent"] = "\c2";
%BBB.RoleColor["Detective"] = "\c1";
}
So Alphadin, yo, what the forget? the botSimGroup contains all the bots so we can loop through that stuff and delete them later. What do we use bots for? We make corpses and microwaves with this.
I make use of a ScriptableObject to store all game necessary data. Match determines if the game is currently in session, the traitorlist is a list of traitors by name, duh, the TraitorCount keeps track of how many Traitors are present, and how many Innocents. By the way, detectives are technically innocents, so there's no detective count. Also I'm not very smart, so instead of keeping track of game time in seconds, I also keep track of time in minutes. maxSeconds determines how long the round will be in seconds. I don't know why I split these up. The TimeDisplay is a amalgamation of the current amount of minutes and seconds left, but formatted. totalEquipment is a count of how many equipment is currently in the game. CurrentDeath ID? We'll talk about that later friends. I'm making this up as I go, and I honestly don't remember. The rest is just constants we need, one for the non-gui healthbar and non-gui colors for roles. Alright enough of that bullstuff, onto the spicy innards of the gamemode.
Alright you forgets, you ready for the
CoreFunctions.cs? It's boring, I wrote comments just for you guys. Take them. I don't want them. I want to talk about something a little more interesting.
function MiniGameSO::messageAllExcept(%a,%b,%c,%d,%e,%f,%g,%h,%j,%k,%l,%m,%n,%o,%p)
{
if(%c $= 'MsgClientKilled')
return; //Stops from displaying death messages
parent::messageAllExcept(%a,%b,%c,%d,%e,%f,%g,%h,%j,%k,%l,%m,%n,%o,%p);
}
function messageClient(%a,%b,%c,%d,%e,%f,%g,%h)
{
if(%b $= 'MsgYourDeath')
return; //Stops from displaying personal death messages.
parent::messageClient(%a,%b,%c,%d,%e,%f,%g,%h);
}
This took a while to get my hands on, and it's honestly a handful. This is easily one of the most important features, so I'll highlight it here.
This code will hide all chat based CIs and death messages.What can you make use of it? Who loving knows.
function serverCmdBBBR_SendHandShake(%client)
{
%client.hasBBBRClient = true;
}
function gameConnection::autoAdminCheck(%client)
{
commandToClient(%client,'BBBR_ReceiveHandShake');
parent::autoAdminCheck(%client);
}
function gameConnection::onConnectionDropped(%client)
{
if(isObject(%client.player))
{
%client.player.removePlayer(BBB_System); //Makes it count as a death
%BBB.testVictory(); //Let's see if we won!
}
parent::onConnectionDropped(%client);
}
function gameConnection::onDeath(%client, %killerPlayer, %killer, %damageType, %a)
{
%BBB = BBB_System;
%client.player.removePlayer(%BBB); //Removes the player from the list of the living and sets them as dead on friendly T UI
%client.player.createCorpse(%damageType); //Creates a body with the outline of their death circumstances.
%BBB.testVictory(); //Checks if anyone one
%client.schedule(100,freeCamera); //Sets someone in spectator mode.
parent::onDeath(%client, %killerPlayer, %killer, %damageType, %a);
}
function GameConnection::spawnPlayer(%client)
{
if(!%client.hasSpawnedOnce && %client.hasBBBRClient)
commandToClient(%client,'BBBR_InitializeHUD');
parent::spawnPlayer(%client);
}
function armor::onadd(%this,%player)
{
Parent::onadd(%this,%player);
if(BBB_System.match)
%player.client.clearGunk();
%player.damagerCount = 0;
}
function GameConnection::clearGunk(%client)
{
if(isObject(%client.player) && BBB_System.match)
{
%client.player.delete();
%client.freeCamera();
}
}
This network of functions is one of the most important ones. First, it lets the game take care of handshakes which manages our GUI. Then, we have to "kill" the player if they disconnect and check to see if we've finally won. It's strange. It took me a metric forgettonne of functions to find which one actually triggers when a player finally spawns. Here's a spoiler for my friends. armor::onadd. enjoy that stuff. When they spawn in a middle of a round, we get them the forget out of there.
function player::damage(%obj, %sourceObject, %position, %damage, %damageType)
{
if(%obj.hasArmor)
%damage *= 0.75; //Reduces damage by 25% if they bought the quality armor.
if(%obj.role $= "" && BBB_System.match)
return; //Makes all undefined players invincible. Undefined occurs when an admin respawns themselves for whatever reason.
if(%obj.getClassName() $= "AIPlayer")
return; //Makes all bots invulnverable
if(BBB_System.match || BBB_System.resetting) //We let players damage each other inbetween round resetting, that's the best part of TTT. We don't tolerate damage before rounds though.
{
%obj.client.schedule(10,cycleUIBBB); //We start up a UI loop.
%brokenTwig = false; //Incase we DO find a copy of the playername in the list.
Now this is arguably the spicy part. We keep track of all that does damage to a person.
%name = "Environmental Damage"; //If we don't know why the forget why they took damage, it was definitely Environmental Damage
if(isObject(%sourceObject.client))
%name = %sourceObject.client.name @ " (" @ %sourceObject.client.BL_ID @ ")"; //Otherwise we pin the blame on the player who did it.
for(%i = 0; %i < %obj.damagerCount; %i ++)
{
if(%obj.damagerName[%i] $= %name)
{
%obj.damageById[%i] += %damage; //We keep track of how much damage each player does to another. This was supposed to be for logging purposes, aka if a player "RDMS" another player who had 5 HP and was just killed in the crossfire. We'll have an idea.
%brokenTwig = true;
break;
}
}
if(!%brokenTwig) //We create a definition incase the player has never hurt this player before.
{
%obj.damagerName[%obj.damagerCount] = %name;
%obj.damageById[%obj.damagerCount] = %damage;
%obj.damagerCount ++;
}
parent::damage(%obj, %sourceObject, %position, %damage, %damageType);
}
}
So yeah. BBB:R was supposed to have state of the art damage tracking. Too bad I don't remember how well it works, I think it does.
At this point I started working on corpse dealing. When we activate, if we activate on a Heal Bot/Corpse, we do what they need to do. We also needed to announce that a corpse had been found. I'm just talking about it here,
here's the goddamn code.
function observer::onTrigger(%this, %obj, %trigger, %state)
{
if((BBB_System.match || BBB_System.resetting) && !isObject(%obj.getControllingClient().player) && %state == 0)
{
if(%obj.curIndex $= "")
%obj.curIndex = 0;
%client = %obj.getControllingClient();
if(%trigger == 0)
{
%obj.curIndex ++;
if(%obj.curIndex >= clientGroup.getCount())
%obj.curIndex = 0;
%recur = 0;
for(%i = %obj.curIndex; %i<clientGroup.getCount();%i++)
{
%clientC = clientGroup.getObject(%i);
if(isObject(%clientC.player))
{
%client.spectateCam(%clientC.player);
commandToClient(%client,'centerPrint',"\c6You are now spectacting\c3" SPC %clientC.name @ "\c6.",5);
%obj.curIndex = %i;
break;
}
if(%i < clientGroup.getCount() - 1)
{
%recur ++;
%i = 0;
}
}
}
if(%trigger == 4)
{
%obj.curIndex --;
if(%obj.curIndex < 0)
%obj.curIndex = clientGroup.getCount() - 1;
%recur = 0;
for(%i = %obj.curIndex; %i >= 0; %i--)
{
%clientC = clientGroup.getObject(%i);
if(isObject(%clientC.player))
{
%client.spectateCam(%clientC.player);
commandToClient(%client,'centerPrint',"\c6You are now spectacting\c3" SPC %clientC.name @ "\c6.",5);
%obj.curIndex = %i;
break;
}
if(%i == 0)
{
%recur ++;
%i = clientGroup.getCount() - 1;
}
}
}
if(%trigger == 2)
%obj.getControllingClient().freeCamera();
}
return;
}
function gameConnection::spectateCam(%client,%player)
{
%cam = %client.Camera;
if(!isObject(%cam))
return;
%cam.setMode(Corpse,%player);
%client.setControlObject(%cam);
}
function SimObject::onCameraEnterOrbit(%obj,%camera)
{
%obj.observerCount++;
}
function SimObject::onCameraLeaveOrbit(%obj,%camera)
{
%obj.observerCount--;
}
I don't remember who wrote this. I think I wrote some of it? It's been a longass while, but here's my controller for Cameras. Click and Right click to rotate between players, hit space to free cam. I'm kinda happy to have this out for friends. Make sure you package the camera code if you plan on using it.
Corpses! So we touched on those earlier. Let's touch on them now big boy style yeah?
function player::createCorpse(%player,%dT)
{
//Creates the bot
%client = %player.client;
%position = %player.getposition();
%bot = new AIPlayer()
{
datablock = PlayerStandardArmor;
position = %position;
};
//Puts the bot in the group of bots so we can clear all the bots
%bot.addBotToGroup();
//Lets us give the bot an item to die with in their hands.
%image = %player.getMountedImage(0);
%player.unMountImage(0);
//Sets the bot's appearence to the dead person.
%temp = %player;
%client.player = %bot;
%client.applyBodyColors();
%client.applyBodyParts();
%client.player = %temp;
%bot.player = %bot;
//Adds a few small aethstetics
%bot.mountImage(%image,0);
serverCMDhug(%bot);
%bot.setCrouching(true);
%bot.playDeathanimation();
if(%player.role $= "Detective")
{
%bot.mountImage(DetectiveImage,2);
for(%op = 0;$hat[%op] !$= "";%op++)
%bot.hideNode($hat[%op]); //Hides all hats.
for(%qw = 0;$accent[%qw] !$= "";%qw++)
%bot.hideNode($accent[%qw]);
}
//Gives the bot all the values of the player's death.
%bot.name = %client.name;
%bot.located = false;
%bot.role = %player.getFormattedRole();
%bot.death = $DamageType_Array[%dT];
%player.hideNode("ALL"); //Hides the dead player so we don't have to see that.
BBB_System.proccessCorpseForm(%player); //Now we go through the long process of making a very detailed death process.
}
function BBB_System::proccessCorpseForm(%BBB,%player)
{
%CorpseFiling = %BBB.DeathDetails[%BBB.currentDeathID];
for(%i = 0; %i < %player.damagerCount; %i ++)
{
%CorpseFiling.damagerName[%i] = %player.damagerName[%i];
%CorpseFiling.damageById[%i] = %player.damageById[%i];
}
%CorpseFiling.damagerCount = %player.damagerCount;
%BBB.DeathDetails[%BBB.currentDeathID] = %CorpseFiling;
%BBB.currentDeathID ++;
}
So what we're doing here, or what I was going to do, was after making a corpse
look like a player, I was going to neatly file away all information. Remember that damage data we gathered earlier? We kept track of all deaths, assigning each case a unique ID. The first death, is death number 0. So when the round was ending, the idea was to loop through our "morgue" so to speak, and log all the information regarding deaths for that round, which would be stored in %BBB.DeathDetails. I never wrote it out of laziness or a lack of drive. The functionality is definitely all there I just, never brought myself to finishing what I had started.
ALRIGHT BOYS QUICK SHOUTOUT TO MY BOY PORT FOR HAVING WRITTEN THIS CODE I USED TO TRANSFER COLOR INFORMATION TO AND FROM HEX, RGB, RGB0-1
function rgbToHex( %rgb )
{
%r = _compToHex( 255 * getWord( %rgb, 0 ) );
%g = _compToHex( 255 * getWord( %rgb, 1 ) );
%b = _compToHex( 255 * getWord( %rgb, 2 ) );
return %r @ %g @ %b;
}
function hexToRgb( %rgb )
{
%r = _hexToComp( getSubStr( %rgb, 0, 2 ) ) / 255;
%g = _hexToComp( getSubStr( %rgb, 2, 2 ) ) / 255;
%b = _hexToComp( getSubStr( %rgb, 4, 2 ) ) / 255;
return %r SPC %g SPC %b;
}
function _compToHex( %comp )
{
%left = mFloor( %comp / 16 );
%comp = mFloor( %comp - %left * 16 );
%left = getSubStr( "0123456789ABCDEF", %left, 1 );
%comp = getSubStr( "0123456789ABCDEF", %comp, 1 );
return %left @ %comp;
}
function _hexToComp( %hex )
{
%left = getSubStr( %hex, 0 );
%comp = getSubStr( %hex, 1 );
%left = striPos( "0123456789ABCDEF", %left );
%comp = striPos( "0123456789ABCDEF", %comp );
if ( %left < 0 || %comp < 0 )
{
return 0;
}
return %left * 16 + %comp;
}
THANKS PORT YOU'RE A BOSS.
Let's talk equipment yeah?
Remember the server.cs? Good.
BBB_System.createEquipment("Armor","Armor","1 Credit\nDamage is reduced to 75% when purchased.",true,"Traitor","255 0 0",1,grantArmor,"","");
BBB_System.createEquipment("Flare","Flare Gun","1 Credit\nA Flare Gun which does 25 damage initially, and 75 over 1.5seconds.",true,"Traitor","255 128 64",1,FlareItem,3,"Item");
What the forget is going on here? This is a goddamn mess!
Nah bitch. Look over here.
function BBB_System::createEquipment(%BBB,%id,%name,%desc,%buyOnce,%alliance,%color,%cost,%data,%slot,%type)
{
%itemData = new ScriptObject(){
id = %id; //Shorthand name that appears on buttons, and how people without a GUI buy
name = %name; //Full name for our lovey buyers
desc = %desc; //What exactly we can do with it
buyOnce = %buyOnce; //Can they only buy it once?
alliance = %alliance; //Who can buy it? Detectives? Traitors? Curiously enough, with how the rest of the system is set up, we could technically have an innocent shop. Food for thought.
color = %color; //What color we make the button in the shop
cost = %cost; //How many credits it costs
data = %data; //The item data OR function name
slot = %slot; //What slot # this item goes into
type = %type; //Is it an item? OR do we run a function upon purchasing?
};
%BBB.equipmentList[%BBB.totalEquipment] = %itemData;
%BBB.equipmentListById[%id] = %itemData;
%BBB.totalEquipment ++;
}
So let's look at our items real quick. The flare gun is defined as a type "Item" so we physically add an item into the buyer's inventory.
The Armor is technically not an item, so it's not type "Item", so we call the function grantArmor on the gameConnection purchasing it.
function GameConnection::grantArmor(%client)
{
%client.player.hasArmor = true;
}
This system was set in place to let people make custom equipment. Good stuff.
The shop system on the other hand, which is tightly linked to the equipment, is a goddamn headache because I designed it to function both with GUI users and without GUI users. It really hurts my head to look at so
I'll just put the code as is, with what comments past Alphadin had written.
Also, on the subject, my past self did a stellar job commenting the
TeamSystem.cs so I'll leave him to take care of things. Oh! Yeah, by the way...
THANKS MR. NOßODY FOR THE REALLY AWESOME SLEUTHING HAT.anyways, that's all I give a stuff to write about. I might look at this later and cry, who knows.
You might be wondering, hey Alph?
I came here for the gamemode, wtf are you doing where's my BBB:R :angery:.
I'm a special snowflake and I deserve attention.I'm not giving it to anyone who doesn't have a clue to fix issues if they crop up as they play with it, I want people with modding knowledge to poke at it, not a server hoster. I don't want to submit this incomplete gamemode as a potential hosting gamemode, I'm not okay with distributing the files for that. If I know you're a capable modder or just someone I fancy, hit me up with a PM. The full gamemode files are all yours. Do with them what you want. Release the stuff if you want for whatever goddamn reason, break my heart. I didn't release an incomplete gamemode this time around. Make secret trades with it. I don't give a stuff, it's yours now.