Jump to content

There are something like bugs in the code


Recommended Posts

In encounters.lua, line 217~227:

local function HalfAdaptiveWaveCount(difficulty, spawner)
	-- Get a wave, cut it in half and ceil() it.
	-- Difficulty of this wave is easy earlier in dungeon.
	return math.ceil(spawner:GetCurrentAdaptiveWaveSize(difficulty)/2)
end

local function HalfAdaptiveWaveCountEasy(difficulty, spawner)
	-- Get a wave, cut it in half and floor() it.
	-- Distributes difficulty such that the early stages of the dungeon are easier.
	return math.ceil(spawner:GetCurrentAdaptiveWaveSize(difficulty)/2)
end

Code comment in function HalfAdaptiveWaveCountEasy() says it will floor() the wave, but it actually ceil() the wave.

 

In sg_player_cannon.lua, line 338:

local NO_AMMO_DODGEVELOCITY_MULT = 1

And line 523:

	local ammomult = inst.sg.statemem.blastshotammo and inst.sg.statemem.blastshotammo < 1 and NO_AMMO_DODGEVELOCITY_MULT or 1 -- Dodging with no ammo moves only 50% as far

Code comment in function ConfigureNewDodge() says "Dodging with no ammo moves only 50% as far", but the multiplier is 1, not 0.5.

And in game, no ammo dodging is actually a fixed distance shorter than normal dodging (see videos below), not 50% of the distance.

5 hours ago, cniumunto said:

In sg_player_cannon.lua, line 338:

local NO_AMMO_DODGEVELOCITY_MULT = 1

And line 523:

	local ammomult = inst.sg.statemem.blastshotammo and inst.sg.statemem.blastshotammo < 1 and NO_AMMO_DODGEVELOCITY_MULT or 1 -- Dodging with no ammo moves only 50% as far

Code comment in function ConfigureNewDodge() says "Dodging with no ammo moves only 50% as far", but the multiplier is 1, not 0.5.

Note: The line numbers of these two lines of code are based on the beta branch. In the live branch, the line numbers are 322 and 507.

On 10/14/2024 at 12:12 AM, cniumunto said:

And in game, no ammo dodging is actually a fixed distance shorter than normal dodging (see videos below), not 50% of the distance.

Add: This fixed distance may be affected by function DoBlastKickback().

In sg_player_cannon.lua, line 581~583 (line 598~600 in the beta):

local function DoBlastKickback(inst)
	inst.Physics:MoveRelFacing(-125 / 150)
end

6*125/150=5, That's exactly the distance (number of grids) between the two lamps in the video.
And line 1540~1545 (line 1625~1630 in the beta):

			FrameEvent(1, function(inst)
				if inst.sg.statemem.blastshotammo > 0 then
					DoBlastKickback(inst) -- Start with a strong burst
				end
				StartNewDodge(inst)
			end),

 

 

Another thing like a bug (only in beta):
In sg_player_cannon_skill_aim.lua, line 309:

return StateGraph("sg_player_cannon_skill_butt", states, events, "skill_cannon_butt")

Is it return to the StateGraph of another skill?

On 10/14/2024 at 12:12 AM, cniumunto said:

In encounters.lua, line 217~227:

local function HalfAdaptiveWaveCount(difficulty, spawner)
	-- Get a wave, cut it in half and ceil() it.
	-- Difficulty of this wave is easy earlier in dungeon.
	return math.ceil(spawner:GetCurrentAdaptiveWaveSize(difficulty)/2)
end

local function HalfAdaptiveWaveCountEasy(difficulty, spawner)
	-- Get a wave, cut it in half and floor() it.
	-- Distributes difficulty such that the early stages of the dungeon are easier.
	return math.ceil(spawner:GetCurrentAdaptiveWaveSize(difficulty)/2)
end

Code comment in function HalfAdaptiveWaveCountEasy() says it will floor() the wave, but it actually ceil() the wave.

Add: The function HalfAdaptiveWaveCountEasy() is only used in encounter e02 and e03 in Great Rotwood Forest.
In encounters.lua, line 649~683 (line 670~704 in the beta):

		e02 = { -- One melee, then an ambush!
			factor = 4,
			exec_fn = function(spawner)
				spawner:SpawnPropDestructibles(4)
				spawner:SpawnWave({ melee1 = 1 })
				spawner:WaitForDefeatedPercentage(.66)
				spawner:WaitForSeconds(0.75)
				spawner:SpawnWave({ ranged1 = HalfAdaptiveWaveCountEasy(Difficulty.id.easy, spawner) })
				spawner:SpawnWave({ ranged1 = HalfAdaptiveWaveCountEasy(Difficulty.id.easy, spawner) })
				if IsAfterMiniboss(spawner) then
					-- If we're past miniboss, add some extra ambushers
					spawner:SpawnWave({ melee1 = HalfAdaptiveWaveCountEasy(Difficulty.id.easy, spawner) })
					spawner:WaitForDefeatedPercentage(0.33)
					spawner:WaitForSeconds(0.75)
					spawner:SpawnWave({ melee2 = SpawnSometimes(1, spawner) })
				end
			end,
		},
		e03 = { -- Just melees, an onslought!
			factor = 4,
			exec_fn = function(spawner)
				spawner:SpawnPropDestructibles(4)
				SpawnRandomTraps(spawner) -- Spawn some traps to make it fun to kill these groups of cabbages
				spawner:SpawnWave({ melee1 = 2 })
				spawner:WaitForDefeatedPercentage(0.5)
				spawner:WaitForSeconds(0.75)
				spawner:SpawnWave({ melee1 = 2 })
				spawner:WaitForDefeatedPercentage(0.5)
				spawner:WaitForSeconds(0.75)
				spawner:SpawnWave({ melee1 = HalfAdaptiveWaveCount(Difficulty.id.easy, spawner) })
				spawner:SpawnWave({ support1 = SpawnAfterMiniboss(1, spawner)})
				spawner:WaitForSeconds(0.75)
				spawner:SpawnWave({ melee1 = HalfAdaptiveWaveCountEasy(Difficulty.id.easy, spawner) })
			end,
		},

In these two encounters, the values passed to the argument "difficulty" are all "difficult.id.easy", and the only possible values for "adaptive_counts" (= adaptive wave size) on the easy difficulty are 4 (when progress<=0.5) or 5 (when progress>0.5), both of which are rounded down to 2 after divided by 2. Because of this, if the function HalfAdaptiveWaveCountEasy() floor() the wave instead of ceil() the wave, the expectation that "the early stages of the dungeon are easier" will not be achieved.

Encounter e04 and m03 in The Molded Grave are almost the same. Is this a bug or intentional?

Encounter e04 in The Molded Grave, in encounters.lua, line 1255~1264 (line 1276~1285 in the beta):

		e04 = { --swarmy worms
			factor = 5,
			exec_fn = function(spawner)
				SpawnRandomTraps(spawner)
				spawner:SpawnPropDestructibles(4)
				spawner:SpawnWave(waves.Raw{ woworm = 2, swarmy = 2 })
				spawner:WaitForDefeatedPercentage(0.8)
				spawner:SpawnWave(waves.Raw{ woworm = 1, swarmy = 3 })
			end,
		},

And encounter m03 in The Molded Grave, in encounters.lua, line 1288~1297 (line 1309~1318 in the beta):

		m03 = { --Woworms
			factor = 2,
			exec_fn = function(spawner)
				SpawnRandomTraps(spawner)
				spawner:SpawnPropDestructibles(3)
				spawner:SpawnWave(waves.Raw{ woworm = 2, swarmy = 2 })
				spawner:WaitForDefeatedPercentage(0.8)
				spawner:SpawnWave(waves.Raw{ swarmy = 3, woworm = 1 })
			end,
		},

Normal miniboss encounter in Nocturne Grove, compared to its previous version and hard version, has this line of code missing:

				spawner:WaitForMinibossHealthPercent(0.6)

Is this a bug or intentional?
For reference:
Normal miniboss encounter in Nocturne Grove, in encounters.lua, line 1944~1958:

Spoiler
		e01 = {
			constraint_fn = function(spawner)
				return not ResolveHuntModifiers().HardMiniboss
			end,
			exec_fn = function(spawner)
				SpawnTrapWave(spawner, waves.Raw{ trap_weed_spikes = 3 })
				spawner:SpawnPropDestructibles(5)
				spawner:SpawnMiniboss(waves.Raw{ gourdo_miniboss = 2 })
				if (TheNet:GetNrPlayersOnRoomChange() > 2) then
					spawner:SpawnWave(waves.Raw{ battoad = 1 }, 0, 0, nil, true)
				end
				spawner:WaitForMinibossHealthPercent(0)
				spawner:CleanUpRemainingEnemies()
			end,
		},

Previous version of normal miniboss encounter in Nocturne Grove (before the Splintershard Cavern Update): 

Spoiler
		e01 = {
			exec_fn = function(spawner)
				SpawnTrapWave(spawner, waves.Raw{ trap_weed_spikes = 3 })
				spawner:SpawnPropDestructibles(5)
				spawner:SpawnMiniboss(waves.Raw{ gourdo_miniboss = 2 })
				spawner:WaitForMinibossHealthPercent(0.6)
				if (TheNet:GetNrPlayersOnRoomChange() > 2) then
					spawner:SpawnWave(waves.Raw{ battoad = 1 }, 0, 0, nil, true)
				end
				spawner:WaitForMinibossHealthPercent(0)
				spawner:CleanUpRemainingEnemies()
			end,
		},

Hard miniboss encounter in Nocturne Grove, in encounters.lua, line 1959~1976:

Spoiler
		e02 = { -- Encounter for hard miniboss
			constraint_fn = function(spawner)
				return ResolveHuntModifiers().HardMiniboss
			end,
			exec_fn = function(spawner)
				SpawnTrapWave(spawner, waves.Raw{ trap_weed_spikes = 3 })
				spawner:SpawnPropDestructibles(5)
				spawner:SpawnMiniboss(waves.Raw{ gourdo_miniboss = 2 })
				spawner:WaitForSeconds(4.9)
				spawner:SpawnWave(waves.Raw{ yammo_miniboss = 1})
				spawner:WaitForMinibossHealthPercent(0.6)
				if (TheNet:GetNrPlayersOnRoomChange() > 2) then
					spawner:SpawnWave(waves.Raw{ battoad = 1 }, 0, 0, nil, true)
				end
				spawner:WaitForMinibossHealthPercent(0)
				spawner:CleanUpRemainingEnemies()
			end,
		},

There is no "airborne" tag at any time in the "cannon_quickrise" and "cannon_quickrise_noammo" states. However, in the animation of these two states, the player character looks airborne for a period of time. The visual perception is not consistent with the actual condition. Is this a bug or intentional?
In sg_player_cannon.lua, line 4141~4331(REV. 639146):

Spoiler
	State({
		name = "cannon_quickrise",
		tags = { "busy", "heavy_attack", "attack", "dodge", "dodging_backwards" },

		onenter = function(inst, real_quickrise)

			inst.AnimState:PlayAnimation("cannon_getup_dodge")
			SGPlayerCommon.Fns.UnsetCanDodge(inst)

			SGPlayerCommon.Fns.SetRollPhysicsSize(inst)
			inst.HitBox:SetInvincible(true)
			SGCommon.Fns.StartJumpingOverHoles(inst)

			if real_quickrise then
				-- We can get here through the normal cannon Heavy combo, so only push the event + present effects when this is a true quickrise.
				inst:PushEvent("quick_rise")
				SGPlayerCommon.Fns.DoCannonQuickRise(inst)
			end

			inst:PushEvent("dodge")
			inst:PushEvent("attack_state_start")
			inst.sg.mem.attack_id = "QUICK_RISE"
			inst.sg.mem.attack_type = "heavy_attack"

			-- TODO: commonize this
			local hitstop = TUNING.HITSTOP_PLAYER_QUICK_RISE_FRAMES
			inst.components.hitstopper:PushHitStop(hitstop)
			inst:DoTaskInAnimFrames(hitstop, function()
				if inst ~= nil and inst:IsValid() then
					if GetRemainingAmmo(inst) > 0 then
						inst.Physics:StartPassingThroughObjects()

						combatutil.StartMeleeAttack(inst)

						inst.components.hitbox:PushCircle(0, 0, ATTACKS.QUICKRISE.RADIUS, HitPriority.MOB_DEFAULT)

						local focus = inst.sg.mem.focus_sequence[GetRemainingAmmo(inst)]
						-- fx, lots that would usually be done in embellisher but since focusness matters we'll do it here:
						EffectEvents.MakeEventSpawnEffect(inst, {
							fxname= focus and "cannon_aoe_explosion_med_focus" or "cannon_aoe_explosion_med",
							offx=0.2,
							offy=0.45,
							offz=-0.1,
							scalex=1.0,
							scalez=1.0,	
						})
						EffectEvents.MakeEventSpawnEffect(inst, {
							fxname=focus and "fx_cannon_smoke_aoe_focus" or "fx_cannon_smoke_aoe",
							offx=0.0,
							offy=0.0,
							offz=-0.1,
							scalex=1.2,
							scalez=1.2,
						})

						ParticleSystemHelper.MakeEventSpawnParticles(inst, {
							duration=90.0,
							particlefxname= focus and "cannon_burst_aoe_sphere_sml_focus" or "cannon_burst_aoe_sphere_sml",
						})
						ParticleSystemHelper.MakeEventSpawnParticles(inst, {
							duration=90.0,
							offx=1.2,
							offy=0.0,
							offz=0.0,
							particlefxname= focus and "cannon_shot_quickrise_focus" or "cannon_shot_quickrise",
							render_in_front=true,
							use_entity_facing=true,
						})

						--sound
						local isFocusAttack = inst.sg.mem.focus_sequence[inst.sg.mem.ammo]
						soundutil.PlayCodeSound(inst, fmodtable.Event.Cannon_shoot_quickrise, {
								max_count = 1,
								fmodparams = {
									isFocusAttack = isFocusAttack,
									cannon_heavyShotType = 2,
									cannon_remainingAmmo_scaled = GetRemainingAmmo(inst) / GetMaxAmmo(inst),
									cannon_ammo_percentDelta = inst.sg.statemem.percent_ammo_changed_param,
								}
							})

						local snapshot_ammo = inst.sg.mem.ammo
						UpdateAmmo(inst, 1)

						if inst.sg.mem.ammo and snapshot_ammo and (inst.sg.mem.ammo <= snapshot_ammo) then
							inst:DoTaskInAnimFrames(2, function(inst) PlayLowAmmoSound(inst, inst.sg.mem.ammo) end)
						end

						ConfigureNewDodge(inst)
						StartNewDodge(inst)
					end
				end
			end)
		end,

		onupdate = function(inst)
			DoDodgeMovement(inst, true)
		end,

		timeline =
		{
			FrameEvent(1, function(inst)
				combatutil.EndMeleeAttack(inst)
			end),
			FrameEvent(3, function(inst)
				DoQuickRiseKickback(inst)
			end),

			--CANCELS
			FrameEvent(8, function(inst)
				inst.sg.statemem.airplant = true
				if inst.sg.statemem.triedplantearly then
					local transitiondata = { maxspeed = inst.sg.statemem.maxspeed, speed = inst.sg.statemem.speed, framessliding = inst.sg.statemem.framessliding }
					inst.sg:GoToState("blast_TO_plant", transitiondata)
				end
			end),
			FrameEvent(11, function(inst)
				inst.sg.statemem.airplant = false
				inst.sg.statemem.groundplant = true
				SGCommon.Fns.SafeStopPassingThroughObjects(inst)
			end),
			FrameEvent(15, SGPlayerCommon.Fns.RemoveBusyState),
		},

		onexit = function(inst)
			inst.HitBox:SetInvincible(false)
			SGPlayerCommon.Fns.UndoRollPhysicsSize(inst)
			SGCommon.Fns.SafeStopPassingThroughObjects(inst)
			inst.Physics:Stop()
			SGCommon.Fns.StopJumpingOverHoles(inst)
		end,


		events =
		{
			EventHandler("animover", function(inst)
				inst.sg:GoToState("idle")
			end),

			EventHandler("controlevent", function(inst, data)
				if data.control == "dodge" then
					local transitiondata = { maxspeed = inst.sg.statemem.maxspeed, speed = inst.sg.statemem.speed, framessliding = inst.sg.statemem.framessliding }
					if inst.sg.statemem.airplant then
						inst.sg:GoToState("blast_TO_plant", transitiondata)
					elseif inst.sg.statemem.groundplant then
						inst.sg:GoToState("cannon_plant_pre", transitiondata)
					else
						inst.sg.statemem.triedplantearly = true
					end
				end
			end),

			EventHandler("hitboxtriggered", OnQuickriseHitBoxTriggered),
		},
	}),

	State({
		name = "cannon_quickrise_noammo",
		tags = { "busy", "heavy_attack", "attack" },

		onenter = function(inst, real_quickrise)
			inst.AnimState:PlayAnimation("cannon_getup_dodge")

			if real_quickrise then
				-- We can get here through the normal cannon Heavy combo, so only push the event + present effects when this is a true quickrise.
				inst:PushEvent("quick_rise")
				SGPlayerCommon.Fns.DoCannonQuickRise(inst)
			end
		end,

		onupdate = function(inst)
		end,

		timeline =
		{
			FrameEvent(1, function(inst)
				PlayNoAmmoSound(inst)
			end),
		},

		onexit = function(inst)
		end,


		events =
		{
			EventHandler("animover", function(inst)
				inst.sg:GoToState("idle")
			end),
		},
	}),

And, although the code comment in the state "cannon_quickrise_noammo" says "We can get here through the normal cannon Heavy combo", it doesn't seem to actually be possible to get to this state through the normal cannon Heavy combo.

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

×
  • Create New...