Hi Pandafox. This is a really neat idea, and you got me thinking. I implemented a different approach to "conditional blocker":
objects.lua
Code: Select all
cloneObject{
name = "conditionalBlocker",
baseObject = "torch_holder",
model = "assets/models/env/dungeon_wall_01.fbx",
onInsertItem = function()
return false
end,
editorIcon = 120,
}
cloneObject{
name = "twoWayConditionalBlocker",
baseObject = "torch_holder",
model = "assets/models/env/dungeon_wall_01.fbx",
onInsertItem = function()
return false
end,
editorIcon = 120,
}
init.lua
Code: Select all
cloneObject{
name = "party",
baseObject = "party",
onMove = function( self , dir )
return partyScript.canMove( self , dir )
end,
onAttack = function( self , weapon )
return partyScript.canAttack( self , weapon )
end,
}
script_entity named partyScript
Code: Select all
canMove = function ( self , dir )
for entity in entitiesAt( self.level , self.x , self.y ) do
if blockerScript.isBlocker( entity ) and
entity.facing == dir then
return false
end
end
return true
end
canAttack = function ( champion , weapon )
for entity in entitiesAt( party.level , party.x , party.y ) do
if blockerScript.isBlocker( entity ) and
entity.facing == party.facing then
return false
end
end
return true
end
script_entity named utilityScript
Code: Select all
allEntities = function( level )
local s = {}
s["offset"] = 0
local c = 0
for i=0,31 do
for j=0,31 do
for k in entitiesAt(level,i,j) do
c = c + 1
s[c] = k
end
end
end
local f = function ( s , v )
local offset = s["offset"] + 1
s["offset"] = offset
return s[offset]
end
return f , s , nil
end
getXYFromDir = function ( dir )
if dir == 0 then
return 0 , -1
elseif dir == 1 then
return 1 , 0
elseif dir == 2 then
return 0 , 1
elseif dir == 3 then
return -1 , 0
end
end
Why did I redefine allEntities?
getXYFromDir is just the same function that others have called getForward . Maybe I should use the standard name for the sake of consistency?
script_entity named blockerScript
Code: Select all
allEntities = utilityScript.allEntities
do --setup a duplicate for each twoWayConditionalBlocker
local twoWays = {}
local c = 0
local maxDungeonLevel = 1
for level = 1,maxDungeonLevel do
for entity in allEntities( level ) do
if entity.name == "twoWayConditionalBlocker" then
c = c + 1
twoWays[c] = entity
end
end
end
for i , v in ipairs( twoWays ) do
local x , y = utilityScript.getXYFromDir( v.facing )
x = x + v.x
y = y + v.y
spawn( "twoWayConditionalBlocker" , v.level , x , y ,
( v.facing + 2 ) % 4 )
end
end
blockers = { "conditionalBlocker" ,
"twoWayConditionalBlocker" ,}
reverseBlockers = {}
for i , v in ipairs( blockers ) do
reverseBlockers[v] = i
end
isBlocker = function ( entity )
return reverseBlockers[entity.name]
end
If you place a twoWayConditionalBlocker in the editor, then the blockerScript will automatically create a duplicate in the adjacent tile facing the opposite way (when you begin preview or play.) You should adjust the line "local maxDungeonLevel = 1" in blockerScript to suit your dungeon. If you place a conditionalBlocker, then it will only render and block movement one way (but looking at it from the other side produces some weird graphical effects.)
Undocumented feature used: torch_holder supports onInsertItem just like alcove.
This version of blockerScript doesn't get a huge benefit out of the setup of blockers and reverseBlockers , but this setup does make it very easy to expand the list of blockers. If you add more blocker type objects, just expand the list of names in blockers.
As compared to your method:
1. Monsters can attack through conditionalBlocker and twoWayConditionalBlocker. If you want to disallow this, then give the monster an onAttack hook similar to the party's onAttack hook. They can't attack through your walls (although a ranged enemy could if the party wasn't adjacent to the wall - see point 4.)
2. Editor placement. You just place these items like you place torch_holder. The editor wants you to place them against walls, but you can always add/remove walls as you go, or you could place them and then move them around with QWEASD. I found this somewhat easier than following the naming scheme that your method required.
3. Normally, if the party tries to move into an obstructed square, they get visual feedback that they started to move in that direction but couldn't complete the movement. With these items, they just don't move at all. Similarly, the party doesn't even punch the wall if they try to attack while facing a blocker, to prevent hitting the monster on the other side. A savvy player could use these quirks to determine where the "fake" walls are. Your walls don't have this problem.
4. You can always throw items through my blockers. I haven't come up with a great way to prevent this. With yours, you can throw an item through it from a distance, but not if you are standing right next to it.
5. It's easy to add an onMove hook to individual monsters ( just copy the party's onMove code.) This allows you to let some monsters pass through (like ghosts) while other monsters are blocked just like the party. With your pressure plate method, each plate either is triggered or is not triggered by monsters, so it's more difficult to conditionalize which monsters get blocked by which blockers.
6. I haven't figured out a good way to deal with the party casting spells. I don't want to prevent them casting self targeting spells while they're facing the blocker, but I do want to prevent them casting spells that would pass through the blocker. Ideally, casting fireburst while facing a blocker would damage the party, just as it does while facing a real wall. You could always take the cop out and use anti-magic zones to avoid dealing with this.
7. As a quirk of my implementation, in the editor you can place a torch "in" the conditionalBlocker. I disabled placing torches in game. If you try, then you just throw the torch through ( see point 4. )
8. Monsters can still see through my blockers. Even if they have onMove and onAttack hooks so that the blockers will block them, their AI will always try to move and attack through the blockers if they see the party on the other side.
Some of these differences you may consider as advantages, some as disadvantages. Mostly, I'm just posting what I have to keep the ideas flowing.