ai-npcs / cubzh.lua
rooben's picture
Update cubzh.lua
9aa841e verified
raw
history blame
9.76 kB
math.randomseed(math.floor(Time.UnixMilli() % 100000))
Modules = {
gigax = "github.com/GigaxGames/integrations/cubzh:9a71b9f",
pathfinding = "github.com/caillef/cubzh-library/pathfinding:5f9c6bd",
floating_island_generator = "github.com/caillef/cubzh-library/floating_island_generator:82d22a5",
easy_onboarding = "github.com/caillef/cubzh-library/easy_onboarding:77728ee",
}
Config = {
Items = { "pratamacam.squirrel" },
}
-- Function to spawn a squirrel above the player
function spawnSquirrelAbovePlayer(player)
local squirrel = Shape(Items.pratamacam.squirrel)
squirrel:SetParent(World)
squirrel.Position = player.Position + Number3(0, 20, 0)
-- make scale smaller
squirrel.LocalScale = 0.5
-- remove collision
squirrel.Physics = PhysicsMode.Dynamic
-- rotate it 90 degrees to the right
squirrel.Rotation = { 0, math.pi * 0.5, 0 }
-- this would make squirrel.Rotation = player.Rotation
World:AddChild(squirrel)
return squirrel
end
local SIMULATION_NAME = "Code Island Adventure" .. tostring(math.random())
local SIMULATION_DESCRIPTION = "An interactive coding adventure on floating islands."
local skills = {
{
name = "TEACH_LOOP",
description = "Teach the concept of looping in programming",
parameter_types = {},
callback = function(client, action)
local npc = client:getNpc(action.character_id)
if not npc then print("Can't find npc") return end
dialog:create("Let's learn about loops! They help us repeat actions easily.", npc.avatar.Head)
Timer(3, function()
dialog:create("Imagine you want to print 'Hello' 5 times. Instead of writing it 5 times, we use a loop!", npc.avatar.Head)
end)
Timer(6, function()
dialog:create("It looks like this: for i = 1, 5 do print('Hello') end", npc.avatar.Head)
end)
end,
action_format_str = "{protagonist_name} taught about loops in programming."
},
{
name = "DEMONSTRATE_LOOP",
description = "Demonstrate a loop visually",
parameter_types = {},
callback = function(client, action)
local npc = client:getNpc(action.character_id)
if not npc then print("Can't find npc") return end
dialog:create("Watch this loop in action!", npc.avatar.Head)
for i = 1, 5 do
Timer(i, function()
local cube = Shape(Items.pratamacam.squirrel) -- Using squirrel as a placeholder for a cube
cube.LocalScale = 0.3
cube.Position = npc.object.Position + Number3(i*2, 5, 0)
World:AddChild(cube)
dialog:create("Created cube " .. i .. "!", npc.avatar.Head)
end)
end
end,
action_format_str = "{protagonist_name} demonstrated a loop by creating cubes."
},
{
name = "EXPLAIN_LOOP_PARTS",
description = "Explain the different parts of a loop",
parameter_types = {},
callback = function(client, action)
local npc = client:getNpc(action.character_id)
if not npc then print("Can't find npc") return end
dialog:create("Let's break down the parts of a loop:", npc.avatar.Head)
Timer(3, function()
dialog:create("1. 'for' keyword: Starts the loop", npc.avatar.Head)
end)
Timer(6, function()
dialog:create("2. 'i = 1': Initialize the loop variable", npc.avatar.Head)
end)
Timer(9, function()
dialog:create("3. '5': The end condition", npc.avatar.Head)
end)
Timer(12, function()
dialog:create("4. 'do': Begins the loop body", npc.avatar.Head)
end)
Timer(15, function()
dialog:create("5. 'end': Ends the loop", npc.avatar.Head)
end)
end,
action_format_str = "{protagonist_name} explained the parts of a loop."
}
}
local locations = {
{
name = "Scientist Island",
description = "A small island with a scientist and its pet chilling.",
},
{
name = "Baker Island",
description = "A small bakery on a floating island in the sky.",
},
{
name = "Pirate Island",
description = "A small floating island in the sky with a pirate and its ship.",
},
{
name = "Center",
description = "Center point between the three islands.",
},
}
local NPCs = {
{
name = "npcteacher",
gameName = "Professor Loop",
physicalDescription = "Friendly-looking teacher with glasses and a digital tablet",
psychologicalProfile = "Patient and enthusiastic about teaching programming concepts",
currentLocationName = "Center",
initialReflections = {
"Welcome to Loop Island! I'm Professor Loop, and I'm here to teach you about loops in programming.",
"Loops are a fundamental concept in coding. They help us repeat actions without writing the same code over and over.",
"Are you ready to learn about loops through some fun visual demonstrations?",
},
}
}
local gigaxWorldConfig = {
simulationName = "Loop Learning Adventure",
simulationDescription = "An interactive coding lesson teaching the concept of loops.",
startingLocationName = "Center",
skills = skills,
locations = locations,
NPCs = NPCs,
}
findLocationByName = function(targetName, config)
for _, node in ipairs(config.locations) do
if string.lower(node.name) == string.lower(targetName) then
return node.position
end
end
end
Client.OnWorldObjectLoad = function(obj)
if obj.Name == "pirate_ship" then
obj.Scale = 1
end
local locationsIndexByName = {}
for k, v in ipairs(gigaxWorldConfig.locations) do
locationsIndexByName[v.name] = k
end
local npcIndexByName = {
NPC_scientist = 1,
NPC_baker = 2,
NPC_pirate = 3,
}
local index = npcIndexByName[obj.Name]
if index then
local pos = obj.Position:Copy()
gigaxWorldConfig.NPCs[index].position = pos
gigaxWorldConfig.NPCs[index].rotation = obj.Rotation:Copy()
local locationName = gigaxWorldConfig.NPCs[index].currentLocationName
local locationIndex = locationsIndexByName[locationName]
gigaxWorldConfig.locations[locationIndex].position = pos
obj:RemoveFromParent()
end
end
Client.OnStart = function()
easy_onboarding:startOnboarding(onboardingConfig)
require("object_skills").addStepClimbing(Player, {
mapScale = MAP_SCALE,
collisionGroups = Map.CollisionGroups,
})
gigaxWorldConfig.locations[4].position = Number3(Map.Width * 0.5, Map.Height - 2, Map.Depth * 0.5) * Map.Scale
floating_island_generator:generateIslands({
nbIslands = 20,
minSize = 4,
maxSize = 7,
safearea = 200, -- min dist of islands from 0,0,0
dist = 750, -- max dist of islands
})
local ambience = require("ambience")
ambience:set(ambience.dusk)
sfx = require("sfx")
Player.Head:AddChild(AudioListener)
dropPlayer = function()
Player.Position = Number3(Map.Width * 0.5, Map.Height + 10, Map.Depth * 0.5) * Map.Scale
Player.Rotation = { 0, 0, 0 }
Player.Velocity = { 0, 0, 0 }
end
World:AddChild(Player)
dropPlayer()
dialog = require("dialog")
dialog:setMaxWidth(400)
pathfinding:createPathfindingMap()
gigax:setConfig(gigaxWorldConfig)
local randomNames = { "aduermael", "soliton", "gdevillele", "caillef", "voxels", "petroglyph" }
Player.Avatar:load({ usernameOrId = randomNames[math.random(#randomNames)] })
end
Client.Action1 = function()
if Player.IsOnGround then
sfx("hurtscream_1", { Position = Player.Position, Volume = 0.4 })
Player.Velocity.Y = 100
if Player.Motion.X == 0 and Player.Motion.Z == 0 then
-- only play jump action when jumping without moving to avoid wandering around to trigger NPCs
gigax:action({
name = "JUMP",
description = "Jump in the air",
parameter_types = {},
action_format_str = "{protagonist_name} jumped up in the air for a moment.",
})
end
end
end
Client.Tick = function(dt)
if Player.Position.Y < -500 then
dropPlayer()
end
end
Client.OnChat = function(payload)
local msg = payload.message
Player:TextBubble(msg, 3, true)
sfx("waterdrop_2", { Position = Player.Position, Pitch = 1.1 + math.random() * 0.5 })
gigax:action({
name = "SAY",
description = "Say smthg out loud",
parameter_types = { "character", "content" },
action_format_str = "{protagonist_name} said '{content}' to {target_name}",
content = msg,
})
print("User: " .. payload.message)
return true
end
onboardingConfig = {
steps = {
{
start = function(onboarding)
local data = {}
data.ui = onboarding:createTextStep("1/3 - Hold click and drag to move the camera.")
data.listener = LocalEvent:Listen(LocalEvent.Name.PointerDrag, function()
Timer(1, function()
onboarding:next()
end)
data.listener:Remove()
end)
return data
end,
stop = function(_, data)
data.ui:remove()
end,
},
{
start = function(onboarding)
local data = {}
data.ui = onboarding:createTextStep("2/3 - Use WASD/ZQSD to move.")
data.listener = LocalEvent:Listen(LocalEvent.Name.KeyboardInput, function()
Timer(1, function()
onboarding:next()
end)
data.listener:Remove()
end)
return data
end,
stop = function(_, data)
data.ui:remove()
end,
},
{
start = function(onboarding)
local data = {}
data.ui = onboarding:createTextStep("3/3 - Press Enter in front of the Pirate to chat.")
Timer(10, function()
onboarding:next()
end)
return data
end,
stop = function(_, data)
data.ui:remove()
end,
},
},
}