NPC shop!!

CGdfc

New member
The npc shop is very useful for games that collect coins or money and where to spend it, in version 4.1.5 this noc does not work but it is possible that in version 5 they add it (I don't know).
Is there any possibility of adding this in the current version? :?:
something simple, for example:
You have 500 coins. you talk to the npc and depending on the money you have, the npc would answer a text or other text with a final reward (unlock weapon, restore life, get item, etc ...) by subtracting the money that was needed, in this example by subtracting 300 coins leaving the counter at 200 coins.
 

dale_coop

Moderator
Staff member
Yes, it doable... It requiress to modify the npc text script.

I did somethinig similar for the 4.0 version last year... (see at 2:08)

https://youtu.be/HDjjLxmoGlk?t=128
 

CGdfc

New member
dale_coop said:
Yes, it doable... It requiress to modify the npc text script.

I did somethinig similar for the 4.0 version last year... (see at 2:08)

https://youtu.be/HDjjLxmoGlk?t=128

amazing dale!
you have tutorial about this? For noobs like me. :lol:
 

dale_coop

Moderator
Staff member
Sorry, I don't have one...
but, if you give me some days, I could make one (compatible with NESmaker 4.1.5).
 

dale_coop

Moderator
Staff member
Here's a 4.1.5 compatible version of what I used for my NPC shop.

LIMITATIONS:
- You can only have 2 NPC per screen (because 2 texts are used per NPC).
- You can only use those prices: 10, 20, 30, 40, 50, 60,70, 80 and 90.

INSTALL:
1/ Add a new constant "NPCmoney" in the "Project Settings > User Constants", with a value "0".
2019-08-13-15-36-51-Project-Settings.png


2/ Modify the "HandleObjectCollisions.asm" script (the one assigned to the "Handle Object Collision" element in your "Project Settings > Script Settings"), around line 160, just after the line:
Code:
	STA textVar
Add this bloc of code:
Code:
	LDA #$00
	STA NPCmoney
	LDA Object_health,x
	BEQ ++
	STA temp
	LDA myMoney+1
	BEQ +
	CLC
	CMP temp
	BCC +	
	;; OK text
	LDA Object_ID,x
	STA textVar
	LDA temp
	STA NPCmoney
	JMP ++
	+
	;; NOK text
	LDA Object_ID,x
	CLC
	ADC #$01
	STA textVar
	++


2/ Modify the HandleTextBox.asm script (the one assigned to the "Handle Text Box" element in your "Project Settings > Script Settings"), around line 318, just after the line:
Code:
	TriggerScreen screenType ;; will flip the current screen type
Add this bloc of code:
Code:
	LDA NPCmoney
	BEQ +
	LDA myMoney+1
	BEQ +
	SubtractValue #$03, myMoney, NPCmoney, #$01
	;; Update the HUD:
	LDA #$01 
	STA hudElementTilesToLoad
	UpdateHud HUD_myMoney
	LDA #$00
	STA NPCmoney
	+


3/ Create your NPC object... as usual.
Now, we will use his "Health" for determine the price of the object he will sell:
"1" for 10
"2" for 20
"3" for 30
...
"9" for 90
If it's a normal NPC, that sells, nothing, keep the health to "0".

2019-08-13-15-39-34-NESMakers-Post-a-reply.png

(here, an npc that will sell at 30)


3/ Create your Texts, you will need to create two consecutive texts...
1 text that will be use to say "OK, you can have enough money"
1 text that will be use for "NO, you do not have enough money"
(it needs to be consecutive because if the player doesn't have enough money the next text is used).

2019-08-13-15-40-24-NES-MAKER-4-1-5-Version-0x159-Basic-Adv-MST.png

(here, in my case, the OK text gives an the item "Magic Type 3")
2019-08-13-15-41-13-NES-MAKER-4-1-5-Version-0x159-Basic-Adv-MST.png

(here, of course, the NOT OK text does nothing)

The most important, set it correctly in the Text Group (the TWO texts need to be consecutives: the "OK", then the "NOT OK").

2019-08-13-15-41-39.png



4/ Assign all that on your screen Infos and place your NPC (corresponding to the OK text) on the screen:
2019-08-13-15-43-25-Screen-Details.png

2019-08-13-15-42-49-NES-MAKER-4-1-5-Version-0x159-Basic-Adv-MST.png

(for my shop, I use 2 NPC, one selling the weapon 2 and one selling the weapon 3, different prices.)


VoilĂ ... it's a limited shop, but it can help for a small game...

If you have issues, don't hesitate to share screenshot of your modifications, settings, ... for me to check.

NPCShop.gif
 

CGdfc

New member
I have an error in the code to subtract money when buying. :(

Routines\Basic\System\HandleTextBox.asm(305): Branch out of range.

line 305 is: "BNE notEndItem"
 

CGdfc

New member
with the change of mugi no more error but ...
It doesn't work for me, it's strange ..

The npc sells the item for 10 gold.
If I speak with less gold, the message is that I don't have enough (it works well)
If I have 10 or more, the npc gives me the object and the message appears that gives it to you. (It works well)
The 10 gold that the npc required are not subtracted and I still have the same money. (sometimes it only takes 1 gold away)
 

CGdfc

New member
dale_coop said:
Check that you did the 2) part of my tutorial.
Else, share your scripts ;) we could check.

This is the 2 scripts, the first I have DC for the additions of other tutorials.

-HandleObjectCollisions_DC.asm

Code:
;; LOAD OBJECT 00
;; OUTER LOOP
;; CHECK IF ACTIVE, IF NOT, SKIP OBJECT
;; LOAD self-collision-box
;; START OBJECT COLLISION LOOP
	;; LDA ONE MORE THAN CURRENT OBJECT
	;; CHECK IF ACTIVE.  IF NOT, SKIP THIS other
	;; IF IT IS ACTIVE, then we have to play them against each other.
		;; IS self object hurt by monsters?  If so, and other object is a monster, respond.
		;; IS self object hurt by weapons?  If so, and other object is a weapon, respond.
		;; at this point, we can still gauge whether or not it's affected by player, like with powerups.
		;; ADD one to the object being checked, loop through other objects.
;; increase object, return to outer loop.  Repeat thorugh all self objects.

;; ObjectFlags:
;; 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
;  |   |   |   |   |   |   |   + -- PERSISTENT (especially good for player!)
;  |   |   |   |   |   |   +------- player type
;  |   |   |   |   |   + ---------- player weapon/projectile type
;  |   |   |   |   +--------------- monster type
;  |   |   |   +------------------- monster weapon/projectile type
;  |   |   +----------------------- pickup / power up
;  |   + -------------------------- target type 
;  +------------------------------- NPC type

;; player type checks monster, mosnter weapon, and pickup.
;; player weapon checks monster and target.
;; nothing else needs checking, as it would all be handled by those two steps.
;; so if it's a monster, do nothing.  if it's a monster projectile, do nothing. 
;; if it's a pickup, or a target, or it ignores all collisions, do nothing.
;; only if it's #%00000110, do something.



HandleObjectCollisions:

	LDA update_screen
	BEQ notChangingScreens
	rts
notChangingScreens:
	
	LDA npc_collision
	AND #%11111110
	STA npc_collision

	;LDA currentBank
	;STA prevBank
	;LDY #BANK_ANIMS
	;JSR bankswitchY
	
	LDX #$00
CollisionOuterLoop:
	TXA
	STA tempx
	LDA Object_status,x
	AND #%10000000
	BNE continueObjectCollisions_objectIsActive
	JMP doneWithThisObjectCollision
continueObjectCollisions_objectIsActive:
	LDA Object_status,x
	AND #%00000100 ;; is ot off screen
	BEQ continueObjectCollisions_objectIsOnScreen
	JMP doneWithThisObjectCollision
continueObjectCollisions_objectIsOnScreen
	LDA Object_status,x
	AND #%00000011
	BEQ continueObjectCollisions_objectIsNotHurtOrInvincible
	JMP doneWithThisObjectCollision
continueObjectCollisions_objectIsNotHurtOrInvincible
	;LDY Object_type,x
	;LDA ObjectFlags,y
	LDA Object_flags,x
	AND #%00000110
	BNE continueObjectCollisions_onlyPlayerTypesCheck
	JMP doneWithThisObjectCollision
continueObjectCollisions_onlyPlayerTypesCheck:
	;; this is either a player or player projectile type of object.
	;; all other types will be taken care of by iterating through these two types.
	;; first, check if it's player type.
	;LdA ObjectFlags,y
	LDA Object_flags,x
	AND #%00000010
	BNE isPlayerTypeForCollision
	JMP notPlayerType_forObjectCollision
isPlayerTypeForCollision:
	LDA player1_object
	STA colX
	;; is player type for object collision
	;; player's index is loaded into tempx
	JSR GetSelfCollisionBox
	;; now we have the collision box for self object
	;; next we loop through objects.
	
	LDX #$00
LoopThroughOtherObjects_player:
	CPX tempx
	BNE dontSkipThisOtherObject
	JMP skipThisOtherObject ;; other object IS the player, the one doing the counting..
dontSkipThisOtherObject:
	LDA Object_status,x
	AND #%00000100
	BEQ dontSkipThisOtherObject_becauseOnScreen
	JMP skipThisOtherObject ;; because it was off screen.
dontSkipThisOtherObject_becauseOnScreen:

	JSR GetOtherCollisionBox
	

	;; now we can do all the compares
	LDA selfNT_R
	CMP otherNT_L
	BCC + ;; no player object collision
	BNE ++ ;; is still possible to see collision.
	LDA selfRight
	CMP otherLeft
	BCC + ;; no player object collision
++ ;; it is still possible there is a collision here.
	LDA otherNT_R
	CMP selfNT_L
	BCC +
	BNE +++
	LDA otherRight
	CMP selfLeft
	BCC +
	
+++ ;; there was a collision here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	LDA otherBottom
	CMP selfTop
	BCC +
	LDA selfBottom
	CMP otherTop
	BCC +

	JMP DoPlayerObjectCollision

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
+ ;; there is no collision here horizontally.
	JMP noPlayerObjectCollision

DoPlayerObjectCollision:


	
	LDA Object_flags,x
	AND #%10000000 ;; is it an NPC
	BNE isAnNPC
	;; is not an NPC
	JMP isNotAnNPCcollision
isAnNPC:
	;;;; do npc stuff.
	LDA npc_collision
	ORA #%00000001
	STA npc_collision
	;;;; enables a button to be used to activate a textbox.
	LDA Object_ID,x
	STA textVar
	;;;;;;;;;;;;;;;;;; npc shop cambio principio
	LDA #$00
	STA NPCmoney
	LDA Object_health,x
	BEQ ++
	STA temp
	LDA myMoney+1
	BEQ +
	CLC
	CMP temp
	BCC +	
	;; OK text
	LDA Object_ID,x
	STA textVar
	LDA temp
	STA NPCmoney
	JMP ++
	+
	;; NOK text
	LDA Object_ID,x
	CLC
	ADC #$01
	STA textVar
	++
    ;;;;;;;;;;;;;;;;;; npc shop cambio final!!!
	;LDA gameHandler
	;ORA #%00100000
	;STA gameHandler

	JMP skipThisOtherObject
	
isNotAnNPCcollision:	
	LDA Object_flags,x
	;LDA ObjectFlags,y
	AND #%00011000 ;; is it a monster type?
	BNE otherIsAMonsterTypeCollision
	JMP otherIsNotAMonsterTypeCollision
otherIsAMonsterTypeCollision:
	LDA Object_status,x
	AND #HURT_STATUS_MASK ;; if the monster is hurt, it can't hurt us
	BEQ yesPlayerObjectCollision
	JMP noPlayerObjectCollision
yesPlayerObjectCollision:
	
	LDA Object_vulnerability,x
	AND #%00000010 ;; in this module, this is ignore player collision
	BEQ doPlayerHurt
	JMP	noPlayerObjectCollision
doPlayerHurt:
	;;;observe health
	TXA
	STA tempx ;; object is in tempx.
	LDX player1_object
	LDA Object_status,x
	AND #HURT_STATUS_MASK
	BEQ playerWasNotHurtDuringCollision
	JMP playerWasHurtDuringCollision
playerWasNotHurtDuringCollision:
	
	LDA Object_vulnerability,x
	AND #%01000000 ;; is he lethal invincible?
	BNE isLethalInvincible
	JMP notLethalInvincible
isLethalInvincible:
	LDX tempx
	LDA Object_x_hi,x
	STA temp
	LDA Object_y_hi,x
	STA temp1
	CreateObject temp, temp1, #OBJ_MONSTER_DEATH, #$00, currentNametable ;; create "splat"
	LDX tempx
	
	;;; ordinarily we'll want to destroy the instance.
	 DeactivateCurrentObject
	;; incrase score, you killed a monster
	PlaySound #SND_SPLAT
	TXA
	STA tempx
	AddValue #$08, myScore, #$01, #$00

	;;; we also need to set up the routine to update the HUD
	;; for this to work right, health must be a "blank-then-draw" type element.
	;STA hudElementTilesToLoad
	;	LDA #$00
	;	STA hudElementTilesMax
		; LDA DrawHudBytes
		; ora #HUD_myScore
		; STA DrawHudBytes
	UpdateHud HUD_myScore
	LDX tempx
	;;
	;; check for monter locks begin:
	CountObjects #%00001000, #$00
	LDA monsterCounter
	CLC
	BEQ +
	JMP ++
	+
		.include SCR_KILLED_LAST_MONSTER
	;; check for monter locks end.
	++	
	JMP skipThisOtherObject
	
notLethalInvincible:
	
	;;;;;;;;;;;;;;;;;
	;;;;;;;;; WHAT HAPPENS WHEN PLAYER IS HURT
	.include SCR_PLAYER_HURT_SCRIPT
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	
playerWasHurtDuringCollision:	
	LDX tempx
	JMP skipThisOtherObject
otherIsNotAMonsterTypeCollision:
	;LDA ObjectFlags,y
	LDA Object_flags,x
	AND #%00100000 ;; is it a 'collectable'?
	BEQ otherIsNotAcollectable
	;;;; IS A pickup / power up
	DeactivateCurrentObject ;; makes the other object go away
							;; since other object is loaded in X
							
;;=========== WHAT DO YOU WANT TO HAVE HAPPEN WHEN YOU COLLECT THIS ITEM?

	JSR HandlePickupPowerup

	
otherIsNotAcollectable:
noPlayerObjectCollision:	
skipThisOtherObject:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BEQ doneLoopThroughOtherObjects_player
	JMP LoopThroughOtherObjects_player
doneLoopThroughOtherObjects_player:
	;; end of player collision
	LDX tempx ;; restore x
	JMP doneWithThisObjectCollision
	
	
	
notPlayerType_forObjectCollision:
	;; is of player weapon type.
	JSR GetSelfCollisionBox
	;; now we have the collision box for self object
	;; next we loop through objects.
	LDX #$00
LoopThroughOtherObjects_weapon:

	CPX tempx
	BNE dontskipThisOtherObject_weapon
	JMP skipThisOtherObject_weapon
dontskipThisOtherObject_weapon
	JSR GetOtherCollisionBox
	;; now we can do all the compares
	LDA selfNT_R
	CMP otherNT_L
	BCC + ;; no player object collision
	BNE ++ ;; is still possible to see collision.
	LDA selfRight
	CMP otherLeft
	BCC + ;; no player object collision
++ ;; it is still possible there is a collision here.
	LDA otherNT_R
	CMP selfNT_L
	BCC +
	BNE +++
	LDA otherRight
	CMP selfLeft
	BCC +
	
+++ ;; there was a collision here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	LDA otherBottom
	CMP selfTop
	BCC +
	LDA selfBottom
	CMP otherTop
	BCC +

	JMP doWeaponObjectCollision

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
+ ;; there is no collision here horizontally.
	JMP noWeaponObjectCollision
doWeaponObjectCollision:
	;; go through the different types of collision possible.
	;; first, check monster OR monser projectile, as that should lead to hurt/death
	;LDY Object_type,x
	;LDA ObjectFlags,y
	LDA Object_flags,x
	AND #%00001000 ;; is it a monster type?
	;;; if you'd like the player weapon to ALSO destroy projectiles
	;;; use #%00011000 here
	BNE otherIsMonsterTypeCollision_weapon
	JMP otherIsNotAMonsterTypeCollision_weapon
otherIsMonsterTypeCollision_weapon:
;;;;;;;;;;;;;;;;;;;;;;;;
	TXA
	PHA
	.include SCR_HANDLE_HURT_MONSTER
	PLA
	TAX
	;;; if monster dies, count monsters
	;; right now, he always dies, so count the monsters.
	JSR countAllMonsters	
	

	
otherIsNotAMonsterTypeCollision_weapon:
	
noWeaponObjectCollision:	
skipThisOtherObject_weapon:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BEQ doneWithLoopingThroughWeaponObjects
	JMP LoopThroughOtherObjects_weapon
doneWithLoopingThroughWeaponObjects:
	
	
	
	;; end of player collision
	LDX tempx ;; restore x
	JMP doneWithThisObjectCollision
	
	
	
	
doneWithThisObjectCollision:
	LDX tempx
	INX
	CPX #TOTAL_MAX_OBJECTS
	BEQ doneWithAllObjects
	JMP CollisionOuterLoop
doneWithAllObjects:
	;;;;;;;;;;;;;;;;;;;;;;;;;
	;; for this module
	;; we will check for the melee position.
	;; but rather than waste space with an entire object
	;; we'll just test it against a single point
	.include SCR_CHECK_SPRITE_WEAPON

	RTS
	
	
	
	
	
	
GetSelfCollisionBox:	
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA selfLeft
	LDA Object_scroll,x
	ADC #$00
	STA selfNT_L
	
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA selfRight
	LDA Object_scroll,x
	ADC #$00
	STA selfNT_R
	
	LDA Object_vulnerability,x
	AND #%10000000
	BEQ noDuckingBit
	LDA Object_bottom
	SEC 
	SBC Object_top
	STA temp
	LDA Object_y_hi,x
	CLC
	ADC temp
	JMP gotSelfTop
noDuckingBit:
	LDA Object_y_hi,x
	CLC
	ADC Object_top,x
gotSelfTop:
	STA selfTop
	LDA Object_y_hi,x
	CLC
	ADC Object_bottom,x
	STA selfBottom
	LDA Object_x_hi,x
	CLC
	ADC Object_origin_x,x
	STA selfCenterX
	LDA Object_y_hi,x
	CLC
	ADC Object_origin_y,x
	STA selfCenterY
	

	RTS
	
GetOtherCollisionBox:
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA otherLeft
	LDA Object_scroll,x
	ADC #$00
	STA otherNT_L
	
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA otherRight
	LDA Object_scroll,x
	ADC #$00
	STA otherNT_R
	
	LDA Object_vulnerability,x
	AND #%10000000
	BEQ noDuckingBit_other
	LDA Object_bottom
	SEC 
	SBC Object_top
	STA temp
	LDA Object_y_hi,x
	CLC
	ADC temp
	JMP gotSelfTop_other
noDuckingBit_other:
	LDA Object_y_hi,x
	CLC
	ADC Object_top,x
gotSelfTop_other:	
	
	STA otherTop
	LDA Object_y_hi,x
	CLC
	ADC Object_bottom,x
	STA otherBottom
	LDA Object_x_hi,x
	CLC
	ADC Object_origin_x,x
	STA otherCenterX
	LDA Object_y_hi,x
	CLC
	ADC Object_origin_y,x
	STA otherCenterY
	

	RTS
	
	
	
	
	
	
	
DetermineRecoilDirection:

	;;;RECOIL
	;;First check for the abs x value
	LDA recoil_selfX
	SEC
	SBC recoil_otherX
	BCS absCheckDone
	EOR #$FF
	CLC
	ADC #$01
absCheckDone:
	STA temp
	LDA recoil_selfY
	SEC
	SBC recoil_otherY
	BCS absCheckDone2
	EOR #$FF
	CLC
	ADC #$01
absCheckDone2:
	CMP temp
	BCS vCol
	LDA recoil_selfX
	CMP recoil_otherX
	BCS recoilRight
	;; recoil left
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_h_speed_lo,x
	LDA #$00
	SEC
	SBC	#RECOIL_SPEED_HI
	STA Object_h_speed_hi,x
	LDA #$00
	STA Object_v_speed_hi,x
	STA Object_v_speed_lo,x
	LDA #%10000000
	STA temp1
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x
	CPX player1_object
	BNE dontChangeScrollDirectionL
	LDA #$00
	STA scrollDirection
dontChangeScrollDirectionL
	RTS
	
recoilRight:
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_h_speed_lo,x
	LDA	#RECOIL_SPEED_HI
	STA Object_h_speed_hi,x
	LDA #$00
	STA Object_v_speed_hi,x
	STA Object_v_speed_lo,x
	LDA #%11000000
	STA temp1
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x
	CPX player1_object
	BNE dontChangeScrollDirectionR
	LDA #$01
	STA scrollDirection
dontChangeScrollDirectionR:
	RTS
	
vCol:
	LDA recoil_selfY
	CMP recoil_otherY
	BCS recoilDown
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_v_speed_lo,x
	LDA #$00
	SEC
	SBC	#RECOIL_SPEED_HI
	STA Object_v_speed_hi,x
	LDA #%00100000
	STA temp1
	LDA #$00
	STA Object_h_speed_hi,x
	STA Object_h_speed_lo,x
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x

	RTS
	
recoilDown:
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_v_speed_lo,x
	LDA #RECOIL_SPEED_HI
	STA Object_v_speed_hi,x
	LDA #%00110000
	STA temp1
	LDA #$00
	STA Object_h_speed_hi,x
	STA Object_h_speed_lo,x
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x
	
	RTS

-HandleTextBox.asm

Code:
HandleTextBox:

;; textboxHandler
 ; 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
  ;7 = Textbox is active.
   ;    6 = Black box is being created, will then create text.
    ;        5 = Attribute update to black.
     ;           4 = text is being created.
      ;             = loop to 5 if more text.
       ;             3 = Black box is being created, with then restore NT
        ;                 2 = Attributes to main NT
         ;                     1 = restore NT
          ;                          0 = check for "more" text.
			


	LDA textboxHandler
	AND #%10000000
	BNE textboxIsActive
	RTS ;; textbox is inactive.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
textboxIsActive:
	LDA updateNametable
	BEQ notAlreadyWritingToNT
	RTS
notAlreadyWritingToNT
	;HideSprites
;; DO TEXT BOX STUFF.
	;; Which text box stuff to do is determined by the textboxHandler byte.
	;; if it is active, but all other bits are inactive, that means we have just activated and we need to turn
	;; this system on.
	LDA textboxHandler
	AND #%01111111
	BNE textboxSettingsAlreadyOn
	;;; get textbox settings
 ;; skip resetting the offset.
	LDA #$00
	STA updateNT_offset
	STA updateHUD_offset

	STA updateNT_H_offset
	STA updateNT_V_offset
	
	;;; zero out the things offsets and start creating the blackout box.
	LDA #%11000000
	STA textboxHandler ;; flow right into the next.
textboxSettingsAlreadyOn:
	
	LdA textboxHandler
	AND #%01000000
	BEQ notCreatingBlackBox
	;;;; CREATE THE BLACK BOX.
	;;;; The frist phase is to create the black box.
	;;;; no matter what color the text box will be, or which palette it will use
	;;;; it will always first create a box of "blanks" so it can be changed to whatever
	;;;; background attribute you'd like without noticing the attribute change.
	JSR CreateBlackBox
	;;; now the black box has been created.
	
notCreatingBlackBox:
	LDA textboxHandler
	AND #%00100000
	BEQ notSettingTextboxAttributes
	JSR isWritingTextboxAttributes
	RTS
	
	;;; set textbox attributes.
notSettingTextboxAttributes:
	LDA textboxHandler
	AND #%00010000
	BEQ notUpdatingTextboxText
	;; updating textbox text.
	JSR isWritingTextToTextbox

	RTS
notUpdatingTextboxText:
	LDA textboxHandler
	AND #%00001000
	BEQ notErasingTextboxText
	JSR CreateBlackBox
	RTS
notErasingTextboxText:
	LDA textboxHandler
	AND #%00000010
	BEQ notRestoringNametables2
;	JSR CheckForEndOfTextString
;	LDA gameHandler
;	AND #%00100000
;	BEQ notRestoringNametables2 ;; because we have finished.
	JSR RestoreNametableData
notRestoringNametables2
	RTS
	

	
	
	
	
	
	
	
	
	
	
	
	
	
getUpdateTileOffsetPosition:
	LDA xScroll
	LSR
	LSR
	LSR
	LSR
	CLC
	ADC	#BOX_1_ORIGIN_X
	CLC
	ADC updateNT_H_offset
	STA tileX
	
	LDA #BOX_1_ORIGIN_Y
	CLC
	ADC updateNT_V_offset
	STA tileY
	JSR coordinatesToMetaNametableValue
	;;; spits out updateNT_pos as low and updateNT_pos+1 as hi of address to write.
	;;; right now, this is in terms of a single nametable, starting at $20 as the high top left corner high byte.
	
	

	
;;;; FOR HITE BYTE:
;;;; Take columnTracker and divide by 2 so you get a value 0-16
;;;; Add that number to BOX_1_ORIGIN_X.
;;;; If the sum is less than 16, this should stay in the same nametable.
;;;; Otherwise, it should cross nametables.
	Ldy columnTracker
	LDA columnTracker
	AND #%00001111
	CLC
	ADC #BOX_1_ORIGIN_X ;; plus offset of what tile you're drawing.
	CLC
	ADC updateNT_H_offset
	AND #%00010000
	BNE +
	;;; same nametable
	LDA columnTracker
	AND #%00010000
	BNE +++
	;; this started in even table,
	;; so it should stay in even table
	JMP updateIsEvenTable
+++	
	;;; this started in odd table
	;;; so it should stay in odd table.
	JMP updateIsOddTable
+
	;;; different nametable
	LDA columnTracker
	AND #%00010000
	BNE +++
	;; this started in even table
	;; so it should now be odd table.
	JMP updateIsOddTable
+++
	;;; this started in odd table
	;;; so it should now be even table.
	JMP updateIsEvenTable

	

updateIsEvenTable:
	LDA updateNT_pos+1
	AND #%00000011 ;; 0,1,2 or 3
	clc
	adc #$20
	STA updateNT_pos+1
	JMP gotHiUpdatePos
	
updateIsOddTable:
	LDA updateNT_pos+1
	AND #%00000011 ;; 0,1,2 or 3
	CLC
	ADC #$24
	STA updateNT_pos+1
	JMP gotHiUpdatePos
gotHiUpdatePos:

	RTS
	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
checkNTforNewTile:
	LDA temp3; updateNT_pos
	AND #%00100000
	CMP #%00100000
	BEQ newTileHasCrossedThreshold
	;; new tile has not crossed threshold.
	;; same nametable

	LDA columnTracker
	AND #%00010000
	BNE updateIsOddTable
	JMP updateIsEvenTable
newTileHasCrossedThreshold:
	
	LDA columnTracker
	AND #%00010000
	BNE updateIsEvenTable
	JMP updateIsOddTable
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;RTS ;; redundant, unnecessary
	
	
isWritingTextToTextbox:
	;;; already in bank 17.
	;; we need to get positioning.

	LDA xScroll
	LSR
	LSR
	LSR
	STA temp
	LDA #BOX_1_ORIGIN_X
	ASL
	CLC
	ADC updateNT_H_offset
	CLC
	ADC temp
	STA temp3
	AND #%00011111
	STA tileX
	
	LDA #BOX_1_ORIGIN_Y
	ASL
	CLC
	ADC updateNT_V_offset
	STA tileY
	
	JSR coordinatesToNametableValue
	JSR checkNTforNewTile
	;; temp has position value.
	;LDA temp
	;STA updateNT_pos
	
	
	
	LDY textVar ;; what string
	LDA screenText,y
	TAY
	LDA stringsTableLo,y
	STA temp16
	LDA stringsTableHi,y
	STA temp16+1
	LDY textboxOffsetHold
	LDA (temp16),y
	
	;; this is where we determine if this is a special character or a normal letter/number to update.
	CMP #_END
	BNE +
;;;; END:
EndText:
	;; this is and _END value.
	;; it turns off writing to the textbox.
	
	LDA textboxHandler
	AND #%01111111
	STA textboxHandler
	;;;;; textbox handler stays on the same state
	;;;;; but deactivates.
	;;;;; it will start again if bbutton is pressed again
	;;;;; on state 00001000, which will begin the 'turn off' process.
	
	JMP doneTextUpdate
+
	CMP #$FE ;; is it a new line?
	BNE notANewLine_text
;;;; NEW LINE:


	LDA #$00
	STA	updateNT_H_offset
	INC updateNT_V_offset
	INC textboxOffsetHold
	
	JMP doneTextUpdate
notANewLine_text:

	CMP #_ENDTRIGGER
	BNE notEndTrigger
	;; is an end trigger
	INC textboxOffsetHold ;; get the very next value.
	LDY textboxOffsetHold
	LDA (temp16),y
	STA temp
	;;;; this now has the trigger to change.
	TriggerScreen temp
	JMP EndText
notEndTrigger:

	CMP #_ENDITEM 
	BEQ +
    JMP notEndItem
    + 
	
	;;; gives player an item.
	INC textboxOffsetHold ;; get the very next value.
	LDY textboxOffsetHold
	LDA (temp16),y
	TAY
	;;; this now has the bit to flip in BOSSES DEFEATED constant.
	LDA ValToBitTable_inverse,y
	ORA weaponsUnlocked
	STA weaponsUnlocked
	TriggerScreen screenType ;; will flip the current screen type
	
	;;;;;;principio npc shop 
	LDA NPCmoney
	BEQ +
	LDA myMoney+1
	BEQ +
	SubtractValue #$03, myMoney, NPCmoney, #$01
	;; Update the HUD:
	LDA #$01 
	STA hudElementTilesToLoad
	UpdateHud HUD_myMoney
	LDA #$00
	STA NPCmoney
	+
     ;;;;;;;;;final npc shop 
	
	
	
	
	;PlaySound #SFX_DO_TRIGGER
	JMP EndText
	
notEndItem:
	CMP #_MORE
	BNE notMoreText
	LDA #%10000000
	STA textboxHandler
	;; flip a more text flag.
	LDA #$01
	STA moreText
	INC textboxOffsetHold
	JMP EndText
notMoreText:
;;;; NORMAL VALUE
		;;;;; The look up was a normal letter, number, or other hud value.
	CLC 
	ADC #$C0
	STA updateHUD_fire_Tile	
	
	LDA updateNT_pos
	STA updateHUD_fire_Address_Lo
	LDA updateNT_pos+1
	STA updateHUD_fire_Address_Hi
	INC updateNT_H_offset
	INC textboxOffsetHold
doneTextUpdate:	

	RTS
	
	
	
	
	
	
	
	
	
	
	
CreateBlackBox	
	LDX #$00
	
;;; DRAW METATILE 1
	;; this macro SETS it to change on the next vblank update.
	;; starting at address hi-lo, with the tile in the third argument.
	;; if it sees #BLANK_TILE, it will create a meta tile of four blank tiles.
	;; if it sees any other value, it will create a metatile starting with 
	;; that index as the top left corner.
	
	;;;;;;; IF THE GAME DOES NOT SCROLL:
	;;;;;;; You can simply read the box value, do a little math, to get the offset
	;;;;;;; of the address for the top left corner, and skip the whole business of
	;;;;;;; finding the offset based on the scroll.
	
	;;;;;;; IF THE GAME DOES SCROLL:
	;;;;;;; YOu will need to get the offset value so it is at the box value position
	;;;;;;; compared to the CAMERA rather than zero.
	
	;;;;;;; Fortunately, we already have tables set up for columnTableLo and columnTableHi
	;;;;;;; which we use to handle the column for scrolling.  
	;;;;;;; we can use the columnTracker value to help determine the proper offset.
	
	;;;;;;; There can only be 32 columns.
	
	;;;;;;; And we will use updateNT_offset to continue this over frames, 
	;;;;;;; and to handle tile to offset.
	;;;;;;; this will be set to zero wherever HandleTextBox is activated.
	;;;;;;; It needs to, in some way, be boolean (like a key press, or using a boolean var).
	; LDA #$00
	; STA updateNT_H_offset
	; STA updateNT_V_offset
	;; THESE ALSO MUST BE SET IN THE BOOL VAR PLACE
	;; Whatever code is calling this must also set these to zero.

	
	
	
	LDA #$00
	STA tilesToWrite
	LDA #$04
	STA dummyVar2
DoLoopThing:
	JSR getUpdateTileOffsetPosition
	SetMetaTileToChange updateNT_pos+1, updateNT_pos, #BLANK_TILE

 	INC updateNT_H_offset
	LDA updateNT_H_offset
	CMP #BOX_1_WIDTH
	BEQ ++
	DEC dummyVar2
	LDA dummyVar2
	BEQ +
	JMP DoLoopThing
++ ;; width has been reached.
	;; now to test the height, and to move h pos back to the left.

	
	BEQ dontEndCreatingBlackBox
	JMP EndCreatingBlackBox
dontEndCreatingBlackBox:
	
	LDA #$00
	STA updateNT_offset
	STA updateHUD_offset
	STA updateNT_H_offset
	INC updateNT_V_offset


+	

	LDA #$01
	STA updateNametable  ;; turn on write.
	
	LDA updateNT_V_offset
	CMP #BOX_1_HEIGHT
	BCS turnOffCreatingTextBox
	
	LDA updateNT_H_offset
	CMP #BOX_1_WIDTH
	BEQ turnOffCreatingTextBox
	JMP EndCreatingBlackBox
turnOffCreatingTextBox:
	
	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;; Setup text specifics
	LDA #$00
	STA stringGroupOffset
	STA updateNT_H_offset
	STA updateNT_V_offset
	
	LDA xScroll
	LSR
	LSR
	LSR
	STA temp
	LDA #BOX_1_ORIGIN_X
	ASL
	CLC
	ADC temp
	CLC
	ADC #H_PAD_TEXTBOX
	STA tileX
	LDA #BOX_1_ORIGIN_Y
	ASL
	CLC
	ADC #V_PAD_TEXTBOX
	STA tileY
	
;	LDA #$01
;	STA writingText ;; turn on writing text.
	LDA moreText
	BEQ +
	LDA #$00
	STA moreText
	JMP ++
+
	LDA #$00
	STA textboxOffsetHold
++
	STA updateNT_H_offset
	STA updateNT_V_offset

	LDA textboxHandler
	AND #%00001000
	BEQ continueToUpdatingAttributes
	;;; is turning textbox OFF, so no writing to text.
	;;; will have to allot for changing attributes back with the
	;;; bit 00000100
	LDA #%10101000 ;; turn on attribute update
	STA textboxHandler
	LDA #$00
	STA updateNT_H_offset
	STA updateNT_V_offset
	STA updateNT_offset
	STA updateNT_compensation

;	LDA xScroll_hi

;	AND #%00000001
;	BEQ oddToEven2
;	LDA #$27
;	JMP gotNtDeets2
;oddToEven2:
;	LDA #$23
;gotNtDeets2:
;	STA updateNT_tableLeft
;	STA updateNT_details

	;LDA columnTracker
	LDA #BOX_1_ORIGIN_X
	LSR
	TAY
	LDA attrColumnTableHi,y
	STA updateNT_tableLeft
	STA updateNT_details

	RTS
continueToUpdatingAttributes:
	LDA #%10001000
	STA textboxHandler
	LDA #$00
	STA updateNT_H_offset
	STA updateNT_V_offset
	STA updateNT_offset
	STA updateNT_compensation
	
	
;	LDA xScroll_hi

;	AND #%00000001
;	BEQ oddToEven
;	LDA #$27
;	JMP gotNtDeets:
;oddToEven:
;	LDA #$23
;gotNtDeets:
;	STA updateNT_tableLeft
;	STA updateNT_details
	
	;LDA columnTracker
	LDA updateNT_H_offset
	CLC
	ADC updateNT_offset
	ASL
	CLC
	ADC	columnTracker
	AND #%00011111
	LSR 
	TAY
	LDA attrColumnTableHi,y
	STA updateNT_details
	
	LDA #%10100000
	STA textboxHandler
	;;;;;;;;;;;;;;;;;;;;;;;;;;;
	RTS
EndCreatingBlackBox:	
	LDA #$01
	RTS
	
	
	
	
RestoreNametableData:

	

	;;; FIRST we need to find the metaNametale value from the ROM.
	;;;;;;;;;;LESSER PRIORITY;;;;;;;;;;;;;;
	;;; THEN we need to check it againt collision type to / screen state to see if it should be 
	;;; change (for instance, a tile that changes at night / saved / changes if no monsters, if there are no monsters, etc)
	;;; THEN, we should probably always just restore the hud at the end.
	;;; the problem is, the data we need to fetch is in bank 16, then screen bank, 
	;;; while we are currently in bank 17 with this routine.
	;;; so restoration of nametable, or at least analysis of whate tiles to write, will have to happen OUTSIDE of this routine
	;;; We handle it in HandleBoxes, which will populate updateTile00-03, and respect paths.
	;;; And we handle POSITION to update here.

	LDX #$00
	LDA #$00
	STA tilesToWrite
	
	JSR getUpdateTileOffsetPosition
	
	

	LDA updateNT_pos
	STA temp
	LDA updateNT_pos+1
	STA temp1
	

	SetTileToChange temp1, temp, updateTile_00
	LDA temp
	CLC
	ADC #$01
	STA temp2
	LDA temp1
	ADC #$00
	STA temp3
	
	
	SetTileToChange temp3, temp2, updateTile_01
	
	LDA temp
	CLC
	ADC #$20
	STA temp2
	LDA temp1
	ADC #$00
	STA temp3
	
	
	SetTileToChange temp3, temp2, updateTile_02
	
	LDA temp
	CLC
	ADC #$21
	STA temp2
	LDA temp1
	ADC #$00
	STA temp3
	
	SetTileToChange temp3, temp2, updateTile_03
	INC tilesToWrite
	LDA #$01
	STA updateNametable
	
	INC updateNT_H_offset
	LDA updateNT_H_offset
	CMP #BOX_1_WIDTH
	BNE dontReturnToGame
	LDA #$00
	STA updateNT_H_offset
	INC updateNT_V_offset
	LDA updateNT_V_offset
	CMP #BOX_1_HEIGHT
	BNE dontReturnToGame
	LDA #$00
	STA textboxHandler
	STA updateHUD_offset
	LDA gameHandler
	AND #%11011111
	STA gameHandler
	;ShowSprites

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; check to see if there is behavior after a text box.

;;;; THIS WOULD WARP YOU TO A SCREEN AFTER TEXTBOX.
	; LDA #$01
	; STA activateWarpFlag
	; PlaySound #SND_ENTER
	; LDX player1_object
	; LDA Object_x_hi,x
	; STA mapPosX
	; LDA Object_y_hi,x
	; STA mapPosY
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
	
	
dontReturnToGame:

	RTS
	
	
	
	
	
	
	
	
isWritingTextboxAttributes:
	;; handle all four quadrants of the attribute.
	
	
	RTS
 

dale_coop

Moderator
Staff member
On your HUD, how many places do you use to display your myMoney element? 2 digits? 3 digits?
This script is meant for a 3 digits... but if you have only 2 digits, change the
Code:
	SubtractValue #$03, myMoney, NPCmoney, #$01
for a :
Code:
	SubtractValue #$02, myMoney, NPCmoney, #$01

(maybe it's the issue, here)
 

CGdfc

New member
dale_coop said:
On your HUD, how many places do you use to display your myMoney element? 2 digits? 3 digits?
This script is meant for a 3 digits... but if you have only 2 digits, change the
Code:
	SubtractValue #$03, myMoney, NPCmoney, #$01
for a :
Code:
	SubtractValue #$02, myMoney, NPCmoney, #$01

(maybe it's the issue, here)

yes 3 digit,i change to 2 or change the line you say but dont work.
npc subtract 1, yes, 1 gold/coin. but not 10
 

dale_coop

Moderator
Staff member
Oh sorry! I got it...
You are using the 4.1.5... but this module doesn't contain the 4.1.1 patch (why? Joe must have forgot to include the modified files in that version).
(This patch fix the SubtractValue macro that has a bug).

So you need to install the 4.1.1 engine patch :
Download the file: http://joshuafallon.com/wp-content/uploads/NESMaker4.1.1EnginePatch.zip (keep it zipped)
And to install it, in NESmaker, via the menu "Project > Run Project Script"... and select "NESMaker4.1.1EnginePatch.zip". Validate the messages.
 

dale_coop

Moderator
Staff member
So keep using the :
Code:
	SubtractValue #$03, myMoney, NPCmoney, #$01
because you have a 3 digits hud money element.
 
Top Bottom