diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 0a5af7469..f75ab2db1 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3907,6 +3907,10 @@ Values are \(lqautodetect\(rq, \(lqdefault\(rq, or \(lqvga\(rq. Setting \(lqvga\(rq (or \(lqautodetect\(rq with vga hardware present) will cause the game to display tiles. Cannot be set with the \(oqO\(cq command. +.lp video_height +Set the VGA mode resolution height (MS-DOS only, with video:vga) +.lp video_width +Set the VGA mode resolution width (MS-DOS only, with video:vga) .lp videocolors Set the color palette for PC systems using NO_TERMS (default 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7760385e7..f362f36b3 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4342,6 +4342,12 @@ Setting {\it vga\/} (or {\it autodetect\/} with vga hardware present) will cause the game to display tiles. Cannot be set with the `{\tt O}' command. %.lp +\item[\ib{video\verb+_+height}] +Set the VGA mode resolution height (MS-DOS only, with video:vga) +%.lp +\item[\ib{video\verb+_+width}] +Set the VGA mode resolution width (MS-DOS only, with video:vga) +%.lp \item[\ib{videocolors}] \begin{sloppypar} Set the color palette for PC systems using NO\verb+_+TERMS diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 1f822bc9d..513f78178 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.74 $ $NHDT-Date: 1579914040 2020/01/25 01:00:40 $ +$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.76 $ $NHDT-Date: 1580043420 2020/01/26 12:57:00 $ General Fixes and Modified Features ----------------------------------- @@ -47,6 +47,7 @@ if at the edge of the map window, trying to move farther fails but used a turn hero can no longer wear blindfold/towel/lenses when poly'd into headless form revamp achievement tracking for exploring Mine's End and Sokoban (by acquiring luckstone and bag of holding or amulet of reflection, respectively) +throttle long worm growth rate and HP accumulation Fixes to 3.7.0-x Problems that Were Exposed Via git Repository @@ -110,8 +111,8 @@ when hallucinating, see hallucinated liquids when looking at water or lava on the map applying a spellbook hints about read charges left wizard mode wishing for level topology can now create hidden doors (ask for - "secret door" when at a door or wall location) and hidden corridor - spots ("secret corridor" at a corridor location) + "secret door" when at a door or wall location), hidden corridor + spots ("secret corridor" at a corridor location), and clouds tiny chance for randomly created spellbooks to be Discworld novels instead of having only one in the first book or scroll shop created (won't occur in hero's initial inventory or NPC priest inventory or be diff --git a/include/extern.h b/include/extern.h index a21b76988..ef9f64838 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1578895332 2020/01/13 06:02:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.785 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1580044333 2020/01/26 13:12:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.786 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1437,7 +1437,7 @@ E int FDECL(mfndpos, (struct monst *, coord *, long *, long)); E boolean FDECL(monnear, (struct monst *, int, int)); E void NDECL(dmonsfree); E void FDECL(elemental_clog, (struct monst *)); -E int FDECL(mcalcmove, (struct monst *)); +E int FDECL(mcalcmove, (struct monst *, BOOLEAN_P)); E void NDECL(mcalcdistress); E void FDECL(replmon, (struct monst *, struct monst *)); E void FDECL(relmon, (struct monst *, struct monst **)); diff --git a/include/flag.h b/include/flag.h index 0cfd3dabc..d92bd5dc8 100644 --- a/include/flag.h +++ b/include/flag.h @@ -367,6 +367,10 @@ struct instance_flags { int wc_map_mode; /* specify map viewing options, mostly * for backward compatibility */ int wc_player_selection; /* method of choosing character */ +#if defined(MSDOS) + unsigned wc_video_width; /* X resolution of screen */ + unsigned wc_video_height; /* Y resolution of screen */ +#endif boolean wc_splash_screen; /* display an opening splash screen or not */ boolean wc_popup_dialog; /* put queries in pop up dialogs instead of * in the message window */ diff --git a/include/obj.h b/include/obj.h index 6b0d9c5e9..1cf298dba 100644 --- a/include/obj.h +++ b/include/obj.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 obj.h $NHDT-Date: 1508827590 2017/10/24 06:46:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.6 obj.h $NHDT-Date: 1580036271 2020/01/26 10:57:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.66 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -114,7 +114,6 @@ struct obj { #define leashmon corpsenm /* gets m_id of attached pet */ #define fromsink corpsenm /* a potion from a sink */ #define novelidx corpsenm /* 3.6 tribute - the index of the novel title */ -#define record_achieve_special corpsenm int usecount; /* overloaded for various things that tally */ #define spestudied usecount /* # of times a spellbook has been studied */ unsigned oeaten; /* nutrition left in food, if partly eaten */ diff --git a/include/rm.h b/include/rm.h index 09553de13..f660ec83b 100644 --- a/include/rm.h +++ b/include/rm.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 rm.h $NHDT-Date: 1578258722 2020/01/05 21:12:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ +/* NetHack 3.6 rm.h $NHDT-Date: 1580070206 2020/01/26 20:23:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.78 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -528,16 +528,19 @@ struct rm { #define SV7 0x80 #define SVALL 0xFF -#define doormask flags -#define altarmask flags -#define wall_info flags -#define ladder flags -#define drawbridgemask flags -#define looted flags -#define icedpool flags - +/* if these get changed or expanded, make sure wizard-mode wishing becomes + aware of the new usage */ +#define doormask flags /* door, sdoor (note conflict with wall_info) */ +#define altarmask flags /* alignment and maybe temple */ +#define wall_info flags /* wall, sdoor (note conflict with doormask) */ +#define ladder flags /* up or down */ +#define drawbridgemask flags /* what's underneath when the span is open */ +#define looted flags /* used for throne, tree, fountain, sink, door */ +#define icedpool flags /* used for ice (in case it melts) */ +/* horizonal applies to walls, doors (including sdoor); also to iron bars + even though they don't have separate symbols for horizontal and vertical */ #define blessedftn horizontal /* a fountain that grants attribs */ -#define disturbed horizontal /* a grave that has been disturbed */ +#define disturbed horizontal /* a grave that has been disturbed */ struct damage { struct damage *next; diff --git a/include/tileset.h b/include/tileset.h index 2ab145e66..9f6e86b5b 100644 --- a/include/tileset.h +++ b/include/tileset.h @@ -18,9 +18,15 @@ struct TileImage { boolean FDECL(read_tiles, (const char *filename, BOOLEAN_P true_color)); const struct Pixel *NDECL(get_palette); +boolean FDECL(set_tile_type, (BOOLEAN_P true_color)); void NDECL(free_tiles); const struct TileImage *FDECL(get_tile, (unsigned tile_index)); +/* For resizing tiles */ +struct TileImage *FDECL(stretch_tile, (const struct TileImage *, + unsigned, unsigned)); +void FDECL(free_tile, (struct TileImage *)); + /* Used internally by the tile set code */ struct TileSetImage { /* Image data */ diff --git a/src/allmain.c b/src/allmain.c index 5998e38b8..22faeabd7 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 allmain.c $NHDT-Date: 1578448653 2020/01/08 01:57:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.6 allmain.c $NHDT-Date: 1580044340 2020/01/26 13:12:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.138 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -125,7 +125,7 @@ boolean resuming; to skip dead monsters here because they will have been purged at end of their previous round of moving */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - mtmp->movement += mcalcmove(mtmp); + mtmp->movement += mcalcmove(mtmp, TRUE); /* occasionally add another monster; since this takes place after movement has been allotted, the new @@ -139,7 +139,7 @@ boolean resuming; /* calculate how much time passed. */ if (u.usteed && u.umoved) { /* your speed doesn't augment steed's speed */ - moveamt = mcalcmove(u.usteed); + moveamt = mcalcmove(u.usteed, TRUE); } else { moveamt = g.youmonst.data->mmove; diff --git a/src/mon.c b/src/mon.c index 6fd50d7f7..e2d571f5e 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1577759850 2019/12/31 02:37:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.319 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1580044343 2020/01/26 13:12:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.320 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -633,9 +633,11 @@ register struct monst *mtmp; return 0; } +/* calculate 'mon's movement for current turn; called from moveloop() */ int -mcalcmove(mon) +mcalcmove(mon, m_moving) struct monst *mon; +boolean m_moving; /* True: adjust for moving; False: just adjust for speed */ { int mmove = mon->data->mmove; int mmove_adj; @@ -656,17 +658,18 @@ struct monst *mon; mmove = ((rn2(2) ? 4 : 5) * mmove) / 3; } - /* Randomly round the monster's speed to a multiple of NORMAL_SPEED. - This makes it impossible for the player to predict when they'll get - a free turn (thus preventing exploits like "melee kiting"), while - retaining guarantees about shopkeepers not being outsped by a - normal-speed player, normal-speed players being unable to open up - a gap when fleeing a normal-speed monster, etc. */ - mmove_adj = mmove % NORMAL_SPEED; - mmove -= mmove_adj; - if (rn2(NORMAL_SPEED) < mmove_adj) - mmove += NORMAL_SPEED; - + if (m_moving) { + /* Randomly round the monster's speed to a multiple of NORMAL_SPEED. + This makes it impossible for the player to predict when they'll + get a free turn (thus preventing exploits like "melee kiting"), + while retaining guarantees about shopkeepers not being outsped + by a normal-speed player, normal-speed players being unable + to open up a gap when fleeing a normal-speed monster, etc. */ + mmove_adj = mmove % NORMAL_SPEED; + mmove -= mmove_adj; + if (rn2(NORMAL_SPEED) < mmove_adj) + mmove += NORMAL_SPEED; + } return mmove; } diff --git a/src/objnam.c b/src/objnam.c index b2897c906..7708294f6 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 objnam.c $NHDT-Date: 1579261291 2020/01/17 11:41:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.288 $ */ +/* NetHack 3.7 objnam.c $NHDT-Date: 1580070220 2020/01/26 20:23:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.291 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2952,8 +2952,8 @@ char *bp, *p; int locked, trapped; { struct rm *lev; - boolean madeterrain = FALSE, badterrain = FALSE; - int trap, x = u.ux, y = u.uy; + boolean madeterrain = FALSE, badterrain = FALSE, didblock; + int trap, oldtyp, x = u.ux, y = u.uy; for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) { struct trap *t; @@ -2978,21 +2978,25 @@ int locked, trapped; /* furniture and terrain (use at your own risk; can clobber stairs or place furniture on existing traps which shouldn't be allowed) */ lev = &levl[x][y]; + oldtyp = lev->typ; + didblock = does_block(x, y, lev); p = eos(bp); if (!BSTRCMPI(bp, p - 8, "fountain")) { lev->typ = FOUNTAIN; g.level.flags.nfountains++; - if (!strncmpi(bp, "magic ", 6)) - lev->blessedftn = 1; + lev->looted = 0; /* overlays 'flags' */ + lev->blessedftn = !strncmpi(bp, "magic ", 6); pline("A %sfountain.", lev->blessedftn ? "magic " : ""); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 6, "throne")) { lev->typ = THRONE; + lev->looted = 0; /* overlays 'flags' */ pline("A throne."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "sink")) { lev->typ = SINK; g.level.flags.nsinks++; + lev->looted = 0; /* overlays 'flags' */ pline("A sink."); madeterrain = TRUE; @@ -3000,6 +3004,7 @@ int locked, trapped; } else if (!BSTRCMPI(bp, p - 4, "pool") || !BSTRCMPI(bp, p - 4, "moat")) { lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL : MOAT; + lev->flags = 0; del_engr_at(x, y); pline("A %s.", (lev->typ == POOL) ? "pool" : "moat"); /* Must manually make kelp! */ @@ -3009,6 +3014,7 @@ int locked, trapped; /* also matches "molten lava" */ } else if (!BSTRCMPI(bp, p - 4, "lava")) { lev->typ = LAVAPOOL; + lev->flags = 0; del_engr_at(x, y); pline("A pool of molten lava."); if (!(Levitation || Flying)) @@ -3028,29 +3034,51 @@ int locked, trapped; al = A_NONE; else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */ al = !rn2(6) ? A_NONE : (rn2((int) A_LAWFUL + 2) - 1); - lev->altarmask = Align2amask(al); + lev->altarmask = Align2amask(al); /* overlays 'flags' */ pline("%s altar.", An(align_str(al))); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 5, "grave") || !BSTRCMPI(bp, p - 9, "headstone")) { make_grave(x, y, (char *) 0); - pline("%s.", IS_GRAVE(lev->typ) ? "A grave" - : "Can't place a grave here"); - madeterrain = TRUE; + if (IS_GRAVE(lev->typ)) { + lev->looted = 0; /* overlays 'flags' */ + lev->disturbed = !strncmpi(bp, "disturbed ", 10); + pline("A %sgrave.", lev->disturbed ? "disturbed " : ""); + madeterrain = TRUE; + } else { + pline("Can't place a grave here"); + badterrain = TRUE; + } } else if (!BSTRCMPI(bp, p - 4, "tree")) { lev->typ = TREE; + lev->looted = 0; /* overlays 'flags' */ pline("A tree."); - block_point(x, y); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "bars")) { lev->typ = IRONBARS; + lev->flags = 0; + /* [FIXME: if this isn't a wall or door location where 'horizontal' + is already set up, that should be calculated for this spot. + Unforutnately, it can be tricky; placing one in open space + and then another adjacent might need to recalculate first one.] */ pline("Iron bars."); madeterrain = TRUE; + } else if (!BSTRCMPI(bp, p - 5, "cloud")) { + lev->typ = CLOUD; + lev->flags = 0; + pline("A cloud."); + madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 11, "secret door")) { - if (lev->typ == DOOR - || (IS_WALL(lev->typ) && lev->typ != DBWALL)) { + /* require door or wall so that the 'horizontal' flag will + already have the correct value (it will matter once the + secret door is discovered and becomes a regular door); + player might choose to put SDOOR on top of existing SDOOR + to control its trapped state; iron bars are surrogate walls */ + if (lev->typ == DOOR || lev->typ == SDOOR + || (IS_WALL(lev->typ) && lev->typ != DBWALL) + || lev->typ == IRONBARS) { lev->typ = SDOOR; - lev->wall_info = 0; + lev->wall_info = 0; /* overlays 'flags' */ /* lev->horizontal stays as-is */ /* no special handling for rogue level is necessary; exposing a secret door there yields a doorless doorway */ @@ -3068,7 +3096,6 @@ int locked, trapped; #endif if (trapped) lev->doormask |= D_TRAPPED; - block_point(x, y); pline("Secret door."); madeterrain = TRUE; } else { @@ -3078,7 +3105,7 @@ int locked, trapped; } else if (!BSTRCMPI(bp, p - 15, "secret corridor")) { if (lev->typ == CORR) { lev->typ = SCORR; - block_point(x, y); + /* neither CORR nor SCORR uses 'flags' or 'horizontal' */ pline("Secret corridor."); madeterrain = TRUE; } else { @@ -3094,11 +3121,41 @@ int locked, trapped; if (u.uinwater && !is_pool(u.ux, u.uy)) { u.uinwater = 0; /* leave the water */ docrt(); - g.vision_full_recalc = 1; - } else if (u.utrap && u.utraptype == TT_LAVA - && !is_lava(u.ux, u.uy)) { - reset_utrap(FALSE); + /* [block/unblock_point was handled by docrt -> vision_recalc] */ + } else { + if (u.utrap && u.utraptype == TT_LAVA && !is_lava(u.ux, u.uy)) + reset_utrap(FALSE); + + if (does_block(x, y, lev)) { + if (!didblock) + block_point(x, y); + } else { + if (didblock) + unblock_point(x, y); + } } + /* fixups for replaced terrain that aren't handled above; + for fountain placed on fountain or sink placed on sink, the + increment above gets canceled out by the decrement here; + otherwise if fountain or sink was replaced, there's one less */ + if (IS_FOUNTAIN(oldtyp)) + g.level.flags.nfountains--; + else if (IS_SINK(oldtyp)) + g.level.flags.nsinks--; + /* horizontal is overlaid by fountain->blessedftn, grave->disturbed */ + if (IS_FOUNTAIN(oldtyp) || IS_GRAVE(oldtyp) + || IS_WALL(oldtyp) || oldtyp == IRONBARS + || IS_DOOR(oldtyp) || oldtyp == SDOOR) { + /* when new terrain is a fountain, 'blessedftn' was explicitly + set above; likewise for grave and 'disturbed'; when it's a + secret door, the old type was a wall or a door and we retain + the 'horizontal' value from those */ + if (!IS_FOUNTAIN(lev->typ) && !IS_GRAVE(lev->typ) + && lev->typ != SDOOR) + lev->horizontal = 0; /* also clears blessedftn, disturbed */ + } + /* note: lev->lit and lev->nondiggable retain their values even + though those might not make sense with the new terrain */ } if (madeterrain || badterrain) { /* cast 'const' away; caller won't modify this */ diff --git a/src/options.c b/src/options.c index e1701782d..1af0e9d2b 100644 --- a/src/options.c +++ b/src/options.c @@ -3368,8 +3368,31 @@ boolean tinitial, tfrom_file; return retval; } #endif /* VIDEOSHADES */ - #ifdef MSDOS + fullname = "video_width"; + if (match_optname(opts, fullname, 7, TRUE)) { + if (duplicate) + complain_about_duplicate(opts, 1); + if (negated) { + bad_negation(fullname, FALSE); + return FALSE; + } + op = string_for_opt(opts, negated); + iflags.wc_video_width = strtol(op, NULL, 10); + return FALSE; + } + fullname = "video_height"; + if (match_optname(opts, fullname, 7, TRUE)) { + if (duplicate) + complain_about_duplicate(opts, 1); + if (negated) { + bad_negation(fullname, FALSE); + return FALSE; + } + op = string_for_opt(opts, negated); + iflags.wc_video_height = strtol(op, NULL, 10); + return FALSE; + } #ifdef NO_TERMS /* video:string -- must be after longer tests */ fullname = "video"; diff --git a/src/sp_lev.c b/src/sp_lev.c index 3a76dbb72..ee40ce331 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1574646949 2019/11/25 01:55:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.141 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1580036285 2020/01/26 10:58:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.148 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1571,10 +1571,13 @@ struct mkroom *croom; /* * If this is a specific item of the right type and it is being - * created on the right level, flag it as the designated item + * created on the right level, record its obj->o_id to be able + * to recognize it as the designated item * used to detect a special achievement (to whit, reaching and * exploring the target level, although the exploration part * might be short-circuited if a monster brings object to hero). + * Achievement is accomplished and the recorded o_id is cleared + * if/when it gets added into hero's inventory. * * Random items of the appropriate type won't trigger a false * match--they'll fail the (id != -1) test above--but the level diff --git a/src/windows.c b/src/windows.c index 476c8bbf8..89d1cd7fa 100644 --- a/src/windows.c +++ b/src/windows.c @@ -876,7 +876,6 @@ const char *status_fieldnm[MAXBLSTATS]; const char *status_fieldfmt[MAXBLSTATS]; char *status_vals[MAXBLSTATS]; boolean status_activefields[MAXBLSTATS]; -NEARDATA winid WIN_STATUS; void genl_status_init() diff --git a/src/worm.c b/src/worm.c index 113d00a32..920a2766f 100644 --- a/src/worm.c +++ b/src/worm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 worm.c $NHDT-Date: 1579990313 2020/01/25 22:11:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 worm.c $NHDT-Date: 1580043421 2020/01/26 12:57:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.42 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -218,26 +218,65 @@ struct monst *worm; seg->nseg = new_seg; /* attach it to the end of the list */ wheads[wnum] = new_seg; /* move the end pointer */ - /* - * [maybe] FIXME? - * scheduling wgrowtime[] seems to be based on normal movement - * speed (12) but long worms move at 1/4 of that (3), so they'll - * reach the scheduled 'moves' more quickly (in terms of their - * actual movement) and grow faster than was probably intended. - */ if (wgrowtime[wnum] <= g.moves) { - if (!wgrowtime[wnum]) + int whplimit, whpcap, wsegs = count_wsegs(worm); + + /* first set up for the next time to grow */ + if (!wgrowtime[wnum]) { + /* new worm; usually grow a tail segment on its next turn */ wgrowtime[wnum] = g.moves + rnd(5); - else - wgrowtime[wnum] += rn1(15, 3); - worm->mhp += 3; - if (worm->mhp > MHPMAX) - worm->mhp = MHPMAX; - if (worm->mhp > worm->mhpmax) - worm->mhpmax = worm->mhp; - } else - /* The worm doesn't grow, so the last segment goes away. */ + } else { + int mmove = mcalcmove(worm, FALSE), + /* prior to 3.7.0, next-grow increment was 3..17 but since + it got checked every 4th turn when the speed 3 worm got + to move, it was effectively 0..5; also, its usage was + 'wgrowtime += incr', so often 'wgrowtime' would be + exceeded by 'moves' on consecutive turns for the worm, + resulting in an excessively rapid growth cycle */ + incr = rn1(10, 2); /* 2..12; after adjusting for long worn + * speed of 3, effective value is 8..48 */ + + incr = (incr * NORMAL_SPEED) / max(mmove, 1); + wgrowtime[wnum] = g.moves + incr; + } + + /* increase HP based on number of segments; if it has shrunk, it + won't gain new HP until regaining previous peak segment count; + when wounded (whether from damage or from shrinking), the HP + which might have been 'new' will heal */ + whplimit = !worm->m_lev ? 4 : (8 * (int) worm->m_lev); + /* note: wsegs includes the hidden segment co-located with the head */ + if (wsegs > 33) + whplimit += 2 * (wsegs - 33), wsegs = 33; + if (wsegs > 22) + whplimit += 4 * (wsegs - 22), wsegs = 22; + if (wsegs > 11) + whplimit += 6 * (wsegs - 11), wsegs = 11; + whplimit += 8 * wsegs; + if (whplimit > MHPMAX) + whplimit = MHPMAX; + + worm->mhp += d(2, 2); /* 2..4, average 3 */ + whpcap = max(whplimit, worm->mhpmax); + if (worm->mhp < whpcap) { + /* can't exceed segment-derived limit unless level increase after + peak tail growth has already done so; when that isn't the case, + if segment growth exceeds current max HP then increase it */ + if (worm->mhp > whpcap) + worm->mhp = whpcap; + if (worm->mhp > worm->mhpmax) + worm->mhpmax = worm->mhp; + } else { + if (worm->mhp > worm->mhpmax) + worm->mhp = worm->mhpmax; + } + } else { + /* The worm doesn't grow, so the last segment goes away. + (Done after inserting an extra segment at the head, so it + isn't getting smaller here, just changing location without + having to move any of the intermediate segments.) */ shrink_worm(wnum); + } } /* @@ -253,10 +292,11 @@ struct monst *worm; { shrink_worm((int) worm->wormno); /* shrink */ - if (worm->mhp > 3) - worm->mhp -= 3; /* mhpmax not changed ! */ - else - worm->mhp = 1; + if (worm->mhp > count_wsegs(worm)) { + worm->mhp -= d(2, 2); /* 2..4, average 3; note: mhpmax not changed! */ + if (worm->mhp < 1) + worm->mhp = 1; + } } /* diff --git a/sys/msdos/msdos.c b/sys/msdos/msdos.c index b68041943..c3230140e 100644 --- a/sys/msdos/msdos.c +++ b/sys/msdos/msdos.c @@ -64,6 +64,11 @@ tgetch() { char ch; +#ifdef SCREEN_VESA + if (iflags.usevesa) { + vesa_flush_text(); + } +#endif /*SCREEN_VESA*/ /* BIOSgetch can use the numeric key pad on IBM compatibles. */ #ifdef SIMULATE_CURSOR if (iflags.grmode && cursor_flag) diff --git a/sys/msdos/pcvideo.h b/sys/msdos/pcvideo.h index bc2f876fd..cfe45c1eb 100644 --- a/sys/msdos/pcvideo.h +++ b/sys/msdos/pcvideo.h @@ -181,6 +181,10 @@ struct overview_planar_cell_struct { #define ATTRIB_VGA_INTENSE 14 /* Intense White 94/06/07 palette chg*/ #endif /*SCREEN_VGA || SCREEN_8514*/ +#if defined(SCREEN_VESA) +#define BACKGROUND_VESA_COLOR 240 +#endif /*SCREEN_VESA*/ + #if defined(PC9800) static unsigned char attr98[CLR_MAX] = { 0xe1, /* 0 white */ @@ -249,6 +253,7 @@ E void FDECL(txt_xputc, (CHAR_P, int)); /* ### vidvga.c ### */ +enum vga_pan_direction { pan_left, pan_up, pan_right, pan_down }; #ifdef SCREEN_VGA E void NDECL(vga_backsp); E void FDECL(vga_clear_screen, (int)); @@ -274,7 +279,7 @@ E void FDECL(vga_tty_startup, (int *, int *)); E void FDECL(vga_xputs, (const char *, int, int)); E void FDECL(vga_xputc, (CHAR_P, int)); E void FDECL(vga_xputg, (int, int, unsigned)); -E void FDECL(vga_userpan, (BOOLEAN_P)); +E void FDECL(vga_userpan, (enum vga_pan_direction)); E void FDECL(vga_overview, (BOOLEAN_P)); E void FDECL(vga_traditional, (BOOLEAN_P)); E void NDECL(vga_refresh); @@ -303,10 +308,11 @@ E void FDECL(vesa_tty_startup, (int *, int *)); E void FDECL(vesa_xputs, (const char *, int, int)); E void FDECL(vesa_xputc, (CHAR_P, int)); E void FDECL(vesa_xputg, (int, int, unsigned)); -E void FDECL(vesa_userpan, (BOOLEAN_P)); +E void FDECL(vesa_userpan, (enum vga_pan_direction)); E void FDECL(vesa_overview, (BOOLEAN_P)); E void FDECL(vesa_traditional, (BOOLEAN_P)); E void NDECL(vesa_refresh); +E void NDECL(vesa_flush_text); #endif /* SCREEN_VESA */ #endif /* NO_TERMS */ diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 65ae2e845..39b27c4b3 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -17,18 +17,23 @@ #include "wintty.h" #include "tileset.h" -#define BACKGROUND_VESA_COLOR 1 #define FIRST_TEXT_COLOR 240 +extern int total_tiles_used; /* tile.c */ + +struct VesaCharacter { + int colour; + int chr; +}; + static unsigned long FDECL(vesa_SetWindow, (int window, unsigned long offset)); static unsigned long FDECL(vesa_ReadPixel32, (unsigned x, unsigned y)); static void FDECL(vesa_WritePixel32, (unsigned x, unsigned y, unsigned long color)); static void FDECL(vesa_WritePixel, (unsigned x, unsigned y, unsigned color)); -static unsigned long FDECL(vesa_MakeColor, (unsigned r, unsigned g, unsigned b)); -static void FDECL(vesa_GetRGB, ( - unsigned long color, - unsigned char *rp, unsigned char *gp, unsigned char *bp)); +static void FDECL(vesa_WritePixelRow, (unsigned long offset, + unsigned char const *p_row, unsigned p_row_size)); +static unsigned long FDECL(vesa_MakeColor, (struct Pixel)); static void FDECL(vesa_FillRect, ( unsigned left, unsigned top, unsigned width, unsigned height, @@ -36,26 +41,35 @@ static void FDECL(vesa_FillRect, ( static void NDECL(vesa_redrawmap); static void FDECL(vesa_cliparound, (int, int)); +#if 0 static void FDECL(decal_packed, (const struct TileImage *tile, unsigned special)); +#endif static void FDECL(vesa_SwitchMode, (unsigned mode)); +static void NDECL(vesa_SetViewPort); static boolean FDECL(vesa_SetPalette, (const struct Pixel *)); static boolean FDECL(vesa_SetHardPalette, (const struct Pixel *)); static boolean FDECL(vesa_SetSoftPalette, (const struct Pixel *)); -static void FDECL(vesa_DisplayCell, (const struct TileImage *tile, int, int)); -static void FDECL(vesa_DisplayCellInMemory, (const struct TileImage *tile, - int, char buf[TILE_Y][640*2])); +static void FDECL(vesa_DisplayCell, (int, int, int)); static unsigned FDECL(vesa_FindMode, (unsigned long mode_addr, unsigned bits)); -static void FDECL(vesa_WriteChar, (int, int, int, int, BOOLEAN_P)); -static void FDECL(vesa_WriteCharInMemory, (int, int, char buf[TILE_Y][640*2], - int)); +static void FDECL(vesa_WriteChar, (int, int, int, int)); +static void FDECL(vesa_WriteCharXY, (int, int, int, int)); +static void FDECL(vesa_WriteCharTransparent, (int, int, int, int)); +static void FDECL(vesa_WriteTextRow, (int pixx, int pixy, + struct VesaCharacter const *t_row, unsigned t_row_width)); +static boolean FDECL(vesa_GetCharPixel, (int, unsigned, unsigned)); +static unsigned char FDECL(vesa_GetCharPixelRow, (int, unsigned, unsigned)); +static unsigned long FDECL(vesa_DoublePixels, (unsigned long)); +static unsigned long FDECL(vesa_TriplePixels, (unsigned long)); static void FDECL(vesa_WriteStr, (const char *, int, int, int, int)); -static char __far *NDECL(vesa_FontPtrs); +static unsigned char __far *NDECL(vesa_FontPtrs); +static void FDECL(vesa_process_tile, (struct TileImage *tile)); #ifdef POSITIONBAR static void NDECL(positionbar); #endif extern int clipx, clipxmax; /* current clipping column from wintty.c */ +extern int clipy, clipymax; /* current clipping row from wintty.c */ extern int curcol, currow; /* current column and row */ extern int g_attribute; extern int attrib_text_normal; /* text mode normal attribute */ @@ -90,10 +104,11 @@ static struct map_struct { } #define TOP_MAP_ROW 1 -static int viewport_size = 40; +static int viewport_cols = 40; +static int viewport_rows = ROWNO; static const struct Pixel defpalette[] = { /* Colors for text and the position bar */ - { 0x18, 0x18, 0x18, 0xff }, /* CLR_BLACK */ + { 0x00, 0x00, 0x00, 0xff }, /* CLR_BLACK */ { 0xaa, 0x00, 0x00, 0xff }, /* CLR_RED */ { 0x00, 0xaa, 0x00, 0xff }, /* CLR_GREEN */ { 0x99, 0x40, 0x00, 0xff }, /* CLR_BROWN */ @@ -120,19 +135,34 @@ static unsigned short vesa_y_center; /* Y centering offset */ static unsigned short vesa_scan_line; /* Bytes per scan line */ static int vesa_read_win; /* Select the read window */ static int vesa_write_win; /* Select the write window */ +static unsigned long vesa_win_func; static unsigned long vesa_win_pos[2]; /* Window position */ static unsigned long vesa_win_addr[2]; /* Window physical address */ +static unsigned long vesa_segment; /* Selector of linear frame buffer */ static unsigned long vesa_win_size; /* Window size */ static unsigned long vesa_win_gran; /* Window granularity */ static unsigned char vesa_pixel_size; static unsigned char vesa_pixel_bytes; static unsigned char vesa_red_pos; -static unsigned char vesa_red_size; +static unsigned char vesa_red_shift; static unsigned char vesa_green_pos; -static unsigned char vesa_green_size; +static unsigned char vesa_green_shift; static unsigned char vesa_blue_pos; -static unsigned char vesa_blue_size; +static unsigned char vesa_blue_shift; static unsigned long vesa_palette[256]; +static unsigned vesa_char_width = 8, vesa_char_height = 16; +static unsigned vesa_oview_width, vesa_oview_height; +static unsigned char **vesa_tiles; +static unsigned char **vesa_oview_tiles; + +#ifdef SIMULATE_CURSOR +static unsigned long *undercursor; +#endif + +/* Used to cache character writes for speed */ +static struct VesaCharacter chr_cache[100]; +static unsigned chr_cache_size; +static unsigned chr_cache_pixx, chr_cache_pixy, chr_cache_lastx; struct OldModeInfo { unsigned mode; @@ -221,6 +251,20 @@ error: return FALSE; } +static unsigned +vesa_map_frame_buffer(unsigned phys_addr, unsigned size) +{ + __dpmi_meminfo info; + int rc; + + info.handle = 0; + info.address = phys_addr; + info.size = size; + rc = __dpmi_physical_address_mapping(&info); + + return rc == 0 ? info.address : 0; +} + /* Set the memory window and return the offset */ static unsigned long vesa_SetWindow(window, offset) @@ -245,7 +289,14 @@ unsigned long offset; regs.h.bl = window; regs.x.dx = offset / vesa_win_gran; pos = regs.x.dx * vesa_win_gran; - (void) __dpmi_int(VIDEO_BIOS, ®s); + + if (vesa_win_func != 0) { + regs.x.cs = vesa_win_func >> 16; + regs.x.ip = vesa_win_func & 0xFFFF; + (void) __dpmi_simulate_real_mode_procedure_retf(®s); + } else { + (void) __dpmi_int(VIDEO_BIOS, ®s); + } vesa_win_pos[window] = pos; } @@ -268,31 +319,66 @@ unsigned x, y; unsigned long addr, color; unsigned i; - switch (vesa_pixel_size) { - case 8: - addr = vesa_SetWindow(vesa_read_win, offset); - color = _farpeekb(_dos_ds, addr); - break; + if (vesa_segment != 0) { + /* Linear frame buffer in use */ + switch (vesa_pixel_size) { + case 8: + color = _farpeekb(vesa_segment, offset); + break; - case 15: - case 16: - addr = vesa_SetWindow(vesa_read_win, offset); - color = _farpeekw(_dos_ds, addr); - break; + case 15: + case 16: + color = _farpeekw(vesa_segment, offset); + break; - case 24: - /* Pixel may cross a window boundary */ - color = 0; - for (i = 0; i < 3; ++i) { - addr = vesa_SetWindow(vesa_read_win, offset + i); - color |= (unsigned long) _farpeekb(_dos_ds, addr) << (i * 8); + case 24: + /* Don't cross a 4 byte boundary if it can be avoided */ + if (offset & 1) { + color = _farpeekl(vesa_segment, offset - 1) >> 8; + } else { + color = _farpeekl(vesa_segment, offset) & 0xFFFFFFFF; + } + break; + + case 32: + color = _farpeekl(vesa_segment, offset); + break; + + default: /* Shouldn't happen */ + color = 0; + break; } - break; + } else { + switch (vesa_pixel_size) { + case 8: + addr = vesa_SetWindow(vesa_read_win, offset); + color = _farpeekb(_dos_ds, addr); + break; - case 32: - addr = vesa_SetWindow(vesa_read_win, offset); - color = _farpeekl(_dos_ds, addr); - break; + case 15: + case 16: + addr = vesa_SetWindow(vesa_read_win, offset); + color = _farpeekw(_dos_ds, addr); + break; + + case 24: + /* Pixel may cross a window boundary */ + color = 0; + for (i = 0; i < 3; ++i) { + addr = vesa_SetWindow(vesa_read_win, offset + i); + color |= (unsigned long) _farpeekb(_dos_ds, addr) << (i * 8); + } + break; + + case 32: + addr = vesa_SetWindow(vesa_read_win, offset); + color = _farpeekl(_dos_ds, addr); + break; + + default: /* Shouldn't happen */ + color = 0; + break; + } } return color; } @@ -306,30 +392,62 @@ unsigned long color; unsigned long addr; unsigned i; - switch (vesa_pixel_size) { - case 8: - addr = vesa_SetWindow(vesa_write_win, offset); - _farpokeb(_dos_ds, addr, color); - break; + if (vesa_segment != 0) { + /* Linear frame buffer in use */ + switch (vesa_pixel_size) { + case 8: + _farpokeb(vesa_segment, offset, color); + break; - case 15: - case 16: - addr = vesa_SetWindow(vesa_write_win, offset); - _farpokew(_dos_ds, addr, color); - break; + case 15: + case 16: + _farpokew(vesa_segment, offset, color); + break; - case 24: - /* Pixel may cross a window boundary */ - for (i = 0; i < 3; ++i) { - addr = vesa_SetWindow(vesa_read_win, offset + i); - _farpokeb(_dos_ds, addr, (unsigned char) (color >> (i * 8))); + case 24: + if (offset & 1) { + _farpokeb(vesa_segment, offset + 0, color & 0xFF); + _farpokew(vesa_segment, offset + 1, color >> 8); + } else { + _farpokew(vesa_segment, offset + 0, color & 0xFFFF); + _farpokeb(vesa_segment, offset + 2, color >> 16); + } + break; + + case 32: + _farpokel(vesa_segment, offset, color); + break; + + default: /* Shouldn't happen */ + color = 0; + break; } - break; + } else { + switch (vesa_pixel_size) { + case 8: + addr = vesa_SetWindow(vesa_write_win, offset); + _farpokeb(_dos_ds, addr, color); + break; - case 32: - addr = vesa_SetWindow(vesa_write_win, offset); - _farpokel(_dos_ds, addr, color); - break; + case 15: + case 16: + addr = vesa_SetWindow(vesa_write_win, offset); + _farpokew(_dos_ds, addr, color); + break; + + case 24: + /* Pixel may cross a window boundary */ + for (i = 0; i < 3; ++i) { + addr = vesa_SetWindow(vesa_write_win, offset + i); + _farpokeb(_dos_ds, addr, (unsigned char) (color >> (i * 8))); + } + break; + + case 32: + addr = vesa_SetWindow(vesa_write_win, offset); + _farpokel(_dos_ds, addr, color); + break; + } } } @@ -345,54 +463,93 @@ unsigned color; } } -static unsigned long -vesa_MakeColor(r, g, b) -unsigned r, g, b; +static void +vesa_WritePixelRow(offset, p_row, p_row_size) +unsigned long offset; +unsigned char const *p_row; +unsigned p_row_size; { - r = (r & 0xFF) >> (8 - vesa_red_size); - g = (g & 0xFF) >> (8 - vesa_green_size); - b = (b & 0xFF) >> (8 - vesa_blue_size); - return ((unsigned long) r << vesa_red_pos) - | ((unsigned long) g << vesa_green_pos) - | ((unsigned long) b << vesa_blue_pos); + if (vesa_segment != 0) { + /* Linear frame buffer in use */ + movedata(_go32_my_ds(), (unsigned)p_row, vesa_segment, offset, p_row_size); + } else { + /* Windowed frame buffer in use */ + unsigned long addr = vesa_SetWindow(vesa_write_win, offset); + unsigned i = 0; + while (offset + p_row_size > vesa_win_pos[vesa_write_win] + vesa_win_size) { + /* The row passes the end of the current window. Write what we can, + * and advance the window */ + unsigned w = (vesa_win_pos[vesa_write_win] + vesa_win_size) + - (offset + i); + dosmemput(p_row + i, w, addr); + i += w; + addr = vesa_SetWindow(vesa_write_win, offset + i); + } + dosmemput(p_row + i, p_row_size - i, addr); + } } -static void -vesa_GetRGB(color, rp, gp, bp) -unsigned long color; -unsigned char *rp, *gp, *bp; +static unsigned long +vesa_MakeColor(p) +struct Pixel p; { - unsigned r, g, b; - - r = color >> vesa_red_pos; - g = color >> vesa_green_pos; - b = color >> vesa_blue_pos; - r <<= 8 - vesa_red_size; - g <<= 8 - vesa_green_size; - b <<= 8 - vesa_blue_size; - *rp = (unsigned char) r; - *gp = (unsigned char) g; - *bp = (unsigned char) b; + unsigned long r = p.r >> vesa_red_shift; + unsigned long g = p.g >> vesa_green_shift; + unsigned long b = p.b >> vesa_blue_shift; + return (r << vesa_red_pos) + | (g << vesa_green_pos) + | (b << vesa_blue_pos); } static void vesa_FillRect(left, top, width, height, color) unsigned left, top, width, height, color; { + unsigned p_row_size = width * vesa_pixel_bytes; + unsigned char *p_row = (unsigned char *) alloc(p_row_size); + unsigned long c32 = vesa_palette[color & 0xFF]; + unsigned long offset = top * (unsigned long)vesa_scan_line + left * vesa_pixel_bytes; unsigned x, y; - for (y = 0; y < height; ++y) { + switch (vesa_pixel_bytes) { + case 1: + memset(p_row, color, p_row_size); + break; + + case 2: for (x = 0; x < width; ++x) { - vesa_WritePixel(left + x, top + y, color); + ((uint16_t *)p_row)[x] = c32; } + break; + + case 3: + for (x = 0; x < width; ++x) { + p_row[3*x + 0] = c32 & 0xFF; + p_row[3*x + 1] = (c32 >> 8) & 0xFF; + p_row[3*x + 2] = c32 >> 16 ; + } + break; + + case 4: + for (x = 0; x < width; ++x) { + ((uint32_t *)p_row)[x] = c32; + } + break; } + + for (y = 0; y < height; ++y) { + vesa_WritePixelRow(offset, p_row, p_row_size); + offset += vesa_scan_line; + } + + free(p_row); } void vesa_get_scr_size() { - CO = 80; - LI = 29; + CO = vesa_x_res / vesa_char_width; + LI = vesa_y_res / vesa_char_height - 1; } void @@ -423,11 +580,12 @@ void vesa_cl_end(col, row) int col, row; { - unsigned left = vesa_x_center + col * 8; - unsigned top = vesa_y_center + row * 16; - unsigned width = (CO - 1 - col) * 8; - unsigned height = 16; + unsigned left = vesa_x_center + col * vesa_char_width; + unsigned top = vesa_y_center + row * vesa_char_height; + unsigned width = (CO - 1 - col) * vesa_char_width; + unsigned height = vesa_char_height; + vesa_flush_text(); vesa_FillRect(left, top, width, height, BACKGROUND_VESA_COLOR); } @@ -436,14 +594,12 @@ void vesa_cl_eos(cy) int cy; { - int count; - cl_end(); if (cy < LI - 1) { - unsigned left = vesa_x_center; - unsigned top = vesa_y_center + cy * 16; - unsigned width = 640; - unsigned height = (LI - 1 - cy) * 16; + unsigned left = 0; + unsigned top = vesa_y_center + cy * vesa_char_height; + unsigned width = vesa_x_res; + unsigned height = (LI - 1 - cy) * vesa_char_height; vesa_FillRect(left, top, width, height, BACKGROUND_VESA_COLOR); } @@ -521,7 +677,10 @@ int attr; ++row; break; default: - vesa_WriteChar((unsigned char) ch, col, row, attr, FALSE); + vesa_WriteChar((unsigned char) ch, col, row, attr); + if (ch == '.') { + vesa_flush_text(); + } if (col < (CO - 1)) ++col; break; @@ -542,7 +701,6 @@ unsigned special; /* special feature: corpse, invis, detected, pet, ridden - int col, row; int attr; int ry; - const struct TileImage *packcell; row = currow; col = curcol; @@ -556,13 +714,15 @@ unsigned special; /* special feature: corpse, invis, detected, pet, ridden - attr = (g_attribute == 0) ? attrib_gr_normal : g_attribute; map[ry][col].attr = attr; if (iflags.traditional_view) { - vesa_WriteChar((unsigned char) ch, col, row, attr, FALSE); + vesa_WriteChar((unsigned char) ch, col, row, attr); } else { - if ((col >= clipx) && (col <= clipxmax)) { - packcell = get_tile(glyph2tile[glyphnum]); + if ((col >= clipx) && (col <= clipxmax) + && (ry >= clipy) && (ry <= clipymax)) { +#if 0 if (!iflags.over_view && map[ry][col].special) decal_packed(packcell, special); - vesa_DisplayCell(packcell, col - clipx, row); +#endif + vesa_DisplayCell(glyph2tile[glyphnum], col - clipx, ry - clipy); } } if (col < (CO - 1)) @@ -596,19 +756,36 @@ static void vesa_cliparound(x, y) int x, y; { - int oldx = clipx; + int oldx = clipx, oldy = clipy; if (!iflags.tile_view || iflags.over_view || iflags.traditional_view) return; - if (x < clipx + 5) { - clipx = max(0, x - (viewport_size / 2)); - clipxmax = clipx + (viewport_size - 1); - } else if (x > clipxmax - 5) { - clipxmax = min(COLNO - 1, x + (viewport_size / 2)); - clipx = clipxmax - (viewport_size - 1); + if (viewport_cols < COLNO) { + if (x < clipx + 5) { + clipx = max(0, x - (viewport_cols / 2)); + clipxmax = clipx + (viewport_cols - 1); + } else if (x > clipxmax - 5) { + clipxmax = min(COLNO - 1, x + (viewport_cols / 2)); + clipx = clipxmax - (viewport_cols - 1); + } + } else { + clipx = 0; + clipxmax = COLNO - 1; } - if (clipx != oldx) { + if (viewport_rows < ROWNO) { + if (y < clipy + 5) { + clipy = max(0, y - (viewport_rows / 2)); + clipymax = clipy + (viewport_rows - 1); + } else if (y > clipymax - 5) { + clipymax = min(ROWNO, y + (viewport_rows / 2)); + clipy = clipymax - (viewport_rows - 1); + } + } else { + clipy = 0; + clipymax = ROWNO - 1; + } + if (clipx != oldx || clipy != oldy) { if (on_level(&u.uz0, &u.uz) && !g.restoring) /* (void) doredraw(); */ vesa_redrawmap(); @@ -618,70 +795,139 @@ int x, y; static void vesa_redrawmap() { - int x, y, t; - const struct TileImage *packcell; + unsigned y_top = TOP_MAP_ROW * vesa_char_height; + unsigned y_bottom = vesa_y_res - 5 * vesa_char_height; + unsigned x, y, cx, cy, px, py; + unsigned long color; + unsigned long offset = y_top * (unsigned long) vesa_scan_line; + unsigned char *p_row = NULL; + unsigned p_row_width; - /* y here is in screen rows*/ - /* Build each row in local memory, then write, to minimize use of the - window switch function */ - for (y = 0; y < ROWNO; ++y) { - char buf[TILE_Y][640*2]; + /* + * The map is drawn in pixel-row order (pixel row 0, then row 1, etc.), + * rather than cell by cell, to minimize calls to vesa_SetWindow. + */ + if (iflags.traditional_view) { + /* Text mode */ + y = y_top; + for (cy = clipy; cy <= clipymax && cy < ROWNO; ++cy) { + struct VesaCharacter t_row[COLNO]; + for (cx = clipx; cx <= clipxmax && cx < COLNO; ++cx) { + t_row[cx].chr = map[cy][cx].ch; + t_row[cx].colour = map[cy][cx].attr; + } + vesa_WriteTextRow(0, y, t_row + clipx, cx - clipx); + x = (cx - clipx) * vesa_char_width; + if (x < vesa_x_res) { + vesa_FillRect(x, y, vesa_x_res - x, vesa_char_height, BACKGROUND_VESA_COLOR); + } + y += vesa_char_height; + } + } else if (iflags.over_view) { + /* Overview mode */ + const unsigned char *tile; - for (x = clipx; x <= clipxmax; ++x) { - if (iflags.traditional_view) { - vesa_WriteCharInMemory((unsigned char) map[y][x].ch, x, - buf, map[y][x].attr); - } else { - t = map[y][x].glyph; - packcell = get_tile(glyph2tile[t]); - if (!iflags.over_view && map[y][x].special) - decal_packed(packcell, map[y][x].special); - vesa_DisplayCellInMemory(packcell, x - clipx, buf); + p_row_width = vesa_oview_width * vesa_pixel_bytes; + y = y_top; + for (cy = 0; cy < ROWNO; ++cy) { + for (py = 0; py < vesa_oview_height; ++py) { + for (cx = 0; cx < COLNO; ++cx) { + tile = vesa_oview_tiles[glyph2tile[map[cy][cx].glyph]]; + vesa_WritePixelRow(offset + p_row_width * cx, tile + p_row_width * py, p_row_width); + } + x = COLNO * vesa_oview_width; + if (x < vesa_x_res) { + vesa_FillRect(x, y, vesa_x_res - x, 1, BACKGROUND_VESA_COLOR); + } + offset += vesa_scan_line; + ++y; } } - if (iflags.over_view && vesa_pixel_size != 8) { - for (t = 0; t < TILE_Y; ++t) { - for (x = 0; x < 640; ++x) { - unsigned long c1 = vesa_palette[buf[t][x * 2 + 0]]; - unsigned long c2 = vesa_palette[buf[t][x * 2 + 1]]; - unsigned char r1, r2, g1, g2, b1, b2; + } else { + /* Normal tiled mode */ + const unsigned char *tile; - vesa_GetRGB(c1, &r1, &g1, &b1); - vesa_GetRGB(c2, &r2, &g2, &b2); - r1 = (r1 + r2) / 2; - g1 = (g1 + g2) / 2; - b1 = (b1 + b2) / 2; - vesa_WritePixel32(x, (y + TOP_MAP_ROW) * TILE_Y + t, - vesa_MakeColor(r1, g1, b1)); + p_row_width = iflags.wc_tile_width * vesa_pixel_bytes; + y = y_top; + for (cy = clipy; cy <= clipymax && cy < ROWNO; ++cy) { + for (py = 0; py < iflags.wc_tile_height; ++py) { + for (cx = clipx; cx <= clipxmax && cx < COLNO; ++cx) { + tile = vesa_tiles[glyph2tile[map[cy][cx].glyph]]; + vesa_WritePixelRow(offset + p_row_width * (cx - clipx), tile + p_row_width * py, p_row_width); } - } - } else { - for (t = 0; t < TILE_Y; ++t) { - for (x = 0; x < 640; ++x) { - vesa_WritePixel(x, (y + TOP_MAP_ROW) * TILE_Y + t, buf[t][x]); + x = (cx - clipx) * iflags.wc_tile_width; + if (x < vesa_x_res) { + vesa_FillRect(x, y, vesa_x_res - x, 1, BACKGROUND_VESA_COLOR); } + offset += vesa_scan_line; + ++y; } } } + /* Loops leave y as the start of any remaining unfilled space */ + if (y < y_bottom) { + vesa_FillRect(0, y, vesa_x_res, y_bottom - y, BACKGROUND_VESA_COLOR); + } + + free(p_row); } #endif /* USE_TILES && CLIPPING */ void -vesa_userpan(left) -boolean left; +vesa_userpan(pan) +enum vga_pan_direction pan; { - int x; - /* pline("Into userpan"); */ if (iflags.over_view || iflags.traditional_view) return; - if (left) - x = min(COLNO - 1, clipxmax + 10); - else - x = max(0, clipx - 10); - vesa_cliparound(x, 10); /* y value is irrelevant on VGA clipping */ - positionbar(); - vesa_DrawCursor(); + + switch (pan) { + case pan_left: + if (viewport_cols < COLNO) { + clipxmax = clipx - 1; + clipx = clipxmax - (viewport_cols - 1); + if (clipx < 0) { + clipx = 0; + clipxmax = viewport_cols - 1; + } + } + break; + + case pan_right: + if (viewport_cols < COLNO) { + clipx = clipxmax + 1; + clipxmax = clipx + (viewport_cols - 1); + if (clipxmax > COLNO - 1) { + clipxmax = COLNO - 1; + clipx = clipxmax - (viewport_cols - 1); + } + } + break; + + case pan_up: + if (viewport_rows < ROWNO) { + clipymax = clipy - 1; + clipy = clipymax - (viewport_rows - 1); + if (clipy < 0) { + clipy = 0; + clipymax = viewport_rows - 1; + } + } + break; + + case pan_down: + if (viewport_rows < ROWNO) { + clipy = clipymax + 1; + clipymax = clipy + (viewport_rows - 1); + if (clipymax > ROWNO - 1) { + clipymax = ROWNO - 1; + clipy = clipymax - (viewport_rows - 1); + } + } + break; + } + + vesa_refresh(); } void @@ -692,13 +938,29 @@ boolean on; if (on) { iflags.over_view = TRUE; clipx = 0; - clipxmax = CO - 1; + clipxmax = COLNO - 1; + clipy = 0; + clipymax = ROWNO - 1; } else { iflags.over_view = FALSE; - clipx = max(0, (curcol - viewport_size / 2)); - if (clipx > ((CO - 1) - viewport_size)) - clipx = (CO - 1) - viewport_size; - clipxmax = clipx + (viewport_size - 1); + if (viewport_cols < COLNO) { + clipx = max(0, (curcol - viewport_cols / 2)); + if (clipx > ((COLNO - 1) - viewport_cols)) + clipx = (COLNO - 1) - viewport_cols; + clipxmax = clipx + (viewport_cols - 1); + } else { + clipx = 0; + clipxmax = COLNO - 1; + } + if (viewport_rows < ROWNO) { + clipy = max(0, (currow - viewport_rows / 2)); + if (clipy > ((ROWNO - 1) - viewport_rows)) + clipy = (ROWNO - 1) - viewport_rows; + clipymax = clipy + (viewport_rows - 1); + } else { + clipy = 0; + clipymax = ROWNO - 1; + } } } @@ -711,14 +973,30 @@ boolean on; /* switch_symbols(FALSE); */ iflags.traditional_view = TRUE; clipx = 0; - clipxmax = CO - 1; + clipxmax = COLNO - 1; + clipy = 0; + clipymax = ROWNO - 1; } else { iflags.traditional_view = FALSE; if (!iflags.over_view) { - clipx = max(0, (curcol - viewport_size / 2)); - if (clipx > ((CO - 1) - viewport_size)) - clipx = (CO - 1) - viewport_size; - clipxmax = clipx + (viewport_size - 1); + if (viewport_cols < COLNO) { + clipx = max(0, (curcol - viewport_cols / 2)); + if (clipx > ((COLNO - 1) - viewport_cols)) + clipx = (COLNO - 1) - viewport_cols; + clipxmax = clipx + (viewport_cols - 1); + } else { + clipx = 0; + clipxmax = COLNO - 1; + } + if (viewport_rows < ROWNO) { + clipy = max(0, (currow - viewport_rows / 2)); + if (clipy > ((ROWNO - 1) - viewport_rows)) + clipy = (ROWNO - 1) - viewport_rows; + clipymax = clipy + (viewport_rows - 1); + } else { + clipy = 0; + clipymax = ROWNO - 1; + } } } } @@ -731,6 +1009,7 @@ vesa_refresh() vesa_DrawCursor(); } +#if 0 static void decal_packed(gp, special) const struct TileImage *gp; @@ -745,6 +1024,7 @@ unsigned special; } else if (special & MG_RIDDEN) { } } +#endif /* * Open tile files, @@ -756,10 +1036,17 @@ unsigned special; void vesa_Init(void) { + static boolean inited = FALSE; const struct Pixel *paletteptr; -#ifdef USE_TILES + unsigned i; + unsigned num_pixels, num_oview_pixels; const char *tile_file; int tilefailure = 0; + + if (inited) return; + inited = TRUE; + +#ifdef USE_TILES /* * Attempt to open the required tile files. If we can't * don't perform the video mode switch, use TTY code instead. @@ -769,9 +1056,9 @@ vesa_Init(void) if (tile_file == NULL || *tile_file == '\0') { tile_file = "nhtiles.bmp"; } - if (!read_tiles(tile_file, FALSE)) + if (!read_tiles(tile_file, vesa_pixel_size > 8)) tilefailure |= 1; - if (get_palette() == NULL) + if (vesa_pixel_size == 8 && get_palette() == NULL) tilefailure |= 4; if (tilefailure) { @@ -788,10 +1075,23 @@ vesa_Init(void) } #endif + vesa_mode = 0xFFFF; /* might want an 8 bit mode after loading tiles */ + vesa_detect(); if (vesa_mode == 0xFFFF) { - vesa_detect(); + raw_printf("Reverting to TTY mode, no VESA mode available.", + tilefailure); + wait_synch(); + iflags.usevga = 0; + iflags.tile_view = FALSE; + iflags.over_view = FALSE; + CO = 80; + LI = 25; + /* clear_screen() */ /* not vesa_clear_screen() */ + return; } + vesa_SwitchMode(vesa_mode); + vesa_SetViewPort(); windowprocs.win_cliparound = vesa_cliparound; #ifdef USE_TILES paletteptr = get_palette(); @@ -805,7 +1105,107 @@ vesa_Init(void) font = vesa_FontPtrs(); clear_screen(); clipx = 0; - clipxmax = clipx + (viewport_size - 1); + clipxmax = clipx + (viewport_cols - 1); + clipy = 0; + clipymax = clipy + (viewport_rows - 1); + + /* Set the size of the tiles for the overview mode */ + vesa_oview_width = vesa_x_res / COLNO; + if (vesa_oview_width > iflags.wc_tile_width) { + vesa_oview_width = iflags.wc_tile_width; + } + vesa_oview_height = (vesa_y_res - (TOP_MAP_ROW + 4) * vesa_char_height) + / ROWNO; + if (vesa_oview_height > iflags.wc_tile_height) { + vesa_oview_height = iflags.wc_tile_height; + } + + /* Use the map font size to set the font size */ + /* Supported sizes are 8x16, 16x32, 24x48 and 32x64 */ + vesa_char_height = iflags.wc_fontsiz_map; + if (vesa_char_height <= 0 || vesa_char_height > vesa_y_res / 30) { + vesa_char_height = vesa_y_res / 30; + } + if (vesa_char_height < 32) { + vesa_char_height = 16; + } else if (vesa_char_height < 48) { + vesa_char_height = 32; + } else if (vesa_char_height < 64) { + vesa_char_height = 48; + } else { + vesa_char_height = 64; + } + vesa_char_width = vesa_char_height / 2; + + /* Process tiles for the current video mode */ + vesa_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); + vesa_oview_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); + num_pixels = iflags.wc_tile_width * iflags.wc_tile_height; + num_oview_pixels = vesa_oview_width * vesa_oview_height; + set_tile_type(vesa_pixel_size > 8); + for (i = 0; i < total_tiles_used; ++i) { + const struct TileImage *tile = get_tile(i); + struct TileImage *ov_tile = stretch_tile(tile, vesa_oview_width, vesa_oview_height); + unsigned j; + unsigned char *t_img = (unsigned char *) alloc(num_pixels * vesa_pixel_bytes); + unsigned char *ot_img = (unsigned char *) alloc(num_oview_pixels * vesa_pixel_bytes); + vesa_tiles[i] = t_img; + vesa_oview_tiles[i] = ot_img; + switch (vesa_pixel_bytes) { + case 1: + memcpy(t_img, tile->indexes, num_pixels); + memcpy(ot_img, ov_tile->indexes, num_oview_pixels); + break; + + case 2: + for (j = 0; j < num_pixels; ++j) { + ((uint16_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); + } + for (j = 0; j < num_oview_pixels; ++j) { + ((uint16_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); + } + break; + + case 3: + for (j = 0; j < num_pixels; ++j) { + unsigned long color = vesa_MakeColor(tile->pixels[j]); + t_img[3*j + 0] = color & 0xFF; + t_img[3*j + 1] = (color >> 8) & 0xFF; + t_img[3*j + 2] = (color >> 16) & 0xFF; + } + for (j = 0; j < num_oview_pixels; ++j) { + unsigned long color = vesa_MakeColor(ov_tile->pixels[j]); + ot_img[3*j + 0] = color & 0xFF; + ot_img[3*j + 1] = (color >> 8) & 0xFF; + ot_img[3*j + 2] = (color >> 16) & 0xFF; + } + break; + + case 4: + for (j = 0; j < num_pixels; ++j) { + ((uint32_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); + } + for (j = 0; j < num_oview_pixels; ++j) { + ((uint32_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); + } + break; + } + free_tile(ov_tile); + } + free_tiles(); +} + +/* Set the size of the map viewport */ +static void +vesa_SetViewPort() +{ + unsigned y_reserved = (TOP_MAP_ROW + 5) * vesa_char_height; + unsigned y_map = vesa_y_res - y_reserved; + + viewport_cols = vesa_x_res / iflags.wc_tile_width; + viewport_rows = y_map / iflags.wc_tile_height; + if (viewport_cols > COLNO) viewport_cols = COLNO; + if (viewport_rows > ROWNO) viewport_rows = ROWNO; } /* @@ -813,7 +1213,7 @@ vesa_Init(void) * * If mode == MODETEXT (0x03), then the card is placed into text * mode. Otherwise, the card is placed in the mode selected by - * vesa_detect. Supported modes are those with packed 8 bit pixels. + * vesa_detect. * */ static void @@ -824,18 +1224,25 @@ unsigned mode; if (mode == MODETEXT) { iflags.grmode = 0; + memset(®s, 0, sizeof(regs)); regs.x.ax = mode; (void) __dpmi_int(VIDEO_BIOS, ®s); +#ifdef SIMULATE_CURSOR + free(undercursor); + undercursor = NULL; +#endif } else if (mode >= 0x100) { iflags.grmode = 1; + memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F02; - regs.x.bx = mode & 0x81FF; + regs.x.bx = mode; (void) __dpmi_int(VIDEO_BIOS, ®s); /* Record that the window position is unknown */ vesa_win_pos[0] = 0xFFFFFFFF; vesa_win_pos[1] = 0xFFFFFFFF; } else { iflags.grmode = 0; /* force text mode for error msg */ + memset(®s, 0, sizeof(regs)); regs.x.ax = MODETEXT; (void) __dpmi_int(VIDEO_BIOS, ®s); g_attribute = attrib_text_normal; @@ -851,7 +1258,14 @@ unsigned mode; void vesa_Finish(void) { - free_tiles(); + int i; + + for (i = 0; i < total_tiles_used; ++i) { + free(vesa_tiles[i]); + free(vesa_oview_tiles[i]); + } + free(vesa_tiles); + free(vesa_oview_tiles); vesa_SwitchMode(MODETEXT); windowprocs.win_cliparound = tty_cliparound; g_attribute = attrib_text_normal; @@ -869,18 +1283,18 @@ vesa_Finish(void) * address of the appropriate character definition table for * the current graphics mode into interrupt vector 0x43 (0000:010C). */ -static char __far * +static unsigned char __far * vesa_FontPtrs(void) { USHORT __far *tmp; - char __far *retval; + unsigned char __far *retval; USHORT fseg, foff; tmp = (USHORT __far *) MK_PTR(((USHORT) FONT_PTR_SEGMENT), ((USHORT) FONT_PTR_OFFSET)); foff = READ_ABSOLUTE_WORD(tmp); ++tmp; fseg = READ_ABSOLUTE_WORD(tmp); - retval = (char __far *) MK_PTR(fseg, foff); + retval = (unsigned char __far *) MK_PTR(fseg, foff); return retval; } @@ -898,6 +1312,7 @@ vesa_detect() __dpmi_regs regs; unsigned long mode_addr; struct ModeInfoBlock mode_info; + const char *mode_str; vbe_info_seg = __dpmi_allocate_dos_memory( (sizeof(vbe_info) + 15) / 16, @@ -910,6 +1325,7 @@ vesa_detect() dosmemput(&vbe_info, sizeof(vbe_info), vbe_info_seg * 16L); /* Request VESA BIOS information */ + memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F00; regs.x.di = 0; regs.x.es = vbe_info_seg; @@ -927,8 +1343,29 @@ vesa_detect() mode_addr = (vbe_info.VideoModePtr >> 16) * 16L + (vbe_info.VideoModePtr & 0xFFFF); + /* Allow the user to select a specific mode */ + mode_str = getenv("NH_DISPLAY_MODE"); + if (mode_str != NULL) { + char *end; + unsigned long num = strtoul(mode_str, &end, 16); + if (*end == '\0') { + /* Can we select this mode? */ + if (vesa_GetModeInfo(num, &mode_info) + && mode_info.XResolution >= 640 + && mode_info.YResolution >= 480 + && mode_info.BitsPerPixel >= 8) { + vesa_mode = num & 0x47FF; + } + } + if (vesa_mode == 0xFFFF) + mode_str = NULL; + } + /* Scan the mode list for an acceptable mode */ - vesa_mode = vesa_FindMode(mode_addr, 32); + if (get_palette() != NULL && vesa_mode == 0xFFFF) + vesa_mode = vesa_FindMode(mode_addr, 8); + if (vesa_mode == 0xFFFF) + vesa_mode = vesa_FindMode(mode_addr, 32); if (vesa_mode == 0xFFFF) vesa_mode = vesa_FindMode(mode_addr, 24); if (vesa_mode == 0xFFFF) @@ -944,51 +1381,61 @@ vesa_detect() vesa_GetModeInfo(vesa_mode, &mode_info); vesa_x_res = mode_info.XResolution; vesa_y_res = mode_info.YResolution; - vesa_x_center = (vesa_x_res - 640) / 2; - vesa_y_center = (vesa_y_res - 480) / 2; + vesa_x_center = 0; + vesa_y_center = 0; vesa_scan_line = mode_info.BytesPerScanLine; vesa_win_size = mode_info.WinSize * 1024L; vesa_win_gran = mode_info.WinGranularity * 1024L; vesa_pixel_size = mode_info.BitsPerPixel; vesa_pixel_bytes = (vesa_pixel_size + 7) / 8; if (vbe_info.VbeVersion >= 0x0300) { - vesa_red_pos = mode_info.RedFieldPosition; - vesa_red_size = mode_info.RedMaskSize; - vesa_green_pos = mode_info.GreenFieldPosition; - vesa_green_size = mode_info.GreenMaskSize; - vesa_blue_pos = mode_info.BlueFieldPosition; - vesa_blue_size = mode_info.BlueMaskSize; + if (mode_info.ModeAttributes & 0x80) { + vesa_red_pos = mode_info.LinRedFieldPosition; + vesa_red_shift = 8 - mode_info.LinRedMaskSize; + vesa_green_pos = mode_info.LinGreenFieldPosition; + vesa_green_shift = 8 - mode_info.LinGreenMaskSize; + vesa_blue_pos = mode_info.LinBlueFieldPosition; + vesa_blue_shift = 8 - mode_info.LinBlueMaskSize; + } else { + vesa_red_pos = mode_info.RedFieldPosition; + vesa_red_shift = 8 - mode_info.RedMaskSize; + vesa_green_pos = mode_info.GreenFieldPosition; + vesa_green_shift = 8 - mode_info.GreenMaskSize; + vesa_blue_pos = mode_info.BlueFieldPosition; + vesa_blue_shift = 8 - mode_info.BlueMaskSize; + } } else { switch (vesa_pixel_size) { case 15: vesa_blue_pos = 0; - vesa_blue_size = 5; + vesa_blue_shift = 3; vesa_green_pos = 5; - vesa_green_size = 5; + vesa_green_shift = 3; vesa_red_pos = 10; - vesa_red_size = 5; + vesa_red_shift = 3; break; case 16: vesa_blue_pos = 0; - vesa_blue_size = 5; + vesa_blue_shift = 3; vesa_green_pos = 5; - vesa_green_size = 6; + vesa_green_shift = 2; vesa_red_pos = 11; - vesa_red_size = 5; + vesa_red_shift = 3; break; case 24: case 32: vesa_blue_pos = 0; - vesa_blue_size = 8; + vesa_blue_shift = 0; vesa_green_pos = 8; - vesa_green_size = 8; + vesa_green_shift = 0; vesa_red_pos = 16; - vesa_red_size = 8; + vesa_red_shift = 0; break; } } + vesa_win_func = mode_info.WinFuncPtr; vesa_win_addr[0] = mode_info.WinASegment * 16L; vesa_win_addr[1] = mode_info.WinBSegment * 16L; vesa_win_pos[0] = 0xFFFFFFFF; /* position unknown */ @@ -1010,6 +1457,26 @@ vesa_detect() goto error; /* Shouldn't happen */ } + /* Configure a linear frame buffer if we have it */ + if ((mode_info.ModeAttributes & 0x80) != 0 + && (mode_str == NULL || (vesa_mode & 0x4000) != 0)) { + unsigned sel = vesa_segment; + unsigned win_size = mode_info.BytesPerScanLine * mode_info.YResolution; + unsigned addr = vesa_map_frame_buffer(mode_info.PhysBasePtr, win_size); + if (sel == 0) { + sel = __dpmi_allocate_ldt_descriptors(1); + } + if (addr != 0) { + vesa_mode |= 0x4000; + vesa_segment = sel; + __dpmi_set_segment_base_address(sel, addr); + __dpmi_set_segment_limit(sel, (win_size - 1) | 0xFFF); + } else { + __dpmi_free_ldt_descriptor(sel); + vesa_segment = 0; + } + } + __dpmi_free_dos_memory(vbe_info_sel); return TRUE; @@ -1027,6 +1494,13 @@ unsigned bits; struct ModeInfoBlock mode_info0, mode_info; unsigned model = (bits == 8) ? 4 : 6; + if (iflags.wc_video_width < 640) { + iflags.wc_video_width = 640; + } + if (iflags.wc_video_height < 480) { + iflags.wc_video_height = 480; + } + memset(&mode_info, 0, sizeof(mode_info)); selected_mode = 0xFFFF; while (1) { @@ -1038,8 +1512,8 @@ unsigned bits; if (!vesa_GetModeInfo(mode, &mode_info0)) continue; /* Check that the mode is acceptable */ - if (mode_info0.XResolution < 640) continue; - if (mode_info0.YResolution < 480) continue; + if (mode_info0.XResolution < iflags.wc_video_width) continue; + if (mode_info0.YResolution < iflags.wc_video_height) continue; if (mode_info0.NumberOfPlanes != 1) continue; if (mode_info0.BitsPerPixel != bits) continue; if (mode_info0.NumberOfBanks != 1) continue; @@ -1065,66 +1539,239 @@ unsigned bits; * */ static void -vesa_WriteChar(chr, col, row, colour, transparent) +vesa_WriteChar(chr, col, row, colour) int chr, col, row, colour; -boolean transparent; { - int i, j; int pixx, pixy; - unsigned char __far *fp = font; - unsigned char fnt; + /* min() protects from callers */ + pixx = min(col, (CO - 1)) * vesa_char_width; + pixy = min(row, (LI - 1)) * vesa_char_height; - pixx = min(col, (CO - 1)) * 8; /* min() protects from callers */ - pixy = min(row, (LI - 1)) * 16; /* assumes 8 x 16 char set */ pixx += vesa_x_center; pixy += vesa_y_center; - for (i = 0; i < MAX_ROWS_PER_CELL; ++i) { - fnt = READ_ABSOLUTE((fp + chr * 16 + i)); - for (j = 0; j < 8; ++j) { - if (fnt & (0x80 >> j)) { - vesa_WritePixel(pixx + j, pixy + i, colour + FIRST_TEXT_COLOR); - } else if (!transparent) { - vesa_WritePixel(pixx + j, pixy + i, BACKGROUND_VESA_COLOR); + vesa_WriteCharXY(chr, pixx, pixy, colour); +} + +/* + * As vesa_WriteChar, but specify coordinates by pixel and allow + * transparency + */ +static void +vesa_WriteCharXY(chr, pixx, pixy, colour) +int chr, pixx, pixy, colour; +{ + /* Flush if cache is full or if not contiguous to the last character */ + if (chr_cache_size >= SIZE(chr_cache)) { + vesa_flush_text(); + } + if (chr_cache_size != 0 && chr_cache_lastx + vesa_char_width != pixx) { + vesa_flush_text(); + } + if (chr_cache_size != 0 && chr_cache_pixy != pixy) { + vesa_flush_text(); + } + /* Add to cache and write later */ + if (chr_cache_size == 0) { + chr_cache_pixx = pixx; + chr_cache_pixy = pixy; + } + chr_cache_lastx = pixx; + chr_cache[chr_cache_size].chr = chr; + chr_cache[chr_cache_size].colour = colour; + ++chr_cache_size; +} + +/* + * Draw a character with a transparent background + * Don't bother cacheing; only the position bar and the cursor use this + */ +static void +vesa_WriteCharTransparent(chr, pixx, pixy, colour) +int chr, pixx, pixy, colour; +{ + int px, py; + + for (py = 0; py < vesa_char_height; ++py) { + for (px = 0; px < vesa_char_width; ++px) { + if (vesa_GetCharPixel(chr, px, py)) { + vesa_WritePixel(pixx + px, pixy + py, colour + FIRST_TEXT_COLOR); } } } } -/* - * Like vesa_WriteChar, but draw the character in local memory rather than in - * the VGA frame buffer. - * - * vesa_redrawmap uses this to gather a row of cells in local memory and then - * draw them in strict row-major order, minimizing the use of the VESA - * windowing function. - * - */ -static void -vesa_WriteCharInMemory(chr, col, buf, colour) -int chr, col; -char buf[TILE_Y][640*2]; -int colour; +void +vesa_flush_text() { - int i, j; - int pixx; + if (chr_cache_size == 0) return; - unsigned char __far *fp = font; - unsigned char fnt; + vesa_WriteTextRow(chr_cache_pixx, chr_cache_pixy, chr_cache, chr_cache_size); + chr_cache_size = 0; +} - pixx = min(col, (CO - 1)) * 8; /* min() protects from callers */ +static void +vesa_WriteTextRow(pixx, pixy, t_row, t_row_width) +int pixx, pixy; +struct VesaCharacter const *t_row; +unsigned t_row_width; +{ + int x, px, py; + unsigned i; + unsigned p_row_width = t_row_width * vesa_char_width * vesa_pixel_bytes; + unsigned char *p_row = (unsigned char *) alloc(p_row_width); + unsigned long offset = pixy * (unsigned long)vesa_scan_line + pixx * vesa_pixel_bytes; + unsigned char fg[4], bg[4]; - for (i = 0; i < MAX_ROWS_PER_CELL; ++i) { - fnt = READ_ABSOLUTE((fp + chr * 16 + i)); - for (j = 0; j < 8; ++j) { - if (fnt & (0x80 >> j)) { - buf[i][pixx + j] = colour + FIRST_TEXT_COLOR; + /* Preprocess the background color */ + if (vesa_pixel_bytes == 1) { + bg[0] = BACKGROUND_VESA_COLOR; + } else { + unsigned long pix = vesa_palette[BACKGROUND_VESA_COLOR]; + bg[0] = pix & 0xFF; + bg[1] = (pix >> 8) & 0xFF; + bg[2] = (pix >> 16) & 0xFF; + bg[3] = (pix >> 24) & 0xFF; + } + + /* First loop: draw one raster line of all row entries */ + for (py = 0; py < vesa_char_height; ++py) { + /* Second loop: draw one raster line of one character */ + x = 0; + for (i = 0; i < t_row_width; ++i) { + int chr = t_row[i].chr; + int colour = t_row[i].colour + FIRST_TEXT_COLOR; + /* Preprocess the foreground color */ + if (vesa_pixel_bytes == 1) { + fg[0] = colour; } else { - buf[i][pixx + j] = BACKGROUND_VESA_COLOR; + unsigned long pix = vesa_palette[colour]; + fg[0] = pix & 0xFF; + fg[1] = (pix >> 8) & 0xFF; + fg[2] = (pix >> 16) & 0xFF; + fg[3] = (pix >> 24) & 0xFF; + } + /* Third loop: draw eight pixels */ + for (px = 0; px < vesa_char_width; px += 8) { + /* Fourth loop: draw one pixel */ + int px2; + unsigned char fnt = vesa_GetCharPixelRow(chr, px, py); + int l = vesa_char_width - px; + if (l > 8) { + l = 8; + } + for (px2 = 0; px2 < l; ++px2) { + if (fnt & 0x80) { + memcpy(p_row + x, fg, vesa_pixel_bytes); + } else { + memcpy(p_row + x, bg, vesa_pixel_bytes); + } + x += vesa_pixel_bytes; + fnt <<= 1; + } } } + vesa_WritePixelRow(offset, p_row, p_row_width); + offset += vesa_scan_line; } + free(p_row); +} + +static boolean +vesa_GetCharPixel(ch, x, y) +int ch; +unsigned x, y; +{ + unsigned x2; + unsigned char fnt; + + x2 = x % 8; + + fnt = vesa_GetCharPixelRow(ch, x, y); + return (fnt & (0x80 >> x2)) != 0; +} + +static unsigned char +vesa_GetCharPixelRow(ch, x, y) +int ch; +unsigned x, y; +{ + unsigned x1; + unsigned char fnt; + size_t offset; + + if (x >= vesa_char_width) return 0; + if (y >= vesa_char_height) return 0; + + x1 = x / 8; + + const unsigned char __far *fp; + + if (ch < 0 || 255 < ch) return FALSE; + offset = ch * 16 + (y * 16 / vesa_char_height); + fp = font; + fnt = READ_ABSOLUTE((fp + offset)); + + if (vesa_char_width != 8) { + unsigned long fnt2 = fnt; + unsigned width = vesa_char_width; + if (width % 3 == 0) { + fnt2 = vesa_TriplePixels(fnt2); + width /= 3; + } + while (width > 8) { + fnt2 = vesa_DoublePixels(fnt2); + width /= 2; + } + fnt2 <<= 32 - vesa_char_width; + fnt = (unsigned char)(fnt2 >> (24 - 8 * x1)); + } + + return fnt; +} + +/* Scale font pixels horizontally */ +static unsigned long +vesa_DoublePixels(fnt) +unsigned long fnt; +{ + static const unsigned char double_bits[] = { + 0x00, 0x03, 0x0C, 0x0F, + 0x30, 0x33, 0x3C, 0x3F, + 0xC0, 0xC3, 0xCC, 0xCF, + 0xF0, 0xF3, 0xFC, 0xFF + }; + unsigned i; + unsigned long fnt2; + + fnt2 = 0; + for (i = 0; i < 16; i += 4) { + unsigned long b4 = (fnt >> i) & 0xF; + fnt2 |= (unsigned long)double_bits[b4] << (i * 2); + } + return fnt2; +} + +static unsigned long +vesa_TriplePixels(fnt) +unsigned long fnt; +{ + static const unsigned short triple_bits[] = { + 00000, 00007, 00070, 00077, + 00700, 00707, 00770, 00777, + 07000, 07007, 07070, 07077, + 07700, 07707, 07770, 07777 + }; + unsigned i; + unsigned long fnt2; + + fnt2 = 0; + for (i = 0; i < 11; i += 4) { + unsigned long b4 = (fnt >> i) & 0xF; + fnt2 |= (unsigned long)triple_bits[b4] << (i * 3); + } + return fnt2; } /* @@ -1132,93 +1779,45 @@ int colour; * at the desired location (col,row). * * Note: (col,row) in this case refer to the coordinate location in - * NetHack character grid terms, (ie. the 40 x 25 character grid), + * NetHack character grid terms, relative to the map viewport, * not the x,y pixel location. * */ static void -vesa_DisplayCell(tile, col, row) -const struct TileImage *tile; +vesa_DisplayCell(tilenum, col, row) +int tilenum; int col, row; { - int i, j, pixx, pixy; + unsigned char const *tile; + unsigned t_width, t_height; + unsigned char const *tptr; + int px, py, pixx, pixy; + unsigned long offset; + unsigned p_row_width; - pixx = col * TILE_X; - pixy = row * TILE_Y; if (iflags.over_view) { - pixx /= 2; - pixx += vesa_x_center; - pixy += vesa_y_center; - if (vesa_pixel_size != 8) { - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; j += 2) { - unsigned index = i * tile->width + j; - unsigned long c1 = vesa_palette[tile->indexes[index + 0]]; - unsigned long c2 = vesa_palette[tile->indexes[index + 1]]; - unsigned char r1, r2, g1, g2, b1, b2; - - vesa_GetRGB(c1, &r1, &g1, &b1); - vesa_GetRGB(c2, &r2, &g2, &b2); - r1 = (r1 + r2) / 2; - g1 = (g1 + g2) / 2; - b1 = (b1 + b2) / 2; - vesa_WritePixel32(pixx + j / 2, pixy + i, - vesa_MakeColor(r1, g1, b1)); - } - } - } else { - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; j += 2) { - unsigned index = i * tile->width + j; - vesa_WritePixel(pixx + j / 2, pixy + i, tile->indexes[index]); - } - } - } + tile = vesa_oview_tiles[tilenum]; + t_width = vesa_oview_width; + t_height = vesa_oview_height; } else { - pixx += vesa_x_center; - pixy += vesa_y_center; - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; ++j) { - unsigned index = i * tile->width + j; - vesa_WritePixel(pixx + j, pixy + i, tile->indexes[index]); - } - } + tile = vesa_tiles[tilenum]; + t_width = iflags.wc_tile_width; + t_height = iflags.wc_tile_height; } -} -/* - * Like vesa_DisplayCell, but draw the tile in local memory rather than in - * the VGA frame buffer. - * - * vesa_redrawmap uses this to gather a row of cells in local memory and then - * draw them in strict row-major order, minimizing the use of the VESA - * windowing function. - * - */ -static void -vesa_DisplayCellInMemory(tile, col, buf) -const struct TileImage *tile; -int col; -char buf[TILE_Y][640*2]; -{ - int i, j, pixx; + p_row_width = t_width * vesa_pixel_bytes; - pixx = col * TILE_X; - if (iflags.over_view && vesa_pixel_size == 8) { - pixx /= 2; - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; j += 2) { - unsigned index = i * tile->width + j; - buf[i][pixx + j / 2] = tile->indexes[index]; - } - } - } else { - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; ++j) { - unsigned index = i * tile->width + j; - buf[i][pixx + j] = tile->indexes[index]; - } - } + pixx = col * t_width; + pixy = row * t_height + TOP_MAP_ROW * vesa_char_height; + pixx += vesa_x_center; + pixy += vesa_y_center; + offset = pixy * (unsigned long)vesa_scan_line + pixx * vesa_pixel_bytes; + tptr = tile; + + for (py = 0; py < t_height; ++py) { + vesa_WritePixelRow(offset, tptr, p_row_width); + offset += vesa_scan_line; + tptr += p_row_width; } } @@ -1242,7 +1841,7 @@ int len, col, row, colour; i = 0; us = (const unsigned char *) s; while ((*us != 0) && (i < len) && (col < (CO - 1))) { - vesa_WriteChar(*us, col, row, colour, FALSE); + vesa_WriteChar(*us, col, row, colour); ++us; ++i; ++col; @@ -1252,8 +1851,8 @@ int len, col, row, colour; /* * Initialize the VGA palette with the desired colours. This * must be a series of 720 bytes for use with a card in 256 - * colour mode at 640 x 480. The first 240 palette entries are - * used by the tile set; the last 16 are reserved for text. + * colour mode. The first 240 palette entries are used by the + * tile set; the last 16 are reserved for text. * */ static boolean @@ -1261,9 +1860,9 @@ vesa_SetPalette(palette) const struct Pixel *palette; { if (vesa_pixel_size == 8) { - vesa_SetHardPalette(palette); + return vesa_SetHardPalette(palette); } else { - vesa_SetSoftPalette(palette); + return vesa_SetSoftPalette(palette); } } @@ -1271,13 +1870,10 @@ static boolean vesa_SetHardPalette(palette) const struct Pixel *palette; { - const struct Pixel *p = palette; int palette_sel = -1; /* custodial */ int palette_seg; - unsigned long palette_ptr; + unsigned char p2[1024]; unsigned i, shift; - unsigned char r, g, b; - unsigned long color; __dpmi_regs regs; palette_seg = __dpmi_allocate_dos_memory( 1024 / 16, &palette_sel); @@ -1297,36 +1893,23 @@ const struct Pixel *palette; shift = 8 - regs.h.bh; } + /* First, try the VESA palette function */ /* Set the tile set and text colors */ - palette_ptr = palette_seg * 16L; #ifdef USE_TILES for (i = 0; i < FIRST_TEXT_COLOR; ++i) { - r = p->r >> shift; - g = p->g >> shift; - b = p->b >> shift; - color = ((unsigned long) r << 16) - | ((unsigned long) g << 8) - | ((unsigned long) b << 0); - _farpokel(_dos_ds, palette_ptr, color); - palette_ptr += 4; - ++p; + p2[i*4 + 0] = palette[i].b >> shift; + p2[i*4 + 1] = palette[i].g >> shift; + p2[i*4 + 2] = palette[i].r >> shift; } -#else - palette_ptr += FIRST_TEXT_COLOR * 4; #endif - p = defpalette; for (i = FIRST_TEXT_COLOR; i < 256; ++i) { - r = p->r >> shift; - g = p->g >> shift; - b = p->b >> shift; - color = ((unsigned long) r << 16) - | ((unsigned long) g << 8) - | ((unsigned long) b << 0); - _farpokel(_dos_ds, palette_ptr, color); - palette_ptr += 4; - ++p; + p2[i*4 + 0] = defpalette[i-FIRST_TEXT_COLOR].b >> shift; + p2[i*4 + 1] = defpalette[i-FIRST_TEXT_COLOR].g >> shift; + p2[i*4 + 2] = defpalette[i-FIRST_TEXT_COLOR].r >> shift; } + /* Call the BIOS */ + dosmemput(p2, 256*4, palette_seg*16L); memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F09; regs.h.bl = 0; @@ -1336,6 +1919,33 @@ const struct Pixel *palette; regs.x.es = palette_seg; (void) __dpmi_int(VIDEO_BIOS, ®s); + /* If that didn't work, use the original BIOS function */ + if (regs.x.ax != 0x004F) { + /* Set the tile set and text colors */ +#ifdef USE_TILES + for (i = 0; i < FIRST_TEXT_COLOR; ++i) { + p2[i*3 + 0] = palette[i].r >> shift; + p2[i*3 + 1] = palette[i].g >> shift; + p2[i*3 + 2] = palette[i].b >> shift; + } +#endif + for (i = FIRST_TEXT_COLOR; i < 256; ++i) { + p2[i*3 + 0] = defpalette[i-FIRST_TEXT_COLOR].r >> shift; + p2[i*3 + 1] = defpalette[i-FIRST_TEXT_COLOR].g >> shift; + p2[i*3 + 2] = defpalette[i-FIRST_TEXT_COLOR].b >> shift; + } + + /* Call the BIOS */ + dosmemput(p2, 256*3, palette_seg*16L); + memset(®s, 0, sizeof(regs)); + regs.x.ax = 0x1012; + regs.x.cx = 256; + regs.x.bx = 0; + regs.x.dx = 0; + regs.x.es = palette_seg; + (void) __dpmi_int(VIDEO_BIOS, ®s); + } + __dpmi_free_dos_memory(palette_sel); return TRUE; @@ -1350,33 +1960,29 @@ const struct Pixel *palette; { const struct Pixel *p; unsigned i; - unsigned char r, g, b; /* Set the tile set and text colors */ #ifdef USE_TILES - p = palette; - for (i = 0; i < FIRST_TEXT_COLOR; ++i) { - r = p->r; - g = p->g; - b = p->b; - vesa_palette[i] = vesa_MakeColor(r, g, b); - ++p; + if (palette != NULL) { + p = palette; + for (i = 0; i < FIRST_TEXT_COLOR; ++i) { + vesa_palette[i] = vesa_MakeColor(*p); + ++p; + } } #endif p = defpalette; for (i = FIRST_TEXT_COLOR; i < 256; ++i) { - r = p->r; - g = p->g; - b = p->b; - vesa_palette[i] = vesa_MakeColor(r, g, b); + vesa_palette[i] = vesa_MakeColor(*p); ++p; } + return TRUE; } #ifdef POSITIONBAR #define PBAR_ROW (LI - 4) -#define PBAR_COLOR_ON 16 /* slate grey background colour of tiles */ +#define PBAR_COLOR_ON 6 /* slate grey background colour of tiles */ #define PBAR_COLOR_OFF 0 /* bluish grey, used in old style only */ #define PBAR_COLOR_STAIRS CLR_BROWN /* brown */ #define PBAR_COLOR_HERO CLR_WHITE /* creamy white */ @@ -1387,7 +1993,7 @@ void vesa_update_positionbar(posbar) char *posbar; { - char *p = pbar; + unsigned char *p = pbar; if (posbar) while (*posbar) *p++ = *posbar++; @@ -1397,13 +2003,13 @@ char *posbar; static void positionbar() { - char *posbar = pbar; + unsigned char *posbar = pbar; int feature, ucol; - int k, x, y, colour, row; int startk, stopk; boolean nowhere = FALSE; - int pixy = (PBAR_ROW * MAX_ROWS_PER_CELL); + int pixx, col; + int pixy = (PBAR_ROW * vesa_char_height); int tmp; if (!iflags.grmode || !iflags.tile_view) @@ -1416,53 +2022,45 @@ positionbar() #endif return; } + startk = clipx * vesa_x_res / COLNO; + stopk = (clipxmax + 1) * vesa_x_res / COLNO; #ifdef OLD_STYLE - for (y = pixy; y < (pixy + MAX_ROWS_PER_CELL); ++y) { - for (x = 0; x < 640; ++x) { - k = x / 8; - if ((k < clipx) || (k > clipxmax)) { - colour = PBAR_COLOR_OFF; - } else - colour = PBAR_COLOR_ON; - vesa_WritePixel(x + vesa_x_center, y + vesa_y_center, colour); - } - } + vesa_FillRect(0, pixy, startk, vesa_char_height, + PBAR_COLOR_OFF + FIRST_TEXT_COLOR); + vesa_FillRect(startk, pixy, stopk - startk, vesa_char_height, + PBAR_COLOR_ON + FIRST_TEXT_COLOR); + vesa_FillRect(stopk, pixy, vesa_x_res - stopk, vesa_char_height, + PBAR_COLOR_OFF + FIRST_TEXT_COLOR); #else - for (y = pixy, row = 0; y < (pixy + MAX_ROWS_PER_CELL); ++y, ++row) { - if ((!row) || (row == (ROWS_PER_CELL - 1))) { - startk = 0; - stopk = SCREENBYTES; - } else { - startk = clipx; - stopk = clipxmax; - } - for (x = 0; x < 640; ++x) { - k = x / 8; - if ((k < startk) || (k > stopk)) - colour = BACKGROUND_VGA_COLOR; - else - colour = PBAR_COLOR_ON; - vesa_WritePixel(x + vesa_x_center, y + vesa_y_center, colour); - } - } + vesa_FillRect(0, pixy, vesa_x_res, 1, PBAR_COLOR_ON + FIRST_TEXT_COLOR); + vesa_FillRect(0, pixy + 1, startk, vesa_char_height - 2, + BACKGROUND_VESA_COLOR); + vesa_FillRect(startk, pixy + 1, stopk - startk, vesa_char_height - 2, + PBAR_COLOR_ON + FIRST_TEXT_COLOR); + vesa_FillRect(stopk, pixy + 1, vesa_x_res - stopk, vesa_char_height - 2, + BACKGROUND_VESA_COLOR); + vesa_FillRect(0, pixy + vesa_char_height - 1, vesa_x_res, 1, + PBAR_COLOR_ON + FIRST_TEXT_COLOR); #endif ucol = 0; if (posbar) { while (*posbar != 0) { feature = *posbar++; + col = *posbar++; + pixx = col * vesa_x_res / COLNO; switch (feature) { case '>': - vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE); + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_STAIRS); break; case '<': - vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE); + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_STAIRS); break; case '@': - ucol = (int) *posbar++; - vesa_WriteChar(feature, ucol, PBAR_ROW, PBAR_COLOR_HERO, TRUE); + ucol = col; + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_HERO); break; default: /* unanticipated symbols */ - vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE); + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_STAIRS); break; } } @@ -1471,51 +2069,73 @@ positionbar() if (inmap) { tmp = curcol + 1; if ((tmp != ucol) && (curcol >= 0)) - vesa_WriteChar('_', tmp, PBAR_ROW, PBAR_COLOR_HERO, TRUE); + vesa_WriteCharTransparent('_', tmp * vesa_x_res / COLNO, pixy, + PBAR_COLOR_HERO); } #endif + vesa_flush_text(); } #endif /*POSITIONBAR*/ #ifdef SIMULATE_CURSOR -static unsigned long undercursor[TILE_Y][TILE_X]; - void vesa_DrawCursor() { - unsigned x, y, left, top, right, bottom, width; + static boolean last_inmap = FALSE; + unsigned x, y, left, top, right, bottom, width, height; boolean isrogue = Is_rogue_level(&u.uz); boolean halfwidth = (isrogue || iflags.over_view || iflags.traditional_view || !inmap); int curtyp; + if (inmap && !last_inmap) { + vesa_redrawmap(); + } + last_inmap = inmap; + if (!cursor_type && inmap) return; /* CURSOR_INVIS - nothing to do */ + if (undercursor == NULL) { + /* size for the greater of one tile or one character */ + unsigned size1 = vesa_char_width * vesa_char_height; + unsigned size2 = iflags.wc_tile_width * iflags.wc_tile_height; + undercursor = (unsigned long *) alloc( + sizeof(undercursor[0]) * max(size1, size2)); + } x = min(curcol, (CO - 1)); /* protection from callers */ y = min(currow, (LI - 1)); /* protection from callers */ - if (!halfwidth && ((x < clipx) || (x > clipxmax))) + if (!halfwidth + && ((x < clipx) || (x > clipxmax) || (y < clipy) || (y > clipymax))) return; - if (inmap) + if (inmap) { x -= clipx; - left = x * TILE_X; /* convert to pixels */ - top = y * TILE_Y; - if (halfwidth) { - left /= 2; - width = TILE_X / 2; - } else { - width = TILE_X; + y -= clipy; + } + /* convert to pixels */ + if (!inmap || iflags.traditional_view) { + width = vesa_char_width; + height = vesa_char_height; + } else if (iflags.over_view) { + width = vesa_oview_width; + height = vesa_oview_height; + } else { + width = iflags.wc_tile_width; + height = iflags.wc_tile_height; + } + left = x * width + vesa_x_center; + top = y * height + vesa_y_center; + if (y >= TOP_MAP_ROW) { + top -= (height - vesa_char_height) * TOP_MAP_ROW; } - left += vesa_x_center; - top += vesa_y_center; right = left + width - 1; - bottom = top + TILE_Y - 1; + bottom = top + height - 1; - for (y = 0; y < ROWS_PER_CELL; ++y) { + for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - undercursor[y][x] = vesa_ReadPixel32(left + x, top + y); + undercursor[y * width + x] = vesa_ReadPixel32(left + x, top + y); } } @@ -1577,35 +2197,45 @@ vesa_DrawCursor() void vesa_HideCursor() { - unsigned x, y, left, top, width; + unsigned x, y, left, top, width, height; boolean isrogue = Is_rogue_level(&u.uz); boolean halfwidth = (isrogue || iflags.over_view || iflags.traditional_view || !inmap); - int curtyp; if (!cursor_type && inmap) return; /* CURSOR_INVIS - nothing to do */ + if (undercursor == NULL) + return; x = min(curcol, (CO - 1)); /* protection from callers */ y = min(currow, (LI - 1)); /* protection from callers */ - if (!halfwidth && ((x < clipx) || (x > clipxmax))) + if (!halfwidth + && ((x < clipx) || (x > clipxmax) || (y < clipy) || (y > clipymax))) return; - if (inmap) + if (inmap) { x -= clipx; - left = x * TILE_X; /* convert to pixels */ - top = y * TILE_Y; - if (halfwidth) { - left /= 2; - width = TILE_X / 2; - } else { - width = TILE_X; + y -= clipy; + } + /* convert to pixels */ + if (!inmap || iflags.traditional_view) { + width = vesa_char_width; + height = vesa_char_height; + } else if (iflags.over_view) { + width = vesa_oview_width; + height = vesa_oview_height; + } else { + width = iflags.wc_tile_width; + height = iflags.wc_tile_height; + } + left = x * width + vesa_x_center; + top = y * height + vesa_y_center; + if (y >= TOP_MAP_ROW) { + top -= (height - vesa_char_height) * TOP_MAP_ROW; } - left += vesa_x_center; - top += vesa_y_center; - for (y = 0; y < ROWS_PER_CELL; ++y) { + for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - vesa_WritePixel32(left + x, top + y, undercursor[y][x]); + vesa_WritePixel32(left + x, top + y, undercursor[y * width + x]); } } } diff --git a/sys/msdos/vidvga.c b/sys/msdos/vidvga.c index d945247d0..768f8d619 100644 --- a/sys/msdos/vidvga.c +++ b/sys/msdos/vidvga.c @@ -502,18 +502,20 @@ boolean clearfirst; #endif /* USE_TILES && CLIPPING */ void -vga_userpan(left) -boolean left; +vga_userpan(pan) +enum vga_pan_direction pan; { int x; /* pline("Into userpan"); */ if (iflags.over_view || iflags.traditional_view) return; - if (left) + if (pan == pan_left) x = min(COLNO - 1, clipxmax + 10); - else + else if (pan == pan_right) x = max(0, clipx - 10); + else + return; vga_cliparound(x, 10); /* y value is irrelevant on VGA clipping */ positionbar(); vga_DrawCursor(); diff --git a/sys/share/NetHack.cnf b/sys/share/NetHack.cnf index 58154bfce..3d29538a0 100644 --- a/sys/share/NetHack.cnf +++ b/sys/share/NetHack.cnf @@ -27,6 +27,10 @@ OPTIONS=rawio,BIOS,symset:IBMGraphics_2,roguesymset:RogueEpyx # To use VGA graphical tiles on an MS-DOS PC with VGA or better,uncomment # this: #OPTIONS=video:autodetect +# And to explicitly set the resolution: +#OPTIONS=video_width:1024 +#OPTIONS=video_height:768 + # If your machine is NEC PC-9800, use: #OPTIONS=rawio,BIOS,video:default diff --git a/win/X11/winX.c b/win/X11/winX.c index 05d82e0eb..f6e537541 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -95,9 +95,6 @@ static XtSignalId X11_sig_id; #endif #endif -/* this is only needed until X11_status_* routines are written */ -extern NEARDATA winid WIN_STATUS; - /* Interface definition, for windows.c */ struct window_procs X11_procs = { "X11", diff --git a/win/gnome/gnbind.c b/win/gnome/gnbind.c index 0b99c2453..2c2182301 100644 --- a/win/gnome/gnbind.c +++ b/win/gnome/gnbind.c @@ -19,9 +19,6 @@ winid WIN_WORN = WIN_ERR; extern void tty_raw_print(const char *); extern void tty_raw_print_bold(const char *); -/* this is only needed until gnome_status_* routines are written */ -extern NEARDATA winid WIN_STATUS; - /* Interface definition, for windows.c */ struct window_procs Gnome_procs = { "Gnome", WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L, diff --git a/win/share/bmptiles.c b/win/share/bmptiles.c index 641415915..b75bbdc54 100644 --- a/win/share/bmptiles.c +++ b/win/share/bmptiles.c @@ -107,15 +107,6 @@ struct TileSetImage *image; if (!read_info_header(fp, &header2)) goto error; if (!check_info_header(&header2)) goto error; -#if 0 /* TODO */ - if (header2.Compression == BI_PNG) { - /* Image data is an embedded PNG bit stream */ - boolean ok = do_read_png_tiles(fp, image)); - fclose(fp); - return ok; - } -#endif - /* header2.Height < 0 means the Y coordinate is reversed; the origin is * top left rather than bottom left */ image->width = header2.Width; @@ -300,7 +291,7 @@ struct TileSetImage *image; break; case 32: for (x = 0; x < image->width; ++x) { - uint32 color = read_u32(row_bytes + x * 2); + uint32 color = read_u32(row_bytes + x * 4); row[x] = build_pixel(&header2, color); } break; @@ -609,7 +600,7 @@ uint32 color; if (mask == 0) return 0; bits = 0xFFFF; /* 0xFF, 0xF, 0x3, 0x1 */ shift = 16; /* 8, 4, 2, 1 */ - while (bits != 0) { + while (shift != 0) { if ((mask & bits) == 0) { mask >>= shift; color >>= shift; diff --git a/win/share/tileset.c b/win/share/tileset.c index d37b91281..e9b448ab0 100644 --- a/win/share/tileset.c +++ b/win/share/tileset.c @@ -6,6 +6,7 @@ #include "tileset.h" static void FDECL(get_tile_map, (const char *)); +static unsigned FDECL(gcd, (unsigned, unsigned)); static void FDECL(split_tiles, (const struct TileSetImage *)); static void FDECL(free_image, (struct TileSetImage *)); @@ -100,6 +101,29 @@ error: return FALSE; } +/* Free tile memory not required by the chosen display mode */ +boolean +set_tile_type(true_color) +boolean true_color; +{ + unsigned i; + + if (tiles) { + if (true_color) { + for (i = 0; i < num_tiles; ++i) { + free((genericptr_t) tiles[i].indexes); + tiles[i].indexes = NULL; + } + have_palette = FALSE; + } else { + for (i = 0; i < num_tiles; ++i) { + free((genericptr_t) tiles[i].pixels); + tiles[i].pixels = NULL; + } + } + } +} + const struct Pixel * get_palette() { @@ -155,6 +179,112 @@ unsigned tile_index; return &tiles[tile_index]; } +/* Note that any tile returned by this function must be freed */ +struct TileImage * +stretch_tile(inp_tile, out_width, out_height) +const struct TileImage *inp_tile; +unsigned out_width, out_height; +{ + unsigned x_scale_inp, x_scale_out, y_scale_inp, y_scale_out; + unsigned divisor; + unsigned size; + struct TileImage *out_tile; + unsigned x_inp, y_inp, x2, y2, x_out, y_out; + unsigned pos; + + /* Derive the scale factors */ + divisor = gcd(out_width, inp_tile->width); + x_scale_inp = inp_tile->width / divisor; + x_scale_out = out_width / divisor; + divisor = gcd(out_height, inp_tile->height); + y_scale_inp = inp_tile->height / divisor; + y_scale_out = out_height / divisor; + + /* Derive the stretched tile */ + out_tile = (struct TileImage *) alloc(sizeof(struct TileImage)); + out_tile->width = out_width; + out_tile->height = out_height; + size = out_width * out_height; + if (inp_tile->pixels != NULL) { + out_tile->pixels = (struct Pixel *) alloc(size * sizeof(struct Pixel)); + divisor = x_scale_inp * y_scale_inp; + for (y_out = 0; y_out < out_height; ++y_out) { + for (x_out = 0; x_out < out_width; ++x_out) { + unsigned r, g, b, a; + + /* Derive output pixels by blending input pixels */ + r = 0; + g = 0; + b = 0; + a = 0; + for (y2 = 0; y2 < y_scale_inp; ++y2) { + y_inp = (y_out * y_scale_inp + y2) / y_scale_out; + for (x2 = 0; x2 < x_scale_inp; ++x2) { + x_inp = (x_out * x_scale_inp + x2) / x_scale_out; + pos = y_inp * inp_tile->width + x_inp; + r += inp_tile->pixels[pos].r; + g += inp_tile->pixels[pos].g; + b += inp_tile->pixels[pos].b; + a += inp_tile->pixels[pos].a; + } + } + + pos = y_out * out_width + x_out; + out_tile->pixels[pos].r = r / divisor; + out_tile->pixels[pos].g = g / divisor; + out_tile->pixels[pos].b = b / divisor; + out_tile->pixels[pos].a = a / divisor; + } + } + } else { + out_tile->pixels = NULL; + } + + /* If the output device uses a palette, we can't blend; just pick + a subset of the pixels */ + if (inp_tile->indexes != NULL) { + out_tile->indexes = (unsigned char *) alloc(size); + for (y_out = 0; y_out < out_height; ++y_out) { + for (x_out = 0; x_out < out_width; ++x_out) { + pos = y_out * out_width + x_out; + x_inp = x_out * x_scale_inp / x_scale_out; + y_inp = y_out * y_scale_inp / y_scale_out; + out_tile->indexes[pos] = + inp_tile->indexes[y_inp * inp_tile->width + x_inp]; + } + } + } else { + out_tile->indexes = NULL; + } + return out_tile; +} + +/* Free a tile returned by stretch_tile */ +/* Do NOT use this with tiles returned by get_tile */ +void +free_tile(tile) +struct TileImage *tile; +{ + if (tile != NULL) { + free(tile->indexes); + free(tile->pixels); + free(tile); + } +} + +/* Return the greatest common divisor */ +static unsigned +gcd(a, b) +unsigned a, b; +{ + while (TRUE) { + if (b == 0) return a; + a %= b; + if (a == 0) return b; + b %= a; + } +} + static void split_tiles(image) const struct TileSetImage *image; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index a6ad2f7a2..46a378088 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -72,9 +72,6 @@ extern short glyph2tile[]; #define HUPSKIP_RESULT(RES) /*empty*/ #endif /* ?HANGUP_HANDLING */ -/* this is only needed until tty_status_* routines are written */ -extern NEARDATA winid WIN_STATUS; - /* Interface definition, for windows.c */ struct window_procs tty_procs = { "tty", @@ -155,11 +152,12 @@ char defmorestr[] = "--More--"; #if defined(USE_TILES) && defined(MSDOS) boolean clipping = FALSE; /* clipping on? */ int clipx = 0, clipxmax = 0; +int clipy = 0, clipymax = 0; #else static boolean clipping = FALSE; /* clipping on? */ static int clipx = 0, clipxmax = 0; -#endif static int clipy = 0, clipymax = 0; +#endif #endif /* CLIPPING */ #if defined(USE_TILES) && defined(MSDOS)