CoolFace
Apppublic

AEUPH/AI-NPC-VR

sourceHugging Facecc-by-4.0updated 2y agoView on Hugging Face
0likes
cubzh.lua468 linesDownload Raw Back to root
1math.randomseed(math.floor(Time.UnixMilli() % 100000))2 3Modules = {4	gigax = "github.com/GigaxGames/integrations/cubzh:9a71b9f",5	pathfinding = "github.com/caillef/cubzh-library/pathfinding:5f9c6bd",6	floating_island_generator = "github.com/caillef/cubzh-library/floating_island_generator:82d22a5",7	easy_onboarding = "github.com/caillef/cubzh-library/easy_onboarding:77728ee",8}9 10Config = {11	Items = { "pratamacam.squirrel" },12}13 14-- Function to spawn a squirrel above the player15function spawnSquirrelAbovePlayer(player)16	local squirrel = Shape(Items.pratamacam.squirrel)17	squirrel:SetParent(World)18	squirrel.Position = player.Position + Number3(0, 20, 0)19	-- make scale smaller20	squirrel.LocalScale = 0.521	-- remove collision22	squirrel.Physics = PhysicsMode.Dynamic23	-- rotate it 90 degrees to the right24	squirrel.Rotation = { 0, math.pi * 0.5, 0 }25	-- this would make squirrel.Rotation = player.Rotation26	World:AddChild(squirrel)27	return squirrel28end29 30local SIMULATION_NAME = "Islands" .. tostring(math.random())31local SIMULATION_DESCRIPTION = "Three floating islands."32 33 34local skills = {35	{36		name = "SAY",37		description = "Say smthg out loud",38		parameter_types = { "character", "content" },39		callback = function(client, action)40			local npc = client:getNpc(action.character_id)41			if not npc then42				print("Can't find npc")43				return44			end45			dialog:create(action.content, npc.avatar.Head)46			print(string.format("%s: %s", npc.gameName, action.content))47		end,48		action_format_str = "{protagonist_name} said '{content}' to {target_name}",49	},50	{51		name = "MOVE",52		description = "Move to a new location",53		parameter_types = { "location" },54		callback = function(client, action, config)55			local targetName = action.target_name56			local targetPosition = findLocationByName(targetName, config)57			if not targetPosition then58				print("tried to move to an unknown place", targetName)59				return60			end61			local npc = client:getNpc(action.character_id)62			dialog:create("I'm going to " .. targetName, npc.avatar.Head)63			print(string.format("%s: %s", npc.gameName, "I'm going to " .. targetName))64			local origin = Map:WorldToBlock(npc.object.Position)65			local destination = Map:WorldToBlock(targetPosition) + Number3(math.random(-1, 1), 0, math.random(-1, 1))66			local canMove = pathfinding:moveObjectTo(npc.object, origin, destination)67			if not canMove then68				dialog:create("I can't go there", npc.avatar.Head)69				return70			end71		end,72		action_format_str = "{protagonist_name} moved to {target_name}",73	},74	{75		name = "GREET",76		description = "Greet a character by waving your hand at them",77		parameter_types = { "character" },78		callback = function(client, action)79			local npc = client:getNpc(action.character_id)80			if not npc then81				print("Can't find npc")82				return83			end84 85			dialog:create("<Greets you warmly!>", npc.avatar.Head)86			print(string.format("%s: %s", npc.gameName, "<Greets you warmly!>"))87 88			npc.avatar.Animations.SwingRight:Play()89		end,90		action_format_str = "{protagonist_name} waved their hand at {target_name} to greet them",91	},92	{93		name = "JUMP",94		description = "Jump in the air",95		parameter_types = {},96		callback = function(client, action)97			local npc = client:getNpc(action.character_id)98			if not npc then99				print("Can't find npc")100				return101			end102 103			dialog:create("<Jumps in the air!>", npc.avatar.Head)104			print(string.format("%s: %s", npc.gameName, "<Jumps in the air!>"))105 106			npc.object.avatarContainer.Physics = PhysicsMode.Dynamic107			npc.object.avatarContainer.Velocity.Y = 50108			Timer(3, function()109				npc.object.avatarContainer.Physics = PhysicsMode.Trigger110			end)111		end,112		action_format_str = "{protagonist_name} jumped up in the air for a moment.",113	},114	{115		name = "FOLLOW",116		description = "Follow a character around for a while",117		parameter_types = { "character" },118		callback = function(client, action)119			local npc = client:getNpc(action.character_id)120			if not npc then121				print("Can't find npc")122				return123			end124 125			dialog:create("I'm following you", npc.avatar.Head)126			print(string.format("%s: %s", npc.gameName, "I'm following you"))127 128			followHandler = pathfinding:followObject(npc.object, Player)129			return {130				followHandler = followHandler,131			}132		end,133		onEndCallback = function(_, data)134			data.followHandler:Stop()135		end,136		action_format_str = "{protagonist_name} followed {target_name} for a while.",137	},138	{139		name = "FIRECRACKER",140		description = "Perform a fun, harmless little explosion to make people laugh!",141		parameter_types = { "character" },142		callback = function(client, action)143			local npc = client:getNpc(action.character_id)144			if not npc then145				print("Can't find npc")146				return147			end148 149			require("explode"):shapes(npc.avatar)150			dialog:create("*boom*", npc.avatar.Head)151			npc.avatar.IsHidden = true152			Timer(5, function()153				dialog:create("Aaaaand... I'm back!", npc.avatar.Head)154				npc.avatar.IsHidden = false155			end)156		end,157		action_format_str = "{protagonist_name} exploded like a firecracker, with a bang!",158	},--[[159	{160        name = "GIVEAPPLE",161        description = "Give a pice of bread (or a baguette) to someone",162        parameter_types = {"character"},163        callback = function(client, action)164			local npc = client:getNpc(action.character_id)165			if not npc then print("Can't find npc") return end166			local shape = MutableShape()167			shape:AddBlock(Color.Red, 0, 0, 0)168			shape.Scale = 4169			Player:EquipRightHand(shape)170			dialog:create("Here is an apple for you!", npc.avatar.Head)171        end,172		action_format_str = "{protagonist_name} gave you a piece of bread!"173    }, --]]174	{175        name = "GIANT",176        description = "Double your height to become a giant for a few seconds.",177        parameter_types = {"character"},178        callback = function(client, action)179			local npc = client:getNpc(action.character_id)180			if not npc then print("Can't find npc") return end181 182			npc.object.Scale = npc.object.Scale * 2183			dialog:create("I am taller than you now!", npc.avatar.Head)184        end,185		action_format_str = "{protagonist_name} doubled his height!"186    },187	{188		name = "GIVEHAT",189		description = "Give a party hat to someone",190		parameter_types = { "character" },191		callback = function(client, action)192			local npc = client:getNpc(action.character_id)193			if not npc then194				print("Can't find npc")195				return196			end197 198			Object:Load("claire.party_hat", function(obj)199				require("hierarchyactions"):applyToDescendants(obj, { includeRoot = true }, function(o)200					o.Physics = PhysicsMode.Disabled201				end)202				Player:EquipHat(obj)203			end)204			dialog:create("Let's get the party started!", npc.avatar.Head)205		end,206		action_format_str = "{protagonist_name} gave you a piece of bread!",207	},208	{209		name = "FLYINGSQUIRREL",210		description = "Summon a flying squirrel - only the scientist can do this!!",211		parameter_types = {},212		callback = function(client, action)213			local npc = client:getNpc(action.character_id)214			if not npc then215				print("Can't find npc")216				return217			end218 219			local squirrel = spawnSquirrelAbovePlayer(Player)220			dialog:create("Wooh, squirrel!", npc.avatar.Head)221			-- make it disappear after a while222			Timer(5, function()223				squirrel:RemoveFromParent()224				squirrel = nil225			end)226		end,227		action_format_str = "{protagonist_name} summoned a flying squirrel! It's vibrating with excitement!",228	},229}230 231local locations = {232	{233		name = "Scientist Island",234		description = "A small island with a scientist and its pet chilling.",235	},236	{237		name = "Baker Island",238		description = "A small bakery on a floating island in the sky.",239	},240	{241		name = "Pirate Island",242		description = "A small floating island in the sky with a pirate and its ship.",243	},244	{245		name = "Center",246		description = "Center point between the three islands.",247	},248}249 250local NPCs = {251	{252		name = "npcscientist",253		gameName = "Scientist",254		physicalDescription = "Short, with a stern expression and sharp eyes",255		psychologicalProfile = "Grumpy but insightful, this scientist loves to surprise his guests with cool tricks! For example, he frequently lights up little firecrackers to create harmless and fun explosions.",256		currentLocationName = "Scientist Island",257		initialReflections = {258			"I just arrived on this island to feed my pet, he loves tulips so much.",259			"Yesterday, you were visited by a newcomer. He asked you to perform fun tricks like increase your size or create little explosions - you complied merrily!",260			"I am a scientist that works on new pets for everyone, so that each individual can have the pet of their dreams",261			"I am a bit allergic to the tulip but Fredo my pet loves it so much, I have to dock here with my vehicle. The pet is placed at the back of my flying scooter when we move to another place.",262		},263	},264	{265		name = "npcbaker",266		gameName = "Baker",267		physicalDescription = "Tall, with a solemn demeanor and thoughtful eyes",268		psychologicalProfile = "Wise and mysterious, calm under pressure",269		currentLocationName = "Baker Island",270		initialReflections = {271			"I am a baker and I make food for everyone that pass by.",272			"I am a bit stressed that the flour didn't arrived yet, my cousin Joe should arrive soon with the delivery but he is late and I worry a bit.",273			"I love living here on these floating islands, the view is amazing from my wind mill.",274			"I like to talk to strangers like the pirate that just arrived or the scientist coming time to time to feed his pet.",275		},276	},277	{278		name = "npcpirate",279		gameName = "Pirate",280		physicalDescription = "Average height, with bright green eyes and a warm smile",281		psychologicalProfile = "Friendly and helpful, quick-witted and resourceful",282		currentLocationName = "Pirate Island",283		initialReflections = {284			"Ahoy, matey! I'm Captain Ruby Storm, a fearless lass from the seven skies.",285			"I've docked me floating ship on this here floating isle to sell me wares (almost legally) retrieved treasures from me last daring adventure.",286			"So, who be lookin' to trade with a swashbuckler like meself?",287		},288	},289}290 291local gigaxWorldConfig = {292	simulationName = SIMULATION_NAME,293	simulationDescription = SIMULATION_DESCRIPTION,294	startingLocationName = "Center",295	skills = skills,296	locations = locations,297	NPCs = NPCs,298}299 300findLocationByName = function(targetName, config)301	for _, node in ipairs(config.locations) do302		if string.lower(node.name) == string.lower(targetName) then303			return node.position304		end305	end306end307 308Client.OnWorldObjectLoad = function(obj)309	if obj.Name == "pirate_ship" then310		obj.Scale = 1311	end312 313	local locationsIndexByName = {}314	for k, v in ipairs(gigaxWorldConfig.locations) do315		locationsIndexByName[v.name] = k316	end317	local npcIndexByName = {318		NPC_scientist = 1,319		NPC_baker = 2,320		NPC_pirate = 3,321	}322 323	local index = npcIndexByName[obj.Name]324	if index then325		local pos = obj.Position:Copy()326		gigaxWorldConfig.NPCs[index].position = pos327		gigaxWorldConfig.NPCs[index].rotation = obj.Rotation:Copy()328 329		local locationName = gigaxWorldConfig.NPCs[index].currentLocationName330		local locationIndex = locationsIndexByName[locationName]331		gigaxWorldConfig.locations[locationIndex].position = pos332		obj:RemoveFromParent()333	end334end335 336Client.OnStart = function()337	easy_onboarding:startOnboarding(onboardingConfig)338 339	require("object_skills").addStepClimbing(Player, {340		mapScale = MAP_SCALE,341		collisionGroups = Map.CollisionGroups,342	})343 344	gigaxWorldConfig.locations[4].position = Number3(Map.Width * 0.5, Map.Height - 2, Map.Depth * 0.5) * Map.Scale345 346	floating_island_generator:generateIslands({347		nbIslands = 20,348		minSize = 4,349		maxSize = 7,350		safearea = 200, -- min dist of islands from 0,0,0351		dist = 750, -- max dist of islands352	})353 354	local ambience = require("ambience")355	ambience:set(ambience.dusk)356 357	sfx = require("sfx")358	Player.Head:AddChild(AudioListener)359 360	dropPlayer = function()361		Player.Position = Number3(Map.Width * 0.5, Map.Height + 10, Map.Depth * 0.5) * Map.Scale362		Player.Rotation = { 0, 0, 0 }363		Player.Velocity = { 0, 0, 0 }364	end365	World:AddChild(Player)366	dropPlayer()367 368	dialog = require("dialog")369	dialog:setMaxWidth(400)370 371	pathfinding:createPathfindingMap()372 373	gigax:setConfig(gigaxWorldConfig)374 375	local randomNames = { "aduermael", "soliton", "gdevillele", "caillef", "voxels", "petroglyph" }376	Player.Avatar:load({ usernameOrId = randomNames[math.random(#randomNames)] })377end378 379Client.Action1 = function()380	if Player.IsOnGround then381		sfx("hurtscream_1", { Position = Player.Position, Volume = 0.4 })382		Player.Velocity.Y = 100383		if Player.Motion.X == 0 and Player.Motion.Z == 0 then384			-- only play jump action when jumping without moving to avoid wandering around to trigger NPCs385			gigax:action({386				name = "JUMP",387				description = "Jump in the air",388				parameter_types = {},389				action_format_str = "{protagonist_name} jumped up in the air for a moment.",390			})391		end392	end393end394 395Client.Tick = function(dt)396	if Player.Position.Y < -500 then397		dropPlayer()398	end399end400 401Client.OnChat = function(payload)402	local msg = payload.message403 404	Player:TextBubble(msg, 3, true)405	sfx("waterdrop_2", { Position = Player.Position, Pitch = 1.1 + math.random() * 0.5 })406 407	gigax:action({408		name = "SAY",409		description = "Say smthg out loud",410		parameter_types = { "character", "content" },411		action_format_str = "{protagonist_name} said '{content}' to {target_name}",412		content = msg,413	})414 415	print("User: " .. payload.message)416	return true417end418 419onboardingConfig = {420	steps = {421		{422			start = function(onboarding)423				local data = {}424				data.ui = onboarding:createTextStep("1/3 - Hold click and drag to move the camera.")425				data.listener = LocalEvent:Listen(LocalEvent.Name.PointerDrag, function()426					Timer(1, function()427						onboarding:next()428					end)429					data.listener:Remove()430				end)431				return data432			end,433			stop = function(_, data)434				data.ui:remove()435			end,436		},437		{438			start = function(onboarding)439				local data = {}440				data.ui = onboarding:createTextStep("2/3 - Use WASD/ZQSD to move.")441				data.listener = LocalEvent:Listen(LocalEvent.Name.KeyboardInput, function()442					Timer(1, function()443						onboarding:next()444					end)445					data.listener:Remove()446				end)447 448				return data449			end,450			stop = function(_, data)451				data.ui:remove()452			end,453		},454		{455			start = function(onboarding)456				local data = {}457				data.ui = onboarding:createTextStep("3/3 - Press Enter in front of the Pirate to chat.")458				Timer(10, function()459					onboarding:next()460				end)461				return data462			end,463			stop = function(_, data)464				data.ui:remove()465			end,466		},467	},468}