I am a software developer with a passion for game design. A few of my all-time favorite games are Star Wars Galaxies (SOE), World of Warcraft (Blizzard), FFXIV (Square Enix), Jade Dynasty (Perfect World Entertainment), and Baldur’s Gate 3 (Larian Studios).
I am pursuing a Bachelor of Science in Software Engineering at Western Governors University while working full-time as an Infrastructure Engineer at NBME. In this role, I leverage my technical expertise and systems knowledge to deliver exceptional customer service to internal and external users.
Game development has become my creative outlet and passion. I have led multiple projects, managing teams to ensure content is delivered on time while incorporating player feedback to enhance the experience.
Balancing a full-time job and academic responsibilities has honed my focus and self-learning abilities, as evidenced by the projects showcased in this portfolio—all completed outside of my busy schedule.
My studio, Round Table Interactive, LLC, is currently developing Clover: Kingdom of Magic. It is a multiplayer, open-world action RPG. The game world is lightly inspired by animanga like Black Clover. The level design, enemies, spell effects, and storyline are being developed around a “magic” theme. As players first embark on their adventure, they choose which magical “Tome” to use, allowing them access to different abilities based on the chosen element.
In addition to leading a team of 4 for this project, I am the main system designer and programmer, working on all parts of the game. This includes level design, character models and abilities, UX/UI, and gameplay systems.
This is a game that is built on Roblox’s internal engine. Leveraging Lua, I am coding all of the game’s core systems, from NPC pathfinding to ability selection and attack patterns, hitbox detection, client-server data replication, projectile physics simulation, calculating collision detections, and finite-state machines for combat states.
Players will progress vertically by defeating mobs in the open world, leveling their character as they gain exp. At any point in time, players can engage in PvP combat and earn additional rewards. Additionally, C: KoM features a racing system where players can challenge each other in “flying broom” races for rewards.
--[[ Applies a given velocity to the target's abilityLV instance.
This function was also used to develop and test another method of movement involving tweening an anchored HRP, which has since been deprecated. ]]
function physicsModule.applyVelocity(character:Model, physData:{})
local effectsFolder = basicFunctions.getEffectsFolderFromCharacter(character)
local humrp = character:FindFirstChild("HumanoidRootPart")
local humanoid:Humanoid = character:FindFirstChild("Humanoid")
if humrp and humanoid and humanoid.Health > 0 then
local useLinearVelocity = true -- Set to false when testing non-lv movement
local distance:number = physData.knockbackDistance
local duration:number = physData.knockbackDuration
local direction = physData.knockbackDirection
local speed = distance/duration
-- Formula for init velocity is U = 2 * (d / t) - Vf
-- Initial velocity is calculated here so that we can tween down to the final velocity (0) with given duration, while still covering given distance.
local finalVelocity = humanoid.WalkSpeed * (direction).Unit
local initialVelocity = (2 * (distance/duration) - finalVelocity.Magnitude) * (direction).Unit
--print("DEBUG: Applying velocity", initialVelocity, "to", character.Name, ". Final velocity:", finalVelocity)
--print("DEBUG: Final velocity:", finalVelocity)
if duration > 0 then
local lockPart = effectsFolder:FindFirstChild("CharacterLockPart")
lockPart.AlignOrientation.Enabled = false
if useLinearVelocity == true then
local lv = humrp:FindFirstChild("abilityLV")
local timeDelta = 0
local timer = lv.Timer
local maxTime = timer.Max
local startTime = timer.Start
startTime.Value = basicFunctions.convertTo2f(tick())
if timer.Value <= 0 then
-- This is a fresh status, first enable Linear Velocity instance.
lv.VectorVelocity = initialVelocity
lv.Enabled = true
-- Set timer vals
timer.Value = duration
maxTime.Value = duration
-- Start the timer
local timerConn = nil
local function stopTimer()
if lv.Parent == nil then
timerConn:Disconnect()
timerConn = nil
return
end
--print("DEBUG: Clearing timer for physics effect. Timer val left:", timer.Value, "Current time:", tick())
--print("DEBUG Physics effect timer ended. Final vector velocity value:", lv.VectorVelocity)
timer.Value = 0
maxTime.Value = 0
lv.Enabled = false
timerConn:Disconnect()
timerConn = nil
return
end
local elapsed = 0
timerConn = RunService.Heartbeat:Connect(function(dt)
elapsed += dt
if timer.Value > 0 then
if character.Parent == nil or (not humanoid or humanoid.Health <= 0) then
stopTimer()
return
end
timeDelta = elapsed
timer.Value = basicFunctions.convertTo2f(maxTime.Value - timeDelta)
if timer.Value <= 0 then
stopTimer()
return
end
local alphaValue = ts:GetValue(math.min(elapsed / duration, 1), Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local calculatedVelocity = basicFunctions.lerp(initialVelocity, finalVelocity, alphaValue)
lv.VectorVelocity = calculatedVelocity
else
stopTimer()
end
end)
else
--print("DEBUG: Updating timer for NEW vector.")
-- Reset the timer if new effect is being applied.
timer.Value = duration
maxTime.Value = duration
-- Set new velocity val
lv.VectorVelocity = initialVelocity
--print(timer.Value)
--print(maxTime.Value)
end
else -- Tween HRP directly to a set position.
local targetPart = Instance.new("Part")
targetPart.Name = "Target Part"
targetPart.CanCollide = false
targetPart.CanQuery = false
targetPart.CanTouch = false
targetPart.Size = Vector3.new(3, 3, 3)
targetPart.Transparency = 0.4
targetPart.Anchored = true
targetPart.Color = Color3.new(0, 0.517647, 1)
targetPart.CFrame = humrp.CFrame
targetPart.Position = targetPart.CFrame.Position + (direction * distance)
targetPart.Parent = effectsFolder
Debris:AddItem(targetPart, duration + 1)
print("DEBUG: Tweening", character.Name, "to", targetPart.CFrame)
local hrpTween = ts:Create(humrp, TweenInfo.new(duration),{["CFrame"] = targetPart.CFrame})
local tweenCompleted;
if physicsModule.characterList[character] == nil then
physicsModule.characterList[character] = {
isMoving = true
}
else
physicsModule.characterList[character] = {
isMoving = true
}
end
tweenCompleted = hrpTween.Completed:Connect(function(playbackState)
physicsModule.characterList[character].isMoving = false
humrp.Anchored = false
tweenCompleted:Disconnect()
end)
humrp.Anchored = true
hrpTween:Play()
end
end
end
end
--[[Function 'castProjectile' called by casting ability. Performs some of the setup for 'fireProjectile'. Determines what happens when the projectile has reached the end of its path, through the 'castEnded' function.]]
toolsModule.castProjectile = function(stringID:string, abilityPhase:string, abilityData:{}, states:{}, spellVars:{}, keyfrConns:{}, hitboxPart:Part, craterParams:{}, castRotation:CFrame)
-- Enable damage detection
spellVars.canDmg = true
-- Will shoot a projectile so init projectile terminated to false.
spellVars.projectileTerminated = false
-- Face character toward cursor before firing projectile.
if abilityData.followMouse == nil or abilityData.followMouse == true then
local rootPos = humrp.Position
humrp.CFrame = CFrame.new(rootPos, Vector3.new(calculatedCrosshair.X, rootPos.Y, calculatedCrosshair.Z))
end
-- calculate raycast info
local origin, direction = sendRaycastData(spellVars.renderedSpell.PrimaryPart)
local pathBlocked = hitboxModule.detectPartsAtOrigin(player, spellVars.renderedSpell.PrimaryPart)
-- Render on other clients.
reAbilityPhases:FireServer(stringID, abilityData.inputKey, "executeAbility", origin, calculatedCrosshair, pathBlocked, "dmgOn", spellVars)
-- Initialize hit entities data for hitbox
local touchedEntities = {}
local function castEnded(raycastResult:RaycastResult)
--print("DEBUG: Cast terminating.")
spellVars.projectileTerminated = true
if not touchedEntities.Break then
--print("DEBUG: This spell collided with the environment. Generating crater. Total length travelled:", distTravelled, "last point:", lastProjectilePoint)
--print("DEBUG: last position:", cast:GetPosition())
local segments = craterParams.segments
local radius = craterParams.radius
local height = craterParams.height
local width = craterParams.width
local xAngle = craterParams.xAngle
local speed = craterParams.speed
local lifetime = craterParams.lifetime
--spellVars.craterParams = {origin = origin, direction = direction, segments = segments, radius = radius, height = height, width = width, xAngle = xAngle, speed = speed, lifetime = lifetime}
rockModule.crater(nil, nil, segments, radius, height, width, xAngle, speed, lifetime, raycastResult)
else
spellVars.breakEarly = true
end
toolsModule.fadeOut(stringID, abilityPhase, abilityData, true, states, spellVars, keyfrConns)
end
local function lengthChanged(projectileConn:RBXScriptConnection)
if hitboxModule.findInstanceInEffectsFolder(spellVars.renderedSpell, player) then -- Verify that the instance being manipulated is not already destroyed before attempting to move it.
hitboxModule.createHitbox(player, hitboxPart, touchedEntities, stringID, abilityData, origin, direction)
if touchedEntities.Break then
if projectileConn ~= nil then
projectileConn:Disconnect()
projectileConn = nil
end
castEnded()
end
end
end
local maxDistance = 0
if pathBlocked then
--print("DEBUG: Path was blocked.")
else
maxDistance = abilityData["hitbox1"].maxDistance
end
spellVars.projectileFired = true
local raycastParams = RaycastParams.new()
raycastParams.CollisionGroup = "Spells" -- adds the raycast to the "Spells" collision group so that it cannot collide with anything unless specified in the collision group's settings
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {character, effectsFolder}
raycastParams.IgnoreWater = true
raycastParams.RespectCanCollide = true
projectileModule.fireProjectile(spellVars.renderedSpell, origin, direction, maxDistance, abilityData["hitbox1"].speed, castRotation, lengthChanged, castEnded, raycastParams)
end
--[[
Primary projectile mover. Used to move a given model (param_1 projectile) over the length of a specified direction and max distance.
This function creates an update event that fires each frame. The delta in OS time is measured between each frame to ensure that the projectile's speed is not effected by poor framerate.
Additionally, this function uses raycasts to check if the projectile has hit a wall or other object. If so, the projectile is destroyed and the update event is disconnected.
Optionally pass in functions to perform on frame update and on projectile reached last point.
Function "fireProjectile" is called from the toolsModule library "castProjectile" function. castProjectile performs preliminary steps like calculating origin, direction, and determining raycast parameters.
]]
projectileModule.fireProjectile = function(projectile:Model, origin:Vector3, direction:Vector3, maxDistance:number, speed:number, projectileOrientation:CFrame, lengthChangedFunc:"Func", castEndedFunc:"Func", raycastParams:RaycastParams)
for i, child in workspace.Testing.junk:GetChildren() do
child:Destroy()
end
-- Calculate goal position based on given origin, direction, and max distance.
local goalPosition = origin + direction * maxDistance
if projectileModule.debugMode then
-- Create a small part at the point
local smallPart = Instance.new("Part")
smallPart.CanCollide = false
smallPart.CanQuery = false
smallPart.Size = Vector3.new(5, 5, 5) -- Size of the small part
smallPart.Color = Color3.new(1, 0, 0.0156863)
smallPart.Position = goalPosition
smallPart.Anchored = true
smallPart.Parent = workspace.Testing.junk
end
local timeToReachGoal = maxDistance / speed -- Time to reach goal is checked on each frame update. If time elapsed > timeToReachGoal, projectile will stop moving.
-- Look @ goal posiiton before firing projectile.
if projectileOrientation ~= nil then
projectile:PivotTo(CFrame.lookAt(projectile.PrimaryPart.Position, goalPosition) * projectileOrientation)
else
projectile:PivotTo(CFrame.lookAt(projectile.PrimaryPart.Position, goalPosition))
end
local elapsed = 0
local originalCFrame = projectile:GetPivot()
local numSteps = 0
--print("DEBUG: Firing projectile. Time to reach goal:", timeToReachGoal, "seconds.")
local conn = nil
conn = runService.Heartbeat:Connect(function(dt) -- Called after every frame update. 'dt' is the time delta (in seconds) between last update and current update.
elapsed += dt
if elapsed < timeToReachGoal then
if projectile ~= nil and projectile.Parent ~= nil then
local currentPosition = projectile.PrimaryPart.Position
local distanceTravelled = (currentPosition - origin).Magnitude
local distanceLeft = maxDistance - distanceTravelled
local alphaValue = ts:GetValue(math.min(elapsed / timeToReachGoal, 1), Enum.EasingStyle.Linear, Enum.EasingDirection.Out) -- Get the alpha val to determine how far the projectile should move along its determined trajectory.
local lerpPos = basicFunctions.lerp(originalCFrame.Position, goalPosition, alphaValue) -- Translate alpha value to world position.
--print("DEBUG: Elapsed time:", elapsed, "alphaValue:", alphaValue, "lerpPos:", lerpPos)
local raycastDistance = (lerpPos - currentPosition).Magnitude -- Determine how many studs to raycast out in front of the projectile's root part.
-- Fire the raycast
local raycastResult:RaycastResult = workspace:Raycast(currentPosition, direction * raycastDistance, raycastParams)
if raycastResult then -- If there is a result, that means there is an obstacle between the projectile and its intended destination. Simulate collision here.
if projectileModule.debugMode then
print("DEBUG: Found object to collide with. I had this much distance left on the projectile cast:", distanceLeft)
print("DEBUG: Raycast hit something. \n", raycastResult)
end
projectile:PivotTo(originalCFrame - originalCFrame.Position + raycastResult.Position)
conn:Disconnect()
conn = nil
if lengthChangedFunc ~= nil then
lengthChangedFunc(conn)
end
--[[Any necessary projectile cleanup not already handled by the parent castProjectile function happens here.
Common use case for this function is to include the crater spawning logic for when a projectile hits a wall. ]]
if castEndedFunc ~= nil then
castEndedFunc(raycastResult)
end
else
projectile:PivotTo(originalCFrame - originalCFrame.Position + lerpPos)
if lengthChangedFunc ~= nil then
lengthChangedFunc(conn)
end
end
if projectileModule.debugMode then
-- Show points along path
local stepSize = 1 -- Distance between each small part
numSteps += 1
local point = origin + direction * (numSteps * stepSize)
-- Create a small part at the point
local smallPart = Instance.new("Part")
smallPart.CanCollide = false
smallPart.CanQuery = false
smallPart.Size = Vector3.new(0.2, 0.2, 0.2) -- Size of the small part
smallPart.CFrame = projectile:GetPivot() --point
smallPart.Anchored = true
smallPart.Parent = workspace.Testing.junk
end
end
else
-- The time to reach the goal has elapsed, so the projectile has reached its destination (or should be terminated to avoid incorrect positioning due to lag.)
conn:Disconnect()
conn = nil
-- After connection is disconnected, run this.
if castEndedFunc ~= nil then
castEndedFunc()
end
end
end)
end
Saiyan Craft was a customized Minecraft server and subsequent community of gamers. I designed, configured, and implemented all of the backend processes supporting this server. I also designed the community website, managed user permissions both in-game and on the website, and coded updates for the game with JavaScript.
This effort was accomplished with the help of a team of 4 other staff, who helped to design, manage and communicate updates to our player base.
At its core, the server was themed around a “Dragon Ball Z” Minecraft mod. Players could create their own character, follow a “Dragon Ball” – themed questline, level up, spend stat points on new abilities, and participate in both PvP and PvE activities.
Snippet from the server’s voting platform: “Saiyan Craft is a 24/7 Dragon Block C server. Dragon Block C is a mod for Minecraft based on the popular show and Manga Dragon Ball Z. You can become a Human, Namekian, or even a Saiyan like Goku and fight against enemies like Frieza and Saibamen in order to train and become strong enough to use powerful Ki blasts! The mod also includes a saga feature which allows you to complete sagas while you train and fight enemies like Vegeta and the Androids. (The Dragon Block C mod and its components are required to join the server.)”
// ## Created by Kish
// ## © SAIYANCRAFT 2020
// ## FOR USE ON SAIYAN CRAFT OFFICIAL SERVER ****ONLY****
/* ## Labyrinth customnpc mini-boss for SC Halloween event 2020.*/
// ** INIT **
//Attack and Health
npc.setMeleeStrength(14750000);
npc.setRangedStrength(14750000);
npc.setMaxHealth(2000000000);
npc.setHealth(2000000000);
npc.setTempData("halfHpLineSaid", false);
npc.setTempData("nearDeathLineSaid", false);
npc.setTempData("transformed", false);
npc.setTempData("dead", false);
npc.setTempData("hitsTracker", []);
npc.setTempData("damageTracker", []);
npc.setTempData("lastHp", npc.getHealth());
var x = npc.x;
var y = npc.y;
var z = npc.z;
world.spawnClone(x-5, y, z-5, 6, "MinionFinale");
world.spawnClone(x-5, y, z+5, 6, "MinionFinale");
world.spawnClone(x+5, y, z+5, 6, "MinionFinale");
world.spawnClone(x+5, y, z-5, 6, "MinionFinale");
world.spawnClone(x-10, y, z-10, 6, "MinionFinale");
world.spawnClone(x-10, y, z+10, 6, "MinionFinale");
world.spawnClone(x+10, y, z+10, 6, "MinionFinale");
world.spawnClone(x+10, y, z-10, 6, "MinionFinale");
world.spawnClone(x-15, y, z-15, 6, "MinionFinale");
world.spawnClone(x-15, y, z+15, 6, "MinionFinale");
world.spawnClone(x+15, y, z+15, 6, "MinionFinale");
world.spawnClone(x+15, y, z-15, 6, "MinionFinale");
// ** UPDATE **
// ## Created by Kish
// ## © SAIYANCRAFT 2020
// ## FOR USE ON SAIYAN CRAFT OFFICIAL SERVER ****ONLY****
var rand = Math.floor(Math.random() * 10);
var x = (npc.x+(" "));
var y = (npc.y+(" "));
var z = (npc.z+(" "));
var coords = (x+y+z);
var dead = npc.getTempData("dead");
// Variables for damage tracker.
var dmg = npc.getTempData("lastHp") - npc.getHealth();
var playerName = npc.getTempData("damagedBy");
var inHitsTracker = false;
var inDamageTracker = false;
var hitsTracker = npc.getTempData("hitsTracker");
var damageTracker = npc.getTempData("damageTracker");
// DAMAGE TRACKER CODE
// Prevents npc from adding to damage trackers if it hasn't been hit.
if (npc.getTempData("addToDamage"))
{
//Check for player name in hits tracker. If player has damaged npc, add +1 to total hits. If it hasn't, add player name to list of hits tracked.
for (var i in hitsTracker){
if (i == playerName)
{
hitsTracker[i] += 1;
npc.setTempData("hitsTracker", hitsTracker);
inHitsTracker = true;
//npc.say("Player " + playerName + " found in hit tracker. Adding 1 hit."); DEBUG
}
}
if (!inHitsTracker)
{
hitsTracker[playerName] = 1;
npc.setTempData("hitsTracker", hitsTracker);
//npc.say("Player " + playerName + " NOT found in hit tracker. Adding 1 hit."); DEBUG
}
// Check for player name in damage tracker. If player has damaged npc, add dmg amount to total damage. If it hasn't, add player name and current damage to damage tracked.
for (var i in damageTracker){
if (i == playerName)
{
damageTracker[i] += dmg;
npc.setTempData("damageTracker", damageTracker);
inDamageTracker = true;
//npc.say("Player " + playerName + " found in damage tracker. Adding " + dmg + " damage."); DEBUG
}
}
if (!inDamageTracker)
{
damageTracker[playerName] = dmg;
npc.setTempData("damageTracker", damageTracker);
//npc.say("Player " + playerName + " NOT found in damage tracker. Adding " + dmg + " damage."); DEBUG
}
}
npc.setTempData("addToDamage", false);
// Initialize target var
var target = npc.getTempData("target");
// ** NOTE TO SELF: NPC'S SET TO "DEFENSE FACTION MEMBERS" WILL BREAK THE INSTANT TRANSMISSION SCRIPT. THIS FACTION SETTING SEEMS TO HANDLE TARGETING DIFFERENTLY.
// Instant transmission
function instantTransmission(x, y, z){
// Teleports close to player.
var newX = Math.random()*2 + x;
var newZ = Math.random()*2 + z;
npc.setPosition(newX, y, newZ);
npc.executeCommand("playsound mob.wither.shoot " + target.getName());
}
if((npc.isAttacking()) && (dead != true) && (target != null))
{
// If npc isn't within range of the player, teleport to player OR shoot ki blast (lower chance for blast). This npc randomly picks between both options.
if((Math.abs(target.x - npc.x) > 20) || (Math.abs(target.y - npc.y) > 10) || (Math.abs(target.z - npc.z) > 20))
{
if(rand <= 3) /// Lower # = lower chance (range from 0 - 10)
{
npc.executeCommand("/dbcspawnki 1 1 9000000 0 5 1 1 100 " +coords);
}
else
{
instantTransmission(target.x, target.y, target.z);
}
}
}
// ** TARGET **
npc.setTempData("target", event.getTarget());
// ** DAMAGED **
// ## Created by Kish
// ## © SAIYANCRAFT 2020
// ## FOR USE ON SAIYAN CRAFT OFFICIAL SERVER ****ONLY****
var source = event.getSource();
var dmg = npc.getMaxHealth() - npc.getHealth();
var halfHpLineSaid = npc.getTempData("halfHpLineSaid");
var nearDeathLineSaid = npc.getTempData("nearDeathLineSaid");
var rand = Math.floor(Math.random() * 10);
// Start gathering information for damage tracker when damaged.
var playerName = source.getName();
npc.setTempData("lastHp", npc.getHealth());
npc.setTempData("damagedBy", playerName);
npc.setTempData("addToDamage", true);
// Say half hp & near-death lines
if ((dmg >= npc.getMaxHealth() / 2) && !halfHpLineSaid)
{
npc.say("This world will be mine. Bow before greatness.");
halfHpLineSaid = true;
npc.setTempData("halfHpLineSaid", halfHpLineSaid);
}
else if ((dmg >= npc.getMaxHealth() / 1.25) && !nearDeathLineSaid)
{
npc.say(".");
nearDeathLineSaid = true;
npc.setTempData("nearDeathLineSaid", nearDeathLineSaid);
}
// ** KILLED **
// ## Created by Kish
// ## © SAIYANCRAFT 2020
// ## FOR USE ON SAIYAN CRAFT OFFICIAL SERVER ****ONLY****
var source = event.getSource();
var playerName = source.getName();
var oneShotKill = true;
// Send message on death
//source.sendMessage("Goku: (Thank you...)");
npc.setTempData("dead", true);
// Reward players based on their contribution to the kill.
var hitsTracker = npc.getTempData("hitsTracker");
var damageTracker = npc.getTempData("damageTracker");
var maxHealth = npc.getMaxHealth();
var minReward = maxHealth / 100; // 1% of boss's hp.
var regReward = maxHealth / 20; // 5% of boss's hp.
var goldReward = maxHealth / 10; // 10% of boss's hp.
var diamondReward = maxHealth / 3; // 30% of boss's hp.
var legendaryReward = maxHealth / 2; // half of boss's hp.
for (var i in hitsTracker)
{
// If the player that kills this NPC isn't found in the damage tracker (because they got last hit or killed it in one hit,) oneShotKill flag stays true, and they are rewarded after players who contributed damage are rewarded.
if (i == playerName)
{
oneShotKill = false;
}
if (damageTracker[i] >= legendaryReward)
{
npc.executeCommand("/citems give -p " + i + " 500milticket -a 10");
npc.executeCommand("/citems give -p " + i + " SoulSack -a 10");
npc.executeCommand("/citems give -p " + i + " immortalsoul -a 10");
npc.executeCommand("/citems give -p " + i + " DemonSlayerGlaive -a 1");
npc.executeCommand("/msg " + i + " You have received the &4Legendary &flevel reward for your contribution in defeating " + npc.getName() + ".");
}
else if (damageTracker[i] >= diamondReward)
{
npc.executeCommand("/citems give -p " + i + " 500milticket -a 8");
npc.executeCommand("/citems give -p " + i + " SoulSack -a 7");
npc.executeCommand("/citems give -p " + i + " immortalsoul -a 5");
npc.executeCommand("/citems give -p " + i + " DemonSlayerGlaive -a 1");
npc.executeCommand("/msg " + i + " You have received the &bDiamond &flevel reward for your contribution in defeating " + npc.getName() + ".");
}
else if (damageTracker[i] >= goldReward)
{
npc.executeCommand("/citems give -p " + i + " 500milticket -a 7");
npc.executeCommand("/citems give -p " + i + " SoulSack -a 5");
npc.executeCommand("/citems give -p " + i + " immortalsoul -a 4");
npc.executeCommand("/citems give -p " + i + " DemonSlayerGlaive -a 1");
npc.executeCommand("/msg " + i + " You have been rewarded for your contribution in defeating " + npc.getName() + ".");
}
else if (damageTracker[i] >= regReward)
{
npc.executeCommand("/citems give -p " + i + " 500milticket -a 6");
npc.executeCommand("/citems give -p " + i + " SoulSack -a 2");
npc.executeCommand("/citems give -p " + i + " immortalsoul -a 4");
npc.executeCommand("/citems give -p " + i + " DemonSlayerGlaive -a 1");
npc.executeCommand("/msg " + i + " You have been rewarded for your contribution in defeating " + npc.getName() + ".");
}
else if ((hitsTracker[i] >= 15) || (damageTracker[i] >= minReward))
{
npc.executeCommand("/citems give -p " + i + " 500milticket -a 4");
npc.executeCommand("/citems give -p " + i + " SoulSack -a 1");
npc.executeCommand("/citems give -p " + i + " immortalsoul -a 4");
npc.executeCommand("/citems give -p " + i + " DemonSlayerGlaive -a 1");
npc.executeCommand("/msg " + i + " You have been rewarded for your contribution in defeating " + npc.getName() + ".");
}
}
if (oneShotKill)
{
npc.executeCommand("/citems give -p " + playerName + " 500milticket -a 4");
npc.executeCommand("/citems give -p " + playerName + " SoulSack -a 1");
npc.executeCommand("/citems give -p " + playerName + " immortalsoul -a 4");
npc.executeCommand("/citems give -p " + playerName + " DemonSlayerGlaive -a 1");
npc.executeCommand("/msg " + playerName + " You have been rewarded for your contribution in defeating " + npc.getName() + ".");
}
else
{
// npc.executeCommand("/msg " + playerName + " Debug! OneShotKill was " + oneShotKill + "!");
}
Au Luxe 79 was a high-end custom “Travel Box” store. I was hired as a full-stack developer to help design, develop and implement a full web application. The site was designed based on customer feedback and intent.
I developed the entire solution, both front-end and backend, and utilized a dedicated web server host to run the site. JavaScript, HTML5 and CSS were used on the front-end to style the web pages, while PHP and SQL were utilized to communicate information to and from the site’s database.
Au Luxe 79 featured a custom “buzz-feed” style product purchase flow. Customers would create an account, choose a product, and then fill out a stylized survey. The answers to the survey were attached to the customer’s order, and sent to the store owner on product purchase. The answers were then used to create a personalized product. Users could sign in and view the answers to their past surveys.
Portfolio page designed for and by Adnan el-Bedawi.
Contact:
Email: elbedawia@yahoo.com
LinkedIn: https://www.linkedin.com/in/aedev98/