| Section | Description | | ———– | ———– | | Experience | Company Projects - No Source | | Projects | Personal and Contract + Source Available | | Demonstrations | Videos of Demonstrating Systems where code is not available |
Game Design and Production Instructor
Looty Games - Avatar Air Bender Fighting Game (Lua)
Twin Atlas - MTV, Teen Wolf, West Elm (Lua)
WEREWOLF ESCAPE PROMO | WEREWOLF ESCAPE GAME
WELD ELM PROMO | WEST ELM GAME
CUDO - CUDO World RPG (Unreleased) (Lua)
Fishing Simulator (Lua, JavaScript)

A project for testing networking and top-down gameplay.
SOURCE: https://github.com/TechProgramming/Fallen-Star-Horizon
// Fill out your copyright notice in the Description page of Project Settings.
#include "FSHInteractComponent.h"
#include "Components/SphereComponent.h"
#include "FallenStarHorizon/Character/FSHCharacter.h"
#include "FallenStarHorizon/UserInterface/HUD/FSHInteractOption.h"
#include "FallenStarHorizon/Weapon/FSHWeapon.h"
// Sets default values for this component's properties
UFSHInteractComponent::UFSHInteractComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts
void UFSHInteractComponent::BeginPlay()
{
Super::BeginPlay();
AActor* Owner = this->GetOwner();
SetWidgetVisible(false, nullptr);
if (Owner && Owner->HasAuthority())
{
InteractionSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
InteractionSphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
InteractionSphere->OnComponentBeginOverlap.AddDynamic(this, &UFSHInteractComponent::OnSphereBeginOverlap);
InteractionSphere->OnComponentEndOverlap.AddDynamic(this, &UFSHInteractComponent::OnSphereEndOverlap);
}
}
void UFSHInteractComponent::SetWidgetVisible(bool bIsVisible, AActor* Instigator)
{
AActor* Owner = this->GetOwner();
if (Owner && InteractPromptWidget)
{
InteractPromptWidget->SetVisibility(bIsVisible);
if (auto* Character = Cast<AFSHCharacter>(Instigator))
{
switch (InteractObjectType)
{
case EInteractObjectType::EIOT_Weapon:
{
auto* Weapon = Cast<AFSHWeapon>(GetOwner());
Character->PushReplicationOverlappingWeapon(Weapon);
return;
}
case EInteractObjectType::EIOT_Custom:
{
return;
}
}
}
}
}
void UFSHInteractComponent::SetCanInteract(bool bNewIsActive)
{
bActive = bNewIsActive;
//UFSHInteractComponent::SetWidgetVisible(bIsVisible, Instigator);
}
bool UFSHInteractComponent::GetCanInteract()
{
return bActive;
}
void UFSHInteractComponent::Trigger(AActor* Instigator)
{
OnTriggered.Broadcast(Instigator);
Triggered(Instigator);
}
void UFSHInteractComponent::Triggered_Implementation(AActor* Instigator)
{
}
void UFSHInteractComponent::OnSphereBeginOverlap(UPrimitiveComponent* OverlapComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBoxIndex, bool bFromSweep, const FHitResult& SweepResult)
{
AActor* Owner = this->GetOwner();
AFSHCharacter* FSHCharacter = Cast<AFSHCharacter>(OtherActor);
if (Owner && FSHCharacter)
{
if(UFSHInteractOption* InteractionOption = Cast<UFSHInteractOption>(InteractPromptWidget->GetUserWidgetObject()))
{
InteractionOption->ActionTextBlock->SetText(FText::FromString(DisplayText));
// Push
FSHCharacter->InteractableComponentsNearby.Emplace(this);
}
}
}
void UFSHInteractComponent::OnSphereEndOverlap(UPrimitiveComponent* OverlapComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBoxIndex)
{
AActor* Owner = this->GetOwner();
AFSHCharacter* FSHCharacter = Cast<AFSHCharacter>(OtherActor);
if (Owner && FSHCharacter)
{
//if(GEngine) {
// GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT("END 1"));
//}
if(UFSHInteractOption* InteractionOption = Cast<UFSHInteractOption>(InteractPromptWidget->GetUserWidgetObject()))
{
//if(GEngine) {
// GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT("END 2"));
//}
const int32 ItemIndex = FSHCharacter->InteractableComponentsNearby.IndexOfByKey(this);
FSHCharacter->InteractableComponentsNearby.RemoveAt(ItemIndex);
SetWidgetVisible(false, nullptr);
}
}
}
// Called every frame
void UFSHInteractComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}

SOURCE: https://github.com/TechProgramming/VRProjectExample
Source: https://github.com/TechProgramming/VRProjectExample/blob/master/Source/VRProjectExample/VRPLever.cpp
#include "VRPLever.h"
#include <Kismet/KismetMathLibrary.h>
// Sets default values
AVRPLever::AVRPLever()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
Lever = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Lever"));
GrabComponent = CreateDefaultSubobject<UVRPGrabComponent>(TEXT("GrabComponent"));
RootComponent = Scene;
Lever->SetupAttachment(Scene);
GrabComponent->SetupAttachment(Lever);
}
// Called when the game starts or when spawned
void AVRPLever::BeginPlay()
{
Super::BeginPlay();
GrabComponent->OnGrabbedDelegate.AddDynamic(this, &ThisClass::OnGrabbed);
GrabComponent->OnDroppedDelegate.AddDynamic(this, &ThisClass::OnDropped);
}
// Called every frame
void AVRPLever::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AVRPLever::UpdateRotation()
{
if (!MotionControllerRef || !bIsGrabbed) {
return;
}
FVector ControllerWorldPos = MotionControllerRef->GetComponentLocation();
FVector LeverPivotPos = Scene->GetComponentLocation();
FVector ControllerLocalPos = Scene->GetComponentTransform().InverseTransformPosition(ControllerWorldPos);
FVector MovementDelta = ControllerLocalPos - InitialGrabOffset;
float RotationDelta = -MovementDelta.X * RotationSensitivity;
float NewPitch = FMath::Clamp(
InitialLeverRotation.Pitch + RotationDelta,
-70.0f,
70.0f
);
FRotator NewRotation = FRotator(
NewPitch,
InitialLeverRotation.Yaw,
InitialLeverRotation.Roll
);
Lever->SetRelativeRotation(NewRotation);
}
void AVRPLever::OnGrabbed(UMotionControllerComponent* MotionController)
{
MotionControllerRef = MotionController;
GetWorldTimerManager().SetTimer(UpdateRotationTimerHandle, this, &ThisClass::UpdateRotation, 0.01f, true);
bIsGrabbed = true;
InitialLeverRotation = Lever->GetRelativeRotation();
FVector ControllerWorldPos = MotionController->GetComponentLocation();
InitialGrabOffset = Scene->GetComponentTransform().InverseTransformPosition(ControllerWorldPos);
}
void AVRPLever::OnDropped()
{
GetWorldTimerManager().ClearTimer(UpdateRotationTimerHandle);
MotionControllerRef = nullptr;
bIsGrabbed = false;
}

SOURCE: https://github.com/TechProgramming/NecromancersTomb
WEBSITE: https://clanlabs.co/
GRAPH: https://blueprintue.com/blueprint/njj2tnol/
GRAPH: https://blueprintue.com/blueprint/ruoybuci/

SOURCE CODE: https://github.com/TechProgramming/Clan-Labs-Snapshot

SOURCE: https://github.com/TechProgramming/Clan-Labs-Snapshot/blob/main/ClanLabs-API/src/services/user.service.js
const setUserExperience = async (group, userId, amount) => {
// Get User
let userData = await getUserById(group, userId);
if(!userData) {
userData = await createUserById(group, userId);
if(userData == "User doesn't exist on Roblox") return userData;
}
// XP Magic? - Thanks Doge
if(amount < userData.experience) {
amount = userData.experience - amount;
amount = -amount;
} else if(amount > userData.experience) amount - amount - userData.experience;
// Clamp to Settings
let maxChangeAmount = group.settings.experienceAPIAwardLimit;
if(amount >= 0) amount = require('../utils/clamp.js')(amount, 0, maxChangeAmount)
else amount = require('../utils/clamp.js')(amount, -maxChangeAmount, 0);
// Increment XP
let newUserData = await userDB.incrementXP(group, userId, amount);
// Create Audit Log
auditService.createAudit(group, {
targetId: newUserData.userid,
targetName: newUserData.username,
auditString: `(**API**): Changed **${newUserData.username}**'s XP from **${userData ? userData.experience : "0"}** to **${newUserData.experience + amount}**!`,
});
// Auto Rank Member
await (require('../utils/autoRank.js')(group, newUserData));
// Get Updated User
newUserData = await getUserById(group, userId);
return newUserData;
}
SOURCE CODE: https://github.com/TechProgramming/Area-24
GAME: https://www.roblox.com/games/7027296354/SCP-Area-24
This is a project that was developed as a fan game for an SCP community where you could roleplay different teams and SCP creatures.
SOURCE: https://github.com/TechProgramming/Area-24/blob/master/src/Server/Server/Services/Core/SCP/Entities/035.luau
function SCP:BreachWander()
if script:GetAttribute("Debug_Print") then
print("035 Breach Wandering");
end;
local scpHumanoidRootPart = self.Agent.Character.HumanoidRootPart;
local scpPosition = scpHumanoidRootPart.Position;
if not self.Agent.Directive then
if script:GetAttribute("Debug_Print") then
print("035 No Directive is Available, requesting new Directive");
end;
math.randomseed(tick());
local nodeTarget = SCPNodemap:GetChildren()[math.random(1, #SCPNodemap:GetChildren())]:GetAttribute("NodePosition");
local success = AgentService:CreateDirective(self.Agent, nodeTarget, SCPNodemap);
self:BreachWander();
else
if script:GetAttribute("Debug_Print") then
print("035 Directive Available, continuing");
end;
SCP:DebugDirective();
if self.Agent.WaypointType ~= "Breach" then
local nodeTarget = self.Agent.Directive[1];
local success = AgentService:CreateWaypoints(self.Agent,
Vector3.new(math.random(scpPosition.X - PATH_START_BIAS, scpPosition.X + PATH_START_BIAS), scpPosition.Y,math.random(scpPosition.Z - PATH_START_BIAS, scpPosition.Z + PATH_START_BIAS)),
Vector3.new(math.random(nodeTarget.X - PATH_END_BIAS, nodeTarget.X + PATH_END_BIAS), nodeTarget.Y, math.random(nodeTarget.Z - PATH_END_BIAS, nodeTarget.Z + PATH_END_BIAS)),
"Breach");
if success then
self:BreachWander();
else
self:RemoveWaypoint();
if not self.Agent.Directed then
self.Agent.Directed = 0;
end;
self.Agent.Directed += 1;
if self.Agent.Directed >= DIRECT_TIMEOUT then
AgentService:ClearDirective(self.Agent);
end;
end;
else
self:DoWaypoint();
if (Normalize(self.Agent.Directive[1]) - Normalize(scpHumanoidRootPart.Position)).magnitude <= DIRECTIVE_REACH_RADIUS then
if math.random(1, 100) <= SPEECH_CHANCE then
SCP:Speak();
end;
table.remove(self.Agent.Directive, 1);
self:RemoveWaypoint();
if #self.Agent.Directive <= 0 then
AgentService:ClearDirective(self.Agent);
else
self:BreachWander();
end;
end;
end;
end;
end