Hello everyone. I’m needing a second coder to work with on Inner Animal, my second coder MyCheeze is rather extremely busy with school and will be unable to do much (or any at all) work on Inner Animal during the next year due to it being her final year of high school. Because of this I’m needing to find a replacement coder ASAP. With that being said, I’ll answer some question and explain what I’m looking for.
#1.) What is “Inner Animal”?
Inner Animal is an anthropomorphic game that is heavily inspired by the Hotline Miami series and is a mode-round based game, every round a different gamemode is randomly chosen to be played. Every gamemode is different then the rest, but all have the same gameplay more or less just with different objectives.Some of the gamemodes are completely free for all, others are team based or are both FFA and team based. Gamemodes vary from CTF, Deathmatch, KotH, Team Juggernaut vs everyone else, wave survival and elimination to name a few.
Gameplay at its very core is you running around shooting, slicing, knocking around and jumping on everyone else to win, however it sounds boring and simple right? Its not so simple, in just a single hit your life could be over, most weapons in Inner Animal are one hit kills, with the exception that some characters require multiple hits to kill. But thats not everything either, every map has a limited number of weapons that will spawn, melee weapons have an infinite number of uses and never break, while ranged weapons only have a single magazine and cannot pick up ammo or reload. Even though a gun in your hand stops spitting lead and only clicks when you pull the trigger, it is not useless. You can throw everything you can pick up at your enemies to knock them down or kill them. There are plenty of world objects around you to use as a makeshift weapon if you simply look around, just be warned that if you throw a something at someone and miss, they might pick up that object and use it on you.
tl:dr: A modernized, updated and overall much better version of this with bloody fluff everywhere. (Requires 2 or more players) / the roblox multiplayer version of this and this.
#2.) How is Inner Animal going to be monetized?
(Just about) Everything in Inner Animal is obtainable for freeif a player plays long enough, characters are unlocked by playing the game long enough to get the necessary amount of experience points to unlock, or can be bought with premium currency (which has no official name so I’ll refer to it as “Gold”) . Gold is used to purchase new characters (which is the much faster way of obtaining them rather then playing the game till you have the required amount of points) and consumables. Consumables in the game are essentially stat boosters and have 3 types of consumability:
1.) Time-based consumables
Time-based consumables only get consumed as you play the game, meaning that for every second you are playing during a round, the consumable is being used. These consumables do not stack but rather have their time extended whenever purchased or awarded.
2.) Per-life consumables
These are consumed once per life.
3.) Per-round consumables
These are only consumed once the round has ended.
Consumables have different effects, some are EXP boosts, armor, increased movement speed, lethal punches/throws, extra ammo, etc. And to make it fair for all players, consumables are not strictly pay-to-obtain. You are awarded a random consumable for winning, being the MVP, most kills, best accuracy, and just playing the game in general. Gold is also awarded to players who login daily, win a lot and complete daily challenges and rank up. (To keep it from being a complete P2W). With all of that being said, the only thing that will be sold is the premium currency. And in the future after release, character packs will also be available for purchase.
3.) What am I looking for?
I’m looking for an advanced coder who is able to get things done by a reasonable deadline, has knowledge and experience with writing their own 2D physics in a 3D world (This includes collisions solving, pushing objects around and custom hinges) and is able to read, to understand and to work with my coding, as well as being able to write clean, efficient and readable code and as well as comment their own code.
An example of my coding...
[code] local Server = nil local Client = nil
return function(ForcedRunMode) – Fury is a function, it’s to keep client and server code different while in PlaySolo.
if Client ~= nil and ForcedRunMode == “Client” then – (game:GetService(“RunService”):IsClient() and game.Players.LocalPlayer ~= nil) then
Client:PrintVersion()
Client:PrintRunMode()
return Client
elseif Server ~= nil and ForcedRunMode == “Server” then – (game:GetService(“RunService”):IsServer() or game.Players.LocalPlayer == nil) then
Server:PrintVersion()
Server:PrintRunMode()
return Server
end
– ROBLOX Services –
local RunService = game:GetService(“RunService”)
local RS = game:GetService(“ReplicatedStorage”)
local Players = game:GetService(“Players”)
local RbxUtility = LoadLibrary(“RbxUtility”)
– Glboal Variables –
local Root = script
local CustomServicesFolder = Root:WaitForChild(“Custom Services”)
local CustomObjectsFolder = Root:WaitForChild(“CustomObjects”)
local ModulesFolder = Root:WaitForChild(“Modules”)
local RemotesFolder = Root:WaitForChild(“Remotes”)
local FuryReplicationRequest – Is set later
local FuryReplicationUpdate – Is set later
local InitialPing – Is set later
local PingRemote – Is set later
– Engine Variables –
local EngineName = Root.Name
local Engine = {}
Engine.FilePaths = {}
– Engine Configuration –
Engine.Configuration = {
EngineName = EngineName,
Version = “v2”,
PrintHeader = EngineName…": “,
WarnHeader = EngineName…” Warning: “,
ErrorHeader = EngineName…” Error: ",
}
Engine.RunMode = nil – Is set under Engine Logic
function Engine:PrintVersion()
warn(Engine.Configuration.PrintHeader…" “…Engine.Configuration.Version…” is up and running!")
end
function Engine:GetRunMode()
local RunMode
if RunService:IsClient() and game.Players.LocalPlayer ~= nil then
RunMode = “Client”
if Client == nil then
Client = Engine
end
elseif RunService:IsServer() or game.Players.LocalPlayer == nil then
RunMode = “Server”
if Server == nil then
Server = Engine
end
end
return RunMode
end
function Engine:PrintRunMode()
print(Engine.Configuration.PrintHeader…" "…Engine.RunMode)
end
–@Name: Engine Create
–@Description: Mimics the RbxUtility:Create()
–@Parameters:
– ClassName: The ClassName of the object you wish to create.
– Properties:
function Engine:Create(ClassName, Properties)
local Object = Instance.new(ClassName)
if Properties then
local Parent
for Property, Value in pairs(Properties) do
if Property == “Parent” then
Parent = Value
else
Object[Property] = Value
end
end
Object.Parent = Parent
end
return Object
end
–@Name: Engine Pools
–@Description: A Pool is a table where all scripts on the local machine will have access to add or remove pools and edit pool data. –
–@Parameters:
– PoolName: The name of the pool.
– IsDynamic: If true, the pool will have the ablity to be listened to by scripts via .Updated event.
– PoolData: Any sort of data that needs to be added to the pool upon creation.
Engine.Pools = {}
function Engine:CreatePool(PoolName, PoolData, IsDynamic)
if IsDynamic then
local Pool = {}
Pool.Data = {}
Pool.Updated = RbxUtility:CreateSignal()
for i,v in pairs(PoolData) do
Pool.Data[i] = v
end
Pool.__index = (function(self, Index)
return Pool[Index] or self.Data[Index]
end)
Pool.__newindex = (function(self, Index, Value)
self.Data[Index] = Value
self.Updated:fire(Index, Value)
end)
Engine.Pools[PoolName] = setmetatable({}, Pool)
else
Engine.Pools[PoolName] = PoolData or {}
end
return Engine.Pools[PoolName]
end
function Engine:GetPool(PoolName)
if Engine.Pools[PoolName] == nil then
Engine.Pools[PoolName] = {}
end
return Engine.Pools[PoolName]
end
function Engine:DeletePool(PoolName)
Engine.Pools[PoolName] = nil
end
– @Name: Custom objects
– @Description: Allows for you to load user created objects.
function Engine:LoadCustomObject(PATH, NAME)
if string.sub(PATH, #PATH) ~= “/” then
PATH = PATH…"/"
end
return Engine.FilePaths[PATH][NAME]
end
–[[
do
local function Recursive(Folder, FilePath)
if FilePath == nil then
FilePath = "Objects/"
end
for i,v in pairs(Folder:GetChildren()) do
local NewFilePath = FilePath..v.Name.."/"
local FolderData = {
Parent = Folder.Parent,
Folder = Folder,
}
if v:IsA("ModuleScript") then
FolderData[v.Name] = require(v)
elseif v:IsA("Folder") then
Recursive(v, NewFilePath)
end
Engine.FilePaths[NewFilePath] = FolderData
end
end
Recursive(CustomObjectsFolder)
for i,v in pairs(Engine.FilePaths) do
print(i,v)
end
end
–]]
– Engine Modules –
Engine.Modules = {}
function Engine:GetModule(ModuleName)
if Engine.Modules[ModuleName] ~= nil then
return Engine.Modules[ModuleName]
else
local found = ModulesFolder:FindFirstChild(ModuleName, true)
if found then
if found:IsA(“ModuleScript”) then
local m = require(found)
Engine.Modules[ModuleName] = m
print(Engine.Configuration.PrintHeader…“successfully loaded “…ModuleName…”.”)
return m
else
error(Engine.Configuration.ErrorHeader…“Module '”…tostring(ModuleName)…"’ is not a ModuleScript!")
end
else
error(Engine.Configuration.ErrorHeader…“Module '”…tostring(ModuleName)…"’ wasn’t found!")
end
end
end
– Engine Services –
Engine.Services = {}
– @name: GetService
– @Description: Returns the requested service.
function Engine:GetService(ServiceName) – this function mimics game:GetService() except it allows for custom services.
if Engine.Services[ServiceName] ~= nil then
return Engine.Services[ServiceName]
else
local CustomService = CustomServicesFolder:FindFirstChild(ServiceName, true)
if CustomService ~= nil then
Engine.Services[ServiceName] = require(CustomService)(Engine) – Custom services need to have access to Fury.
return Engine.Services[ServiceName]
elseif CustomService == nil then
Engine.Services[ServiceName] = game:GetService(ServiceName)
return Engine.Services[ServiceName]
end
end
end
– Engine Remotes –
– @Name: GetRemote
– @Description: Yields code until the specified Remote is found at the path.
– @Parameters:
– PATH: Where the specified remote will be.
– NAME: The specific name of the remote you are looking for.
function Engine:GetRemote(PATH, NAME)
if string.sub(PATH, #PATH) ~= “/” then
PATH = PATH…"/"
end
if Engine.FilePaths[PATH] ~= nil then
return Engine.FilePaths[PATH][NAME]
else
warn(“Yielding Fury:GetRemote(”…PATH…", “…NAME…”) until Engine.FilePaths["…PATH…"] is found.")
while Engine.FilePaths[PATH] == nil do
RunService.Heartbeat:wait()
end
warn(“Engine.FilePaths[”…PATH…"] is found. Yielding Fury:GetRemote("…PATH…", “…NAME…”) until Engine.FilePaths["…PATH…"]["…NAME…"] is found.")
while Engine.FilePaths[PATH][NAME] == nil do
RunService.Heartbeat:wait()
end
return Engine.FilePaths[PATH][NAME]
end
end
– @Name: CreateRemote
– @Description: Creates the specified remote object that the CLASSTYPE parameter specifies.
– @Parementers:
– PATH: Where the remote will be instanced and saved to.
– NAME: The name and Index of the remote.
– CLASSTYPE: Specifies which object will be instanced, you can currently use: RemoteEvent, RemoteFunction, BindableEvent, BindableFunction, ScriptSignal
– DO_NOT_REPLICATE_TO_CLIENTS: By default this is set to false and should only be set to true for hidden remotes.
– @Notes: ScriptSignal is never replicated to the clients.
function Engine:CreateRemote(PATH, NAME, CLASSTYPE, DO_NOT_REPLICATE_TO_CLIENTS)
local FolderData
if Engine.FilePaths[PATH] ~= nil then – Check to see if this file path is alreay cached or not.
FolderData = Engine.FilePaths[PATH]
else – Create the new path
local CurrentFolder = Root
local FoldersToSeachFor = {}
local CurrentPath = ""
for NextFolder in string.gmatch(PATH, "[^/]+") do
if CurrentFolder:FindFirstChild(NextFolder) then
CurrentFolder = CurrentFolder[NextFolder]
else
local NewFolder = Instance.new("Folder")
NewFolder.Name = NextFolder
NewFolder.Parent = CurrentFolder
CurrentFolder = NewFolder
end
CurrentPath = CurrentPath..NextFolder.."/"
if Engine.FilePaths[CurrentPath] == nil then
Engine.FilePaths[CurrentPath] = {
Name = NextFolder,
Parent = CurrentFolder.Parent,
Folder = CurrentFolder,
}
end
end
FolderData = Engine.FilePaths[CurrentPath]
end
local InstancedRemote
if FolderData[string.reverse(NAME)] ~= nil and FolderData[string.reverse(NAME)]:IsA("BindableEvent") then --FolderData.Folder:FindFirstChild(string.reverse(NAME)) ~= nil and FolderData.Folder[string.reverse(NAME)]:IsA("BindableEvent") then
InstancedRemote = RbxUtility:CreateSignal()
FolderData[NAME] = InstancedRemote
elseif FolderData[NAME] ~= nil then
InstancedRemote = FolderData[NAME]
FolderData[NAME] = InstancedRemote
else
if CLASSTYPE == "ScriptSignal" then
InstancedRemote = RbxUtility:CreateSignal()
local BindableEvent = Instance.new("BindableEvent") -- This BindableEvent is only here to screw with explioters.
BindableEvent.Name = string.reverse(NAME)
BindableEvent.Parent = FolderData.Folder
elseif CLASSTYPE == "BindableEvent" then
InstancedRemote = Instance.new("BindableEvent")
InstancedRemote.Name = NAME
InstancedRemote.Parent = FolderData.Folder
elseif CLASSTYPE == "BindableFunction" then
InstancedRemote = Instance.new("BindableFunction")
InstancedRemote.Name = NAME
InstancedRemote.Parent = FolderData.Folder
elseif CLASSTYPE == "RemoteEvent" then
InstancedRemote = Instance.new("RemoteEvent")
InstancedRemote.Name = NAME
InstancedRemote.Parent = FolderData.Folder
elseif CLASSTYPE == "RemoteFunction" then
InstancedRemote = Instance.new("RemoteFunction")
InstancedRemote.Name = NAME
InstancedRemote.Parent = FolderData.Folder
end
FolderData[NAME] = InstancedRemote
if Engine.RunMode == "Server" and not DO_NOT_REPLICATE_TO_CLIENTS and CLASSTYPE ~= "ScriptSignal" then
-- TO-DO: Replicate to clients
FuryReplicationUpdate:FireAllClients(
"RemoteCreated", {PATH, NAME, CLASSTYPE}
)
end
end
return InstancedRemote
end
function Engine:GetMouse()
–if Engine.RunMode == “Client” then
if Engine.Mouse == nil then
Engine.Mouse = require(script:WaitForChild(“Mouse”))
end
return Engine.Mouse
–else
– error(“Only clients can request the mouse.”)
–end
end
function Engine:RenderPartBetweenPoints(A,B, KeepTime)
local beam = Instance.new(“Part”, workspace:FindFirstChild(“RayIgnore”) or nil)
beam.BrickColor = BrickColor.new(“Bright red”)
beam.Material = “Neon”
beam.Transparency = 0.25
beam.Anchored = true
beam.Locked = true
beam.CanCollide = false
local distance = (A - B).magnitude
beam.Size = Vector3.new(0.2, 0.2, distance)
beam.CFrame = CFrame.new(A, B) * CFrame.new(0, 0, -distance / 2)
delay(KeepTime or 1,function() beam:Destroy() end)
end
function Engine:Raycast(Position, Direction, IgnoreList, Visualize, RenderTime, terrainCellsAreCubes, ignoreWater)
if IgnoreList == nil then
IgnoreList = {}
end
local Part, Point, Surface = game.Workspace:FindPartOnRay(Ray.new(Position, Direction), terrainCellsAreCubes or false, ignoreWater or false)
if Visualize then
Engine:RenderPartBetweenPoints(Position, Point, RenderTime)
end
return Part, Point, Surface
end
function Engine:RaycastWithIgnoreList(Position, Direction, IgnoreList, Visualize, RenderTime, terrainCellsAreCubes, ignoreWater)
if IgnoreList == nil then
IgnoreList = {}
end
local Part, Point, Surface = game.Workspace:FindPartOnRayWithIgnoreList(Ray.new(Position, Direction),IgnoreList, terrainCellsAreCubes or false, ignoreWater or false)
if Visualize then
Engine:RenderPartBetweenPoints(Position, Point, RenderTime)
end
return Part, Point, Surface
end
function Engine:RaycastWithWhitelist(Position, Direction, IgnoreList, Visualize, RenderTime, terrainCellsAreCubes, ignoreWater)
if IgnoreList == nil then
IgnoreList = {}
end
local Part, Point, Surface = game.Workspace:FindPartOnRayWithWhitelist(Ray.new(Position, Direction),IgnoreList, terrainCellsAreCubes or false, ignoreWater or false)
if Visualize then
Engine:RenderPartBetweenPoints(Position, Point, RenderTime)
end
return Part, Point, Surface
end
– Engine Logic –
local function PopulateFilePaths(RootFolderObject, RootFolderName)
local function Recursive(FolderObject, FilePath, Folder)
if FolderObject:IsA("Folder") then
if Folder == nil then
Folder = {}
end
for FolderIndex, FolderContient in pairs(FolderObject:GetChildren()) do
if FolderObject:IsA("Folder") then
local NewFilePath = FilePath..FolderContient.Name.."/"
Folder[FolderContient.Name] = Recursive(FolderContient, NewFilePath, Folder)
else
Folder[FolderContient.Name] = FolderContient
end
end
Engine.FilePaths[FilePath] = Folder
return Folder
else
return FolderObject
end
end
Recursive(RootFolderObject, RootFolderName.."/")
end
do
PopulateFilePaths(CustomServicesFolder, “Services”)
PopulateFilePaths(CustomObjectsFolder, “Objects”)
PopulateFilePaths(ModulesFolder, “Modules”)
PopulateFilePaths(RemotesFolder, “Remotes”)
for i,v in pairs(Engine.FilePaths) do
print(i,v)
end
end
Engine.RunMode = Engine:GetRunMode()
if Engine.RunMode == “Server” then
FuryReplicationRequest = Instance.new(“RemoteFunction”)
FuryReplicationRequest.Name = “FuryReplicationRequest”
FuryReplicationRequest.Parent = Root
FuryReplicationUpdate = Instance.new("RemoteEvent")
FuryReplicationUpdate.Name = "FuryReplicationUpdate"
FuryReplicationUpdate.Parent = Root
InitialPing = Instance.new("RemoteFunction")
InitialPing.Name = "InitialPing"
InitialPing.Parent = Root
InitialPing.OnServerInvoke = function()
return tick()
end
PingRemote = Instance.new("RemoteFunction")
PingRemote.Name = "PingRemote"
PingRemote.Parent = Root
PingRemote.OnServerInvoke = function()
return tick()
end
Server = Engine
else
FuryReplicationRequest = Root:WaitForChild(“FuryReplicationRequest”, 100)
FuryReplicationUpdate = Root:WaitForChild(“FuryReplicationUpdate”, 100)
InitialPing = Root:WaitForChild(“InitialPing”, 100)
PingRemote = Root:WaitForChild(“PingRemote”, 100)
FuryReplicationUpdate.OnClientEvent:connect(function(ReplicationType, Data)
warn("ReplicationType =", ReplicationType, "= {")
for i,v in pairs(Data) do
print(" "..i,"=",v)
end
print("}")
if ReplicationType == "UpdateFilePaths" then
Engine.FilePaths = Data
elseif ReplicationType == "RemoteCreated" then
warn(ReplicationType)
Engine:CreateRemote(Data[1], Data[2], Data[3])
end
end)
coroutine.resume(coroutine.create(function()
Engine.Ping = 0
local function PingPong()
-- TO-DO: keep this from breaking
LastServerTick2 = LastServerTick1
local s = tick()
LastServerTick1 = PingRemote:InvokeServer(LastServerTick1)
Engine.ServerTick = LastServerTick1
wait(0.2) -- Remotes operate on 1/20th second heartrate.
Engine.Ping = (tick() - s) - 0.2
PingPong()
end
local t = InitialPing:InvokeServer()
LastServerTick2 = LastServerTick1
LastServerTick1 = t
Engine.ServerTick = LastServerTick1
PingPong()
end))
local OnRenderSteppedFunctions = {}
function Engine:AddFunctionToRenderStep(func)
OnRenderSteppedFunctions[#OnRenderSteppedFunctions+1] = func
end
RunService.RenderStepped:connect(function(TimeSinceLastFrame)
Engine.ServerTick = Engine.ServerTick + TimeSinceLastFrame
for i = 1, #OnRenderSteppedFunctions do
OnRenderSteppedFunctions[i](TimeSinceLastFrame)
end
end)
Engine.LocalPlayer = Engine:GetService("Players").LocalPlayer
Client = Engine
end
– print("["…Engine.RunMode…"] FuryReplicationUpdate =",FuryReplicationUpdate)
Engine:PrintVersion()
Engine:PrintRunMode()
return Engine
end
[/code]
For an example of what I am looking for is a much better version of my and MyCheeze’s physics solver that you can see here: Inner Animal Redux - Roblox that can properly handle moving objects and not teleport through walls (you will see that if you jump a few frames while hitting a corner) and be able to create a physics solver that both server and client can use to move characters. By that I mean for the client to use the solver to move its own local character, push open doors, etc and for the server to be able to use it do the same with its own characters (NPCs). You will also be handling the physics and replication of thrown weapons/objects as they spin around in the air while thrown and need to react somewhat believable when they hit an object by “bouncing” off the object.
#4.) I’m interested. How and when do I get monies?
I’m very much aware that money is of a big thing when it comes to hiring developers… And to be 100% honest with you I only have a total of 740 robucks in combined total from GuestCapone and GuestCorleone as shown below:
and with only another month of BC left, I’m only going to make another 789 more robucks from the daily login bonus (if I log in every single day) on GuestCapone… which is a lovely total of 1,529 robucks. Not a whole lot I know, which is why I’m not even going to bother offering R$ upfront because I don’t even have enough to make a newbie jump in joy. What about USD you might be wondering? To also be honest, I only have 54 dollars in cash, mostly in quarters and 0 in my bank account, I had around 750 USD earlier this year but I had to pay the rent for a month due to some medical bills draining my parents account. I also can’t give any of that money because when the game is done I’ll be spending every penny I have (literally) on advertisement for Inner Animal. And before anyone suggests getting a job, I have an unpaying job with my father, one I would love to dump for a paying one but cant because its literally paying the rent and thats it, and if I don’t help him, we literally don’t have a home anymore… With all of that being said, the short answer is you get paid when the game is released, after that it will be every 30 days with 10-40% of all earnings made depending on how much you’re willing to do the work, and how you work with me.
If percentage isn’t enough, only thing left to offer is giving you my best effort at untextured (I’m not that good at texturing) 3D models if you have reference images… I know its not a lot but it is all I can offer. If you’re interested you can send me a PM here on the devforums, and if you know someone who is interested but are not on the devforums, here is a tweet that explains what I am needing and have them send me a direct message on Twitter @guestcapone if they are interested. I look forward to hearing from someone and will answer any questions that you have.