Example #1
0
def addMultiworldShop(rom):
    # Make a copy of the shop into GrandpaUlrira house
    shop_room = RoomEditor(rom, 0x2A1)
    re = RoomEditor(rom, 0x2A9)
    re.objects = [
        obj for obj in shop_room.objects
        if obj.x is not None and obj.type_id != 0xCE
    ] + re.getWarps()
    re.entities = [(1, 6, 0x77), (2, 6, 0x77)]
    re.animation_id = shop_room.animation_id
    re.floor_object = shop_room.floor_object
    re.store(rom)
    # Fix the tileset
    rom.banks[0x20][0x2EB3 + 0x2A9 - 0x100] = rom.banks[0x20][0x2EB3 + 0x2A1 -
                                                              0x100]

    # Load the shopkeeper sprites instead of Grandpa sprites
    entityData.SPRITE_DATA[0x77] = entityData.SPRITE_DATA[0x4D]

    labels = {}
    rom.patch(0x06,
              0x2860,
              "00" * 0x215,
              ASM(
                  """
shopItemsHandler:
; Render the shop items
    ld   h, $00
loop:
    ; First load links position to render the item at
    ldh  a, [$98] ; LinkX
    ldh  [$EE], a ; X
    ldh  a, [$99] ; LinkY
    sub  $0E
    ldh  [$EC], a ; Y
    ; Check if this is the item we have picked up
    ld   a, [$C509] ; picked up item in shop
    dec  a
    cp   h
    jr   z, .renderCarry

    ld   a, h
    swap a
    add  a, $20
    ldh  [$EE], a ; X
    ld   a, $30
    ldh  [$EC], a ; Y
.renderCarry:
    ld   a, h
    push hl
    ldh  [$F1], a ; variant
    cp   $03
    jr   nc, .singleSprite
    ld   de, ItemsDualSpriteData
    call $3BC0 ; render sprite pair
    jr   .renderDone
.singleSprite:
    ld   de, ItemsSingleSpriteData
    call $3C77 ; render sprite
.renderDone:

    pop  hl
.skipItem:
    inc  h
    ld   a, $07
    cp   h
    jr   nz, loop

;   check if we want to pickup or drop an item
    ldh  a, [$CC]
    and  $30 ; A or B button
    call nz, checkForPickup

;   check if we have an item
    ld   a, [$C509] ; carry item
    and  a
    ret  z

    ; Set that link has picked something up
    ld   a, $01
    ld   [$C15C], a
    call $0CAF ; reset spin attack...

    ; Check if we are trying to exit the shop and so drop our item.
    ldh  a, [$99]
    cp   $78
    ret  c
    xor  a
    ld   [$C509], a

    ret

checkForPickup:
    ldh  a, [$9E] ; direction
    cp   $02
    ret  nz
    ldh  a, [$99] ; LinkY
    cp   $48
    ret  nc

    ld   a, $13
    ldh  [$F2], a ; play SFX

    ld   a, [$C509] ; picked up shop item
    and  a
    jr   nz, .drop

    ldh  a, [$98] ; LinkX
    sub  $08
    swap a
    and  $07
    ld   [$C509], a ; picked up shop item
    ret
.drop:
    xor  a
    ld   [$C509], a
    ret

ItemsDualSpriteData:
    db   $60, $08, $60, $28 ; zol
    db   $68, $09 ; chicken (left)
ItemsSingleSpriteData: ; (first 3 entries are still dual sprites)
    db   $6A, $09 ; chicken (right)
    db   $14, $02, $14, $22 ; piece of power
;Real single sprite data starts here
    db   $00, $0F ; bomb
    db   $38, $0A ; rupees
    db   $20, $0C ; medicine
    db   $28, $0C ; heart

;------------------------------------trying to buy something starts here
talkHandler:
    ld   a, [$C509] ; carry item
    add  a, a
    ret  z ; check if we have something to buy
    sub  $02

    ld   hl, itemNames
    ld   e, a
    ld   d, b ; b=0
    add  hl, de
    ld   e, [hl]
    inc  hl
    ld   d, [hl]

    ld   hl, wCustomMessage
    call appendString
    dec  hl
    call padString
    ld   de, postMessage
    call appendString
    dec  hl
    ld   a, $fe
    ld   [hl], a
    ld   de, $FFEF
    add  hl, de
    ldh  a, [$EE]
    swap a
    and  $0F
    add  a, $30
    ld   [hl], a
    ld   a, $C9
    call $2385 ; open dialog
    call $3B12 ; increase entity state
    ret

appendString:
    ld   a, [de]
    inc  de
    and  a
    ret  z
    ldi  [hl], a
    jr   appendString

padString:
    ld   a, l
    and  $0F
    ret  z
    ld   a, $20
    ldi  [hl], a
    jr   padString

itemNames:
    dw itemZol
    dw itemChicken
    dw itemPieceOfPower
    dw itemBombs
    dw itemRupees
    dw itemMedicine
    dw itemHealth

postMessage:
    db  "For player X?       Yes  No  ", $00

itemZol:
    db  m"Slime storm|100 {RUPEES}", $00
itemChicken:
    db  m"Coccu party|50 {RUPEES}", $00
itemPieceOfPower:
    db  m"Piece of Power|50 {RUPEES}", $00
itemBombs:
    db  m"20 Bombs|50 {RUPEES}", $00
itemRupees:
    db  m"100 {RUPEES}|200 {RUPEES}", $00
itemMedicine:
    db  m"Medicine|100 {RUPEES}", $00
itemHealth:
    db  m"Health refill|10 {RUPEES}", $00

TalkResultHandler:
    ld  hl, ItemPriceTableBCD
    ld  a, [$C509]
    dec a
    add a, a
    ld  c, a ; b=0
    add hl, bc
    ldi a, [hl]
    ld  d, [hl]
    ld  e, a
    ld  a, [$DB5D]
    cp  d
    ret c
    jr  nz, .highEnough
    ld  a, [$DB5E]
    cp  e
    ret c
.highEnough:
    ; Got enough money, take it.
    ld  hl, ItemPriceTableDEC
    ld  a, [$C509]
    dec a
    ld  c, a ; b=0
    add hl, bc
    ld  a, [hl]
    ld  [$DB92], a

    ; No longer picked up item
    xor a
    ld  [$C509], a
    ret

ItemPriceTableBCD:
    dw $0100, $0050, $0050, $0050, $0200, $0100, $0010
ItemPriceTableDEC:
    db $64, $32, $32, $32, $C8, $64, $0A

""", 0x6860, labels),
              fill_nop=True)

    # Patch GrandpaUlrira to work as a multiworld shop
    rom.patch(0x06,
              0x1C0E,
              0x1C89,
              ASM(
                  """
    ld   a, $01
    ld   [$C50A], a ; this stops link from using items

;Draw shopkeeper
    ld   de, OwnerSpriteData
    call $3BC0 ; render sprite pair
    ldh  a, [$E7] ; frame counter
    swap a
    and  $01
    call $3B0C ; set sprite variant

    ldh  a, [$F0]
    and  a
    jr   nz, checkTalkingResult

    call $641A ; prevent link from moving into the sprite
    call $645D ; check if talking to NPC
    call c, ${TALKHANDLER:04x} ; talk handling

    ldh  a, [$EE] ; X
    cp   $18
    ret  nz

    ; Jump to other code which is placed on the old owl code. As we do not have enough space here.
    jp   ${SHOPITEMSHANDLER:04x}

checkTalkingResult:
    ld   a, [$C19F]
    and  a
    ret  nz ; still taking
    call $3B12 ; increase entity state
    ld   [hl], $00
    ld   a, [$C177] ; dialog selection
    and  a
    ret  nz
    jp ${TALKRESULTHANDLER:04x}

OwnerSpriteData:
    ;db   $60, $03, $62, $03, $62, $23, $60, $23 ; down
    db   $64, $03, $66, $03, $66, $23, $64, $23 ; up
    ;db   $68, $03, $6A, $03, $6C, $03, $6E, $03 ; left
    ;db   $6A, $23, $68, $23, $6E, $23, $6C, $23 ; right
    """.format(**labels), 0x5C0E),
              fill_nop=True)
Example #2
0
def addMultiworldShop(rom, this_player, player_count):
    # Make a copy of the shop into GrandpaUlrira house
    re = RoomEditor(rom, 0x2A9)
    re.objects = [
        ObjectHorizontal(1, 1, 0x00, 8),
        ObjectHorizontal(1, 2, 0x00, 8),
        ObjectHorizontal(1, 3, 0xCD, 8),
        Object(2, 0, 0xC7),
        Object(7, 0, 0xC7),
        Object(7, 7, 0xFD),
    ] + re.getWarps()
    re.entities = [(0, 6, 0xD4)]
    for n in range(player_count):
        if n != this_player:
            re.entities.append((n + 1, 6, 0xD4))
    re.animation_id = 0x04
    re.floor_object = 0x0D
    re.store(rom)
    # Fix the tileset
    rom.banks[0x20][0x2EB3 + 0x2A9 - 0x100] = rom.banks[0x20][0x2EB3 + 0x2A1 -
                                                              0x100]

    re = RoomEditor(rom, 0x0B1)
    re.getWarps()[0].target_x = 128
    re.store(rom)

    # Load the shopkeeper sprites
    entityData.SPRITE_DATA[0xD4] = entityData.SPRITE_DATA[0x4D]
    rom.patch(0x03, 0x01CF, "00",
              "98")  # Fix the hitbox of the ghost to be 16x16

    # Patch Ghost to work as a multiworld shop
    rom.patch(0x19,
              0x1E18,
              0x20B0,
              ASM(
                  """
    ld   a, $01
    ld   [$C50A], a ; this stops link from using items

    ldh  a, [$EE] ; X
    cp   $08
    ; Jump to other code which is placed on the old owl code. As we do not have enough space here.
    jp   z, shopItemsHandler

;Draw shopkeeper
    ld   de, OwnerSpriteData
    call $3BC0 ; render sprite pair
    ldh  a, [$E7] ; frame counter
    swap a
    and  $01
    call $3B0C ; set sprite variant

    ldh  a, [$F0]
    and  a
    jr   nz, checkTalkingResult

    call $7CA2 ; prevent link from moving into the sprite
    call $7CF0 ; check if talking to NPC
    call c, talkHandler ; talk handling
    ret

checkTalkingResult:
    ld   a, [$C19F]
    and  a
    ret  nz ; still taking
    call $3B12 ; increase entity state
    ld   [hl], $00
    ld   a, [$C177] ; dialog selection
    and  a
    ret  nz
    jp TalkResultHandler

OwnerSpriteData:
    ;db   $60, $03, $62, $03, $62, $23, $60, $23 ; down
    db   $64, $03, $66, $03, $66, $23, $64, $23 ; up
    ;db   $68, $03, $6A, $03, $6C, $03, $6E, $03 ; left
    ;db   $6A, $23, $68, $23, $6E, $23, $6C, $23 ; right

shopItemsHandler:
; Render the shop items
    ld   h, $00
loop:
    ; First load links position to render the item at
    ldh  a, [$98] ; LinkX
    ldh  [$EE], a ; X
    ldh  a, [$99] ; LinkY
    sub  $0E
    ldh  [$EC], a ; Y
    ; Check if this is the item we have picked up
    ld   a, [$C509] ; picked up item in shop
    dec  a
    cp   h
    jr   z, .renderCarry

    ld   a, h
    swap a
    add  a, $20
    ldh  [$EE], a ; X
    ld   a, $30
    ldh  [$EC], a ; Y
.renderCarry:
    ld   a, h
    push hl
    ldh  [$F1], a ; variant
    cp   $03
    jr   nc, .singleSprite
    ld   de, ItemsDualSpriteData
    call $3BC0 ; render sprite pair
    jr   .renderDone
.singleSprite:
    ld   de, ItemsSingleSpriteData
    call $3C77 ; render sprite
.renderDone:

    pop  hl
.skipItem:
    inc  h
    ld   a, $07
    cp   h
    jr   nz, loop

;   check if we want to pickup or drop an item
    ldh  a, [$CC]
    and  $30 ; A or B button
    call nz, checkForPickup

;   check if we have an item
    ld   a, [$C509] ; carry item
    and  a
    ret  z

    ; Set that link has picked something up
    ld   a, $01
    ld   [$C15C], a
    call $0CAF ; reset spin attack...

    ; Check if we are trying to exit the shop and so drop our item.
    ldh  a, [$99]
    cp   $78
    ret  c
    xor  a
    ld   [$C509], a

    ret

checkForPickup:
    ldh  a, [$9E] ; direction
    cp   $02
    ret  nz
    ldh  a, [$99] ; LinkY
    cp   $48
    ret  nc

    ld   a, $13
    ldh  [$F2], a ; play SFX

    ld   a, [$C509] ; picked up shop item
    and  a
    jr   nz, .drop

    ldh  a, [$98] ; LinkX
    sub  $08
    swap a
    and  $07
    ld   [$C509], a ; picked up shop item
    ret
.drop:
    xor  a
    ld   [$C509], a
    ret

ItemsDualSpriteData:
    db   $60, $08, $60, $28 ; zol
    db   $68, $09 ; chicken (left)
ItemsSingleSpriteData: ; (first 3 entries are still dual sprites)
    db   $6A, $09 ; chicken (right)
    db   $14, $02, $14, $22 ; piece of power
;Real single sprite data starts here
    db   $00, $0F ; bomb
    db   $38, $0A ; rupees
    db   $20, $0C ; medicine
    db   $28, $0C ; heart

;------------------------------------trying to buy something starts here
talkHandler:
    ld   a, [$C509] ; carry item
    add  a, a
    ret  z ; check if we have something to buy
    sub  $02

    ld   hl, itemNames
    ld   e, a
    ld   d, b ; b=0
    add  hl, de
    ld   e, [hl]
    inc  hl
    ld   d, [hl]

    ld   hl, wCustomMessage
    call appendString
    dec  hl
    call padString
    ld   de, postMessage
    call appendString
    dec  hl
    ld   a, $fe
    ld   [hl], a
    ld   de, $FFEF
    add  hl, de
    ldh  a, [$EE]
    swap a
    and  $0F
    add  a, $30
    ld   [hl], a
    ld   a, $C9
    call $2385 ; open dialog
    call $3B12 ; increase entity state
    ret

appendString:
    ld   a, [de]
    inc  de
    and  a
    ret  z
    ldi  [hl], a
    jr   appendString

padString:
    ld   a, l
    and  $0F
    ret  z
    ld   a, $20
    ldi  [hl], a
    jr   padString

itemNames:
    dw itemZol
    dw itemChicken
    dw itemPieceOfPower
    dw itemBombs
    dw itemRupees
    dw itemMedicine
    dw itemHealth

postMessage:
    db  "For player X?       Yes  No  ", $00

itemZol:
    db  m"Slime storm|100 {RUPEES}", $00
itemChicken:
    db  m"Coccu party|50 {RUPEES}", $00
itemPieceOfPower:
    db  m"Piece of Power|50 {RUPEES}", $00
itemBombs:
    db  m"10 Bombs|50 {RUPEES}", $00
itemRupees:
    db  m"100 {RUPEES}|200 {RUPEES}", $00
itemMedicine:
    db  m"Medicine|100 {RUPEES}", $00
itemHealth:
    db  m"Health refill|10 {RUPEES}", $00

TalkResultHandler:
    ld  hl, ItemPriceTableBCD
    ld  a, [$C509]
    dec a
    add a, a
    ld  c, a ; b=0
    add hl, bc
    ldi a, [hl]
    ld  d, [hl]
    ld  e, a
    ld  a, [$DB5D]
    cp  d
    ret c
    jr  nz, .highEnough
    ld  a, [$DB5E]
    cp  e
    ret c
.highEnough:
    ; Got enough money, take it.
    ld  hl, ItemPriceTableDEC
    ld  a, [$C509]
    dec a
    ld  c, a ; b=0
    add hl, bc
    ld  a, [hl]
    ld  [$DB92], a ; set substract buffer
    
    ; Set the item to send
    ld  hl, $DDFE
    ld  a, [$C509] ; currently picked up item 
    ldi [hl], a
    ldh a, [$EE]   ; X position of NPC
    ldi [hl], a
    ld  hl, $DDF7
    set 2, [hl]

    ; No longer picked up item
    xor a
    ld  [$C509], a
    ret

ItemPriceTableBCD:
    dw $0100, $0050, $0050, $0050, $0200, $0100, $0010
ItemPriceTableDEC:
    db $64, $32, $32, $32, $C8, $64, $0A
    """, 0x5E18),
              fill_nop=True)