+ static int ep_active_bomb[] =
+ {
+ EL_DYNAMITE_ACTIVE,
+ EL_EM_DYNAMITE_ACTIVE,
+ EL_DYNABOMB_PLAYER_1_ACTIVE,
+ EL_DYNABOMB_PLAYER_2_ACTIVE,
+ EL_DYNABOMB_PLAYER_3_ACTIVE,
+ EL_DYNABOMB_PLAYER_4_ACTIVE,
+ EL_SP_DISK_RED_ACTIVE,
+
+ -1
+ };
+
+ static int ep_inactive[] =
+ {
+ EL_EMPTY,
+ EL_EMPTY_SPACE_1,
+ EL_EMPTY_SPACE_2,
+ EL_EMPTY_SPACE_3,
+ EL_EMPTY_SPACE_4,
+ EL_EMPTY_SPACE_5,
+ EL_EMPTY_SPACE_6,
+ EL_EMPTY_SPACE_7,
+ EL_EMPTY_SPACE_8,
+ EL_EMPTY_SPACE_9,
+ EL_EMPTY_SPACE_10,
+ EL_EMPTY_SPACE_11,
+ EL_EMPTY_SPACE_12,
+ EL_EMPTY_SPACE_13,
+ EL_EMPTY_SPACE_14,
+ EL_EMPTY_SPACE_15,
+ EL_EMPTY_SPACE_16,
+ EL_SAND,
+ EL_WALL,
+ EL_BD_WALL,
+ EL_WALL_SLIPPERY,
+ EL_STEELWALL,
+ EL_AMOEBA_DEAD,
+ EL_QUICKSAND_EMPTY,
+ EL_QUICKSAND_FAST_EMPTY,
+ EL_STONEBLOCK,
+ EL_ROBOT_WHEEL,
+ EL_KEY_1,
+ EL_KEY_2,
+ EL_KEY_3,
+ EL_KEY_4,
+ EL_EM_KEY_1,
+ EL_EM_KEY_2,
+ EL_EM_KEY_3,
+ EL_EM_KEY_4,
+ EL_EMC_KEY_5,
+ EL_EMC_KEY_6,
+ EL_EMC_KEY_7,
+ EL_EMC_KEY_8,
+ EL_GATE_1,
+ EL_GATE_2,
+ EL_GATE_3,
+ EL_GATE_4,
+ EL_GATE_1_GRAY,
+ EL_GATE_2_GRAY,
+ EL_GATE_3_GRAY,
+ EL_GATE_4_GRAY,
+ EL_GATE_1_GRAY_ACTIVE,
+ EL_GATE_2_GRAY_ACTIVE,
+ EL_GATE_3_GRAY_ACTIVE,
+ EL_GATE_4_GRAY_ACTIVE,
+ EL_EM_GATE_1,
+ EL_EM_GATE_2,
+ EL_EM_GATE_3,
+ EL_EM_GATE_4,
+ EL_EM_GATE_1_GRAY,
+ EL_EM_GATE_2_GRAY,
+ EL_EM_GATE_3_GRAY,
+ EL_EM_GATE_4_GRAY,
+ EL_EM_GATE_1_GRAY_ACTIVE,
+ EL_EM_GATE_2_GRAY_ACTIVE,
+ EL_EM_GATE_3_GRAY_ACTIVE,
+ EL_EM_GATE_4_GRAY_ACTIVE,
+ EL_EMC_GATE_5,
+ EL_EMC_GATE_6,
+ EL_EMC_GATE_7,
+ EL_EMC_GATE_8,
+ EL_EMC_GATE_5_GRAY,
+ EL_EMC_GATE_6_GRAY,
+ EL_EMC_GATE_7_GRAY,
+ EL_EMC_GATE_8_GRAY,
+ EL_EMC_GATE_5_GRAY_ACTIVE,
+ EL_EMC_GATE_6_GRAY_ACTIVE,
+ EL_EMC_GATE_7_GRAY_ACTIVE,
+ EL_EMC_GATE_8_GRAY_ACTIVE,
+ EL_DC_GATE_WHITE,
+ EL_DC_GATE_WHITE_GRAY,
+ EL_DC_GATE_WHITE_GRAY_ACTIVE,
+ EL_DC_GATE_FAKE_GRAY,
+ EL_DYNAMITE,
+ EL_EM_DYNAMITE,
+ EL_INVISIBLE_STEELWALL,
+ EL_INVISIBLE_WALL,
+ EL_INVISIBLE_SAND,
+ EL_LAMP,
+ EL_LAMP_ACTIVE,
+ EL_WALL_EMERALD,
+ EL_WALL_DIAMOND,
+ EL_WALL_BD_DIAMOND,
+ EL_WALL_EMERALD_YELLOW,
+ EL_DYNABOMB_INCREASE_NUMBER,
+ EL_DYNABOMB_INCREASE_SIZE,
+ EL_DYNABOMB_INCREASE_POWER,
+#if 0
+ EL_SOKOBAN_OBJECT,
+#endif
+ EL_SOKOBAN_FIELD_EMPTY,
+ EL_SOKOBAN_FIELD_FULL,
+ EL_WALL_EMERALD_RED,
+ EL_WALL_EMERALD_PURPLE,
+ EL_ACID_POOL_TOPLEFT,
+ EL_ACID_POOL_TOPRIGHT,
+ EL_ACID_POOL_BOTTOMLEFT,
+ EL_ACID_POOL_BOTTOM,
+ EL_ACID_POOL_BOTTOMRIGHT,
+ EL_MAGIC_WALL,
+ EL_MAGIC_WALL_DEAD,
+ EL_BD_MAGIC_WALL,
+ EL_BD_MAGIC_WALL_DEAD,
+ EL_DC_MAGIC_WALL,
+ EL_DC_MAGIC_WALL_DEAD,
+ EL_AMOEBA_TO_DIAMOND,
+ EL_BLOCKED,
+ EL_SP_EMPTY,
+ EL_SP_BASE,
+ EL_SP_PORT_RIGHT,
+ EL_SP_PORT_DOWN,
+ EL_SP_PORT_LEFT,
+ EL_SP_PORT_UP,
+ EL_SP_GRAVITY_PORT_RIGHT,
+ EL_SP_GRAVITY_PORT_DOWN,
+ EL_SP_GRAVITY_PORT_LEFT,
+ EL_SP_GRAVITY_PORT_UP,
+ EL_SP_PORT_HORIZONTAL,
+ EL_SP_PORT_VERTICAL,
+ EL_SP_PORT_ANY,
+ EL_SP_DISK_RED,
+#if 0
+ EL_SP_DISK_YELLOW,
+#endif
+ EL_SP_CHIP_SINGLE,
+ EL_SP_CHIP_LEFT,
+ EL_SP_CHIP_RIGHT,
+ EL_SP_CHIP_TOP,
+ EL_SP_CHIP_BOTTOM,
+ EL_SP_HARDWARE_GRAY,
+ EL_SP_HARDWARE_GREEN,
+ EL_SP_HARDWARE_BLUE,
+ EL_SP_HARDWARE_RED,
+ EL_SP_HARDWARE_YELLOW,
+ EL_SP_HARDWARE_BASE_1,
+ EL_SP_HARDWARE_BASE_2,
+ EL_SP_HARDWARE_BASE_3,
+ EL_SP_HARDWARE_BASE_4,
+ EL_SP_HARDWARE_BASE_5,
+ EL_SP_HARDWARE_BASE_6,
+ EL_SP_GRAVITY_ON_PORT_LEFT,
+ EL_SP_GRAVITY_ON_PORT_RIGHT,
+ EL_SP_GRAVITY_ON_PORT_UP,
+ EL_SP_GRAVITY_ON_PORT_DOWN,
+ EL_SP_GRAVITY_OFF_PORT_LEFT,
+ EL_SP_GRAVITY_OFF_PORT_RIGHT,
+ EL_SP_GRAVITY_OFF_PORT_UP,
+ EL_SP_GRAVITY_OFF_PORT_DOWN,
+ EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
+ EL_CONVEYOR_BELT_4_SWITCH_LEFT,
+ EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
+ EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
+ EL_SIGN_EXCLAMATION,
+ EL_SIGN_RADIOACTIVITY,
+ EL_SIGN_STOP,
+ EL_SIGN_WHEELCHAIR,
+ EL_SIGN_PARKING,
+ EL_SIGN_NO_ENTRY,
+ EL_SIGN_UNUSED_1,
+ EL_SIGN_GIVE_WAY,
+ EL_SIGN_ENTRY_FORBIDDEN,
+ EL_SIGN_EMERGENCY_EXIT,
+ EL_SIGN_YIN_YANG,
+ EL_SIGN_UNUSED_2,
+ EL_SIGN_SPERMS,
+ EL_SIGN_BULLET,
+ EL_SIGN_HEART,
+ EL_SIGN_CROSS,
+ EL_SIGN_FRANKIE,
+ EL_DC_STEELWALL_1_LEFT,
+ EL_DC_STEELWALL_1_RIGHT,
+ EL_DC_STEELWALL_1_TOP,
+ EL_DC_STEELWALL_1_BOTTOM,
+ EL_DC_STEELWALL_1_HORIZONTAL,
+ EL_DC_STEELWALL_1_VERTICAL,
+ EL_DC_STEELWALL_1_TOPLEFT,
+ EL_DC_STEELWALL_1_TOPRIGHT,
+ EL_DC_STEELWALL_1_BOTTOMLEFT,
+ EL_DC_STEELWALL_1_BOTTOMRIGHT,
+ EL_DC_STEELWALL_1_TOPLEFT_2,
+ EL_DC_STEELWALL_1_TOPRIGHT_2,
+ EL_DC_STEELWALL_1_BOTTOMLEFT_2,
+ EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
+ EL_DC_STEELWALL_2_LEFT,
+ EL_DC_STEELWALL_2_RIGHT,
+ EL_DC_STEELWALL_2_TOP,
+ EL_DC_STEELWALL_2_BOTTOM,
+ EL_DC_STEELWALL_2_HORIZONTAL,
+ EL_DC_STEELWALL_2_VERTICAL,
+ EL_DC_STEELWALL_2_MIDDLE,
+ EL_DC_STEELWALL_2_SINGLE,
+ EL_STEELWALL_SLIPPERY,
+ EL_EMC_STEELWALL_1,
+ EL_EMC_STEELWALL_2,
+ EL_EMC_STEELWALL_3,
+ EL_EMC_STEELWALL_4,
+ EL_EMC_WALL_SLIPPERY_1,
+ EL_EMC_WALL_SLIPPERY_2,
+ EL_EMC_WALL_SLIPPERY_3,
+ EL_EMC_WALL_SLIPPERY_4,
+ EL_EMC_WALL_1,
+ EL_EMC_WALL_2,
+ EL_EMC_WALL_3,
+ EL_EMC_WALL_4,
+ EL_EMC_WALL_5,
+ EL_EMC_WALL_6,
+ EL_EMC_WALL_7,
+ EL_EMC_WALL_8,
+ EL_EMC_WALL_9,
+ EL_EMC_WALL_10,
+ EL_EMC_WALL_11,
+ EL_EMC_WALL_12,
+ EL_EMC_WALL_13,
+ EL_EMC_WALL_14,
+ EL_EMC_WALL_15,
+ EL_EMC_WALL_16,
+
+ -1
+ };
+
+ static int ep_em_slippery_wall[] =
+ {
+ -1
+ };
+
+ static int ep_gfx_crumbled[] =
+ {
+ EL_SAND,
+ EL_LANDMINE,
+ EL_DC_LANDMINE,
+ EL_TRAP,
+ EL_TRAP_ACTIVE,
+
+ -1
+ };
+
+ static int ep_editor_cascade_active[] =
+ {
+ EL_INTERNAL_CASCADE_BD_ACTIVE,
+ EL_INTERNAL_CASCADE_EM_ACTIVE,
+ EL_INTERNAL_CASCADE_EMC_ACTIVE,
+ EL_INTERNAL_CASCADE_RND_ACTIVE,
+ EL_INTERNAL_CASCADE_SB_ACTIVE,
+ EL_INTERNAL_CASCADE_SP_ACTIVE,
+ EL_INTERNAL_CASCADE_DC_ACTIVE,
+ EL_INTERNAL_CASCADE_DX_ACTIVE,
+ EL_INTERNAL_CASCADE_MM_ACTIVE,
+ EL_INTERNAL_CASCADE_DF_ACTIVE,
+ EL_INTERNAL_CASCADE_CHARS_ACTIVE,
+ EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
+ EL_INTERNAL_CASCADE_CE_ACTIVE,
+ EL_INTERNAL_CASCADE_GE_ACTIVE,
+ EL_INTERNAL_CASCADE_ES_ACTIVE,
+ EL_INTERNAL_CASCADE_REF_ACTIVE,
+ EL_INTERNAL_CASCADE_USER_ACTIVE,
+ EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
+
+ -1
+ };
+
+ static int ep_editor_cascade_inactive[] =
+ {
+ EL_INTERNAL_CASCADE_BD,
+ EL_INTERNAL_CASCADE_EM,
+ EL_INTERNAL_CASCADE_EMC,
+ EL_INTERNAL_CASCADE_RND,
+ EL_INTERNAL_CASCADE_SB,
+ EL_INTERNAL_CASCADE_SP,
+ EL_INTERNAL_CASCADE_DC,
+ EL_INTERNAL_CASCADE_DX,
+ EL_INTERNAL_CASCADE_MM,
+ EL_INTERNAL_CASCADE_DF,
+ EL_INTERNAL_CASCADE_CHARS,
+ EL_INTERNAL_CASCADE_STEEL_CHARS,
+ EL_INTERNAL_CASCADE_CE,
+ EL_INTERNAL_CASCADE_GE,
+ EL_INTERNAL_CASCADE_ES,
+ EL_INTERNAL_CASCADE_REF,
+ EL_INTERNAL_CASCADE_USER,
+ EL_INTERNAL_CASCADE_DYNAMIC,
+
+ -1
+ };
+
+ static int ep_obsolete[] =
+ {
+ EL_PLAYER_OBSOLETE,
+ EL_KEY_OBSOLETE,
+ EL_EM_KEY_1_FILE_OBSOLETE,
+ EL_EM_KEY_2_FILE_OBSOLETE,
+ EL_EM_KEY_3_FILE_OBSOLETE,
+ EL_EM_KEY_4_FILE_OBSOLETE,
+ EL_ENVELOPE_OBSOLETE,
+
+ -1
+ };
+
+ static struct
+ {
+ int *elements;
+ int property;
+ } element_properties[] =
+ {
+ { ep_diggable, EP_DIGGABLE },
+ { ep_collectible_only, EP_COLLECTIBLE_ONLY },
+ { ep_dont_run_into, EP_DONT_RUN_INTO },
+ { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
+ { ep_dont_touch, EP_DONT_TOUCH },
+ { ep_indestructible, EP_INDESTRUCTIBLE },
+ { ep_slippery, EP_SLIPPERY },
+ { ep_can_change, EP_CAN_CHANGE },
+ { ep_can_move, EP_CAN_MOVE },
+ { ep_can_fall, EP_CAN_FALL },
+ { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
+ { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
+ { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
+ { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
+ { ep_explodes_smashed, EP_EXPLODES_SMASHED },
+ { ep_explodes_impact, EP_EXPLODES_IMPACT },
+ { ep_walkable_over, EP_WALKABLE_OVER },
+ { ep_walkable_inside, EP_WALKABLE_INSIDE },
+ { ep_walkable_under, EP_WALKABLE_UNDER },
+ { ep_passable_over, EP_PASSABLE_OVER },
+ { ep_passable_inside, EP_PASSABLE_INSIDE },
+ { ep_passable_under, EP_PASSABLE_UNDER },
+ { ep_droppable, EP_DROPPABLE },
+ { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
+ { ep_pushable, EP_PUSHABLE },
+ { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
+ { ep_protected, EP_PROTECTED },
+ { ep_throwable, EP_THROWABLE },
+ { ep_can_explode, EP_CAN_EXPLODE },
+ { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
+
+ { ep_empty_space, EP_EMPTY_SPACE },
+ { ep_player, EP_PLAYER },
+ { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
+ { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
+ { ep_switchable, EP_SWITCHABLE },
+ { ep_bd_element, EP_BD_ELEMENT },
+ { ep_sp_element, EP_SP_ELEMENT },
+ { ep_sb_element, EP_SB_ELEMENT },
+ { ep_gem, EP_GEM },
+ { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
+ { ep_food_penguin, EP_FOOD_PENGUIN },
+ { ep_food_pig, EP_FOOD_PIG },
+ { ep_historic_wall, EP_HISTORIC_WALL },
+ { ep_historic_solid, EP_HISTORIC_SOLID },
+ { ep_classic_enemy, EP_CLASSIC_ENEMY },
+ { ep_belt, EP_BELT },
+ { ep_belt_active, EP_BELT_ACTIVE },
+ { ep_belt_switch, EP_BELT_SWITCH },
+ { ep_tube, EP_TUBE },
+ { ep_acid_pool, EP_ACID_POOL },
+ { ep_keygate, EP_KEYGATE },
+ { ep_amoeboid, EP_AMOEBOID },
+ { ep_amoebalive, EP_AMOEBALIVE },
+ { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
+ { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
+ { ep_can_grow, EP_CAN_GROW },
+ { ep_active_bomb, EP_ACTIVE_BOMB },
+ { ep_inactive, EP_INACTIVE },
+
+ { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
+
+ { ep_gfx_crumbled, EP_GFX_CRUMBLED },
+
+ { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
+ { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
+
+ { ep_obsolete, EP_OBSOLETE },
+
+ { NULL, -1 }
+ };
+
+ int i, j, k;
+
+ // always start with reliable default values (element has no properties)
+ // (but never initialize clipboard elements after the very first time)
+ // (to be able to use clipboard elements between several levels)
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
+ for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
+ SET_PROPERTY(i, j, FALSE);
+
+ // set all base element properties from above array definitions
+ for (i = 0; element_properties[i].elements != NULL; i++)
+ for (j = 0; (element_properties[i].elements)[j] != -1; j++)
+ SET_PROPERTY((element_properties[i].elements)[j],
+ element_properties[i].property, TRUE);
+
+ // copy properties to some elements that are only stored in level file
+ for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
+ for (j = 0; copy_properties[j][0] != -1; j++)
+ if (HAS_PROPERTY(copy_properties[j][0], i))
+ for (k = 1; k <= 4; k++)
+ SET_PROPERTY(copy_properties[j][k], i, TRUE);
+
+ // set static element properties that are not listed in array definitions
+ for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
+ SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
+
+ clipboard_elements_initialized = TRUE;
+}
+
+void InitElementPropertiesEngine(int engine_version)
+{
+ static int no_wall_properties[] =
+ {
+ EP_DIGGABLE,
+ EP_COLLECTIBLE_ONLY,
+ EP_DONT_RUN_INTO,
+ EP_DONT_COLLIDE_WITH,
+ EP_CAN_MOVE,
+ EP_CAN_FALL,
+ EP_CAN_SMASH_PLAYER,
+ EP_CAN_SMASH_ENEMIES,
+ EP_CAN_SMASH_EVERYTHING,
+ EP_PUSHABLE,
+
+ EP_PLAYER,
+ EP_GEM,
+ EP_FOOD_DARK_YAMYAM,
+ EP_FOOD_PENGUIN,
+ EP_BELT,
+ EP_BELT_ACTIVE,
+ EP_TUBE,
+ EP_AMOEBOID,
+ EP_AMOEBALIVE,
+ EP_ACTIVE_BOMB,
+
+ EP_ACCESSIBLE,
+
+ -1
+ };
+
+ int i, j;
+
+ /* important: after initialization in InitElementPropertiesStatic(), the
+ elements are not again initialized to a default value; therefore all
+ changes have to make sure that they leave the element with a defined
+ property (which means that conditional property changes must be set to
+ a reliable default value before) */
+
+ // resolve group elements
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ ResolveGroupElement(EL_GROUP_START + i);
+
+ // set all special, combined or engine dependent element properties
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ // do not change (already initialized) clipboard elements here
+ if (IS_CLIPBOARD_ELEMENT(i))
+ continue;
+
+ // ---------- INACTIVE ----------------------------------------------------
+ SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
+ i <= EL_CHAR_END) ||
+ (i >= EL_STEEL_CHAR_START &&
+ i <= EL_STEEL_CHAR_END)));
+
+ // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
+ SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
+ IS_WALKABLE_INSIDE(i) ||
+ IS_WALKABLE_UNDER(i)));
+
+ SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
+ IS_PASSABLE_INSIDE(i) ||
+ IS_PASSABLE_UNDER(i)));
+
+ SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
+ IS_PASSABLE_OVER(i)));
+
+ SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
+ IS_PASSABLE_INSIDE(i)));
+
+ SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
+ IS_PASSABLE_UNDER(i)));
+
+ SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
+ IS_PASSABLE(i)));
+
+ // ---------- COLLECTIBLE -------------------------------------------------
+ SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
+ IS_DROPPABLE(i) ||
+ IS_THROWABLE(i)));
+
+ // ---------- SNAPPABLE ---------------------------------------------------
+ SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
+ IS_COLLECTIBLE(i) ||
+ IS_SWITCHABLE(i) ||
+ i == EL_BD_ROCK));
+
+ // ---------- WALL --------------------------------------------------------
+ SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
+
+ for (j = 0; no_wall_properties[j] != -1; j++)
+ if (HAS_PROPERTY(i, no_wall_properties[j]) ||
+ i >= EL_FIRST_RUNTIME_UNREAL)
+ SET_PROPERTY(i, EP_WALL, FALSE);
+
+ if (IS_HISTORIC_WALL(i))
+ SET_PROPERTY(i, EP_WALL, TRUE);
+
+ // ---------- SOLID_FOR_PUSHING -------------------------------------------
+ if (engine_version < VERSION_IDENT(2,2,0,0))
+ SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
+ else
+ SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
+ !IS_DIGGABLE(i) &&
+ !IS_COLLECTIBLE(i)));
+
+ // ---------- DRAGONFIRE_PROOF --------------------------------------------
+ if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
+ SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
+ else
+ SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
+ i != EL_ACID));
+
+ // ---------- EXPLOSION_PROOF ---------------------------------------------
+ if (i == EL_FLAMES)
+ SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
+ else if (engine_version < VERSION_IDENT(2,2,0,0))
+ SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
+ else
+ SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
+ (!IS_WALKABLE(i) ||
+ IS_PROTECTED(i))));
+
+ if (IS_CUSTOM_ELEMENT(i))
+ {
+ // these are additional properties which are initially false when set
+
+ // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
+ if (DONT_TOUCH(i))
+ SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
+ if (DONT_COLLIDE_WITH(i))
+ SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
+
+ // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
+ if (CAN_SMASH_EVERYTHING(i))
+ SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
+ if (CAN_SMASH_ENEMIES(i))
+ SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
+ }
+
+ // ---------- CAN_SMASH ---------------------------------------------------
+ SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
+ CAN_SMASH_ENEMIES(i) ||
+ CAN_SMASH_EVERYTHING(i)));
+
+ // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
+ SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
+ EXPLODES_BY_FIRE(i)));
+
+ // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
+ SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
+ EXPLODES_SMASHED(i)));
+
+ // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
+ SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
+ EXPLODES_IMPACT(i)));
+
+ // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
+ SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
+
+ // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
+ SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
+ i == EL_BLACK_ORB));
+
+ // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
+ SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
+ CAN_MOVE(i) ||
+ IS_CUSTOM_ELEMENT(i)));
+
+ // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
+ SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
+ i == EL_SP_ELECTRON));
+
+ // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
+ if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
+ SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
+ getMoveIntoAcidProperty(&level, i));
+
+ // ---------- DONT_COLLIDE_WITH -------------------------------------------
+ if (MAYBE_DONT_COLLIDE_WITH(i))
+ SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
+ getDontCollideWithProperty(&level, i));
+
+ // ---------- SP_PORT -----------------------------------------------------
+ SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
+ IS_PASSABLE_INSIDE(i)));
+
+ // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
+ for (j = 0; j < level.num_android_clone_elements; j++)
+ SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
+ (!IS_EMPTY(i) &&
+ IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
+
+ // ---------- CAN_CHANGE --------------------------------------------------
+ SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
+ for (j = 0; j < element_info[i].num_change_pages; j++)
+ if (element_info[i].change_page[j].can_change)
+ SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
+
+ // ---------- HAS_ACTION --------------------------------------------------
+ SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
+ for (j = 0; j < element_info[i].num_change_pages; j++)
+ if (element_info[i].change_page[j].has_action)
+ SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
+
+ // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
+ SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
+ HAS_ACTION(i)));
+
+ // ---------- GFX_CRUMBLED ------------------------------------------------
+ SET_PROPERTY(i, EP_GFX_CRUMBLED,
+ element_info[i].crumbled[ACTION_DEFAULT] !=
+ element_info[i].graphic[ACTION_DEFAULT]);
+
+ // ---------- EDITOR_CASCADE ----------------------------------------------
+ SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
+ IS_EDITOR_CASCADE_INACTIVE(i)));
+ }
+
+ // dynamically adjust element properties according to game engine version
+ {
+ static int ep_em_slippery_wall[] =
+ {
+ EL_WALL,
+ EL_STEELWALL,
+ EL_EXPANDABLE_WALL,
+ EL_EXPANDABLE_WALL_HORIZONTAL,
+ EL_EXPANDABLE_WALL_VERTICAL,
+ EL_EXPANDABLE_WALL_ANY,
+ EL_EXPANDABLE_STEELWALL_HORIZONTAL,
+ EL_EXPANDABLE_STEELWALL_VERTICAL,
+ EL_EXPANDABLE_STEELWALL_ANY,
+ EL_EXPANDABLE_STEELWALL_GROWING,
+ -1
+ };
+
+ static int ep_em_explodes_by_fire[] =
+ {
+ EL_EM_DYNAMITE,
+ EL_EM_DYNAMITE_ACTIVE,
+ EL_MOLE,
+ -1
+ };
+
+ // special EM style gems behaviour
+ for (i = 0; ep_em_slippery_wall[i] != -1; i++)
+ SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
+ level.em_slippery_gems);
+
+ // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
+ SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
+ (level.em_slippery_gems &&
+ engine_version > VERSION_IDENT(2,0,1,0)));
+
+ // special EM style explosion behaviour regarding chain reactions
+ for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
+ SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
+ level.em_explodes_by_fire);
+ }
+
+ // this is needed because some graphics depend on element properties
+ if (game_status == GAME_MODE_PLAYING)
+ InitElementGraphicInfo();
+}
+
+void InitElementPropertiesGfxElement(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
+ }
+}
+
+static void InitGlobal(void)
+{
+ int graphic;
+ int i;
+
+ for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
+ {
+ // check if element_name_info entry defined for each element in "main.h"
+ if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
+ Fail("undefined 'element_name_info' entry for element %d", i);
+
+ element_info[i].token_name = element_name_info[i].token_name;
+ element_info[i].class_name = element_name_info[i].class_name;
+ element_info[i].editor_description= element_name_info[i].editor_description;
+ }
+
+ for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
+ {
+ // check if global_anim_name_info defined for each entry in "main.h"
+ if (i < NUM_GLOBAL_ANIM_TOKENS &&
+ global_anim_name_info[i].token_name == NULL)
+ Fail("undefined 'global_anim_name_info' entry for anim %d", i);
+
+ global_anim_info[i].token_name = global_anim_name_info[i].token_name;
+ }
+
+ // create hash to store URLs for global animations
+ anim_url_hash = newSetupFileHash();
+
+ // create hash from image config list
+ image_config_hash = newSetupFileHash();
+ for (i = 0; image_config[i].token != NULL; i++)
+ setHashEntry(image_config_hash,
+ image_config[i].token,
+ image_config[i].value);
+
+ // create hash from element token list
+ element_token_hash = newSetupFileHash();
+ for (i = 0; element_name_info[i].token_name != NULL; i++)
+ setHashEntry(element_token_hash,
+ element_name_info[i].token_name,
+ int2str(i, 0));
+
+ // create hash from graphic token list
+ graphic_token_hash = newSetupFileHash();
+ for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
+ if (strSuffix(image_config[i].value, ".png") ||
+ strSuffix(image_config[i].value, ".pcx") ||
+ strSuffix(image_config[i].value, ".wav") ||
+ strEqual(image_config[i].value, UNDEFINED_FILENAME))
+ setHashEntry(graphic_token_hash,
+ image_config[i].token,
+ int2str(graphic++, 0));
+
+ // create hash from font token list
+ font_token_hash = newSetupFileHash();
+ for (i = 0; font_info[i].token_name != NULL; i++)
+ setHashEntry(font_token_hash,
+ font_info[i].token_name,
+ int2str(i, 0));
+
+ // set default filenames for all cloned graphics in static configuration
+ for (i = 0; image_config[i].token != NULL; i++)
+ {
+ if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
+ {
+ char *token = image_config[i].token;
+ char *token_clone_from = getStringCat2(token, ".clone_from");
+ char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
+
+ if (token_cloned != NULL)
+ {
+ char *value_cloned = getHashEntry(image_config_hash, token_cloned);
+
+ if (value_cloned != NULL)
+ {
+ // set default filename in static configuration
+ image_config[i].value = value_cloned;
+
+ // set default filename in image config hash
+ setHashEntry(image_config_hash, token, value_cloned);
+ }
+ }
+
+ free(token_clone_from);
+ }
+ }
+
+ // always start with reliable default values (all elements)
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ ActiveElement[i] = i;
+
+ // now add all entries that have an active state (active elements)
+ for (i = 0; element_with_active_state[i].element != -1; i++)
+ {
+ int element = element_with_active_state[i].element;
+ int element_active = element_with_active_state[i].element_active;
+
+ ActiveElement[element] = element_active;
+ }
+
+ // always start with reliable default values (all buttons)
+ for (i = 0; i < NUM_IMAGE_FILES; i++)
+ ActiveButton[i] = i;
+
+ // now add all entries that have an active state (active buttons)
+ for (i = 0; button_with_active_state[i].button != -1; i++)
+ {
+ int button = button_with_active_state[i].button;
+ int button_active = button_with_active_state[i].button_active;
+
+ ActiveButton[button] = button_active;
+ }
+
+ // always start with reliable default values (all fonts)
+ for (i = 0; i < NUM_FONTS; i++)
+ ActiveFont[i] = i;
+
+ // now add all entries that have an active state (active fonts)
+ for (i = 0; font_with_active_state[i].font_nr != -1; i++)
+ {
+ int font = font_with_active_state[i].font_nr;
+ int font_active = font_with_active_state[i].font_nr_active;
+
+ ActiveFont[font] = font_active;
+ }
+
+ global.autoplay_leveldir = NULL;
+ global.patchtapes_leveldir = NULL;
+ global.convert_leveldir = NULL;
+ global.dumplevel_leveldir = NULL;
+ global.dumptape_leveldir = NULL;
+ global.create_sketch_images_dir = NULL;
+ global.create_collect_images_dir = NULL;
+
+ global.frames_per_second = 0;
+ global.show_frames_per_second = FALSE;
+
+ global.border_status = GAME_MODE_LOADING;
+ global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
+
+ global.use_envelope_request = FALSE;
+
+ global.user_names = NULL;
+}
+
+static void Execute_Command(char *command)
+{
+ int i;
+
+ if (strEqual(command, "print graphicsinfo.conf"))
+ {
+ Print("# You can configure additional/alternative image files here.\n");
+ Print("# (The entries below are default and therefore commented out.)\n");
+ Print("\n");
+ Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
+ Print("\n");
+ Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+ Print("\n");
+
+ for (i = 0; image_config[i].token != NULL; i++)
+ Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
+ image_config[i].value));
+
+ exit(0);
+ }
+ else if (strEqual(command, "print soundsinfo.conf"))
+ {
+ Print("# You can configure additional/alternative sound files here.\n");
+ Print("# (The entries below are default and therefore commented out.)\n");
+ Print("\n");
+ Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
+ Print("\n");
+ Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+ Print("\n");
+
+ for (i = 0; sound_config[i].token != NULL; i++)
+ Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
+ sound_config[i].value));
+
+ exit(0);
+ }
+ else if (strEqual(command, "print musicinfo.conf"))
+ {
+ Print("# You can configure additional/alternative music files here.\n");
+ Print("# (The entries below are default and therefore commented out.)\n");
+ Print("\n");
+ Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
+ Print("\n");
+ Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+ Print("\n");
+
+ for (i = 0; music_config[i].token != NULL; i++)
+ Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
+ music_config[i].value));
+
+ exit(0);
+ }
+ else if (strEqual(command, "print editorsetup.conf"))
+ {
+ Print("# You can configure your personal editor element list here.\n");
+ Print("# (The entries below are default and therefore commented out.)\n");
+ Print("\n");
+
+ // this is needed to be able to check element list for cascade elements
+ InitElementPropertiesStatic();
+ InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
+
+ PrintEditorElementList();
+
+ exit(0);
+ }
+ else if (strEqual(command, "print helpanim.conf"))
+ {
+ Print("# You can configure different element help animations here.\n");
+ Print("# (The entries below are default and therefore commented out.)\n");
+ Print("\n");
+
+ for (i = 0; helpanim_config[i].token != NULL; i++)
+ {
+ Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
+ helpanim_config[i].value));
+
+ if (strEqual(helpanim_config[i].token, "end"))
+ Print("#\n");
+ }
+
+ exit(0);
+ }
+ else if (strEqual(command, "print helptext.conf"))
+ {
+ Print("# You can configure different element help text here.\n");
+ Print("# (The entries below are default and therefore commented out.)\n");
+ Print("\n");
+
+ for (i = 0; helptext_config[i].token != NULL; i++)
+ Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
+ helptext_config[i].value));
+
+ exit(0);
+ }
+ else if (strPrefix(command, "dump level "))
+ {
+ char *filename = &command[11];
+
+ if (fileExists(filename))
+ {
+ LoadLevelFromFilename(&level, filename);
+ DumpLevel(&level);
+
+ exit(0);
+ }
+
+ char *leveldir = getStringCopy(filename); // read command parameters
+ char *level_nr = strchr(leveldir, ' ');
+
+ if (level_nr == NULL)
+ Fail("cannot open file '%s'", filename);
+
+ *level_nr++ = '\0';
+
+ global.dumplevel_leveldir = leveldir;
+ global.dumplevel_level_nr = atoi(level_nr);
+
+ program.headless = TRUE;
+ }
+ else if (strPrefix(command, "dump tape "))
+ {
+ char *filename = &command[10];
+
+ if (fileExists(filename))
+ {
+ LoadTapeFromFilename(filename);
+ DumpTape(&tape);
+
+ exit(0);
+ }
+
+ char *leveldir = getStringCopy(filename); // read command parameters
+ char *level_nr = strchr(leveldir, ' ');
+
+ if (level_nr == NULL)
+ Fail("cannot open file '%s'", filename);
+
+ *level_nr++ = '\0';
+
+ global.dumptape_leveldir = leveldir;
+ global.dumptape_level_nr = atoi(level_nr);
+
+ program.headless = TRUE;
+ }
+ else if (strPrefix(command, "autoplay ") ||
+ strPrefix(command, "autoffwd ") ||
+ strPrefix(command, "autowarp ") ||
+ strPrefix(command, "autotest ") ||
+ strPrefix(command, "autosave ") ||
+ strPrefix(command, "autoupload ") ||
+ strPrefix(command, "autofix "))
+ {
+ char *arg_ptr = strchr(command, ' ');
+ char *str_ptr = getStringCopy(arg_ptr); // read command parameters
+
+ global.autoplay_mode =
+ (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
+ strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
+ strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
+ strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
+ strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
+ strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
+ strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
+ AUTOPLAY_MODE_NONE);
+
+ while (*str_ptr != '\0') // continue parsing string
+ {
+ // cut leading whitespace from string, replace it by string terminator
+ while (*str_ptr == ' ' || *str_ptr == '\t')
+ *str_ptr++ = '\0';
+
+ if (*str_ptr == '\0') // end of string reached
+ break;
+
+ if (global.autoplay_leveldir == NULL) // read level set string
+ {
+ global.autoplay_leveldir = str_ptr;
+ global.autoplay_all = TRUE; // default: play all tapes
+
+ for (i = 0; i < MAX_TAPES_PER_SET; i++)
+ global.autoplay_level[i] = FALSE;
+ }
+ else // read level number string
+ {
+ int level_nr = atoi(str_ptr); // get level_nr value
+
+ if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
+ global.autoplay_level[level_nr] = TRUE;
+
+ global.autoplay_all = FALSE;
+ }
+
+ // advance string pointer to the next whitespace (or end of string)
+ while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
+ str_ptr++;
+ }
+
+ if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
+ program.headless = TRUE;
+ }
+ else if (strPrefix(command, "patch tapes "))
+ {
+ char *str_ptr = getStringCopy(&command[12]); // read command parameters
+
+ // skip leading whitespace
+ while (*str_ptr == ' ' || *str_ptr == '\t')
+ str_ptr++;
+
+ if (*str_ptr == '\0')
+ Fail("cannot find MODE in command '%s'", command);
+
+ global.patchtapes_mode = str_ptr; // store patch mode
+
+ // advance to next whitespace (or end of string)
+ while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
+ str_ptr++;
+
+ while (*str_ptr != '\0') // continue parsing string
+ {
+ // cut leading whitespace from string, replace it by string terminator
+ while (*str_ptr == ' ' || *str_ptr == '\t')
+ *str_ptr++ = '\0';
+
+ if (*str_ptr == '\0') // end of string reached
+ break;
+
+ if (global.patchtapes_leveldir == NULL) // read level set string
+ {
+ global.patchtapes_leveldir = str_ptr;
+ global.patchtapes_all = TRUE; // default: patch all tapes
+
+ for (i = 0; i < MAX_TAPES_PER_SET; i++)
+ global.patchtapes_level[i] = FALSE;
+ }
+ else // read level number string
+ {
+ int level_nr = atoi(str_ptr); // get level_nr value
+
+ if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
+ global.patchtapes_level[level_nr] = TRUE;
+
+ global.patchtapes_all = FALSE;
+ }
+
+ // advance string pointer to the next whitespace (or end of string)
+ while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
+ str_ptr++;
+ }
+
+ if (global.patchtapes_leveldir == NULL)
+ {
+ if (strEqual(global.patchtapes_mode, "help"))
+ global.patchtapes_leveldir = UNDEFINED_LEVELSET;
+ else
+ Fail("cannot find LEVELDIR in command '%s'", command);
+ }
+
+ program.headless = TRUE;
+ }
+ else if (strPrefix(command, "convert "))
+ {
+ char *str_copy = getStringCopy(strchr(command, ' ') + 1);
+ char *str_ptr = strchr(str_copy, ' ');
+
+ global.convert_leveldir = str_copy;
+ global.convert_level_nr = -1;
+
+ if (str_ptr != NULL) // level number follows
+ {
+ *str_ptr++ = '\0'; // terminate leveldir string
+ global.convert_level_nr = atoi(str_ptr); // get level_nr value
+ }
+
+ program.headless = TRUE;
+ }
+ else if (strPrefix(command, "create sketch images "))
+ {
+ global.create_sketch_images_dir = getStringCopy(&command[21]);
+
+ if (access(global.create_sketch_images_dir, W_OK) != 0)
+ Fail("image target directory '%s' not found or not writable",
+ global.create_sketch_images_dir);
+ }
+ else if (strPrefix(command, "create collect image "))
+ {
+ global.create_collect_images_dir = getStringCopy(&command[21]);
+
+ if (access(global.create_collect_images_dir, W_OK) != 0)
+ Fail("image target directory '%s' not found or not writable",
+ global.create_collect_images_dir);
+ }
+ else if (strPrefix(command, "create CE image "))
+ {
+ CreateCustomElementImages(&command[16]);
+
+ exit(0);
+ }
+ else
+ {
+ FailWithHelp("unrecognized command '%s'", command);
+ }
+
+ // disable networking if any valid command was recognized
+ options.network = setup.network_mode = FALSE;
+}
+
+static void InitSetup(void)
+{
+ LoadUserNames(); // global user names
+ LoadUserSetup(); // global user number
+
+ LoadSetup(); // global setup info
+
+ // set some options from setup file
+
+ if (setup.options.verbose)
+ options.verbose = TRUE;
+
+ if (setup.options.debug)
+ options.debug = TRUE;
+
+ if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
+ options.debug_mode = getStringCopy(setup.options.debug_mode);
+
+ if (setup.debug.show_frames_per_second)
+ global.show_frames_per_second = TRUE;
+}
+
+static void InitGameInfo(void)
+{
+ game.restart_level = FALSE;
+
+ game.request_active = FALSE;
+ game.request_active_or_moving = FALSE;
+
+ game.use_masked_elements_initial = FALSE;
+}
+
+static void InitPlayerInfo(void)
+{
+ int i;
+
+ // choose default local player
+ local_player = &stored_player[0];
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ stored_player[i].connected_locally = FALSE;
+ stored_player[i].connected_network = FALSE;
+ }
+
+ local_player->connected_locally = TRUE;
+}
+
+static void InitArtworkInfo(void)
+{
+ LoadArtworkInfo();
+}
+
+static char *get_string_in_brackets(char *string)
+{
+ char *string_in_brackets = checked_malloc(strlen(string) + 3);
+
+ sprintf(string_in_brackets, "[%s]", string);
+
+ return string_in_brackets;
+}
+
+static char *get_level_id_suffix(int id_nr)
+{
+ char *id_suffix = checked_malloc(1 + 3 + 1);
+
+ if (id_nr < 0 || id_nr > 999)
+ id_nr = 0;
+
+ sprintf(id_suffix, ".%03d", id_nr);
+
+ return id_suffix;
+}
+
+static void InitArtworkConfig(void)
+{
+ static char *image_id_prefix[MAX_NUM_ELEMENTS +
+ NUM_FONTS +
+ NUM_GLOBAL_ANIM_TOKENS + 1];
+ static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
+ NUM_GLOBAL_ANIM_TOKENS + 1];
+ static char *music_id_prefix[NUM_MUSIC_PREFIXES +
+ NUM_GLOBAL_ANIM_TOKENS + 1];
+ static char *action_id_suffix[NUM_ACTIONS + 1];
+ static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
+ static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
+ static char *level_id_suffix[MAX_LEVELS + 1];
+ static char *dummy[1] = { NULL };
+ static char *ignore_generic_tokens[] =
+ {
+ "name",
+ "sort_priority",
+ "program_title",
+ "program_copyright",
+ "program_company",
+
+ NULL
+ };
+ static char **ignore_image_tokens;
+ static char **ignore_sound_tokens;
+ static char **ignore_music_tokens;
+ int num_ignore_generic_tokens;
+ int num_ignore_image_tokens;
+ int num_ignore_sound_tokens;
+ int num_ignore_music_tokens;
+ int i;
+
+ // dynamically determine list of generic tokens to be ignored
+ num_ignore_generic_tokens = 0;
+ for (i = 0; ignore_generic_tokens[i] != NULL; i++)
+ num_ignore_generic_tokens++;
+
+ // dynamically determine list of image tokens to be ignored
+ num_ignore_image_tokens = num_ignore_generic_tokens;
+ for (i = 0; image_config_vars[i].token != NULL; i++)
+ num_ignore_image_tokens++;
+ ignore_image_tokens =
+ checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
+ for (i = 0; i < num_ignore_generic_tokens; i++)
+ ignore_image_tokens[i] = ignore_generic_tokens[i];
+ for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
+ ignore_image_tokens[num_ignore_generic_tokens + i] =
+ image_config_vars[i].token;
+ ignore_image_tokens[num_ignore_image_tokens] = NULL;
+
+ // dynamically determine list of sound tokens to be ignored
+ num_ignore_sound_tokens = num_ignore_generic_tokens;
+ ignore_sound_tokens =
+ checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
+ for (i = 0; i < num_ignore_generic_tokens; i++)
+ ignore_sound_tokens[i] = ignore_generic_tokens[i];
+ ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
+
+ // dynamically determine list of music tokens to be ignored
+ num_ignore_music_tokens = num_ignore_generic_tokens;
+ ignore_music_tokens =
+ checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
+ for (i = 0; i < num_ignore_generic_tokens; i++)
+ ignore_music_tokens[i] = ignore_generic_tokens[i];
+ ignore_music_tokens[num_ignore_music_tokens] = NULL;
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ image_id_prefix[i] = element_info[i].token_name;
+ for (i = 0; i < NUM_FONTS; i++)
+ image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
+ for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
+ image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
+ global_anim_info[i].token_name;
+ image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ sound_id_prefix[i] = element_info[i].token_name;
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ sound_id_prefix[MAX_NUM_ELEMENTS + i] =
+ get_string_in_brackets(element_info[i].class_name);
+ for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
+ sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
+ global_anim_info[i].token_name;
+ sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
+
+ for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
+ music_id_prefix[i] = music_prefix_info[i].prefix;
+ for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
+ music_id_prefix[NUM_MUSIC_PREFIXES + i] =
+ global_anim_info[i].token_name;
+ music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
+
+ for (i = 0; i < NUM_ACTIONS; i++)
+ action_id_suffix[i] = element_action_info[i].suffix;
+ action_id_suffix[NUM_ACTIONS] = NULL;
+
+ for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
+ direction_id_suffix[i] = element_direction_info[i].suffix;
+ direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
+
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ special_id_suffix[i] = special_suffix_info[i].suffix;
+ special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
+
+ for (i = 0; i < MAX_LEVELS; i++)
+ level_id_suffix[i] = get_level_id_suffix(i);
+ level_id_suffix[MAX_LEVELS] = NULL;
+
+ InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
+ image_id_prefix, action_id_suffix, direction_id_suffix,
+ special_id_suffix, ignore_image_tokens);
+ InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
+ sound_id_prefix, action_id_suffix, dummy,
+ special_id_suffix, ignore_sound_tokens);
+ InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
+ music_id_prefix, action_id_suffix, special_id_suffix,
+ level_id_suffix, ignore_music_tokens);
+}
+
+static void InitMixer(void)
+{
+ OpenAudio();
+
+ StartMixer();
+}
+
+static void InitVideoOverlay(void)
+{
+ // if virtual buttons are not loaded from setup file, repeat initializing
+ // virtual buttons grid with default values now that video is initialized
+ if (!setup.touch.grid_initialized)
+ InitSetup();
+
+ InitTileCursorInfo();
+ InitOverlayInfo();
+}
+
+void InitGfxBuffers(void)
+{
+ static int win_xsize_last = -1;
+ static int win_ysize_last = -1;
+
+ // create additional image buffers for double-buffering and cross-fading
+
+ if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
+ {
+ // used to temporarily store the backbuffer -- only re-create if changed
+ ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
+ ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
+
+ win_xsize_last = WIN_XSIZE;
+ win_ysize_last = WIN_YSIZE;
+ }
+
+ ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
+ ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
+ ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
+
+ // initialize screen properties
+ InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
+ REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+ bitmap_db_field);
+ InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
+ InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
+ InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
+ InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
+ InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
+ InitGfxClipRegion(FALSE, -1, -1, -1, -1);
+
+ // required if door size definitions have changed
+ InitGraphicCompatibilityInfo_Doors();
+
+ InitGfxBuffers_EM();
+ InitGfxBuffers_SP();
+ InitGfxBuffers_MM();
+}
+
+static void InitGfx(void)
+{
+ struct GraphicInfo *graphic_info_last = graphic_info;
+ char *filename_font_initial = NULL;
+ char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
+ char *image_token[NUM_INITIAL_IMAGES] =
+ {
+ CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
+ CONFIG_TOKEN_GLOBAL_BUSY,
+ CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
+ CONFIG_TOKEN_BACKGROUND,
+ CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
+ CONFIG_TOKEN_BACKGROUND_LOADING
+ };
+ struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
+ {
+ &init.busy_initial,
+ &init.busy,
+ &init.busy_playfield
+ };
+ Bitmap *bitmap_font_initial = NULL;
+ int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
+ int i, j, k;
+
+ // determine settings for initial font (for displaying startup messages)
+ for (i = 0; image_config[i].token != NULL; i++)
+ {
+ for (j = 0; j < NUM_INITIAL_FONTS; j++)
+ {
+ char font_token[128];
+ int len_font_token;
+
+ sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
+ len_font_token = strlen(font_token);
+
+ if (strEqual(image_config[i].token, font_token))
+ {
+ filename_font_initial = image_config[i].value;
+ }
+ else if (strlen(image_config[i].token) > len_font_token &&
+ strncmp(image_config[i].token, font_token, len_font_token) == 0)
+ {
+ if (strEqual(&image_config[i].token[len_font_token], ".x"))
+ font_initial[j].src_x = atoi(image_config[i].value);
+ else if (strEqual(&image_config[i].token[len_font_token], ".y"))
+ font_initial[j].src_y = atoi(image_config[i].value);
+ else if (strEqual(&image_config[i].token[len_font_token], ".width"))
+ font_initial[j].width = atoi(image_config[i].value);
+ else if (strEqual(&image_config[i].token[len_font_token], ".height"))
+ font_initial[j].height = atoi(image_config[i].value);
+ }
+ }
+ }
+
+ for (j = 0; j < NUM_INITIAL_FONTS; j++)
+ {
+ font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
+ font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
+ }
+
+ if (filename_font_initial == NULL) // should not happen
+ Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
+
+ InitGfxBuffers();
+ InitGfxCustomArtworkInfo();
+ InitGfxOtherSettings();
+
+ InitGfxTileSizeInfo(TILESIZE, TILESIZE);
+
+ bitmap_font_initial = LoadCustomImage(filename_font_initial);
+
+ for (j = 0; j < NUM_INITIAL_FONTS; j++)
+ font_initial[j].bitmap = bitmap_font_initial;
+
+ InitFontGraphicInfo();
+
+ InitMenuDesignSettings_Static();
+
+ // initialize settings for initial images with default values
+ for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+ for (j = 0; j < NUM_GFX_ARGS; j++)
+ parameter[i][j] =
+ get_graphic_parameter_value(image_config_suffix[j].value,
+ image_config_suffix[j].token,
+ image_config_suffix[j].type);
+
+ // read settings for initial images from default custom artwork config
+ char *gfx_config_filename = getPath3(options.graphics_directory,
+ GFX_DEFAULT_SUBDIR,
+ GRAPHICSINFO_FILENAME);
+
+ if (fileExists(gfx_config_filename))
+ {
+ SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
+
+ if (setup_file_hash)
+ {
+ for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+ {
+ char *filename = getHashEntry(setup_file_hash, image_token[i]);
+
+ if (filename)
+ {
+ filename_image_initial[i] = getStringCopy(filename);
+
+ for (j = 0; image_config_suffix[j].token != NULL; j++)
+ {
+ int type = image_config_suffix[j].type;
+ char *suffix = image_config_suffix[j].token;
+ char *token = getStringCat2(image_token[i], suffix);
+ char *value = getHashEntry(setup_file_hash, token);
+
+ checked_free(token);
+
+ if (value)
+ parameter[i][j] =
+ get_graphic_parameter_value(value, suffix, type);
+ }
+ }
+ }
+
+ // read values from custom graphics config file
+ InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
+
+ freeSetupFileHash(setup_file_hash);
+ }
+ }
+
+ for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+ {
+ if (filename_image_initial[i] == NULL)
+ {
+ int len_token = strlen(image_token[i]);
+
+ // read settings for initial images from static default artwork config
+ for (j = 0; image_config[j].token != NULL; j++)
+ {
+ if (strEqual(image_config[j].token, image_token[i]))
+ {
+ filename_image_initial[i] = getStringCopy(image_config[j].value);
+ }
+ else if (strlen(image_config[j].token) > len_token &&
+ strncmp(image_config[j].token, image_token[i], len_token) == 0)
+ {
+ for (k = 0; image_config_suffix[k].token != NULL; k++)
+ {
+ if (strEqual(&image_config[j].token[len_token],
+ image_config_suffix[k].token))
+ parameter[i][k] =
+ get_graphic_parameter_value(image_config[j].value,
+ image_config_suffix[k].token,
+ image_config_suffix[k].type);
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+ {
+ if (filename_image_initial[i] == NULL) // should not happen
+ Fail("cannot get filename for '%s'", image_token[i]);
+
+ image_initial[i].bitmaps =
+ checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
+
+ if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
+ image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
+ LoadCustomImage(filename_image_initial[i]);
+
+ checked_free(filename_image_initial[i]);
+ }
+
+ for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+ image_initial[i].use_image_size = TRUE;
+
+ graphic_info = image_initial; // graphic == 0 => image_initial
+
+ for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+ set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
+
+ graphic_info = graphic_info_last;
+
+ for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
+ {
+ // set image size for busy animations
+ init_busy[i]->width = image_initial[i].width;
+ init_busy[i]->height = image_initial[i].height;
+ }
+
+ SetLoadingBackgroundImage();
+
+ ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+ DrawProgramInfo();
+
+ InitGfxDrawBusyAnimFunction(DrawInitAnim);
+ InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
+ InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
+ InitGfxDrawTileCursorFunction(DrawTileCursor);
+ InitGfxDrawEnvelopeRequestFunction(DrawEnvelopeRequestToScreen);
+
+ gfx.fade_border_source_status = global.border_status;
+ gfx.fade_border_target_status = global.border_status;
+ gfx.masked_border_bitmap_ptr = backbuffer;
+
+ // use copy of busy animation to prevent change while reloading artwork
+ init_last = init;
+}
+
+static void InitGfxBackground(void)
+{
+ fieldbuffer = bitmap_db_field;
+ SetDrawtoField(DRAW_TO_BACKBUFFER);
+
+ ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+ redraw_mask = REDRAW_ALL;
+}
+
+static void InitLevelInfo(void)
+{
+ LoadLevelInfo(); // global level info
+ LoadLevelSetup_LastSeries(); // last played series info
+ LoadLevelSetup_SeriesInfo(); // last played level info
+
+ if (global.autoplay_leveldir &&
+ global.autoplay_mode != AUTOPLAY_MODE_TEST)
+ {
+ leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
+ global.autoplay_leveldir);
+ if (leveldir_current == NULL)
+ leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
+ }
+
+ SetLevelSetInfo(leveldir_current->identifier, level_nr);
+}
+
+static void InitLevelArtworkInfo(void)
+{
+ LoadLevelArtworkInfo();
+}
+
+static void InitImages(void)
+{
+ print_timestamp_init("InitImages");
+
+#if 0
+ Debug("init:InitImages", "leveldir_current->identifier == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
+ Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
+ Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
+ Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
+ leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
+#endif
+
+ setLevelArtworkDir(artwork.gfx_first);
+
+#if 0
+ Debug("init:InitImages", "leveldir_current->identifier == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
+ Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
+ Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
+ Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
+ leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
+#endif
+
+#if 0
+ Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
+ leveldir_current->identifier,
+ artwork.gfx_current_identifier,
+ artwork.gfx_current->identifier,
+ leveldir_current->graphics_set,
+ leveldir_current->graphics_path);
+#endif
+
+ UPDATE_BUSY_STATE();
+
+ ReloadCustomImages();
+ print_timestamp_time("ReloadCustomImages");
+
+ UPDATE_BUSY_STATE();
+
+ LoadCustomElementDescriptions();
+ print_timestamp_time("LoadCustomElementDescriptions");
+
+ UPDATE_BUSY_STATE();
+
+ LoadMenuDesignSettings();
+ print_timestamp_time("LoadMenuDesignSettings");
+
+ UPDATE_BUSY_STATE();
+
+ ReinitializeGraphics();
+ print_timestamp_time("ReinitializeGraphics");
+
+ LoadMenuDesignSettings_AfterGraphics();
+ print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
+
+ UPDATE_BUSY_STATE();
+
+ print_timestamp_done("InitImages");
+}
+
+static void InitSound(void)
+{
+ print_timestamp_init("InitSound");
+
+ // set artwork path to send it to the sound server process
+ setLevelArtworkDir(artwork.snd_first);
+
+ InitReloadCustomSounds();
+ print_timestamp_time("InitReloadCustomSounds");
+
+ ReinitializeSounds();
+ print_timestamp_time("ReinitializeSounds");
+
+ print_timestamp_done("InitSound");
+}
+
+static void InitMusic(void)
+{
+ print_timestamp_init("InitMusic");
+
+ // set artwork path to send it to the sound server process
+ setLevelArtworkDir(artwork.mus_first);
+
+ InitReloadCustomMusic();
+ print_timestamp_time("InitReloadCustomMusic");
+
+ ReinitializeMusic();
+ print_timestamp_time("ReinitializeMusic");
+
+ print_timestamp_done("InitMusic");
+}
+
+static void InitArtworkDone(void)
+{
+ if (program.headless)
+ return;
+
+ InitGlobalAnimations();
+}
+
+static void InitNetworkSettings(void)
+{
+ boolean network_enabled = (options.network || setup.network_mode);
+ char *network_server = (options.server_host != NULL ? options.server_host :
+ setup.network_server_hostname);
+
+ if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
+ network_server = NULL;
+
+ InitNetworkInfo(network_enabled,
+ FALSE,
+ options.serveronly,
+ network_server,
+ options.server_port);
+}
+
+void InitNetworkServer(void)
+{
+ if (!network.enabled || network.connected)
+ return;
+
+ LimitScreenUpdates(FALSE);
+
+ if (game_status == GAME_MODE_LOADING)
+ DrawProgramInfo();
+
+ if (!ConnectToServer(network.server_host, network.server_port))
+ {
+ network.enabled = FALSE;
+
+ setup.network_mode = FALSE;
+ }
+ else
+ {
+ SendToServer_ProtocolVersion();
+ SendToServer_PlayerName(setup.player_name);
+ SendToServer_NrWanted(setup.network_player_nr + 1);
+
+ network.connected = TRUE;
+ }
+
+ // short time to recognize result of network initialization
+ if (game_status == GAME_MODE_LOADING)
+ Delay_WithScreenUpdates(1000);
+}
+
+static boolean CheckArtworkConfigForCustomElements(char *filename)
+{
+ SetupFileHash *setup_file_hash;
+ boolean redefined_ce_found = FALSE;
+
+ // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
+
+ if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
+ {
+ BEGIN_HASH_ITERATION(setup_file_hash, itr)
+ {
+ char *token = HASH_ITERATION_TOKEN(itr);
+
+ if (strPrefix(token, "custom_"))
+ {
+ redefined_ce_found = TRUE;
+
+ break;
+ }
+ }
+ END_HASH_ITERATION(setup_file_hash, itr)
+
+ freeSetupFileHash(setup_file_hash);
+ }
+
+ return redefined_ce_found;
+}
+
+static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
+{
+ char *filename_base, *filename_local;
+ boolean redefined_ce_found = FALSE;
+
+ setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
+
+#if 0
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "leveldir_current->identifier == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "leveldir_current->graphics_path == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "leveldir_current->graphics_set == '%s'",
+ leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
+ leveldir_current == NULL ? "[NULL]" :
+ LEVELDIR_ARTWORK_SET(leveldir_current, type));
+#endif
+
+ // first look for special artwork configured in level series config
+ filename_base = getCustomArtworkLevelConfigFilename(type);
+
+#if 0
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "filename_base == '%s'", filename_base);
+#endif
+
+ if (fileExists(filename_base))
+ redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
+
+ filename_local = getCustomArtworkConfigFilename(type);
+
+#if 0
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "filename_local == '%s'", filename_local);
+#endif
+
+ if (filename_local != NULL && !strEqual(filename_base, filename_local))
+ redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
+
+#if 0
+ Debug("init:CheckArtworkTypeForRedefinedCustomElements",
+ "redefined_ce_found == %d", redefined_ce_found);
+#endif
+
+ return redefined_ce_found;
+}
+
+static void InitOverrideArtwork(void)
+{
+ boolean redefined_ce_found = FALSE;
+
+ // to check if this level set redefines any CEs, do not use overriding
+ gfx.override_level_graphics = FALSE;
+ gfx.override_level_sounds = FALSE;
+ gfx.override_level_music = FALSE;
+
+ // now check if this level set has definitions for custom elements
+ if (setup.override_level_graphics == AUTO ||
+ setup.override_level_sounds == AUTO ||
+ setup.override_level_music == AUTO)
+ redefined_ce_found =
+ (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
+ CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
+ CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
+
+#if 0
+ Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
+ redefined_ce_found);
+#endif
+
+ if (redefined_ce_found)
+ {
+ // this level set has CE definitions: change "AUTO" to "FALSE"
+ gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
+ gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
+ gfx.override_level_music = (setup.override_level_music == TRUE);
+ }
+ else
+ {
+ // this level set has no CE definitions: change "AUTO" to "TRUE"
+ gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
+ gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
+ gfx.override_level_music = (setup.override_level_music != FALSE);
+ }
+
+#if 0
+ Debug("init:InitOverrideArtwork", "%d, %d, %d",
+ gfx.override_level_graphics,
+ gfx.override_level_sounds,
+ gfx.override_level_music);
+#endif
+}
+
+static char *setNewArtworkIdentifier(int type)
+{
+ static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
+ static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
+ static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
+ static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
+ static boolean initialized[3] = { FALSE, FALSE, FALSE };
+ TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
+ boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
+ char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
+ char *leveldir_identifier = leveldir_current->identifier;
+ // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
+ char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
+ boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
+ TreeInfo *custom_artwork_set =
+ getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
+ boolean has_custom_artwork_set = (custom_artwork_set != NULL);
+ char *artwork_current_identifier;
+ char *artwork_new_identifier = NULL; // default: nothing has changed
+
+ // leveldir_current may be invalid (level group, parent link)
+ if (!validLevelSeries(leveldir_current))
+ return NULL;
+
+ /* 1st step: determine artwork set to be activated in descending order:
+ --------------------------------------------------------------------
+ 1. setup artwork (when configured to override everything else)
+ 2. artwork set configured in "levelinfo.conf" of current level set
+ (artwork in level directory will have priority when loading later)
+ 3. artwork in level directory (stored in artwork sub-directory)
+ 4. setup artwork (currently configured in setup menu) */
+
+ if (setup_override_artwork)
+ artwork_current_identifier = setup_artwork_set;
+ else if (has_level_artwork_set)
+ artwork_current_identifier = leveldir_artwork_set;
+ else if (has_custom_artwork_set)
+ artwork_current_identifier = leveldir_identifier;
+ else
+ artwork_current_identifier = setup_artwork_set;
+
+ /* 2nd step: check if it is really needed to reload artwork set
+ ------------------------------------------------------------ */
+
+ // ---------- reload if level set and also artwork set has changed ----------
+ if (last_leveldir_identifier[type] != leveldir_identifier &&
+ (last_has_custom_artwork_set[type] || has_custom_artwork_set))
+ artwork_new_identifier = artwork_current_identifier;
+
+ last_leveldir_identifier[type] = leveldir_identifier;
+ last_has_custom_artwork_set[type] = has_custom_artwork_set;
+
+ // ---------- reload if "override artwork" setting has changed --------------
+ if (last_override_level_artwork[type] != setup_override_artwork)
+ artwork_new_identifier = artwork_current_identifier;
+
+ last_override_level_artwork[type] = setup_override_artwork;
+
+ // ---------- reload if current artwork identifier has changed --------------
+ if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
+ artwork_new_identifier = artwork_current_identifier;
+
+ // (we cannot compare string pointers here, so copy string content itself)
+ setString(&last_artwork_identifier[type], artwork_current_identifier);
+
+ // ---------- set new artwork identifier ----------
+ *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
+
+ // ---------- do not reload directly after starting -------------------------
+ if (!initialized[type])
+ artwork_new_identifier = NULL;
+
+ initialized[type] = TRUE;
+
+ return artwork_new_identifier;
+}
+
+static void InitArtworkIdentifier(void)
+{
+ setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
+ setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
+ setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
+}
+
+void ReloadCustomArtwork(int force_reload)
+{
+ int last_game_status = game_status; // save current game status
+ char *gfx_new_identifier;
+ char *snd_new_identifier;
+ char *mus_new_identifier;
+ boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
+ boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
+ boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
+ boolean reload_needed;
+
+ InitOverrideArtwork();
+
+ AdjustGraphicsForEMC();
+ AdjustSoundsForEMC();
+
+ gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
+ snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
+ mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
+
+ reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
+ snd_new_identifier != NULL || force_reload_snd ||
+ mus_new_identifier != NULL || force_reload_mus);
+
+ if (!reload_needed)
+ return;
+
+ print_timestamp_init("ReloadCustomArtwork");
+
+ SetGameStatus(GAME_MODE_LOADING);
+
+ FadeOut(REDRAW_ALL);
+
+ SetLoadingBackgroundImage();
+
+ ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
+ print_timestamp_time("ClearRectangleOnBackground");
+
+ FadeIn(REDRAW_ALL);
+
+ UPDATE_BUSY_STATE();
+
+ InitMissingFileHash();
+
+ if (gfx_new_identifier != NULL || force_reload_gfx)
+ {
+#if 0
+ Debug("init:ReloadCustomArtwork",
+ "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
+ artwork.gfx_current_identifier,
+ gfx_new_identifier,
+ artwork.gfx_current->identifier,
+ leveldir_current->graphics_set);
+#endif
+
+ InitImages();
+ print_timestamp_time("InitImages");
+ }
+
+ if (snd_new_identifier != NULL || force_reload_snd)
+ {
+ InitSound();
+ print_timestamp_time("InitSound");
+ }
+
+ if (mus_new_identifier != NULL || force_reload_mus)
+ {
+ InitMusic();
+ print_timestamp_time("InitMusic");
+ }
+
+ InitArtworkDone();
+
+ SetGameStatus(last_game_status); // restore current game status
+
+ FadeOut(REDRAW_ALL);
+
+ RedrawGlobalBorder();
+
+ // force redraw of (open or closed) door graphics
+ SetDoorState(DOOR_OPEN_ALL);
+ CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
+
+ FadeSetEnterScreen();
+ FadeSkipNextFadeOut();
+
+ print_timestamp_done("ReloadCustomArtwork");
+
+ LimitScreenUpdates(FALSE);
+}
+
+void KeyboardAutoRepeatOffUnlessAutoplay(void)
+{
+ if (global.autoplay_leveldir == NULL)
+ KeyboardAutoRepeatOff();
+}
+
+void DisplayExitMessage(char *format, va_list ap)
+{
+ // also check for initialized video (headless flag may be temporarily unset)
+ if (program.headless || !video.initialized)
+ return;
+
+ // check if draw buffer and fonts for exit message are already available
+ if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
+ return;
+
+ int font_1 = FC_RED;
+ int font_2 = FC_YELLOW;
+ int font_3 = FC_BLUE;
+ int font_width = getFontWidth(font_2);
+ int font_height = getFontHeight(font_2);
+ int sx = SX;
+ int sy = SY;
+ int sxsize = WIN_XSIZE - 2 * sx;
+ int sysize = WIN_YSIZE - 2 * sy;
+ int line_length = sxsize / font_width;
+ int max_lines = sysize / font_height;
+ int num_lines_printed;
+
+ gfx.sx = sx;
+ gfx.sy = sy;
+ gfx.sxsize = sxsize;
+ gfx.sysize = sysize;
+
+ sy = 20;
+
+ ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+ DrawTextSCentered(sy, font_1, "Fatal error:");
+ sy += 3 * font_height;;
+
+ num_lines_printed =
+ DrawTextBufferVA(sx, sy, format, ap, font_2,
+ line_length, line_length, max_lines,
+ 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
+ sy += (num_lines_printed + 3) * font_height;
+
+ DrawTextSCentered(sy, font_1, "For details, see the following error file:");
+ sy += 3 * font_height;
+
+ num_lines_printed =
+ DrawTextBuffer(sx, sy, program.log_filename, font_2,
+ line_length, line_length, max_lines,
+ 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
+
+ DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
+
+ redraw_mask = REDRAW_ALL;
+
+ // force drawing exit message even if screen updates are currently limited
+ LimitScreenUpdates(FALSE);
+
+ BackToFront();
+
+ // deactivate toons on error message screen
+ setup.toons = FALSE;
+
+ WaitForEventToContinue();
+}
+
+
+// ============================================================================
+// OpenAll()
+// ============================================================================
+
+void OpenAll(void)
+{
+ print_timestamp_init("OpenAll");
+
+ SetGameStatus(GAME_MODE_LOADING);
+
+ InitCounter();
+
+ InitGlobal(); // initialize some global variables
+
+ InitRND(NEW_RANDOMIZE);
+ InitSimpleRandom(NEW_RANDOMIZE);
+ InitBetterRandom(NEW_RANDOMIZE);
+
+ InitMissingFileHash();
+
+ print_timestamp_time("[init global stuff]");
+
+ InitSetup();
+
+ print_timestamp_time("[init setup/config stuff (1)]");
+
+ if (options.execute_command)
+ Execute_Command(options.execute_command);
+
+ InitNetworkSettings();
+
+ InitRuntimeInfo();
+
+ if (network.serveronly)
+ {
+#if defined(PLATFORM_UNIX)
+ NetworkServer(network.server_port, TRUE);
+#else
+ Warn("networking only supported in Unix version");
+#endif
+
+ exit(0); // never reached, server loops forever
+ }
+
+ InitGameInfo();
+ print_timestamp_time("[init setup/config stuff (2)]");
+ InitPlayerInfo();
+ print_timestamp_time("[init setup/config stuff (3)]");
+ InitArtworkInfo(); // needed before loading gfx, sound & music
+ print_timestamp_time("[init setup/config stuff (4)]");
+ InitArtworkConfig(); // needed before forking sound child process
+ print_timestamp_time("[init setup/config stuff (5)]");
+ InitMixer();
+ print_timestamp_time("[init setup/config stuff (6)]");
+
+ InitJoysticks();
+
+ print_timestamp_time("[init setup/config stuff]");
+
+ InitVideoDefaults();
+ InitVideoDisplay();
+ InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
+ InitVideoOverlay();
+
+ InitEventFilter(FilterMouseMotionEvents);
+
+ print_timestamp_time("[init video stuff]");
+
+ InitElementPropertiesStatic();
+ InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
+ InitElementPropertiesGfxElement();
+
+ print_timestamp_time("[init element properties stuff]");
+
+ InitGfx();
+
+ print_timestamp_time("InitGfx");
+
+ InitLevelInfo();
+ print_timestamp_time("InitLevelInfo");
+
+ InitLevelArtworkInfo();
+ print_timestamp_time("InitLevelArtworkInfo");
+
+ InitOverrideArtwork(); // needs to know current level directory
+ print_timestamp_time("InitOverrideArtwork");
+
+ InitArtworkIdentifier(); // needs to know current level directory
+ print_timestamp_time("InitArtworkIdentifier");
+
+ InitImages(); // needs to know current level directory
+ print_timestamp_time("InitImages");
+
+ InitSound(); // needs to know current level directory
+ print_timestamp_time("InitSound");
+
+ InitMusic(); // needs to know current level directory
+ print_timestamp_time("InitMusic");
+
+ InitArtworkDone();
+
+ InitGfxBackground();
+
+ em_open_all();
+ sp_open_all();
+ mm_open_all();
+
+ if (global.autoplay_leveldir)
+ {
+ AutoPlayTapes();
+ return;
+ }
+ else if (global.patchtapes_leveldir)
+ {
+ PatchTapes();
+ return;
+ }
+ else if (global.convert_leveldir)
+ {
+ ConvertLevels();
+ return;
+ }
+ else if (global.dumplevel_leveldir)
+ {
+ DumpLevels();
+ return;
+ }
+ else if (global.dumptape_leveldir)
+ {
+ DumpTapes();
+ return;
+ }
+ else if (global.create_sketch_images_dir)
+ {
+ CreateLevelSketchImages();
+ return;
+ }
+ else if (global.create_collect_images_dir)
+ {
+ CreateCollectElementImages();
+ return;
+ }
+
+ InitNetworkServer();
+
+ SetGameStatus(GAME_MODE_MAIN);
+
+ FadeSetEnterScreen();
+ if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
+ FadeSkipNextFadeOut();
+
+ print_timestamp_time("[post-artwork]");
+
+ print_timestamp_done("OpenAll");
+
+ if (setup.ask_for_remaining_tapes)
+ setup.ask_for_uploading_tapes = TRUE;
+
+ DrawMainMenu();
+
+#if 0
+ Debug("internal:path", "SDL_GetBasePath() == '%s'",
+ SDL_GetBasePath());
+ Debug("internal:path", "SDL_GetPrefPath() == '%s'",
+ SDL_GetPrefPath("artsoft", "rocksndiamonds"));
+#if defined(PLATFORM_ANDROID)
+ Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
+ SDL_AndroidGetInternalStoragePath());
+ Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
+ SDL_AndroidGetExternalStoragePath());
+ Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
+ (SDL_AndroidGetExternalStorageState() &
+ SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
+ SDL_AndroidGetExternalStorageState() &
+ SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
+#endif
+#endif
+}
+
+static boolean WaitForApiThreads(void)
+{
+ DelayCounter thread_delay = { 10000 };
+
+ if (program.api_thread_count == 0)
+ return TRUE;
+
+ // deactivate global animations (not accessible in game state "loading")
+ setup.toons = FALSE;
+
+ // set game state to "loading" to be able to show busy animation
+ SetGameStatus(GAME_MODE_LOADING);
+
+ ResetDelayCounter(&thread_delay);
+
+ // wait for threads to finish (and fail on timeout)
+ while (program.api_thread_count > 0)
+ {
+ if (DelayReached(&thread_delay))
+ {
+ Error("failed waiting for threads - TIMEOUT");
+
+ return FALSE;
+ }
+
+ UPDATE_BUSY_STATE();
+
+ Delay(20);
+ }
+
+ return TRUE;
+}
+
+void CloseAllAndExit(int exit_value)
+{
+ WaitForApiThreads();
+
+ StopSounds();
+ FreeAllSounds();
+ FreeAllMusic();
+ CloseAudio(); // called after freeing sounds (needed for SDL)
+
+ em_close_all();
+ sp_close_all();
+
+ FreeAllImages();
+
+ // !!! TODO !!!
+ // set a flag to tell the network server thread to quit and wait for it
+ // using SDL_WaitThread()
+ //
+ // Code used with SDL 1.2:
+ // if (network.server_thread) // terminate network server
+ // SDL_KillThread(network.server_thread);
+
+ CloseVideoDisplay();
+ ClosePlatformDependentStuff();
+
+ if (exit_value != 0 && !options.execute_command)
+ {
+ // fall back to default level set (current set may have caused an error)
+ SaveLevelSetup_LastSeries_Deactivate();
+
+ // tell user where to find error log file which may contain more details
+ // (error notification now directly displayed on screen inside R'n'D
+ // NotifyUserAboutErrorFile(); // currently only works for Windows
+ }