├── .circleci └── config.yml ├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── pullRequest.yml ├── .gitignore ├── .golangci.yml ├── .travis.yml ├── .vscode └── extensions.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── build.sh ├── d2app ├── app.go ├── app_test.go ├── capture_state.go ├── console_commands.go └── initialization.go ├── d2common ├── d2cache │ ├── cache.go │ └── doc.go ├── d2calculation │ ├── calcstring.go │ ├── calculation.go │ ├── d2lexer │ │ ├── lexer.go │ │ └── lexer_test.go │ └── d2parser │ │ ├── d2parser.go │ │ ├── operations.go │ │ ├── parser.go │ │ └── parser_test.go ├── d2data │ ├── animation_data.go │ ├── d2compression │ │ ├── huffman.go │ │ └── wav.go │ ├── d2video │ │ ├── binkdecoder.go │ │ └── doc.go │ └── doc.go ├── d2datautils │ ├── bitmuncher.go │ ├── bitstream.go │ ├── bitstream_test.go │ ├── doc.go │ ├── stream_reader.go │ ├── stream_reader_test.go │ ├── stream_writer.go │ └── stream_writer_test.go ├── d2enum │ ├── animation_frame.go │ ├── animation_frame_direction.go │ ├── animation_frame_event.go │ ├── composite_type.go │ ├── composite_type_string.go │ ├── difficulty.go │ ├── doc.go │ ├── draw_effect.go │ ├── encoding_type.go │ ├── equipped_slot.go │ ├── filter.go │ ├── game_event.go │ ├── hero.go │ ├── hero_stance.go │ ├── hero_string.go │ ├── hero_string2enum.go │ ├── input_button.go │ ├── input_key.go │ ├── input_priority.go │ ├── inventory_item_type.go │ ├── item_affix_type.go │ ├── item_armor_class.go │ ├── item_event_functions.go │ ├── item_events.go │ ├── item_quality.go │ ├── layer_stream_type.go │ ├── level_generation_types.go │ ├── level_teleport_flags.go │ ├── monster_alignment_type.go │ ├── monster_animation_mode.go │ ├── monster_animation_mode_string.go │ ├── monster_combat_type.go │ ├── monumod_const_index.go │ ├── npc_action_type.go │ ├── numeric_labels.go │ ├── object_animation_mode.go │ ├── object_animation_mode_string.go │ ├── object_animation_mode_string2enum.go │ ├── object_type.go │ ├── operator_type.go │ ├── pet_icon_type.go │ ├── player_animation_mode.go │ ├── player_animation_mode_string.go │ ├── quests.go │ ├── readme.md │ ├── region_id.go │ ├── region_layer.go │ ├── render_type.go │ ├── skill_class.go │ ├── terminal_category.go │ ├── tile.go │ ├── weapon_class.go │ ├── weapon_class_string.go │ └── weapon_class_string2enum.go ├── d2fileformats │ ├── d2animdata │ │ ├── animdata.go │ │ ├── animdata_test.go │ │ ├── block.go │ │ ├── doc.go │ │ ├── events.go │ │ ├── hash.go │ │ ├── record.go │ │ └── testdata │ │ │ ├── AnimData.d2 │ │ │ └── BadData.d2 │ ├── d2cof │ │ ├── cof.go │ │ ├── cof_dir_lookup.go │ │ ├── cof_layer.go │ │ └── doc.go │ ├── d2dat │ │ ├── dat.go │ │ ├── dat_color.go │ │ ├── dat_palette.go │ │ └── doc.go │ ├── d2dc6 │ │ ├── dc6.go │ │ ├── dc6.ksy │ │ ├── dc6_frame.go │ │ ├── dc6_frame_header.go │ │ ├── dc6_header.go │ │ └── doc.go │ ├── d2dcc │ │ ├── dcc.go │ │ ├── dcc_cell.go │ │ ├── dcc_dir_lookup.go │ │ ├── dcc_direction.go │ │ ├── dcc_direction_frame.go │ │ ├── dcc_pixel_buffer_entry.go │ │ └── doc.go │ ├── d2ds1 │ │ ├── doc.go │ │ ├── ds1.go │ │ ├── floor_shadow_record.go │ │ ├── object.go │ │ ├── substitution_group.go │ │ ├── substitution_record.go │ │ ├── tile_record.go │ │ └── wall_record.go │ ├── d2dt1 │ │ ├── block.go │ │ ├── doc.go │ │ ├── dt1.go │ │ ├── gfx_decode.go │ │ ├── material.go │ │ ├── subtile.go │ │ ├── subtile_test.go │ │ └── tile.go │ ├── d2mpq │ │ ├── crypto.go │ │ ├── doc.go │ │ ├── mpq.go │ │ ├── mpq_block.go │ │ ├── mpq_data_stream.go │ │ ├── mpq_file_record.go │ │ ├── mpq_hash.go │ │ ├── mpq_header.go │ │ └── mpq_stream.go │ ├── d2pl2 │ │ ├── doc.go │ │ ├── pl2.go │ │ ├── pl2_color.go │ │ ├── pl2_color_24bits.go │ │ ├── pl2_palette.go │ │ └── pl2_palette_transform.go │ ├── d2tbl │ │ ├── doc.go │ │ └── text_dictionary.go │ └── d2txt │ │ ├── data_dictionary.go │ │ └── doc.go ├── d2geom │ ├── doc.go │ ├── point.go │ ├── rectangle.go │ └── size.go ├── d2interface │ ├── animation.go │ ├── archive.go │ ├── audio_provider.go │ ├── cache.go │ ├── data_stream.go │ ├── doc.go │ ├── input_events.go │ ├── input_handlers.go │ ├── input_manager.go │ ├── input_service.go │ ├── map_entity.go │ ├── navigate.go │ ├── palette.go │ ├── renderer.go │ ├── sound_effect.go │ ├── surface.go │ └── terminal.go ├── d2loader │ ├── asset │ │ ├── asset.go │ │ ├── doc.go │ │ ├── source.go │ │ └── types │ │ │ ├── asset_types.go │ │ │ ├── doc.go │ │ │ └── source_types.go │ ├── doc.go │ ├── filesystem │ │ ├── asset.go │ │ ├── doc.go │ │ ├── loader_provider.go │ │ └── source.go │ ├── loader.go │ ├── loader_test.go │ ├── mpq │ │ ├── asset.go │ │ ├── doc.go │ │ └── source.go │ └── testdata │ │ ├── A │ │ ├── common.txt │ │ └── exclusive_a.txt │ │ ├── B │ │ ├── common.txt │ │ └── exclusive_b.txt │ │ ├── C │ │ ├── common.txt │ │ └── exclusive_c.txt │ │ └── D.mpq ├── d2math │ ├── d2vector │ │ ├── doc.go │ │ ├── position.go │ │ ├── position_test.go │ │ ├── vector.go │ │ └── vector_test.go │ ├── doc.go │ ├── math.go │ ├── math_test.go │ ├── ranged_number.go │ └── ranged_number_test.go ├── d2path │ ├── doc.go │ └── path.go ├── d2resource │ ├── doc.go │ ├── languages_map.go │ ├── music_defs.go │ └── resource_paths.go ├── d2util │ ├── assets │ │ ├── assets.go │ │ ├── noto_sans_mono_8x16.bmp │ │ └── zipbmp.go │ ├── debug_print.go │ ├── doc.go │ ├── logger.go │ ├── logger_test.go │ ├── palette.go │ ├── rgba_color.go │ ├── stringutils.go │ └── timeutils.go └── doc.go ├── d2core ├── d2asset │ ├── animation.go │ ├── asset_manager.go │ ├── composite.go │ ├── d2asset.go │ ├── dc6_animation.go │ ├── dcc_animation.go │ ├── doc.go │ └── font.go ├── d2audio │ ├── create.go │ ├── doc.go │ ├── ebiten │ │ ├── ebiten_audio_provider.go │ │ └── ebiten_sound_effect.go │ ├── sdl2 │ │ ├── sdl2_audio_provider.go │ │ └── sdl2_sound_effect.go │ ├── sound_engine.go │ └── sound_environment.go ├── d2config │ ├── d2config.go │ ├── default_directories.go │ ├── defaults.go │ └── doc.go ├── d2gui │ ├── box.go │ ├── button.go │ ├── common.go │ ├── doc.go │ ├── gui_manager.go │ ├── label.go │ ├── label_button.go │ ├── layout.go │ ├── layout_entry.go │ ├── layout_scrollbar.go │ ├── spacer.go │ ├── sprite.go │ ├── style.go │ └── widget.go ├── d2hero │ ├── doc.go │ ├── hero_skill.go │ ├── hero_skill_util.go │ ├── hero_state.go │ ├── hero_state_factory.go │ └── hero_stats_state.go ├── d2input │ ├── d2input.go │ ├── doc.go │ ├── ebiten │ │ └── ebiten_input.go │ ├── handler_event.go │ ├── input_manager.go │ ├── key_event.go │ ├── keychars_event.go │ ├── mouse_event.go │ ├── mousemove_event.go │ └── sdl2 │ │ └── sdl2_input_service.go ├── d2inventory │ ├── character_equipment.go │ ├── doc.go │ ├── hero_objects.go │ ├── inventory_item.go │ ├── inventory_item_armor.go │ ├── inventory_item_factory.go │ ├── inventory_item_misc.go │ └── inventory_item_weapon.go ├── d2item │ ├── context.go │ ├── diablo2item │ │ ├── doc.go │ │ ├── item.go │ │ ├── item_factory.go │ │ ├── item_property.go │ │ └── item_property_test.go │ ├── doc.go │ ├── equipper.go │ └── item.go ├── d2map │ ├── d2mapengine │ │ ├── doc.go │ │ ├── engine.go │ │ ├── map_tile.go │ │ └── pathfind.go │ ├── d2mapentity │ │ ├── animated_entity.go │ │ ├── cast_overlay.go │ │ ├── doc.go │ │ ├── factory.go │ │ ├── item.go │ │ ├── map_entity.go │ │ ├── map_entity_test.go │ │ ├── missile.go │ │ ├── npc.go │ │ ├── object.go │ │ ├── object_init.go │ │ └── player.go │ ├── d2mapgen │ │ ├── act1_overworld.go │ │ ├── d2wilderness │ │ │ └── wilderness_tile_types.go │ │ ├── doc.go │ │ └── map_generator.go │ ├── d2maprenderer │ │ ├── camera.go │ │ ├── doc.go │ │ ├── renderer.go │ │ ├── tile_cache.go │ │ └── viewport.go │ └── d2mapstamp │ │ ├── doc.go │ │ ├── factory.go │ │ └── stamp.go ├── d2records │ ├── armor_type_loader.go │ ├── armor_type_record.go │ ├── automagic_loader.go │ ├── automagic_record.go │ ├── automap_loader.go │ ├── automap_record.go │ ├── belts_loader.go │ ├── belts_record.go │ ├── body_locations_loader.go │ ├── body_locations_record.go │ ├── books_loader.go │ ├── books_record.go │ ├── calculations_loader.go │ ├── calculations_record.go │ ├── charstats_loader.go │ ├── charstats_record.go │ ├── color_loader.go │ ├── color_record.go │ ├── component_codes_loader.go │ ├── component_codes_record.go │ ├── composite_type_loader.go │ ├── composite_type_record.go │ ├── constants.go │ ├── cube_modifier_loader.go │ ├── cube_modifier_record.go │ ├── cube_type_loader.go │ ├── cube_type_record.go │ ├── cubemain_loader.go │ ├── cubemain_record.go │ ├── difficultylevels_loader.go │ ├── difficultylevels_record.go │ ├── doc.go │ ├── elemtype_loader.go │ ├── elemtype_record.go │ ├── events_loader.go │ ├── events_record.go │ ├── experience_loader.go │ ├── experience_record.go │ ├── gamble_loader.go │ ├── gamble_record.go │ ├── gems_loader.go │ ├── gems_record.go │ ├── hireling_description_loader.go │ ├── hireling_description_record.go │ ├── hireling_loader.go │ ├── hireling_record.go │ ├── hit_class_loader.go │ ├── hit_class_record.go │ ├── inventory_loader.go │ ├── inventory_record.go │ ├── item_affix_loader.go │ ├── item_affix_record.go │ ├── item_armor_loader.go │ ├── item_common_loader.go │ ├── item_common_record.go │ ├── item_low_quality_loader.go │ ├── item_low_quality_record.go │ ├── item_misc_loader.go │ ├── item_quality_loader.go │ ├── item_quality_record.go │ ├── item_ratio_loader.go │ ├── item_ratio_record.go │ ├── item_types_loader.go │ ├── item_types_record.go │ ├── item_weapons_loader.go │ ├── itemstatcost_loader.go │ ├── itemstatcost_record.go │ ├── level_details_loader.go │ ├── level_details_record.go │ ├── level_maze_loader.go │ ├── level_maze_record.go │ ├── level_presets_loader.go │ ├── level_presets_record.go │ ├── level_substitutions_loader.go │ ├── level_substitutions_record.go │ ├── level_types_loader.go │ ├── level_types_record.go │ ├── level_warp_loader.go │ ├── level_warp_record.go │ ├── missiles_loader.go │ ├── missiles_record.go │ ├── monster_ai_loader.go │ ├── monster_ai_record.go │ ├── monster_equipment_loader.go │ ├── monster_equipment_record.go │ ├── monster_levels_loader.go │ ├── monster_levels_record.go │ ├── monster_mode_loader.go │ ├── monster_mode_record.go │ ├── monster_placement_loader.go │ ├── monster_placement_record.go │ ├── monster_preset_loader.go │ ├── monster_preset_record.go │ ├── monster_property_loader.go │ ├── monster_property_record.go │ ├── monster_sequence_loader.go │ ├── monster_sequence_record.go │ ├── monster_sound_loader.go │ ├── monster_sound_record.go │ ├── monster_stats2_loader.go │ ├── monster_stats2_record.go │ ├── monster_stats_loader.go │ ├── monster_stats_record.go │ ├── monster_super_unique_loader.go │ ├── monster_super_unique_record.go │ ├── monster_type_loader.go │ ├── monster_type_record.go │ ├── monster_unique_affix_loader.go │ ├── monster_unique_affix_record.go │ ├── monster_unique_modifiers_loader.go │ ├── monster_unique_modifiers_record.go │ ├── npc_loader.go │ ├── npc_record.go │ ├── object_details_loader.go │ ├── object_details_record.go │ ├── object_groups_loader.go │ ├── object_groups_record.go │ ├── object_lookup_record.go │ ├── object_lookup_record_data.go │ ├── object_lookup_record_test.go │ ├── object_mode_loader.go │ ├── object_mode_record.go │ ├── object_types_loader.go │ ├── object_types_record.go │ ├── overlays_loader.go │ ├── overlays_record.go │ ├── pet_type_loader.go │ ├── pet_type_record.go │ ├── player_class_loader.go │ ├── player_class_record.go │ ├── player_mode_loader.go │ ├── player_mode_record.go │ ├── player_type_loader.go │ ├── player_type_record.go │ ├── property_descriptor.go │ ├── property_loader.go │ ├── property_record.go │ ├── rare_affix.go │ ├── rare_affix_loader.go │ ├── rare_prefix_loader.go │ ├── rare_prefix_record.go │ ├── rare_suffix_loader.go │ ├── rare_suffix_record.go │ ├── record_loader.go │ ├── record_manager.go │ ├── runeword_loader.go │ ├── runeword_record.go │ ├── set_item_loader.go │ ├── set_item_record.go │ ├── set_loader.go │ ├── set_record.go │ ├── shrine_loader.go │ ├── shrine_record.go │ ├── skill_description_loader.go │ ├── skill_description_record.go │ ├── skill_details_loader.go │ ├── skill_details_record.go │ ├── sound_details_loader.go │ ├── sound_details_record.go │ ├── sound_environment_loader.go │ ├── sound_environment_record.go │ ├── states_loader.go │ ├── states_record.go │ ├── storepage_loader.go │ ├── storepage_record.go │ ├── treasure_class_loader.go │ ├── treasure_class_record.go │ ├── unique_appellation_loader.go │ ├── unique_appellations_record.go │ ├── unique_items_loader.go │ ├── unique_items_record.go │ ├── weapon_class_loader.go │ └── weapon_class_record.go ├── d2render │ ├── create.go │ ├── d2sdl2renderer │ │ ├── sdl2_globals.go │ │ ├── sdl2_renderer.go │ │ ├── sdl2_surface.go │ │ └── sdl2_surfacestate.go │ └── ebiten │ │ ├── doc.go │ │ ├── ebiten_panic_screen.go │ │ ├── ebiten_renderer.go │ │ ├── ebiten_surface.go │ │ ├── filter_helper.go │ │ └── surface_state.go ├── d2screen │ ├── d2screen.go │ ├── doc.go │ ├── loading_state.go │ ├── loading_update.go │ └── screen_manager.go ├── d2stats │ ├── diablo2stats │ │ ├── doc.go │ │ ├── stat.go │ │ ├── stat_factory.go │ │ ├── stat_test.go │ │ ├── stat_value.go │ │ ├── stat_value_stringers.go │ │ ├── statlist.go │ │ └── statlist_test.go │ ├── doc.go │ ├── stat.go │ ├── stat_list.go │ └── stat_value.go ├── d2term │ ├── commmand.go │ ├── d2term.go │ ├── doc.go │ ├── terminal.go │ ├── terminal_logger.go │ └── terminal_test.go └── d2ui │ ├── button.go │ ├── checkbox.go │ ├── color_tokens.go │ ├── custom_widget.go │ ├── d2ui.go │ ├── doc.go │ ├── drawable.go │ ├── frame.go │ ├── label.go │ ├── label_button.go │ ├── scrollbar.go │ ├── sprite.go │ ├── textbox.go │ ├── tooltip.go │ ├── ui_manager.go │ ├── widget.go │ └── widget_group.go ├── d2discord.png ├── d2game ├── d2gamescreen │ ├── blizzard_intro.go │ ├── character_select.go │ ├── cinematics.go │ ├── credits.go │ ├── doc.go │ ├── game.go │ ├── gui_testing.go │ ├── main_menu.go │ ├── map_engine_testing.go │ └── select_hero_class.go └── d2player │ ├── binding_layout.go │ ├── doc.go │ ├── equipment_slot.go │ ├── escape_menu.go │ ├── game_controls.go │ ├── globeWidget.go │ ├── help_overlay.go │ ├── hero_stats_panel.go │ ├── hud.go │ ├── input_callback_listener.go │ ├── inventory.go │ ├── inventory_grid.go │ ├── key_binding_menu.go │ ├── key_map.go │ ├── mini_panel.go │ ├── move_gold_panel.go │ ├── quest_log.go │ ├── skill_row.go │ ├── skill_select_menu.go │ ├── skill_select_panel.go │ ├── skillicon.go │ └── skilltree.go ├── d2logo.ico ├── d2logo.png ├── d2networking ├── client_listener.go ├── d2client │ ├── d2clientconnectiontype │ │ ├── connectiontype.go │ │ └── doc.go │ ├── d2localclient │ │ ├── doc.go │ │ └── local_client_connection.go │ ├── d2remoteclient │ │ ├── doc.go │ │ └── remote_client_connection.go │ ├── doc.go │ ├── game_client.go │ └── server_connection.go ├── d2netpacket │ ├── d2netpackettype │ │ ├── doc.go │ │ └── message_type.go │ ├── doc.go │ ├── net_packet.go │ ├── packet_add_player.go │ ├── packet_generate_map.go │ ├── packet_item_spawn.go │ ├── packet_move_player.go │ ├── packet_ping.go │ ├── packet_player_cast.go │ ├── packet_player_connection_request.go │ ├── packet_player_disconnect_request.go │ ├── packet_pong.go │ ├── packet_save_player.go │ ├── packet_server_closed.go │ ├── packet_server_full.go │ └── packet_update_server_info.go ├── d2server │ ├── client_connection.go │ ├── d2tcpclientconnection │ │ └── tcp_client_connection.go │ ├── d2udpclientconnection │ │ └── udp_client_connection.go │ ├── doc.go │ └── game_server.go ├── dedicated_server.go └── doc.go ├── d2script ├── doc.go └── engine.go ├── d2thread └── mainthread.go ├── debug_font.png ├── docs ├── ButtonMedium.png ├── ButtonWide.png ├── CNAME ├── Gameplay.png ├── Inventory.png ├── MainMenuSS.png ├── SelectHeroSS.png ├── areas.gif ├── diablo.woff ├── game_panels.png ├── index.html └── style.css ├── go.mod ├── go.sum ├── main.go ├── rh.exe ├── scripts ├── server │ └── server.js └── test.js ├── tagdev.bat └── utils └── extract-mpq ├── doc.go └── extract-mpq.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/golang:1.14 6 | working_directory: /go/src/github.com/OpenDiablo2/OpenDiablo2 7 | steps: 8 | - checkout 9 | - run: sudo apt-get install -y libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libgl1-mesa-dev libsdl2-dev libasound2-dev > /dev/null 2>&1 10 | - run: go get -v -t -d ./... 11 | - run: go build . 12 | #- run: go test -v ./... 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 4 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: essial 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Version [e.g. 22] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/pullRequest.yml: -------------------------------------------------------------------------------- 1 | name: pull_request 2 | "on": [pull_request] 3 | jobs: 4 | build: 5 | name: '' 6 | runs-on: self-hosted 7 | continue-on-error: true 8 | steps: 9 | - name: Set up Go 1.14 10 | uses: actions/setup-go@v2.1.3 11 | with: 12 | go-version: 1.14 13 | id: go 14 | 15 | - name: Check out code 16 | uses: actions/checkout@v2.3.4 17 | 18 | - name: Install dependencies 19 | run: | 20 | sudo apt-get update 21 | sudo apt-get install -y xvfb libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libgl1-mesa-dev libsdl2-dev libasound2-dev > /dev/null 2>&1 22 | 23 | - name: Run golangci-lint 24 | continue-on-error: false 25 | uses: golangci/golangci-lint-action@v2.3.0 26 | with: 27 | version: v1.32 28 | 29 | - name: Run tests 30 | env: 31 | DISPLAY: ":99.0" 32 | run: | 33 | xvfb-run --auto-servernum go test -v -race ./... 34 | 35 | - name: Build binary 36 | run: go build . 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*__debug_bin 2 | .vscode/**/* 3 | !.vscode/extensions.json 4 | **/Client.exe 5 | **/Client 6 | .idea/**/* 7 | .vs/**/* 8 | /OpenDiablo2.exe 9 | /OpenDiablo2 10 | **/*.pprof 11 | *.swp 12 | .*.swp 13 | tags 14 | heap.out 15 | heap.pdf 16 | .DS_Store 17 | pprof 18 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "golang.go", 4 | "soren.go-coverage-viewer" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | * OPEN DIABLO 2 2 | Tim "essial" Sarbin 3 | ndechiara 4 | mewmew 5 | grazz 6 | Erexo 7 | Ziemas 8 | liberodark 9 | cardoso 10 | Mirey 11 | Lectem 12 | wtfblub 13 | q3cpma 14 | averrin 15 | Dylan "Gravestench" Knuth 16 | Intyre 17 | Gürkan Kaymak 18 | Maxime "malavv" Lavigne 19 | Ripolak 20 | dafe 21 | presiyan 22 | Natureknight 23 | Ganitzsh 24 | 25 | * PATREON SUPPORTERS 26 | K C 27 | Blixt 28 | Dylan Knuth 29 | Brendan Porter 30 | Frazier Phillips 31 | 32 | * DIABLO2 LOGO 33 | Jose Pardilla (th3-prophetman) 34 | 35 | * DT1 File Specifications 36 | Paul SIRAMY 37 | 38 | * Other Specifications and general info 39 | Various users on Phrozen Keep 40 | -------------------------------------------------------------------------------- /d2app/capture_state.go: -------------------------------------------------------------------------------- 1 | package d2app 2 | 3 | type captureState int 4 | 5 | const ( 6 | captureStateNone captureState = iota 7 | captureStateFrame 8 | captureStateGif 9 | ) 10 | -------------------------------------------------------------------------------- /d2common/d2cache/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2cache provides a generic caching implementation 2 | package d2cache 3 | -------------------------------------------------------------------------------- /d2common/d2calculation/calcstring.go: -------------------------------------------------------------------------------- 1 | package d2calculation 2 | 3 | // CalcString is a type of string often used in datafiles to specify 4 | // a value that is calculated dynamically based on the stats of the relevant 5 | // source, for instance a missile might have a movement speed of lvl*2 6 | type CalcString string 7 | 8 | // Issue #689 9 | // info about calcstrings can be found here: https://d2mods.info/forum/kb/viewarticle?a=371 10 | -------------------------------------------------------------------------------- /d2common/d2calculation/d2parser/d2parser.go: -------------------------------------------------------------------------------- 1 | // Package d2parser contains the code for parsing calculation strings. 2 | package d2parser 3 | -------------------------------------------------------------------------------- /d2common/d2data/d2video/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2video provides a bink video decoder 2 | package d2video 3 | -------------------------------------------------------------------------------- /d2common/d2data/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2data provides file compression utilities, video decoders, and file loaders 2 | // for the txt files inside of diablo's mpq files 3 | package d2data 4 | -------------------------------------------------------------------------------- /d2common/d2datautils/bitstream_test.go: -------------------------------------------------------------------------------- 1 | package d2datautils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBitStreamBits(t *testing.T) { 8 | data := []byte{0xAA} 9 | bitStream := CreateBitStream(data) 10 | shouldBeOne := 0 11 | 12 | for i := 0; i < 8; i++ { 13 | bit := bitStream.ReadBits(1) 14 | if bit != shouldBeOne { 15 | t.Fatalf("Expected %d but got %d on iteration %d", shouldBeOne, bit, i) 16 | } 17 | 18 | if shouldBeOne == 1 { 19 | shouldBeOne = 0 20 | } else { 21 | shouldBeOne = 1 22 | } 23 | } 24 | } 25 | 26 | func TestBitStreamBytes(t *testing.T) { 27 | data := []byte{0xAA, 0xBB, 0xCC, 0xDD, 0x12, 0x34, 0x56, 0x78} 28 | bitStream := CreateBitStream(data) 29 | 30 | for i := 0; i < 8; i++ { 31 | b := byte(bitStream.ReadBits(8)) 32 | if b != data[i] { 33 | t.Fatalf("Expected %d but got %d on iteration %d", data[i], b, i) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /d2common/d2datautils/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2datautils is a utility package that provides helper functions/classes 2 | // for parsing the original diablo2 files. (eg. parsers for binary files that aren't byte-aligned) 3 | package d2datautils 4 | -------------------------------------------------------------------------------- /d2common/d2datautils/stream_writer_test.go: -------------------------------------------------------------------------------- 1 | package d2datautils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStreamWriterByte(t *testing.T) { 8 | sr := CreateStreamWriter() 9 | data := []byte{0x12, 0x34, 0x56, 0x78} 10 | 11 | for _, d := range data { 12 | sr.PushByte(d) 13 | } 14 | 15 | output := sr.GetBytes() 16 | for i, d := range data { 17 | if output[i] != d { 18 | t.Fatalf("sr.PushByte() pushed %X, but wrote %X instead", d, output[i]) 19 | } 20 | } 21 | } 22 | 23 | func TestStreamWriterWord(t *testing.T) { 24 | sr := CreateStreamWriter() 25 | data := []byte{0x12, 0x34, 0x56, 0x78} 26 | 27 | sr.PushUint16(0x3412) 28 | sr.PushUint16(0x7856) 29 | 30 | output := sr.GetBytes() 31 | for i, d := range data { 32 | if output[i] != d { 33 | t.Fatalf("sr.PushWord() pushed byte %X to %d, but %X was expected instead", output[i], i, d) 34 | } 35 | } 36 | } 37 | 38 | func TestStreamWriterDword(t *testing.T) { 39 | sr := CreateStreamWriter() 40 | data := []byte{0x12, 0x34, 0x56, 0x78} 41 | 42 | sr.PushUint32(0x78563412) 43 | 44 | output := sr.GetBytes() 45 | for i, d := range data { 46 | if output[i] != d { 47 | t.Fatalf("sr.PushDword() pushed byte %X to %d, but %X was expected instead", output[i], i, d) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /d2common/d2enum/animation_frame.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // AnimationFrame represents a single frame of animation. 4 | type AnimationFrame int 5 | 6 | // AnimationFrame types 7 | const ( 8 | AnimationFrameNoEvent AnimationFrame = iota 9 | AnimationFrameAttack 10 | AnimationFrameMissile 11 | AnimationFrameSound 12 | AnimationFrameSkill 13 | ) 14 | -------------------------------------------------------------------------------- /d2common/d2enum/animation_frame_direction.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // AnimationFrameDirection enumerates animation frame directions used in d2datadict.MonsterSequenceFrame 4 | type AnimationFrameDirection int 5 | 6 | // Animation frame directions 7 | const ( 8 | SouthWest AnimationFrameDirection = iota 9 | NorthWest 10 | NorthEast 11 | SouthEast 12 | South 13 | West 14 | North 15 | East 16 | ) 17 | -------------------------------------------------------------------------------- /d2common/d2enum/animation_frame_event.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // AnimationFrameEvent enumerates events used in d2datadict.MonsterSequenceFrame 4 | type AnimationFrameEvent int 5 | 6 | // Animation frame events 7 | const ( 8 | NoEvent AnimationFrameEvent = iota 9 | MeleeAttack 10 | MissileAttack 11 | PlaySound 12 | LaunchSpell 13 | ) 14 | -------------------------------------------------------------------------------- /d2common/d2enum/composite_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | //go:generate stringer -linecomment -type CompositeType -output composite_type_string.go 4 | 5 | // CompositeType represents a composite type 6 | type CompositeType int 7 | 8 | // Composite types 9 | const ( 10 | CompositeTypeHead CompositeType = iota // HD 11 | CompositeTypeTorso // TR 12 | CompositeTypeLegs // LG 13 | CompositeTypeRightArm // RA 14 | CompositeTypeLeftArm // LA 15 | CompositeTypeRightHand // RH 16 | CompositeTypeLeftHand // LH 17 | CompositeTypeShield // SH 18 | CompositeTypeSpecial1 // S1 19 | CompositeTypeSpecial2 // S2 20 | CompositeTypeSpecial3 // S3 21 | CompositeTypeSpecial4 // S4 22 | CompositeTypeSpecial5 // S5 23 | CompositeTypeSpecial6 // S6 24 | CompositeTypeSpecial7 // S7 25 | CompositeTypeSpecial8 // S8 26 | CompositeTypeMax 27 | ) 28 | -------------------------------------------------------------------------------- /d2common/d2enum/difficulty.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // DifficultyType is an enum for the possible difficulties 4 | type DifficultyType int 5 | 6 | const ( 7 | // DifficultyNormal is the normal difficulty 8 | DifficultyNormal DifficultyType = iota 9 | // DifficultyNightmare is the nightmare difficulty 10 | DifficultyNightmare 11 | // DifficultyHell is the hell difficulty 12 | DifficultyHell 13 | ) 14 | -------------------------------------------------------------------------------- /d2common/d2enum/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2enum provides enumerations used throughout 2 | // the OpenDiablo2 codebase. 3 | package d2enum 4 | -------------------------------------------------------------------------------- /d2common/d2enum/encoding_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // EncodingType represents a encoding type 4 | type EncodingType int 5 | 6 | // Encoding types 7 | const ( 8 | EncodeDefault EncodingType = iota 9 | ) 10 | -------------------------------------------------------------------------------- /d2common/d2enum/equipped_slot.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // EquippedSlot represents the type of equipment slot 4 | type EquippedSlot int 5 | 6 | // Equipped slot ID's 7 | const ( 8 | EquippedSlotNone EquippedSlot = iota 9 | EquippedSlotHead 10 | EquippedSlotTorso 11 | EquippedSlotLegs 12 | EquippedSlotRightArm 13 | EquippedSlotLeftArm 14 | EquippedSlotLeftHand 15 | EquippedSlotRightHand 16 | EquippedSlotNeck 17 | EquippedSlotBelt 18 | EquippedSlotGloves 19 | ) 20 | -------------------------------------------------------------------------------- /d2common/d2enum/filter.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // Filter represents the type of texture filter to be used when an image is magnified or minified. 4 | type Filter int 5 | 6 | const ( 7 | // FilterDefault represents the default filter. 8 | FilterDefault Filter = iota 9 | 10 | // FilterNearest represents nearest (crisp-edged) filter 11 | FilterNearest 12 | 13 | // FilterLinear represents linear filter 14 | FilterLinear 15 | ) 16 | -------------------------------------------------------------------------------- /d2common/d2enum/hero_stance.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // HeroStance used to render hero stance 4 | type HeroStance int 5 | 6 | // HeroStance types 7 | const ( 8 | HeroStanceIdle HeroStance = iota 9 | HeroStanceIdleSelected 10 | HeroStanceApproaching 11 | HeroStanceSelected 12 | HeroStanceRetreating 13 | ) 14 | -------------------------------------------------------------------------------- /d2common/d2enum/hero_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type Hero"; DO NOT EDIT. 2 | 3 | package d2enum 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[HeroNone-0] 12 | _ = x[HeroBarbarian-1] 13 | _ = x[HeroNecromancer-2] 14 | _ = x[HeroPaladin-3] 15 | _ = x[HeroAssassin-4] 16 | _ = x[HeroSorceress-5] 17 | _ = x[HeroAmazon-6] 18 | _ = x[HeroDruid-7] 19 | } 20 | 21 | const _Hero_name = "BarbarianNecromancerPaladinAssassinSorceressAmazonDruid" 22 | 23 | var _Hero_index = [...]uint8{0, 0, 9, 20, 27, 35, 44, 50, 55} 24 | 25 | func (i Hero) String() string { 26 | if i < 0 || i >= Hero(len(_Hero_index)-1) { 27 | return "Hero(" + strconv.FormatInt(int64(i), 10) + ")" 28 | } 29 | return _Hero_name[_Hero_index[i]:_Hero_index[i+1]] 30 | } 31 | -------------------------------------------------------------------------------- /d2common/d2enum/hero_string2enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by "string2enum -samepkg -linecomment -type Hero"; DO NOT EDIT. 2 | 3 | package d2enum 4 | 5 | import "fmt" 6 | 7 | // HeroFromString returns the Hero enum corresponding to s. 8 | func HeroFromString(s string) Hero { 9 | if len(s) == 0 { 10 | return 0 11 | } 12 | for i := range _Hero_index[:len(_Hero_index)-1] { 13 | if s == _Hero_name[_Hero_index[i]:_Hero_index[i+1]] { 14 | return Hero(i) 15 | } 16 | } 17 | panic(fmt.Errorf("unable to locate Hero enum corresponding to %q", s)) 18 | } 19 | 20 | func _(s string) { 21 | // Check for duplicate string values in type "Hero". 22 | switch s { 23 | // 0 24 | case "": 25 | // 1 26 | case "Barbarian": 27 | // 2 28 | case "Necromancer": 29 | // 3 30 | case "Paladin": 31 | // 4 32 | case "Assassin": 33 | // 5 34 | case "Sorceress": 35 | // 6 36 | case "Amazon": 37 | // 7 38 | case "Druid": 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /d2common/d2enum/input_button.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // MouseButton represents a traditional 3-button mouse 4 | type MouseButton int 5 | 6 | const ( 7 | // MouseButtonLeft is the left mouse button 8 | MouseButtonLeft MouseButton = iota 9 | // MouseButtonMiddle is the middle mouse button 10 | MouseButtonMiddle 11 | // MouseButtonRight is the right mouse button 12 | MouseButtonRight 13 | // MouseButtonMin is the lowest MouseButton 14 | MouseButtonMin = MouseButtonLeft 15 | // MouseButtonMax is the highest MouseButton 16 | MouseButtonMax = MouseButtonRight 17 | ) 18 | 19 | // MouseButtonMod represents a "modified" mouse button action. This could mean, for example, ctrl-mouse_left 20 | type MouseButtonMod int 21 | 22 | const ( 23 | // MouseButtonModLeft is a modified left mouse button 24 | MouseButtonModLeft MouseButtonMod = 1 << iota 25 | // MouseButtonModMiddle is a modified middle mouse button 26 | MouseButtonModMiddle 27 | // MouseButtonModRight is a modified right mouse button 28 | MouseButtonModRight 29 | ) 30 | -------------------------------------------------------------------------------- /d2common/d2enum/input_priority.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // Priority of the event handler 4 | type Priority int 5 | 6 | // Priorities 7 | const ( 8 | PriorityLow Priority = iota 9 | PriorityDefault 10 | PriorityHigh 11 | ) 12 | -------------------------------------------------------------------------------- /d2common/d2enum/inventory_item_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // InventoryItemType represents a inventory item type 4 | type InventoryItemType int 5 | 6 | // Inventry item types 7 | const ( 8 | InventoryItemTypeItem InventoryItemType = iota 9 | InventoryItemTypeWeapon 10 | InventoryItemTypeArmor 11 | ) 12 | -------------------------------------------------------------------------------- /d2common/d2enum/item_affix_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // ItemAffixSuperType represents a item affix super type 4 | type ItemAffixSuperType int 5 | 6 | // Super types 7 | const ( 8 | ItemAffixPrefix ItemAffixSuperType = iota 9 | ItemAffixSuffix 10 | ) 11 | 12 | // ItemAffixSubType represents a item affix sub type 13 | type ItemAffixSubType int 14 | 15 | // Sub types 16 | const ( 17 | ItemAffixCommon ItemAffixSubType = iota 18 | ItemAffixMagic 19 | ) 20 | -------------------------------------------------------------------------------- /d2common/d2enum/item_armor_class.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // ArmorClass is a 3-character token for the armor. It's used for speed calculations. 4 | type ArmorClass string 5 | 6 | // Armor classes 7 | const ( 8 | ArmorClassLite = "lit" 9 | ArmorClassMedium = "med" 10 | ArmorClassHeavy = "hvy" 11 | ) 12 | -------------------------------------------------------------------------------- /d2common/d2enum/item_quality.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // ItemQuality is used for enumerating item quality values 4 | type ItemQuality int 5 | 6 | // Item qualities 7 | const ( 8 | LowQuality ItemQuality = iota + 1 9 | Normal 10 | Superior 11 | Magic 12 | Set 13 | Rare 14 | Unique 15 | Crafted 16 | Tempered 17 | ) 18 | -------------------------------------------------------------------------------- /d2common/d2enum/layer_stream_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // LayerStreamType represents a layer stream type 4 | type LayerStreamType int 5 | 6 | // Layer stream types 7 | const ( 8 | LayerStreamWall1 LayerStreamType = iota 9 | LayerStreamWall2 10 | LayerStreamWall3 11 | LayerStreamWall4 12 | LayerStreamOrientation1 13 | LayerStreamOrientation2 14 | LayerStreamOrientation3 15 | LayerStreamOrientation4 16 | LayerStreamFloor1 17 | LayerStreamFloor2 18 | LayerStreamShadow 19 | LayerStreamSubstitute 20 | ) 21 | -------------------------------------------------------------------------------- /d2common/d2enum/level_generation_types.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // from levels.txt, field `DrlgType` 4 | // https://d2mods.info/forum/kb/viewarticle?a=301 5 | 6 | // LevelGenerationType Setting for Level Generation: You have 3 possibilities here: 7 | // 1 Random Maze 8 | // 2 Preset Area 9 | // 3 Wilderness level 10 | type LevelGenerationType int 11 | 12 | // Level generation types 13 | const ( 14 | LevelTypeRandomMaze LevelGenerationType = iota 15 | LevelTypePreset 16 | LevelTypeWilderness 17 | ) 18 | -------------------------------------------------------------------------------- /d2common/d2enum/level_teleport_flags.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // from levels.txt, field `Teleport` 4 | // https://d2mods.info/forum/kb/viewarticle?a=301 5 | 6 | // TeleportFlag Controls if teleport is allowed in that level. 7 | // 0 = Teleport not allowed 8 | // 1 = Teleport allowed 9 | // 2 = Teleport allowed, but not able to use teleport throu walls/objects 10 | // (maybe for objects) 11 | type TeleportFlag int 12 | 13 | // Teleport flag types 14 | const ( 15 | TeleportDisabled TeleportFlag = iota 16 | TeleportEnabled 17 | TeleportEnabledLimited 18 | ) 19 | -------------------------------------------------------------------------------- /d2common/d2enum/monster_alignment_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // MonsterAlignmentType determines the hostility of the monster towards players 4 | type MonsterAlignmentType int 5 | 6 | const ( 7 | // MonsterEnemy flag will make monsters hostile towards players 8 | MonsterEnemy MonsterAlignmentType = iota 9 | 10 | // MonsterFriend will make monsters friendly towards players 11 | // this is likely used by NPC's and summons 12 | MonsterFriend 13 | 14 | // MonsterNeutral will make monsters not care about players or monsters 15 | // this flag is used for `critter` type monsters 16 | MonsterNeutral 17 | ) 18 | -------------------------------------------------------------------------------- /d2common/d2enum/monster_combat_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // MonsterCombatType is used for setting the monster as melee or ranged 4 | type MonsterCombatType int 5 | 6 | const ( 7 | // MonsterMelee is a flag that sets the monster as melee-only 8 | MonsterMelee MonsterCombatType = iota 9 | 10 | // MonsterRanged is a flag that sets the monster as ranged-only 11 | MonsterRanged 12 | ) 13 | -------------------------------------------------------------------------------- /d2common/d2enum/monumod_const_index.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // MonUModConstIndex is used as an index into d2datadict.MonsterUniqueModifierConstants 4 | type MonUModConstIndex int 5 | 6 | // Unique monster modifier constants 7 | const ( 8 | ChampionChance MonUModConstIndex = iota 9 | MinionHPBonus 10 | MinionHPBonusNightmare 11 | MinionHPBonusHell 12 | ChampionHPBonus 13 | ChampionHPBonusNightmare 14 | ChampionHPBonusHell 15 | UniqueHPBonus 16 | UniqueHPBonusNightmare 17 | UniqueHPBonusHell 18 | ChampionAttackRatingBonus 19 | ChampionDamageBonus 20 | StrongMinionAttackRatingBonus 21 | StrongMinionDamageBonus 22 | MinionElementalDamageMinBonus 23 | MinionElementalDamageMinBonusNightmare 24 | MinionElementalDamageMinBonusHell 25 | MinionElementalDamageMaxBonus 26 | MinionElementalDamageMaxBonusNightmare 27 | MinionElementalDamageMaxBonusHell 28 | ChampionElementalDamageMinBonus 29 | ChampionElementalDamageMinBonusNightmare 30 | ChampionElementalDamageMinBonusHell 31 | ChampionElementalDamageMaxBonus 32 | ChampionElementalDamageMaxBonusNightmare 33 | ChampionElementalDamageMaxBonusHell 34 | UniqueElementalDamageMinBonus 35 | UniqueElementalDamageMinBonusNightmare 36 | UniqueElementalDamageMinBonusHell 37 | UniqueElementalDamageMaxBonus 38 | UniqueElementalDamageMaxBonusNightmare 39 | UniqueElementalDamageMaxBonusHell 40 | ) 41 | -------------------------------------------------------------------------------- /d2common/d2enum/npc_action_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // NPCActionType determines composite mode animations for NPC's as they move around 4 | type NPCActionType int 5 | 6 | // NPCAction types 7 | // https://github.com/OpenDiablo2/OpenDiablo2/issues/811 8 | const ( 9 | NPCActionInvalid NPCActionType = iota 10 | NPCAction1 11 | NPCAction2 12 | NPCAction3 13 | NPCActionSkill1 14 | ) 15 | -------------------------------------------------------------------------------- /d2common/d2enum/object_animation_mode.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | //go:generate stringer -linecomment -type ObjectAnimationMode -output object_animation_mode_string.go 4 | //go:generate string2enum -samepkg -linecomment -type ObjectAnimationMode -output object_animation_mode_string2enum.go 5 | 6 | // ObjectAnimationMode represents object animation modes 7 | type ObjectAnimationMode int 8 | 9 | // Object animation modes 10 | const ( 11 | ObjectAnimationModeNeutral ObjectAnimationMode = iota // NU 12 | ObjectAnimationModeOperating // OP 13 | ObjectAnimationModeOpened // ON 14 | ObjectAnimationModeSpecial1 // S1 15 | ObjectAnimationModeSpecial2 // S2 16 | ObjectAnimationModeSpecial3 // S3 17 | ObjectAnimationModeSpecial4 // S4 18 | ObjectAnimationModeSpecial5 // S5 19 | ) 20 | -------------------------------------------------------------------------------- /d2common/d2enum/object_animation_mode_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type ObjectAnimationMode -output object_animation_mode_string.go"; DO NOT EDIT. 2 | 3 | package d2enum 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[ObjectAnimationModeNeutral-0] 12 | _ = x[ObjectAnimationModeOperating-1] 13 | _ = x[ObjectAnimationModeOpened-2] 14 | _ = x[ObjectAnimationModeSpecial1-3] 15 | _ = x[ObjectAnimationModeSpecial2-4] 16 | _ = x[ObjectAnimationModeSpecial3-5] 17 | _ = x[ObjectAnimationModeSpecial4-6] 18 | _ = x[ObjectAnimationModeSpecial5-7] 19 | } 20 | 21 | const _ObjectAnimationMode_name = "NUOPONS1S2S3S4S5" 22 | 23 | var _ObjectAnimationMode_index = [...]uint8{0, 2, 4, 6, 8, 10, 12, 14, 16} 24 | 25 | func (i ObjectAnimationMode) String() string { 26 | if i < 0 || i >= ObjectAnimationMode(len(_ObjectAnimationMode_index)-1) { 27 | return "ObjectAnimationMode(" + strconv.FormatInt(int64(i), 10) + ")" 28 | } 29 | return _ObjectAnimationMode_name[_ObjectAnimationMode_index[i]:_ObjectAnimationMode_index[i+1]] 30 | } 31 | -------------------------------------------------------------------------------- /d2common/d2enum/object_animation_mode_string2enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by "string2enum -samepkg -linecomment -type ObjectAnimationMode -output object_animation_mode_string2enum.go"; DO NOT EDIT. 2 | 3 | package d2enum 4 | 5 | import "fmt" 6 | 7 | // ObjectAnimationModeFromString returns the ObjectAnimationMode enum corresponding to s. 8 | func ObjectAnimationModeFromString(s string) ObjectAnimationMode { 9 | if len(s) == 0 { 10 | return 0 11 | } 12 | for i := range _ObjectAnimationMode_index[:len(_ObjectAnimationMode_index)-1] { 13 | if s == _ObjectAnimationMode_name[_ObjectAnimationMode_index[i]:_ObjectAnimationMode_index[i+1]] { 14 | return ObjectAnimationMode(i) 15 | } 16 | } 17 | panic(fmt.Errorf("unable to locate ObjectAnimationMode enum corresponding to %q", s)) 18 | } 19 | 20 | func _(s string) { 21 | // Check for duplicate string values in type "ObjectAnimationMode". 22 | switch s { 23 | // 0 24 | case "NU": 25 | // 1 26 | case "OP": 27 | // 2 28 | case "ON": 29 | // 3 30 | case "S1": 31 | // 4 32 | case "S2": 33 | // 5 34 | case "S3": 35 | // 6 36 | case "S4": 37 | // 7 38 | case "S5": 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /d2common/d2enum/object_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // ObjectType is the type of an object 4 | type ObjectType int 5 | 6 | // Object types 7 | const ( 8 | ObjectTypePlayer ObjectType = iota 9 | ObjectTypeCharacter 10 | ObjectTypeItem 11 | ) 12 | -------------------------------------------------------------------------------- /d2common/d2enum/pet_icon_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // PetIconType determines the pet icon type 4 | type PetIconType int 5 | 6 | // Pet icon types 7 | // The information has been gathered from [https:// d2mods.info/forum/kb/viewarticle?a=355] 8 | const ( 9 | NoIcon PetIconType = iota 10 | ShowIconOnly 11 | ShowIconAndQuantity // Quantity, such as the number of skeletons 12 | ShowIconOnly2 13 | ) 14 | -------------------------------------------------------------------------------- /d2common/d2enum/readme.md: -------------------------------------------------------------------------------- 1 | # OpenDiablo2 Enums 2 | Items in this folder are compiled with two programs. You can obtain them 3 | by running the following: 4 | ``` 5 | go get golang.org/x/tools/cmd/stringer 6 | go get github.com/mewspring/tools/cmd/string2enum 7 | ``` 8 | Once you have the tools installed, simply run the following command in this 9 | folder to regenerate the support files: 10 | ``` 11 | go generate 12 | ``` 13 | If you add any enums (e.g. `AnimationMode`), make sure to add the following to the end of the 14 | file: 15 | ```go 16 | //go:generate stringer -linecomment -type AnimationMode 17 | ``` 18 | -------------------------------------------------------------------------------- /d2common/d2enum/region_id.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // RegionIdType represents a region ID 4 | type RegionIdType int //nolint:golint,stylecheck // many changed needed when changing to ID 5 | 6 | // Regions 7 | const ( 8 | RegionNone RegionIdType = iota 9 | RegionAct1Town 10 | RegionAct1Wilderness 11 | RegionAct1Cave 12 | RegionAct1Crypt 13 | RegionAct1Monestary 14 | RegionAct1Courtyard 15 | RegionAct1Barracks 16 | RegionAct1Jail 17 | RegionAct1Cathedral 18 | RegionAct1Catacombs 19 | RegionAct1Tristram 20 | RegionAct2Town 21 | RegionAct2Sewer 22 | RegionAct2Harem 23 | RegionAct2Basement 24 | RegionAct2Desert 25 | RegionAct2Tomb 26 | RegionAct2Lair 27 | RegionAct2Arcane 28 | RegionAct3Town 29 | RegionAct3Jungle 30 | RegionAct3Kurast 31 | RegionAct3Spider 32 | RegionAct3Dungeon 33 | RegionAct3Sewer 34 | RegionAct4Town 35 | RegionAct4Mesa 36 | RegionAct4Lava 37 | RegonAct5Town 38 | RegionAct5Siege 39 | RegionAct5Barricade 40 | RegionAct5Temple 41 | RegionAct5IceCaves 42 | RegionAct5Baal 43 | RegionAct5Lava 44 | ) 45 | -------------------------------------------------------------------------------- /d2common/d2enum/region_layer.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // RegionLayerType represents a region layer 4 | type RegionLayerType int 5 | 6 | // Region layer types 7 | const ( 8 | RegionLayerTypeFloors RegionLayerType = iota 9 | RegionLayerTypeWalls 10 | RegionLayerTypeShadows 11 | ) 12 | -------------------------------------------------------------------------------- /d2common/d2enum/render_type.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // RenderType defines the type of rendering engine to use 4 | type RenderType int 5 | 6 | // Render types 7 | const ( 8 | Ebiten RenderType = iota + 1 9 | ) 10 | -------------------------------------------------------------------------------- /d2common/d2enum/terminal_category.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | // TermCategory applies styles to the lines in the Terminal 4 | type TermCategory int 5 | 6 | // Terminal Category types 7 | const ( 8 | TermCategoryNone TermCategory = iota 9 | TermCategoryInfo 10 | TermCategoryWarning 11 | TermCategoryError 12 | ) 13 | -------------------------------------------------------------------------------- /d2common/d2enum/weapon_class.go: -------------------------------------------------------------------------------- 1 | package d2enum 2 | 3 | //go:generate stringer -linecomment -type WeaponClass -output weapon_class_string.go 4 | //go:generate string2enum -samepkg -linecomment -type WeaponClass -output weapon_class_string2enum.go 5 | 6 | // WeaponClass represents a weapon class 7 | type WeaponClass int 8 | 9 | // Weapon classes 10 | const ( 11 | WeaponClassNone WeaponClass = iota // 12 | WeaponClassHandToHand // hth 13 | WeaponClassBow // bow 14 | WeaponClassOneHandSwing // 1hs 15 | WeaponClassOneHandThrust // 1ht 16 | WeaponClassStaff // stf 17 | WeaponClassTwoHandSwing // 2hs 18 | WeaponClassTwoHandThrust // 2ht 19 | WeaponClassCrossbow // xbw 20 | WeaponClassLeftJabRightSwing // 1js 21 | WeaponClassLeftJabRightThrust // 1jt 22 | WeaponClassLeftSwingRightSwing // 1ss 23 | WeaponClassLeftSwingRightThrust // 1st 24 | WeaponClassOneHandToHand // ht1 25 | WeaponClassTwoHandToHand // ht2 26 | ) 27 | -------------------------------------------------------------------------------- /d2common/d2enum/weapon_class_string2enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by "string2enum -samepkg -linecomment -type WeaponClass -output weapon_class_string2enum.go"; DO NOT EDIT. 2 | 3 | package d2enum 4 | 5 | import "fmt" 6 | 7 | // WeaponClassFromString returns the WeaponClass enum corresponding to s. 8 | func WeaponClassFromString(s string) WeaponClass { 9 | if len(s) == 0 { 10 | return 0 11 | } 12 | for i := range _WeaponClass_index[:len(_WeaponClass_index)-1] { 13 | if s == _WeaponClass_name[_WeaponClass_index[i]:_WeaponClass_index[i+1]] { 14 | return WeaponClass(i) 15 | } 16 | } 17 | panic(fmt.Errorf("unable to locate WeaponClass enum corresponding to %q", s)) 18 | } 19 | 20 | func _(s string) { 21 | // Check for duplicate string values in type "WeaponClass". 22 | switch s { 23 | // 0 24 | case "": 25 | // 1 26 | case "hth": 27 | // 2 28 | case "bow": 29 | // 3 30 | case "1hs": 31 | // 4 32 | case "1ht": 33 | // 5 34 | case "stf": 35 | // 6 36 | case "2hs": 37 | // 7 38 | case "2ht": 39 | // 8 40 | case "xbw": 41 | // 9 42 | case "1js": 43 | // 10 44 | case "1jt": 45 | // 11 46 | case "1ss": 47 | // 12 48 | case "1st": 49 | // 13 50 | case "ht1": 51 | // 14 52 | case "ht2": 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/block.go: -------------------------------------------------------------------------------- 1 | package d2animdata 2 | 3 | type block struct { 4 | recordCount uint32 5 | records []*AnimationDataRecord 6 | } 7 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2animdata provides a file parser for AnimData files. AnimData files have the '.d2' 2 | // file extension, but we do not call this package `d2d2` because multiple file types share this 3 | // extension, and because the project namespace prefix makes it sound terrible. 4 | package d2animdata 5 | 6 | /* 7 | The AnimData.d2 file is a binary file that contains speed and event data for animations. 8 | 9 | The data is encoded as little-endian binary data 10 | 11 | The file contents look like this: 12 | 13 | type animData struct { 14 | blocks [256]struct{ 15 | recordCount uint8 16 | records []struct{ // *see note below 17 | name [8]byte // last byte is always \0 18 | framesPerDirection uint32 19 | speed uint16 // **see note below 20 | _ uint16 // just padded 0's 21 | events [144]uint8 // ***see not below 22 | } 23 | } 24 | } 25 | 26 | *NOTE: can contain 0 to 67 records 27 | 28 | **NOTE: game fps is assumed to be 25, the speed is calculated as (25 * record.speed / 256) 29 | 30 | **NOTE: Animation events can be one of `None`, `Attack`, `Missile`, `Sound`, or `Skill` 31 | 32 | */ 33 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/events.go: -------------------------------------------------------------------------------- 1 | package d2animdata 2 | 3 | // AnimationEvent represents an event that can happen on a frame of animation 4 | type AnimationEvent byte 5 | 6 | // Animation events 7 | const ( 8 | AnimationEventNone AnimationEvent = iota 9 | AnimationEventAttack 10 | AnimationEventMissile 11 | AnimationEventSound 12 | AnimationEventSkill 13 | ) 14 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/hash.go: -------------------------------------------------------------------------------- 1 | package d2animdata 2 | 3 | import "strings" 4 | 5 | type hashTable [numBlocks]byte 6 | 7 | func hashName(name string) byte { 8 | hashBytes := []byte(strings.ToUpper(name)) 9 | 10 | var hash uint32 11 | for hashByteIdx := range hashBytes { 12 | hash += uint32(hashBytes[hashByteIdx]) 13 | } 14 | 15 | hash %= numBlocks 16 | 17 | return byte(hash) 18 | } 19 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/record.go: -------------------------------------------------------------------------------- 1 | package d2animdata 2 | 3 | // AnimationDataRecord represents a single record from the AnimData.d2 file 4 | type AnimationDataRecord struct { 5 | name string 6 | framesPerDirection uint32 7 | speed uint16 8 | events map[int]AnimationEvent 9 | } 10 | 11 | // FPS returns the frames per second for this animation record 12 | func (r *AnimationDataRecord) FPS() float64 { 13 | speedf := float64(r.speed) 14 | divisorf := float64(speedDivisor) 15 | basef := float64(speedBaseFPS) 16 | 17 | return basef * speedf / divisorf 18 | } 19 | 20 | // FrameDurationMS returns the duration in milliseconds that a frame is displayed 21 | func (r *AnimationDataRecord) FrameDurationMS() float64 { 22 | return milliseconds / r.FPS() 23 | } 24 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/testdata/AnimData.d2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2common/d2fileformats/d2animdata/testdata/AnimData.d2 -------------------------------------------------------------------------------- /d2common/d2fileformats/d2animdata/testdata/BadData.d2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2common/d2fileformats/d2animdata/testdata/BadData.d2 -------------------------------------------------------------------------------- /d2common/d2fileformats/d2cof/cof_layer.go: -------------------------------------------------------------------------------- 1 | package d2cof 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // CofLayer is a structure that represents a single layer in a COF file. 6 | type CofLayer struct { 7 | Type d2enum.CompositeType 8 | Shadow byte 9 | Selectable bool 10 | Transparent bool 11 | DrawEffect d2enum.DrawEffect 12 | WeaponClass d2enum.WeaponClass 13 | } 14 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2cof/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2cof contains the logic for loading and processing COF files. 2 | package d2cof 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dat/dat.go: -------------------------------------------------------------------------------- 1 | package d2dat 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 4 | 5 | const ( 6 | // index offset helpers 7 | b = iota 8 | g 9 | r 10 | o 11 | ) 12 | 13 | // Load loads a DAT file. 14 | func Load(data []byte) (d2interface.Palette, error) { 15 | palette := &DATPalette{} 16 | 17 | for i := 0; i < 256; i++ { 18 | // offsets look like i*3+n, where n is 0,1,2 19 | palette.colors[i] = &DATColor{b: data[i*o+b], g: data[i*o+g], r: data[i*o+r]} 20 | } 21 | 22 | return palette, nil 23 | } 24 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dat/dat_palette.go: -------------------------------------------------------------------------------- 1 | package d2dat 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 7 | ) 8 | 9 | const ( 10 | numColors = 256 11 | ) 12 | 13 | // DATPalette represents a 256 color palette. 14 | type DATPalette struct { 15 | colors [numColors]d2interface.Color 16 | } 17 | 18 | // NumColors returns the number of colors in the palette 19 | func (p *DATPalette) NumColors() int { 20 | return len(p.colors) 21 | } 22 | 23 | // GetColors returns the slice of colors in the palette 24 | func (p *DATPalette) GetColors() [numColors]d2interface.Color { 25 | return p.colors 26 | } 27 | 28 | // GetColor returns a color by index 29 | func (p *DATPalette) GetColor(idx int) (d2interface.Color, error) { 30 | if color := p.colors[idx]; color != nil { 31 | return color, nil 32 | } 33 | 34 | return nil, fmt.Errorf("cannot find color index '%d in palette'", idx) 35 | } 36 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dat/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2dat contains the logic for loading and processing DAT files. 2 | package d2dat 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dc6/dc6_frame.go: -------------------------------------------------------------------------------- 1 | package d2dc6 2 | 3 | // DC6Frame represents a single frame in a DC6. 4 | type DC6Frame struct { 5 | Flipped uint32 6 | Width uint32 7 | Height uint32 8 | OffsetX int32 9 | OffsetY int32 10 | Unknown uint32 11 | NextBlock uint32 12 | Length uint32 13 | FrameData []byte // size is the value of Length 14 | Terminator []byte // 3 bytes 15 | } 16 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dc6/dc6_frame_header.go: -------------------------------------------------------------------------------- 1 | package d2dc6 2 | 3 | // DC6FrameHeader represents the header of a frame in a DC6. 4 | type DC6FrameHeader struct { 5 | Flipped int32 `struct:"int32"` 6 | Width int32 `struct:"int32"` 7 | Height int32 `struct:"int32"` 8 | OffsetX int32 `struct:"int32"` 9 | OffsetY int32 `struct:"int32"` 10 | Unknown uint32 `struct:"uint32"` 11 | NextBlock uint32 `struct:"uint32"` 12 | Length uint32 `struct:"uint32"` 13 | } 14 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dc6/dc6_header.go: -------------------------------------------------------------------------------- 1 | package d2dc6 2 | 3 | // DC6Header represents the file header of a DC6 file. 4 | type DC6Header struct { 5 | Version int32 `struct:"int32"` 6 | Flags uint32 `struct:"uint32"` 7 | Encoding uint32 `struct:"uint32"` 8 | Termination []byte `struct:"[4]byte"` 9 | Directions int32 `struct:"int32"` 10 | FramesPerDirection int32 `struct:"int32"` 11 | } 12 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dc6/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2dc6 contains the logic for loading and processing DC6 files. 2 | package d2dc6 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dcc/dcc_cell.go: -------------------------------------------------------------------------------- 1 | package d2dcc 2 | 3 | // DCCCell represents a single cell in a DCC file. 4 | type DCCCell struct { 5 | Width int 6 | Height int 7 | XOffset int 8 | YOffset int 9 | LastWidth int 10 | LastHeight int 11 | LastXOffset int 12 | LastYOffset int 13 | } 14 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dcc/dcc_pixel_buffer_entry.go: -------------------------------------------------------------------------------- 1 | package d2dcc 2 | 3 | // DCCPixelBufferEntry represents a single entry in the pixel buffer. 4 | type DCCPixelBufferEntry struct { 5 | Value [4]byte 6 | Frame int 7 | FrameCellIndex int 8 | } 9 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dcc/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2dcc contains the logic for loading and processing DCC files. 2 | package d2dcc 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2ds1 provides functionality for loading/processing DS1 files 2 | package d2ds1 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/floor_shadow_record.go: -------------------------------------------------------------------------------- 1 | package d2ds1 2 | 3 | // FloorShadowRecord represents a floor or shadow record in a DS1 file. 4 | type FloorShadowRecord struct { 5 | Prop1 byte 6 | Sequence byte 7 | Unknown1 byte 8 | Style byte 9 | Unknown2 byte 10 | Hidden bool 11 | RandomIndex byte 12 | Animated bool 13 | YAdjust int 14 | } 15 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/object.go: -------------------------------------------------------------------------------- 1 | package d2ds1 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2path" 5 | ) 6 | 7 | // Object is a game world object 8 | type Object struct { 9 | Type int 10 | ID int 11 | X int 12 | Y int 13 | Flags int 14 | Paths []d2path.Path 15 | } 16 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/substitution_group.go: -------------------------------------------------------------------------------- 1 | package d2ds1 2 | 3 | // SubstitutionGroup represents a substitution group in a DS1 file. 4 | type SubstitutionGroup struct { 5 | TileX int32 6 | TileY int32 7 | WidthInTiles int32 8 | HeightInTiles int32 9 | Unknown int32 10 | } 11 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/substitution_record.go: -------------------------------------------------------------------------------- 1 | package d2ds1 2 | 3 | // SubstitutionRecord represents a substitution record in a DS1 file. 4 | type SubstitutionRecord struct { 5 | Unknown uint32 6 | } 7 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/tile_record.go: -------------------------------------------------------------------------------- 1 | package d2ds1 2 | 3 | // TileRecord represents a tile record in a DS1 file. 4 | type TileRecord struct { 5 | Floors []FloorShadowRecord // Collection of floor records 6 | Walls []WallRecord // Collection of wall records 7 | Shadows []FloorShadowRecord // Collection of shadow records 8 | Substitutions []SubstitutionRecord // Collection of substitutions 9 | } 10 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2ds1/wall_record.go: -------------------------------------------------------------------------------- 1 | package d2ds1 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // WallRecord represents a wall record. 6 | type WallRecord struct { 7 | Type d2enum.TileType 8 | Zero byte 9 | Prop1 byte 10 | Sequence byte 11 | Unknown1 byte 12 | Style byte 13 | Unknown2 byte 14 | Hidden bool 15 | RandomIndex byte 16 | YAdjust int 17 | } 18 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dt1/block.go: -------------------------------------------------------------------------------- 1 | package d2dt1 2 | 3 | // Block represents a DT1 block 4 | type Block struct { 5 | X int16 6 | Y int16 7 | GridX byte 8 | GridY byte 9 | Format BlockDataFormat 10 | EncodedData []byte 11 | Length int32 12 | FileOffset int32 13 | } 14 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dt1/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2dt1 provides functionality for loading/processing DT1 files. 2 | // https://d2mods.info/forum/viewtopic.php?t=65163 3 | package d2dt1 4 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dt1/material.go: -------------------------------------------------------------------------------- 1 | package d2dt1 2 | 3 | // MaterialFlags represents the material flags. Lots of unknowns for now... 4 | type MaterialFlags struct { 5 | Other bool 6 | Water bool 7 | WoodObject bool 8 | InsideStone bool 9 | OutsideStone bool 10 | Dirt bool 11 | Sand bool 12 | Wood bool 13 | Lava bool 14 | Snow bool 15 | } 16 | 17 | // NewMaterialFlags represents the material flags 18 | // nolint:gomnd // Binary values 19 | func NewMaterialFlags(data uint16) MaterialFlags { 20 | return MaterialFlags{ 21 | Other: data&0x0001 == 0x0001, 22 | Water: data&0x0002 == 0x0002, 23 | WoodObject: data&0x0004 == 0x0004, 24 | InsideStone: data&0x0008 == 0x0008, 25 | OutsideStone: data&0x0010 == 0x0010, 26 | Dirt: data&0x0020 == 0x0020, 27 | Sand: data&0x0040 == 0x0040, 28 | Wood: data&0x0080 == 0x0080, 29 | Lava: data&0x0100 == 0x0100, 30 | Snow: data&0x0400 == 0x0400, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dt1/subtile_test.go: -------------------------------------------------------------------------------- 1 | package d2dt1 2 | 3 | import ( 4 | "testing" 5 | 6 | testify "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewSubTile(t *testing.T) { 10 | assert := testify.New(t) 11 | data := []byte{1, 2, 4, 8, 16, 32, 64, 128} 12 | 13 | for i, b := range data { 14 | tile := NewSubTileFlags(b) 15 | assert.Equal(i == 0, tile.BlockWalk) 16 | assert.Equal(i == 1, tile.BlockLOS) 17 | assert.Equal(i == 2, tile.BlockJump) 18 | assert.Equal(i == 3, tile.BlockPlayerWalk) 19 | assert.Equal(i == 4, tile.Unknown1) 20 | assert.Equal(i == 5, tile.BlockLight) 21 | assert.Equal(i == 6, tile.Unknown2) 22 | assert.Equal(i == 7, tile.Unknown3) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2dt1/tile.go: -------------------------------------------------------------------------------- 1 | package d2dt1 2 | 3 | // Tile is a representation of a map tile 4 | type Tile struct { 5 | Direction int32 6 | RoofHeight int16 7 | MaterialFlags MaterialFlags 8 | Height int32 9 | Width int32 10 | Type int32 11 | Style int32 12 | Sequence int32 13 | RarityFrameIndex int32 14 | SubTileFlags [25]SubTileFlags 15 | blockHeaderPointer int32 16 | blockHeaderSize int32 17 | Blocks []Block 18 | } 19 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2mpq/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2mpq contains the functions for handling MPQ files. 2 | package d2mpq 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2mpq/mpq_data_stream.go: -------------------------------------------------------------------------------- 1 | package d2mpq 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 4 | 5 | var _ d2interface.DataStream = &MpqDataStream{} // Static check to confirm struct conforms to interface 6 | 7 | // MpqDataStream represents a stream for MPQ data. 8 | type MpqDataStream struct { 9 | stream *Stream 10 | } 11 | 12 | // Read reads data from the data stream 13 | func (m *MpqDataStream) Read(p []byte) (n int, err error) { 14 | totalRead, err := m.stream.Read(p, 0, uint32(len(p))) 15 | return int(totalRead), err 16 | } 17 | 18 | // Seek sets the position of the data stream 19 | func (m *MpqDataStream) Seek(offset int64, whence int) (int64, error) { 20 | m.stream.Position = uint32(offset + int64(whence)) 21 | return int64(m.stream.Position), nil 22 | } 23 | 24 | // Close closes the data stream 25 | func (m *MpqDataStream) Close() error { 26 | m.stream = nil 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2mpq/mpq_file_record.go: -------------------------------------------------------------------------------- 1 | package d2mpq 2 | 3 | // MpqFileRecord represents a file record in an MPQ 4 | type MpqFileRecord struct { 5 | MpqFile string 6 | IsPatch bool 7 | UnpatchedMpqFile string 8 | } 9 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2mpq/mpq_hash.go: -------------------------------------------------------------------------------- 1 | package d2mpq 2 | 3 | import "io" 4 | 5 | // Hash represents a hashed file entry in the MPQ file 6 | type Hash struct { // 16 bytes 7 | A uint32 8 | B uint32 9 | Locale uint16 10 | Platform uint16 11 | BlockIndex uint32 12 | } 13 | 14 | // Name64 returns part A and B as uint64 15 | func (h *Hash) Name64() uint64 { 16 | return uint64(h.A)<<32 | uint64(h.B) 17 | } 18 | 19 | //nolint:gomnd // number 20 | func (mpq *MPQ) readHashTable() error { 21 | if _, err := mpq.file.Seek(int64(mpq.header.HashTableOffset), io.SeekStart); err != nil { 22 | return err 23 | } 24 | 25 | hashData, err := decryptTable(mpq.file, mpq.header.HashTableEntries, "(hash table)") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | mpq.hashes = make(map[uint64]*Hash) 31 | 32 | for n, i := uint32(0), uint32(0); i < mpq.header.HashTableEntries; n, i = n+4, i+1 { 33 | e := &Hash{ 34 | A: hashData[n], 35 | B: hashData[n+1], 36 | // https://github.com/OpenDiablo2/OpenDiablo2/issues/812 37 | Locale: uint16(hashData[n+2] >> 16), //nolint:gomnd // // binary data 38 | Platform: uint16(hashData[n+2] & 0xFFFF), //nolint:gomnd // // binary data 39 | BlockIndex: hashData[n+3], 40 | } 41 | mpq.hashes[e.Name64()] = e 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2mpq/mpq_header.go: -------------------------------------------------------------------------------- 1 | package d2mpq 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | // Header Represents a MPQ file 10 | type Header struct { 11 | Magic [4]byte 12 | HeaderSize uint32 13 | ArchiveSize uint32 14 | FormatVersion uint16 15 | BlockSize uint16 16 | HashTableOffset uint32 17 | BlockTableOffset uint32 18 | HashTableEntries uint32 19 | BlockTableEntries uint32 20 | } 21 | 22 | func (mpq *MPQ) readHeader() error { 23 | if _, err := mpq.file.Seek(0, io.SeekStart); err != nil { 24 | return err 25 | } 26 | 27 | if err := binary.Read(mpq.file, binary.LittleEndian, &mpq.header); err != nil { 28 | return err 29 | } 30 | 31 | if string(mpq.header.Magic[:]) != "MPQ\x1A" { 32 | return errors.New("invalid mpq header") 33 | } 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2pl2/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2pl2 handles processing of PL2 palette files. 2 | package d2pl2 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2pl2/pl2.go: -------------------------------------------------------------------------------- 1 | package d2pl2 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/go-restruct/restruct" 7 | ) 8 | 9 | // PL2 represents a palette file. 10 | type PL2 struct { 11 | BasePalette PL2Palette 12 | 13 | LightLevelVariations [32]PL2PaletteTransform 14 | InvColorVariations [16]PL2PaletteTransform 15 | SelectedUintShift PL2PaletteTransform 16 | AlphaBlend [3][256]PL2PaletteTransform 17 | AdditiveBlend [256]PL2PaletteTransform 18 | MultiplicativeBlend [256]PL2PaletteTransform 19 | HueVariations [111]PL2PaletteTransform 20 | RedTones PL2PaletteTransform 21 | GreenTones PL2PaletteTransform 22 | BlueTones PL2PaletteTransform 23 | UnknownVariations [14]PL2PaletteTransform 24 | MaxComponentBlend [256]PL2PaletteTransform 25 | DarkendColorShift PL2PaletteTransform 26 | 27 | TextColors [13]PL2Color24Bits 28 | TextColorShifts [13]PL2PaletteTransform 29 | } 30 | 31 | // Load uses restruct to read the binary pl2 data into structs 32 | func Load(data []byte) (*PL2, error) { 33 | result := &PL2{} 34 | 35 | restruct.EnableExprBeta() 36 | 37 | err := restruct.Unpack(data, binary.LittleEndian, &result) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return result, nil 43 | } 44 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2pl2/pl2_color.go: -------------------------------------------------------------------------------- 1 | package d2pl2 2 | 3 | // PL2Color represents an RGBA color 4 | type PL2Color struct { 5 | R uint8 6 | G uint8 7 | B uint8 8 | _ uint8 9 | } 10 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2pl2/pl2_color_24bits.go: -------------------------------------------------------------------------------- 1 | package d2pl2 2 | 3 | // PL2Color24Bits represents an RGB color 4 | type PL2Color24Bits struct { 5 | R uint8 6 | G uint8 7 | B uint8 8 | } 9 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2pl2/pl2_palette.go: -------------------------------------------------------------------------------- 1 | package d2pl2 2 | 3 | // PL2Palette represents a PL2 palette. 4 | type PL2Palette struct { 5 | Colors [256]PL2Color 6 | } 7 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2pl2/pl2_palette_transform.go: -------------------------------------------------------------------------------- 1 | package d2pl2 2 | 3 | // PL2PaletteTransform represents a PL2 palette transform. 4 | type PL2PaletteTransform struct { 5 | Indices [256]uint8 6 | } 7 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2tbl/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2tbl provides a file parser for tbl string table files 2 | package d2tbl 3 | -------------------------------------------------------------------------------- /d2common/d2fileformats/d2txt/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2txt provides a parser implementation for diablo TSV data files 2 | package d2txt 3 | -------------------------------------------------------------------------------- /d2common/d2geom/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2geom is a utility package for anything related to geometry 2 | package d2geom 3 | -------------------------------------------------------------------------------- /d2common/d2geom/point.go: -------------------------------------------------------------------------------- 1 | package d2geom 2 | 3 | // Point represents a point 4 | type Point struct { 5 | X int 6 | Y int 7 | } 8 | 9 | // Pointf represents a point with float coordinates 10 | type Pointf struct { 11 | X float64 12 | Y float64 13 | } 14 | -------------------------------------------------------------------------------- /d2common/d2geom/rectangle.go: -------------------------------------------------------------------------------- 1 | package d2geom 2 | 3 | // Rectangle represents a rectangle 4 | type Rectangle struct { 5 | Left int 6 | Top int 7 | Width int 8 | Height int 9 | } 10 | 11 | // Bottom returns y of the bottom point of the rectangle 12 | func (v *Rectangle) Bottom() int { 13 | return v.Top + v.Height 14 | } 15 | 16 | // Right returns x of the right point of the rectangle 17 | func (v *Rectangle) Right() int { 18 | return v.Left + v.Width 19 | } 20 | 21 | // IsInRect returns if the given position is in the rectangle or not 22 | func (v *Rectangle) IsInRect(x, y int) bool { 23 | return x >= v.Left && x < v.Left+v.Width && y >= v.Top && y < v.Top+v.Height 24 | } 25 | -------------------------------------------------------------------------------- /d2common/d2geom/size.go: -------------------------------------------------------------------------------- 1 | package d2geom 2 | 3 | // Size represents a size 4 | type Size struct { 5 | Width, Height int 6 | } 7 | -------------------------------------------------------------------------------- /d2common/d2interface/animation.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 8 | ) 9 | 10 | // Animation is an animation 11 | type Animation interface { 12 | BindRenderer(Renderer) 13 | Clone() Animation 14 | SetSubLoop(startFrame, EndFrame int) 15 | Advance(elapsed float64) error 16 | GetCurrentFrameSurface() Surface 17 | Render(target Surface) 18 | RenderFromOrigin(target Surface, shadow bool) 19 | RenderSection(sfc Surface, bound image.Rectangle) 20 | GetFrameSize(frameIndex int) (int, int, error) 21 | GetCurrentFrameSize() (int, int) 22 | GetFrameBounds() (int, int) 23 | GetCurrentFrame() int 24 | GetFrameCount() int 25 | IsOnFirstFrame() bool 26 | IsOnLastFrame() bool 27 | GetDirectionCount() int 28 | SetDirection(directionIndex int) error 29 | GetDirection() int 30 | SetCurrentFrame(frameIndex int) error 31 | Rewind() 32 | PlayForward() 33 | PlayBackward() 34 | Pause() 35 | SetPlayLoop(loop bool) 36 | SetPlaySpeed(playSpeed float64) 37 | SetPlayLength(playLength float64) 38 | SetColorMod(colorMod color.Color) 39 | GetPlayedCount() int 40 | ResetPlayedCount() 41 | SetEffect(effect d2enum.DrawEffect) 42 | SetShadow(shadow bool) 43 | } 44 | -------------------------------------------------------------------------------- /d2common/d2interface/archive.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | // Archive is an abstract representation of a game archive file 4 | // For the original Diablo II, archives are always MPQ's, but 5 | // OpenDiablo2 can handle any kind of archive file as long as it 6 | // implements this interface 7 | type Archive interface { 8 | Path() string 9 | Contains(string) bool 10 | Size() uint32 11 | Close() error 12 | ReadFile(fileName string) ([]byte, error) 13 | ReadFileStream(fileName string) (DataStream, error) 14 | ReadTextFile(fileName string) (string, error) 15 | Listfile() ([]string, error) 16 | } 17 | -------------------------------------------------------------------------------- /d2common/d2interface/audio_provider.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | // AudioProvider is something that can play music, load audio files managed 4 | // by the asset manager, and set the game engine's volume levels 5 | type AudioProvider interface { 6 | PlayBGM(song string) 7 | LoadSound(sfx string, loop bool, bgm bool) (SoundEffect, error) 8 | SetVolumes(bgmVolume, sfxVolume float64) 9 | } 10 | -------------------------------------------------------------------------------- /d2common/d2interface/cache.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | // Cache stores arbitrary data for fast retrieval 4 | type Cache interface { 5 | SetVerbose(verbose bool) 6 | GetWeight() int 7 | GetBudget() int 8 | Insert(key string, value interface{}, weight int) error 9 | Retrieve(key string) (interface{}, bool) 10 | Clear() 11 | } 12 | 13 | // Cacher is something that has a cache 14 | type Cacher interface { 15 | ClearCache() 16 | GetCache() Cache 17 | } 18 | -------------------------------------------------------------------------------- /d2common/d2interface/data_stream.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | // DataStream is a data stream 4 | type DataStream interface { 5 | Read(p []byte) (n int, err error) 6 | Seek(offset int64, whence int) (int64, error) 7 | Close() error 8 | } 9 | -------------------------------------------------------------------------------- /d2common/d2interface/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2interface defines interfaces for the OpenDiablo2 engine 2 | package d2interface 3 | -------------------------------------------------------------------------------- /d2common/d2interface/input_events.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // HandlerEvent holds the qualifiers for a key or mouse event 6 | type HandlerEvent interface { 7 | KeyMod() d2enum.KeyMod 8 | ButtonMod() d2enum.MouseButtonMod 9 | X() int 10 | Y() int 11 | } 12 | 13 | // KeyEvent represents an event associated with a keyboard key 14 | type KeyEvent interface { 15 | HandlerEvent 16 | Key() d2enum.Key 17 | // Duration represents the number of frames this key has been pressed for 18 | Duration() int 19 | } 20 | 21 | // KeyCharsEvent represents an event associated with a keyboard character being pressed 22 | type KeyCharsEvent interface { 23 | HandlerEvent 24 | Chars() []rune 25 | } 26 | 27 | // MouseEvent represents a mouse event 28 | type MouseEvent interface { 29 | HandlerEvent 30 | Button() d2enum.MouseButton 31 | } 32 | 33 | // MouseMoveEvent represents a mouse movement event 34 | type MouseMoveEvent interface { 35 | HandlerEvent 36 | } 37 | -------------------------------------------------------------------------------- /d2common/d2interface/input_manager.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // InputManager manages an InputService 6 | type InputManager interface { 7 | Advance(elapsedTime, currentTime float64) error 8 | BindHandlerWithPriority(InputEventHandler, d2enum.Priority) error 9 | BindHandler(h InputEventHandler) error 10 | UnbindHandler(handler InputEventHandler) error 11 | } 12 | -------------------------------------------------------------------------------- /d2common/d2interface/map_entity.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" 4 | 5 | // MapEntity is something that can be positioned on and rendered on the game map 6 | type MapEntity interface { 7 | ID() string 8 | Render(target Surface) 9 | Advance(tickTime float64) 10 | GetPosition() d2vector.Position 11 | GetVelocity() d2vector.Vector 12 | GetSize() (width, height int) 13 | GetLayer() int 14 | GetPositionF() (float64, float64) 15 | Label() string 16 | Selectable() bool 17 | Highlight() 18 | } 19 | -------------------------------------------------------------------------------- /d2common/d2interface/navigate.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" 5 | ) 6 | 7 | // Navigator is used for transitioning between game screens 8 | type Navigator interface { 9 | ToMainMenu(errorMessageOptional ...string) 10 | ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, connHost string) 11 | ToCreateGame(filePath string, connType d2clientconnectiontype.ClientConnectionType, connHost string) 12 | ToCharacterSelect(connType d2clientconnectiontype.ClientConnectionType, connHost string) 13 | ToMapEngineTest(region int, level int) 14 | ToCredits() 15 | ToCinematics() 16 | } 17 | -------------------------------------------------------------------------------- /d2common/d2interface/palette.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | const numColors = 256 4 | 5 | // Color represents a color 6 | type Color interface { 7 | R() uint8 8 | G() uint8 9 | B() uint8 10 | A() uint8 11 | RGBA() uint32 12 | SetRGBA(uint32) 13 | BGRA() uint32 14 | SetBGRA(uint32) 15 | } 16 | 17 | // Palette is a color palette 18 | type Palette interface { 19 | NumColors() int 20 | GetColors() [numColors]Color 21 | GetColor(idx int) (Color, error) 22 | } 23 | -------------------------------------------------------------------------------- /d2common/d2interface/renderer.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | type RenderCallback = func(Surface) error 4 | 5 | type UpdateCallback = func() error 6 | 7 | // Renderer interface defines the functionality of a renderer 8 | type Renderer interface { 9 | GetRendererName() string 10 | SetWindowIcon(fileName string) 11 | Run(render RenderCallback, update UpdateCallback, width, height int, title string) error 12 | IsDrawingSkipped() bool 13 | CreateSurface(surface Surface) (Surface, error) 14 | NewSurface(width, height int) Surface 15 | IsFullScreen() bool 16 | SetFullScreen(fullScreen bool) 17 | SetVSyncEnabled(vsync bool) 18 | GetVSyncEnabled() bool 19 | GetCursorPos() (int, int) 20 | CurrentFPS() float64 21 | ShowPanicScreen(message string) 22 | Print(target interface{}, str string) error 23 | } 24 | -------------------------------------------------------------------------------- /d2common/d2interface/sound_effect.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | // SoundEffect is something that that the AudioProvider can Play or Stop 4 | type SoundEffect interface { 5 | Play() 6 | Stop() 7 | SetPan(pan float64) 8 | IsPlaying() bool 9 | SetVolume(volume float64) 10 | } 11 | -------------------------------------------------------------------------------- /d2common/d2interface/surface.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 8 | ) 9 | 10 | // Surface represents a renderable surface. 11 | type Surface interface { 12 | Renderer() Renderer 13 | Clear(color color.Color) error 14 | DrawRect(width, height int, color color.Color) 15 | DrawLine(x, y int, color color.Color) 16 | DrawTextf(format string, params ...interface{}) 17 | GetSize() (width, height int) 18 | GetDepth() int 19 | Pop() 20 | PopN(n int) 21 | PushColor(color color.Color) 22 | PushEffect(effect d2enum.DrawEffect) 23 | PushFilter(filter d2enum.Filter) 24 | PushTranslation(x, y int) 25 | PushSkew(x, y float64) 26 | PushScale(x, y float64) 27 | PushBrightness(brightness float64) 28 | PushSaturation(saturation float64) 29 | Render(surface Surface) error 30 | // Renders a section of the surface enclosed by bounds 31 | RenderSection(surface Surface, bound image.Rectangle) error 32 | ReplacePixels(pixels *[]byte) error 33 | Screenshot() *image.RGBA 34 | } 35 | -------------------------------------------------------------------------------- /d2common/d2interface/terminal.go: -------------------------------------------------------------------------------- 1 | package d2interface 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // Terminal is a drop-down terminal and shell 6 | // It is used throughout the codebase, most parts of the engine will 7 | // `bind` commands, which are available for use in the shell 8 | type Terminal interface { 9 | BindLogger() 10 | 11 | Advance(elapsed float64) error 12 | OnKeyDown(event KeyEvent) bool 13 | OnKeyChars(event KeyCharsEvent) bool 14 | Render(surface Surface) error 15 | Execute(command string) error 16 | Rawf(category d2enum.TermCategory, format string, params ...interface{}) 17 | Printf(format string, params ...interface{}) 18 | Infof(format string, params ...interface{}) 19 | Warningf(format string, params ...interface{}) 20 | Errorf(format string, params ...interface{}) 21 | Clear() 22 | Visible() bool 23 | Hide() 24 | Show() 25 | Bind(name, description string, arguments []string, fn func(args []string) error) error 26 | Unbind(name ...string) error 27 | } 28 | 29 | // TerminalLogger is used tomake the Terminal write out 30 | // (eg. to the system shell or to a file) 31 | type TerminalLogger interface { 32 | Write(p []byte) (int, error) 33 | } 34 | -------------------------------------------------------------------------------- /d2common/d2loader/asset/asset.go: -------------------------------------------------------------------------------- 1 | package asset 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader/asset/types" 8 | ) 9 | 10 | // Asset represents a game asset. It has a type, an asset source, a sub-path (within the 11 | // asset source), and it can read data and seek within the data 12 | type Asset interface { 13 | fmt.Stringer 14 | io.Reader 15 | io.Seeker 16 | io.Closer 17 | Type() types.AssetType 18 | Source() Source 19 | Path() string 20 | Data() ([]byte, error) 21 | } 22 | -------------------------------------------------------------------------------- /d2common/d2loader/asset/doc.go: -------------------------------------------------------------------------------- 1 | // Package asset provides interfaces for Asset and Source 2 | package asset 3 | -------------------------------------------------------------------------------- /d2common/d2loader/asset/source.go: -------------------------------------------------------------------------------- 1 | package asset 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // Source is an abstraction for something that can load and list assets 9 | type Source interface { 10 | fmt.Stringer 11 | Open(name string) (io.ReadSeeker, error) 12 | Path() string 13 | Exists(subPath string) bool 14 | } 15 | -------------------------------------------------------------------------------- /d2common/d2loader/asset/types/asset_types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "strings" 4 | 5 | // AssetType represents the type of an asset 6 | type AssetType int 7 | 8 | // Asset types 9 | const ( 10 | AssetTypeUnknown AssetType = iota 11 | AssetTypeJSON 12 | AssetTypeStringTable 13 | AssetTypeDataDictionary 14 | AssetTypePalette 15 | AssetTypePaletteTransform 16 | AssetTypeCOF 17 | AssetTypeDC6 18 | AssetTypeDCC 19 | AssetTypeDS1 20 | AssetTypeDT1 21 | AssetTypeWAV 22 | AssetTypeD2 23 | ) 24 | 25 | // Ext2AssetType determines the AssetType with the given file extension 26 | func Ext2AssetType(ext string) AssetType { 27 | ext = strings.ToLower(ext) 28 | ext = strings.ReplaceAll(ext, ".", "") 29 | 30 | lookup := map[string]AssetType{ 31 | "json": AssetTypeJSON, 32 | "tbl": AssetTypeStringTable, 33 | "txt": AssetTypeDataDictionary, 34 | "dat": AssetTypePalette, 35 | "pl2": AssetTypePaletteTransform, 36 | "cof": AssetTypeCOF, 37 | "dc6": AssetTypeDC6, 38 | "dcc": AssetTypeDCC, 39 | "ds1": AssetTypeDS1, 40 | "dt1": AssetTypeDT1, 41 | "wav": AssetTypeWAV, 42 | "d2": AssetTypeD2, 43 | } 44 | 45 | if knownType, found := lookup[ext]; found { 46 | return knownType 47 | } 48 | 49 | return AssetTypeUnknown 50 | } 51 | -------------------------------------------------------------------------------- /d2common/d2loader/asset/types/doc.go: -------------------------------------------------------------------------------- 1 | // Package types provides an enumeration of Asset and Source types, as well as some utility 2 | // functions 3 | package types 4 | -------------------------------------------------------------------------------- /d2common/d2loader/asset/types/source_types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq" 8 | ) 9 | 10 | // SourceType represents the type of the asset source 11 | type SourceType int 12 | 13 | // Asset sources 14 | const ( 15 | AssetSourceUnknown SourceType = iota 16 | AssetSourceFileSystem 17 | AssetSourceMPQ 18 | ) 19 | 20 | // Ext2SourceType returns the SourceType from the given file extension 21 | func Ext2SourceType(ext string) SourceType { 22 | ext = strings.ToLower(ext) 23 | ext = strings.ReplaceAll(ext, ".", "") 24 | 25 | lookup := map[string]SourceType{ 26 | "mpq": AssetSourceMPQ, 27 | } 28 | 29 | if knownType, found := lookup[ext]; found { 30 | return knownType 31 | } 32 | 33 | return AssetSourceUnknown 34 | } 35 | 36 | // CheckSourceType attempts to determine the source type of the source 37 | func CheckSourceType(path string) SourceType { 38 | // on MacOS, the MPQ's from blizzard don't have file extensions 39 | // so we just attempt to init the file as an mpq 40 | if mpq, err := d2mpq.New(path); err == nil { 41 | _ = mpq.Close() 42 | return AssetSourceMPQ 43 | } 44 | 45 | ext := filepath.Ext(path) 46 | 47 | return Ext2SourceType(ext) 48 | } 49 | -------------------------------------------------------------------------------- /d2common/d2loader/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2loader provides a file loader which works in terms of `Source`s and `Asset`s. 2 | // A `Source` is something that resembles a filesystem, and an `Asset` is something that 3 | // implements `io.ReadSeeker`. 4 | package d2loader 5 | -------------------------------------------------------------------------------- /d2common/d2loader/filesystem/doc.go: -------------------------------------------------------------------------------- 1 | // Package filesystem provides a filesystem Asset and Source implementation for d2loader 2 | package filesystem 3 | -------------------------------------------------------------------------------- /d2common/d2loader/filesystem/loader_provider.go: -------------------------------------------------------------------------------- 1 | package filesystem 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader/asset" 5 | ) 6 | 7 | // OnAddSource is a shim method to allow loading of filesystem sources 8 | func OnAddSource(path string) (asset.Source, error) { 9 | return &Source{ 10 | Root: path, 11 | }, nil 12 | } 13 | -------------------------------------------------------------------------------- /d2common/d2loader/mpq/doc.go: -------------------------------------------------------------------------------- 1 | // Package mpq provides an MPQ Asset and Source implementation for d2loader 2 | package mpq 3 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/A/common.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/A/exclusive_a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/B/common.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/B/exclusive_b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/C/common.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/C/exclusive_c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /d2common/d2loader/testdata/D.mpq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2common/d2loader/testdata/D.mpq -------------------------------------------------------------------------------- /d2common/d2math/d2vector/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2vector provides an implementation of a 2D Euclidean vector using float64 to store the two values. 2 | /* 3 | Vector uses d2math.Epsilon for approximate equality and comparison. Note: SetLength, Reflect, ReflectSurface and Rotate 4 | do not (per their unit tests) return exact values but ones within Epsilon range of the expected value.*/ 5 | package d2vector 6 | -------------------------------------------------------------------------------- /d2common/d2math/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2math provides mathematical functions not included in Golang's standard math library. 2 | /* 3 | The decimal numeric type used is float64. 4 | 5 | Math also dictates the threshold for approximate equality (d2math.Epsilon). This is currently used both for moving 6 | entities and for approximate floating point equality in vector functions. See d2vector.*/ 7 | package d2math 8 | -------------------------------------------------------------------------------- /d2common/d2path/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2path is a utility package for functionality related to map entity pathing 2 | package d2path 3 | -------------------------------------------------------------------------------- /d2common/d2path/path.go: -------------------------------------------------------------------------------- 1 | package d2path 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" 4 | 5 | // Path represents a path 6 | type Path struct { 7 | Position d2vector.Position 8 | Action int 9 | } 10 | -------------------------------------------------------------------------------- /d2common/d2resource/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2resource stores the paths of the resources inside the mpq files. 2 | package d2resource 3 | -------------------------------------------------------------------------------- /d2common/d2util/assets/noto_sans_mono_8x16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2common/d2util/assets/noto_sans_mono_8x16.bmp -------------------------------------------------------------------------------- /d2common/d2util/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2util is a utility package for general-purpose functions used frequently throughout the 2 | // codebase. 3 | package d2util 4 | -------------------------------------------------------------------------------- /d2common/d2util/palette.go: -------------------------------------------------------------------------------- 1 | package d2util 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 7 | ) 8 | 9 | // ImgIndexToRGBA converts the given indices byte slice and palette into 10 | // a byte slice of RGBA values 11 | func ImgIndexToRGBA(indexData []byte, palette d2interface.Palette) []byte { 12 | bytesPerPixel := 4 13 | colorData := make([]byte, len(indexData)*bytesPerPixel) 14 | 15 | for i := 0; i < len(indexData); i++ { 16 | // Index zero is hardcoded transparent regardless of palette 17 | if indexData[i] == 0 { 18 | continue 19 | } 20 | 21 | c, err := palette.GetColor(int(indexData[i])) 22 | if err != nil { 23 | log.Print(err) 24 | } 25 | 26 | colorData[i*bytesPerPixel] = c.R() 27 | colorData[i*bytesPerPixel+1] = c.G() 28 | colorData[i*bytesPerPixel+2] = c.B() 29 | colorData[i*bytesPerPixel+3] = c.A() 30 | } 31 | 32 | return colorData 33 | } 34 | -------------------------------------------------------------------------------- /d2common/d2util/rgba_color.go: -------------------------------------------------------------------------------- 1 | package d2util 2 | 3 | import "image/color" 4 | 5 | // Color converts an rgba uint32 to a colorEnabled.RGBA 6 | func Color(rgba uint32) color.RGBA { 7 | result := color.RGBA{} 8 | a, b, g, r := 0, 1, 2, 3 9 | byteWidth := 8 10 | byteMask := 0xff 11 | 12 | for idx := 0; idx < 4; idx++ { 13 | shift := idx * byteWidth 14 | component := uint8(rgba>>shift) & uint8(byteMask) 15 | 16 | switch idx { 17 | case a: 18 | result.A = component 19 | case b: 20 | result.B = component 21 | case g: 22 | result.G = component 23 | case r: 24 | result.R = component 25 | } 26 | } 27 | 28 | return result 29 | } 30 | -------------------------------------------------------------------------------- /d2common/d2util/timeutils.go: -------------------------------------------------------------------------------- 1 | package d2util 2 | 3 | import "time" 4 | 5 | const ( 6 | nanoseconds = 1000000000.0 7 | ) 8 | 9 | // Now returns how many seconds have elapsed since Unix time (January 1, 1970 UTC) 10 | func Now() float64 { 11 | // Unix time in nanoseconds divided by how many nanoseconds in a second 12 | return float64(time.Now().UnixNano()) / nanoseconds 13 | } 14 | -------------------------------------------------------------------------------- /d2common/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2common provides common stuff for all of the other 2 | // high-level packages in the codebase 3 | package d2common 4 | -------------------------------------------------------------------------------- /d2core/d2asset/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2asset has behaviors to load and save assets from disk. 2 | package d2asset 3 | -------------------------------------------------------------------------------- /d2core/d2audio/create.go: -------------------------------------------------------------------------------- 1 | package d2audio 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio/sdl2" 8 | 9 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 10 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" 11 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" 12 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio/ebiten" 13 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" 14 | ) 15 | 16 | func Create(logLevel d2util.LogLevel, assetManager *d2asset.AssetManager, config *d2config.Configuration) d2interface.AudioProvider { 17 | switch strings.ToUpper(config.Backend) { 18 | case "EBITEN": 19 | return ebiten.Create(logLevel, assetManager) 20 | case "SDL2": 21 | return sdl2.Create(logLevel, assetManager) 22 | default: 23 | panic(fmt.Errorf("no audio provider available for backend %s", config.Backend)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /d2core/d2audio/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2audio provides AudioProvider implementations 2 | package d2audio 3 | -------------------------------------------------------------------------------- /d2core/d2audio/sdl2/sdl2_audio_provider.go: -------------------------------------------------------------------------------- 1 | package sdl2 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" 6 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" 7 | ) 8 | 9 | const sampleRate = 22050 10 | const logPrefix = "SDL2 Audio Provider" 11 | 12 | var _ d2interface.AudioProvider = &SDL2AudioProvider{} // Static check to confirm struct conforms to interface 13 | 14 | type SDL2AudioProvider struct { 15 | asset *d2asset.AssetManager 16 | logger *d2util.Logger 17 | } 18 | 19 | func Create(l d2util.LogLevel, am *d2asset.AssetManager) *SDL2AudioProvider { 20 | result := &SDL2AudioProvider{ 21 | asset: am, 22 | } 23 | 24 | result.logger = d2util.NewLogger() 25 | result.logger.SetLevel(l) 26 | result.logger.SetPrefix(logPrefix) 27 | 28 | return result 29 | } 30 | 31 | func (a SDL2AudioProvider) PlayBGM(song string) { 32 | 33 | } 34 | 35 | func (a SDL2AudioProvider) LoadSound(sfx string, loop bool, bgm bool) (d2interface.SoundEffect, error) { 36 | return CreateSoundEffect() 37 | } 38 | 39 | func (a SDL2AudioProvider) SetVolumes(bgmVolume, sfxVolume float64) { 40 | 41 | } 42 | -------------------------------------------------------------------------------- /d2core/d2audio/sdl2/sdl2_sound_effect.go: -------------------------------------------------------------------------------- 1 | package sdl2 2 | 3 | type SDL2SoundEffect struct { 4 | } 5 | 6 | func CreateSoundEffect() (*SDL2SoundEffect, error) { 7 | result := &SDL2SoundEffect{} 8 | 9 | return result, nil 10 | } 11 | 12 | func (S SDL2SoundEffect) Play() { 13 | 14 | } 15 | 16 | func (S SDL2SoundEffect) Stop() { 17 | 18 | } 19 | 20 | func (S SDL2SoundEffect) SetPan(pan float64) { 21 | 22 | } 23 | 24 | func (S SDL2SoundEffect) IsPlaying() bool { 25 | return true 26 | } 27 | 28 | func (S SDL2SoundEffect) SetVolume(volume float64) { 29 | 30 | } 31 | -------------------------------------------------------------------------------- /d2core/d2config/default_directories.go: -------------------------------------------------------------------------------- 1 | package d2config 2 | 3 | import ( 4 | "os" 5 | "path" 6 | ) 7 | 8 | const ( 9 | od2ConfigDirName = "OpenDiablo2" 10 | ) 11 | 12 | const ( 13 | od2ConfigFileName = "config.json" 14 | ) 15 | 16 | // DefaultConfigPath returns the absolute path for the default config file location 17 | func DefaultConfigPath() string { 18 | if configDir, err := os.UserConfigDir(); err == nil { 19 | return path.Join(configDir, od2ConfigDirName, od2ConfigFileName) 20 | } 21 | 22 | return LocalConfigPath() 23 | } 24 | 25 | // LocalConfigPath returns the absolute path to the directory of the OpenDiablo2 executable 26 | func LocalConfigPath() string { 27 | return path.Join(path.Dir(os.Args[0]), od2ConfigFileName) 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2config/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2config contains configuration management for OpenDiablo2 2 | package d2config 3 | -------------------------------------------------------------------------------- /d2core/d2gui/button.go: -------------------------------------------------------------------------------- 1 | package d2gui 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 5 | ) 6 | 7 | type buttonState int 8 | 9 | const ( 10 | buttonStateDefault buttonState = iota 11 | buttonStatePressed 12 | buttonStatePressedToggled 13 | ) 14 | 15 | const ( 16 | grey = 0x404040ff 17 | ) 18 | 19 | // Button is a user actionable drawable toggle switch 20 | type Button struct { 21 | widgetBase 22 | 23 | width int 24 | height int 25 | state buttonState 26 | surfaces []d2interface.Surface 27 | } 28 | 29 | func (b *Button) onMouseButtonDown(_ d2interface.MouseEvent) bool { 30 | b.state = buttonStatePressed 31 | 32 | return false 33 | } 34 | 35 | func (b *Button) onMouseButtonUp(_ d2interface.MouseEvent) bool { 36 | b.state = buttonStateDefault 37 | 38 | return false 39 | } 40 | 41 | func (b *Button) onMouseLeave(_ d2interface.MouseMoveEvent) bool { 42 | b.state = buttonStateDefault 43 | 44 | return false 45 | } 46 | 47 | func (b *Button) render(target d2interface.Surface) { 48 | target.Render(b.surfaces[b.state]) 49 | } 50 | 51 | func (b *Button) getSize() (width, height int) { 52 | return b.width, b.height 53 | } 54 | -------------------------------------------------------------------------------- /d2core/d2gui/common.go: -------------------------------------------------------------------------------- 1 | package d2gui 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" 6 | ) 7 | 8 | func renderSegmented(animation d2interface.Animation, segmentsX, segmentsY, frameOffset int, 9 | target d2interface.Surface) error { 10 | var currentY int 11 | 12 | for y := 0; y < segmentsY; y++ { 13 | var currentX, maxHeight int 14 | 15 | for x := 0; x < segmentsX; x++ { 16 | if err := animation.SetCurrentFrame(x + y*segmentsX + frameOffset*segmentsX*segmentsY); err != nil { 17 | return err 18 | } 19 | 20 | target.PushTranslation(x+currentX, y+currentY) 21 | animation.Render(target) 22 | target.Pop() 23 | 24 | width, height := animation.GetCurrentFrameSize() 25 | maxHeight = d2math.MaxInt(maxHeight, height) 26 | currentX += width 27 | } 28 | 29 | currentY += maxHeight 30 | } 31 | 32 | return nil 33 | } 34 | 35 | func half(n int) int { 36 | return n / 2 37 | } 38 | -------------------------------------------------------------------------------- /d2core/d2gui/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2gui provides higher-level ui component management 2 | package d2gui 3 | -------------------------------------------------------------------------------- /d2core/d2gui/layout_entry.go: -------------------------------------------------------------------------------- 1 | package d2gui 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 6 | ) 7 | 8 | type layoutEntry struct { 9 | widget widget 10 | 11 | x int 12 | y int 13 | width int 14 | height int 15 | 16 | mouseOver bool 17 | mouseDown [3]bool 18 | } 19 | 20 | // IsIn layout entry, spc. of an event. 21 | func (l *layoutEntry) IsIn(event d2interface.HandlerEvent) bool { 22 | sx, sy := l.widget.ScreenPos() 23 | rect := d2geom.Rectangle{Left: sx, Top: sy, Width: l.width, Height: l.height} 24 | 25 | return rect.IsInRect(event.X(), event.Y()) 26 | } 27 | -------------------------------------------------------------------------------- /d2core/d2gui/spacer.go: -------------------------------------------------------------------------------- 1 | package d2gui 2 | 3 | // SpacerStatic is a spacer with explicit width and height, meaning 4 | // that it wont dynamically expand within a layout 5 | type SpacerStatic struct { 6 | widgetBase 7 | 8 | width int 9 | height int 10 | } 11 | 12 | func createSpacerStatic(width, height int) *SpacerStatic { 13 | spacer := &SpacerStatic{width: width, height: height} 14 | spacer.SetVisible(true) 15 | 16 | return spacer 17 | } 18 | 19 | func (s *SpacerStatic) getSize() (width, height int) { 20 | return s.width, s.height 21 | } 22 | 23 | // SpacerDynamic is a spacer that will expand within a layout, 24 | // depending on the layout position and alignment types 25 | type SpacerDynamic struct { 26 | widgetBase 27 | } 28 | 29 | func createSpacerDynamic() *SpacerDynamic { 30 | spacer := &SpacerDynamic{} 31 | spacer.SetVisible(true) 32 | spacer.SetExpanding(true) 33 | 34 | return spacer 35 | } 36 | -------------------------------------------------------------------------------- /d2core/d2hero/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2hero utilities for managing a hero state. 2 | package d2hero 3 | -------------------------------------------------------------------------------- /d2core/d2hero/hero_skill_util.go: -------------------------------------------------------------------------------- 1 | package d2hero 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" 4 | 5 | // HydrateSkills will load the SkillRecord & SkillDescriptionRecord from the asset manager, using the skill ID. 6 | // This is done to avoid serializing the whole record data of HeroSkill to a game save or network packets. 7 | // We cant do this while unmarshalling because there is no reference to the asset manager. 8 | func HydrateSkills(skills map[int]*HeroSkill, asset *d2asset.AssetManager) { 9 | for skillID, skill := range skills { 10 | skill.SkillRecord = asset.Records.Skill.Details[skillID] 11 | skill.SkillDescriptionRecord = asset.Records.Skill.Descriptions[skill.SkillRecord.Skilldesc] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /d2core/d2hero/hero_state.go: -------------------------------------------------------------------------------- 1 | package d2hero 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" 6 | ) 7 | 8 | // HeroState stores the state of the player 9 | type HeroState struct { 10 | HeroName string `json:"heroName"` 11 | HeroType d2enum.Hero `json:"heroType"` 12 | Act int `json:"act"` 13 | FilePath string `json:"-"` 14 | Equipment d2inventory.CharacterEquipment `json:"equipment"` 15 | Stats *HeroStatsState `json:"stats"` 16 | Skills map[int]*HeroSkill `json:"skills"` 17 | X float64 `json:"x"` 18 | Y float64 `json:"y"` 19 | LeftSkill int `json:"leftSkill"` 20 | RightSkill int `json:"rightSkill"` 21 | Gold int `json:"Gold"` 22 | Difficulty d2enum.DifficultyType `json:"difficulty"` 23 | } 24 | -------------------------------------------------------------------------------- /d2core/d2input/d2input.go: -------------------------------------------------------------------------------- 1 | package d2input 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 7 | ) 8 | 9 | var ( 10 | // ErrHasReg shows the input system already has a registered handler 11 | ErrHasReg = errors.New("input system already has provided handler") 12 | // ErrNotReg shows the input system has no registered handler 13 | ErrNotReg = errors.New("input system does not have provided handler") 14 | ) 15 | 16 | // Static checks to confirm struct conforms to interface 17 | var _ d2interface.InputEventHandler = &HandlerEvent{} 18 | var _ d2interface.KeyEvent = &KeyEvent{} 19 | var _ d2interface.KeyCharsEvent = &KeyCharsEvent{} 20 | var _ d2interface.MouseEvent = &MouseEvent{} 21 | var _ d2interface.MouseMoveEvent = &MouseMoveEvent{} 22 | -------------------------------------------------------------------------------- /d2core/d2input/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2input provides an input manager 2 | package d2input 3 | -------------------------------------------------------------------------------- /d2core/d2input/handler_event.go: -------------------------------------------------------------------------------- 1 | package d2input 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 5 | ) 6 | 7 | // HandlerEvent is an event that EventHandlers will process and respond to 8 | type HandlerEvent struct { 9 | keyMod d2enum.KeyMod 10 | buttonMod d2enum.MouseButtonMod 11 | x int 12 | y int 13 | } 14 | 15 | // KeyMod yields the modifier for a key action 16 | func (e *HandlerEvent) KeyMod() d2enum.KeyMod { 17 | return e.keyMod 18 | } 19 | 20 | // ButtonMod yields the modifier for a button action 21 | func (e *HandlerEvent) ButtonMod() d2enum.MouseButtonMod { 22 | return e.buttonMod 23 | } 24 | 25 | // X returns the x screen coordinate for the event 26 | func (e *HandlerEvent) X() int { 27 | return e.x 28 | } 29 | 30 | // Y returns the y screen coordinate for the event 31 | func (e *HandlerEvent) Y() int { 32 | return e.y 33 | } 34 | -------------------------------------------------------------------------------- /d2core/d2input/key_event.go: -------------------------------------------------------------------------------- 1 | package d2input 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // KeyEvent represents key events 6 | type KeyEvent struct { 7 | HandlerEvent 8 | key d2enum.Key 9 | // Duration represents the number of frames this key has been pressed for 10 | duration int 11 | } 12 | 13 | // Key returns the key 14 | func (e *KeyEvent) Key() d2enum.Key { 15 | return e.key 16 | } 17 | 18 | // Duration returns the duration 19 | func (e *KeyEvent) Duration() int { 20 | return e.duration 21 | } 22 | -------------------------------------------------------------------------------- /d2core/d2input/keychars_event.go: -------------------------------------------------------------------------------- 1 | package d2input 2 | 3 | // KeyCharsEvent represents a key character event 4 | type KeyCharsEvent struct { 5 | HandlerEvent 6 | chars []rune 7 | } 8 | 9 | // Chars returns the characters 10 | func (e *KeyCharsEvent) Chars() []rune { 11 | return e.chars 12 | } 13 | -------------------------------------------------------------------------------- /d2core/d2input/mouse_event.go: -------------------------------------------------------------------------------- 1 | package d2input 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // MouseEvent represents a mouse event 6 | type MouseEvent struct { 7 | HandlerEvent 8 | mouseButton d2enum.MouseButton 9 | } 10 | 11 | // KeyMod returns the key mod 12 | func (e *MouseEvent) KeyMod() d2enum.KeyMod { 13 | return e.HandlerEvent.keyMod 14 | } 15 | 16 | // ButtonMod represents a button mod 17 | func (e *MouseEvent) ButtonMod() d2enum.MouseButtonMod { 18 | return e.HandlerEvent.buttonMod 19 | } 20 | 21 | // X returns the event's X position 22 | func (e *MouseEvent) X() int { 23 | return e.HandlerEvent.x 24 | } 25 | 26 | // Y returns the event's Y position 27 | func (e *MouseEvent) Y() int { 28 | return e.HandlerEvent.y 29 | } 30 | 31 | // Button returns the mouse button 32 | func (e *MouseEvent) Button() d2enum.MouseButton { 33 | return e.mouseButton 34 | } 35 | -------------------------------------------------------------------------------- /d2core/d2input/mousemove_event.go: -------------------------------------------------------------------------------- 1 | package d2input 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // MouseMoveEvent represents a mouse movement event 6 | type MouseMoveEvent struct { 7 | HandlerEvent 8 | } 9 | 10 | // KeyMod represents the key mod 11 | func (e *MouseMoveEvent) KeyMod() d2enum.KeyMod { 12 | return e.HandlerEvent.keyMod 13 | } 14 | 15 | // ButtonMod represents the button mod 16 | func (e *MouseMoveEvent) ButtonMod() d2enum.MouseButtonMod { 17 | return e.HandlerEvent.buttonMod 18 | } 19 | 20 | // X represents the X position 21 | func (e *MouseMoveEvent) X() int { 22 | return e.HandlerEvent.x 23 | } 24 | 25 | // Y represents the Y position 26 | func (e *MouseMoveEvent) Y() int { 27 | return e.HandlerEvent.y 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2inventory/character_equipment.go: -------------------------------------------------------------------------------- 1 | package d2inventory 2 | 3 | // CharacterEquipment stores equipments of a character 4 | type CharacterEquipment struct { 5 | Head *InventoryItemArmor `json:"head"` // Head 6 | Torso *InventoryItemArmor `json:"torso"` // TR 7 | Legs *InventoryItemArmor `json:"legs"` // Legs 8 | RightArm *InventoryItemArmor `json:"rightArm"` // RA 9 | LeftArm *InventoryItemArmor `json:"leftArm"` // LA 10 | LeftHand *InventoryItemWeapon `json:"leftHand"` // LH 11 | RightHand *InventoryItemWeapon `json:"rightHand"` // RH 12 | Shield *InventoryItemArmor `json:"shield"` // SH 13 | // S1-S8? 14 | } 15 | -------------------------------------------------------------------------------- /d2core/d2inventory/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2inventory provides representations of player inventory 2 | package d2inventory 3 | -------------------------------------------------------------------------------- /d2core/d2inventory/hero_objects.go: -------------------------------------------------------------------------------- 1 | package d2inventory 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 5 | ) 6 | 7 | // HeroObjects map contains the hero type to CharacterEquipments 8 | type HeroObjects map[d2enum.Hero]CharacterEquipment 9 | -------------------------------------------------------------------------------- /d2core/d2inventory/inventory_item.go: -------------------------------------------------------------------------------- 1 | package d2inventory 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // InventoryItem defines the functionality of an inventory item 6 | type InventoryItem interface { 7 | // GetInventoryItemName returns the name of this inventory item 8 | GetInventoryItemName() string 9 | // GetInventoryItemType returns the type of item this is 10 | GetInventoryItemType() d2enum.InventoryItemType 11 | // GetInventoryGridSize returns the width/height grid size of this inventory item 12 | GetInventoryGridSize() (int, int) 13 | // Returns the item code 14 | GetItemCode() string 15 | // Serializes the object for transport 16 | Serialize() []byte 17 | } 18 | -------------------------------------------------------------------------------- /d2core/d2item/context.go: -------------------------------------------------------------------------------- 1 | package d2item 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2core/d2stats" 4 | 5 | // StatContext is anything which has a `StatList` method which yields a StatList. 6 | // This is used for resolving stat dependencies for showing actual values, like 7 | // stats that are based off of the current character level 8 | type StatContext interface { 9 | Equipper 10 | BaseStatList() d2stats.StatList 11 | StatList() d2stats.StatList 12 | } 13 | -------------------------------------------------------------------------------- /d2core/d2item/diablo2item/doc.go: -------------------------------------------------------------------------------- 1 | // Package diablo2item provides the Diablo 2 implementation of items for 2 | // the OpenDiablo2 interfaces 3 | package diablo2item 4 | -------------------------------------------------------------------------------- /d2core/d2item/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2item provides a generic interface for what the OpenDiablo2 2 | // engine considers to be an item. 3 | package d2item 4 | -------------------------------------------------------------------------------- /d2core/d2item/equipper.go: -------------------------------------------------------------------------------- 1 | package d2item 2 | 3 | // Equipper is an interface for something that can equip items 4 | type Equipper interface { 5 | EquippedItems() []Item 6 | CarriedItems() []Item 7 | } 8 | -------------------------------------------------------------------------------- /d2core/d2item/item.go: -------------------------------------------------------------------------------- 1 | package d2item 2 | 3 | // Item describes all types of item that can be placed in the 4 | // player inventory grid (not just things that can be equipped!) 5 | type Item interface { 6 | Context() StatContext 7 | SetContext(StatContext) 8 | 9 | Label() string 10 | Description() string 11 | } 12 | -------------------------------------------------------------------------------- /d2core/d2map/d2mapengine/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2mapengine provides a map engine, something that 2 | // can place diablo tile data onto a plane. 3 | package d2mapengine 4 | -------------------------------------------------------------------------------- /d2core/d2map/d2mapengine/pathfind.go: -------------------------------------------------------------------------------- 1 | package d2mapengine 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" 7 | ) 8 | 9 | // PathFind finds a path between given start and dest positions and returns the positions of the path 10 | func (m *MapEngine) PathFind(start, dest d2vector.Position) []d2vector.Position { 11 | points := make([]d2vector.Position, 0) 12 | _, point := m.checkLos(start, dest) 13 | points = append(points, point) 14 | 15 | return points 16 | } 17 | 18 | // checkLos finds out if there is a clear line of sight between two points 19 | func (m *MapEngine) checkLos(start, end d2vector.Position) (bool, d2vector.Position) { 20 | dv := d2vector.Position{Vector: *end.Clone()} 21 | dv.Subtract(&start.Vector) 22 | dx := dv.X() 23 | dy := dv.Y() 24 | N := math.Max(math.Abs(dx), math.Abs(dy)) 25 | 26 | var divN float64 27 | if N == 0 { 28 | divN = 0.0 29 | } else { 30 | divN = 1.0 / N // nolint:gomnd // we're just taking inverse... 31 | } 32 | 33 | xstep := dx * divN 34 | ystep := dy * divN 35 | x := start.X() 36 | y := start.Y() 37 | 38 | for i := 0; i <= int(N); i++ { 39 | x += xstep 40 | y += ystep 41 | 42 | if m.SubTileAt(int(math.Floor(x)), int(math.Floor(y))).BlockWalk { 43 | return false, d2vector.NewPosition(x-xstep, y-ystep) 44 | } 45 | } 46 | 47 | return true, end 48 | } 49 | -------------------------------------------------------------------------------- /d2core/d2map/d2mapentity/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2mapentity provides representations of map entities 2 | package d2mapentity 3 | -------------------------------------------------------------------------------- /d2core/d2map/d2mapgen/d2wilderness/wilderness_tile_types.go: -------------------------------------------------------------------------------- 1 | // Package d2wilderness provides an enumeration of wilderness types 2 | package d2wilderness 3 | 4 | // nolint:golint // these don't require individual explanations. 5 | const ( 6 | TreeBorderSouth int = iota + 4 7 | TreeBorderWest 8 | TreeBorderNorth 9 | TreeBorderEast 10 | TreeBorderSouthWest 11 | TreeBorderNorthWest 12 | TreeBorderNorthEast 13 | TreeBorderSouthEast 14 | TreeBoxNorthEast 15 | TreeBoxSouthEast 16 | TreeBoxSouthWest 17 | TreeBoxNorthWest 18 | WallBorderWest 19 | WallBorderEast 20 | WallBorderWestFenceNorth 21 | WallBorderNorthWest 22 | WallBorderWestFenceSouth 23 | WallBorderNorthFenceEast 24 | WallBorderNorthFenceWest 25 | WallBoxSouthEast 26 | WallBorderNorthUndergroundPassageEntrance 27 | WallBorderWestUndergroundPassageEntrance 28 | WaterBorderEast 29 | WaterBorderWest 30 | WaterBridgeEast 31 | StoneFill1 32 | StoneFill2 33 | CorralFill 34 | RandomTreesAndWallBoxLarge 35 | TreeBoxNorthSouth 36 | ShrineWithFenceAndTrees 37 | RandomTreesAndWallBoxSmall 38 | TreeBoxWestEastWithNorthSouthPath 39 | TreeBoxNorthSouthWithEastWestPath 40 | SwampFill1 41 | SwampFill2 42 | TreeFill 43 | Ruin 44 | FallenCamp1 45 | FallenCamp2 46 | FallenCampBishbosh 47 | Camp 48 | Pond 49 | Cottages1 50 | Cottages2 51 | Cottages3 52 | Bivouac 53 | CaveEntrance 54 | DenOfEvilEntrance 55 | ) 56 | -------------------------------------------------------------------------------- /d2core/d2map/d2mapgen/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2mapgen provides map generator implementations for use with the map engine. 2 | package d2mapgen 3 | -------------------------------------------------------------------------------- /d2core/d2map/d2maprenderer/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2maprenderer provides a renderer for the map engine 2 | package d2maprenderer 3 | -------------------------------------------------------------------------------- /d2core/d2map/d2mapstamp/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2mapstamp provides a representation of a preset map 2 | // which can be used "like a stamp" by the map engine 3 | package d2mapstamp 4 | -------------------------------------------------------------------------------- /d2core/d2records/armor_type_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func armorTypesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(ArmorTypes) 9 | 10 | for d.Next() { 11 | record := &ArmorTypeRecord{ 12 | Name: d.String("Name"), 13 | Token: d.String("Token"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Animation.Token.Armor = records 24 | 25 | r.Debugf("Loaded %d ArmorType records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/armor_type_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ArmorTypes is a map of ArmorTypeRecords 4 | type ArmorTypes map[string]*ArmorTypeRecord 5 | 6 | // ArmorTypeRecord describes an armor type. It has a name and 3-character token. 7 | // The token is used to change the character animation mode. 8 | type ArmorTypeRecord struct { 9 | Name string 10 | Token string 11 | } 12 | -------------------------------------------------------------------------------- /d2core/d2records/automap_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func autoMapLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(AutoMaps, 0) 9 | 10 | var frameFields = []string{"Cel1", "Cel2", "Cel3", "Cel4"} 11 | 12 | for d.Next() { 13 | record := &AutoMapRecord{ 14 | LevelName: d.String("LevelName"), 15 | TileName: d.String("TileName"), 16 | 17 | Style: d.Number("Style"), 18 | StartSequence: d.Number("StartSequence"), 19 | EndSequence: d.Number("EndSequence"), 20 | 21 | // Note: aren't useful see the AutoMapRecord struct. 22 | //Type1: d.String("Type1"), 23 | //Type2: d.String("Type2"), 24 | //Type3: d.String("Type3"), 25 | //Type4: d.String("Type4"), 26 | } 27 | record.Frames = make([]int, len(frameFields)) 28 | 29 | for i := range frameFields { 30 | record.Frames[i] = d.Number(frameFields[i]) 31 | } 32 | 33 | records = append(records, record) 34 | } 35 | 36 | if d.Err != nil { 37 | return d.Err 38 | } 39 | 40 | r.Debugf("Loaded %d AutoMap records", len(records)) 41 | 42 | r.Level.AutoMaps = records 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /d2core/d2records/body_locations_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func bodyLocationsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(BodyLocations) 9 | 10 | for d.Next() { 11 | location := &BodyLocationRecord{ 12 | Name: d.String("Name"), 13 | Code: d.String("Code"), 14 | } 15 | records[location.Code] = location 16 | } 17 | 18 | if d.Err != nil { 19 | panic(d.Err) 20 | } 21 | 22 | r.Debugf("Loaded %d BodyLocation records", len(records)) 23 | 24 | r.BodyLocations = records 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/body_locations_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // BodyLocations contains the body location records 4 | type BodyLocations map[string]*BodyLocationRecord 5 | 6 | // BodyLocationRecord describes a body location that items can be equipped to 7 | type BodyLocationRecord struct { 8 | Name string 9 | Code string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/books_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func booksLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(Books) 9 | 10 | for d.Next() { 11 | record := &BookRecord{ 12 | Name: d.String("Name"), 13 | Namco: d.String("Namco"), 14 | Completed: d.String("Completed"), 15 | ScrollSpellCode: d.String("ScrollSpellCode"), 16 | BookSpellCode: d.String("BooksSpellCode"), 17 | Pspell: d.Number("pSpell"), 18 | SpellIcon: d.Number("SpellIcon"), 19 | ScrollSkill: d.String("ScrollSkill"), 20 | BookSkill: d.String("BookSkill"), 21 | BaseCost: d.Number("BaseCost"), 22 | CostPerCharge: d.Number("CostPerCharge"), 23 | } 24 | records[record.Namco] = record 25 | } 26 | 27 | if d.Err != nil { 28 | panic(d.Err) 29 | } 30 | 31 | r.Debugf("Loaded %d Book records", len(records)) 32 | 33 | r.Item.Books = records 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /d2core/d2records/books_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Books stores all of the BookRecords 4 | type Books map[string]*BookRecord 5 | 6 | // BookRecord is a representation of a row from books.txt 7 | type BookRecord struct { 8 | Name string 9 | Namco string // The displayed name, where the string prefix is "Tome" 10 | Completed string 11 | ScrollSpellCode string 12 | BookSpellCode string 13 | Pspell int 14 | SpellIcon int 15 | ScrollSkill string 16 | BookSkill string 17 | BaseCost int 18 | CostPerCharge int 19 | } 20 | -------------------------------------------------------------------------------- /d2core/d2records/calculations_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func skillCalcLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records, err := loadCalculations(r, d, "Skill") 9 | if err != nil { 10 | return err 11 | } 12 | 13 | r.Calculation.Skills = records 14 | 15 | return nil 16 | } 17 | 18 | func missileCalcLoader(r *RecordManager, d *d2txt.DataDictionary) error { 19 | records, err := loadCalculations(r, d, "Missile") 20 | if err != nil { 21 | return err 22 | } 23 | 24 | r.Calculation.Missiles = records 25 | 26 | return nil 27 | } 28 | 29 | func loadCalculations(r *RecordManager, d *d2txt.DataDictionary, name string) (Calculations, error) { 30 | records := make(Calculations) 31 | 32 | for d.Next() { 33 | record := &CalculationRecord{ 34 | Code: d.String("code"), 35 | Description: d.String("*desc"), 36 | } 37 | records[record.Code] = record 38 | } 39 | 40 | if d.Err != nil { 41 | return nil, d.Err 42 | } 43 | 44 | r.Debugf("Loaded %d %s Calculation records", len(records), name) 45 | 46 | return records, nil 47 | } 48 | -------------------------------------------------------------------------------- /d2core/d2records/calculations_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Calculations is where calculation records are stored 4 | type Calculations map[string]*CalculationRecord 5 | 6 | // CalculationRecord The skillcalc.txt and misscalc.txt files are essentially lookup tables 7 | // for the Skills.txt and Missiles.txt Calc functions To avoid duplication (since they have 8 | // identical fields) they are both represented by the CalculationRecord type 9 | type CalculationRecord struct { 10 | Code string 11 | Description string 12 | } 13 | -------------------------------------------------------------------------------- /d2core/d2records/color_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func colorsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(Colors) 9 | 10 | for d.Next() { 11 | record := &ColorRecord{ 12 | TransformColor: d.String("Transform Color"), 13 | Code: d.String("Code"), 14 | } 15 | 16 | records[record.TransformColor] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Colors = records 24 | 25 | r.Debugf("Loaded %d Color records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/color_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Colors is a map of ColorRecords 4 | type Colors map[string]*ColorRecord 5 | 6 | // ColorRecord is a representation of a color transform 7 | type ColorRecord struct { 8 | TransformColor string 9 | Code string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/component_codes_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func componentCodesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(ComponentCodes) 9 | 10 | for d.Next() { 11 | record := &ComponentCodeRecord{ 12 | Component: d.String("component"), 13 | Code: d.String("code"), 14 | } 15 | records[record.Component] = record 16 | } 17 | 18 | if d.Err != nil { 19 | return d.Err 20 | } 21 | 22 | r.Debugf("Loaded %d ComponentCode records", len(records)) 23 | 24 | r.ComponentCodes = records 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/component_codes_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ComponentCodes is a lookup table for DCC Animation Component Subtype, 4 | // it links hardcoded data with the txt files 5 | type ComponentCodes map[string]*ComponentCodeRecord 6 | 7 | // ComponentCodeRecord represents a single row from compcode.txt 8 | type ComponentCodeRecord struct { 9 | Component string 10 | Code string 11 | } 12 | -------------------------------------------------------------------------------- /d2core/d2records/composite_type_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func compositeTypeLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(CompositeTypes) 9 | 10 | for d.Next() { 11 | record := &CompositeTypeRecord{ 12 | Name: d.String("Name"), 13 | Token: d.String("Token"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Animation.Token.Composite = records 24 | 25 | r.Debugf("Loaded %d CompositeType records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/composite_type_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // CompositeTypes is a map of CompositeTypeRecords 4 | type CompositeTypes map[string]*CompositeTypeRecord 5 | 6 | // CompositeTypeRecord describes a layer for an animated composite (multi-sprite entities). 7 | // The token is used for changing character animation modes. 8 | type CompositeTypeRecord struct { 9 | Name string 10 | Token string 11 | } 12 | -------------------------------------------------------------------------------- /d2core/d2records/constants.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // these show up in a lot of txt files where blizzard added LoD expansion stuff 4 | const ( 5 | expansionString = "Expansion" // used in the txt files to denote where LoD expansion data starts 6 | expansionCode = 100 // a version code for LoD expansion content 7 | ) 8 | -------------------------------------------------------------------------------- /d2core/d2records/cube_modifier_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func cubeModifierLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(CubeModifiers) 9 | 10 | for d.Next() { 11 | record := &CubeModifierRecord{ 12 | Name: d.String("cube modifier type"), 13 | Token: d.String("Code"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Item.Cube.Modifiers = records 24 | 25 | r.Debugf("Loaded %d CubeModifier records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/cube_modifier_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // CubeModifiers is a map of CubeModifierRecords 4 | type CubeModifiers map[string]*CubeModifierRecord 5 | 6 | // CubeModifierRecord is a name and 3-character token for cube modifier codes and gem types 7 | type CubeModifierRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/cube_type_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func cubeTypeLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(CubeTypes) 9 | 10 | for d.Next() { 11 | record := &CubeTypeRecord{ 12 | Name: d.String("cube item class"), 13 | Token: d.String("Code"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Item.Cube.Types = records 24 | 25 | r.Debugf("Loaded %d CubeType records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/cube_type_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // CubeTypes is a map of CubeTypeRecords 4 | type CubeTypes map[string]*CubeTypeRecord 5 | 6 | // CubeTypeRecord is a name and 3-character token for cube item types 7 | type CubeTypeRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2records provides a RecordManager implementation which is used to parse 2 | // the various txt files from the d2 mpq archives. Each data dictionary (txt file) is 3 | // parsed into slices or maps of structs. There is a struct type defined for each txt file. 4 | // 5 | // The RecordManager is a singleton that loads all of the txt files and export them as 6 | // data members. The RecordManager is meant to be used a a singleton member, exported by the 7 | // AssetManager in d2core/d2asset. 8 | package d2records 9 | -------------------------------------------------------------------------------- /d2core/d2records/elemtype_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadElemTypes loads ElemTypeRecords into ElemTypes 8 | func elemTypesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(ElemTypes) 10 | 11 | for d.Next() { 12 | record := &ElemTypeRecord{ 13 | ElemType: d.String("Elemental Type"), 14 | Code: d.String("Code"), 15 | } 16 | records[record.ElemType] = record 17 | } 18 | 19 | if d.Err != nil { 20 | return d.Err 21 | } 22 | 23 | r.Debugf("Loaded %d ElemType records", len(records)) 24 | 25 | r.ElemTypes = records 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/elemtype_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ElemTypes stores the ElemTypeRecords 4 | type ElemTypes map[string]*ElemTypeRecord 5 | 6 | // ElemTypeRecord represents a single line in ElemType.txt 7 | type ElemTypeRecord struct { 8 | // ElemType Elemental damage type name 9 | ElemType string 10 | 11 | // Code Elemental damage type code 12 | Code string 13 | } 14 | -------------------------------------------------------------------------------- /d2core/d2records/events_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadEvents loads all of the event records from events.txt 8 | func eventsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(Events, 0) 10 | 11 | for d.Next() { 12 | record := &EventRecord{ 13 | Event: d.String("event"), 14 | } 15 | 16 | records = append(records, record) 17 | } 18 | 19 | if d.Err != nil { 20 | return d.Err 21 | } 22 | 23 | r.Debugf("Loaded %d Event records", len(records)) 24 | 25 | r.Character.Events = records 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/events_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Events holds all of the event records from events.txt 4 | type Events []*EventRecord 5 | 6 | // EventRecord is a representation of a single row from events.txt 7 | type EventRecord struct { 8 | Event string 9 | } 10 | -------------------------------------------------------------------------------- /d2core/d2records/experience_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // ExperienceBreakpoints describes the required experience 6 | // for each level for each character class 7 | type ExperienceBreakpoints map[int]*ExperienceBreakpointRecord 8 | 9 | // ExperienceMaxLevels defines the max character levels 10 | type ExperienceMaxLevels map[d2enum.Hero]int 11 | 12 | // ExperienceBreakpointRecord describes the experience points required to 13 | // gain a level for all character classes 14 | type ExperienceBreakpointRecord struct { 15 | Level int 16 | HeroBreakpoints map[d2enum.Hero]int 17 | Ratio int 18 | } 19 | -------------------------------------------------------------------------------- /d2core/d2records/gamble_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func gambleLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(Gamble) 9 | 10 | for d.Next() { 11 | record := &GambleRecord{ 12 | Name: d.String("name"), 13 | Code: d.String("code"), 14 | } 15 | records[record.Name] = record 16 | } 17 | 18 | if d.Err != nil { 19 | return d.Err 20 | } 21 | 22 | r.Debugf("Loaded %d Gamble records", len(records)) 23 | 24 | r.Gamble = records 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/gamble_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Gamble is a map of GambleRecords 4 | type Gamble map[string]*GambleRecord 5 | 6 | // GambleRecord is a representation of an item type that can be gambled for at vendors 7 | type GambleRecord struct { 8 | Name string 9 | Code string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/gems_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Gems stores all of the GemRecords 4 | type Gems map[string]*GemRecord 5 | 6 | // GemRecord is a representation of a single row of gems.txt 7 | // it describes the properties of socketable items 8 | type GemRecord struct { 9 | Name string 10 | Letter string 11 | Transform int 12 | Code string 13 | Nummods int 14 | WeaponMod1Code string 15 | WeaponMod1Param int 16 | WeaponMod1Min int 17 | WeaponMod1Max int 18 | WeaponMod2Code string 19 | WeaponMod2Param int 20 | WeaponMod2Min int 21 | WeaponMod2Max int 22 | WeaponMod3Code string 23 | WeaponMod3Param int 24 | WeaponMod3Min int 25 | WeaponMod3Max int 26 | HelmMod1Code string 27 | HelmMod1Param int 28 | HelmMod1Min int 29 | HelmMod1Max int 30 | HelmMod2Code string 31 | HelmMod2Param int 32 | HelmMod2Min int 33 | HelmMod2Max int 34 | HelmMod3Code string 35 | HelmMod3Param int 36 | HelmMod3Min int 37 | HelmMod3Max int 38 | ShieldMod1Code string 39 | ShieldMod1Param int 40 | ShieldMod1Min int 41 | ShieldMod1Max int 42 | ShieldMod2Code string 43 | ShieldMod2Param int 44 | ShieldMod2Min int 45 | ShieldMod2Max int 46 | ShieldMod3Code string 47 | ShieldMod3Param int 48 | ShieldMod3Min int 49 | ShieldMod3Max int 50 | } 51 | -------------------------------------------------------------------------------- /d2core/d2records/hireling_description_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func hirelingDescriptionLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(HirelingDescriptions) 9 | 10 | for d.Next() { 11 | record := &HirelingDescriptionRecord{ 12 | Name: d.String("Hireling Description"), 13 | Token: d.String("Code"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Hireling.Descriptions = records 24 | 25 | r.Debugf("Loaded %d HirelingDescription records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/hireling_description_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // HirelingDescriptions is a lookup table for hireling subtype codes 4 | type HirelingDescriptions map[string]*HirelingDescriptionRecord 5 | 6 | // HirelingDescriptionRecord represents is a hireling subtype 7 | type HirelingDescriptionRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/hit_class_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func hitClassLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(HitClasses) 9 | 10 | for d.Next() { 11 | record := &HitClassRecord{ 12 | Name: d.String("Hit Class"), 13 | Token: d.String("Code"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Animation.Token.HitClass = records 24 | 25 | r.Debugf("Loaded %d HitClass records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/hit_class_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // HitClasses is a map of HitClassRecords 4 | type HitClasses map[string]*HitClassRecord 5 | 6 | // HitClassRecord is used for changing character animation modes. 7 | type HitClassRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/inventory_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 4 | 5 | // Inventory holds all of the inventory records from inventory.txt 6 | type Inventory map[string]*InventoryRecord //nolint:gochecknoglobals // Currently global by design 7 | 8 | // InventoryRecord represents a single row from inventory.txt, it describes the grid 9 | // layout and positioning of various inventory-related ui panels. 10 | type InventoryRecord struct { 11 | Name string 12 | Panel *box 13 | Grid *grid 14 | Slots map[d2enum.EquippedSlot]*box 15 | } 16 | 17 | type box struct { 18 | Left int 19 | Right int 20 | Top int 21 | Bottom int 22 | Width int 23 | Height int 24 | } 25 | 26 | type grid struct { 27 | Box *box 28 | Rows int 29 | Columns int 30 | CellWidth int 31 | CellHeight int 32 | } 33 | -------------------------------------------------------------------------------- /d2core/d2records/item_armor_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 7 | ) 8 | 9 | func armorLoader(r *RecordManager, d *d2txt.DataDictionary) error { 10 | if r.Item.Armors != nil { 11 | return nil // already loaded 12 | } 13 | 14 | records, err := loadCommonItems(d, d2enum.InventoryItemTypeArmor) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | r.Debugf("Loaded %d Armor Item records", len(records)) 20 | 21 | r.Item.Armors = records 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /d2core/d2records/item_low_quality_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func lowQualityLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(LowQualities, 0) 9 | 10 | for d.Next() { 11 | record := &LowQualityRecord{ 12 | Name: d.String("Hireling Description"), 13 | } 14 | 15 | records = append(records, record) 16 | } 17 | 18 | if d.Err != nil { 19 | panic(d.Err) 20 | } 21 | 22 | r.Item.LowQualityPrefixes = records 23 | 24 | r.Debugf("Loaded %d LowQuality records", len(records)) 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/item_low_quality_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // LowQualities is a slice of LowQualityRecords 4 | type LowQualities []*LowQualityRecord 5 | 6 | // LowQualityRecord is a name prefix that can be used for low quality item names 7 | type LowQualityRecord struct { 8 | Name string 9 | } 10 | -------------------------------------------------------------------------------- /d2core/d2records/item_misc_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 7 | ) 8 | 9 | // LoadMiscItems loads ItemCommonRecords from misc.txt 10 | func miscItemsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 11 | records, err := loadCommonItems(d, d2enum.InventoryItemTypeItem) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | r.Debugf("Loaded %d Misc Item records", len(records)) 17 | 18 | r.Item.Misc = records 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /d2core/d2records/item_quality_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ItemQualities stores all of the QualityRecords 4 | type ItemQualities map[string]*ItemQualityRecord 5 | 6 | // ItemQualityRecord represents a single row of ItemQualities.txt, which controls 7 | // properties for superior quality items 8 | type ItemQualityRecord struct { 9 | NumMods int 10 | Mod1Code string 11 | Mod1Param int 12 | Mod1Min int 13 | Mod1Max int 14 | Mod2Code string 15 | Mod2Param int 16 | Mod2Min int 17 | Mod2Max int 18 | 19 | // The following fields determine this row's applicability to 20 | // categories of item. 21 | Armor bool 22 | Weapon bool 23 | Shield bool 24 | Thrown bool 25 | Scepter bool 26 | Wand bool 27 | Staff bool 28 | Bow bool 29 | Boots bool 30 | Gloves bool 31 | Belt bool 32 | 33 | Level int 34 | Multiply int 35 | Add int 36 | } 37 | -------------------------------------------------------------------------------- /d2core/d2records/item_ratio_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ItemRatios holds all of the ItemRatioRecords from ItemRatio.txt 4 | type ItemRatios map[string]*ItemRatioRecord 5 | 6 | // DropRatioInfo is a helper struct for item drop calculation 7 | type DropRatioInfo struct { 8 | Frequency int 9 | Divisor int 10 | DivisorMin int 11 | } 12 | 13 | // ItemRatioRecord encapsulates information found in ItemRatio.txt, it specifies drop ratios 14 | // for various types of items 15 | // The information has been gathered from [https://d2mods.info/forum/kb/viewarticle?a=387] 16 | type ItemRatioRecord struct { 17 | Function string 18 | // 0 for classic, 1 for LoD 19 | Version bool 20 | 21 | // 0 for normal, 1 for exceptional 22 | Uber bool 23 | ClassSpecific bool 24 | 25 | // All following fields are used in item drop calculation 26 | UniqueDropInfo DropRatioInfo 27 | RareDropInfo DropRatioInfo 28 | SetDropInfo DropRatioInfo 29 | MagicDropInfo DropRatioInfo 30 | HiQualityDropInfo DropRatioInfo 31 | NormalDropInfo DropRatioInfo 32 | } 33 | -------------------------------------------------------------------------------- /d2core/d2records/item_weapons_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 7 | ) 8 | 9 | // LoadWeapons loads weapon records 10 | func weaponsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 11 | records, err := loadCommonItems(d, d2enum.InventoryItemTypeWeapon) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | r.Debugf("Loaded %d Weapon records", len(records)) 17 | 18 | r.Item.Weapons = records 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /d2core/d2records/level_maze_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func levelMazeDetailsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(LevelMazeDetails) 9 | 10 | for d.Next() { 11 | record := &LevelMazeDetailRecord{ 12 | Name: d.String("Name"), 13 | LevelID: d.Number("Level"), 14 | NumRoomsNormal: d.Number("Rooms"), 15 | NumRoomsNightmare: d.Number("Rooms(N)"), 16 | NumRoomsHell: d.Number("Rooms(H)"), 17 | SizeX: d.Number("SizeX"), 18 | SizeY: d.Number("SizeY"), 19 | } 20 | records[record.LevelID] = record 21 | } 22 | 23 | if d.Err != nil { 24 | return d.Err 25 | } 26 | 27 | r.Debugf("Loaded %d LevelMazeDetail records", len(records)) 28 | 29 | r.Level.Maze = records 30 | 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /d2core/d2records/level_maze_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // LevelMazeDetails stores all of the LevelMazeDetailRecords 4 | type LevelMazeDetails map[int]*LevelMazeDetailRecord 5 | 6 | // LevelMazeDetailRecord is a representation of a row from lvlmaze.txt 7 | // these records define the parameters passed to the maze level generator 8 | type LevelMazeDetailRecord struct { 9 | // descriptive, not loaded in game. Corresponds with Name field in 10 | // Levels.txt 11 | Name string // Name 12 | 13 | // ID from Levels.txt 14 | // NOTE: Cave 1 is the Den of Evil, its associated treasure level is quest 15 | // only. 16 | LevelID int // Level 17 | 18 | // the minimum number of .ds1 map sections that will make up the maze in 19 | // Normal, Nightmare and Hell difficulties. 20 | NumRoomsNormal int // Rooms 21 | NumRoomsNightmare int // Rooms(N) 22 | NumRoomsHell int // Rooms(H) 23 | 24 | // the size in the X\Y direction of any component ds1 map section. 25 | SizeX int // SizeX 26 | SizeY int // SizeY 27 | 28 | // Possibly related to how adjacent .ds1s are connected with each other, 29 | // but what the different values are for is unknown. 30 | // Merge int // Merge 31 | 32 | // Included in the original Diablo II beta tests and in the demo version. 33 | // Beta 34 | } 35 | -------------------------------------------------------------------------------- /d2core/d2records/level_presets_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // LevelPresets stores all of the LevelPresetRecords 4 | type LevelPresets map[int]LevelPresetRecord 5 | 6 | // LevelPresetRecord is a representation of a row from lvlprest.txt 7 | // these records define parameters for the preset level map generator 8 | type LevelPresetRecord struct { 9 | Files [6]string 10 | Name string 11 | DefinitionID int 12 | LevelID int 13 | SizeX int 14 | SizeY int 15 | Pops int 16 | PopPad int 17 | FileCount int 18 | Dt1Mask uint 19 | Populate bool 20 | Logicals bool 21 | Outdoors bool 22 | Animate bool 23 | KillEdge bool 24 | FillBlanks bool 25 | AutoMap bool 26 | Scan bool 27 | Beta bool 28 | Expansion bool 29 | } 30 | -------------------------------------------------------------------------------- /d2core/d2records/level_types_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // LevelTypes stores all of the LevelTypeRecords 4 | type LevelTypes []*LevelTypeRecord 5 | 6 | // LevelTypeRecord is a representation of a row from lvltype.txt 7 | // the fields describe what ds1 files a level uses 8 | type LevelTypeRecord struct { 9 | Files [32]string 10 | Name string 11 | ID int 12 | Act int 13 | Beta bool 14 | Expansion bool 15 | } 16 | -------------------------------------------------------------------------------- /d2core/d2records/level_warp_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func levelWarpsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(LevelWarps) 9 | 10 | for d.Next() { 11 | record := &LevelWarpRecord{ 12 | Name: d.String("Name"), 13 | ID: d.Number("Id"), 14 | SelectX: d.Number("SelectX"), 15 | SelectY: d.Number("SelectY"), 16 | SelectDX: d.Number("SelectDX"), 17 | SelectDY: d.Number("SelectDY"), 18 | ExitWalkX: d.Number("ExitWalkX"), 19 | ExitWalkY: d.Number("ExitWalkY"), 20 | OffsetX: d.Number("OffsetX"), 21 | OffsetY: d.Number("OffsetY"), 22 | LitVersion: d.Bool("LitVersion"), 23 | Tiles: d.Number("Tiles"), 24 | Direction: d.String("Direction"), 25 | } 26 | records[record.ID] = record 27 | } 28 | 29 | if d.Err != nil { 30 | return d.Err 31 | } 32 | 33 | r.Debugf("Loaded %d LevelWarp records", len(records)) 34 | 35 | r.Level.Warp = records 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /d2core/d2records/level_warp_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // LevelWarps loaded from txt records 4 | type LevelWarps map[int]*LevelWarpRecord 5 | 6 | // LevelWarpRecord is a representation of a row from lvlwarp.txt 7 | // it describes the warp graphics offsets and dimensions for levels 8 | type LevelWarpRecord struct { 9 | Name string 10 | ID int 11 | SelectX int 12 | SelectY int 13 | SelectDX int 14 | SelectDY int 15 | ExitWalkX int 16 | ExitWalkY int 17 | OffsetX int 18 | OffsetY int 19 | LitVersion bool 20 | Tiles int 21 | Direction string 22 | } 23 | -------------------------------------------------------------------------------- /d2core/d2records/monster_ai_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadMonsterAI loads MonsterAIRecords from monai.txt 8 | func monsterAiLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(MonsterAI) 10 | 11 | for d.Next() { 12 | record := &MonsterAIRecord{ 13 | AI: d.String("AI"), 14 | } 15 | records[record.AI] = record 16 | } 17 | 18 | if d.Err != nil { 19 | return d.Err 20 | } 21 | 22 | r.Debugf("Loaded %d MonsterAI records", len(records)) 23 | 24 | r.Monster.AI = records 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/monster_ai_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // MonsterAI holds the MonsterAIRecords, The monai.txt file is a lookup table for unit AI codes 4 | type MonsterAI map[string]*MonsterAIRecord 5 | 6 | // MonsterAIRecord represents a single row from monai.txt 7 | type MonsterAIRecord struct { 8 | AI string 9 | } 10 | -------------------------------------------------------------------------------- /d2core/d2records/monster_equipment_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | const ( 4 | numMonEquippedItems = 3 5 | fmtLocation = "loc%d" 6 | fmtQuality = "mod%d" 7 | fmtCode = "item%d" 8 | ) 9 | 10 | // MonsterEquipment stores the MonsterEquipmentRecords 11 | type MonsterEquipment map[string][]*MonsterEquipmentRecord 12 | 13 | // MonsterEquipmentRecord represents a single line in monequip.txt 14 | // Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=365] 15 | type MonsterEquipmentRecord struct { 16 | // Name of monster, pointer to MonStats.txt 17 | Name string 18 | 19 | // If true, monster is created by level, otherwise created by skill 20 | OnInit bool 21 | 22 | // Not written in description, only appear on monsters with OnInit false, 23 | // Level of skill for which this equipment row can be used? 24 | Level int 25 | 26 | Equipment []*monEquip 27 | } 28 | 29 | type monEquip struct { 30 | // Code of item, probably from ItemCommonRecords 31 | Code string 32 | 33 | // Location the body location of the item 34 | Location string 35 | 36 | // Quality of the item 37 | Quality int 38 | } 39 | -------------------------------------------------------------------------------- /d2core/d2records/monster_mode_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadMonModes loads monster records 8 | func monsterModeLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(MonModes) 10 | 11 | for d.Next() { 12 | record := &MonModeRecord{ 13 | Name: d.String("name"), 14 | Token: d.String("token"), 15 | Code: d.String("code"), 16 | } 17 | records[record.Name] = record 18 | } 19 | 20 | if d.Err != nil { 21 | return d.Err 22 | } 23 | 24 | r.Debugf("Loaded %d MonMode records", len(records)) 25 | 26 | r.Monster.Modes = records 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /d2core/d2records/monster_mode_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // MonModes stores all of the MonModeRecords 4 | type MonModes map[string]*MonModeRecord 5 | 6 | // MonModeRecord is a representation of a single row of Monmode.txt 7 | type MonModeRecord struct { 8 | Name string 9 | Token string 10 | Code string 11 | } 12 | -------------------------------------------------------------------------------- /d2core/d2records/monster_placement_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadMonsterPlacements loads the MonsterPlacementRecords into MonsterPlacements. 8 | func monsterPlacementsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(MonsterPlacements, 0) 10 | 11 | for d.Next() { 12 | records = append(records, MonsterPlacementRecord(d.String("code"))) 13 | } 14 | 15 | if d.Err != nil { 16 | return d.Err 17 | } 18 | 19 | r.Monster.Placements = records 20 | 21 | r.Debugf("Loaded %d MonsterPlacement records", len(records)) 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /d2core/d2records/monster_placement_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // MonsterPlacements stores the MonsterPlacementRecords. 4 | type MonsterPlacements []MonsterPlacementRecord 5 | 6 | // MonsterPlacementRecord represents a line from MonPlace.txt. 7 | type MonsterPlacementRecord string 8 | -------------------------------------------------------------------------------- /d2core/d2records/monster_preset_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadMonPresets loads monster presets from monpresets.txt 8 | func monsterPresetLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(MonPresets) 10 | 11 | for d.Next() { 12 | act := int32(d.Number("Act")) 13 | if _, ok := records[act]; !ok { 14 | records[act] = make([]string, 0) 15 | } 16 | 17 | records[act] = append(records[act], d.String("Place")) 18 | } 19 | 20 | if d.Err != nil { 21 | return d.Err 22 | } 23 | 24 | r.Debugf("Loaded %d MonPreset records", len(records)) 25 | 26 | r.Monster.Presets = records 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /d2core/d2records/monster_preset_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // MonPresets stores monster presets 4 | type MonPresets map[int32][]string 5 | -------------------------------------------------------------------------------- /d2core/d2records/monster_property_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | const ( 4 | numMonProps = 6 5 | fmtProp = "prop%d%s" 6 | fmtChance = "chance%d%s" 7 | fmtPar = "par%d%s" 8 | fmtMin = "min%d%s" 9 | fmtMax = "max%d%s" 10 | fmtNormal = "" 11 | fmtNightmare = " (N)" 12 | fmtHell = " (H)" 13 | ) 14 | 15 | // MonsterProperties stores all of the MonPropRecords 16 | type MonsterProperties map[string]*MonPropRecord 17 | 18 | // MonPropRecord is a representation of a single row of monprop.txt 19 | type MonPropRecord struct { 20 | ID string 21 | 22 | Properties struct { 23 | Normal [numMonProps]*MonProp 24 | Nightmare [numMonProps]*MonProp 25 | Hell [numMonProps]*MonProp 26 | } 27 | } 28 | 29 | // MonProp is a monster property 30 | type MonProp struct { 31 | Code string 32 | Param string 33 | Chance int 34 | Min int 35 | Max int 36 | } 37 | -------------------------------------------------------------------------------- /d2core/d2records/monster_sequence_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadMonsterSequences loads the MonsterSequenceRecords into MonsterSequences 8 | func monsterSequencesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(MonsterSequences) 10 | 11 | for d.Next() { 12 | name := d.String("sequence") 13 | 14 | if _, ok := records[name]; !ok { 15 | record := &MonsterSequenceRecord{ 16 | Name: name, 17 | Frames: make([]*MonsterSequenceFrame, 0), 18 | } 19 | records[name] = record 20 | } 21 | 22 | records[name].Frames = append(records[name].Frames, &MonsterSequenceFrame{ 23 | Mode: d.String("mode"), 24 | Frame: d.Number("frame"), 25 | Direction: d.Number("dir"), 26 | Event: d.Number("event"), 27 | }) 28 | } 29 | 30 | if d.Err != nil { 31 | return d.Err 32 | } 33 | 34 | r.Debugf("Loaded %d MonsterSequence records", len(records)) 35 | 36 | r.Monster.Sequences = records 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /d2core/d2records/monster_sequence_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // MonsterSequences contains the MonsterSequenceRecords 4 | type MonsterSequences map[string]*MonsterSequenceRecord 5 | 6 | // MonsterSequenceRecord contains a record for a monster sequence 7 | // Composed of multiple lines from monseq.txt with the same name in the first column. 8 | // Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=395] 9 | type MonsterSequenceRecord struct { 10 | 11 | // Name of the sequence, referred to by monstats.txt 12 | Name string 13 | 14 | // Frames of this sequence 15 | Frames []*MonsterSequenceFrame 16 | } 17 | 18 | // MonsterSequenceFrame represents a single frame of a monster sequence 19 | type MonsterSequenceFrame struct { 20 | // The animation mode for this frame (refers to MonMode.txt) 21 | Mode string 22 | 23 | // The frame of the animation mode used for this frame of the sequence 24 | Frame int 25 | 26 | // Direction of the frame, enumerated by d2enum.AnimationFrameDirection 27 | Direction int 28 | 29 | // Event triggered by this frame 30 | Event int 31 | } 32 | -------------------------------------------------------------------------------- /d2core/d2records/monster_type_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func monsterTypesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(MonsterTypes) 9 | 10 | for d.Next() { 11 | record := &MonTypeRecord{ 12 | Type: d.String("type"), 13 | Equiv1: d.String("equiv1"), 14 | Equiv2: d.String("equiv2"), 15 | Equiv3: d.String("equiv3"), 16 | StrSing: d.String("strsing"), 17 | StrPlural: d.String("strplur"), 18 | } 19 | records[record.Type] = record 20 | } 21 | 22 | if d.Err != nil { 23 | panic(d.Err) 24 | } 25 | 26 | r.Debugf("Loaded %d MonType records", len(records)) 27 | 28 | r.Monster.Types = records 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /d2core/d2records/monster_type_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // MonsterTypes stores all of the MonTypeRecords 4 | type MonsterTypes map[string]*MonTypeRecord 5 | 6 | // MonTypeRecord is a representation of a single row of MonType.txt. 7 | type MonTypeRecord struct { 8 | Type string 9 | Equiv1 string 10 | Equiv2 string 11 | Equiv3 string 12 | // StrSing is the string displayed for the singular form (Skeleton), note 13 | // that this is unused in the original engine, since the only modifier 14 | // display code that accesses MonType uses StrPlur. 15 | StrSing string 16 | StrPlural string 17 | } 18 | -------------------------------------------------------------------------------- /d2core/d2records/monster_unique_affix_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/gravestench/akara" 5 | ) 6 | 7 | // UniqueMonsterPrefix is a representation of a possible name prefix for a unique monster instance 8 | // eg _Blood_ Wing the Quick 9 | type UniqueMonsterPrefix = UniqueMonsterAffixRecord 10 | 11 | // UniqueMonsterSuffix is a representation of a possible name suffix for a unique monster instance. 12 | // eg. Blood Wing _the Quick_ 13 | type UniqueMonsterSuffix = UniqueMonsterAffixRecord 14 | 15 | // UniqueMonsterAffixes is a map of UniqueMonsterAffixRecords. The key is the string table lookup key. 16 | type UniqueMonsterAffixes map[string]*UniqueMonsterAffixRecord 17 | 18 | // UniqueMonsterAffixRecord is a string table key and a bit vector for the possible monster types 19 | // that the suffix can be used with. 20 | type UniqueMonsterAffixRecord struct { 21 | StringTableKey string 22 | MonsterTypeFlags *akara.BitSet 23 | } 24 | -------------------------------------------------------------------------------- /d2core/d2records/npc_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | const ( 4 | costDivisor = 1024. 5 | ) 6 | 7 | // NPCs stores the NPCRecords 8 | type NPCs map[string]*NPCRecord 9 | 10 | // NPCRecord represents a single line in NPC.txt 11 | // The information has been gathered from [https:// d2mods.info/forum/kb/viewarticle?a=387] 12 | type NPCRecord struct { 13 | // Name is an ID pointer to row of this npc in monstats.txt 14 | Name string 15 | 16 | Multipliers *costMultiplier 17 | 18 | QuestMultipliers map[int]*costMultiplier 19 | 20 | // MaxBuy is the maximum amount of gold an NPC will pay for an item for the corresponding 21 | // difficulty 22 | MaxBuy struct { 23 | Normal int 24 | Nightmare int 25 | Hell int 26 | } 27 | } 28 | 29 | type costMultiplier struct { 30 | // Buy is a percentage of base item price used when an item is bought by NPC 31 | Buy float64 32 | 33 | // Sell is a percentage of base item price used when an item is sold by NPC 34 | Sell float64 35 | 36 | // Repair is a percentage of base item price used to calculate the base repair price 37 | Repair float64 38 | } 39 | -------------------------------------------------------------------------------- /d2core/d2records/object_mode_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func objectModesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(ObjectModes) 9 | 10 | for d.Next() { 11 | record := &ObjectModeRecord{ 12 | Name: d.String("Name"), 13 | Token: d.String("Token"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Object.Modes = records 24 | 25 | r.Debugf("Loaded %d ObjectMode records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/object_mode_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ObjectModes is a map of ObjectModeRecords 4 | type ObjectModes map[string]*ObjectModeRecord 5 | 6 | // ObjectModeRecord is a representation of an animation mode for an object 7 | type ObjectModeRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/object_types_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 7 | ) 8 | 9 | // LoadObjectTypes loads ObjectTypeRecords from objtype.txt 10 | func objectTypesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 11 | records := make(ObjectTypes, 0) 12 | 13 | for d.Next() { 14 | record := ObjectTypeRecord{ 15 | Name: sanitizeObjectString(d.String("Name")), 16 | Token: sanitizeObjectString(d.String("Token")), 17 | } 18 | 19 | records = append(records, record) 20 | } 21 | 22 | if d.Err != nil { 23 | return d.Err 24 | } 25 | 26 | r.Debugf("Loaded %d ObjectType records", len(records)) 27 | 28 | r.Object.Types = records 29 | 30 | return nil 31 | } 32 | 33 | func sanitizeObjectString(str string) string { 34 | result := strings.TrimSpace(strings.ReplaceAll(str, string(byte(0)), "")) 35 | result = strings.ToLower(result) 36 | 37 | return result 38 | } 39 | -------------------------------------------------------------------------------- /d2core/d2records/object_types_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // ObjectTypes contains the name and token for objects 4 | type ObjectTypes []ObjectTypeRecord 5 | 6 | // ObjectTypeRecord is a representation of a row from objtype.txt 7 | type ObjectTypeRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/overlays_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func overlaysLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(Overlays) 9 | 10 | for d.Next() { 11 | record := &OverlayRecord{ 12 | Name: d.String("Overlay"), 13 | Filename: d.String("Filename"), 14 | Version: d.Bool("Version"), 15 | PreDraw: d.Bool("PreDraw"), 16 | XOffset: d.Number("Xoffset"), 17 | YOffset: d.Number("Yoffset"), 18 | Height1: d.Number("Height1"), 19 | Height2: d.Number("Height1"), 20 | Height3: d.Number("Height1"), 21 | Height4: d.Number("Height1"), 22 | AnimRate: d.Number("AnimRate"), 23 | Trans: d.Number("Trans"), 24 | InitRadius: d.Number("InitRadius"), 25 | Radius: d.Number("Radius"), 26 | Red: uint8(d.Number("Red")), 27 | Green: uint8(d.Number("Green")), 28 | Blue: uint8(d.Number("Blue")), 29 | } 30 | 31 | records[record.Name] = record 32 | } 33 | 34 | if d.Err != nil { 35 | return d.Err 36 | } 37 | 38 | r.Debugf("Loaded %d Overlay records", len(records)) 39 | 40 | r.Layout.Overlays = records 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /d2core/d2records/pet_type_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func petTypesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(PetTypes) 9 | 10 | for d.Next() { 11 | record := &PetTypeRecord{ 12 | Name: d.String("pet type"), 13 | ID: d.Number("idx"), 14 | GroupID: d.Number("group"), 15 | BaseMax: d.Number("basemax"), 16 | Warp: d.Bool("warp"), 17 | Range: d.Bool("range"), 18 | PartySend: d.Bool("partysend"), 19 | Unsummon: d.Bool("unsummon"), 20 | Automap: d.Bool("automap"), 21 | IconName: d.String("name"), 22 | DrawHP: d.Bool("drawhp"), 23 | IconType: d.Number("icontype"), 24 | BaseIcon: d.String("baseicon"), 25 | MClass1: d.Number("mclass1"), 26 | MIcon1: d.String("micon1"), 27 | MClass2: d.Number("mclass2"), 28 | MIcon2: d.String("micon2"), 29 | MClass3: d.Number("mclass3"), 30 | MIcon3: d.String("micon3"), 31 | MClass4: d.Number("mclass4"), 32 | MIcon4: d.String("micon4"), 33 | } 34 | 35 | records[record.Name] = record 36 | } 37 | 38 | if d.Err != nil { 39 | return d.Err 40 | } 41 | 42 | r.Debugf("Loaded %d PetType records", len(records)) 43 | 44 | r.PetTypes = records 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /d2core/d2records/player_class_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadPlayerClasses loads the PlayerClassRecords into PlayerClasses 8 | func playerClassLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(PlayerClasses) 10 | 11 | for d.Next() { 12 | record := &PlayerClassRecord{ 13 | Name: d.String("Player Class"), 14 | Code: d.String("Code"), 15 | } 16 | 17 | if record.Name == expansionString { 18 | continue 19 | } 20 | 21 | records[record.Name] = record 22 | } 23 | 24 | if d.Err != nil { 25 | panic(d.Err) 26 | } 27 | 28 | if d.Err != nil { 29 | return d.Err 30 | } 31 | 32 | r.Debugf("Loaded %d PlayerClass records", len(records)) 33 | 34 | r.Character.Classes = records 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /d2core/d2records/player_class_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // PlayerClasses stores the PlayerClassRecords 4 | type PlayerClasses map[string]*PlayerClassRecord 5 | 6 | // PlayerClassRecord represents a single line from PlayerClass.txt 7 | // Lookup table for class codes 8 | type PlayerClassRecord struct { 9 | // Name of the player class 10 | Name string 11 | 12 | // Code for the player class 13 | Code string 14 | } 15 | -------------------------------------------------------------------------------- /d2core/d2records/player_mode_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | // LoadPlayerModes loads PlayerModeRecords into PlayerModes 8 | func playerModesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 9 | records := make(PlayerModes) 10 | 11 | for d.Next() { 12 | record := &PlayerModeRecord{ 13 | Name: d.String("Name"), 14 | Token: d.String("Token"), 15 | } 16 | 17 | records[record.Name] = record 18 | } 19 | 20 | if d.Err != nil { 21 | panic(d.Err) 22 | } 23 | 24 | r.Character.Modes = records 25 | 26 | r.Debugf("Loaded %d PlayerMode records", len(records)) 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /d2core/d2records/player_mode_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // PlayerModes stores the PlayerModeRecords 4 | type PlayerModes map[string]*PlayerModeRecord 5 | 6 | // PlayerModeRecord represents a single line in PlayerMode.txt 7 | type PlayerModeRecord struct { 8 | // Player animation mode name 9 | Name string 10 | 11 | // Player animation mode token 12 | Token string 13 | } 14 | -------------------------------------------------------------------------------- /d2core/d2records/player_type_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func playerTypeLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(PlayerTypes) 9 | 10 | for d.Next() { 11 | record := &PlayerTypeRecord{ 12 | Name: d.String("Player Class"), 13 | Token: d.String("Token"), 14 | } 15 | 16 | if record.Name == expansionString { 17 | continue 18 | } 19 | 20 | records[record.Name] = record 21 | } 22 | 23 | if d.Err != nil { 24 | panic(d.Err) 25 | } 26 | 27 | r.Debugf("Loaded %d PlayerType records", len(records)) 28 | 29 | r.Animation.Token.Player = records 30 | 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /d2core/d2records/player_type_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // PlayerTypes is a map of PlayerTypeRecords 4 | type PlayerTypes map[string]*PlayerTypeRecord 5 | 6 | // PlayerTypeRecord is used for changing character animation modes. 7 | type PlayerTypeRecord struct { 8 | Name string 9 | Token string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/property_descriptor.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // PropertyDescriptor is a generic description of a property, used to create properties. 4 | // Sets, SetItems, UniqueItems will all use this generic form of a property 5 | type PropertyDescriptor struct { 6 | // Code is an ID pointer of a property from Properties.txt, 7 | // these columns control each of the eight different full set modifiers a set item can grant you 8 | // at most. 9 | Code string 10 | 11 | // Param is the passed on to the associated property, this is used to pass skill IDs, state IDs, 12 | // monster IDs, montype IDs and the like on to the properties that require them, 13 | // these fields support calculations. 14 | Parameter string 15 | 16 | // Min value to assign to the associated property. 17 | // Certain properties have special interpretations based on stat encoding (e.g. 18 | // chance-to-cast and charged skills). See the File Guides for Properties.txt and ItemStatCost. 19 | // txt for further details. 20 | Min int 21 | 22 | // Max value to assign to the associated property. 23 | // Certain properties have special interpretations based on stat encoding (e.g. 24 | // chance-to-cast and charged skills). See the File Guides for Properties.txt and ItemStatCost. 25 | // txt for further details. 26 | Max int 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/property_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Properties stores all of the PropertyRecords 4 | type Properties map[string]*PropertyRecord 5 | 6 | // PropertyStatRecord contains stat information for a property 7 | type PropertyStatRecord struct { 8 | SetID int 9 | Value int 10 | FunctionID int 11 | StatCode string 12 | } 13 | 14 | // PropertyRecord is a representation of a single row of properties.txt 15 | type PropertyRecord struct { 16 | Code string 17 | Active string 18 | Stats [7]*PropertyStatRecord 19 | } 20 | -------------------------------------------------------------------------------- /d2core/d2records/rare_affix.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // RareItemAffix described both rare prefixes and suffixes 4 | type RareItemAffix struct { 5 | Name string 6 | IncludedTypes []string 7 | ExcludedTypes []string 8 | } 9 | -------------------------------------------------------------------------------- /d2core/d2records/rare_affix_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 7 | ) 8 | 9 | const ( 10 | numRareAffixInclude = 7 11 | fmtRareAffixInclude = "itype%d" 12 | 13 | numRareAffixExclude = 4 14 | fmtRareAffixExclude = "etype%d" 15 | ) 16 | 17 | func rareItemAffixLoader(d *d2txt.DataDictionary) ([]*RareItemAffix, error) { 18 | records := make([]*RareItemAffix, 0) 19 | 20 | for d.Next() { 21 | record := &RareItemPrefixRecord{ 22 | Name: d.String("name"), 23 | IncludedTypes: make([]string, 0), 24 | ExcludedTypes: make([]string, 0), 25 | } 26 | 27 | for idx := 1; idx <= numRareAffixInclude; idx++ { 28 | column := fmt.Sprintf(fmtRareAffixInclude, idx) 29 | if typeCode := d.String(column); typeCode != "" { 30 | record.IncludedTypes = append(record.IncludedTypes, typeCode) 31 | } 32 | } 33 | 34 | for idx := 1; idx <= numRareAffixExclude; idx++ { 35 | column := fmt.Sprintf(fmtRareAffixExclude, idx) 36 | if typeCode := d.String(column); typeCode != "" { 37 | record.ExcludedTypes = append(record.ExcludedTypes, typeCode) 38 | } 39 | } 40 | 41 | records = append(records, record) 42 | } 43 | 44 | return records, d.Err 45 | } 46 | -------------------------------------------------------------------------------- /d2core/d2records/rare_prefix_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func rareItemPrefixLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records, err := rareItemAffixLoader(d) 9 | if err != nil { 10 | return err 11 | } 12 | 13 | r.Item.Rare.Prefix = records 14 | 15 | r.Debugf("Loaded %d RarePrefix records", len(records)) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /d2core/d2records/rare_prefix_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // RarePrefixes is where all RareItemPrefixRecords are stored 4 | type RarePrefixes []*RareItemPrefixRecord 5 | 6 | // RareItemPrefixRecord is a name prefix for rare items (items with more than 2 affixes) 7 | type RareItemPrefixRecord = RareItemAffix 8 | -------------------------------------------------------------------------------- /d2core/d2records/rare_suffix_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func rareItemSuffixLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records, err := rareItemAffixLoader(d) 9 | if err != nil { 10 | return err 11 | } 12 | 13 | r.Debugf("Loaded %d RareSuffix records", len(records)) 14 | 15 | r.Item.Rare.Suffix = records 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /d2core/d2records/rare_suffix_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // RareSuffixes is where all RareItemSuffixRecords are stored 4 | type RareSuffixes []*RareItemSuffixRecord 5 | 6 | // RareItemSuffixRecord is a name suffix for rare items (items with more than 2 affixes) 7 | type RareItemSuffixRecord = RareItemAffix 8 | -------------------------------------------------------------------------------- /d2core/d2records/record_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 4 | 5 | // recordLoader represents something that can load a data dictionary and 6 | // handles placing it in the record manager exports 7 | type recordLoader func(r *RecordManager, dictionary *d2txt.DataDictionary) error 8 | -------------------------------------------------------------------------------- /d2core/d2records/runeword_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Runewords stores all of the RuneRecords 4 | type Runewords map[string]*RuneRecord 5 | 6 | // RuneRecord is a representation of a single row of runes.txt. It defines 7 | // runewords available in the game. 8 | type RuneRecord struct { 9 | Name string 10 | RuneName string // More of a note - the actual name should be read from the TBL files. 11 | Complete bool // An enabled/disabled flag. Only "Complete" runewords work in game. 12 | Server bool // Marks a runeword as only available on ladder, not single player or tcp/ip. 13 | 14 | // The item types for includsion/exclusion for this runeword record 15 | ItemTypes struct { 16 | Include []string 17 | Exclude []string 18 | } 19 | 20 | // Runes slice of ID pointers from Misc.txt, controls what runes are 21 | // required to make the rune word and in what order they are to be socketed. 22 | Runes []string 23 | 24 | Properties []*RunewordProperty 25 | } 26 | 27 | // RunewordProperty is a representation of a stat possessed by this runeword 28 | type RunewordProperty = PropertyDescriptor 29 | -------------------------------------------------------------------------------- /d2core/d2records/set_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Sets contain the set records from sets.txt 4 | type Sets map[string]*SetRecord 5 | 6 | // SetRecord describes the set bonus for a group of set items 7 | type SetRecord struct { 8 | // index 9 | // String key linked to by the set field in SetItems. 10 | // txt - used to tie all of the set's items to the same set. 11 | Key string 12 | 13 | // name 14 | // String key to item's name in a .tbl file. 15 | StringTableKey string 16 | 17 | // Version 0 for vanilla, 100 for LoD expansion 18 | Version int 19 | 20 | // Level 21 | // set level, perhaps intended as a minimum level for partial or full attributes to appear 22 | // (reference only, not loaded into game). 23 | Level int 24 | 25 | // Properties contains the partial and full set bonus properties. 26 | Properties struct { 27 | PartialA []*SetProperty 28 | PartialB []*SetProperty 29 | Full []*SetProperty 30 | } 31 | } 32 | 33 | // SetProperty represents a property possessed by the set 34 | type SetProperty = PropertyDescriptor 35 | -------------------------------------------------------------------------------- /d2core/d2records/shrine_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func shrineLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(map[string]*ShrineRecord) 9 | 10 | for d.Next() { 11 | record := &ShrineRecord{ 12 | ShrineType: d.String("Shrine Type"), 13 | ShrineName: d.String("Shrine name"), 14 | Effect: d.String("Effect"), 15 | Code: d.Number("Code"), 16 | Arg0: d.Number("Arg0"), 17 | Arg1: d.Number("Arg1"), 18 | DurationFrames: d.Number("Duration in frames"), 19 | ResetTimeMinutes: d.Number("reset time in minutes"), 20 | Rarity: d.Number("rarity"), 21 | EffectClass: d.Number("effectclass"), 22 | LevelMin: d.Number("LevelMin"), 23 | } 24 | 25 | records[record.ShrineName] = record 26 | } 27 | 28 | if d.Err != nil { 29 | return d.Err 30 | } 31 | 32 | r.Object.Shrines = records 33 | 34 | r.Debugf("Loaded %d Shrine records", len(records)) 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /d2core/d2records/shrine_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // Shrines contains the Unique Appellations 4 | type Shrines map[string]*ShrineRecord 5 | 6 | // ShrineRecord is a representation of a row from shrines.txt 7 | type ShrineRecord struct { 8 | ShrineType string // None, Recharge, Booster, or Magic 9 | ShrineName string // Name of the Shrine 10 | Effect string // Effect on the player 11 | Code int // Unique identifier 12 | Arg0 int // ? (0-400) 13 | Arg1 int // ? (0-2000) 14 | DurationFrames int // How long the shrine lasts in frames 15 | ResetTimeMinutes int // How many minutes until the shrine resets? 16 | Rarity int // 1-3 17 | EffectClass int // 0-4 18 | LevelMin int // 0-32 19 | } 20 | -------------------------------------------------------------------------------- /d2core/d2records/sound_details_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // SoundDetails is a map of the SoundEntries 4 | type SoundDetails map[string]*SoundDetailRecord 5 | 6 | // SoundDetailRecord represents a sound entry 7 | type SoundDetailRecord struct { 8 | Handle string 9 | FileName string 10 | Index int 11 | Volume int 12 | GroupSize int 13 | FadeIn int 14 | FadeOut int 15 | Duration int 16 | Compound int 17 | Reverb int 18 | Falloff int 19 | Priority int 20 | Block1 int 21 | Block2 int 22 | Block3 int 23 | Loop bool 24 | DeferInst bool 25 | StopInst bool 26 | Cache bool 27 | AsyncOnly bool 28 | Stream bool 29 | Stereo bool 30 | Tracking bool 31 | Solo bool 32 | MusicVol bool 33 | } 34 | -------------------------------------------------------------------------------- /d2core/d2records/sound_environment_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // SoundEnvironments contains the SoundEnviron records 4 | type SoundEnvironments map[int]*SoundEnvironRecord 5 | 6 | // SoundEnvironRecord describes the different sound environments. Not listed on Phrozen Keep. 7 | type SoundEnvironRecord struct { 8 | Handle string 9 | Index int 10 | Song int 11 | DayAmbience int 12 | NightAmbience int 13 | DayEvent int 14 | NightEvent int 15 | EventDelay int 16 | Indoors int 17 | Material1 int 18 | Material2 int 19 | EAXEnviron int 20 | EAXEnvSize int 21 | EAXEnvDiff int 22 | EAXRoomVol int 23 | EAXRoomHF int 24 | EAXDecayTime int 25 | EAXDecayHF int 26 | EAXReflect int 27 | EAXReflectDelay int 28 | EAXReverb int 29 | EAXRevDelay int 30 | EAXRoomRoll int 31 | EAXAirAbsorb int 32 | } 33 | -------------------------------------------------------------------------------- /d2core/d2records/storepage_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func storePagesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(StorePages) 9 | 10 | for d.Next() { 11 | record := &StorePageRecord{ 12 | StorePage: d.String("Store Page"), 13 | Code: d.String("Code"), 14 | } 15 | records[record.StorePage] = record 16 | } 17 | 18 | if d.Err != nil { 19 | panic(d.Err) 20 | } 21 | 22 | r.Item.StorePages = records 23 | 24 | r.Debugf("Loaded %d StorePage records", len(records)) 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/storepage_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // StorePages is a map of all store page records 4 | type StorePages map[string]*StorePageRecord 5 | 6 | // StorePageRecord represent a row in the storepage.txt file 7 | type StorePageRecord struct { 8 | StorePage string 9 | Code string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/treasure_class_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // TreasureClass contains all of the TreasureClassRecords 4 | type TreasureClass map[string]*TreasureClassRecord 5 | 6 | // TreasureClassRecord represents a rule for item drops in diablo 2 7 | type TreasureClassRecord struct { 8 | Name string 9 | Group int 10 | Level int 11 | NumPicks int 12 | FreqUnique int 13 | FreqSet int 14 | FreqRare int 15 | FreqMagic int 16 | FreqNoDrop int 17 | Treasures []*Treasure 18 | } 19 | 20 | // Treasure describes a treasure to drop 21 | // the Name is either a reference to an item, or to another treasure class 22 | type Treasure struct { 23 | Code string 24 | Probability int 25 | } 26 | -------------------------------------------------------------------------------- /d2core/d2records/unique_appellation_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func uniqueAppellationsLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(UniqueAppellations) 9 | 10 | for d.Next() { 11 | record := &UniqueAppellationRecord{ 12 | Name: d.String("Name"), 13 | } 14 | 15 | records[record.Name] = record 16 | } 17 | 18 | if d.Err != nil { 19 | return d.Err 20 | } 21 | 22 | r.Monster.Unique.Appellations = records 23 | 24 | r.Debugf("Loaded %d UniqueAppellation records", len(records)) 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /d2core/d2records/unique_appellations_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // UniqueAppellations contains all of the UniqueAppellationRecords 4 | type UniqueAppellations map[string]*UniqueAppellationRecord 5 | 6 | // UniqueAppellationRecord described the extra suffix of a unique monster name 7 | type UniqueAppellationRecord struct { 8 | // The title 9 | Name string 10 | } 11 | -------------------------------------------------------------------------------- /d2core/d2records/weapon_class_loader.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" 5 | ) 6 | 7 | func weaponClassesLoader(r *RecordManager, d *d2txt.DataDictionary) error { 8 | records := make(WeaponClasses) 9 | 10 | for d.Next() { 11 | record := &WeaponClassRecord{ 12 | Name: d.String("Weapon Class"), 13 | Token: d.String("Code"), 14 | } 15 | 16 | records[record.Name] = record 17 | } 18 | 19 | if d.Err != nil { 20 | panic(d.Err) 21 | } 22 | 23 | r.Animation.Token.Weapon = records 24 | 25 | r.Debugf("Loaded %d WeaponClass records", len(records)) 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /d2core/d2records/weapon_class_record.go: -------------------------------------------------------------------------------- 1 | package d2records 2 | 3 | // WeaponClasses is a map of WeaponClassRecords 4 | type WeaponClasses map[string]*WeaponClassRecord 5 | 6 | // WeaponClassRecord describes a weapon class. It has a name and 3-character token. 7 | // The token is used to change the character animation mode. 8 | type WeaponClassRecord struct { 9 | Name string 10 | Token string 11 | } 12 | -------------------------------------------------------------------------------- /d2core/d2render/create.go: -------------------------------------------------------------------------------- 1 | package d2render 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/d2sdl2renderer" 8 | 9 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 10 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2config" 11 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten" 12 | ) 13 | 14 | func Create(config *d2config.Configuration) (d2interface.Renderer, error) { 15 | switch strings.ToUpper(config.Backend) { 16 | case "EBITEN": 17 | return ebiten.CreateRenderer(config) 18 | case "SDL2": 19 | return d2sdl2renderer.CreateRenderer(config) 20 | default: 21 | panic(fmt.Errorf("no renderer available for backend: %s", config.Backend)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /d2core/d2render/d2sdl2renderer/sdl2_globals.go: -------------------------------------------------------------------------------- 1 | package d2sdl2renderer 2 | 3 | import "sync" 4 | 5 | var ( 6 | sdlMutex sync.Mutex 7 | ) 8 | -------------------------------------------------------------------------------- /d2core/d2render/d2sdl2renderer/sdl2_surfacestate.go: -------------------------------------------------------------------------------- 1 | package d2sdl2renderer 2 | 3 | import ( 4 | "image/color" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 7 | ) 8 | 9 | type surfaceState struct { 10 | x int 11 | y int 12 | //filter ebiten.Filter 13 | color color.Color 14 | brightness float64 15 | saturation float64 16 | effect d2enum.DrawEffect 17 | skewX, skewY float64 18 | scaleX, scaleY float64 19 | } 20 | 21 | func (s *surfaceState) Clear() { 22 | *s = surfaceState{} 23 | } 24 | 25 | -------------------------------------------------------------------------------- /d2core/d2render/ebiten/doc.go: -------------------------------------------------------------------------------- 1 | // Package ebiten provides a renderer implementation using Ebiten 2 | package ebiten 3 | -------------------------------------------------------------------------------- /d2core/d2render/ebiten/filter_helper.go: -------------------------------------------------------------------------------- 1 | package ebiten 2 | 3 | import ( 4 | "github.com/hajimehoshi/ebiten/v2" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 7 | ) 8 | 9 | func d2ToEbitenFilter(filter d2enum.Filter) ebiten.Filter { 10 | switch filter { 11 | case d2enum.FilterNearest: 12 | return ebiten.FilterNearest 13 | default: 14 | return ebiten.FilterLinear 15 | } 16 | } 17 | 18 | // func ebitenToD2Filter(filter ebiten.Filter) d2enum.Filter { 19 | // switch filter { 20 | // case ebiten.FilterDefault: 21 | // return d2enum.FilterDefault 22 | // case ebiten.FilterLinear: 23 | // return d2enum.FilterLinear 24 | // case ebiten.FilterNearest: 25 | // return d2enum.FilterNearest 26 | // } 27 | // 28 | // return d2enum.FilterDefault 29 | // } 30 | -------------------------------------------------------------------------------- /d2core/d2render/ebiten/surface_state.go: -------------------------------------------------------------------------------- 1 | package ebiten 2 | 3 | import ( 4 | "image/color" 5 | 6 | "github.com/hajimehoshi/ebiten/v2" 7 | 8 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 9 | ) 10 | 11 | type surfaceState struct { 12 | x int 13 | y int 14 | filter ebiten.Filter 15 | color color.Color 16 | brightness float64 17 | saturation float64 18 | effect d2enum.DrawEffect 19 | skewX, skewY float64 20 | scaleX, scaleY float64 21 | } 22 | 23 | func (s *surfaceState) Clear() { 24 | *s = surfaceState{} 25 | } 26 | -------------------------------------------------------------------------------- /d2core/d2screen/d2screen.go: -------------------------------------------------------------------------------- 1 | package d2screen 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 5 | ) 6 | 7 | // Screen is an exported interface 8 | type Screen interface{} 9 | 10 | // ScreenLoadHandler is an exported interface 11 | type ScreenLoadHandler interface { 12 | // OnLoad performs all necessary loading to prepare a screen to be shown such as loading assets, placing and binding 13 | // of ui elements, etc. This loading is done asynchronously. The provided channel will allow implementations to 14 | // provide progress via Error, Progress, or Done 15 | OnLoad(loading LoadingState) 16 | } 17 | 18 | // ScreenUnloadHandler is an exported interface 19 | type ScreenUnloadHandler interface { 20 | OnUnload() error 21 | } 22 | 23 | // ScreenRenderHandler is an exported interface 24 | type ScreenRenderHandler interface { 25 | Render(target d2interface.Surface) 26 | } 27 | 28 | // ScreenAdvanceHandler is an exported interface 29 | type ScreenAdvanceHandler interface { 30 | Advance(elapsed float64) error 31 | } 32 | -------------------------------------------------------------------------------- /d2core/d2screen/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2screen provides a means of defining and 2 | // transitioning between game screens 3 | package d2screen 4 | -------------------------------------------------------------------------------- /d2core/d2screen/loading_state.go: -------------------------------------------------------------------------------- 1 | package d2screen 2 | 3 | // LoadingState represents the loading state 4 | type LoadingState struct { 5 | updates chan loadingUpdate 6 | } 7 | -------------------------------------------------------------------------------- /d2core/d2screen/loading_update.go: -------------------------------------------------------------------------------- 1 | package d2screen 2 | 3 | const progressCompleted = 1.0 4 | 5 | type loadingUpdate struct { 6 | progress float64 7 | err error 8 | done bool 9 | } 10 | 11 | // Error provides a way for callers to report an error during loading. 12 | func (l *LoadingState) Error(err error) { 13 | l.updates <- loadingUpdate{err: err} 14 | } 15 | 16 | // Progress provides a way for callers to report the ratio between `0` and `1` of the progress made loading a screen. 17 | func (l *LoadingState) Progress(ratio float64) { 18 | l.updates <- loadingUpdate{progress: ratio} 19 | } 20 | 21 | // Done provides a way for callers to report that screen loading has been completed. 22 | func (l *LoadingState) Done() { 23 | l.updates <- loadingUpdate{progress: progressCompleted} 24 | l.updates <- loadingUpdate{done: true} 25 | } 26 | -------------------------------------------------------------------------------- /d2core/d2stats/diablo2stats/doc.go: -------------------------------------------------------------------------------- 1 | // Package diablo2stats is the Diablo 2 stats implementation 2 | package diablo2stats 3 | -------------------------------------------------------------------------------- /d2core/d2stats/diablo2stats/stat_value_stringers.go: -------------------------------------------------------------------------------- 1 | package diablo2stats 2 | -------------------------------------------------------------------------------- /d2core/d2stats/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2stats provides item/skill/character stats functionality 2 | package d2stats 3 | -------------------------------------------------------------------------------- /d2core/d2stats/stat.go: -------------------------------------------------------------------------------- 1 | package d2stats 2 | 3 | // Stat a generic interface for a stat. It is something which can be 4 | // combined with other stats, holds one or more values, and handles the 5 | // way that it is printed as a string 6 | type Stat interface { 7 | Name() string 8 | Clone() Stat 9 | Copy(Stat) Stat 10 | Combine(Stat) (combined Stat, err error) 11 | String() string 12 | Values() []StatValue 13 | SetValues(...StatValue) 14 | Priority() int 15 | } 16 | -------------------------------------------------------------------------------- /d2core/d2stats/stat_list.go: -------------------------------------------------------------------------------- 1 | package d2stats 2 | 3 | // StatList is useful for reducing stats. 4 | // They provide a context for stats to alter other stats or infer values 5 | // during stat assignment/calculation 6 | type StatList interface { 7 | Index(idx int) Stat 8 | Stats() []Stat 9 | SetStats([]Stat) StatList 10 | Clone() StatList 11 | ReduceStats() StatList 12 | RemoveStatAtIndex(idx int) Stat 13 | AppendStatList(other StatList) StatList 14 | Pop() Stat 15 | Push(Stat) StatList 16 | } 17 | -------------------------------------------------------------------------------- /d2core/d2term/commmand.go: -------------------------------------------------------------------------------- 1 | package d2term 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | func (t *Terminal) commandList([]string) error { 8 | names := make([]string, 0, len(t.commands)) 9 | for name := range t.commands { 10 | names = append(names, name) 11 | } 12 | 13 | sort.Strings(names) 14 | t.Infof("available actions (%d):", len(names)) 15 | 16 | for _, name := range names { 17 | entry := t.commands[name] 18 | if entry.arguments != nil { 19 | t.Infof("%s: %s; %v", name, entry.description, entry.arguments) 20 | continue 21 | } 22 | 23 | t.Infof("%s: %s", name, entry.description) 24 | } 25 | 26 | return nil 27 | } 28 | 29 | func (t *Terminal) commandClear([]string) error { 30 | t.Clear() 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /d2core/d2term/d2term.go: -------------------------------------------------------------------------------- 1 | package d2term 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 6 | ) 7 | 8 | // New creates and initializes the terminal 9 | func New(inputManager d2interface.InputManager) (*Terminal, error) { 10 | term, err := NewTerminal() 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | if err := inputManager.BindHandlerWithPriority(term, d2enum.PriorityHigh); err != nil { 16 | return nil, err 17 | } 18 | 19 | return term, nil 20 | } 21 | -------------------------------------------------------------------------------- /d2core/d2term/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2term provides a in-game terminal that allows 2 | // executing custom commands for debugging 3 | package d2term 4 | -------------------------------------------------------------------------------- /d2core/d2term/terminal_logger.go: -------------------------------------------------------------------------------- 1 | package d2term 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "strings" 8 | ) 9 | 10 | type terminalLogger struct { 11 | terminal *Terminal 12 | buffer bytes.Buffer 13 | writer io.Writer 14 | } 15 | 16 | func (tl *terminalLogger) Write(p []byte) (int, error) { 17 | n, err := tl.buffer.Write(p) 18 | if err != nil { 19 | return n, err 20 | } 21 | 22 | reader := bufio.NewReader(&tl.buffer) 23 | termBytes, _, err := reader.ReadLine() 24 | 25 | if err != nil { 26 | return n, err 27 | } 28 | 29 | line := string(termBytes) 30 | lineLower := strings.ToLower(line) 31 | 32 | switch { 33 | case strings.Index(lineLower, "error") > 0: 34 | tl.terminal.Errorf(line) 35 | case strings.Index(lineLower, "warning") > 0: 36 | tl.terminal.Warningf(line) 37 | default: 38 | tl.terminal.Printf(line) 39 | } 40 | 41 | return tl.writer.Write(p) 42 | } 43 | 44 | func (tl *terminalLogger) BindToTerminal(t *Terminal) { 45 | tl.terminal = t 46 | } 47 | -------------------------------------------------------------------------------- /d2core/d2ui/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2ui provides ui elements like buttons, scrollbars, 2 | // checkboxes, text labels, etc. 3 | package d2ui 4 | -------------------------------------------------------------------------------- /d2core/d2ui/drawable.go: -------------------------------------------------------------------------------- 1 | package d2ui 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 5 | ) 6 | 7 | // Drawable represents an instance that can be drawn 8 | type Drawable interface { 9 | Render(target d2interface.Surface) 10 | Advance(elapsed float64) error 11 | GetSize() (width, height int) 12 | SetPosition(x, y int) 13 | GetPosition() (x, y int) 14 | OffsetPosition(xo, yo int) 15 | GetVisible() bool 16 | SetVisible(visible bool) 17 | SetRenderPriority(priority RenderPriority) 18 | GetRenderPriority() (priority RenderPriority) 19 | } 20 | -------------------------------------------------------------------------------- /d2discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2discord.png -------------------------------------------------------------------------------- /d2game/d2gamescreen/blizzard_intro.go: -------------------------------------------------------------------------------- 1 | package d2gamescreen 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2video" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" 6 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" 7 | ) 8 | 9 | // BlizzardIntro represents the Blizzard Intro screen 10 | type BlizzardIntro struct { 11 | asset *d2asset.AssetManager 12 | videoDecoder *d2video.BinkDecoder 13 | } 14 | 15 | // CreateBlizzardIntro creates a Blizzard Intro screen 16 | func CreateBlizzardIntro(asset *d2asset.AssetManager) *BlizzardIntro { 17 | return &BlizzardIntro{ 18 | asset: asset, 19 | } 20 | } 21 | 22 | // OnLoad loads the resources for the Blizzard Intro screen 23 | func (v *BlizzardIntro) OnLoad(loading d2screen.LoadingState) { 24 | videoBytes, err := v.asset.LoadFile("/data/local/video/BlizNorth640x480.bik") 25 | if err != nil { 26 | loading.Error(err) 27 | return 28 | } 29 | 30 | loading.Progress(fiftyPercent) 31 | 32 | v.videoDecoder, err = d2video.CreateBinkDecoder(videoBytes) 33 | if err != nil { 34 | loading.Error(err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /d2game/d2gamescreen/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2gamescreen is where the game screens are defined 2 | package d2gamescreen 3 | -------------------------------------------------------------------------------- /d2game/d2player/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2player contains the information necessary for managing players 2 | package d2player 3 | -------------------------------------------------------------------------------- /d2game/d2player/equipment_slot.go: -------------------------------------------------------------------------------- 1 | package d2player 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" 6 | ) 7 | 8 | // EquipmentSlot represents an equipment slot for a player 9 | type EquipmentSlot struct { 10 | item InventoryItem 11 | x int 12 | y int 13 | width int 14 | height int 15 | } 16 | 17 | func genEquipmentSlotsMap(record *d2records.InventoryRecord) map[d2enum.EquippedSlot]EquipmentSlot { 18 | slotMap := map[d2enum.EquippedSlot]EquipmentSlot{} 19 | 20 | slots := []d2enum.EquippedSlot{ 21 | d2enum.EquippedSlotHead, 22 | d2enum.EquippedSlotTorso, 23 | d2enum.EquippedSlotLegs, 24 | d2enum.EquippedSlotRightArm, 25 | d2enum.EquippedSlotLeftArm, 26 | d2enum.EquippedSlotLeftHand, 27 | d2enum.EquippedSlotRightHand, 28 | d2enum.EquippedSlotNeck, 29 | d2enum.EquippedSlotBelt, 30 | d2enum.EquippedSlotGloves, 31 | } 32 | 33 | for _, slot := range slots { 34 | box := record.Slots[slot] 35 | equipmentSlot := EquipmentSlot{ 36 | nil, 37 | box.Left, 38 | box.Bottom + cellPadding, 39 | box.Width, 40 | box.Height, 41 | } 42 | slotMap[slot] = equipmentSlot 43 | } 44 | 45 | return slotMap 46 | } 47 | -------------------------------------------------------------------------------- /d2game/d2player/input_callback_listener.go: -------------------------------------------------------------------------------- 1 | package d2player 2 | 3 | type inputCallbackListener interface { 4 | OnPlayerMove(x, y float64) 5 | OnPlayerCast(skillID int, x, y float64) 6 | } 7 | -------------------------------------------------------------------------------- /d2game/d2player/skill_row.go: -------------------------------------------------------------------------------- 1 | package d2player 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" 6 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" 7 | ) 8 | 9 | // SkillListRow represents a row of skills that is shown when the skill select menu is rendered. 10 | type SkillListRow struct { 11 | Rectangle d2geom.Rectangle 12 | Skills []*d2hero.HeroSkill 13 | cachedImage d2interface.Surface 14 | } 15 | 16 | // AddSkill appends to the skills of the row. 17 | func (s *SkillListRow) AddSkill(skill *d2hero.HeroSkill) { 18 | s.Skills = append(s.Skills, skill) 19 | } 20 | 21 | // GetWidth returns the width based on the size of the skills. 22 | func (s *SkillListRow) GetWidth() int { 23 | return skillIconWidth * len(s.Skills) 24 | } 25 | 26 | // GetRectangle returns the rectangle of the list. 27 | func (s *SkillListRow) GetRectangle() d2geom.Rectangle { 28 | return s.Rectangle 29 | } 30 | 31 | // IsInRect returns true when the list has any skills and coordinates are in the rectangle of the list. 32 | func (s *SkillListRow) IsInRect(x, y int) bool { 33 | // if there are no skills, row won't be rendered and it shouldn't be considered visible 34 | return len(s.Skills) > 0 && s.Rectangle.IsInRect(x, y) 35 | } 36 | -------------------------------------------------------------------------------- /d2logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2logo.ico -------------------------------------------------------------------------------- /d2logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/d2logo.png -------------------------------------------------------------------------------- /d2networking/client_listener.go: -------------------------------------------------------------------------------- 1 | package d2networking 2 | 3 | import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" 4 | 5 | // ClientListener is an interface used to pass packet data from 6 | // ClientConnections to GameServer and GameClient. 7 | type ClientListener interface { 8 | OnPacketReceived(packet d2netpacket.NetPacket) error 9 | } 10 | -------------------------------------------------------------------------------- /d2networking/d2client/d2clientconnectiontype/connectiontype.go: -------------------------------------------------------------------------------- 1 | package d2clientconnectiontype 2 | 3 | // ClientConnectionType is an enum referring to types implementing 4 | // d2server.ClientConnection and d2client.ClientConnection. 5 | type ClientConnectionType int 6 | 7 | // 8 | const ( 9 | Local ClientConnectionType = iota // Local client 10 | LANServer // Server 11 | LANClient // Remote client 12 | ) 13 | -------------------------------------------------------------------------------- /d2networking/d2client/d2clientconnectiontype/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2clientconnectiontype provides types for client connections 2 | package d2clientconnectiontype 3 | -------------------------------------------------------------------------------- /d2networking/d2client/d2localclient/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2localclient facilitates communication between a local client and server. 2 | package d2localclient 3 | -------------------------------------------------------------------------------- /d2networking/d2client/d2remoteclient/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2remoteclient facilitates communication between a remote client and server. 2 | package d2remoteclient 3 | -------------------------------------------------------------------------------- /d2networking/d2client/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2client provides client side connection, map and entity 2 | // management. 3 | /* 4 | GameClient declares the ServerConnection interface to interact with 5 | local and remote servers. LocalClientConnection is the host and 6 | creates the game server (see d2server).*/ 7 | package d2client 8 | -------------------------------------------------------------------------------- /d2networking/d2client/server_connection.go: -------------------------------------------------------------------------------- 1 | package d2client 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2networking" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" 6 | ) 7 | 8 | // ServerConnection is an interface for abstracting local and 9 | // remote server connections. 10 | type ServerConnection interface { 11 | Open(connectionString string, saveFilePath string) error 12 | Close() error 13 | SendPacketToServer(packet d2netpacket.NetPacket) error 14 | SetClientListener(listener d2networking.ClientListener) 15 | } 16 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/d2netpackettype/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2netpackettype defines types which are encoded to JSON and sent in network 2 | // packet payloads. 3 | // Package d2netpacket/d2netpackettype defines a uint32 enumerable representing each 4 | // packet type. 5 | // 6 | // A struct is defined for each packet type. Each struct comes with a function which 7 | // returns a NetPacket declaring the type enum (header) followed by the associated 8 | // struct (body). The NetPacket is marshaled to JSON for transport. On receipt of 9 | // the packet, the enum is read as a single byte then the remaining data (the struct) 10 | // is unmarshalled to the type associated with the type enum. 11 | package d2netpackettype 12 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2netpacket provides all of the different types of packets 2 | package d2netpacket 3 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/packet_item_spawn.go: -------------------------------------------------------------------------------- 1 | package d2netpacket 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" 7 | ) 8 | 9 | // SpawnItemPacket contains the data required to create a Item entity 10 | type SpawnItemPacket struct { 11 | X int `json:"x"` 12 | Y int `json:"y"` 13 | Codes []string `json:"codes"` 14 | } 15 | 16 | // CreateSpawnItemPacket returns a NetPacket which declares a 17 | // SpawnItemPacket with the data in given parameters. 18 | func CreateSpawnItemPacket(x, y int, codes ...string) (NetPacket, error) { 19 | spawnItemPacket := SpawnItemPacket{ 20 | X: x, 21 | Y: y, 22 | Codes: codes, 23 | } 24 | 25 | b, err := json.Marshal(spawnItemPacket) 26 | if err != nil { 27 | return NetPacket{PacketType: d2netpackettype.SpawnItem}, err 28 | } 29 | 30 | return NetPacket{ 31 | PacketType: d2netpackettype.SpawnItem, 32 | PacketData: b, 33 | }, nil 34 | } 35 | 36 | // UnmarshalSpawnItem unmarshals the given data to a SpawnItemPacket struct 37 | func UnmarshalSpawnItem(packet []byte) (SpawnItemPacket, error) { 38 | var p SpawnItemPacket 39 | if err := json.Unmarshal(packet, &p); err != nil { 40 | return p, err 41 | } 42 | 43 | return p, nil 44 | } 45 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/packet_ping.go: -------------------------------------------------------------------------------- 1 | package d2netpacket //nolint:dupl // ServerClosed and Ping just happen to be very similar packets 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" 8 | ) 9 | 10 | // PingPacket contains the time at which it was sent. It is sent by the 11 | // server and instructs the client to respond with a Pong packet. 12 | type PingPacket struct { 13 | TS time.Time `json:"ts"` 14 | } 15 | 16 | // CreatePingPacket returns a NetPacket which declares a GenerateMapPacket 17 | // with the the current time. 18 | func CreatePingPacket() (NetPacket, error) { 19 | ping := PingPacket{ 20 | TS: time.Now(), 21 | } 22 | 23 | b, err := json.Marshal(ping) 24 | if err != nil { 25 | return NetPacket{PacketType: d2netpackettype.Ping}, err 26 | } 27 | 28 | return NetPacket{ 29 | PacketType: d2netpackettype.Ping, 30 | PacketData: b, 31 | }, nil 32 | } 33 | 34 | // UnmarshalPing unmarshals the given data to a PingPacket struct 35 | func UnmarshalPing(packet []byte) (PingPacket, error) { 36 | var p PingPacket 37 | if err := json.Unmarshal(packet, &p); err != nil { 38 | return p, err 39 | } 40 | 41 | return p, nil 42 | } 43 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/packet_pong.go: -------------------------------------------------------------------------------- 1 | package d2netpacket 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" 8 | ) 9 | 10 | // PongPacket contains the time at which it was sent and the ID of the 11 | // client. It is sent by the client in response to a Pong packet. 12 | type PongPacket struct { 13 | ID string `json:"id"` 14 | TS time.Time `json:"ts"` 15 | } 16 | 17 | // CreatePongPacket returns a NetPacket which declares a PongPacket with 18 | // the current time and given ID. 19 | func CreatePongPacket(id string) (NetPacket, error) { 20 | pong := PongPacket{ 21 | ID: id, 22 | TS: time.Now(), 23 | } 24 | 25 | b, err := json.Marshal(pong) 26 | if err != nil { 27 | return NetPacket{PacketType: d2netpackettype.Pong}, err 28 | } 29 | 30 | return NetPacket{ 31 | PacketType: d2netpackettype.Pong, 32 | PacketData: b, 33 | }, nil 34 | } 35 | 36 | // UnmarshalPong unmarshals the given data to a PongPacket struct 37 | func UnmarshalPong(packet []byte) (PongPacket, error) { 38 | var resp PongPacket 39 | 40 | if err := json.Unmarshal(packet, &resp); err != nil { 41 | return resp, err 42 | } 43 | 44 | return resp, nil 45 | } 46 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/packet_server_closed.go: -------------------------------------------------------------------------------- 1 | package d2netpacket //nolint:dupl // ServerClosed and Ping just happen to be very similar packets 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" 8 | ) 9 | 10 | // ServerClosedPacket contains the current time. It is sent by the server 11 | // to inform a client that the server has shut down. 12 | type ServerClosedPacket struct { 13 | TS time.Time `json:"ts"` 14 | } 15 | 16 | // CreateServerClosedPacket returns a NetPacket which declares a 17 | // ServerClosedPacket with the current time. 18 | func CreateServerClosedPacket() (NetPacket, error) { 19 | serverClosed := ServerClosedPacket{ 20 | TS: time.Now(), 21 | } 22 | 23 | b, err := json.Marshal(serverClosed) 24 | if err != nil { 25 | return NetPacket{PacketType: d2netpackettype.ServerClosed}, err 26 | } 27 | 28 | return NetPacket{ 29 | PacketType: d2netpackettype.ServerClosed, 30 | PacketData: b, 31 | }, nil 32 | } 33 | 34 | // UnmarshalServerClosed unmarshals the given data to a ServerClosedPacket struct 35 | func UnmarshalServerClosed(packet []byte) (ServerClosedPacket, error) { 36 | var resp ServerClosedPacket 37 | 38 | if err := json.Unmarshal(packet, &resp); err != nil { 39 | return resp, err 40 | } 41 | 42 | return resp, nil 43 | } 44 | -------------------------------------------------------------------------------- /d2networking/d2netpacket/packet_server_full.go: -------------------------------------------------------------------------------- 1 | package d2netpacket 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" 7 | ) 8 | 9 | // ServerFullPacket is sent by the server to inform a client that the 10 | // server has reached the max number of allowed connections. 11 | type ServerFullPacket struct{} 12 | 13 | // CreateServerFullPacket returns a NetPacket which declares a ServerFullPacket. 14 | func CreateServerFullPacket() (NetPacket, error) { 15 | serverClosed := ServerFullPacket{} 16 | 17 | b, err := json.Marshal(serverClosed) 18 | if err != nil { 19 | return NetPacket{PacketType: d2netpackettype.ServerFull}, err 20 | } 21 | 22 | return NetPacket{ 23 | PacketType: d2netpackettype.ServerFull, 24 | PacketData: b, 25 | }, nil 26 | } 27 | 28 | // UnmarshalServerFull unmarshalls the given data to a ServerFullPacket struct 29 | func UnmarshalServerFull(packet []byte) (ServerFullPacket, error) { 30 | var resp ServerFullPacket 31 | 32 | if err := json.Unmarshal(packet, &resp); err != nil { 33 | return resp, err 34 | } 35 | 36 | return resp, nil 37 | } 38 | -------------------------------------------------------------------------------- /d2networking/d2server/client_connection.go: -------------------------------------------------------------------------------- 1 | package d2server 2 | 3 | import ( 4 | "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" 5 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" 6 | "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" 7 | ) 8 | 9 | // ClientConnection is an interface for abstracting local and remote 10 | // clients. 11 | type ClientConnection interface { 12 | GetUniqueID() string 13 | GetConnectionType() d2clientconnectiontype.ClientConnectionType 14 | SendPacketToClient(packet d2netpacket.NetPacket) error 15 | GetPlayerState() *d2hero.HeroState 16 | SetPlayerState(playerState *d2hero.HeroState) 17 | } 18 | -------------------------------------------------------------------------------- /d2networking/d2server/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2server provides connection management and client synchronization. 2 | /* 3 | Data is encoded to JSON and compressed using gzip. Transport is over UDP. 4 | The server is authoritative for both local and remote clients.*/ 5 | package d2server 6 | -------------------------------------------------------------------------------- /d2networking/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2networking provides client and server implementations for OpenDiablo2. 2 | /* 3 | The server is authoritative and communicates with local and remote clients over UDP.*/ 4 | package d2networking 5 | -------------------------------------------------------------------------------- /d2script/doc.go: -------------------------------------------------------------------------------- 1 | // Package d2script provides a virtual machine for 2 | // interpretting scripts 3 | package d2script 4 | -------------------------------------------------------------------------------- /debug_font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/debug_font.png -------------------------------------------------------------------------------- /docs/ButtonMedium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/ButtonMedium.png -------------------------------------------------------------------------------- /docs/ButtonWide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/ButtonWide.png -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | opendiablo2.com -------------------------------------------------------------------------------- /docs/Gameplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/Gameplay.png -------------------------------------------------------------------------------- /docs/Inventory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/Inventory.png -------------------------------------------------------------------------------- /docs/MainMenuSS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/MainMenuSS.png -------------------------------------------------------------------------------- /docs/SelectHeroSS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/SelectHeroSS.png -------------------------------------------------------------------------------- /docs/areas.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/areas.gif -------------------------------------------------------------------------------- /docs/diablo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/diablo.woff -------------------------------------------------------------------------------- /docs/game_panels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/docs/game_panels.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/OpenDiablo2/OpenDiablo2 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0 7 | github.com/go-restruct/restruct v1.2.0-alpha 8 | github.com/google/uuid v1.1.2 9 | github.com/gravestench/akara v0.0.0-20201014060234-a64208a7fd3c 10 | github.com/hajimehoshi/ebiten v1.12.6 11 | github.com/hajimehoshi/ebiten/v2 v2.1.0-alpha.9 12 | github.com/pkg/errors v0.9.1 // indirect 13 | github.com/pkg/profile v1.5.0 14 | github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac 15 | github.com/stretchr/testify v1.4.0 16 | github.com/veandco/go-sdl2 v0.4.5 17 | golang.org/x/exp v0.0.0-20201008143054-e3b2a7f2fdc7 // indirect 18 | golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 19 | golang.org/x/sys v0.0.0-20201028215240-c5abc1b1d397 // indirect 20 | gopkg.in/sourcemap.v1 v1.0.5 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/OpenDiablo2/OpenDiablo2/d2app" 7 | ) 8 | 9 | // GitBranch is set by the CI build process to the name of the branch 10 | //nolint:gochecknoglobals // This is filled in by the build system 11 | var GitBranch string = "local" 12 | 13 | // GitCommit is set by the CI build process to the commit hash 14 | //nolint:gochecknoglobals // This is filled in by the build system 15 | var GitCommit string = "build" 16 | 17 | func main() { 18 | log.SetFlags(log.Lshortfile) 19 | 20 | instance := d2app.Create(GitBranch, GitCommit) 21 | 22 | if instance.ShouldTerminateImmediately() { 23 | return 24 | } 25 | 26 | if err := instance.Run(); err != nil { 27 | panic(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rh.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/essial/OpenDiablo2/53e0b51d64063858f3c892f08b83270709cde76d/rh.exe -------------------------------------------------------------------------------- /scripts/server/server.js: -------------------------------------------------------------------------------- 1 | debugPrint('Server script initialized.') 2 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 2 | // debugPrint(test()) 3 | 4 | -------------------------------------------------------------------------------- /tagdev.bat: -------------------------------------------------------------------------------- 1 | git tag -d dev 2 | git push origin :refs/tags/dev 3 | git tag dev 4 | git push origin dev 5 | -------------------------------------------------------------------------------- /utils/extract-mpq/doc.go: -------------------------------------------------------------------------------- 1 | // This command line utility provides a way to extract mpq files. 2 | // 3 | // Flags: 4 | // -o [directory] Output directory 5 | // -v Enable verbose output 6 | // 7 | // Usage: 8 | // First run `go install extract-mpq.go` in this directory. 9 | // Navigate to the Diablo II directory (ex: C:/Program Files (x86)/Diablo II) 10 | // then run extract-mpq(.exe) with the filename of the mpq to be extracted. 11 | // 12 | // extract-mpq d2char.mpq 13 | package main 14 | --------------------------------------------------------------------------------