 |
Castle Paradox
|
View previous topic :: View next topic |
Author |
Message |
Fenrir-Lunaris WUT

Joined: 03 Feb 2003 Posts: 1747
|
Posted: Tue May 05, 2009 2:18 pm Post subject: Zelda Script Debugging and wall checking? |
|
|
There's an issue with this script regarding wall collision checking - namely that there isn't any. I'd like to fix that, but before then, I'll post what is essentially the meat and bones of Arcadia Incident Report's main plotscript. So here goes:
Code: | script,lifebar,begin
FOE10HP := 3
FOE11HP := 3
FOE12HP := 3
FOE13HP := 3
FOE14HP := 3
FOE15HP := 3
FOE16HP := 3
FOE17HP := 3
FOE18HP := 3
FOE19HP := 3
set tag (tag:safe,off)
|
This sequence starts the script up and sets every enemy on the map to have 3 HP. It also turns a tag off, just because.
Code: |
if (checktag(tag:safe) == on) then (show no value)
wait (1)
while (checktag (tag:safe) == off) do (
|
This segment is mainly redundant but is necessery when combined with another script. Mainly it causes the game to NOT display your health when you're safe. It also sets up the loop and the meat of the script when you aren't safe...
Code: |
(
set hero speed (me,0)
if (key is pressed (key:Pageup)) then (set hero direction (me,right), put hero (me,hero-x +
hero-speed, hero-y -- hero-speed))
if (key is pressed (key:right)) then (set hero direction (me,right),put hero (me,hero-x +
hero-speed,hero-y))
if (key is pressed (key:Pagedown)) then (set hero direction (me,right), put hero (me,hero-x + hero-speed,
hero-y + hero-speed))
if (key is pressed (key:Home)) then (set hero direction (me,left), put hero (me,hero-x -- hero-speed, hero-y
-- hero-speed))
if (key is pressed (key:left)) then (set hero direction (me,left),put hero (me,hero-x -- hero-speed,hero-y))
if (key is pressed (key:End)) then (set hero direction (me,left), put hero (me,hero-x -- hero-speed, hero-y
+ hero-speed))
if (key is pressed (key:up)) then (set hero direction (me,up), put hero (me,hero-x,hero-y -- hero-speed))
if (key is pressed (key:down)) then (set hero direction (me,down), put hero (me,hero-x,hero-y + hero-speed))
)
|
Craziness here. Firstly, setting the hero's speed to ZERO all but disables the player controlled input for walking with the default OHR keys. Their walking speed is reset when entering a "safe" zone, thus creating the illusion of total control in and out of battles.
hero-x is the hero's X position in pixels.
hero-y is the hero's Y position in pixels too.
hero-speed is the hero's innate "speed" stat, 1,2,4,5,10,or 20. By default this value is 4, but equipment can change this stat (and must ONLY change it to one of the listed values!!).
The script then runs a case by case basis for movement by checking keys. You'll note that it's presently set up to use a numpad for walking, and that moving on a "diagonal" sets you to face either the left or right aspect of that diagonal. There's no real reason to do that, aside from playable area essentially being all but a widescreen resolution at 16x9 tiles, and longstanding game tradition. Eventually the extra "diagonals" will get rewritten to simply check if two keys are being pressed at once, but not for now.
The basic gist is that if a directional key is pressed, the hero is "moved" by it's "speed" in pixels in that direction.
An astute observer of OHRRPGCE plotscripting will very quickly realize that there is NO checking for wall collision at this point. Mostly this is an artifact of the code's original tile-based movement. I would like to fix this with a VERY simple check. Ideally, this would be done by checking whether the TILES around the hero's sprite have an "A" passability bitset, and not checking the actual walls themselves - then simply disallow moving the hero sprite into the boundaries of that tile. I've a feeling this would be faster than checking each individual wall passability, but I could be wrong - and I have no idea on how to do either.
Code: |
if (checktag(tag:safe) == on) then (show no value)
KYLEHP := get hero stat(hero:Kyle,stat:HP,current stat)
hero-x := hero pixel x(me)
hero-y := hero pixel y(me)
hero-speed := get hero stat(hero:Kyle,stat:Speed,current stat)
show value (KYLEHP)
|
Again, the redundant check for hero HP. For some reason, this step has to appear multiple times. It is also here that the various variables are defined and continuously updated. Player HP is displayed in real time, as we will shortly see.
Code: |
if ( KYLEHP >= (100) ) then ( KYLEHP := 99 )
if (KYLEHP >= (50)) then ( Alter NPC (0,NPCstat:picture,32),Alter NPC (9,NPCstat:picture,35))
if (KYLEHP <= (49)) then ( Alter NPC (0,NPCstat:picture,33),Alter NPC (9,NPCstat:picture,36))
if (KYLEHP <= (25)) then ( Alter NPC (0,NPCstat:picture,34),Alter NPC (9,NPCstat:picture,37))
if (get hero stat (me,stat:MP,current stat) == (3)) then ( Set NPC direction (0,up))
if (get hero stat (me,stat:MP,current stat) == (2)) then ( Set NPC direction (0,right))
if (get hero stat (me,stat:MP,current stat) == (1)) then ( Set NPC direction (0,down))
if (get hero stat (me,stat:MP,current stat) == (0)) then ( Set NPC direction (0,left))
if (KYLEHP <= (0)) then ( KYLEHP := 99 , death , exit script )
delete item (item:TIMER,1)
|
These steps quickly check the hero's hp and mp, then alter a pair of NPCs to visually display changes in the hero's status. This is mostly the "color scale" warning used throughout Arcadia Incident of blue-yellow-red. MP is a simpler check of simply rotating one NPC roundabout to display how many grenades/flashbangs the player has left. It also starts the death script if the player's HP ever drops below zero. Finally, it removes ONE item called "timer", which is integral to how often enemies can attack you (it's the period of invincibility you get, and you CAN remove it!)
Code: |
#process FOE hp
if (FOE10HP == 3) then (set NPC direction (20,up))
if (FOE10HP == 2) then (set NPC direction (20,right))
if (FOE10HP == 1) then (set NPC direction (20,down))
if (FOE10HP <= 0) then (destroy NPC (20),FOE10,FOE10HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
|
This sequence will get repeated no more and no less than ten times with different NPCs. It tracks how much health a given foe has, and alters its own lifebar graphic accordingly. Once the NPC drops to zero hp or less, it kills the graphic, runs the foe self destruction script, gives you a little money, experience, and makes this particular enemy do one of three scream sound effects. Nifty. Fast, too.
Code: |
if (FOE11HP == 3) then (set NPC direction (21,up))
if (FOE11HP == 2) then (set NPC direction (21,right))
if (FOE11HP == 1) then (set NPC direction (21,down))
if (FOE11HP <= 0) then (destroy NPC (21),FOE11,FOE11HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE12HP == 3) then (set NPC direction (22,up))
if (FOE12HP == 2) then (set NPC direction (22,right))
if (FOE12HP == 1) then (set NPC direction (22,down))
if (FOE12HP <= 0) then (destroy NPC (22),FOE12,FOE12HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE13HP == 3) then (set NPC direction (23,up))
if (FOE13HP == 2) then (set NPC direction (23,right))
if (FOE13HP == 1) then (set NPC direction (23,down))
if (FOE13HP <= 0) then (destroy NPC (23),FOE13,FOE13HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE14HP == 3) then (set NPC direction (24,up))
if (FOE14HP == 2) then (set NPC direction (24,right))
if (FOE14HP == 1) then (set NPC direction (24,down))
if (FOE14HP <= 0) then (destroy NPC (24),FOE14,FOE14HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE15HP == 3) then (set NPC direction (25,up))
if (FOE15HP == 2) then (set NPC direction (25,right))
if (FOE15HP == 1) then (set NPC direction (25,down))
if (FOE15HP <= 0) then (destroy NPC (25),FOE15,FOE15HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE16HP == 3) then (set NPC direction (26,up))
if (FOE16HP == 2) then (set NPC direction (26,right))
if (FOE16HP == 1) then (set NPC direction (26,down))
if (FOE16HP <= 0) then (destroy NPC (26),FOE16,FOE16HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE17HP == 3) then (set NPC direction (27,up))
if (FOE17HP == 2) then (set NPC direction (27,right))
if (FOE17HP == 1) then (set NPC direction (27,down))
if (FOE17HP <= 0) then (destroy NPC (27),FOE17,FOE17HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE18HP == 3) then (set NPC direction (28,up))
if (FOE18HP == 2) then (set NPC direction (28,right))
if (FOE18HP == 1) then (set NPC direction (28,down))
if (FOE18HP <= 0) then (destroy NPC (28),FOE18,FOE18HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
if (FOE19HP == 3) then (set NPC direction (29,up))
if (FOE19HP == 2) then (set NPC direction (29,right))
if (FOE19HP == 1) then (set NPC direction (29,down))
if (FOE19HP <= 0) then (destroy NPC (29),FOE19,FOE19HP := 3
get money (2)
play sound (random (13,15),false,true)
give experience (hero:kyle,1))
|
Told you it would be repeated often.
This whole bit below just looks to see if the hero is invincible, THEN cycles through the enemy collision detection
Code: |
if ((inventory (6))<=0) then (
|
This checks to see if you have any of those "Timer" items, indicating your invincibility. If you have none, the game will check if you're within range of an enemy.
Code: |
if ((hero-x) -- npc pixel x(10) << 30 &&
npc pixel x(10) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(10) << 30 &&
npc pixel y(10) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
|
If the enemy is even remotely close to the player, it'll try and attack them. This segment gets repeated a LOT.
Code: |
if ((hero-x) -- npc pixel x(11) << 30 &&
npc pixel x(11) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(11) << 30 &&
npc pixel y(11) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(12) << 30 &&
npc pixel x(12) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(12) << 30 &&
npc pixel y(12) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(13) << 30 &&
npc pixel x(13) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(13) << 30 &&
npc pixel y(13) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(14) << 30 &&
npc pixel x(14) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(14) << 30 &&
npc pixel y(14) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(15) << 30 &&
npc pixel x(15) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(15) << 30 &&
npc pixel y(15) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(16) << 30 &&
npc pixel x(16) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(16) << 30 &&
npc pixel y(16) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(17) << 30 &&
npc pixel x(17) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(17) << 30 &&
npc pixel y(17) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(18) << 30 &&
npc pixel x(18) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(18) << 30 &&
npc pixel y(18) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
if ((hero-x) -- npc pixel x(19) << 30 &&
npc pixel x(19) -- (hero-x) << 30 &&
(hero-y) -- npc pixel y(19) << 30 &&
npc pixel y(19) -- (hero-y) << 30 ) then (if (checktag (tag:INVINCIBLE) == off ) then (attacked))
)
|
See?
Code: |
if ((NPC Y (10) == Hero Y (me)) or (NPC X (10) == Hero X (me))) then, begin
Alter NPC (10,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (10,NPCstat:move type,NPCmovetype:wander)
end
|
This is the enemy "AI", and checks their movements. The basic gist is that if the hero's X or Y coordinates match a given NPC's, then the NPC behavior will change accordingly. If you're in the same x or y axis, this particular foe will try and chase you (as in it's spotted you), but if neither condition is met it'll simply wander around searching for the player. Different enemies can be configured to have different behaviour protocols depending on their x/y position, OR if you're clever they can also have a third or fourth set of behaviors that kick in when they're within a certain DISTANCE of the player. I'd like to cover this at a later date, but for now, this enemy behavior is adequate for a weak foe.
Also this segment will conditionally be repeated, albeit differently for each enemy possible.
Code: |
if ((NPC Y (11) == Hero Y (me)) or (NPC X (11) == Hero X (me))) then, begin
Alter NPC (11,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (11,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (12) == Hero Y (me)) or (NPC X (12) == Hero X (me))) then, begin
Alter NPC (12,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (12,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (13) == Hero Y (me)) or (NPC X (13) == Hero X (me))) then, begin
Alter NPC (13,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (13,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (14) == Hero Y (me)) or (NPC X (14) == Hero X (me))) then, begin
Alter NPC (14,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (14,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (15) == Hero Y (me)) or (NPC X (15) == Hero X (me))) then, begin
Alter NPC (15,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (15,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (16) == Hero Y (me)) or (NPC X (16) == Hero X (me))) then, begin
Alter NPC (16,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (16,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (17) == Hero Y (me)) or (NPC X (17) == Hero X (me))) then, begin
Alter NPC (17,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (17,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (18) == Hero Y (me)) or (NPC X (18) == Hero X (me))) then, begin
Alter NPC (18,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (18,NPCstat:move type,NPCmovetype:wander)
end
if ((NPC Y (19) == Hero Y (me)) or (NPC X (19) == Hero X (me))) then, begin
Alter NPC (19,NPCstat:move type,NPCmovetype:chaseyou)
end, else, begin
Alter NPC (19,NPCstat:move type,NPCmovetype:wander)
end
|
See? Thankfully it's almost over.
Code: |
wait (1)
)
exit script
end
|
And the end of the script. It does the ONE required wait for a loop, then keeps repeating UNTIL the "safe" tag is inevitably activated, then it terminates immediately.
Any hints for how to check "walls" and/or NPC passability would be wonderful. |
|
Back to top |
|
 |
Spoon Weaver

Joined: 18 Nov 2008 Posts: 421 Location: @home
|
Posted: Sun May 10, 2009 6:43 pm Post subject: |
|
|
I put out a tech demo, with the script in .txt format, that answers the wall issue for zelda types.
Basically, right after moving, it takes a look at the square you're currently on to see if it's a wall, and if it is, undoes the moving that just took place.
Of course with tile detection the way it is, I had to use strategically located pixels to check my "current" tile.
So, in order to put something like this in to your script. First you'd have to make room in between your moving codes.
Code: | if (key is pressed (key:up)) then (set hero direction (me,up), put hero (me,hero-x,hero-y -- hero-speed))
#Here would be your target place
if (key is pressed (key:down)) then (set hero direction (me,down), put hero (me,hero-x,hero-y + hero-speed))
|
now in this space you'll want to address the above moving if then statement. since it's up you'll want to check the tile your 2 top corners are currently in. these corners would be
hero-x,hero-y and hero-x+19,hero-y
(of course, since your sprite it's a block that takes up the whole square you might want to give it some breathing room like this
hero-x+2,hero-y+2 and hero-x+17,hero-y+2
but thats not too important)
Now you'll want to check the tile those pixels are in by doing something like this.
Code: | read pass block (hero-x/20,hero-y/20)
read pass block (hero-x/20,(hero-y+19)/20)
|
these will return the wall number of the tile your top pixels are in.
with this you can make the if statement.
Code: | if (read pass block (hero-x/20,hero-y/20)==15 ,or, read pass block (hero-x/20,(hero-y+19)/20)==15) |
Now that you have the if statement you need the then statement. The then statement should undone the movement that just happened to put your pixels in the undesired tile. In this case the movement was moving up, so you'll want to move down. This is what you're looking for.
Code: |
if ( read pass block (hero-x/20,hero-y/20)==15 ,or, read pass block (hero-x/20,(hero-y+19)/20)==15 ) then ( put hero (me,hero-x,hero-y + hero-speed) )
|
Now just slap that into the target space, and repeat for all moving functions. The diagonal moving you have might present a challenge, but since you plan to get rid of it I guess it's not a problem.
That should do the trick.
As far as NPC passability, that would be pretty hard. basically you'd want to do the same thing, but check the npc at pixel instead of the wall at tile. Of course they could still just walk into you if they aren't also equipped with scripted moving and passability checks. good luck with that. |
|
Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|