/* * Hit the ground hard? */ static int thing_hit_ground (levelp level, thingp t, thingp it) { if (t->fall_speed > THING_FALL_SPEED_HIT_MONST) { if (thing_is_monst(it)) { THING_LOG(t, "fell (%f) and landed on monst", t->fall_speed); (void) thing_hit(level, it, t, 1); } } if (t->falling_too_fast) { t->falling_too_fast = 0; if (thing_is_player(t) || thing_is_monst(t) || thing_is_bomb(t)) { THING_LOG(t, "fell too fast (%f) and hit the ground", t->fall_speed); (void) thing_hit(level, t, it, 1); } } t->fall_speed = 0; return (true); }
void thing_modify_spending_points (thingp t, int val) { if (!thing_is_player(t)) { return; } thing_stats_modify_spending_points(t, val); if (val > 0) { MSG_SERVER_SHOUT_AT(INFO, t, 0, 0, "%%%%fg=green$+%d spending points", val); MSG_SERVER_SHOUT_AT(INFO, t, 0, 0, "%%%%fg=gray$Press s to spend points"); } }
static void thing_dead_ (levelp level, thingp t, char *reason) { /* * Detach from the owner */ thing_remove_hooks(level, t); if (!reason) { ERR("thing %s dead for no reason? why? why? why?!", thing_logname(t)); } if (thing_is_dungeon_floor(t) || thing_is_ladder(t) || thing_is_door(t) || thing_is_cloud_effect(t) || thing_is_explosion(t) || thing_is_projectile(t) || thing_is_rock(t) || thing_is_wall(t)) { /* * Too boring to log. */ } else { THING_LOG(t, "Dead, why(%s)", reason); } if (thing_is_player(t)) { /* * Tell the poor player they've croaked it. */ hiscore_try_to_add("Happless human", reason, t->score); } const char *sound = tp_sound_on_death(thing_tp(t)); if (sound) { MSG_SHOUT_AT(SOUND, t, t->x, t->y, "%s", sound); } /* * Move the weapon behind the poor thing. */ thing_set_weapon_placement(level, t); myfree(reason); }
void thing_wield_shield (thingp t, tpp shield) { thingp existing_shield = thing_shield_carry_anim(t); if (existing_shield) { THING_LOG(t, "wield with existing shield %s", thing_logname(existing_shield)); } if (!shield) { THING_ERR(t, "could not wield null pointer!"); return; } const char *carry_as = tp_shield_carry_anim(shield); if (!carry_as) { THING_ERR(t, "could not wield shield %s", tp_short_name(shield)); return; } tpp what = tp_find(carry_as); if (!what) { THING_ERR(t, "Could not find %s to wield", carry_as); return; } if (t->shield == shield) { if (t->on_server) { if (thing_wid(existing_shield)) { /* * Add onto existing shield. */ thing_stats_modify_hp(existing_shield, tp_get_stats_max_hp(what)); return; } } else { return; } } if (thing_is_player(t)) { THING_LOG(t, "wield shield %s", tp_short_name(shield)); } t->shield = shield; widp shield_carry_anim_wid; if (t->on_server) { shield_carry_anim_wid = wid_game_map_server_replace_tile( wid_game_map_server_grid_container, t->x, t->y, existing_shield, /* thing */ what, 0, /* tpp data */ 0 /* item */, 0 /* stats */); } else { shield_carry_anim_wid = wid_game_map_client_replace_tile( wid_game_map_client_grid_container, t->x, t->y, 0, what); } if (!shield_carry_anim_wid) { THING_ERR(t, "failed wield shield %s", tp_short_name(shield)); return; } /* * Save the thing id so the client wid can keep track of the shield. */ thingp child = wid_get_thing(shield_carry_anim_wid); thing_set_shield_carry_anim(t, child); child->dir = t->dir; /* * Attach to the thing. */ thing_set_owner(child, t); if (t->on_server) { thing_update(t); } }
void thing_dead (levelp level, thingp t, thingp killer, const char *reason, ...) { /* * If in a shop, this might be the shopkeeper. */ thingp owner = thing_owner(level, t); /* * If an arrow, this might be an elf. */ thingp real_killer = 0; if (killer) { real_killer = thing_owner(level, killer); if (!real_killer) { real_killer = killer; } } va_list args; verify(t); tpp tp = thing_tp(t); /* * If the reason of death was collection, some things we do not want * to do. */ if (!t->is_collected) { /* * When it dies, doth it polymorph and thus avoid the reaper? * e.g. a mob spawner dying and creating a smaller one. */ const char *polymorph = tp_polymorph_on_death(tp); if (thing_is_sawblade(t) && killer) { /* * Skip polymorph if there is a killer. We want the blades to * just vanish and not get more bloody. That only happens if * there is no killer and we force a polymorph. */ } else if (polymorph) { tpp what = tp_find(polymorph); if (!what) { ERR("could now find %s to polymorph into on %s death", polymorph, thing_logname(t)); } /* * It doth polymorph. */ t->tp_id = tp_to_id(what); t->hp = what->max_hp; return; } /* * Or perhaps it does die, but spawns something else, like the * player dying and creating a mob spawner. */ const char *spawn = tp_spawn_on_death(tp); if (spawn) { thingp newt = thing_mob_spawn_on_death(level, t); /* * If this is the player death then give the gravestone a lot of * health or it can be immediately killed by a lingering explosion * that killed the player too. */ if (newt && thing_is_player(t)) { newt->hp = 200; } } } /* * If a wall is gone, remove the decorations. */ if (thing_is_wall(t)) { if (!level->is_being_destroyed) { /* * Keep walls on edge of level to stop things falling off. */ if ((int)t->x <= 0) { return; } if ((int)t->x >= MAP_WIDTH-1) { return; } if ((int)t->y <= 0) { return; } if ((int)t->y >= MAP_HEIGHT-1) { return; } int dx, dy; for (dx = -1; dx <= 1; dx++) { for (dy = -1; dy <= 1; dy++) { widp w; while (map_find_wall_deco_at(level, t->x + dx, t->y + dy, &w)) { thing_destroy(level, wid_get_thing(w), __FUNCTION__); } } } } } /* * You only die once. */ if (thing_is_dead(t)) { return; } thing_set_is_dead(t, true); /* * Bounty for the killer? */ uint32_t xp = tp_get_bonus_score_on_death(tp); if (xp && real_killer) { /* * Did someone throw this weapon and gets the xp? */ int32_t val = tp_get_bonus_score_on_death(tp); if (val) { real_killer->score += val; if (thing_is_player(real_killer)) { #if 0 if (thing_is_cloud_effect(killer)) { /* * Too many packets if we kill a lot of things in one * go. * * But it looks nice... 8) */ } else { #endif MSG_SHOUT_AT(OVER_THING, t, 0, 0, "%%%%font=%s$%%%%fg=%s$+%d XP", "large", "gold", val); #if 0 } #endif } } } /* * Flash briefly red on death. */ if (thing_is_monst(t) || thing_is_mob_spawner(t) || thing_is_rock(t) || thing_is_wall(t) || thing_is_door(t)) { widp w = t->wid; if (w) { wid_set_mode(w, WID_MODE_ACTIVE); if (!wid_is_hidden(w)) { wid_set_color(w, WID_COLOR_BLIT, RED); } } } /* * Boom! If this bomb is not being collected then make it blow up. */ { #if 0 if (thing_is_treasure(t)) { CON("%s destroyed",thing_logname(t)); if (owner) { CON(" %s owner is keeper",thing_logname(owner)); } if (killer) { CON(" %s killer ",thing_logname(killer)); } if (real_killer) { CON(" %s real_killer ",thing_logname(real_killer)); } } #endif if (!t->is_collected) { if (thing_is_bomb(t) || thing_is_fireball(t) || thing_is_bullet(t)) { level_place_explosion(level, 0, /* owner */ thing_tp(t), t->x, t->y, t->x, t->y); } /* * Breaking stuff in a shop? bad idea. */ if (thing_is_treasure(t)) { if (owner && thing_is_shopkeeper(owner)) { if (thing_is_player(real_killer)) { shop_break_message(level, real_killer, owner); } else { shop_whodunnit_break_message(level, real_killer, owner); } } } } else { /* * Collecting a thing? */ if (thing_is_treasure(t)) { if (owner && thing_is_shopkeeper(owner)) { if (thing_is_player(real_killer)) { shop_collect_message(level, real_killer, t); } } } } } /* * Stop bouncing or swaying. */ if (t->wid) { if (tp_is_effect_pulse(tp)) { wid_scaling_to_pct_in(t->wid, 1.0, 1.0, 0, 0); } if (tp_is_effect_sway(tp)) { wid_rotate_to_pct_in(t->wid, 0, 0, 0, 0); } } /* * Log the means of death! */ if (reason) { va_start(args, reason); thing_dead_(level, t, dynvprintf(reason, args)); va_end(args); } else { thing_dead_(level, t, 0); } }
void thing_reached_teleport (levelp level, thingp t, thingp teleport) { int tx[MAP_WIDTH*MAP_HEIGHT]; int ty[MAP_WIDTH*MAP_HEIGHT]; int poss = 0; int x, y; int delay = 20; if (!thing_is_player(t)) { /* * Only move other things sometimes. */ if ((myrand() % 100) < 95) { return; } } if (!time_have_x_tenths_passed_since(delay, t->timestamp_last_teleport)) { return; } for (x = 0; x < MAP_WIDTH; x++) { for (y = 0; y < MAP_HEIGHT; y++) { level_map_tile *tile = &level->map_grid.tile[x][y][MAP_DEPTH_EXPLOSION]; tpp it = tile->tp; if (!it) { continue; } if (!tp_is_teleport(it)) { continue; } if (DISTANCE(t->x, t->y, x, y) < 2.0) { continue; } color tcol; color col; tcol = teleport->data.col; col = tile->data.col; if (color_none(col) || color_none(tcol)) { tx[poss] = x; ty[poss] = y; poss++; continue; } if (color_cmp(col, tcol)) { tx[poss] = x; ty[poss] = y; poss++; continue; } } } if (!poss) { return; } t->timestamp_last_teleport = time_get_time_ms(); poss = myrand() % poss; double nx = tx[poss]; double ny = ty[poss]; /* * Don't let lava spawn continually on top of itself. */ if (thing_is_lava(t) && map_is_lava_at(level, nx, ny)) { return; } /* * Don't let water spawn continually on top of itself. */ if (thing_is_water(t) && map_is_water_at(level, nx, ny)) { return; } /* * Don't let acid spawn continually on top of itself. */ if (thing_is_acid(t) && map_is_acid_at(level, nx, ny)) { return; } /* * Don't let water spawn continually on top of itself. */ if (thing_is_water(t) && map_is_water_at(level, nx, ny)) { return; } /* * Don't let monsters spawn continually on top of themselves. */ if (thing_is_monst(t) && map_is_monst_at(level, nx, ny)) { return; } THING_LOG(t, "Teleport"); wid_move_end(t->wid); thing_wid_update(level, t, nx, ny, false /* smooth */, true /* is_new */); /* * Make sure the client sees the jump */ thing_handle_collisions(level, t); MSG_SHOUT_AT(SOUND, t, t->x, t->y, "teleport"); }
void thing_stats_check_for_changes (thingp t) { if (!thing_is_player(t)) { return; } int ranged_modifier = thing_stats_val_to_modifier(thing_stats_get_attack_ranged(t)); switch (ranged_modifier) { case 2: if (!thing_has_ability_reverse_shot(t)) { t->has_ability_reverse_shot = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$reverse shot ability unlocked", "vlarge", "green"); } break; case 3: if (!thing_has_ability_double_shot(t)) { t->has_ability_double_shot = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$double shot ability unlocked", "vlarge", "green"); } break; case 4: if (!thing_has_ability_triple_shot(t)) { t->has_ability_triple_shot = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$triple shot ability unlocked", "vlarge", "green"); } break; case 5: if (!thing_has_ability_burst_shot(t)) { t->has_ability_burst_shot = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$burst shot ability unlocked", "vlarge", "green"); } break; } int melee_modifier = thing_stats_val_to_modifier(thing_stats_get_attack_melee(t)); switch (melee_modifier) { case 2: if (!thing_has_ability_reverse_swing(t)) { t->has_ability_reverse_swing = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$reverse swing ability unlocked", "vlarge", "green"); } break; case 3: if (!thing_has_ability_double_speed_swing(t)) { t->has_ability_double_speed_swing = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$double speed swing ability unlocked", "vlarge", "green"); } break; case 4: if (!thing_has_ability_triple_speed_swing(t)) { t->has_ability_triple_speed_swing = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$triple speed swing unlocked", "vlarge", "green"); } break; case 5: if (!thing_has_ability_double_damage_swing(t)) { t->has_ability_double_damage_swing = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$double damage swing unlocked", "vlarge", "green"); } break; } int health_modifier = thing_stats_val_to_modifier(thing_stats_get_toughness(t)); switch (health_modifier) { case 2: if (!thing_has_ability_rage(t)) { t->has_ability_rage = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$rage when quarter health unlocked", "vlarge", "green"); } break; case 3: if (!thing_has_ability_perma_rage(t)) { t->has_ability_perma_rage = true; MSG_SERVER_SHOUT_AT(POPUP, t, 0, 0, "%%%%font=%s$%%%%fg=%s$permanent rage ability unlocked", "vlarge", "green"); } break; } }
void thing_reached_teleport (thingp t, thingp teleport) { int tx[MAP_WIDTH*MAP_HEIGHT]; int ty[MAP_WIDTH*MAP_HEIGHT]; int poss = 0; int x, y; int delay = 20; if (!thing_is_player(t)) { /* * Only move other things sometimes. */ if ((myrand() % 100) < 95) { return; } } if (!time_have_x_tenths_passed_since(delay, t->timestamp_last_teleport)) { return; } for (x = 0; x < MAP_WIDTH; x++) { for (y = 0; y < MAP_HEIGHT; y++) { level_map_tile *tile = &server_level->map_grid.tile[x][y][MAP_DEPTH_EXPLOSION]; tpp it = tile->tp; if (!it) { continue; } if (!tp_is_teleport(it)) { continue; } if (DISTANCE(t->x, t->y, x, y) < 2.0) { continue; } const char *tcol; const char *col; if (teleport->data) { tcol = teleport->data->col_name; } else { tcol = 0; } col = tile->data.col_name; if (!col || !tcol) { tx[poss] = x; ty[poss] = y; poss++; continue; } if (!strcmp(col, tcol)) { tx[poss] = x; ty[poss] = y; poss++; continue; } } } if (!poss) { return; } t->timestamp_last_teleport = time_get_time_ms(); poss = myrand() % poss; double nx = tx[poss]; double ny = ty[poss]; /* * Don't let lava spawn continually on top of itself. */ if (thing_is_lava(t) && map_is_lava_at(server_level, nx, ny)) { return; } /* * Don't let acid spawn continually on top of itself. */ if (thing_is_acid(t) && map_is_acid_at(server_level, nx, ny)) { return; } /* * Don't let water spawn continually on top of itself. */ if (thing_is_water(t) && map_is_water_at(server_level, nx, ny)) { return; } /* * Don't let monsters spawn continually on top of themselves. */ if (thing_is_monst(t) && map_is_monst_at(server_level, nx, ny)) { return; } THING_LOG(t, "teleport"); wid_move_end(t->wid); thing_server_wid_update(t, nx, ny, true /* is_new */); thing_update(t); /* * Make sure the client sees the jump */ t->needs_tx_refresh_xy_and_template_id = 1; thing_handle_collisions(wid_game_map_server_grid_container, t); MSG_SERVER_SHOUT_AT_ALL_PLAYERS(SOUND, t->x, t->y, "teleport"); }
/* * Try to fall down */ int thing_fall (levelp level, thingp t) { double x = t->x; double y = t->y + 0.015; thingp it; if (thing_is_monst(t) || thing_is_ladder(t) || thing_is_player(t)) { it = thing_overlaps(level, t, t->x, t->y, thing_is_ladder); if (it) { thing_hit_ground(level, t, it); return (false); } } if (t->jump_speed) { t->fall_speed = 0; return (false); } if (thing_is_monst(t) || thing_is_player(t)) { if (t->fall_speed > THING_FALL_SPEED_HIT_SPIKES) { it = thing_overlaps(level, t, t->x, t->y, thing_is_spikes); if (it) { THING_LOG(t, "fell onto spikes (%f)", t->fall_speed); (void) thing_hit(level, t, it, 1); } } } it = thing_hit_fall_obstacle(level, t, x, y); if (it) { thing_hit_ground(level, t, it); return (false); } t->fall_speed += THING_FALL_SPEED_GRAVITY; t->falling_too_fast = false; if (t->fall_speed - t->jump_speed > THING_FALL_SPEED_TOO_FAST) { t->falling_too_fast = true; } if (t->fall_speed > 1) { t->fall_speed = 1; } y = t->y + t->fall_speed; it = thing_hit_fall_obstacle(level, t, x, y); if (it) { y = t->y + t->fall_speed / 2; it = thing_hit_fall_obstacle(level, t, x, y); if (it) { y = t->y + t->fall_speed / 4; it = thing_hit_fall_obstacle(level, t, x, y); if (it) { y = t->y + t->fall_speed / 4; it = thing_hit_fall_obstacle(level, t, x, y); if (it) { thing_hit_ground(level, t, it); return (false); } } } } thing_wid_update(level, t, x, y, true, false /* is new */); thing_handle_collisions(level, t); return (true); }