Page 33 of 75

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

Posted: Wed Jul 13, 2016 9:43 pm
by minmay
Would this work?
Image
(double-sided wall behind the demon head)
This way the only things that are moved are the entrance door and the wall texts.

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

Posted: Wed Jul 13, 2016 10:22 pm
by Jgwman
Yup, that should be fine. You can go ahead and make that change if you want, or I'll get to it in a few hours.

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

Posted: Thu Jul 14, 2016 8:08 am
by minmay
Something in Twisted Forest is occasionally and randomly dealing about 300 damage to the party regardless of their location.

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

Posted: Thu Jul 14, 2016 8:10 am
by Jgwman
I noticed that; it was extremely weird. I think the same thing happened once or twice in the final room of the final boss fight. Not sure what exactly it's related to.

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

Posted: Thu Jul 14, 2016 9:15 am
by minmay
Ok, I figured it out. The problem is you are not checking to make sure the party is actually on the same level as the monster in your brains. Take this one:

Code: Select all

			onThink = function(self)
			 if self.go.x == party.x then 
			  self:turnTowardsParty()
			  if math.abs(self.go.y - party.y) <= 1 then self:performAction("basicAttack")
			  else self:performAction("charge") end
			 elseif self.go.y == party.y then 
			  self:turnTowardsParty()
			  if math.abs(self.go.x - party.x) <= 1 then self:performAction("basicAttack")
			  else self:performAction("charge") end
			 else self:moveTowardsParty() end
			 return true
			end,
If I'm in some random level and happen to be at an x,y position adjacent to the mt_oni that's on a completely different level, the mt_oni attacks the party.

Any testers who would like the mod to be playable can run this in the console:

Code: Select all

mt_oni_1.brain:disable()
Also, the return values of your brains are wrong in general. You should not return true if no action is performed. Instead, if you want to perform no action, you should call self:wait() (which returns true so just type "return self:wait()") which will always succeed.
All actions return true if successfully performed and false if not. Once an action is successfully performed there is no reason to try to perform additional actions, just return true.
More info:
viewtopic.php?f=22&t=9919
https://github.com/JKos/log2doc/wiki/Co ... -component



Edit: You know what, I'm just going to give you some examples of custom brains from Avenie's Awful Adventure:
SpoilerShow
Very simple complete brain (remember this monster has the swarm property so it needs to be at the same square as the party to attack):

Code: Select all

		{
			class = "SwarmBrain",
			name = "brain",
			sight = 5,
			allAroundSight = true,
			onThink = function(self)
				if self.go.level == party.level and self.go.x == party.x and self.go.y == party.y and self.go.elevation == party.elevation then
					return self:performAction("basicAttack")
				else
					return self:wanderIfPartyNotDetected() or self:pursuit()
				end
			end,
		},
Here is one that pastes extra behaviour onto an existing brain, but falling back to the existing brain often. In this case the monster has three new attacks in addition to the typical uggardian frontal attack: a projectile turnAttack, "sidesAttack" which shoots projectiles both left and right of the monster, and "doubleAttack" which blasts a large area in front of the monster. The custom brain is used to fire these new attacks when the monster is in a good position to hit the party. The rest of the time (or when the attacks are on cooldown) it falls back to the default UggardianBrain for its movement and frontal attack behaviour.

Code: Select all

		{
			class = "UggardianBrain",
			name = "brain",
			sight = 6,
			morale = 100,
			seeInvisible = true,
			allAroundSight = true, -- so that using sidesAttack makes sense
			-- Very simple behaviour modification, just use the new attacks when the party is in a good position to
			-- get hit by them.
			onThink = function(self)
				if self.seesParty and party.level == self.go.level and party.elevation == self.go.elevation
				and self.go.map:checkLineOfFire(self.go.x,self.go.y,party.x,party.y,self.go.elevation) then
					if (self.partyLeft or self.partyRight) and not (self.partyAhead or self.partyBehind) then
					-- cannot use self:turnAttack() to autodetect facing as it only succeeds if party is adjacent
						return math.random() < 0.5 and self:performAction("turnAttack",self.partyRight and 1 or -1) or self:performAction("sidesAttack") or self:performAction("turnAttack",self.partyRight and 1 or -1)
					-- When either frontal attack could conceivably hit the party, don't favor double attack
					-- Note that if double attack fails and ranged attack could hit, builtin brain will do
					-- ranged attack
					elseif self.partyStraightAhead and math.random() < 0.5 and self:performAction("rangedAttack") then
						return true
					-- Only use double attack when there's ample space for it and the party is not too close
					elseif self.partyAhead and self.partyDistY > 1 and math.abs(self.partyDistX) <= 1 then
						local xAxis = self.go.facing%2==0
						if xAxis then
							if self.go.map:checkLineOfFire(self.go.x+1,self.go.y,self.go.x+1,party.y,self.go.elevation)
							and self.go.map:checkLineOfFire(self.go.x-1,self.go.y,self.go.x-1,party.y,self.go.elevation) then
								return self:performAction("doubleAttack")
							end
						else
							if self.go.map:checkLineOfFire(self.go.x,self.go.y+1,party.x,self.go.y+1,self.go.elevation)
							and self.go.map:checkLineOfFire(self.go.x,self.go.y-1,party.x,self.go.y-1,self.go.elevation) then
								return self:performAction("doubleAttack")
							end
						end
					end
				end
				return false
			end,
		},
Finally, here is a brain that has custom low-level movement behaviour. The default brain and builtin pathfinding like BrainComponent:moveTowardsParty() are almost never used; the monster only falls back to the builtin brain when it's far away from the party and wants to slowly approach.

Code: Select all

		{ -- Note: we use performAction("rangedattack") instead of rangedAttack()
		-- because rangedAttack() only shoots if the party is in front of the monster
		-- and we want to fire regardless of the party's direction
		-- XXX: This could be cleaned up a lot, make some local functions?
		-- XXX: This sight radius is exceptionally long
			class = "RangedBrain",
			name = "brain",
			sight = 8,
			allAroundSight = true,
			morale = 100,
			seeInvisible = true,
			onThink = function(self)
				if self.seesParty then
					-- If X blast could hit party, do it, since it's our
					-- signature move
					if self.partyDiagonal and self:performAction("xBlast") then
						return true
					elseif self.partyAdjacent then
						local free = {}
						if not self.blockedFront then
							table.insert(free,0)
						end
						if not self.blockedLeft then
							table.insert(free,3)
						end
						if not self.blockedRight then
							table.insert(free,1)
						end
						if not self.blockedBack then
							table.insert(free,2)
						end
						-- Run away, because artillery is not good in melee.
						-- We don't use flee() here because it would turn, which
						-- is a waste of time on this monster.
						-- ...but attack occasionally.
						if (#free==0 or math.random() < 0.15) and self:performAction("rangedAttack") then
							return true
						elseif #free ~= 0 then
							local choice = free[math.random(#free)]
							if choice == 0 then
								return self:moveForward() or self:wander()
							elseif choice == 1 then
								return self:strafeRight() or self:wander()
							elseif choice == 2 then
								return self:moveBackward() or self:wander()
							else -- 3
								return self:strafeLeft() or self:wander()
							end
						else
							return self:wander() -- turn uselessly like a dork
						end
					elseif self.partyDistY == 0 or self.partyDistX == 0 then
						return self.go.map:checkLineOfFire(self.go.x,self.go.y,party.x,party.y,self.go.elevation) and self:performAction("rangedAttack") or self:wander()
					else
						-- Let the builtin AI approach sometimes, otherwise there are situations where the party can be completely safe by staying on one square
						if math.random() < 0.2 then
							return false
						end
						-- Try to line up for a ranged attack.
						local x
						if math.abs(self.partyDistY) == math.abs(self.partyDistX) then
							x = math.random() < 0.5
						else
							x = (math.abs(self.partyDistY) > math.abs(self.partyDistX))
						end
						if x then
							if self.partyDistX > 0 then
								if (not self.blockedRight) and self:strafeRight() then return true end
							else
								if (not self.blockedLeft) and self:strafeLeft() then return true end
							end
							if self.partyDistY > 0 then
								if (not self.blockedBack) and self:moveBackward() then return true end
							elseif self.partyDistY < 0 then
								if (not self.blockedFront) and self:moveForward() then return true end
							end
						else
							if self.partyDistY > 0 then
								if (not self.blockedBack) and self:moveBackward() then return true end
							else
								if (not self.blockedFront) and self:moveForward() then return true end
							end
							if self.partyDistX > 0 then
								if (not self.blockedRight) and self:strafeRight() then return true end
							else
								if (not self.blockedLeft) and self:strafeLeft() then return true end
							end
						end
					end
				end
				return self:wander()
			end,
		},

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

Posted: Thu Jul 14, 2016 9:47 am
by Jgwman
Ah, I thought I had already been checking the level (and I seem to remember the problem happening in the forest before I added any custom AIs, but evidently not).

I did intentionally have the brain never fall back on basic behavior, as it seemed pointless if I implemented my AI correctly (in my case; I can see why it'd be useful), which was why I was returning true in all cases.

I did not realize there was a sleep function, but makes sense. Will fix shortly.

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

Posted: Thu Jul 14, 2016 10:00 am
by minmay
Jgwman wrote:I did intentionally have the brain never fall back on basic behavior, as it seemed pointless if I implemented my AI correctly (in my case; I can see why it'd be useful), which was why I was returning true in all cases.
Yes but if you return true without performing any action successfully, the brain will think again on the next frame, it's standard for brains to wait() instead.

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

Posted: Thu Jul 14, 2016 10:03 am
by Jgwman
Yeah, I get that. Makes sense, I just hadn't seen pretty much any of the reference info on it and kinda winged it.

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

Posted: Thu Jul 14, 2016 12:00 pm
by AndakRainor
Hello, I just sent my drop box account name to Isaac. Can I get there a last "stable" version? I would like to see the new additions to the project and train for the integration of my room. After that I will be ready to redo it when ever you want.

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

Posted: Thu Jul 14, 2016 1:58 pm
by Jgwman
If you can wait just a bit, I'll upload a version which should be good to go (stability wise) with everything that's been added. Will update this post once it's up.

EDIT: Revision 34 just added to the Dropbox, should be stable. Andak, if you see this, feel free to PM me your Dropbox info if Isaac hasn't invited you yet (I'm pretty sure anyone on the project can add new members)