Zombies+ Icon

Zombies+

Minecraft Mod

Key Highlights

140,000+
Total Downloads
Java
Programming Language
IntelliJ
Development Environment

Project Overview

Zombies+ is a comprehensive Minecraft mod that enhances the zombie experience in the game. This project represents a significant milestone in my Java development journey, achieving over 140,000 downloads across multiple platforms. The mod introduces new zombie variants, behaviors, and gameplay mechanics that seamlessly integrate with Minecraft's existing systems.

Through this project, I refined my understanding of Java programming, object-oriented design patterns, and the Minecraft modding ecosystem. The mod's success demonstrates my ability to create engaging content that resonates with a large community of players.

Skills & Learning Outcomes

  • Advanced Java programming and object-oriented design principles
  • Proficiency with IntelliJ IDEA Community Edition development environment
  • Minecraft Forge API and modding framework expertise
  • Game balance and player experience design
  • Large-scale community management and user feedback integration (140k+ downloads)

Project Gallery

Runner Zombie
Shadow Zombie
Cave Zombie
Miner Zombie
Brute Zombie
Crossbow Zombie
Weak Zombie
Axe Zombie
Shrieker Zombie
Baby Zombie
Pale Cave Zombie
Slow Zombie
Crawler Zombie
Bow Zombie
Vile Zombie
Sword Zombie
Leaper Zombie

The abstract base class that all zombie variants extend from. This class handles core zombie behavior, animations, and spawn conditions:

package net.trial.zombies_plus.entity.custom;

import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.*;

public abstract class abstractZombieEntity extends Zombie {
    
    public final AnimationState idleAnimationState = new AnimationState();
    private int idleAnimationTimeout = 0;
    private boolean hasSetupAnim = false;
    private boolean leftArmVisible = true;
    private boolean rightArmVisible = true;
    private static boolean isAggro = false;

    public abstractZombieEntity(EntityType<? extends Zombie> pEntityType, Level pLevel) {
        super(pEntityType, pLevel);
        this.refreshDimensions();
    }
    
    public ResourceLocation getTexture(){
        return new ResourceLocation("minecraft", "textures/entity/zombie/zombie.png");
    }
   
   public static boolean getAggressiveState(){
    return isAggro;
   }

   @Override
   public boolean isAggressive() {
        isAggro = super.isAggressive();
        return isAggro;
   }

   @Override
   protected boolean convertsInWater() {
       return false;
   }

   public float rotlerpRad(float pAngle, float pMaxAngle, float pMul) {
    float f = (pMul - pMaxAngle) % ((float)Math.PI * 2F);
    if (f < -(float)Math.PI) {
       f += ((float)Math.PI * 2F);
    }
    if (f >= (float)Math.PI) {
       f -= ((float)Math.PI * 2F);
    }
    return pMaxAngle + pAngle * f;
 }

    @Override
    public void tick() {
        super.tick();
        if(this.level().isClientSide()) {
            setupAnimationStates();
        }
    }

    protected void setupAnimationStates() {
        if(this.idleAnimationTimeout <= 0) {
            this.idleAnimationTimeout = this.random.nextInt(40) + 80;
            this.idleAnimationState.start(this.tickCount);
        } else {
            --this.idleAnimationTimeout--;
        }
    }

    @Override
    public void setBaby(boolean pChildZombie) {
        // Prevents baby zombies
    }

    @Override
    protected void updateWalkAnimation(float pPartialTick) {
        float f;
        if(this.getPose() == Pose.STANDING) {
            f = Math.min(pPartialTick * 6F, 1f);
        } else {
            f = 0f;
        }
        this.walkAnimation.update(f, 0.2f);
    }

    public static boolean canSpawnDuringDay(ServerLevelAccessor pLevel, BlockPos pPos, RandomSource pRandom) {
        DimensionType dimensiontype = pLevel.dimensionType();
        int i = dimensiontype.monsterSpawnBlockLightLimit();
        if (i < 15 && pLevel.getBrightness(LightLayer.BLOCK, pPos) > i) {
            return false;
        }
        int j = pLevel.getLevel().isThundering() ? pLevel.getMaxLocalRawBrightness(pPos, 10) : pLevel.getMaxLocalRawBrightness(pPos);
        return j <= dimensiontype.monsterSpawnLightTest().sample(pRandom);
    }

    @Override
    public EntityDimensions getDimensions(Pose pPose) {
        return EntityDimensions.scalable(DEFAULT_BB_WIDTH, 2f);
    }
}

Click to expand and view the full code implementation.

Junk Food Additions Icon

Junk Food Additions

Minecraft Mod

Key Highlights

3,000+
Total Downloads
Java
Programming Language
IntelliJ
Development Environment

Project Overview

Junk Food Additions is a fun and creative Minecraft mod that adds a variety of junk food items to the game. This project served as an excellent learning experience in texture creation and item implementation within Minecraft's framework. Each food item was carefully designed with custom textures that match the game's pixelated aesthetic.

The mod demonstrates my ability to create cohesive content that enhances gameplay while maintaining the vanilla Minecraft experience. Through this project, I developed skills in digital art, texture painting, and game asset integration.

Skills & Learning Outcomes

  • Java programming and Minecraft Forge API implementation
  • Digital texture creation and pixel art design
  • Game item balancing and crafting recipe design
  • Asset integration and resource pack management
  • Community engagement and mod distribution (3k+ downloads)
Cardboard Momoa Icon

Cardboard Momoa

Lethal Company Mod

Key Highlights

40,000+
Total Downloads
C#
Programming Language
VS 2022
Development Environment

Project Overview

Cardboard Momoa is a Lethal Company mod that introduces a custom AI entity with advanced pathfinding capabilities. This project represents a significant achievement in AI programming, reaching over 40,000 downloads on Thunderstore. The mod features sophisticated enemy behavior that challenges players while maintaining game balance.

Through developing this mod, I refined my skills in AI pathfinding algorithms, C# programming, and Unity engine integration. The project demonstrates my ability to create complex game mechanics that enhance player experience and engagement.

Skills & Learning Outcomes

  • Advanced AI pathfinding algorithms and navigation systems
  • C# programming and Unity engine integration
  • Visual Studio 2022 development workflow and debugging
  • Game AI behavior design and state machine implementation
  • Performance optimization for real-time AI systems (40k+ downloads)

Here's the core setup method for configuring the Jason Momoa AI entity:

jasonBehaviour.yellSound= scream;
jasonBehaviour.scrapeSound= scrape;
jasonBehaviour.eye= momoaBody.transform.Find("Eye");
jasonBehaviour.animStopPoints= momoaBody.transform.Find("AnimContainer").GetComponent<AnimationStopPoints>();
momoaBody.GetComponent<EnemyAICollisionDetect>().mainScript= jasonBehaviour;
jasonBehaviour.mainCollider= momoaBody.GetComponent<BoxCollider>();

LethalLib.Modules.NetworkPrefabs.RegisterNetworkPrefab(jasonMomoa.enemyPrefab);
Utilities.FixMixerGroups(jasonMomoa.enemyPrefab);
Enemies.RegisterEnemy(jasonMomoa, Configuration.configSpawnWeight.Value, Levels.LevelTypes.All, null, null);

Click to expand and view the full code implementation.

Gallery

Cardboard Momoa gameplay showing multiple entities with targeting UI
Scraptopia Icon

Scraptopia

Lethal Company Mod

Key Highlights

8,000+
Total Downloads
C#
Programming Language
VS 2022
Development Environment

Project Overview

Scraptopia is a custom scrap mod for Lethal Company that introduces unique and creative collectible items to the game. This project served as my introduction to C# programming and the Visual Studio development environment. Through this mod, I learned the fundamentals of 3D modeling, texture painting, and game asset integration.

The mod features custom-designed items including various themed donuts and decorative objects that players can discover and collect throughout their missions. Each item was carefully modeled and textured to fit seamlessly into the game's aesthetic while adding personality to the gameplay experience.

Skills & Learning Outcomes

  • Introduction to C# programming language and object-oriented concepts
  • Proficiency with Visual Studio 2022 development environment
  • 3D modeling and texture painting for game assets
  • Game modding and asset integration workflows
  • Community engagement and user feedback management (8,000+ downloads)

Here's the core method used to register custom scrap items in the mod:

void setupScrapItem(Item item, int rarity)
{
    if (item != null && rarity > 0)
    {
        NetworkPrefabs.RegisterNetworkPrefab(item.spawnPrefab);
        Utilities.FixMixerGroups(item.spawnPrefab);
        Items.RegisterScrap(item, rarity, Levels.LevelTypes.All);
        Logger.LogInfo($"{item} has been loaded!");
    }
}

Click to expand and view the full code implementation.

UncannyJackBlack Icon

UncannyJackBlack

Lethal Company Mod

Key Highlights

12,000+
Total Downloads
C#
Programming Language
VS 2022
Development Environment

Project Overview

UncannyJackBlack is a Lethal Company mod that introduces a custom AI entity with advanced animations and pathfinding systems. This project served as my introduction to AI animation programming and complex behavior systems. The mod features a unique enemy with fluid animations and intelligent navigation that creates engaging gameplay moments.

Through this project, I learned the fundamentals of animation state machines, AI behavior trees, and pathfinding algorithms. The mod's success with over 12,000 downloads demonstrates the quality of the implementation and player engagement.

Skills & Learning Outcomes

  • Introduction to AI animation systems and state machines
  • AI pathfinding algorithms and navigation mesh integration
  • C# programming and Unity animation controller setup
  • Behavior tree design and AI decision-making systems
  • Game balance and enemy design principles (12k+ downloads)

This code implements the chase behavior with a 20-second timeout system. Jack Black acts as a "streak killer" - if no kill occurs within 20 seconds, he returns to wandering mode, creating dynamic gameplay tension.

PlayerControllerB closestPlayer = GetClosestPlayer();
SetMovingTowardsTargetPlayer(closestPlayer);
ModMain.LOGGER.LogInfo(sinceLastKill);

if ((Time.time - sinceLastKill) >= 20f)
{
    ModMain.LOGGER.LogInfo("Switching back to wandering!");
    this.creatureAnimator.SetTrigger("stopChase");
    SwitchToBehaviourState((int)behaviourStates.wanderingState);
    StartSearch(base.transform.position, this.searchRoutine);
    break;
}

audioSource.PlayOneShot(screamAudio);
StopSearch(this.searchRoutine);
this.agent.speed= chaseSpeed;
ModMain.LOGGER.LogInfo("Chasing closest player");
break;

Click to expand and view the full code implementation.

Gallery

Uncanny Jack Black in-game with glowing eyes
Escape Jason Icon

Escape Jason

Roblox Game

Key Highlights

1,000+
Total Visits
LUA
Programming Language
Roblox Studio
Development Environment

Project Overview

Escape Jason is a horror-themed Roblox game that challenges players to escape from a relentless pursuer. This project represents my introduction to game development on the Roblox platform and LUA programming. The game features atmospheric environments, suspenseful gameplay mechanics, and engaging chase sequences that keep players on edge.

Through developing this game, I learned the fundamentals of Roblox Studio, LUA scripting, and game design principles. The project successfully attracted over 1,000 visits and built a small community of around 100 players who enjoyed the horror experience.

Skills & Learning Outcomes

  • Introduction to LUA programming and scripting fundamentals
  • Proficiency with Roblox Studio development environment
  • Game design principles and horror game mechanics
  • Level design and atmospheric environment creation
  • Community building and player engagement (1,000+ visits, ~100 community members)

Project Gallery

Military Camp Area
Dark Interior Scene
Farm Environment
Nighttime Forest Scene

The complete AI pathfinding script that controls Jason's behavior, including player detection, raycasting for line of sight, chasing mechanics, waypoint patrolling, anti-stuck mechanisms, ground smash effects, and jumpscare functionality:

local jason = script.Parent
local Enemy = jason:WaitForChild("Enemy")
local jasonisStuck = false

-- set the bounds of the model

local AgentParameters = {
    ["AgentRadius"] = 7,
    ["AgentHeight"] = 5,
    ["AgentCanJump"] = true
}

-- grab the walkpoints of that the AI will move to
local Waypoints = jason:WaitForChild("Waypoints").Value:GetChildren()

-- grab the necessary services

local PathFindingService = game:GetService("PathfindingService")
local DebrisService = game:GetService("DebrisService")
-- SimplePath is a Roblox pathfinding module developed by other creators
-- The link to SimplePath is here: https://grayzcale.github.io/simplepath/
local SimplePath = require(game.ServerStorage:WaitForChild("SimplePath"))
local Players = game:GetService("Players")
local MaxDistance = jason:WaitForChild("MaxDistance")
local DefaultSpeed = jason:WaitForChild("DefaultSpeed")
local ChaseSpeed = jason:WaitForChild("ChaseSpeed")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RoundEventsFolder = ReplicatedStorage:WaitForChild("RoundEvents")
local JumpscareEvent = RoundEventsFolder:WaitForChild("Jumpscare")
local ChasedTarget, ChasingTime = nil, 0
local TimeOutCount = 0
local MaxChasingTime = jason:WaitForChild("MaxChasingTime").Value

-- function that checks to see if we can see a player to chase
local function CanSeeTarget(Target)
    -- roblox raycasts can be used to check of line of sight between two positions

    local Origin = jason.HumanoidRootPart.Position
    local Direction = (Target.HumanoidRootPart.Position - Origin).unit * MaxDistance.Value
    local RayParams = RaycastParams.new()

    RayParams.FilterType = Enum.RaycastFilterType.Blacklist
    RayParams.FilterDescendantsInstances = {Enemy}
    local RaycastResult = game.Workspace:Raycast(Origin, Direction, RayParams)
    if RaycastResult then
        local RaycastInstance = RaycastResult.Instance
        if RaycastInstance then
            if RaycastInstance:IsDescendantOf(Target) and not Target:FindFirstChild("Bubble Shield") then
                ChasingTime = 0
                jason.ChasingTime.Value = ChasingTime
                return true
            else
                if ChasedTarget then
                    if ChasedTarget == game.Players:GetPlayerFromCharacter(Target).Name then
                        ChasingTime += 1
                        jason.ChasingTime.Value = ChasingTime
                        if ChasingTime > MaxChasingTime then
                            return "LostInterest"
                        else
                            return true
                        end
                    end
                end
            end
        end
    end
end

-- gets all players in the game and measures their distance
-- returns the closest player
local function LocatePlayers()
    local ClosestDistance, ClosestPlayer = math.huge, nil
    for index, player in pairs(Players:GetPlayers()) do
        local character = player.Character
        if character then
            local Distance = (jason.HumanoidRootPart.Position - character.HumanoidRootPart.Position).Magnitude
            if Distance < MaxDistance.Value then
                local CanSeeState = CanSeeTarget(character)
                if CanSeeState == "LostInterest" then
                    return CanSeeState
                end
                if Distance < ClosestDistance and CanSeeState == true then
                    ClosestDistance, ClosestPlayer = Distance, player
                end
            end
        end
    end
    return ClosestPlayer
end

-- we use SimplePath to create the paths
local MainPath = SimplePath.new(jason)
MainPath.Visualize = false

local AttackPath = SimplePath.new(jason)
AttackPath.Visualize = false


-- just in case the AI gets stuck on an object, we resolve by jumping
local function FixPath(errorType)
    if errorType == "AgentStuck" then
        Enemy:ChangeState(Enum.HumanoidStateType.Jumping)
        wait(.5)
        Enemy:ChangeState(Enum.HumanoidStateType.Jumping)
    end
end

AttackPath.Error:Connect(FixPath)
MainPath.Error:Connect(FixPath)

local function GetWaypoint()
    local chosenWaypoint = Waypoints[math.random(1, #Waypoints)]
    return chosenWaypoint
end


-- we chase a player down
local function AttackPlayer(TargetedCharacter)
    Enemy.WalkSpeed = ChaseSpeed.Value
    local TargetedPlayer = Players:GetPlayerFromCharacter(TargetedCharacter)
    if TargetedCharacter:FindFirstChild("Humanoid") then
        TargetedCharacter.Humanoid:SetAttribute("Chased", true)
    else
        return false
    end
    if TargetedPlayer then
        ChasedTarget = TargetedPlayer.Name
    end
    MainPath:Stop()

    -- difference apperances based on game difficulty and map
    jason.Head.Transparency = 1
    jason.Head.face.Transparency = 1
    if jason.Difficulty.Value == "Nightmare" then
        jason.Nightmare.Transparency = 0
    else
        jason.Angry.Transparency = 0
    end
    if jason.Map.Value == "jason's Carnival" then
        jason.Nightmare.Transparency = 1
        jason.Angry.Transparency = 1
        jason.Carnival.Transparency = 0
    end
    jason.Head.Scream:Play()
    -- continous chasing
    local Distance
    repeat
        local TargetedPlayer = LocatePlayers()
        if ChasedTarget then
            if TargetedPlayer then
                if TargetedPlayer == "LostInterest" then
                    ChasingTime = 0
                    game.Workspace:FindFirstChild(ChasedTarget).Humanoid:SetAttribute("Chased", nil)
                    ChasedTarget = nil
                    jason.Head.Transparency = 0
                    jason.Head.face.Transparency = 0
                    jason.Angry.Transparency = 1
                    print("Broken")
                    break
                end
                if ChasedTarget == TargetedPlayer.Name then
                else
                    game.Workspace:FindFirstChild(ChasedTarget).Humanoid:SetAttribute("Chased", nil)
                    AttackPlayer(TargetedPlayer)
                end
            end
        end
        Distance = (jason.HumanoidRootPart.Position - TargetedCharacter.HumanoidRootPart.Position).Magnitude
        AttackPath:Run(TargetedCharacter.HumanoidRootPart, AgentParameters)
    until Distance < 10 or ChasedTarget == nil
    -- we eliminate the player and jumpscare them
    if Distance < 10 then
        TargetedCharacter:BreakJoints()
        JumpscareEvent:FireClient(Players:GetPlayerFromCharacter(TargetedCharacter))
    end
end

-- we walk to waypoint on the map
local function WalkToGoal(CurrentWaypoint)
    -- cosmetic/appearance changes
    local TargetedPlayer
    jason.Head.Transparency = 0
    jason.Head.face.Transparency = 0
    jason.Angry.Transparency = 1
    jason.Nightmare.Transparency = 1
    jason.Carnival.Transparency = 1
    jason.Head.Scream:Stop()
    -- calm patrolling
    repeat
        Enemy.WalkSpeed = DefaultSpeed.Value
        local Distance = (jason.HumanoidRootPart.Position - CurrentWaypoint.Position).Magnitude
        MainPath:Run(CurrentWaypoint, AgentParameters)
        TargetedPlayer = LocatePlayers()
        if TargetedPlayer then
            break
        end
    until Distance < 5
    if TargetedPlayer then
        local TargetedCharacter = TargetedPlayer.Character
        if TargetedCharacter and TargetedCharacter.Humanoid.Health > 0 then
            AttackPlayer(TargetedCharacter)
        end
    end
end

-- patrol (as in just walk to waypoints around the map scanning for players)
local function Patrol()
    local CurrentWaypoint = GetWaypoint()
    WalkToGoal(CurrentWaypoint)
end

local StuckTimer = 0
local NewPosition, OldPosition

task.spawn(function()
        while task.wait(1) do
            NewPosition = jason.HumanoidRootPart.Position
            if OldPosition then
                local positionDifference = (NewPosition - OldPosition).Magnitude
                if positionDifference < 2 then
                    StuckTimer += 1
                end
            end
            if StuckTimer == 2 then
                jason.HumanoidRootPart.CFrame = jason.HumanoidRootPart:GetPivot() + Vector3.new(math.random( -5, 5), 0, math.random( -5, 5))
                StuckTimer = 0
            end
            OldPosition = jason.HumanoidRootPart.Position
        end
    end
end

local DebrisService = game:GetService("DebrisService")
local PhysicsService = game:GetService("PhysicsService")

jason.HumanoidRootPart.CollisionGroup = "jason"

-- ground smash effects
Enemy.StateChanged:Connect(function(oldState, newState)
        if oldState == Enum.HumanoidStateType.Freefall and newState == Enum.HumanoidStateType.Landed then
            local function CrashDebris()
                local randomDebrisCount = math.random(5, 13)
                local RaycastParameters = Instance.new("RaycastParams")
                RaycastParameters.FilterType = Enum.RaycastFilterType.Blacklist
                RaycastParameters.FilterDescendantsInstances = {jason}
                local RaycastResult = game.Workspace:Raycast(jason.HumanoidRootPart.Position, Vector3.new(0, -50, 0), RaycastParameters)
                local RaycastInstance
                if RaycastResult then
                    RaycastInstance = RaycastResult.Instance
                end
                local FloorColor
                if RaycastInstance then
                    FloorColor = RaycastInstance.Color
                end
                for i = randomDebrisCount, 0, -1 do
                    local newDebris = Instance.new("Part", game.Workspace)
                    newDebris.Position = jason.HumanoidRootPart.Position - Vector3.new(0, 3, 0)
                    newDebris.Size = Vector3.new(math.random(1, 3), math.random(1, 3), math.random(1, 3))
                    if FloorColor then
                        newDebris.Color = FloorColor
                    end
                    newDebris.Material = Enemy.FloorMaterial
                    local PathfindingMod = Instance.new("PathfindingModifier", newDebris)
                    PathfindingMod.PassThrough = true
                    newDebris.CollisionGroup = "CrashDebris"
                    local Attachment = Instance.new("Attachment", newDebris)
                    local VectorForce = Instance.new("VectorForce", newDebris)
                    VectorForce.ApplyAtCenterOfMass = true
                    VectorForce.Attachment0 = Attachment
                    VectorForce.Force = Vector3.new(0, 2500, 0)
                    delay(.3, function()
                        VectorForce.Force = Vector3.new(0, 0, 0)
                    end)
                    VectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
                    DebrisService:AddItem(VectorForce, 3)
                    DebrisService:AddItem(newDebris, 10)
                end
            end
        end
        jason.Head.LandCrash:Play()
        CrashDebris()
    end
end

-- main loop that does the patrolling
while wait() do
    Patrol()
end