[Open / Signup] One Room Round Robin 3 - Calling All Modders

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
minmay
Posts: 2777
Joined: Mon Sep 23, 2013 2:24 am

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by minmay »

AndakRainor wrote:Oh the aliased Euclidean circle shape was intended then! Is it a problem?
It is, because it means the range of zone effects is effectively longer on diagonals than in compass directions (although the problem does not appear until ranges greater than 2 are used).
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by AndakRainor »

Would you say this geometry should be used for a spell such as Oracle? (It hudPrints the distance from the party to nearby secrets in its range). For damage zone effects, monsters on diagonals will take more time to reach the party... But this geometry is not very natural, I would say tile based movements do not need additional taxicab features. arcane_nova would feel weird with this. I can't think about a situation where euclidean range would feel as weird as taxicab.
minmay
Posts: 2777
Joined: Mon Sep 23, 2013 2:24 am

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by minmay »

AndakRainor wrote:Would you say this geometry should be used for a spell such as Oracle? (It hudPrints the distance from the party to nearby secrets in its range).
No idea. Oracle is a strange spell in the first place because secrets are often not placed in the same location as whatever triggers them, for example all the secrets in Xanathar's room are inside walls. I think I'll just drop this issue.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by AndakRainor »

Good point. It could detect secret buttons instead, what do you think about that? (or do you plan on using third eye in the mod?)
minmay
Posts: 2777
Joined: Mon Sep 23, 2013 2:24 am

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by minmay »

AndakRainor wrote:Good point. It could detect secret buttons instead, what do you think about that? (or do you plan on using third eye in the mod?)
I don't plan to include the Third Eye, though of course someone else can if they want to. (If someone else does add it, they should use the current version of third_eye.lua in this spoiler:)
SpoilerShow

Code: Select all

defineObject{
	baseObject = "base_item",
	name = "third_eye",
	components = {
		{
			class = "Model",
			model = "mod_assets/MinAssets/items/tools/third_eye/third_eye.fbx",
		},
		{
			class = "Item",
			weight = 0.2,
			uiName = "The Third Eye",
			description = [[This emerald seems to emit its own light.]],
			gameEffect = "Detects secret buttons",
			gfxAtlas = "mod_assets/MinAssets/items/icons.dds",
			gfxIndex = 30,
			traits = {"tool"},
			onEquipItem = function(self, champion, slot)
				party.thirdeyetimer:enable()
			end,
			onUnequipItem = function(self, champion, slot)
				-- Don't disable the timer if the party still has a second third eye in open inventory
				for c = 1,4 do
					local champ = party.party:getChampion(c)
					if champ:getEnabled() then
						for _,itm in champ:carriedItems() do
							if itm and itm.go.name == self.go.name and itm ~= self then return end
						end
					end
				end
				party.thirdeyetimer:disable()
			end,
		},
		{
			class = "Model",
			name = "beam1",
			model = "mod_assets/MinAssets/items/tools/third_eye/item_beam.fbx",
			offset = vec(0, 0.05, 0),
		},
		{
			class = "Model",
			name = "beam2",
			model = "mod_assets/MinAssets/items/tools/third_eye/item_beam.fbx",
			offset = vec(0, 0.05, 0),
		},
		{
			class = "Model",
			name = "beam3",
			model = "mod_assets/MinAssets/items/tools/third_eye/item_beam.fbx",
			offset = vec(0, 0.05, 0),
		},
		{
			class = "Model",
			name = "beam4",
			model = "mod_assets/MinAssets/items/tools/third_eye/item_beam.fbx",
			offset = vec(0, 0.05, 0),
		},
		{
			class = "Model",
			name = "beam5",
			model = "mod_assets/MinAssets/items/tools/third_eye/item_beam.fbx",
			offset = vec(0, 0.05, 0),
		},
		{
			class = "Model",
			name = "beam6",
			model = "mod_assets/MinAssets/items/tools/third_eye/item_beam.fbx",
			offset = vec(0, 0.05, 0),
		},
		{
			class = "Light",
			offset = vec(0, 0.1, 0),
			range = 5,
			color = vec(0.7, 1, 0.3),
			brightness = 10,
			castShadow = false,
			fillLight = true,
			onUpdate = function(self)
				local time = Time.currentTime()
				self.go.beam1:setRotationAngles(time*-66.7332%360,time*30.731%360,time*-52.07%360)
				self.go.beam2:setRotationAngles(time*74.04%360,time*-70.75%360,time*43.4%360)
				self.go.beam3:setRotationAngles(time*87.74%360,time*34.15%360,time*-76.038%360)
				self.go.beam4:setRotationAngles(180+time*-66.7332%360,180+time*30.731%360,180+time*-52.07%360)
				self.go.beam5:setRotationAngles(180+time*74.04%360,180+time*-70.75%360,180+time*43.4%360)
				self.go.beam6:setRotationAngles(180+time*87.74%360,180+time*34.15%360,180+time*-76.038%360)
			end,
		},
	}
}
defineObject{
	name = "thirdeye",
	components = {
		{
			class = "Light",
			type = "spot",
			spotAngle = 4,
			spotSharpness = 0.01,
			rotation = vec(0, 0, 0),
			range = 15,
			color = vec(0.7, 1, 0.3),
			brightness = 30,
			castShadow = false,
			onUpdate = function(self)
				if party.party:isCarrying("third_eye") then
					-- Keep at party's position
					local wpos = party:getWorldPosition()
					wpos[2] = wpos[2]+2
					self.go:setWorldPosition(wpos)

					-- Find the closest button on this level
					local minDist = party.thirdeyescript.MAX_DISTANCE
					local closestButton = nil
					for i,button in ipairs(party.thirdeyescript.getSecretButtons(party.level)) do
						local e = findEntity(button)
						if e and not (e.facing == 0 and e.y > party.y or -- Only check buttons that the party could POTENTIALLY have line of sight to
							e.facing == 1 and e.x < party.x or
							e.facing == 2 and e.y < party.y or
							e.facing == 3 and e.x > party.x) then
							-- We are not concerned about the offset here
							local ewpos = e:getWorldPosition()
							local dist = ((wpos[1]-ewpos[1])^2+(wpos[2]-ewpos[2])^2+(wpos[3]-ewpos[3])^2)^(1/2)
							if dist <= minDist then
								minDist = dist
								closestButton = e
							end
						end
					end

					-- Point at the closest button, or turn off if no buttons
					if closestButton then
						local posb = closestButton:getWorldPosition()
						local off = party.thirdeyescript.getButtonOffset(closestButton.name)
						posb[1] = posb[1]+(closestButton.facing == 0 and off[1] or closestButton.facing == 1 and off[3] or closestButton.facing == 2 and -off[1] or -off[3])
						posb[2] = posb[2]+off[2]
						posb[3] = posb[3]+(closestButton.facing == 0 and off[3] or closestButton.facing == 1 and -off[1] or closestButton.facing == 2 and -off[3] or off[1])
						local xDistance = posb[1] - wpos[1]
						local zDistance = posb[3] - wpos[3]
						local yDistance = posb[2] - wpos[2]
						local xzDistance = math.sqrt(xDistance*xDistance+zDistance*zDistance)
						local pitch = -math.pi/2
						local yaw = math.pi/2
						if xzDistance ~= 0 then
							pitch = math.atan(yDistance/xzDistance)
						elseif yDistance > 0 then
							pitch = math.pi/2
						end
						if zDistance > 0 then
							yaw = -math.atan(xDistance/zDistance)
						elseif zDistance ~= 0 then
							yaw = -math.atan(xDistance/zDistance)+math.pi
						elseif xDistance > 0 then
							yaw = -math.pi/2
						end
						self:setRange(minDist+2.2)
						self:setBrightness((party.thirdeyescript.MAX_DISTANCE-minDist)/party.thirdeyescript.MAX_DISTANCE*party.thirdeyescript.BRIGHTNESS)
						--self:setRotationAngles(math.deg(pitch),math.deg(yaw),0)
						-- Rotate entire object for convenience
						self.go:setWorldRotationAngles(math.deg(pitch),math.deg(yaw),0)

						if not thirdEyeBeam then spawn("thirdeye_beam",self.go.level,self.go.x,self.go.y,0,self.go.elevation,"thirdEyeBeam") end
						thirdEyeBeam:setPosition(self.go.x,self.go.y,0,self.go.elevation,self.go.level)
						thirdEyeBeam:setWorldPosition(wpos)
						-- Now scale the beam's z so that the beam is the right length
						local mat = self.go:getWorldRotation()
						local zscale = minDist/3 -- model is 3m long
						-- approximate lesser scaling factor for x and y, so that the beam
						-- is close to the right width. Making it the exact right width actually
						-- looks bad on the edges
						local xyscale = zscale * 0.3
						for i=1,4 do
							mat.z[i] = mat.z[i] * zscale
							mat.x[i] = mat.x[i] * xyscale
							mat.y[i] = mat.y[i] * xyscale
						end
						thirdEyeBeam:setWorldRotation(mat)
						thirdEyeBeam.model:enable()
					else
						self:setRange(0)
						if thirdEyeBeam then thirdEyeBeam.model:disable() end
					end
				else
					self.go:destroyDelayed()
					if thirdEyeBeam then thirdEyeBeam:destroy() end
				end
			end,
		},
	},
	placement = "floor",
}
defineObject{
	name = "thirdeye_beam",
	components = {
		{
			class = "Model",
			model = "mod_assets/MinAssets/items/tools/third_eye/beam.fbx",
		},
	},
	placement = "floor",
}
--[[defineParticleSystem{
	name = "thirdeye",
	emitters = {
		-- fog1
		{
			emissionRate = 256,
			emissionTime = 0,
			maxParticles = 1000,
			boxMin = {-0.01, -0.01, 0},
			boxMax = {0.01, 0.01, 6},
			sprayAngle = {0,0},
			velocity = {0.01,0.01},
			objectSpace = true,
			texture = "assets/textures/particles/dust_particle_dif.tga",
			lifetime = {0.5,2},
			color0 = { 0, 1, 0},
			opacity = 2,
			fadeIn = 0.5,
			fadeOut = 2,
			size = {0.04, 0.08},
			gravity = {0,0,0},
			airResistance = 0.1,
			rotationSpeed = 0.1,
			blendMode = "Additive",
		},
	}
}]]
defineMaterial{
	name = "thirdeye_beam",
	diffuseMap = "mod_assets/MinAssets/items/tools/third_eye/beam.tga",
	doubleSided = true,
	lighting = false,
	alphaTest = false,
	blendMode = "Additive",
	textureAddressMode = "Clamp",
	glossiness = 0,
	depthBias = 0,
	onUpdate = function(self, time)
		-- fade out at long distance
		if thirdEyeLight then
			self:setTexcoordScaleOffset(1, 1, 0, math.min(0,-0.5+(thirdEyeLight.light:getBrightness()/party.thirdeyescript.BRIGHTNESS)^(1/2)))
		end
	end,
}
defineMaterial{
	name = "thirdeye_beam_item",
	diffuseMap = "mod_assets/MinAssets/items/tools/third_eye/beam.tga",
	doubleSided = true,
	lighting = false,
	alphaTest = false,
	blendMode = "Additive",
	textureAddressMode = "Clamp",
	glossiness = 0,
	depthBias = 0,
}
defineMaterial{
	name = "third_eye",
	diffuseMap = "mod_assets/MinAssets/items/tools/third_eye/third_eye_dif.tga",
	specularMap = "mod_assets/MinAssets/items/tools/third_eye/third_eye_dif.tga",
	normalMap = "assets/textures/env/healing_crystal_normal.tga",
	doubleSided = false,
	lighting = false,
	ambientOcclusion = false,
	alphaTest = false,
	blendMode = "Opaque",
	textureAddressMode = "Wrap",
	glossiness = 20,
	depthBias = 0,
	
	-- custom shader
	shader = "crystal",
	shadeTex = "mod_assets/MinAssets/items/tools/third_eye/third_eye_dif.tga",
	shadeTexAngle = 0,
	crystalIntensity = 2,
	onUpdate = function(self, time)
		self:setParam("shadeTexAngle", time*0.8)
	end,
}
defineObject{
	name = "party",
	baseObject = "party",
	components = {
		{
			class = "Timer",
			name = "thirdeyetimer",
			timerInterval = 0.00001,
			onActivate = function(self)
				if not findEntity("thirdEyeLight") then
					spawn("thirdeye",self.go.level,self.go.x,self.go.y,self.go.facing,self.go.elevation,"thirdEyeLight")
				end
				if thirdEyeLight and thirdEyeLight.level ~= party.level then
					thirdEyeLight:setPosition(party.x,party.y,0,party.elevation,party.level)
				end
			end,
			enabled = false,
		},
		{
			class = "Script",
			name = "thirdeyescript",
			source = [[
-- The maximum allowed distance from the party for a detected button, in meters.
MAX_DISTANCE = 15
-- Brightness of the light if it is right on top of the button. Will decrease
-- linearly as distance from the button increases, ending at 0 brightness (light
-- is removed) at MAX_DISTANCE.
BRIGHTNESS = 64
do
-- offsets used for multiple objects
local g1dsblarge = {0,1.86697,0.06029}
local g1dsbsmall = {0.312,1.91593,-0.005}
local g1psbsmall = {0,1.70663,-0.071}
local g1tsblarge = {0.40781,1.73694,0.025}
local g1tsbsmall = {-0.08450,1.97,-0.005}
local g1tsbtiny = {-0.13955,1.39222,-0.005}

-- permanent variable
buttons = {
	dungeon_secret_button_small = {-0.355,1.03484,0},
	dungeon_secret_button_large = {-0.409,1.375,-0.0157},
	mine_support_secret_button = {-0.5084,1.75266,-0.05846},
	tomb_secret_button_small = {-0.371,1.30075,-0.002},
	forest_ruins_secret_button_small = {0.47917,1.02165,-0.14998},
	forest_ruins_secret_button_big = {0.05694,1.62365,-0.17098},
	dm_secret_button_big = {0,1.9895,-0.03613},
	dm_secret_button_midbottom = {0.00287,0.92838,-0.06473},
	dm_secret_button_crack = {0.66989,0.7803,0.09607},
	dm_secret_button_mini = {0.74954,2.11307,-0.05127},
	g1_dungeon_secret_button_large = g1dsblarge,
	g1_dungeon_secret_button_small = g1dsbsmall,
	g1_deep_dungeon_secret_button_large = g1dsblarge,
	g1_deep_dungeon_secret_button_small = g1dsbsmall,
	g1_northern_dungeon_secret_button_large = g1dsblarge,
	g1_northern_dungeon_secret_button_small = g1dsbsmall,
	g1_prison_secret_button = g1psbsmall,
	g1_solaris_prison_secret_button = g1psbsmall,
	g1_temple_secret_button_large = g1tsblarge,
	g1_temple_secret_button_small = g1tsbsmall,
	g1_temple_secret_button_tiny = g1tsbtiny,
	g1_dark_temple_secret_button_large = g1tsblarge,
	g1_dark_temple_secret_button_small = g1tsbsmall,
	g1_dark_temple_secret_button_tiny = g1tsbtiny,
	g1_darkelf_temple_secret_button_large = g1tsblarge,
	g1_darkelf_temple_secret_button_small = g1tsbsmall,
	g1_darkelf_temple_secret_button_tiny = g1tsbtiny,
	g1_deep_temple_secret_button_large = g1tsblarge,
	g1_deep_temple_secret_button_small = g1tsbsmall,
	g1_deep_temple_secret_button_tiny = g1tsbtiny,
	g1_frozen_temple_secret_button_large = g1tsblarge,
	g1_frozen_temple_secret_button_small = g1tsbsmall,
	g1_frozen_temple_secret_button_tiny = g1tsbtiny,
	g1_high_temple_secret_button_large = g1tsblarge,
	g1_high_temple_secret_button_small = g1tsbsmall,
	g1_high_temple_secret_button_tiny = g1tsbtiny,
	g1_highelf_temple_secret_button_large = g1tsblarge,
	g1_highelf_temple_secret_button_small = g1tsbsmall,
	g1_highelf_temple_secret_button_tiny = g1tsbtiny,
	g1_southern_temple_secret_button_large = g1tsblarge,
	g1_southern_temple_secret_button_small = g1tsbsmall,
	g1_southern_temple_secret_button_tiny = g1tsbtiny,
	g1_mine_secret_button_large = {0.39,1.74,0},
	g1_mine_secret_button_small = {-0.24,1.95,0},
	g1_town_secret_button_large = g1dsblarge,
	g1_town_secret_button_small = g1dsbsmall,
}
end
cache = {}
function getSecretButtons(level)
	if not cache[level] then
		updateCache(level)
	end
	return cache[level]
end

-- a button that once existed is gone, remove it from cache
function deadButton(level,index)
	table.remove(cache[level],index)
end

function updateCache(level)
	cache[level] = {}
	for e in Dungeon.getMap(level):allEntities() do
		if buttons[e.name] then
			table.insert(cache[level],e.id)
		end
	end
end

function getButtonOffset(button)
	return buttons[button]
end]]
		},
	},
}
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
Isaac
Posts: 3182
Joined: Fri Mar 02, 2012 10:02 pm

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by Isaac »

AndakRainor wrote:Good point. It could detect secret buttons instead, what do you think about that? (or do you plan on using third eye in the mod?)
Secrets are also scripted; as when the correct item is placed upon the altar... I think to really work, the spell would require a dedicated listing of what secrets it can detect in the specific dungeon [mod] where it is used. The list [table] could include the location of the secret, and a suitable '8-ball' style cryptic hint ~specific to the secret.

In "Lands of Lore", party members could spot a secret, and mention it aloud. The same could be done for the casting mage; perhaps even have a duration of effect, where the mage makes comments if they pass near a detectable secret. [Comments, but not the outright answer.]
minmay
Posts: 2777
Joined: Mon Sep 23, 2013 2:24 am

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by minmay »

Yes, definitely. Cut any spells you want.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: [Open / Signup] One Room Round Robin 3 - Calling All Mod

Post by AndakRainor »

I have a list of 49 spells now, versus 20 in Isle of Nex. Do you want me to continue?
Post Reply