Help activating NPCs

Ask and answer questions about making games and related topics. Unrelated topics go in that other forum.

Moderators: marionline, SDHawk

Post Reply
User avatar
pjbebi
Slime Knight
Posts: 112
Joined: Mon Jul 31, 2017 7:22 am
Location: Florida
Contact:

Help activating NPCs

Post by pjbebi »

I'm doing a zelda style and I'm running a colliding npc script during my keypress. "hit" is the script which runs when I collide with npc 16, but it only works when I walk onto their position. I know it's because I am only returning their position when I take a step.
How can I make a looping script that returns the position of my npc every few ticks? If so, how can I trigger it? Sorry my lingo isn't up to snuff. any help would be great.

Code: Select all

script,collide,begin
enem := npc reference (16)
enemx := npc x (enem)
enemy := npc y (enem)
hx := hero x (me)
hy := hero y (me)
while ((enemx == hx) && (enemy == hy)) do (hit)
end
Last edited by pjbebi on Mon Jul 31, 2017 7:31 am, edited 2 times in total.
KutKu
Red Slime
Posts: 31
Joined: Sun Jan 01, 2017 11:17 pm

Post by KutKu »

Hello pjbebi!

Welcome to Slime Salad! First I'd like to ask, how long have you been using OHR? And how recently have you begun to apply yourself learning Hspeak?

Anyway, with that said, I'd like to take a crack at your challenge and hopefully help you out!
pjbebi wrote:"hit" is the script which runs when I collide with npc 16, but it only works when I walk onto their position. I know it's because I am only returning their position when I take a step.

Code: Select all

variable (enemy HP)

while (enemy hit == TRUE) do(
	enemy HP -= 1
)
TL;DR: Try turning on the "Pushability" option in "Edit NPCs" on the Map menu

The "while" command will "do" whatever is inside it continuously until it's terminator is met, in this case "the enemy is still hit". In this example I wrote up, this would get the job done.

However, there are a lot of expectations that the player will have from having played other LOZ type games in the past that I don't believe you may have addressed before beginning this project.

A key thought process to programming is to "understand the problem" before doing anything.

So collision detection in games work with two things. A hitbox and a hurtbox. In the LOZ the first hitbox a player would see, would be from the wooden sword. You press A and Link pokes an enemy with his sword.

Player presses A
Link pokes with his sword
- Sword stays on screen for 1/3 a sec
--Any enemies in the area of the sword?
--- Yes!
---- Can this enemy be hurt by the sword?
----- Yes!
------ Enemy is hurt for X damage
------- Is the enemies HP more than 0?
-------- Yes!
--------- Dead enemy
-------- No!
--------- Enemy is knocked back and has invincibility frames
--- No!
-- Link is "stuck" for 1/4 a sec
-- Is Link at full health?
--- Yes!
---- LASER BEAM!
----- *I'm skipping writing out what happens with the laser beam*
- Player can move/ input other commands again

This may seem like a long list of work but remember that all it is at the end of the day is just a few simple commands.

OHR also has a built in option for NPC pushabilty. It's the ninth option in "Edit NPCs" this should help. However, whether or not it's going to behave the way you want is up to you.
User avatar
pjbebi
Slime Knight
Posts: 112
Joined: Mon Jul 31, 2017 7:22 am
Location: Florida
Contact:

Post by pjbebi »

I've been tinkering with ohrrpgce off and on since the late 90s, but only really when I have an idea for a game. So when plotscripting became a part of it, I became familiar with the basic commands... but to make something new and fun, I am learning to use most of everything else involved.

Anyway. thanks for the tip! this is what I'm doing and it's working. I set my enemy npc to pushable and activated with touch. when activated it runs this script


Code: Select all

plotscript,l1enemy,begin
enem := npc reference (16)
enemx := npc x (enem)
enemy := npc y (enem)
hx := hero x (me)
hy := hero y (me)
set npc obstructs (enem,false)
if (check tag(tag:invincible)==off) then (
while ((enemx == hx) && (enemy == hy)) do (hit))
end
this is my "hit" script

Code: Select all

script,hit,begin
set tag (20,on)
d := hero direction (me)
map cure (1,0,0)
play sound (1)
suspend player
set hero palette (0,5)
walk hero (0,d,-1)
wait for hero (0)
resume player
$3="Ouch!"
status
set tag (6,on)
expression
wait (20)
set hero palette (0,4)
set tag (20,off)
end
"expression" is a script that changes a slice based on tags. it looks like this.


Image
Last edited by pjbebi on Tue Aug 01, 2017 5:58 pm, edited 1 time in total.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Hello!

If you wanted to check something every few ticks, the easiest way to do so is to set a map atorun script which uses a while loop, like so:

Code: Select all

plotscript, hit checker, begin
  while (current map == 5) do (
    if (...) then (hit)   #Some condition
    wait(3)  #Check every 3 ticks
  )
end
Right, as KutKu said, walking on top of an NPC to damage it seems unusual. But lets start by considering that.
The x,y position (measured in tiles) of a hero NPC is whichever tile its top-left corner is on. Unfortunately this means that when you walk a tile to the left, your X coordinate immediately decreases by 1, but if you walk to the right, you have to go 20 pixels before it changes. So comparing x and y is not a good way to do hit detection unless the two things you're comparing aren't moving.

Instead, the most flexible way to do collision checking is to use slice commands. (See Slices Tutorial for an introduction to slices).

In particular, you can check whether the player overlaps an NPC (even slightly) using slice collide like so:

Code: Select all

hero sl := get hero slice(me)
enemy sl := get npc slice(16)  # First copy of NPC 16. You can pass an NPC reference instead
if (slice collide(hero sl, enemy sl)) then (hit)
Of course, the NPC has to be set to Step-On or obstruction needs to be suspended, otherwise you can't walk onto the NPC.

Now if you wanted to use a different hitbox, such as a sword in front of the hero, you would just use a different slice. It could even be an invisible container slice; it doesn't have to be a sprite.
Last edited by TMC on Tue Aug 01, 2017 5:59 pm, edited 1 time in total.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Heh, one minute before me.
That looks really nice!
Funny, yet another new-actually-old user. Did you ever previously post on OHR forums?

OK, now that I see you're trying to hurt the hero rather than the NPC it makes more sense. Everything I wrote before still holds.

Funny, I never knew that you could use walkhero with a negative distance to walk backwards! I'm pretty surprised to be learning something new like that after all these years.

Beware, telling a hero to walk while they're already in the middle of a step will cause tile misalignment. Which will happen if you're just checking every few ticks, and the NPC and hero could both meet mid-step.
It's not so trivial to fix that. If could be done by stopping the hero as soon as they're realigned, something like:

Code: Select all

walk hero (0,d,-1)
wait(1)  # Don't stop immediately
while (hero is walking(me)) do (
  if ((hero pixel x(me), mod, 20) == 0 && (hero pixel y(me), mod, 20) == 0) then (
    walk hero(me, d, 0)  # Walking 0 tiles stops the hero
  )
  wait
)
But there's another problem: what if the hero is standing still and the enemy come up behind them? Then the hero ought to turn to face the enemy before walking backwards. And what if they come from the side? Maybe that can be ignored.

I wrote some scripts to help Foxley with this exact problem of hero and NPC knockback, in the Zelda-like he's working on, here. His game also uses tile-based movement, with heroes knocked back a tile when they touch an enemy. It turned out that getting knockback to work correctly without anyone getting stuck in a wall was very tricky. So you may want to adapt those scripts rather than finding out by yourself about nasty edge cases.
The big complication in that game is that the hero/npc can get knocked either forward, backwards or diagonally depending on where the enemy is, and they need to end up realigned to the tile grid after the knockback, without going into a wall. So the problem will be more manageable if you don't allow such complicated knockback.
(Note the "calculate player hurtdir" script at the bottom of that post; it calculates which direction the enemy is coming at the hero).
Also, I think Foxley may have said something about releasing his scripts?

(Edit: corrected some false information about Foxley's game being pixel-based).
Last edited by TMC on Tue Aug 01, 2017 6:29 pm, edited 3 times in total.
User avatar
pjbebi
Slime Knight
Posts: 112
Joined: Mon Jul 31, 2017 7:22 am
Location: Florida
Contact:

Post by pjbebi »

for some reason, I couldn't get my loop to work until I used your TMC's script. thanks so much for the advice!

now I'm trying to use multiple copies of an npc, but my script is only referencing one copy. is there a way I can reference multliple copies of an npc to one variable?
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Post by Foxley »

I'll try to get my .HSS cleaned up and commented soon. Should've done it a while back anyhow.

Basically, making a Zelda-like in OHRRPGCE is way harder than it seems, as I found out the hard way. Hopefully the scripts will help make things less painful.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Oh, nice.

If there are multiple enemies, you will generally want to check each NPC separately, like so:

Code: Select all

plotscript, hit checker, begin
  while (current map == 5) do (
    variable(copy, npc, hero sl, enemy sl)
    hero sl := get hero slice(me)
    for(copy, 0, npc copy count(16) -- 1) do (
       npc := npc reference(16, copy)
       enemy sl := get npc slice(npc)
       if (slice collide(hero sl, enemy sl)) then (hit)
    )
    wait(2)
  )
end
(Although it is possible to use the "find colliding slice" command to avoid looping over NPCs)
Last edited by TMC on Wed Aug 02, 2017 5:06 am, edited 3 times in total.
User avatar
pjbebi
Slime Knight
Posts: 112
Joined: Mon Jul 31, 2017 7:22 am
Location: Florida
Contact:

Post by pjbebi »

I'm still getting misaligned when hit by enemies, but I'm working on it. this is my code for colliding with enemies (if you have an interest)

Code: Select all

script,checkmap,begin
	while (current map == 0, or, 
	current map == 3, or, 
	current map ==4) do 
		(
		hx := hero x (me)
		hy := hero y (me)
		hpx := hero pixel x (me)
		hpy := hero pixel y (me)
		coin := npc reference (20)
		heart := npc reference (21)
		rock := npc reference (22)
		hs := npc at spot (hx,hy)
		black := 3
	collide
	wait (1)
	)
end

script,collide,begin
variable (npcid, numrefs, i)

for (npcid, 0, 35, 1) do (
numrefs := npc copy count(npcid)
	
	for (i, 0, numrefs --1, 1) do (
	enem := npc reference (npcid, i)
	
	if ((npc pixel x(enem) >= hpx -- 19) &&
		&#40;npc pixel x&#40;enem&#41; <= &#40;hpx +19&#41;&#41; &&
		&#40;npc pixel y&#40;enem&#41; >= hpy -- 19&#41; &&
		&#40;npc pixel y&#40;enem&#41; <= &#40;hpy +19&#41;&#41;&#41;
	then &#40;
	if &#40;hs == coin, or, hs == heart, or, hs == rock&#41; then &#40;&#41;
	else if &#40;read zone &#40;1,hx,hy&#41;&#41; then &#40;hit&#41;
	else if &#40;read zone &#40;4,hx,hy&#41;&#41; then &#40;hit2&#41;
	&#41;
	&#41;
&#41;
end
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

The cause of your misalignment is whatever you're doing in your hit/hit2 scripts, not the scripts you posted. The scripts you posted look correct...

...except that the line "if (hs == coin, or, hs == heart, or, hs == rock) then ()" is wrong. hs may or may not be the NPC you're colliding with. Sometimes when you collide with the coin, heart or rock, hs will be zero so you'll run the hit/hit2 script but it appear that you don't want to do that. Also, this isn't going to work if you have more than one coin/heart/rock anyway.
Instead, write something like
"if (npcid == 20, or, npcid == 21, or, npcid == 22) then ()"
Post Reply